比较预处理器宏是否相等

标签 c macros c-preprocessor

我有一些来自一些 .dbc 文件的粗略生成的 header 。 由于一些消息表示数组中的元素,因此结构是相等的,因此生成的宏是相等的。因为我在代码中填充了一些结构数组,所以我想省力并为所有对象使用相同的宏,但为了确保定义没有改变,我想在编译时测试宏是否相等。

例子:

#define GET_PATTERN_01_PATTERNPOINT02Y(buf) (0 \
    | (uint16)(-(uint16)((buf[7] >> 6) & 0x01) << 15) \
    | (uint8)(+(uint8)((buf[6] >> 0) & 0xff) << 0) \
    | (uint16)(+(uint16)((buf[7] >> 0) & 0x7f) << 8) \
)

#define GET_PATTERN_02_PATTERNPOINT04Y(buf) (0 \
    | (uint16)(-(uint16)((buf[7] >> 6) & 0x01) << 15) \
    | (uint8)(+(uint8)((buf[6] >> 0) & 0xff) << 0) \
    | (uint16)(+(uint16)((buf[7] >> 0) & 0x7f) << 8) \
)

#if GET_PATTERN_01_PATTERNPOINT02Y != GET_PATTERN_02_PATTERNPOINT04Y
#  error blah
#endif

这可能吗? 如果 C++ 中有一些解决方案也可能有帮助。但是宏是固定的。

最佳答案

这是一个可怕的 hack,但似乎至少适用于 GCC 和 C11 的示例:

#include <assert.h>
#include <string.h>

...

#define STRINGIFY(x) STRINGIFY_(x)
#define STRINGIFY_(x) #x

#define ASSERT_SAME(m1, m2)                                          \
  static_assert(strcmp(STRINGIFY(m1(xxx)), STRINGIFY(m2(xxx))) == 0, \
                #m1"() and "#m2"() differ!")

ASSERT_SAME(GET_PATTERN_01_PATTERNPOINT02Y, GET_PATTERN_02_PATTERNPOINT04Y);

您可能需要传递 -std=c11-std=gnu11 ,尽管这里不需要后者。

解释:

  • STRINGIFY(x)返回 x 的扩展作为字符串文字。我们需要使用 STRINGIFY_() 分两步进行字符串化因为#抑制宏扩展。 (一步我们会得到 "<x>" 而不是 "expanded version of <x>" 。)

  • GCC 有一个内置版本 strcmp() ( __builtin_strcmp() ) 在这里使用。它恰好能够在编译时比较常量字符串。如果您通过 -fno-builtin,代码将中断(除非您明确使用 __builtin_strcmp() )。

  • static_assert是 C11 编译时断言。

有了上面的三个要素,我们可以将扩展的宏字符串化(传递一些可能对参数唯一的虚拟标记)并在编译时比较字符串。

是的,这是一个 hack...

在 C++11 中,有更安全的方法可以在编译时比较字符串——参见例如this answer .

附带说明一下,您也可以在运行时执行此操作,而 GCC 和 Clang 的开销为零。 (上面的版本不适用于 Clang,因为它更挑剔 strcmp(...) == 0 不是 static_assert 要求的整数常量表达式。)运行时检查,如

if (strcmp(STRINGIFY(m1(xxx)), STRINGIFY(m2(xxx))) != 0) {
    *report error and exit*
}

当宏相等时得到完全优化。甚至字符串都没有保存在只读数据段中(刚刚检查过)。如果您可以忍受必须运行程序才能发现问题,那么这是一种更可靠的方法。

关于比较预处理器宏是否相等,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28816404/

相关文章:

c - 在 C 中使用 pthreads 时如何避免代码膨胀?

c - 将数据写入动态数组时出现段错误

c - 如何对拒绝字符、空格和多余小数点进行错误控制?

c - gcc中的 '-Wextra'和 '-pedantic'有什么区别?

c - 对于在预处理器阶段展开的循环宏?

c - 如果启用了某个#define,如何防止SVN允许提交?

c - 在 uint128_t 整数上下文中对十六进制字符串的操作

c++ - 延迟评估和/或灵活的宏名称

scala - 在 Scala 宏中使用 Scala 反射

c - 错误: expected statement before ‘)’ token in C macro