【问题标题】:How Do we Interpret This Complex C++ Preprocessor Macro Replacement我们如何解释这个复杂的 C++ 预处理器宏替换
【发布时间】:2014-03-18 12:24:12
【问题描述】:

我正在研究关于 C++ 预处理器如何处理宏替换的 C++ 标准(我需要自己实现 C++ 预处理器的一个子集)。这是我为学习创建的示例:

  #define a x
  #define x(x,y) x(x+a, y+1)

  a(x(90, 80), a(1,2))

通过让VC++ 2010生成预处理器输出文件,发现上面的a(x(90, 80), a(1,2))变成了这样:

90(90+x, 80+1)(90(90+x, 80+1)+x, 1(1+x, 2+1)+1);

但是预处理器是如何得出这个输出的呢?规则太复杂,难以理解。有人可以解释预处理器为得出这样的结果所做的所有步骤吗?

【问题讨论】:

  • 对于初学者来说,宏不是递归应用的。
  • 但是VC++没有为递归宏生成警告/错误
  • 我没有暗示应该有。我只是说x(x+a, y+1) 不可能使用x(x,y) 定义进行扩展,这将x 可以扩展到两种可能性。
  • @JavaMan,你想得到什么样的警告?关于您对 FLM 参数使用与 FLM 本身相同的名称? (FLM = 类函数宏)
  • @JavaMan,坦率地说,规则相当复杂。它们在简单的情况下非常直观地起作用,但在像你这样的情况下,它们就是地狱。 C 语言标准需要 13 页来布局。

标签: c++ macros c-preprocessor


【解决方案1】:

旧答案,顺序不准确(见编辑):

让我们从你的表达开始:

a(x(90, 80), a(1, 2))

现在,由于我们有 #define a x,它被扩展为:

  x(x(90, 80), x(1, 2))
//  ^^^^^^^^^  ^^^^^^^
//   arg 'x'    arg 'y'

我们可以套用x(x,y)的定义,即#define x(x,y) x(x+a, y+1)

x(90, 80)(x(90, 80)+a, x(1, 2)+1)

还有另一个将扩展x(...) 的通道。您还可以注意到,前面表达式中 > 的 +a 已扩展为 +x

  90(90+a, 80+1)(90(90+a, 80+1)+x, 1(1+a, 2+1)+1)
//                             ^^
//                          expanded

最后:剩下的+a 扩展为+x

  90(90+x, 80+1)(90(90+x, 80+1)+x, 1(1+x, 2+1)+1)
//     ^^             ^^              ^^
//  expanded       expanded        expanded

希望没有错误。

请注意,您对x(x,y) 的定义非常模糊(对于人类而言):宏名称和参数共享相同的名称。请注意,即使没有它,宏也不是递归的,所以如果你有

#define x(u,v) x(u+a, b+1)

不会扩展到类似

x(u+a+a+a+a, b+1+1+1+1)

这是因为在定义宏 x 时,它的名称对内部宏定义不“可用”。

另一个小提示:对于 gcc,输出并不完全相同,因为 gcc 在被替换的标记之间添加了空格(但如果删除它们,它将与 msvc 相同)。

EDIT:从 dyp 的 cmets 来看,这个顺序并不准确。实际上,参数是先展开,再代入宏表达式。这句话的最后一部分很重要:这意味着宏参数列表没有被重新评估。可以把它想象成:用占位符代替参数扩展宏,然后扩展参数,然后用它们各自的参数替换占位符。所以,简而言之,这和我之前解释的一样,但是这里是正确的顺序(详细操作):

> Expansion of a(x(90, 80), a(1, 2))
    > Substitution of 'a' into 'x' (now: 'x(x(90, 80), a(1, 2))')
    > Expansion of x(x(90, 80), a(1, 2)) [re-scan]
        > Macro 'x(X, Y)' is expanded to 'X(X+a,Y+1)'
        > Expansion of 'x(90,80)' (first argument)
            > Macro 'x(X,Y)' is expanded to 'X(X+a,Y+1)'
            > Argument '90' does not need expansion (ie, expanded to same)
            > Argument '80' does not need expansion (ie, expanded to same)
            > Substitution with 'X=90' and 'Y=80': '90(90+a, 80+1)'
            > Re-scan of result (ignoring macro name 'x')
                > Substitution of 'a' into 'x': '90(90+x, 80+1)'
        > Expansion of 'a(1,2)' (second argument)
            > Substitution of 'a' into 'x'
            > Expansion of 'x(1,2)' [re-scan]
                > Macro 'x(X,Y)' is expanded to 'X(X+a,Y+1)'
                > Argument '1' does not need expansion (ie, expanded to same)
                > Argument '2' does not need expansion (ie, expanded to same)
                > Substitution with 'X=1' and 'Y=2': '1(1+a, 2+1)'
                > Re-scan of result (ignoring macro name 'x')
                    > Substitution of 'a' into 'x': '1(1+x, 2+1)'
        > Substitution with X='90(90+x, 80+1)' and Y='1(1+x, 2+1)'
          Result: '90(90+x, 80+1)(90(90+x, 80+1)+a, 1(1+x, 2+1)+1)'
        > Re-scan of result
            > Substitution of 'a' into 'x'
              Result: '90(90+x, 80+1)(90(90+x, 80+1)+x, 1(1+x, 2+1)+1)'
Last result is result of whole expansion:
    90(90+x, 80+1)(90(90+x, 80+1)+x, 1(1+x, 2+1)+1)

【讨论】:

  • 好的,我用“通过”这个词很不正式,我会在我的 Wifi 网络稳定时重新表述。
  • @dyp 我同意非递归解释,这大致就是我在回答中的内容。但是,我认为通常宏在参数之前扩展(这更有意义)。我有一个small test here,我只希望没有错误;)。 (更准确地说,我认为一些预处理器首先扩展宏,其他一些首先扩展参数,例如 msvc )。
  • @dyp 这是另一个例子:coliru.stacked-crooked.com/a/bc2ca5a04f21c8f9
  • 好的,在制作了another example 并阅读了你的之后,我想我们在谈论同样的事情;)。我认为标准方法是“宏观优先”(也许有一些小的极端情况,但这种近似更容易理解)。
  • 是的,我认为“标准扩展”的描述很贴切:)(您的示例中没有出现的是重新扫描中原始宏的未扩展。)
猜你喜欢
  • 2010-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多