6

我有这个有效的代码:

#include <stdio.h>
#define A(x) x B
#define B(x) C(x,
#define C(x,y) y x)
int main( void ) {
    printf( A("1") ("2") "3" );
}

它打印132A宏的重点是将括号中参数后面的内容与之后的所有内容交换,直到另一个右括号)

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

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

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

我意识到发生这种情况是因为编译器试图Z独立处理 的参数,但我需要使用它的右括号作为标记。有没有办法可以在宏中完成这项工作?更改调用语法并不是一个真正的选择。


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

4

3 回答 3

3

查看发生了什么的最简单方法是稍微更改测试用例。

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

Y(A(1)(2)3)

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

Y(1 C(2,3)

此时C吃了似乎属于Y原始文本的右括号并将其替换为右括号。

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

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

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

Z(1 C(2,3)

反而

1 C(2,3

Z隐藏在一个隐藏的“未决扩展”堆栈上。预处理器实际上是在强制执行最后关闭括号所属的文本外观Z,并且C不允许借用它。

我能想到的实现您最初目标的侵入性最小的方法是

#define _A(x) x B
#define B(x) C(x,
#define C(x,y) y x)

#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,这是一个错误,所以我们必须在 的定义中添加一个前导下划线,如果' 参数的第一个标记永远不是可以是的东西,那将是一个错误标记粘贴在下划线之后。AZ

您可能要考虑使用M4而不是尝试将其硬塞到 C 预处理器中。

于 2013-04-08T01:27:14.890 回答
2

eclipse cdt 非常适合调试您的问题。对于 Eclipse,只需将鼠标悬停在宏上即可开始。这是关于它的详细信息:

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

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

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

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

发现错误

注意扩展#3 C("2", "3" 只是“消失”。我认为这是 CDT 说“未终止的参数列表”的方式。不管它消失的情况如何,这是我在调试时更喜欢采用的方法宏。

  • 使用这个工具可以清楚地看到,在 Expansion#2(第三张图片)中,我们有一组未终止的括号,从而定位了错误。

了解解决方案

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

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

yields (using gcc -E main.c -c)

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

在此处输入图像描述

于 2014-03-04T05:11:31.553 回答
0

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

    printf( Z( (A("1")("2") "3")) );
于 2013-04-07T14:26:12.207 回答