简体   繁体   English

比较预处理器宏是否相等

[英]Compare preprocessor macros for equality

I have some crude generated header from some .dbc files. 我从一些.dbc文件中获得了一些粗略生成的头文件。 Since a few of the messages represent elements from an array the structure is equal and so the generated Macros are equal. 由于一些消息表示来自数组的元素,因此结构相等,因此生成的宏相等。 Since I fill some array of struct in the code I would like to save effort and use the same macro for all objects, but to ensure the definitions have not changed I would like to test at compile time if the macros are equal. 由于我在代码中填充了一些struct数组,我想节省工作量并为所有对象使用相同的宏,但为了确保定义没有改变,我想在编译时测试宏是否相等。

Example: 例:

#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

Is this Possible? 这可能吗? If there is some solution in C++ that may also help. 如果C ++中有一些解决方案可能也有帮助。 But the macros are fixed. 但宏是固定的。

This is a horrible hack, but seems to work for your example for GCC and C11 at least: 这是一个可怕的黑客,但似乎至少适用于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);

You might need to pass -std=c11 or -std=gnu11 , though the latter shouldn't be needed here. 您可能需要传递-std=c11-std=gnu11 ,但此处不需要后者。

Explanation: 说明:

  • STRINGIFY(x) returns the expansion of x as a string literal. STRINGIFY(x)返回的扩展x为一个字符串。 We need to do the stringification in two steps using STRINGIFY_() because # suppresses macro expansion. 我们需要使用STRINGIFY_()分两步完成字符串化,因为#抑制了宏扩展。 (With one step we'd get "<x>" instead of "expanded version of <x>" .) (只需一步,我们就会得到"<x>"而不是"<x>""expanded version of <x>" 。)

  • GCC has a built-in version of strcmp() ( __builtin_strcmp() ) which is used here. GCC有一个内置版本的strcmp()__builtin_strcmp() ),在这里使用。 It just happens to be able to compare constant strings at compile-time. 它碰巧能够在编译时比较常量字符串。 The code breaks if you pass -fno-builtin (unless you explicitly use __builtin_strcmp() ). 如果传递-fno-builtin则代码会中断(除非您明确使用__builtin_strcmp() )。

  • static_assert is a C11 compile-time assertion. static_assert是C11编译时断言。

With the three ingredients above we can stringify the expanded macros (passing some dummy token that's likely to be unique for the argument) and compare the strings at compile-time. 使用上面的三个组成部分,我们可以对扩展的宏进行字符串化(传递一些可能对参数唯一的虚拟标记)并在编译时比较字符串。

Yes, this is a hack... 是的,这是一个黑客......

In C++11 there are safer ways to compare strings at compile time -- see eg this answer . 在C ++ 11中,有更安全的方法可以在编译时比较字符串 - 请参阅此答案

As a side note, you could do this at run-time too with zero overhead for GCC and Clang. 作为旁注,您可以在运行时执行此操作,而GCC和Clang的开销为零。 (The version above won't work for Clang as it's pickier about strcmp(...) == 0 not being an integer constant expression as required by static_assert .) A run-time check like (上面的版本不适用于Clang,因为strcmp(...) == 0不是static_assert所要求的整数常量表达式 。)运行时检查如

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

gets completely optimized out when the macros are equal. 当宏相等时,会完全优化。 Not even the strings are kept in the read-only data segment (just checked). 甚至字符串都不保存在只读数据段中(只需检查)。 It's a more robust approach if you can live with having to run the program to discover the problem. 如果你不得不运行程序来发现问题,这是一种更强大的方法。

It possible to do this a bit better by using variadic macros to do the stringification: 通过使用可变参数宏来进行字符串化可以更好地做到这一点:

#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" )

This has two advantages: it works with macros which expand to tuples (eg #define FOO thing1, thing2), and it works with macros which take arguments (without a dummy token like xxx in the other solution). 这有两个优点:它适用于扩展为元组的宏(例如#define FOO thing1,thing2),它适用于带参数的宏(在其他解决方案中没有像xxx这样的虚拟标记)。 Note that the final expansions are compared, not the full expansion histories. 请注意,比较最终的扩展,而不是完整的扩展历史。 So given these #defines: 所以给定这些#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

All of these will trigger a compile-time error: 所有这些都会触发编译时错误:

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);

While of these will compile ok: 虽然这些将编译好:

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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM