【问题标题】:Compare preprocessor macros for equality比较预处理器宏是否相等
【发布时间】:2015-03-02 17:57:01
【问题描述】:

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

例子:

#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++ 中有一些解决方案也可能有所帮助。但是宏是固定的。

【问题讨论】:

  • 不,因为预处理器从编译过程的其余部分运行in a separate phase,这意味着它不知道变量或其他运行时间的东西。
  • 是的,我知道这些阶段。我想可能有人有一些stringify hack。正确解析表达式并不重要。如果只有空格不同,则比较失败也没关系,因为生成器将生成完全相同的代码。
  • 不,因为你的是函数式宏。您可以使用其他类型执行您建议的操作,但如果没有正确数量的参数,您将无法扩展函数样式的宏。
  • 但是如果buf是在运行时填写的,那么只有编译时的预处理器怎么知道里面的数据呢?
  • @JoachimPileborg,我认为他想比较宏定义而不替换参数。但是不,你不能那样做。

标签: c macros c-preprocessor


【解决方案1】:

这是一个可怕的 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_() 分两步进行字符串化,因为# 抑制了宏扩展。 (一步我们会得到"&lt;x&gt;" 而不是"expanded version of &lt;x&gt;"。)

  • 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*
}

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

【讨论】:

  • @Mints97 是的,我知道有一些方法可以将编译时断言破解到 C 的早期版本中。希望使事情保持相当简单。不过也许值得一提。
  • 在阅读一个不相关的答案时,我意识到只做STRINGIFY(m1(xxx)) == STRINGIFY(m2(xxx)) 也可能在实践中起作用。原因是编译器倾向于合并相同的字符串文字,以便它们最终位于相同的地址,这意味着您可以只比较指针。不过不要用那个。 :P
【解决方案2】:

使用可变参数宏进行字符串化可能会做得更好:

#define STRINGIFY_VARIADIC(...) #__VA_ARGS__
#define EXPAND_AND_STRINGIFY_VARIADIC(...) STRINGIFY_VARIADIC (__VA_ARGS__)

#define STATIC_ASSERT_IDENTICAL_EXPANSIONS(macro_a, macro_b) \
   _Static_assert (                                          \
       (                                                     \
         __builtin_strcmp (                                  \
             EXPAND_AND_STRINGIFY_VARIADIC (macro_a),        \
             EXPAND_AND_STRINGIFY_VARIADIC (macro_b) )       \
         == 0                                                \
       ),                                                    \
       "expansions of " #macro_a " and " #macro_b " differ" )

这有两个优点:它适用于扩展为元组的宏 (例如#define FOO thing1,thing2),它与宏一起使用 参数(在其他解决方案中没有像 xxx 这样的虚拟标记)。注意 比较最终的扩展,而不是完整的扩展历史。 因此,鉴于这些#defines:

#define FOO foo
#define BAR bar
#define ARG_DOUBLER(arg) arg, arg
#define ARG_ITSELF(arg) arg
#define OTHER_ARG_DOUBLER(arg) ARG_ITSELF (arg), ARG_ITSELF (arg)
#define SECOND_ARG_NUKER(arg1, arg2) arg1

所有这些都会触发编译时错误:

STATIC_ASSERT_IDENTICAL_EXPANSIONS (FOO, BAR);
STATIC_ASSERT_IDENTICAL_EXPANSIONS (ARG_DOUBLER (x), ARG_DOUBLER (y));
STATIC_ASSERT_IDENTICAL_EXPANSIONS (x, ARG_ITSELF (y));
STATIC_ASSERT_IDENTICAL_EXPANSIONS (SECOND_ARG_NUKER (x, y), y);

虽然这些都可以编译:

STATIC_ASSERT_IDENTICAL_EXPANSIONS (FOO, foo);
STATIC_ASSERT_IDENTICAL_EXPANSIONS (ARG_DOUBLER (x), ARG_DOUBLER (x));
STATIC_ASSERT_IDENTICAL_EXPANSIONS (x, ARG_ITSELF (x));
STATIC_ASSERT_IDENTICAL_EXPANSIONS (SECOND_ARG_NUKER (x, y), x);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-05
    • 2020-05-29
    • 2010-09-23
    • 2011-07-02
    • 2015-03-13
    • 2015-09-16
    相关资源
    最近更新 更多