我知道当##存在时,C预处理器不会扩展宏,因此需要2级宏:
#define CAT_(a,b) a##b
#define CAT(a,b) CAT_(a,b)
但这总会导致递归的宏扩展。
例如,如果我希望R(U)
扩展为R_U1
,而U
是一个宏,不幸的是定义为另一个宏(用于配置),以下代码都不适用于我:
A
#define U1 1
#define R_U1 2
#define U U1
#define R(u) R_##u
乙
#define U1 1
#define R_U1 2
#define U U1
#define C(a, b) a##b
#define R(u) C(R_, u)
对于 A R(U)
展开为R_U
,这意味着宏U
未展开。对于 B ,我得到R_1
,显然已经递归扩展。
那么,是否可以R_U1
获得R(U)
? (不限于我这样做的一般方式。)
答案 0 :(得分:1)
不幸的是,不,这是不可能的(假设你是想询问是否可以U
扩展到U1
,然后粘贴)。
让我们假设一开始我们就是这样:
#define U1 1
#define U U1
U
...然后这一行上的U
,即对象类宏的调用,将扩展到其替换列表(U1
)。这将用U
涂成蓝色重新扫描。重新扫描会发现U1
为类似对象的宏,并将其替换为替换列表(1
),然后将其重新扫描,并将U1
涂成蓝色。结果只是1
。我们在这里调用整个序列是扫描。
因此,如果您应用扫描,则会获得1
。如果不这样做,则只需U
。
我知道当##存在时,C预处理器不会扩展宏,
这是真的,但对你没有帮助。假设我们在:
中添加这些宏#define CAT_(a,b) a##b
#define CAT(a,b) CAT_(a,b)
#define X x_rl
#define Y y_rl
...然后CAT_(X,Y)
会产生XY
,而CAT(X,Y)
会产生x_rly_rl
。这是通过应用参数替换规则。参数替换是类似函数的宏,等同于用类替换列表替换类似对象的宏调用。如果参数出现在替换列表中,并且既不是字符串化也不是粘贴操作的参与者,则在替换参数之前扫描相应的参数。如果参数位于替换列表中,并且 正在进行字符串化或者是粘贴操作的参与者,则参数将替换参数而不进行扫描。但同样,这只意味着如果扫描适用,则会得到1
;如果没有,你会得到U
。
让我们稍微复杂一点来说明:
#define STRINGIFY(...) #__VA_ARGS__
#define PAS(X,Y) STRINGIFY(X##Y)
#define STR(X) STRINGIFY(X)
PAS(U,) // expands to "U"
STR(U) // expands to "1"
这里,STRINGIFY
用作“塞子”;参数替换首先发生在这里,但由于这是对其参数进行字符串化,因此它们不会被扩展。知道了这一点,PAS
显示了参数替换的效果而没有应用扫描; STR
应用扫描时的效果。 STR
的扩展步骤如下所示:
STR(U)
STRINGIFY(1) // U evaluated before replacing X in STR's replacement list
#1
"1"
PAS
看起来像这样:
PAS(U,)
STRINGIFY(U ## <empty-placeholder>) // U *not* evaluated before replacing X
STRINGIFY(U)
#U
"U"
这里没有“棘手的中间”解决方案。使用粘贴运算符延迟参数替换扫描会为您提供U
。不使用它会为您提供1
。
唯一的其他扫描是重新扫描和更换。在此扫描期间,宏被“涂成蓝色”,因此它们无法再展开。但这对你也无济于事。要在中间步骤中结束U1
,您需要将U1
涂成蓝色。但是在展开U1
时它只会被涂成蓝色,唯一的故事是U1
被1
取代,然后重新扫描,导致无法继续工作其中U1
未上漆。
这三个技巧:不评估U
,递归评估U
,并用某些蓝色涂料在某处停止评估U
,这几乎是您可以使用的唯一工具。这些都不会使U
在任何中间步骤中变为U1
(足够长时间来应用粘贴运算符)。