¿Cómo encuentro el nombre de la función de llamada?

votos
34

He estado utilizando PRETTY_FUNCTION para dar salida al nombre de la función actual; sin embargo, he vuelto a implementar algunas funciones y me gustaría saber qué funciones las están llamando.

En C ++, ¿cómo puedo obtener el nombre de la función de la rutina de llamada?

Publicado el 09/12/2008 a las 16:43
fuente por usuario
En otros idiomas...                            


7 respuestas

votos
39

Aquí hay una solución que puedes usar a menudo. Tiene la ventaja de no requerir cambios en el código de la función real ( no se pueden agregar llamadas a las funciones de apilamiento, cambiar los parámetros para pasar los nombres de las funciones o vincular las bibliotecas adicionales ). Para que funcione, solo necesitas usar un poco de magia de preprocesador:

Ejemplo simple

// orignal function name was 'FunctionName'
void FunctionNameReal(...)
{
  // Do Something
}

#undef FunctionName
#define FunctionName printf("Calling FunctionName from %s\n",__FUNCTION__);FunctionNameReal

Debe cambiar el nombre de su función temporalmente, pero consulte la nota a continuación para obtener más sugerencias. Esto dará como resultado una printf()declaración en cada punto de invocación de la función. Obviamente, debe hacer algunos arreglos si llama a una función miembro o necesita capturar el valor devuelto ( Como pasar la llamada __FUNCTION__ a la función y a una función personalizada que devuelve el mismo tipo ... ), pero la técnica básica es la mismo. Es posible que desee utilizar __LINE__y / __FILE__o algunas otras macros de preprocesador según el compilador que tenga. (Este ejemplo es específicamente para MS VC ++, pero probablemente funcione en otros).

Además, es posible que desee poner algo como esto en su encabezado rodeado de #ifdefguardias para activarlo de manera condicional, que también puede gestionar el cambio de nombre de la función real.

ACTUALIZACIÓN [2012-06-21]

Recibí una solicitud para expandir mi respuesta. Como resultado, mi ejemplo anterior es un poco simplista. Aquí hay algunos ejemplos completos de cómo manejar esto, usando C ++.

Ejemplo de fuente completa con un valor de retorno

Usar un classcon operator()hace que esto sea bastante directo. Esta primera técnica funciona para funciones independientes con y sin valores de retorno. operator()solo necesita reflejar el mismo retorno que la función en cuestión, y tener argumentos que coincidan.

Puede compilar esto con una versión que g++ -o test test.cppno informa y g++ -o test test.cpp -DREPORTuna versión que muestra la información de la persona que llama.

#include <iostream>

int FunctionName(int one, int two)
{
  static int calls=0;
  return (++calls+one)*two;
}

#ifdef REPORT
  // class to capture the caller and print it.  
  class Reporter
  {
    public:
      Reporter(std::string Caller, std::string File, int Line)
        : caller_(Caller)
        , file_(File)
        , line_(Line)
      {}

      int operator()(int one, int two)
      {
        std::cout
          << "Reporter: FunctionName() is being called by "
          << caller_ << "() in " << file_ << ":" << line_ << std::endl;
        // can use the original name here, as it is still defined
        return FunctionName(one,two);
      }
    private:
      std::string   caller_;
      std::string   file_;
      int           line_;

  };

// remove the symbol for the function, then define a new version that instead
// creates a stack temporary instance of Reporter initialized with the caller
#  undef FunctionName
#  define FunctionName Reporter(__FUNCTION__,__FILE__,__LINE__)
#endif


void Caller1()
{
  int val = FunctionName(7,9);  // <-- works for captured return value
  std::cout << "Mystery Function got " << val << std::endl;
}

void Caller2()
{
  // Works for inline as well.
  std::cout << "Mystery Function got " << FunctionName(11,13) << std::endl;
}

int main(int argc, char** argv)
{
  Caller1();
  Caller2();
  return 0;
}

Muestra de salida (Informes)

Reporter: FunctionName() is being called by Caller1() in test.cpp:44
Mystery Function got 72
Reporter: FunctionName() is being called by Caller2() in test.cpp:51
Mystery Function got 169

Básicamente, en cualquier lugar que FunctionNameocurra, lo reemplaza con Reporter(__FUNCTION__,__FILE__,__LINE__), el efecto neto de que es el preprocesador escribiendo algún objeto instancing con una llamada inmediata a la operator()función. Puede ver el resultado (en gcc) de las sustituciones del preprocesador con g++ -E -DREPORT test.cpp. Caller2 () se convierte en esto:

void Caller2()
{
  std::cout << "Mystery Function got " << Reporter(__FUNCTION__,"test.cpp",51)(11,13) << std::endl;
}

Puede ver eso __LINE__y __FILE__ha sido sustituido. (No estoy seguro de por qué __FUNCTION__todavía se muestra en el resultado para ser honesto, pero la versión compilada informa la función correcta, por lo que probablemente tenga algo que ver con el preprocesamiento de múltiples pasadas o un error de gcc).

Ejemplo de fuente completa con una función de miembro de clase

Esto es un poco más complicado, pero muy similar al ejemplo anterior. En lugar de simplemente reemplazar la llamada a la función, también estamos reemplazando la clase.

Al igual que en el ejemplo anterior, puede compilar esto para una versión que g++ -o test test.cppno informa y g++ -o test test.cpp -DREPORTpara una versión que muestra la información de la persona que llama.

#include <iostream>

class ClassName
{
  public:
    explicit ClassName(int Member)
      : member_(Member)
      {}

    int FunctionName(int one, int two)
    {
      return (++member_+one)*two;
    }

  private:
    int member_;
};

#ifdef REPORT
  // class to capture the caller and print it.  
  class ClassNameDecorator
  {
    public:
      ClassNameDecorator( int Member)
        : className_(Member)
      {}

      ClassNameDecorator& FunctionName(std::string Caller, std::string File, int Line)
      {
        std::cout
          << "Reporter: ClassName::FunctionName() is being called by "
          << Caller << "() in " << File << ":" << Line << std::endl;
        return *this;
      }
      int operator()(int one, int two)
      {
        return className_.FunctionName(one,two);
      }
    private:
      ClassName className_;
  };


// remove the symbol for the function, then define a new version that instead
// creates a stack temporary instance of ClassNameDecorator.
// FunctionName is then replaced with a version that takes the caller information
// and uses Method Chaining to allow operator() to be invoked with the original
// parameters.
#  undef ClassName
#  define ClassName ClassNameDecorator
#  undef FunctionName
#  define FunctionName FunctionName(__FUNCTION__,__FILE__,__LINE__)
#endif


void Caller1()
{
  ClassName foo(21);
  int val = foo.FunctionName(7,9);  // <-- works for captured return value
  std::cout << "Mystery Function got " << val << std::endl;
}

void Caller2()
{
  ClassName foo(42);
  // Works for inline as well.
  std::cout << "Mystery Function got " << foo.FunctionName(11,13) << std::endl;
}

int main(int argc, char** argv)
{
  Caller1();
  Caller2();
  return 0;
}

Aquí hay un resultado de muestra:

Reporter: ClassName::FunctionName() is being called by Caller1() in test.cpp:56
Mystery Function got 261
Reporter: ClassName::FunctionName() is being called by Caller2() in test.cpp:64
Mystery Function got 702

Los puntos más importantes de esta versión son una clase que decora la clase original y una función de reemplazo que devuelve una referencia a la instancia de la clase, lo que permite operator()hacer la llamada a la función real.

Espero que ayude a alguien!

Respondida el 18/12/2008 a las 16:22
fuente por usuario

votos
17

Aquí hay dos opciones:

  1. Puede obtener una pila completa (incluido el nombre, el módulo y el desplazamiento de la función de llamada) con versiones recientes de glibc con las funciones de rastreo de GNU . Vea mi respuesta aquí para más detalles. Esta es probablemente la cosa más fácil.

  2. Si eso no es exactamente lo que estás buscando, entonces puedes probar libunwind , pero va a requerir más trabajo.

Tenga en cuenta que esto no es algo que pueda conocer de forma estática (como ocurre con PRETTY_FUNCTION); en realidad tienes que caminar la pila para descubrir qué función te llamó. Por lo tanto, esto no es algo que realmente valga la pena hacer en las impresiones ordinarias de depuración. Sin embargo, si desea realizar depuraciones o análisis más serios, esto podría ser útil para usted.

Respondida el 09/12/2008 a las 16:50
fuente por usuario

votos
8

Con la versión 4.8 de GCC ≥ puede utilizar __builtin_FUNCTION- que no debe confundirse con __FUNCTION__y similares - que parece ser un poco oscuro.

Ejemplo:

#include <cstdio>

void foobar(const char* str = __builtin_FUNCTION()){
    std::printf("called by %s\n", str);
}

int main(){
    foobar();
    return 0;
}

salida:

called by main

ejemplo en WandBox

Respondida el 14/05/2017 a las 23:30
fuente por usuario

votos
2

A menos que hay más a la cuestión de lo que le pide explícitamente, simplemente cambiar el nombre de la función y dejar que el compilador / enlazador le diga donde se llama.

Respondida el 21/06/2012 a las 18:48
fuente por usuario

votos
0

Puede utilizar este código, para realizar un seguimiento de los loci de control en la última n puntos en su programa. Uso: véase la función principal de abajo.

// What: Track last few lines in loci of control, gpl/moshahmed_at_gmail
// Test: gcc -Wall -g -lm -std=c11 track.c
#include <stdio.h>
#include <string.h>

#define _DEBUG
#ifdef _DEBUG
#define lsize 255 /* const int lsize=255; -- C++ */
struct locs {
  int   line[lsize];
  char *file[lsize];
  char *func[lsize];
  int  cur; /* cur=0; C++ */
} locs;

#define track do {\
      locs.line[locs.cur]=__LINE__ ;\
      locs.file[locs.cur]=(char*)__FILE__ ;\
      locs.func[locs.cur]=(char*) __builtin_FUNCTION() /* __PRETTY_FUNCTION__ -- C++ */ ;\
      locs.cur=(locs.cur+1) % lsize;\
  } while(0);

void track_start(){
  memset(&locs,0, sizeof locs);
}

void track_print(){
  int i, k;
  for (i=0; i<lsize; i++){
    k = (locs.cur+i) % lsize;
    if (locs.file[k]){
      fprintf(stderr,"%d: %s:%d %s\n",
        k, locs.file[k],
        locs.line[k], locs.func[k]);
    }
  }
}
#else
#define track       do {} while(0)
#define track_start() (void)0
#define track_print() (void)0
#endif


// Sample usage.
void bar(){ track ; }
void foo(){ track ; bar(); }

int main(){
  int k;
  track_start();
  for (k=0;k<2;k++)
    foo();
  track;
  track_print();
  return 0;
} 
Respondida el 21/05/2018 a las 21:34
fuente por usuario

votos
0

En la primera aproximación, solo grep la base de código para los nombres de las funciones. Luego viene Doxygen, y luego el registro dinámico (ambos discutidos por otros).

Respondida el 09/12/2008 a las 17:18
fuente por usuario

votos
0

Probablemente desee los nombres de todas las funciones que potencialmente podrían llamarlos. Esto es básicamente un conjunto de bordes en el gráfico de llamadas. Doxygen puede generar el gráfico de llamadas, y luego es simplemente una cuestión de mirar los bordes entrantes de su nodo de funciones.

Respondida el 09/12/2008 a las 16:54
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more