C++ 如何找到调用函数的名称?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/353180/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:51:08  来源:igfitidea点击:

How do I find the name of the calling function?

c++debugging

提问by Phil Hannent

I have been using PRETTY_FUNCTION to output the current function name, however I have reimplemented some functions and would like to find out which functions are calling them.

我一直在使用 PRETTY_FUNCTION 来输出当前函数名,但是我重新实现了一些函数并想找出哪些函数正在调用它们。

In C++ how can I get the function name of the calling routine?

在 C++ 中,如何获取调用例程的函数名称?

采纳答案by Todd Gamblin

Here are two options:

这里有两个选项:

  1. You can get a full stacktrace (including the name, module, and offset of the calling function) with recent versions of glibc with the GNU backtrace functions. See my answer herefor the details. This is probably the easiest thing.

  2. If that isn't exactly what you're looking for, then you might try libunwind, but it's going to involve more work.

  1. 您可以使用带有GNU 回溯功能的最新版本的 glibc 获得完整的堆栈跟踪(包括调用函数的名称、模块和偏移量)。有关详细信息,请在此处查看我的答案。这可能是最简单的事情了。

  2. 如果这不是您要找的东西,那么您可以尝试libunwind,但它会涉及更多的工作。

Keep in mind that this isn't something you can know statically (as with PRETTY_FUNCTION); you actually have to walk the stack to figure out what function called you. So this isn't something that's really worth doing in ordinary debug printfs. If you want to do more serious debugging or analysis, though, then this might be useful for you.

请记住,这不是您可以静态知道的(与 PRETTY_FUNCTION 一样);您实际上必须遍历堆栈以找出调用您的函数。所以这不是在普通的调试 printfs 中真正值得做的事情。但是,如果您想进行更认真的调试或分析,那么这可能对您有用。

回答by Aaron

Here is a solution you can often use. It has the advantage of requiring no changes to the actual function code (no adding calls to stackwalk functions, changing parameters to pass in function names, or linking to extra libraries.). To get it working, you simply need to use a bit of preprocessor magic:

这是您可以经常使用的解决方案。它的优点是无需更改实际函数代码(无需添加对 stackwalk 函数的调用、更改参数以传入函数名称或链接到额外的库。)。为了让它工作,你只需要使用一些预处理器魔法:

Simple Example

简单示例

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

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

You must rename your function temporarily, but see the note below for more suggestions. This will result in a printf()statement at each point of calling the function. Obviously, you have to make some arrangements if you are calling a member function, or need to capture the return value (Like pass the function call and__FUNCTION__to a custom function that returns the same type...), but the basic technique is the same. You might want to use __LINE__and __FILE__or some other preprocessor macros depending on which compiler you have. (This example is specifically for MS VC++, but probably works in others.)

您必须暂时重命名您的函数,但请参阅下面的注释以获取更多建议。这将导致在printf()调用函数的每个点都有一个语句。显然,如果您正在调用成员函数,或者需要捕获返回值(例如将函数调用传递__FUNCTION__给返回相同类型的自定义函数......),您必须做出一些安排,但基本技术是相同的。您可能想要使用__LINE__和/__FILE__或其他一些预处理器宏,具体取决于您拥有的编译器。(此示例专门针对 MS VC++,但可能适用于其他示例。)

Also, you might want to put something like this in your header surrounded by #ifdefguards to conditionally turn it on, which can handle renaming the actual function for you as well.

此外,您可能希望将这样的内容放在由#ifdef守卫包围的标题中以有条件地将其打开,这也可以为您处理重命名实际功能。

UPDATE [2012-06-21]

更新 [2012-06-21]

I got a request to expand my answer. As it turns out, my above example is a bit simplistic. Here are some fully compiling examples of handling this, using C++.

我收到了一个扩展我的答案的请求。事实证明,我上面的例子有点简单。以下是一些使用 C++ 处理此问题的完全编译示例。

Full Source Example with a return value

带有返回值的完整源示例

Using a classwith operator()makes this pretty straight forward. This first technique works for freestanding functions with and without return values. operator()just needs to reflect the same return as the function in question, and have matching arguments.

使用classwithoperator()使这变得非常简单。第一种技术适用于有和没有返回值的独立函数。 operator()只需要反映与相关函数相同的返回值,并具有匹配的参数。

You can compile this with g++ -o test test.cppfor a non-reporting version and g++ -o test test.cpp -DREPORTfor a version that displays the caller information.

您可以g++ -o test test.cpp为非报告版本和g++ -o test test.cpp -DREPORT显示调用者信息的版本编译它。

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

Sample Output (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

Basically, anywhere that FunctionNameoccurs, it replaces it with Reporter(__FUNCTION__,__FILE__,__LINE__), the net effect of which is the preprocessor writing some object instancing with an immediate call to the operator()function. You can view the result (in gcc) of the preprocessor substitutions with g++ -E -DREPORT test.cpp. Caller2() becomes this:

基本上,在任何FunctionName发生的地方,它都会将其替换为Reporter(__FUNCTION__,__FILE__,__LINE__),其最终效果是预处理器通过立即调用operator()函数来编写一些对象实例。您可以查看预处理器替换的结果(在 gcc 中)g++ -E -DREPORT test.cpp。Caller2() 变成这样:

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

You can see that __LINE__and __FILE__have been substituted. (I'm not sure why __FUNCTION__still shows in the output to be honest, but the compiled version reports the right function, so it probably has something to do with multi-pass preprocessing or a gcc bug.)

您可以看到__LINE____FILE__已被替换。(__FUNCTION__老实说,我不确定为什么仍然在输出中显示,但编译版本报告了正确的功能,因此它可能与多通道预处理或 gcc 错误有关。)

Full Source Example with a Class Member Function

带有类成员函数的完整源代码示例

This is a bit more complicated, but very similar to the previous example. Instead of just replacing the call to the function, we are also replacing the class.

这有点复杂,但与前面的示例非常相似。我们不仅替换了对函数的调用,还替换了类。

Like the above example, you can compile this with g++ -o test test.cppfor a non-reporting version and g++ -o test test.cpp -DREPORTfor a version that displays the caller information.

与上面的示例一样,您可以g++ -o test test.cpp为非报告版本和g++ -o test test.cpp -DREPORT显示调用者信息的版本编译它。

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

Here is sample output:

这是示例输出:

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

The high points of this version are a class that decorates the original class, and a replacement function that returns a reference to the class instance, allowing the operator()to do the actual function call.

这个版本的亮点是一个装饰原始类的类,以及一个返回对类实例的引用的替换函数,允许operator()进行实际的函数调用。

回答by Rusty Shackleford

With GCC version ≥ 4.8 you can use __builtin_FUNCTION— not to be confused with __FUNCTION__and similar — it seems to be a bit obscure.

使用 GCC 版本 ≥ 4.8 你可以使用__builtin_FUNCTION——不要与__FUNCTION__和相似混淆——它似乎有点晦涩。

Example:

例子:

#include <cstdio>

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

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

output:

输出:

called by main

example on WandBox

WandBox 上的示例

回答by bgbarcus

Unless there is more to the question than you explicitly asked, just rename the function and let the compiler/linker tell you where it is called.

除非问题比您明确提出的要多,否则只需重命名函数并让编译器/链接器告诉您调用它的位置。

回答by user

Variation of Aaronanswer. I am not sure whether this answer has this problem, but when you do a #define function, it becomes a global variable, then, if your project has several classes with the same member class function name, all classes will have their function name redefined to the same function.

亚伦答案的变化。我不确定这个答案是否有这个问题,但是当你做 a 时#define function,它变成了一个全局变量,那么,如果你的项目有几个具有相同成员类函数名的类,那么所有类都会将它们的函数名重新定义为相同的功能。

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

Result:

结果:

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

回答by mosh

You can use this code, to track loci of control in last n points in your program. Usage: see main function below.

您可以使用此代码来跟踪程序中最后 n 个点的控制轨迹。用法:见下面的主要功能。

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

回答by MSalters

You probably want the names of all functions that potentially could call them. This is basically a set of edges in the call graph. doxygencan generate the call graph, and then it's simply a matter of looking at the incoming edges of your functions node.

您可能想要所有可能调用它们的函数的名称。这基本上是调用图中的一组边。doxygen可以生成调用图,然后只需查看函数节点的传入边即可。

回答by MSalters

In the firs approximation, just grep the codebase for the function names. Then comes Doxygen, and then dynamic logging (both discussed by others).

在第一个近似中,只需 grep 函数名称的代码库。然后是 Doxygen,然后是动态日志记录(其他人都讨论过)。

回答by Shivam Kumar

Cflowcan be used to get the call graph of the source code written in C/C++. You can parse this call graph to get what you want.

Cflow可用于获取用 C/C++ 编写的源代码的调用图。你可以解析这个调用图来得到你想要的。