macro ? magic ! C语言的宏魔法

C语言对宏的详细规定见另一篇文章,这里只对宏的展开过程做简单说明

  1. 展开参数

    如果宏的参数是宏,则先展开参数

    #define A 1
    #define F(x, y) x+2 ## y
    F(A, A)
    

    F(A) 的展开过程:由于 A 作为参数,又是一个宏,那么先展开这个参数为 1

  2. 把参数的展开结果替换到宏文本中 ,用于 ### 操作的参数则直接替换实参,而不是实参的展开结果

    F(A) 的宏文本是 x+2 把参数的展开结果 1 替换进去就变成 1+2

  3. 处理 ### 操作符

  4. 本次展开完毕,进入重扫描 :对展开结果重新扫描一次,如果发现宏,则重复步骤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__)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值