Como faço para encontrar o nome da função de chamada?

votos
34

Tenho vindo a utilizar PRETTY_FUNCTION para a saída o nome da função atual, no entanto tenho reimplemented algumas funções e gostaria de saber quais funções são chamá-los.

Em C ++ como posso obter o nome da função da rotina de chamada?

Publicado 09/12/2008 em 16:43
fonte usuário
Em outras línguas...                            


8 respostas

votos
39

Aqui está uma solução muitas vezes você pode usar. Ele tem a vantagem de exigir nenhuma alteração para o código de função real ( sem chamadas adicionando ao stackwalk funções, alteração de parâmetros para passar em nomes de função, ou ligando para bibliotecas extras. ). Para fazê-lo funcionar, basta usar um pouco de magia pré-processador:

Exemplo simples

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

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

Você deve renomear sua função temporariamente, mas veja a nota abaixo para mais sugestões. Isso resultará em uma printf()declaração em cada ponto de chamar a função. Obviamente, você tem que fazer alguns arranjos se você estiver chamando uma função membro, ou a necessidade de capturar o valor de retorno ( como passar a chamada de função e __FUNCTION__ para uma função personalizada que retorna o mesmo tipo ... ), mas a técnica básica é a mesmo. Você pode querer usar __LINE__e __FILE__ou algumas outras macros de pré-processamento, dependendo de qual compilador você tem. (Este exemplo é especificamente para MS VC ++, mas provavelmente funciona em outros).

Além disso, você pode querer colocar algo assim em seu cabeçalho cercado por #ifdefguardas para condicionalmente ligá-lo, o que pode lidar com renomeando a função real para você também.

UPDATE [2012-06-21]

Eu tenho um pedido para expandir a minha resposta. Como se vê, o meu exemplo acima é um pouco simplista. Aqui estão alguns exemplos totalmente compilação de lidar com isso, usando C ++.

Exemplo Fonte cheio com um valor de retorno

Usando um classcom operator()marcas este muito para a frente. Esta primeira técnica funciona para autônomo funções com e sem valores de retorno. operator()só precisa de refletir o mesmo retorno que a função em questão, e têm argumentos correspondentes.

Você pode compilar este com g++ -o test test.cppuma versão não-apresentação de relatórios e g++ -o test test.cpp -DREPORTpara uma versão que exibe as informações do chamador.

#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;
}

Exemplo de saída (Relatórios)

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

Basicamente, qualquer lugar que FunctionNameocorre, ele substitui-lo com Reporter(__FUNCTION__,__FILE__,__LINE__), o efeito líquido do que é o pré-processador escrever algum objeto instancing com uma chamada de imediato à operator()função. Você pode ver o resultado (em gcc) das substituições de pré-processamento com g++ -E -DREPORT test.cpp. Caller2 () torna-se o seguinte:

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

Você pode ver que __LINE__e __FILE__foram substituídos. (Eu não sei por que __FUNCTION__ainda mostra na saída para ser honesto, mas a versão compilada relata a função certa, por isso provavelmente tem algo a ver com o pré-processamento multi-pass ou um bug gcc.)

Exemplo Fonte cheio com uma função de membro de classe

Isto é um pouco mais complicado, mas muito semelhante ao exemplo anterior. Em vez de apenas substituir a chamada para a função, também estamos substituindo a classe.

Como no exemplo acima, você pode compilar este com g++ -o test test.cppuma versão não-apresentação de relatórios e g++ -o test test.cpp -DREPORTpara uma versão que exibe as informações do chamador.

#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;
}

Aqui é exemplo de saída:

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

Os pontos altos desta versão são uma classe que decora a classe original, e uma função de substituição que retorna uma referência para a instância da classe, permitindo a operator()fazer a chamada de função real.

Espero que ajude alguém!

Respondeu 18/12/2008 em 16:22
fonte usuário

votos
17

Aqui estão duas opções:

  1. Você pode obter um stacktrace completo (incluindo o nome, módulo, e offset da função de chamada) com versões recentes do glibc com as funções backtrace GNU . Veja minha resposta aqui para os detalhes. Esta é provavelmente a coisa mais fácil.

  2. Se isso não é exatamente o que você está procurando, então você pode tentar libunwind , mas vai envolver mais trabalho.

Tenha em mente que isso não é algo que você pode conhecer estaticamente (como acontece com PRETTY_FUNCTION); você realmente tem que andar a pilha para descobrir o que função chamou. Então isso não é algo que é realmente vale a pena fazer em printfs depuração comuns. Se você quer fazer mais grave a depuração ou análise, no entanto, em seguida, isso pode ser útil para você.

Respondeu 09/12/2008 em 16:50
fonte usuário

votos
8

Com GCC versão ≥ 4,8 você pode usar __builtin_FUNCTION- para não ser confundido com __FUNCTION__e similar - parece ser um pouco obscura.

Exemplo:

#include <cstdio>

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

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

saída:

called by main

exemplo no WandBox

Respondeu 15/05/2017 em 02:30
fonte usuário

votos
2

Variação de Aaron resposta. Não tenho a certeza se esta resposta tem este problema, mas quando você faz um #define function, torna-se uma variável global, em seguida, se seu projeto tem várias classes com o mesmo nome da função de membro de classe, todas as classes terão o seu nome da função redefinido para o mesmo função.

#include <iostream>

struct ClassName {
    int member;
    ClassName(int member) : member(member) { }

    int secretFunctionName(
              int one, int two, const char* caller, const char* file, int line) 
    {
        std::cout << "Reporter: ClassName::function_name() is being called by "
                << caller << "() in " << file << ":" << line << std::endl;

        return (++member+one)*two;
    }
};

#define unique_global_function_name(first, second) \
        secretFunctionName(first, second, __FUNCTION__,__FILE__,__LINE__)

void caller1() {
    ClassName foo(21);
    int val = foo.unique_global_function_name(7, 9);
    std::cout << "Mystery Function got " << val << std::endl;
}

void caller2() {
    ClassName foo(42);
    int val = foo.unique_global_function_name(11, 13);
    std::cout << "Mystery Function got " << val << std::endl;
}

int main(int argc, char** argv) {
    caller1();
    caller2();
    return 0;
}

Resultado:

Reporter: ClassName::function_name() is being called by caller1() in D:\test.cpp:26
Mystery Function got 261
Reporter: ClassName::function_name() is being called by caller2() in D:\test.cpp:33
Mystery Function got 702
Respondeu 17/11/2018 em 16:13
fonte usuário

votos
2

A menos que haja mais para a questão do que você pediu explicitamente, apenas mudar o nome da função e deixar o compilador / vinculador dizer-lhe onde é chamado.

Respondeu 21/06/2012 em 18:48
fonte usuário

votos
0

Você pode usar esse código, para rastrear loci de controle em última n pontos no seu programa. Uso: ver função principal abaixo.

// 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;
} 
Respondeu 22/05/2018 em 00:34
fonte usuário

votos
0

Na aproximação abetos, apenas grep a base de código para os nomes das funções. Em seguida, vem Doxygen, e registro, em seguida, dinâmica (ambos discutidos por outros).

Respondeu 09/12/2008 em 17:18
fonte usuário

votos
0

Você provavelmente quer os nomes de todas as funções que potencialmente poderia chamá-los. Este é basicamente um conjunto de arestas no gráfico de chamadas. Doxygen pode gerar o gráfico de chamadas, e então é simplesmente uma questão de olhar para as bordas de entrada de seu nó de funções.

Respondeu 09/12/2008 em 16:54
fonte usuário

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