C语言对宏的详细规定见另一篇文章,这里只对宏的展开过程做简单说明
-
展开参数
如果宏的参数是宏,则先展开参数
#define A 1 #define F(x, y) x+2 ## y F(A, A)
F(A)
的展开过程:由于A
作为参数,又是一个宏,那么先展开这个参数为1
-
把参数的展开结果替换到宏文本中 ,用于
#
或##
操作的参数则直接替换实参,而不是实参的展开结果F(A)
的宏文本是x+2
把参数的展开结果1
替换进去就变成1+2
-
处理
#
和##
操作符 -
本次展开完毕,进入重扫描 :对展开结果重新扫描一次,如果发现宏,则重复步骤1,直到没有宏可以展开
#define A 0 #define B A #define C B int a = C; /* 展开过程: (每一个->代表重扫描) C -> B -> A -> 0 */
注意,重扫描过程中 涉及自指 的宏只会被展开一次
#define A tag A /* 展开过程: A -> tag A //此处的A依然是宏,但是已经展开一次了就不会再展开,否则就永远都展开不完了 */
举个栗子
#define glue(x,y) a ## b
#define xglue(x,y) glue(x,y)
glue(a,b);
-> ab;
glue(a, glue(b,c));
-> aglue(b,c); //glue(b,c)虽然是宏但是用于##就不展开直接替换
xglue(a, xglue(b,c));
-> glue(a, bc); //xglue(b,c)完全展开为bc再替换进去
-> abc
一些魔法
多一层宏
上面的例子中,嵌套一层宏可以确保参数为宏时正确展开,下面再举个例子
#define double_para 0,0
#define GET_first(_1,...) _1
#define xGET_first(_1,...) GET_first(_1)
GET_first(double_para,1)
->0,0 //double_para完全展开为0,0
xGET_first(double_para,1)
->GET_first(0,0, 1) //double_para完全展开为0,0
->0
统计 __VA_ARGS__
数量
#define get_6th(_1,_2,_3,_4,_5,_6,...) _6
#define count_args(...) get_6th(__VA_ARGS__, 5,4,3,2,1)
count_args(a)
-> get_6th(a, 5,4,3,2,1)
-> 1
count_args(a,b,c)
-> get_6th(a,b,c, 5,4,3,2,1)
-> 3
局限性:本质就是统计逗号,所以无法区分0个和1个参数,这个问题在下面解决
检查逗号
上面统计 __VA_ARGS__
数量本质就是统计 ...
里面的逗号个数,根据这个方法写出下面宏,后面会经常用到
#define get_6th(_1,_2,_3,_4,_5,_6,...) _6
#define CHECK_COMMA(...) get_6th(__VA_ARGS__, 1,1,1,1,0)
CHECK_COMMA(aaa)
-> get_6th(aaa, 1,1,1,1,0)
-> 0
CHECK_COMMA(aa, a)
-> get_6th(aa, a, 1,1,1,1,0)
-> 1
匹配
#define IS_1 "111"
#define IS_0 "Im:0"
#define match(x) IS_ ## x
match(0)
-> IS_0
-> "Im:0"
match(1)
-> IS_1
-> "111"
只能对已知内容进行匹配,没有defual功能,于是有这个解决方案
#define GET_ARGS_3th(_1,_2,_3,...) _3
#define EXPAND(x) x
#define _MATCH_void _,_
#define _MATCH_float _,_
#define power_switch(x) EXPAND(GET_ARGS_3th EXPAND((_MATCH_##x, x, defual)))
power_switch(void) ->void
power_switch(float) -> float
power_switch(int) -> other
power_switch(char) -> other
检查参数是否为空
#define get_3th(_1,_2,_3,...) _3
#define CHECK_COMMA(...) get_3th(__VA_ARGS__, 1,0)
#define COMMA _,_
#define IS_EMPTY(x) CHECK_COMMA(CO ## x ## MMA)
IS_EMPTY();
-> CHECK_COMMA(COMMA); //拼接x,如果为空就会造成COMMA
-> get_6th(_,_, 1,1,1,1,0); // COMMA展开为 _,_
-> 1;
接受多参数的 IS_EMPTY
#define PRIMITIVE_GLUE(x, y) x ## y
#define GLUE(x, y) PRIMITIVE_GLUE(x, y)
#define get_6th(_1,_2,_3,_4,_5,_6,...) _6
#define CHECK_COMMA(...) get_6th(__VA_ARGS__,1,1,1,1,0)
#define COMMA_01 _,_
#define xIS_EMPTY(...) \
CHECK_COMMA( \
GLUE( \
COMMA_, \
GLUE( \
CHECK_COMMA(__VA_ARGS__), \
CHECK_COMMA(COM##__VA_ARGS__##MA_01) \
) \
) \
)
统计 __VA_ARGS__
数量-支持0个参数
#define PRIMITIVE_GLUE(x, y) x ## y
#define GLUE(x, y) PRIMITIVE_GLUE(x, y)
#define GET_ARGS_21th(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,...) _21
#define CHECK_HAS_COMMA_max20(...) GET_ARGS_21th(__VA_ARGS__,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0)
#define ThisIsAComma_01 ,
#define IS_EMPTY(...) \
CHECK_HAS_COMMA_max20( \
GLUE( \
ThisIsAComma_, \
GLUE( \
CHECK_HAS_COMMA_max20(__VA_ARGS__), \
CHECK_HAS_COMMA_max20(ThisIs##__VA_ARGS__##AComma_01) \
) \
) \
)
#define COUNT_ARGS_HELPER_IS_EMPTY_1(...) 0
#define COUNT_ARGS_HELPER_IS_EMPTY_0(...) GET_ARGS_21th(__VA_ARGS__,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
#define COUNT_ARGS_max20(...) GLUE(COUNT_ARGS_HELPER_IS_EMPTY_,IS_EMPTY(__VA_ARGS__))(__VA_ARGS__)