C/C++宏定義的可變參數

轉自 http://www.vimer.cn/2010/03/cc%E5%AE%8F%E5%AE%9A%E4%B9%89%E7%9A%84%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0.html


編寫代碼的過程中,經常會輸出一些調試信息到屏幕上,一般會調用printf這類的函數。
但是當調試解決之後,我們需要手工將這些地方刪除或者註釋掉。
再這次的項目中就用到類似問題,爲了調試程序,再一些地方輸出了很多的信息,隨着項目的調試,輸出的信息越來越多。於是就面臨着,如何處理這些輸出信息的語句。
簡單刪掉,不僅有一定的工作量,而且也不能保證之後就不出現問題,出現問題後這些信息還是有用的。
不去掉,帶着調試信息就上線,這是明顯不允許的。
於是就想到了一個可行的辦法。如下:

void myprintf(char* fmt, ...)
{
}
#ifdef DEBUG
#define printf(fmt, args...) myprintf(fmt, ##args)
#endif

調試階段帶着DEBUG調試,正式上線就可以把printf變成一個空函數了。
這樣做的一個潛在風險是可能會導致默寫glib函數需要調用printf輸出錯誤log也給取消掉了。
令人欣慰的是,大部分glib調用的應該是fprintf。
雖然問題解決了,但是我對args...以及##args還是不太瞭解。上網找了些gcc手冊的資料如下:
帶有可變參數的宏(Macros with a Variable Number of Arguments)
在1999年版本的ISO C 標準中,宏可以象函數一樣,定義時可以帶有可變參數。宏的語法和函數的語法類似。下面有個例子:

#define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)

這裏,‘…’指可變參數。這類宏在被調用時,它(這裏指‘…’)被表示成零個或多個符號,包括裏面的逗號,一直到到右括弧結束爲止。當被調用時,在宏體(macro body)中,那些符號序列集合將代替裏面的__VA_ARGS__標識符。更多的信息可以參考CPP手冊。
GCC始終支持複雜的宏,它使用一種不同的語法從而可以使你可以給可變參數一個名字,如同其它參數一樣。例如下面的例子:

#define debug(format, args...) fprintf (stderr, format, args)

這和上面舉的那個ISO C定義的宏例子是完全一樣的,但是這麼寫可讀性更強並且更容易進行描述。
GNU CPP還有兩種更復雜的宏擴展,支持上面兩種格式的定義格式。
在標準C裏,你不能省略可變參數,但是你卻可以給它傳遞一個空的參數。例如,下面的宏調用在ISO C裏是非法的,因爲字符串後面沒有逗號:

debug ("A message")

GNU CPP在這種情況下可以讓你完全的忽略可變參數。在上面的例子中,編譯器仍然會有問題(complain),因爲宏展開後,裏面的字符串後面會有個多餘的逗號。
爲了解決這個問題,CPP使用一個特殊的‘##’操作。書寫格式爲:

#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

這裏,如果可變參數被忽略或爲空,‘##’操作將使預處理器(preprocessor)去除掉它前面的那個逗號。如果你在宏調用時,確實提供了一些可變參數,GNU CPP也會工作正常,它會把這些可變參數放到逗號的後面。象其它的pasted macro參數一樣,這些參數不是宏的擴展。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章