C中的条件预处理器宏扩展

时间:2020-05-31 11:00:29

标签: c linux gcc linux-kernel c-preprocessor

我有一个宏SOME_MACRO。需要一个参数。

SOME_MACRO的定义:

#define SOME_MACRO(arg) __SOME_MACRO(arg)

如果__SOME_MACRO(arg)"ABC",我希望arg扩展到0。如果arg不为零,则__SOME_MACRO(arg)应扩展为"DEF"

假设我使用参数0进行调用,即:SOME_MACRO(0) 我需要在预处理时测试参数是否为零。

#if <argument is zero>     // line 1
#define __SOME_MACRO(arg) "ABC"
#elif
#define __SOME_MACRO(arg) "DEF"
#endif

我的问题是:第一行应该是什么?

举个例子:

SOME_MACRO(2)应该扩展为"DEF"

SOME_MACRO(0)应该扩展为"ABC"

2 个答案:

答案 0 :(得分:2)

这是不可能的。给#if的表达式在遇到#if的那一行仅计算一次。每次调用宏时都无法再次对其进行评估。

正如Eric Postpischil所说,C没有任何通用的方法来使宏扩展取决于其参数。

一些可能的选择:

  • 使用三元?:运算符的表达式,如注释中建议的pmg所示:

    #define __SOME_MACRO(arg) ((arg) ? "DEF" : "ABC")
    

    然后SOME_MACRO(0)将扩展为((0) ? "DEF" : "ABC")。任何合理的编译器都会注意到该条件是一个常量,并像仅编写"ABC"一样对其进行编译;它不应该为测试或不可达分支生成代码。只要条件本身是编译时常量,甚至可以在必须是编译时常量的表达式中使用带有三元运算符的条件。例如__SOME_MACRO(0)可用于初始化全局变量或静态变量。

    当然,如果所需的扩展不是语法上的表达式,例如如果您尝试使用宏来创建声明或定义,或者在语法上进行更详细的说明。

  • 如果您知道将只使用一小部分可能的参数,则可以使用token pasting with the ## operator将宏扩展为另一组宏:

    #define __SOME_MACRO_0 "ABC"
    #define __SOME_MACRO_1 "DEF"
    #define __SOME_MACRO_2 "DEF"
    #define __SOME_MACRO(arg) __SOME_MACRO_ ## arg
    

    然后,__SOME_MACRO(0)扩展为"ABC"__SOME_MACRO(2)扩展为"DEF"。但是,除非您编写相应的__SOME_MACRO(3)宏,否则__SOME_MACRO_3将不会编译,并且__SOME_MACRO(0+1)根本无法工作,至少不会以我所知的任何方式工作。

    (按照书面规定,__SOME_MACRO(0+1)会扩展为"ABC"+1,与"BC"等效,在找出问题所在之前,可能会引起很多困惑。{{1} }甚至更糟...)

    此外,__SOME_MACRO(1-1)后跟#define ZERO 0仍然无法正常工作,以为可以通过执行此操作来解决

    __SOME_MACRO(ZERO)

答案 1 :(得分:1)

这是请求的行为的解决方案。不要使用它,这是一种不好的做法,您不应该尝试这样做。

/*  Define __SOME_MACRO:

        Defer is used so that its arguments will be macro-replaced before
        Kludge is processed.

        "Kludge" is pasted to arg.  If this makes "Kludge0", it will be
        replaced by two arguments (an empty argument, a comma, and the argument
        "abc").  Otherwise, if it forms a single token, that is one argument.
        (If the pasting does not form a proper token, the behavior is
        undefined.  Most likely, the compiler will report an error.)

        The resulting argument(s) are then passed to Kludge, followed by "DEF".

        Kludge is replaced by its second argument.  If __SOME_MACRO's argument
        was "0", this second argument is "ABC".  Otherwise, it is "DEF".
*/
#define Kludge0             , "ABC"
#define Kludge(x, y,...)    y
#define Defer(x, ...)       Kludge(x, __VA_ARGS__)
#define __SOME_MACRO(arg)   Defer(Kludge##arg, "DEF",)

__SOME_MACRO(0)
__SOME_MACRO(2)