【问题标题】:Conditional Preprocessor macro expansion in CC中的条件预处理器宏扩展
【发布时间】:2020-09-18 17:58:48
【问题描述】:

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

SOME_MACRO的定义:

#define SOME_MACRO(arg) __SOME_MACRO(arg)

如果arg0,我还希望__SOME_MACRO(arg) 扩展到"ABC"。如果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

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

举个例子:

SOME_MACRO(2) 应扩展为 "DEF"

SOME_MACRO(0) 应扩展为 "ABC"

【问题讨论】:

  • SOME_MACRO(1+3-4)你想要什么?
  • 通常,C 标准不提供宏的条件扩展功能。在某些情况下,有些杂乱无章,但应尽可能避免。通常,当有人试图做这种事情时,会有更好的方法来实现他们的实际目标。你想用这个来完成什么——你为什么需要它?
  • @EricPostpischil。 1+3-4 不是很关键。我相信这种情况不会发生(在我的代码中)。所以我基本上不关心1+3-4。严格来说,我希望它扩展到"ABC",但我仍然不太关心这种情况。
  • #define SOME_MACRO(arg) ((arg) ? "DEF" : "ABC")
  • @NeedSomeLuck __SOME_MACRO 请不要使用带有两个前导下划线的标识符。此类标识符由 C 标准保留。

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


【解决方案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)

【讨论】:

    【解决方案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",在您弄清楚问题所在之前可能会引起很多混乱。__SOME_MACRO(1-1) 更糟...)

      另外,#define ZERO 0 后跟 __SOME_MACRO(ZERO) 不能正常工作,认为可以通过这样做来解决

      #define SECOND_MACRO(arg) __SOME_MACRO_ # arg
      #define __SOME_MACRO(arg) SECOND_MACRO(arg)
      

    【讨论】:

    • 好的,我已经编辑了我的问题。您能否提供其他解决问题的方法。谢谢
    • "和 __SOME_MACRO(1+1) 根本无法工作" ...why not?
    • @HWalters:好的,但是现在__SOME_MACRO(1-1) 不起作用,__SOME_MACRO(0+1) 也不起作用。
    猜你喜欢
    • 2018-01-04
    • 2020-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 2016-03-30
    相关资源
    最近更新 更多