[英]Compare preprocessor macros for equality
我從一些.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
這可能嗎? 如果C ++中有一些解決方案可能也有幫助。 但宏是固定的。
這是一個可怕的黑客,但似乎至少適用於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>"
而不是"<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*
}
當宏相等時,會完全優化。 甚至字符串都不保存在只讀數據段中(只需檢查)。 如果你不得不運行程序來發現問題,這是一種更強大的方法。
通過使用可變參數宏來進行字符串化可以更好地做到這一點:
#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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.