C:可以在粘贴前扩展marcos,但不能递归吗?

时间:2017-07-30 21:33:38

标签: macros c-preprocessor

我知道当##存在时,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)? (不限于我这样做的一般方式。)

1 个答案:

答案 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时它只会被涂成蓝色,唯一的故事是U11取代,然后重新扫描,导致无法继续工作其中U1未上漆。

这三个技巧:不评估U,递归评估U,并用某些蓝色涂料在某处停止评估U,这几乎是您可以使用的唯一工具。这些都不会使U在任何中间步骤中变为U1(足够长时间来应用粘贴运算符)。