C预处理器使用父宏的右括号

我有这个代码有效:

#include  #define A(x) x B #define B(x) C(x, #define C(x,y) yx) int main( void ) { printf( A("1") ("2") "3" ); } 

它打印132A宏的点是交换括号中跟随其参数的东西,之后的所有内容,直到另一个结束括号)

但如果我在另一个宏中使用它:

 #define Z(x) x printf( Z( A("1") ("2") "3" ) ); 

我得到编译错误“未终止的函数式宏调用”。

我意识到这是因为编译器试图独立处理Z的参数,但我需要使用它的右括号作为标记。 有没有办法让我在宏中工作? 更改调用语法实际上不是一个选项。


ps在我得到任何回复谈论这是多么糟糕的事情之前,请放心:这不是真正的代码。 制作玩具程序时出现了一个问题,该程序使用define来模拟C语言中的新语言。

查看正在发生的事情的最简单方法是稍微更改测试用例。

 #define A(x) x B #define B(x) C(x, #define C(x,y) yx] /* note close square bracket instead of close paren */ Y(A(1)(2)3) 

预处理到Y(1 3 2] 。这是因为扩展的中间阶段看起来像

 Y(1 C(2,3) 

在这一点上C吃了原始文本中似乎属于Y的紧密的paren 并用一个小括号替换它。

现在,如果A(1)(2)3在宏参数内部会发生什么?

 #define Z(x) x Z(A(1)(2)3) 

由于论证预扫描 ,类似的扩展中间阶段不是

 Z(1 C(2,3) 

反而

 1 C(2,3 

Z隐藏在一个隐藏的“待定扩展”堆栈上。 实际上,预处理器强制执行最终关闭paren属于Z的文本外观,并且不允许C借用它。

我能想到的实现你最初目标的最小侵入方式是

 #define _A(x) x B #define B(x) C(x, #define C(x,y) yx) #define Z(x) ZZ((_##x)) #define ZZ(x) ZZZ x #define ZZZ(x) [x] Z(A(1)(2)3) 

预处理到[1 3 2] 。 我们使用令牌粘贴运算符来防止Z的参数被预扫描,因此我们可以添加一组临时的额外括号以供C使用。 然后ZZZZZ再次脱掉它们。 问题是,如果你没有粘贴x ,那么这是一个错误,所以我们必须在A的定义中添加一个前导下划线,如果Z的参数的第一个标记永远不是某个东西,那将是一个错误可以在下划线后进行令牌粘贴。

您可能想要考虑使用M4而不是试图将其插入到C预处理器中。

eclipse cdt非常适合调试你的问题。 对于eclipse,只需将鼠标hover在宏上即可开始使用。 这里有详细信息:

使用Eclipse进行C / C ++软件开发>> 2.1.7。 宏观扩张

对于你的第二个宏,eclipse显示以下内容:

 int main (void) { printf( Z( A("1") ("2") "3" ) ); } 

在此处输入图像描述在此处输入图像描述在此处输入图像描述在此处输入图像描述在此处输入图像描述

发现错误

请注意扩展#3 C(“2”,“3”只是’消失。我把它当作CDT说’未终止的参数列表’的方式。无论它消失的情况如何,这是我在调试时更喜欢的方法宏。

  • 使用此工具可以清楚地表明,在扩展#2(第三张图像)中,我们有一组未确定的括号,从而找到错误。

了解解决方案

在使用这个工具摆弄了一下之后,我认为这就是你所追求的:

 printf( Z( (A("1") ("2") "3") ) ); yields (using gcc -E main.c -c) printf( ("1" "3" "2") ); 

在此处输入图像描述

这与宏的处理顺序有关。 最简单的解决方案是在Z参数周围添加额外的括号。

  printf( Z( (A("1")("2") "3")) );