Wie kann ich den Namen der anrufenden Funktion finden?

stimmen
34

Ich habe mit PRETTY_FUNCTION haben, aber ich die aktuelle Funktion Namen Ausgabe einige Funktionen neu implementiert und möchte herausfinden, welche Funktionen sie anrufen.

In C ++ wie kann ich den Namen der Funktion des rufenden Routine bekommen?

Veröffentlicht am 09/12/2008 um 16:43
quelle vom benutzer
In anderen Sprachen...                            


9 antworten

stimmen
39

Hier ist eine Lösung , die Sie häufig verwenden können. Es hat den Vorteil , keine Änderungen an den eigentlichen Funktionscode erforderlich ( keine Zugabe Anruf Funktionen stackwalk, Ändern von Parametern in Funktionsnamen zu übergeben, oder die Verknüpfung zu zusätzlichen Bibliotheken. ). Um es zu bekommen arbeiten, müssen Sie einfach ein bisschen Präprozessor Magie benutzen:

einfaches Beispiel

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

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

Sie müssen Ihre Funktion vorübergehend, benennen aber den Hinweis unten für weitere Vorschläge sehen. Dies wird in einer Folge printf()an jedem Punkt Anweisung , um die Funktion aufzurufen. Offensichtlich müssen Sie einige Vorkehrungen treffen , wenn Sie eine Member - Funktion aufrufen, oder müssen Sie den Rückgabewert erfassen ( wie den Funktionsaufruf übergeben und __FUNCTION__ auf eine benutzerdefinierte Funktion , die die gleiche Art zurückkehrt ... ), aber die grundlegende Technik ist die gleich. Vielleicht möchten Sie verwenden __LINE__und __FILE__oder einige andere Präprozessormakros je nachdem , welcher Compiler Sie haben. (Dieses Beispiel ist speziell für MS VC ++, funktioniert aber wahrscheinlich in anderen.)

Außerdem möchten Sie vielleicht so etwas wie dies in der Kopfzeile von umgeben setzen #ifdefWachen bedingt es einschalten, die auch für Sie die eigentliche Funktion Umbenennen verarbeiten kann.

UPDATE [2012-06-21]

Ich habe eine Anfrage meine Antwort zu erweitern. Wie sich herausstellt, meine obigen Beispiel ist ein bisschen simpel. Hier sind einige vollständig Kompilierung Beispiele für diese Handhabung, C ++ verwenden.

Vollständige Quelle Beispiel mit einem Rückgabewert

Mit einem classmit operator()Marken nach vorne ziemlich gerade diese. Diese erste Technik funktioniert für Funktionen mit und ohne Rückgabewerte freistehend. operator()braucht nur die gleiche Rendite wie die Funktion in Frage zu reflektieren, und dazu passende Argumente haben.

Sie können dies kompilieren mit g++ -o test test.cppeiner nicht-Reporting - Version und g++ -o test test.cpp -DREPORTfür eine Version , die die Anruferinformationen anzeigt.

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

Beispielausgabe (Reporting)

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

Grundsätzlich überall , das FunctionNameauftritt, ersetzt er es mit Reporter(__FUNCTION__,__FILE__,__LINE__)dem Nettoeffekt davon ist der Vorprozessor einen Gegenstand Instancing mit einem sofortigen Aufruf an die operator()Schreibfunktion. Sie können mit dem Ergebnis (in gcc) den Präprozessor Substitutionen sehen g++ -E -DREPORT test.cpp. Caller2 () wird folgendermaßen aus :

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

Sie können sehen , dass __LINE__und __FILE__substituiert wurden. (Ich bin nicht sicher , warum __FUNCTION__zeigt nach wie vor in der Ausgabe ehrlich zu sein, aber die kompilierte Version meldet die richtige Funktion, so hat es wahrscheinlich etwas mit Multi-Pass - Vorverarbeitung oder gcc Fehler zu tun.)

Vollständige Quelle Beispiel mit einer Klasse-Member-Funktion

Das ist ein bisschen komplizierter, aber sehr ähnlich dem vorhergehenden Beispiel. Anstatt nur den Aufruf der Funktion zu ersetzen, ersetzen wir auch die Klasse.

Wie das obige Beispiel, können Sie diese kompilieren mit g++ -o test test.cppeiner nicht-Reporting - Version und g++ -o test test.cpp -DREPORTfür eine Version , die die Anruferinformationen anzeigt.

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

Hier ist eine Beispielausgabe:

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

Die Höhepunkte dieser Version sind eine Klasse, die die ursprüngliche Klasse verziert, und eine Ersatzfunktion , die einen Verweis auf die Klasse - Instanz zurückzugibt, so dass die operator()den eigentlichen Funktionsaufruf zu tun.

Hoffe, das hilft jemand!

Beantwortet am 18/12/2008 um 16:22
quelle vom benutzer

stimmen
17

Hier sind zwei Möglichkeiten:

  1. Sie können eine vollständige Stacktrace erhalten (einschließlich der Namen, Modul und der rufenden Funktion Offset) mit aktuellen Versionen von glibc mit dem GNU Backtrace - Funktionen . Siehe meine Antwort hier für die Details. Dies ist wahrscheinlich die einfachste Sache.

  2. Wenn das nicht genau das , was Sie suchen, dann könnten Sie versuchen libunwind , aber es wird mehr Arbeit einzubeziehen.

Denken Sie daran, dass dies nicht etwas, das Sie statisch (wie bei PRETTY_FUNCTION) wissen können; Sie haben tatsächlich den Stapel zu gehen, um herauszufinden, welche Funktion Sie genannt. Das ist also nicht etwas, das wirklich wert ist, in normaler Debug printfs tun. Wenn Sie ernstere Debuggen oder Analyse tun wollen, aber dann könnte dies für Sie nützlich sein.

Beantwortet am 09/12/2008 um 16:50
quelle vom benutzer

stimmen
8

Mit GCC - Version ≥ 4.8 können Sie verwenden __builtin_FUNCTION- nicht zu verwechseln mit __FUNCTION__und ähnlich - es scheint ein wenig unklar zu sein.

Beispiel:

#include <cstdio>

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

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

Ausgabe:

called by main

Beispiel auf WandBox

Beantwortet am 15/05/2017 um 02:30
quelle vom benutzer

stimmen
2

Variation von Aaron Antwort. Ich bin nicht sicher , ob diese Antwort hat dieses Problem, aber wenn Sie ein tun #define function, wird es eine globale Variable, dann, wenn Ihr Projekt mehrere Klassen mit den gleichen Elementklasse Funktionsnamen hat, werden alle Klassen ihre Funktionsnamen auf den gleichen neu definiert haben Funktion.

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

Ergebnis:

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
Beantwortet am 17/11/2018 um 16:13
quelle vom benutzer

stimmen
2

Es sei denn, es mehr auf die Frage, als Sie ausdrücklich gefragt, benennen Sie die Funktion und lassen Sie den Compiler / Linker Ihnen sagen, wo es genannt wird.

Beantwortet am 21/06/2012 um 18:48
quelle vom benutzer

stimmen
0

Sie können diesen Code verwenden, in Ihrem Programm in der letzten n Punkten loci die Kontrolle zu verfolgen. Verbrauch: siehe unten Hauptfunktion.

// 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;
} 
Beantwortet am 22/05/2018 um 00:34
quelle vom benutzer

stimmen
0

In der Tannen Näherung grep nur die Code-Basis für die Funktionsnamen. Dann kommt Doxygen und dann dynamische Protokollierung (beide von anderen diskutiert).

Beantwortet am 09/12/2008 um 17:18
quelle vom benutzer

stimmen
0

Sie wollen wahrscheinlich die Namen aller Funktionen , die möglicherweise sie nennen könnte. Dies ist im Grunde eine Reihe von Kanten in dem Aufrufgraphen. Doxygen kann den Aufrufgraphen, erzeugen und dann ist es einfach eine Frage der an den eingehenden Kanten Ihrer Funktionen Knoten suchen.

Beantwortet am 09/12/2008 um 16:54
quelle vom benutzer

stimmen
-2

Cflow kann verwendet werden , um den Aufrufgraphen des Quellcodes in C / C ++ geschrieben zu bekommen. Sie können diesen Aufruf Graph analysieren zu bekommen , was Sie wollen.

Beantwortet am 23/07/2019 um 17:17
quelle vom benutzer

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