比较预处理器宏是否相等

vla*_*sch 4 c macros c-preprocessor

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

例:

#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
Run Code Online (Sandbox Code Playgroud)

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

Ulf*_*zer 7

这是一个可怕的黑客,但似乎至少适用于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);
Run Code Online (Sandbox Code Playgroud)

您可能需要通过-std=c11-std=gnu11,虽然后者不应该在这里需要.

说明:

  • STRINGIFY(x)返回x字符串文字的扩展.我们需要使用两个步骤来进行字符串化,STRINGIFY_()因为它会#抑制宏扩展.(我们只需要一步"<x>"而不是"expanded version of <x>".)

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

  • static_assert 是C11编译时断言.

使用上面的三个组成部分,我们可以对扩展的宏进行字符串化(传递一些可能对参数唯一的虚拟标记)并在编译时比较字符串.

是的,这是一个黑客......

在C++ 11中,有更安全的方法可以在编译时比较字符串 - 请参阅此答案.

作为旁注,您可以在运行时执行此操作,而GCC和Clang的开销为零.(上面的版本不适用于Clang,因为它strcmp(...) == 0不是一个整数常量表达式,因为它是必需的static_assert.)像运行时检查一样

if (strcmp(STRINGIFY(m1(xxx)), STRINGIFY(m2(xxx))) != 0) {
    *report error and exit*
}
Run Code Online (Sandbox Code Playgroud)

当宏相等时,会完全优化.甚至字符串都不保存在只读数据段中(只需检查).如果你不得不运行程序来发现问题,这是一种更强大的方法.

  • 在阅读一个不相关的答案时,我意识到只执行 `STRINGIFY(m1(xxx)) == STRINGIFY(m2(xxx))` 也可能在实践中起作用。原因是编译器倾向于合并相同的字符串文字,以便它们最终位于相同的地址,这意味着您可以只比较指针。不要使用那个。:P (2认同)