在Visual Studio 2005中,默认状况下,此特性是激活的,但不能与/EP和/P编译选项同时使用。请注意在IDE环境中,不能识别__func__ ,而要用__FUNCTION__ 代替。
Comeau的用户也应使用 __FUNCTION__ ,而不是 __func__ 。
C++ BuilderX的用户则应使用稍稍不一样的名字:__FUNC__ 。
GCC 3.0及更高的版本同时支持 __func__ 和__FUNCTION__ 。工具
仅仅为了获取函数名,就在函数体中嵌入硬编码的字符串,这种方法单调乏味还易致使错误,不如看一下怎样使用新的C99特性,在程序运行时获取函数名吧。
对象反射库、调试工具及代码分析器,常常会须要在运行时访问函数的名称,直到不久前,惟一能完成此项任务而且可移植的方法,是手工在函数体内嵌入一个带有该函数名的硬编码字符串,没必要说,这种方法很是单调无奇,而且容易致使错误。本文将要演示怎样使用新的C99特性,在运行时获取函数名。
那么怎样以编程的方式从当前运行的函数中获得函数名呢?
答案是:使用__FUNCTION__ 及相关宏。
引出问题
一般,在调试中最让人心烦的阶段,是不断地检查是否已调用了特定的函数。对此问题的解决方法,通常是添加一个cout或printf()——若是你使用C语言,以下所示:
void myfunc() { cout<<"myfunc()"<<endl; //其余代码 } |
一般在一个典型的工程中,会包含有数千个函数,要在每一个函数中都加入一条这样的输出语句,无疑难过上“蜀山”啊,所以,须要有一种机制,能够自动地完成这项操做。
获取函数名
做为一个C++程序员,可能常常遇到 __TIME__、__FILE__、__DATE__ 这样的宏,它们会在编译时,分别转换为包含编译时间、处理的转换单元名称及当前时间的字符串。
在最新的ISO C标准中,如你们所知的C99,加入了另外一个有用的、相似宏的表达式__func__,其会报告未修饰过的(也就是未裁剪过的)、正在被访问的函数名。请注意,__func__不是一个宏,由于预处理器对此函数一无所知;相反,它是做为一个隐式声明的常量字符数组实现的:
static const char __func__[] = "function-name"; |
在function-name处,为实际的函数名。为激活此特性,某些编译器须要使用特定的编译标志,请查看相应的编译器文档,以获取具体的资料。
有了它,咱们可免去大多数经过手工修改,来显示函数名的苦差事,以上的例子可以下所示进行重写:
void myfunc() { cout<<"__FUNCTION__"<<endl; } |
官方C99标准为此目的定义的__func__标识符,确实值得你们关注,然而,ISO C++却不彻底支持全部的C99扩展,所以,大多数的编译器提供商都使用 __FUNCTION__ 取而代之,而 __FUNCTION__ 一般是一个定义为 __func__ 的宏,之因此使用这个名字,是由于它已受到了大多数的普遍支持。
在Visual Studio 2005中,默认状况下,此特性是激活的,但不能与/EP和/P编译选项同时使用。请注意在IDE环境中,不能识别__func__ ,而要用__FUNCTION__ 代替。
Comeau的用户也应使用 __FUNCTION__ ,而不是 __func__ 。
C++ BuilderX的用户则应使用稍稍不一样的名字:__FUNC__ 。
GCC 3.0及更高的版本同时支持 __func__ 和__FUNCTION__ 。
一旦可自动获取当前函数名,你能够定义一个以下所示显示任何函数名的函数:
void show_name(const char * name) { cout<<name<<endl; }
void myfunc() { show_name(__FUNCTION__); //输出:myfunc }
void foo() { show_name(__FUNCTION__); //输出:foo } |
由于 __FUNCTION__ 会在函数大括号开始以后就当即初始化,因此,foo()及myfunc()函数可在参数列表中安全地使用它,而不用担忧重载。
签名与修饰名
__FUNCTION__ 特性最初是为C语言设计的,然而,C++程序员也会常常须要有关他们函数的额外信息,在Visual Studio 2005中,还支持另外两种非标准的扩展特性:__FUNCDNAME__ 与 __FUNCSIG__ ,其分别转译为一个函数的修饰名与签名。函数的修饰名很是有用,例如,在你想要检查两个编译器是否共享一样的ABI时,就可派得上用场,另外,它还能帮助你破解那些含义模糊的连接错误,甚至还可用它从一个DLL中调用另外一个用C++连接的函数。在下例中,show_name()报告了函数的修饰名:
void myfunc() { show_name(__FUNCDNAME__); //输出:?myfunc@@YAXXZ } |
一个函数的签名由函数名、参数列表、返回类型、内含的命名空间组成。若是它是一个成员函数,它的类名和const/volatile限定符也将是签名的一部分。如下的代码演示了一个独立的函数与一个const成员函数签名间的不一样之处,两个函数的名称、返回类型、参数彻底相同:
void myfunc(){show_name(__FUNCSIG__); // void __cdecl myfunc(void)}struct S{void myfunc() const {show_name(__FUNCSIG__); //void __thiscall S::myfunc(void) const}}; |