当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何找到调用函数的名称?》经验,为你挑选了3个好方法。

我一直在使用PRETTY_FUNCTION来输出当前的函数名,但是我已经重新实现了一些函数,并希望找出哪些函数正在调用它们.

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



1> Aaron..:

这是您经常使用的解决方案.它的优点是不需要更改实际的功能代码(不添加对stackwalk函数的调用,更改参数以传递函数名称,或链接到额外的库).要使其正常工作,您只需使用一些预处理器魔法:

简单的例子

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

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

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

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

更新[2012-06-21]

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

带有返回值的完整源示例

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

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

#include 

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

样本输出(报告)

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

基本上,在任何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;
}

你可以看到,__LINE____FILE__已取代.(我不确定为什么__FUNCTION__仍然在输出中显示为了诚实,但编译版本报告正确的功能,因此它可能与多次传递预处理或gcc错误有关.)

具有类成员函数的完整源示例

这有点复杂,但与前面的例子非常相似.我们也在替换类,而不是仅仅替换对函数的调用.

与上面的示例类似,您可以g++ -o test test.cpp使用非报告版本和g++ -o test test.cpp -DREPORT显示调用者信息的版本进行编译.

#include 

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

这是示例输出:

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

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

希望有人帮助!


@AndrewDunai很高兴你喜欢它.对于某些情况,它们非常有用.我想它们有点像goto语句:有充分理由避开,但在正确的地方无可替代.= d

2> Todd Gamblin..:

这有两个选择:

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

    如果这不是您正在寻找的,那么您可以尝试libunwind,但它将涉及更多的工作.

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



3> Rusty Shackl..:

GCC版本≥4.8你可以使用__builtin_FUNCTION- 不要混淆__FUNCTION__和类似 - 它似乎有点模糊.

例:

#include 

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

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

输出:

called by main

WandBox上的示例

推荐阅读
牛尾巴2010
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有