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