淺析C++中的間接宏函數

宏函數對於每個C++程序員都決不陌生,就算是初出茅廬的C++程序員也知道如何定義、使用宏函數。

但是當初學者看到類似於以下這種宏函數嵌套的時候,可能還是會比較嘀咕,

#define CONVERTSTR(x) #x
#define CONVERTSTR2(x) CONVERTSTR(x)

第二個宏函數所做的事情不就是再一次調用上面的宏函數嗎,這難道不屬於畫蛇添足嗎?這樣做有什麼意義呢?別急,我們慢慢來捋一下。

瞭解#和##

要想熟練的寫出宏函數,瞭解其中的操作符必不可少,在預編譯體系自定義的幾個操作符中, #和##比較特殊,它們的作用是:

將標識符轉換為字符串,它又被稱為字符串化操作符,用法如下

#define CONVERTSTR(x) #x
string s3 { CONVERTSTR(4) }; //這裡CONVERTSTR(4)被擴展為"4"

將不同的標識符連接起來,它被稱為符號連接操作符,用法如下

struct ABC
{

};

#define DECLARE_MAKE(x) x* Make_##x() {return new x();}
DECLARE_MAKE(ABC) //被擴展為 ABC* Make_ABC{return new ABC();}
ABC * ap = Make_ABC();

可見這兩操作符的運算結果取決於傳入的標識符的名稱,那麼如果傳入的標識符本身就是一個宏變量呢?

宏變量亂入的情況

還是剛剛的例子,

#define CONVERTSTR(x) #x
#define VAR 10
std::cout << CONVERTSTR(VAR);

猜猜,這個時候的輸出是多少?10 還是 VAR?
按照預處理器替換的原則,VAR被替換成10,接著10被轉換為”10″,但是真是這樣嗎?

運行之後發現,輸出是VAR不是10,為什麼呢?

替換規則

這是因為當宏函數中,如果包含瞭#或者##,替換規則會比較特殊,引用一段原文如下:

After the arguments for the invocation of a function-like macro have been identified,
argument substitution takes place. A parameter in the replacement list, unless preceded by
a # or ## preprocessing token or followed by a ## preprocessing token (see below), is
replaced by the corresponding argument after all the macros contained therein have been
expanded. Before being substituted, each argument’s preprocessing tokens are completely
macro replaced as if they formed the rest of the preprocessing file; no other preprocessing
tokens is available.

簡而言之,對於宏函數來說,一般情況下當看到函數體的時候,參數替換就已經完成瞭(像用10替換VAR),但是對於有操作符#和##的參數,這個參數替換步驟就不會發生,所以CONVERTSTR(VAR)隻會擴展為 “VAR”而不會擴展為”10”

修復方法

其實講到這裡答案已經很明顯瞭,使用間接宏函數能完美解決這個問題

#define CONVERTSTR(x) #x
#define CONVERTSTR2(x) CONVERTSTR(x)

在原有函數的基礎上再定義一個包裝函數,這個包裝函數並沒有任何#或者##,這樣就確保瞭參數可以正確展開,接著轉發請求給真正需要使用的那個函數。

#define VAR 10
std::cout << CONVERTSTR2(VAR);

這樣就能確保在使用VAR調用函數的時候它已經被正確展開瞭。

這就是間接宏函數和為什麼要使用它們的原因,希望下次看到它們的時候不要再覺得這是畫蛇添足瞭喲。

到此這篇關於C++中的間接宏函數的文章就介紹到這瞭,更多相關C++間接宏函數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!