【问题标题】:C preprocessor using the closing bracket of a parent macro使用父宏右括号的 C 预处理器
【发布时间】:2013-03-29 13:18:27
【问题描述】:

我有这个有效的代码:

#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 的参数,但我需要使用它的右括号作为标记。有没有办法可以在宏中完成这项工作?更改调用语法并不是一个真正的选择。


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

【问题讨论】:

    标签: c macros c99 c-preprocessor


    【解决方案1】:

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

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

    【讨论】:

    • 但是在使用时不需要需要不同的语法吗? (可能通过将Z 制作成更复杂的宏)。
    【解决方案2】:

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

    #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)
    

    因为argument prescan,类似的扩展中间阶段不是

    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 使用。 ZZZZZ 然后再次剥离它们。需要注意的是,如果您不将xsomething 一起粘贴,则会出现错误,因此我们必须在A 的定义中添加前导下划线,如果Z 参数的第一个标记永远不能在下划线后粘贴标记。

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

    【讨论】:

    • 感谢您的深入解释。不幸的是,该解决方案不适用于我的确切问题(我仍然需要 Z 中的其他宏来扩展)。至于 M4,如果这是一个真正的项目,我会同意,但挑战是使用 C 的奇异预处理器来做到这一点!作为一个相关问题,是否可以使用() 括号而不是[]?即有没有办法转义括号字符?
    • 我不明白你的相关问题。我只是用方括号来说明;就预处理器而言,它们没有任何特殊意义。
    • 我的意思是这条线:#define C(x,y) y x] /* note close square bracket instead of close paren */ 作为),它会中断。
    • 不,等等,我误会了。忽略它。
    【解决方案3】:

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

    C/C++ Software Development with Eclipse >> 2.1.7. Macro Expansion

    对于您的第二个宏,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") );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-09
      • 1970-01-01
      • 2019-05-13
      • 2015-03-29
      • 2011-01-26
      • 1970-01-01
      相关资源
      最近更新 更多