Jak mogę znaleźć nazwę funkcji wywołującej?

głosy
34

I zostały z wykorzystaniem PRETTY_FUNCTION do wyjścia obecna nazwa funkcji, jednak mam przepisany niektóre funkcje i chciałbyś dowiedzieć się, które funkcje są nazywając je.

W C ++ w jaki sposób można uzyskać nazwę funkcji wywołującej rutyny?

Utwórz 09/12/2008 o 16:43
źródło użytkownik
W innych językach...                            


9 odpowiedzi

głosy
39

Oto rozwiązanie można często używać. Ma tę zaletę, że nie wymaga żadnych zmian w kodzie funkcji rzeczywistych ( nie dodając połączeń do stackwalk funkcji, zmiana parametrów przejść w nazwach funkcji lub linkami do dodatkowych bibliotek. ). Aby to działa, po prostu trzeba użyć trochę preprocesora magii:

prostym przykładem

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

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

Musisz zmienić nazwę funkcję tymczasowo, ale patrz uwaga poniżej wiecej sugestii. Będzie to skutkować printf()stwierdzeniem w każdym momencie wywołania funkcji. Oczywiście, trzeba dokonać pewnych ustaleń, jeśli wywołanie funkcji składowej lub potrzebujesz uchwycić wartości zwracanej ( Jak przekazać wywołanie funkcji i __FUNCTION__ do funkcji niestandardowej, która zwraca ten sam typ ... ), ale technika jest podstawowym podobnie. Może chcesz użyć __LINE__i __FILE__czy jakieś inne makra preprocesora zależności od kompilatora masz. (Ten przykład jest specjalnie dla MS VC ++, ale prawdopodobnie działa w innych).

Ponadto, może chcesz umieścić coś takiego w swoim nagłówku otoczony przez #ifdefstrażników warunkowo go włączyć, które mogą obsługiwać zmiany nazwy rzeczywistą funkcję dla Ciebie.

Update [2012-06-21]

Mam prośbę, aby rozwinąć moją odpowiedź. Jak się okazuje, mój Powyższy przykład jest nieco uproszczone. Oto niektóre przykłady w pełni kompilacji obsługi to, używając C ++.

Pełna Źródło Przykład z wartości zwracanej

Używanie classz operator()marek to całkiem proste. Ta pierwsza technika działa dla wolnostojących funkcje i bez wartości zwracanych. operator()po prostu musi odzwierciedlać ten sam zwrot jak funkcja o którym mowa, i mają pasujące argumenty.

Można skompilować z tego g++ -o test test.cppdla nich wersję bez sprawozdawczym oraz g++ -o test test.cpp -DREPORTdo wersji, która wyświetla informacje rozmówcy.

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

Wyjście próbki (raportowanie)

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

Zasadniczo, wszędzie tam, gdzie FunctionNamewystępuje, to zastępuje go Reporter(__FUNCTION__,__FILE__,__LINE__), efekt netto, która jest preprocesor pisząc jakiś przedmiot Instancing z natychmiastowym wywołaniu operator()funkcji. Można wyświetlić wynik (w GCC) z substytucji z preprocesora g++ -E -DREPORT test.cpp. Caller2 () staje się w ten sposób:

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

Widać, że __LINE__i __FILE__zostały podstawione. (Nie jestem pewien, dlaczego __FUNCTION__nadal pokazuje na wyjściu, aby być uczciwym, ale wersja skompilowana informuje właściwą funkcję, więc prawdopodobnie ma coś wspólnego z multi-towarzysza wyprzedzającym lub gcc bug).

Pełna Źródło Przykład z funkcją klasy państwach

To jest nieco bardziej skomplikowana, ale bardzo podobny do poprzedniego przykładu. Zamiast po prostu zastąpienie wywołanie funkcji, jesteśmy również zastąpienie klasę.

Podobnie jak w powyższym przykładzie, można zestawić to z g++ -o test test.cppza wersję bez sprawozdawczym oraz g++ -o test test.cpp -DREPORTdo wersji, która wyświetla informacje rozmówcy.

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

Oto przykładowe dane wyjściowe:

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

Wysokie punkty tej wersji to klasa, która zdobi oryginalny klasę, a funkcja zastępczą, która zwraca odwołanie do instancji klasy, pozwalając operator()zrobić rzeczywiste wywołanie funkcji.

Mam nadzieję, że ktoś pomoże!

Odpowiedział 18/12/2008 o 16:22
źródło użytkownik

głosy
17

Tutaj są dwie opcje:

  1. Można uzyskać pełny StackTrace (nazwa, moduł i przesunięcie funkcji wywołującej) z ostatnich wersji glibc z funkcji ślad GNU . Zobacz moją odpowiedź tutaj o szczegóły. To jest prawdopodobnie najłatwiejsze.

  2. Jeśli nie jest to dokładnie to, czego szukasz, to możesz spróbować libunwind , ale to będzie obejmować więcej pracy.

Należy pamiętać, że nie jest to coś, co można wiedzieć statycznie (jak PRETTY_FUNCTION); trzeba rzeczywiście chodzić stosu, aby dowiedzieć się, co funkcja nazywa cię. Więc to nie jest coś, co naprawdę warto robić w zwykłych printfs debugowania. Jeśli chcesz zrobić bardziej poważne debugowanie lub analizy, choć to może być przydatne dla Ciebie.

Odpowiedział 09/12/2008 o 16:50
źródło użytkownik

głosy
8

W wersji GCC ≥ 4,8 można użyć __builtin_FUNCTION- nie mylić z __FUNCTION__i podobne - to zdaje się być nieco niejasne.

Przykład:

#include <cstdio>

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

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

wydajność:

called by main

Przykładem na WandBox

Odpowiedział 15/05/2017 o 02:30
źródło użytkownik

głosy
2

Odmiana Aaron odpowiedź. Nie jestem pewien, czy ta odpowiedź ma ten problem, ale kiedy zrobić #define function, staje się zmienną globalną, a następnie, jeśli projekt ma kilka klas o tej samej nazwie funkcji klasy członek wszystkie klasy będą miały ich nazwa funkcji nowo do tego samego funkcjonować.

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

Wynik:

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
Odpowiedział 17/11/2018 o 16:13
źródło użytkownik

głosy
2

O ile nie ma więcej do pytania, niż wyraźnie poprosił, po prostu zmienić nazwę funkcji i niech kompilator / linker powiedzieć, gdzie to się nazywa.

Odpowiedział 21/06/2012 o 18:48
źródło użytkownik

głosy
0

Można użyć tego kodu, aby śledzić loci kontroli w punktach n ostatni w swoim programie. Zastosowanie: patrz funkcja głównego poniżej.

// 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;
} 
Odpowiedział 22/05/2018 o 00:34
źródło użytkownik

głosy
0

W jodły zbliżenia, tylko grep kodzie dla nazw funkcji. Potem przychodzi Doxygen, a następnie dynamiczne rejestrowanie (oba omawiane przez innych).

Odpowiedział 09/12/2008 o 17:18
źródło użytkownik

głosy
0

Prawdopodobnie chcesz nazwy wszystkich funkcji, które potencjalnie mogłyby je nazywają. To jest w zasadzie zbiorem krawędzi w grafie połączeń. Doxygen można wygenerować wykres rozmowę, a potem po prostu kwestia patrzenia na przychodzące krawędzi węzła funkcji.

Odpowiedział 09/12/2008 o 16:54
źródło użytkownik

głosy
-2

Cflow mogą być wykorzystane, aby uzyskać wykres wywołania kodu źródłowego napisanego w C / C ++. Można analizować ten wykres połączenia, aby dostać to, czego chcesz.

Odpowiedział 23/07/2019 o 17:17
źródło użytkownik

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