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" ); }
它打印132
( A
宏的点是交换括号中跟随其参数的东西,之后的所有内容,直到另一个结束括号)
但如果我在另一个宏中使用它:
#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
使用。 然后ZZ
和ZZZ
再次脱掉它们。 问题是,如果你没有粘贴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")) );