[英]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.