【问题标题】:C preprocessor macro expansionC 预处理器宏扩展
【发布时间】:2018-01-04 15:02:02
【问题描述】:

我很难理解 C 预处理器在以下上下文中如何应用重写规则。我有以下宏:

#define _A(x) "A" _##x
#define _B(x) "B" _##x
#define X(x) _##x

想法是这些宏中的每一个都使用连接来创建一个新的表达式,它本身可以是一个宏——如果它是一个宏,我希望它被扩展:

现在,正如我所料,以下展开:

X(x)       expands to _x
X(A(x))    expands to "A" _x
X(A(B(x))) expands to "A" "B" _x

然而,一旦同一个宏被多次使用,扩展就会停止:

X(A(A(x)))       expands to "A" _A(x), expected "A" "A" _x
X(B(B(x)))       expands to "B" _B(x), expected "B" "B" _x
X(A(B(A(x))))    expands to "A" "B" _A(x), expected "A" "B" "A" _x 
X(A(B(A(B(x))))) expands to "A" "B" _A(B(x)), expected "A" "B" "A" "B" _x 

我猜这里有某种“只能扩展同名宏一次”的规则?我可以做些什么来让宏以我想要的方式扩展?

【问题讨论】:

  • 这是因为您使用了## 运算符,它不计算宏;这是Nested macro expansion 的副本
  • 也许我的大脑工作不正常,但我不明白这是如何重复这些问题。在您链接的问题中,问题是先扩展宏然后再连接。我想做完全相反的事情:先连接然后展开。此外,至少正在发生一些扩展,或者我发布的示例根本不会扩展(例如,我们会得到 X(A(x)) - _A(x),但 _A(x) 显然会扩展)
  • 我认为你的根本问题是宏预处理器在处理过程中相当简单,它作为一种一次性解析和输出。你想要做的实际上是一个不同的运算符## 运营商可以。听起来您希望运算符告诉预处理器使用## 运算符生成一个符号,然后循环返回并重新评估该符号以查看是否需要对这个新符号执行任何其他操作。这就是预处理器的工作原理。
  • @MrMobster 如果AB 本身是宏,并且您没有使用## 来构建名称,那么您将无限期地递归。这是因为相应的扩展是在 参数替换 阶段完成的,这会在将所有宏参数放入替换列表之前完全扩展它们。作为。不受“蓝色油漆”的影响(又名,6.10.3.4p2,见托比的回答);这是不同的扫描。 重新扫描并进一步更换之后发生; 受 6.10.3.4 约束(因为这就是它正在做的事情),并且 支持同一宏的递归。

标签: c macros c-preprocessor


【解决方案1】:

C99 草案说宏扩展中不允许递归:

6.10.3.4 重新扫描和进一步替换

  1. 在替换列表中的所有参数都被替换后 ### 处理已发生,所有地标预处理标记均已删除。生成的预处理令牌序列 然后重新扫描,以及所有后续预处理 源文件的标记,用于替换更多宏名称。
  2. 如果 被替换的宏的名称在此扫描期间找到 替换列表(不包括源文件的其余部分 预处理令牌),它不会被替换。此外,如果有的话 嵌套替换遇到被替换的宏的名称,它 没有被替换。这些不可替换的宏名称预处理 令牌不再可用于进一步替换,即使它们 稍后在该宏名称所在的上下文中(重新)检查 否则,预处理令牌将被替换。

所以X(A(A(x))) 扩展为"A" _A(x),但正如您所见,扩展本身并没有扩展。

【讨论】:

  • 呸,这就是我所担心的……我不认为你可以用一种巧妙的方式来规避它? :)
  • 我不知道有什么方法可以达到您想要的效果 - 抱歉!
  • @MrMobster 绕过预处理器的最简单方法是使用其他预处理器。以 M4 处理器为例 mbreen.com/m4.html
【解决方案2】:

当我想进行宏扩展时,我通常使用这个图表,我使用标准的第 6.10.3 节构建。希望对你有帮助...

正如 Toby 已经提到的,嵌套的宏不会被递归扩展。

【讨论】:

  • 谢谢,这非常有用!
  • ...但是错了。遵循是-否-否决策路径,该块显示“如果嵌套替换遇到被替换的宏的名称,则不会替换它以避免递归”。那是指蓝色油漆;但在重新扫描和进一步更换期间应用蓝色油漆 (6.10.3.4);并且该块正在描述没有这种限制的参数替换(6.10.3.1)。
  • 糟糕。我得去看看!同时删除答案
  • @H Walters:我的立场是正确的,感谢您指出这一点:)
猜你喜欢
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
  • 2020-09-18
  • 2014-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多