【问题标题】:Prepend argument to a list and apply another macro in C preprocessor macro将参数添加到列表并在 C 预处理器宏中应用另一个宏
【发布时间】:2014-04-30 10:11:32
【问题描述】:

是否可以定义一个预处理器宏,它将以下列方式扩展。

MACRO1(x) (y,z,w)
--> MACRO2(x,y,z,w)

MACRO1 的扩展能否以某种方式消耗宏调用之后的列表的左括号并将其替换为MACRO2(x,,以便预处理器将结果作为有效的宏调用接受(假设MACRO2 已定义)而不是引发unterminated argument list 错误?

我尝试过这样做

#define STRANGE_MACRO(...) __VA_ARGS__
#define STRIP_PAREN(...) __VA_ARGS__)
#define PREPEND_AND_APPLY_STRANGE(x) STRANGE_MACRO(x,STRIP_PAREN

这样称呼它:

PREPEND_AND_APPLY_STRANGE(x) (y,z,w)

产生unerminated argument list 错误。有没有办法让它工作?

至于我想要这种行为的原因,它是为了 esetics,我认为这样的宏调用看起来更好

MACRO1(identifier) (
    more
    complex
    arguments
)

MACRO2(identifier,
    more
    complex
    arguments
)

我只想将前者转变为后者。如果在预处理器规则内不可能,没什么大不了的,我会接受它,但如果是的话,我想知道诀窍。

【问题讨论】:

  • 你对“美学”的看法 [原文如此] 与我的完全不同。 :) 如果是宏调用,它应该看起来像一个,而不是语法错误。
  • 您可能正在寻找something like that

标签: c++ c c-preprocessor


【解决方案1】:

如果你想在替换列表中加入一个不匹配的左括号,你需要添加一个间接层:

#define LPAREN (

#define PREPEND_AND_APPLY_STRANGE(x) STRANGE_MACRO LPAREN x,STRIP_PAREN

这样,在正确的语法完成之前,括号不会被视为调用的一部分。

不幸的是,这种技术有一个限制:当对 STRANGE_MACRO 的调用完全构造时,它已经过了可以重新扫描的点 - 因为表达式的开始和结束是通过两个不同的调用构建的发生在同一级别,整个事情从未出现在一个重新扫描列表中 - 并且永远不会真正扩展;你只会在你的输出中得到STRANGE_MACRO ( x,y,z,w)。您需要在顶层强制重新扫描:

#define EXPAND(...) __VA_ARGS__

宏什么都不做,但确实意味着它的参数将作为一个完整的单元放入重新扫描列表中,因此最终会扩展。所以最接近你想要的语法是这样的:

EXPAND(

MACRO1(identifier) (
    ....
)
MACRO1(identifier) (
    ....
)

)

...因此您不需要使用自己的EXPAND 来破坏每个自定义块,但您确实需要将整个程序包装在一个中。不,您不能通过在EXPAND 中放置#include 指令来隐藏包装器,因为指令不能出现在调用中。 (不过,也许您可​​以通过重新发明 namespace 或您的语法可能需要的类似东西来利用它。)

这也有一个缺点,即 C 编译器会将您的 整个程序 视为在一行上,这会在一定程度上损害错误报告 - 尽管您已经解决了这个问题的一半,因为每个声明块如果原始版本有效,似乎也只会出现在一行上。

【讨论】:

  • 该死的,那个可怜的预处理器:)
  • @Leushenko 这证实了我的怀疑。我担心如果不将其包装在EXPAND() 中,则无法单独调用由同一级别上的两个不同调用的结果组成的宏调用。
  • 我在 C++ 标准中发现了一些有趣的东西。 16.3.4.1 说“重新扫描生成的预处理令牌序列,以及源文件的所有后续预处理令牌”。因此,如果我理解正确,编译器不符合标准,因为它们正在重新扫描与文件其余部分隔离的宏输出。
  • "Subsequent" 预处理标记:在扩展 PREPEND_AND_... 之后,它将扫描位置重置为替换的开始位置,即 STRANGE_MACRO。它没有找到STRANGE_MACRO 的有效参数列表,因此它移动到下一个令牌(LPAREN)。此时扫描位置已移过 STRANGE_MACRO 并且不会再查看它,无论它后面是什么形式(至少在我们用EXPAND 强制它之前)。这完全符合标准。
  • 哦,是的,我看到了问题所在。如果我们有一个#define MACRO1(x) MACRO2(x,STRIP_PAREN,那么MACRO1(x)(y,z,w) 将扩展为MACRO2(x,STRIP_PAREN(y,z,w)。现在这将失败,因为在扩展参数之前,预处理器必须匹配任何宏调用的打开和关闭括号。所以这不能在标准规则内完成。
猜你喜欢
  • 2022-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多