C++中的宏替换

100 篇文章 8 订阅

1.引子
#define cat(x, y) x ## y
那么cat(a, b)和cat(cat(a, b), c)的结果是啥.

#define str_impl(x) #x
#define str(x) str_impl(x)
的意图何在.

2.规则
宏替换是C/C++的预处理中的一部分,在C++标准中有4条规则来定义替换.

规则1:实参替换.
本条规则描述带参数的宏的替换过程.

对于宏定义中的形参,在替换列表中,如果不是作为#或##的操作数,那么将对应实参完全
展开(相当于对实参进行求值),然后将替换列表中的形参替换掉.如果是#或##的操作数,
那么不进行替换.

规则2:多次扫描.

在所有的形参替换为实参后,对结果进行再次扫描,如果发现还有可替换的宏,则进行替换,
否则中止.

规则3:递归替换抑制.

如果在替换列表中发现当前正在展开的宏的名字,那么这里不进行替换.更进一步,在嵌套
的替换过程中发现已经替换过的宏的名字,则不进行替换.

规则4:递归预处理抑制.

如果替换后的结果形成预处理指令,则不执行这条预处理指令.

3.实例
#define cat(x, y) x ## y

在cat(cat(a, b), c)中,首先扫描替换列表,发现x和y两个形参作为##的操作数,那么直接
将实参不作任何处理地搬过来,并进行连接运算,得到结果是cat(a, b)c

若在此基础上增加
#define xcat(x, y) cat(x, y)
在xcat(xcat(a, b), c)中首先对x进行求值,也就是先计算xcat(a, b)的结果,容易得到
值是ab.然后对y进行求值,发现不必进行任何处理,值是c,得到结果cat(ab, c).
然后应用规则2,对cat(ab, c)进行求值,容易得到结果是abc.

显然
#define str_impl(x) #x
#define str(x) str_impl(x)
的意图在于防止#阻止宏作为参数的时候被规则1阻止展开.

再看几个C++标准中的例子:
#define x 3
#define f(a) f(x * (a))
#undef x
#define x 2
#define g f
#define z z[0]
#define h g(~
#define m(a) a(w)
#define w 0,1
#define t(a) a
f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
g(x+(3,4)-w) | h 5) & m(f)^m(m);
其结果分别是
f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
f(2 * (2+(3,4)-0,1)) | f(2 * ( ~ 5)) & f(2 * (0,1))^m(0,1);

对于第一个,主要在于t(t(g)(0) + t)(1)的展开.
容易计算出最外层的t的实参是f(2 * (0)) + t,而作为t的参数传入时其中的t是
正在被展开的宏,所以根据规则3,不对这个t进行处理,保持不变,得到f(2 * (0)) + t(1).

对于第二个,h 5)被替换为g(~5),应用规则2,被替换为f(2 * ( ~ 5)).
而m(m)首先被替换为m(w),然后应用规则2再次进行替换,但是m已经是替换过的了,所以保持
不变,只对w进行替换.

#define str(s) # s
#define xstr(s) str(s)
#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \
x ## s, x ## t)
#define INCFILE(n) vers ## n /* from previous #include example */
#define glue(a, b) a ## b
#define xglue(a, b) glue(a, b)
#define HIGHLOW "hello"
#define LOW LOW ", world"
debug(1, 2);
fputs(str(strncmp("abc\0d", "abc", ’\4’) /* this goes away */
== 0) str(: @\n), s);
#include xstr(INCFILE(2).h)
glue(HIGH, LOW);
xglue(HIGH, LOW)

其结果分别是
printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
fputs("strncmp(\"abc\\0d\", \"abc\", ’\\4’) = = 0" ": @\n", s);
#include "vers2.h"
"hello";
"hello" ", world"

关键是glue和xglue.
对于glue(HIGH, LOW);,首先有一个规则1的抑制,得到HIGHLOW;的结果,然后二次扫描,得到
"hello";
对于xglue(HIGH, LOW)没有抑制效果,所以对参数求值,分别得到HIGH和LOW ", world",即
glue(HIGH, LOW ", world")
然后进行连接操作得到HIGHLOW ", world",最后再扫描一次得到"hello" ", world"

如果考虑字符串的自然的连接,就可以得到"hello, world"了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值