我如何找到调用函数的名称?
我一直在使用PRETTY_FUNCTION来输出当前的函数名称,但是我已经重新实现了一些函数,并想找出哪些函数正在调用它们。
在C ++中,我如何获得调用例程的函数名称?
这里有两个选项:
您可以使用GNU backtrace函数获取最新版本的glibc的完整堆栈跟踪(包括调用函数的名称,模块和偏移量)。 有关详细信息,请参阅我的答案。 这可能是最简单的事情。
如果这不正是你想要的,那么你可能会尝试libunwind,但它会涉及更多的工作。
请记住,这不是静态可以知道的事情(与PRETTY_FUNCTION一样); 你实际上必须走栈来找出你叫什么功能。 所以这在普通的调试printf中并不值得。 但是,如果您想进行更严肃的调试或分析,那么这可能对您有用。
这是您经常可以使用的解决方案。 它具有不需要修改实际功能代码的优点(不需要添加对栈函数的调用,改变参数以传递函数名称或链接到额外的库)。 为了得到它的工作,你只需要使用一些预处理器魔法:
简单的例子
// orignal function name was 'FunctionName'
void FunctionNameReal(...)
{
// Do Something
}
#undef FunctionName
#define FunctionName printf("Calling FunctionName from %sn",__FUNCTION__);FunctionNameReal
您必须暂时重命名您的功能,但请参阅下面的注释获取更多建议。 这将导致在调用该函数的每个点都有一个printf()
语句。 显然,如果你正在调用一个成员函数,或者需要捕获返回值(比如将函数调用和__FUNCTION__
传递给返回相同类型的自定义函数...),则必须做出一些安排,但基本技巧是一样。 您可能需要使用__LINE__
和__FILE__
或其他一些预处理器宏,具体取决于您使用的编译器。 (这个例子专门用于MS VC ++,但可能适用于其他。)
此外,您可能希望在#ifdef
卫兵包围的标题中放置类似这样的内容,以便有条件地打开它,这也可以为您重命名实际功能。
更新[2012-06-21]
我收到了一个请求来扩展我的答案。 事实证明,我上面的例子有点简单。 以下是使用C ++处理这个问题的一些完整编译示例。
具有返回值的完整源代码示例
使用operator()
的class
使得这非常简单。 这第一种技术适用于具有和不具有返回值的独立函数。 operator()
只需要反映与正在讨论的函数相同的返回值,并且具有匹配的参数。
您可以使用g++ -o test test.cpp
编译此g++ -o test test.cpp
为非报告版本g++ -o test test.cpp -DREPORT
,在代码中显示调用者信息的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;
}
示例输出(报告)
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()
函数编写一些对象实例。 您可以使用g++ -E -DREPORT test.cpp
查看预处理器替换的结果(在gcc中)。 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
用于非报告版本,用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;
}
这里是示例输出:
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()
执行实际的函数调用。
希望能帮助别人!
对于GCC版本≥4.8,您可以使用__builtin_FUNCTION
- 不要与__FUNCTION__
和类似的混淆 - 它似乎有点模糊。
例:
#include <cstdio>
void foobar(const char* str = __builtin_FUNCTION()){
std::printf("called by %sn", str);
}
int main(){
foobar();
return 0;
}
输出:
called by main
在WandBox上的例子
链接地址: http://www.djcxy.com/p/86049.html