【问题标题】:Is there a way to both check a macro is defined and it equals a certain value at the same time有没有办法检查一个宏是否已定义并且它同时等于某个值
【发布时间】:2013-06-14 04:30:58
【问题描述】:

我经常在 C 代码中使用类似对象的预处理器宏作为布尔标志来打开和关闭代码部分。

例如

#define DEBUG_PRINT 1

然后像这样使用它

#if(DEBUG_PRINT == 1)
    printf("%s", "Testing");
#endif

但是,如果忘记将包含#define 的头文件包含在源代码中,则会出现问题。由于未声明宏,预处理器将其视为等于 0,并且#if 语句永远不会运行。

当忘记包含头文件时,可能会出现意想不到的、不守规矩的行为。

理想情况下,我希望能够在一行中检查是否定义了宏,并检查它是否等于某个值。如果未定义,预处理器会抛出错误(或警告)。

我正在寻找类似的东西:

#if-def-and-true-else-throw-error(DEBUG_PRINT)
    ...
#endif

就像#ifdef#if 的组合,如果不存在,则使用#error

我已经探索了一些途径,但是,预处理器指令不能在 #define 块内使用,据我所知,如果未定义宏,则没有预处理器选项可以引发错误/警告在#if 语句中使用时。

【问题讨论】:

  • 编译器无法将你从一切中拯救出来......
  • 对。那么您想要的是将 print 函数包装在宏或其他函数中,而不是将其包装在所有调用位置。然后,您可以将错误生成代码整合到一个地方,而不是试图将其散布在您的项目中。
  • 您可能很容易忘记使用#if-def-and-true-else-throw-error(DEBUG_PRINT) 而不是#if DEBUG_PRINT == 1,因此即使存在此功能也不会产生任何有用的效果。
  • 如果你在头文件中定义了 PrintDebug——这是它的逻辑位置——那么如果不包含头文件,代码将无法编译。
  • gcc 有一个选项 (-Wundef) 可以在 #if 指令中评估未定义的标识符时生成警告。

标签: c macros c-preprocessor directive


【解决方案1】:

这可能不适用于一般情况(我认为您的要求没有通用解决方案),但对于您的具体示例,您可能会考虑更改以下代码序列:

#if(DEBUG_PRINT == 1)
    printf("%s", "Testing");
#endif

到:

if (DEBUG_PRINT == 1) {
    printf("%s", "Testing");
}

如果没有定义 DEBUG_PRINT 或者如果它被定义为无法与 1 比较的东西,它将不再冗长并且将无法编译。

【讨论】:

  • 啊啊啊……好主意。我首先想到,不,这不好,引入的额外执行时间怎么样(我来自嵌入式背景)!但话又说回来,这是编译器可以做的最简单的优化之一,不是吗?
  • @gbmhunter,是的,在这种情况下,if 语句本身不包括在 DEBUG 定义为 01 时,如果未定义未声明的标识符,则会抛出未声明的标识符。不能以那种方式影响性能。通过 linux/gcc-4.7.2 验证。
  • " @VoidPointer " 不能那样影响性能。" ——当然可以。对于任何给定的实现和一组选项,它是否会是另一回事。 “已通过 linux/gcc-4.7.2 验证。” -- 你不能用轶事来验证一般的说法。
  • 如果您的编译器没有消除 if 测试以在编译时对两个整数文字进行简单比较(至少对于非调试版本),那么您可能需要寻找另一个工具链。只要启用了某种程度的优化,几乎所有编译器都会在生成的运行时代码中忽略测试(以及测试结果为假时的语句)。
【解决方案2】:

据我所知,如果在 #if 语句中使用宏时未定义宏,则没有预处理器选项会引发错误/警告。

这不可能是错误,因为 C 标准规定行为是合法的。来自 ISO C99 标准的第 6.10.1/3 节:

由于宏扩展和defined 一元的所有替换 运算符已执行,所有剩余的标识符都替换为 pp 号 0....

正如 Jim Balter 在下面的评论中指出的那样,一些编译器(例如 gcc)可能会发出警告。但是,由于用 0 替换无法识别的预处理器令牌的行为是合法的(并且在许多情况下是可取的),我预计在实践中启用此类警告会产生大量噪音。

没有办法完全按照您的意愿行事。如果您想在未定义宏的情况下生成编译失败,则必须显式执行此操作

#if !defined DEBUG_PRINT
#error DEBUG_PRINT is not defined.
#endif

对于每个关心的源文件。或者,您可以将宏转换为类似函数的宏并避免使用#if。例如,您可以定义一个 DEBUG_PRINT 宏,该宏扩展为用于调试构建的 printf 调用,但对于非调试构建扩展为无。任何忽略包含定义宏的标头的文件都将无法编译。


编辑:

关于可取性,我已经多次看到代码使用的地方:

#if ENABLE_SOME_CODE
...
#endif

代替:

#ifdef ENABLE_SOME_CODE
...
#endif

这样#define ENABLE_SOME_CODE 0 会禁用代码而不是启用它。

【讨论】:

  • “这是正确的,因为 C 标准指定了该行为。” - 并不真地。该标准不允许实现发布有关符合程序的诊断,而 gcc 的 -Wundef 将在这种情况下做到这一点。
  • devnull 在上面的 cmets 中提到了 -Wundef;我只是在这里重复了一遍。 “在许多情况下是可取的”——我认为这是相当糟糕的编程实践。 “我希望在实践中启用此类警告会产生大量噪音。” -- 试试看......我认为你更有可能发现错误。
【解决方案3】:

不要直接在源文件中使用 DEBUG_PRINT ,而是将其放在头文件中:

#if !defined(DEBUG_PRINT)
    #error DEBUG_PRINT is not defined
#endif

#if DEBUG_PRINT
    #define PrintDebug([args]) [definition]
#else
    #define PrintDebug
#endif

任何使用 PrintDebug 但不包含头文件的源文件都将无法编译。

如果您需要基于 DEBUG_PRINT 编译调用 PrintDebug 以外的其他代码,请考虑使用 Michael Burr 的建议,即使用普通的 if 而不是 #if(是的,优化器不会在错误的常量测试中生成代码) .

编辑: 只要您没有看起来像宏参数的逗号,您就可以将上面的 PrintDebug 概括为包含或排除任意代码:

#if !defined(IF_DEBUG)
    #error IF_DEBUG is not defined
#endif

#if IF_DEBUG
    #define IfDebug(code) code
#else
    #define IfDebug(code)
#endif

然后你可以写类似的东西

IfDebug(int count1;)  // IfDebug(int count1, count2;) won't work
IfDebug(int count2;)
...
IfDebug(count1++; count2++;)

【讨论】:

  • 好思路,针对调试打印的特殊解决方案。但是,我使用宏开关概念不仅仅用于调试打印。它不适用于包含可变数量的不同代码。
  • @gbmhunter 您已经有了解决方案,我在您添加评论时将其添加为编辑。
【解决方案4】:

是的,您可以同时检查:

#if defined DEBUG  &&  DEBUG == 1
#  define D(...) printf(__VA_ARGS__)
#else
#  define D(...)
#endif

在这个例子中,即使#define DEBUG 0 但它不等于 1,因此不会打印任何内容。

你甚至可以这样做:

#if defined DEBUG  &&  DEBUG
#  define D(...) printf(__VA_ARGS__)
#else
#  define D(...)
#endif

这里如果你#define DEBUG 0 然后D(1,2,3) 也不会打印任何内容

DOC

【讨论】:

    【解决方案5】:

    只需创建一个执行实际打印的宏 DEBUG_PRINT:

    #define DEBUG_PRINT(n, str)    \
                                   \
      if(n == 1)                   \
      {                            \
        printf("%s", str);         \
      }                            \
      else if(n == 2)              \
      {                            \
        do_something_else();       \
      }                            \
                                   \
    #endif
    
    
    #include <stdio.h>
    
    int main()
    {
      DEBUG_PRINT(1, "testing");
    }
    

    如果未定义宏,则会出现编译器错误,因为符号无法识别。

    【讨论】:

      【解决方案6】:
      #if 0 // 0/1
      #define DEBUG_PRINT printf("%s", "Testing")
      #else
      #define DEBUG_PRINT printf("%s")
      #endif
      

      所以当“if 0”时它什么也不做,而当“if 1”时它会执行定义的宏。

      【讨论】:

        猜你喜欢
        • 2016-05-08
        • 1970-01-01
        • 2021-08-10
        • 2021-12-19
        • 2011-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多