2 min to read
可變參數宏 __VA_ARGS__
目錄: C/C++ Macro 宏系列 | CWKSC’s blog | 博客
▌__VA_ARGS__
可變參數宏
宏參數列表以 ...
結尾,__VA_ARGS__
會完整無缺地把輸入到 ...
的内容展開
#define foo(...) __VA_ARGS__
foo(x, y) // x, y
foo(42, meow, owo MEOW!!!) // 42, meow, owo MEOW!!!
一如以往,參數會被轉換成 Token 序列,所以:
- 前後的空格會被忽略
- 中間的空白會被壓縮為一個
▌常見用法
#define debug_printf(str, ...) \
printf("[%s] [%s] [line%d] : " str, \
__FILE__, __func__, __LINE__, __VA_ARGS__)
void Foo(){
debug_printf("test %d", 42);
// 下面是展開步驟 :
printf("[%s] [%s] [line%d] : " str, __FILE__, __func__, __LINE__, __VA_ARGS__);
printf("[%s] [%s] [line%d] : " "test %d", __FILE__, __func__, __LINE__, 42);
printf("[%s] [%s] [line%d] : " "test %d", "D:\\main.cpp", "Foo", 6, 42);
printf("[%s] [%s] [line%d] : test %d", "D:\\main.cpp", "Foo", 6, 42);
}
輸出:
[D:\\main.cpp] [Foo] [line6] : test 42
三個步驟:
- 輸入參數替換 (
str
,__VA_ARGS__
) - 預定義宏展開 (
__FILE__
,__func__
,__LINE__
) - 鄰近字串符合併 (
"ab" "cd"
—>"abcd"
)
▌## __VA_ARGS__
去除額外的逗號
這是編譯器擴展
想一想,如果額外參數的數量為零
以上面那個作為例子,例如 debug_printf("42")
(只輸入了一個參數,...
並沒有接收任何東西)
展開時就會出現了一個額外的逗號,因此編譯會出錯
我們可以把 __VA_ARGS__
改為 ## __ VA_ARGS__
,編譯器會自動把前面的逗號去除
如果不想用編譯器擴展,可以看看:
c - Standard alternative to GCC’s ##__VA_ARGS__
trick? - Stack Overflow
下面有一個喪心病狂的答案 …… 我只是個人類(瑟瑟發抖.jpg
▌Visual Studio 不一致的行為
Visual Studio 在處理 __VA_ARGS__
時出現了與其他編譯器不一致的行為
__VA_ARGS__
會被視為一個整體的 Token 處理
這意味在未重掃描之前會被當成一個參數處理
#define get_args0(args0, ...) args0
#define foo(...) get_args0(__VA_ARGS__)
foo(x, y)
get_args0(__VA_ARGS__)
get_args0(x, y) // x, y 被視為一個整體
x, y
foo(x, y)
展開了 x, y
我們預期的是展開為 x
而不是 x, y
重掃描可以解決這個問題,這個另一篇文章會講,就不在這裏說了
▌Visual Studio 這樣的行為有什麼意義?
如果有超多層的宏函數的時候,而你又不想 __VA_ARGS__
在中途被展開
這種特性可以讓您自行決定預期的 __VA_ARGS__
在什麼時候才生效
但這麻煩到很多開發者,也坑死了超多人
不過在超多層宏的時候你反而會讚歎這個巧妙的設計
▌結語
__VA_ARGS__
可變參數宏要用上手很簡單
不少有趣的宏函數都是圍繞著 __VA_ARGS__
可變參數宏開發
衍生了很多千奇百怪的用法
其實結語也不知道說什麽好
所以就醬啦 OW< 拜~