【问题标题】:Function-style macro with no-op if a condition is false如果条件为假,则无操作的函数式宏
【发布时间】:2019-08-08 02:06:06
【问题描述】:

假设我在代码中的多个位置有以下构造,并希望使我的代码更清晰:

#if HAVE_LIBFOOBAR
    foobar_func(data);
#endif

我正在考虑围绕它编写一个函数样式的宏,它可以处理条件,使代码中的出现看起来像一个常规的函数调用:

    foobar_func_if_available(data)

如果条件为真,这将被替换为对实际函数的调用,否则它将是空操作。

因此,类似于:

#if HAVE_LIBFOOBAR
#define foobar_func_if_available(x) foobar_func(x)
#else
#define foobar_func_if_available(x) {}
#endif

问题:

  • {} 是否作为空操作工作?它不会产生意外的影响(例如在不带括号的if 语句中使用)吗?如果没有,我会用什么?
  • 我是否必须将两个独立的 #defines 包裹在条件句中,或者是否有办法反过来(一个 #define 带有函数样式宏中的条件句)?

编辑:有人建议这是 another question 的副本,但我认为不是:另一个问题是“这个构造解决了什么问题”,我的是“什么构造将解决我的问题”。确实,另一个问题有可能解决我的问题,但它并没有涵盖我问题的所有方面。

【问题讨论】:

标签: c c-preprocessor


【解决方案1】:

((int)0)((void)0) 这样的宏可能是最灵活/最安全的无操作宏。它们很灵活,因为您可以使用它们 在表达式中(不像do{}while(0)),它们不会像{}; 那样破坏if-else

{}(或;)宏如何破坏if-else的示例:

#define foo() {}
if(1) foo(); else bar(); //syntax error because if(1) {}; else bar(); was pasted

如果宏应该模拟整数返回函数,最好在普通整数常量上使用强制转换的整数文字,因为整数常量(尤其是零)可用于更多上下文(案例标签、位域大小、数组大小、null指针常量)而不是非常量整数表达式。

你不需要有两个宏:

#if HAVE_LIBFOOBAR
    #define foobar_func_if_available(x) foobar_func(x)
#else
    #define foobar_func_if_available(x) ((void)0) /*if foobar_func returns void*/
#endif

你可以把条件放在宏里面:

#define foobar_func_if_available(x) \
    (HAVE_LIBFOOBAR?foobar_func(x):((void)0))

即使是一个非常愚蠢的编译器也应该能够优化常量条件输出。

但是,如果您依赖一个空的HAVE_LIBFOOBAR#if 中评估为0,那么上面的方法就行不通了——HAVE_LIBFOOBAR 需要是一个整数。

( 你可以这样做

#if !HAVE_LIBFOOBAR
    #undef HAVE_LIBFOOBAR
    #define HAVE_LIBFOOBAR 0
#endif
#define foobar_func_if_available(x) \
        (HAVE_LIBFOOBAR?foobar_func(x):((void)0))

将空的HAVE_LIBFOOBAR 规范化为0,但除非您将重用现在确定的HAVE_LIBFOOBAR 的定义,否则这似乎比原来的两个foobar_func_if_available 宏更复杂。 )

【讨论】:

    【解决方案2】:

    你不能用一个#define来实现它。

    您不需要{} 作为空操作,您可以通过多种方式定义一个空表达式:

    #define foobar_func_if_available(x)
    #define foobar_func_if_available(x) ;
    #define foobar_func_if_available(x) do{}while(0)
    

    在某些情况下,其中任何一个都可能导致语法问题,但对于 void 函数,两者都不太可能导致问题 - 但是,对于非 void 函数,解决方案会失效。

    完全避免类似函数的宏的更好解决方案是有条件地定义函数体:

    void foobar_func( int n )
    {
        #if defined HAVE_LIBFOOBAR
           // do something
        #else
           // do nothing
        #endif
    }
    

    空函数是否导致无代码取决于编译器和应用的优化级别,但重要的是,代码将在所有对foobar_func() 的调用有效的情况下在语法上工作。担心它是否是无操作可能会让小事大汗淋漓。

    【讨论】:

    • 如果有人忘记分号,这将导致问题:foobar_func_if_available(x) something_else(); -- 没有 LIBFOOBAR 的人会看到这个编译成功,但有它的人会看到编译器错误。传统的解决方案是do{}while(0),后面没有分号。
    • @RaymondChen :对于类似 multi-statement.function 的宏,您的建议是有效的,但对于使用 do-while 的单个函数调用别名,您会阻止在表达式中使用宏。但是我已经严格回答了关于 NO-OP 宏的问题,其他定义没有问题,也不是我的代码。
    • 将其留空也会阻止其在表达式中的使用。
    • @wildplasser :我的替代解决方案应该作为答案而不是 cmets 发布。我知道这个解决方案的不足之处,欢迎提出更好的建议。
    • 我认为空分号不好——如果有人写foobar_func_if_available(x);,它会创建两个空语句。这搞砸了没有支撑的 if-else。
    【解决方案3】:

    创建一个虚拟函数并将#define 指向它(有条件地):


    #if HAVE_LIBFOOBAR
      #define foobar_func_if_available(x) foobar_func(x)
    #else
      int dummy(int ignored)
      {
      return 0;
      }
      #define foobar_func_if_available(x) dummy(x)
    #endif
    

    或者只是:

    #define foobar_func_if_available(x) 0
    

    【讨论】:

    • 在这种情况下,您可以只执行int foobar_func(int u) { return 0; } 并跳过宏。
    • 事实上我的问题被简化了:即使HAVE_LIBFOOBAR 为假,该功能也可能可用,我只是不想让我的代码使用它。虽然跳过宏通常会起作用,但在我的情况下它不会。
    • ((int)0) 会更安全一些。 0((int)0),但它也是空指针常量和整数常量,不经意间大大扩展了它的用途,超过了 ((int)0)
    • 这个想法是模拟原始函数的签名(未显示)因此,对于sin(x),您可以生成或返回 0.0。
    • @wildplasser 我明白了。我只是指出,如果你想模拟返回int 的函数,最好使用((int)0) 而不是0,因为0 可以在非const int 表达式无法使用的许多上下文中使用'不被使用。
    猜你喜欢
    • 1970-01-01
    • 2012-03-11
    • 1970-01-01
    • 1970-01-01
    • 2020-03-06
    • 2016-10-16
    • 1970-01-01
    • 1970-01-01
    • 2015-08-20
    相关资源
    最近更新 更多