我刚学会了X-Macros.您看过X-Macros的实际用途?他们什么时候成为工作的合适工具?
在标准C或GNU扩展中有什么方法可以将内容附加到宏定义中吗? 例如,给定一个宏定义为
#define List foo bar
可以追加,bas
以便它List
扩展,就像我定义它一样
#define List foo bar bas
?
我希望我能做到这样的事情:
#define List foo bar bas
#define List_ Expand(List)
#undef List
#define List Expand(List_) quux
Run Code Online (Sandbox Code Playgroud)
但我无法弄清楚如何定义Expand()
宏,所以它会做我想要的.
动机: 我在这些方面玩歧视/标记的工会:
struct quux_foo { int x; };
struct quux_bar { char *s; };
struct quux_bas { void *p; };
enum quux_type {quux_foo, quux_bar, quux_bas};
struct quux {
enum quux_type type;
union {
struct quux_foo foo;
struct quux_bar bar;
struct quux_bas bas;
} t; …
Run Code Online (Sandbox Code Playgroud) 不久前,我为一个大型项目编写了一组X-macro.我需要维护字符串和枚举引用/哈希值/回调函数等的连贯列表.这是函数回调的样子
#define LREF_LOOKUP_TABLE_TEXT_SIZE 32
#define _LREF_ENUM_LIST(_prefix,_ref,...) _prefix ## _ ## _ref,
#define _LREF_BASE_STRUCT_ENTRY(_prefix,_ref) .text= #_ref "\0", .position= _LREF_ENUM_LIST(_prefix, _ref)
#define _LREF_FUNCTION_STRUCT_LIST(_prefix,_ref,...) {_LREF_BASE_STRUCT_ENTRY(_prefix,_ref) _prefix ## _ ## _ref ## _callback},
#define _LREF_ENUM_TYPEDEF(_prefix) \
typedef enum _prefix \
{ \
_ ## _prefix ## s(_prefix,_LREF_ENUM_LIST) \
_LREF_ENUM_LIST(_prefix,tblEnd) \
} e_ ## _prefix
#define _LREF_LOOKUP_TABLE_TYPEDEF(_prefix, _extras) \
typedef struct _prefix ## _lookup \
{ \
const char text[LREF_LOOKUP_TABLE_TEXT_SIZE]; \
e_ ## _prefix position; \
_extras \
} _prefix ##_lookup_t
#define LREF_GENERIC_LOOKUP_TABLE(_prefix, …
Run Code Online (Sandbox Code Playgroud) 可以说,在许多情况下,X宏增加了安全性,因为例如,更容易确保生成的数组长度相同.
但是,Misra C(来自2004参考)规则似乎有很多限制使用的预处理器规则:
规则19.1(建议)文件中的#include语句应该只有其他预处理程序指令或注释开头.
如果源表位于包含的其他文件中,例如生成数组,则很麻烦.但这是一个咨询规则,因此可以解决.
规则19.4(必需)C宏只能扩展为支撑初始值设定项,常量,字符串文字,带括号的表达式,类型限定符,存储类说明符或do-while-zero结构.
不应该是问题,因为大多数X宏用于生成数组初始化器或常量.
规则19.6(必填)#undef不得使用.
使一些X-macro使用模式变得不可能.不幸的是,但并没有完全阻止X-macros.
规则19.7(建议)应该优先使用函数,而不是像函数一样的宏.
仅限咨询规则.
规则19.12(必需)在单个宏定义中最多只能出现一次#或##预处理器运算符.
可以使用嵌套宏来解决.
规则19.13(建议)不应使用#和##预处理器运算符.
例如,在生成枚举时很麻烦,但这只是一个建议规则.
规则19.15(必需)应采取预防措施,以防止头文件的内容被包含两次.
在某些情况下很麻烦,但可以解决.
看看上面的内容,如果你小心的话,似乎可以使用带有Misra C代码的X-macros.
我的结论是正确的,还是有一些我失踪的规则?
我正在使用x-macros来减少重复次数和代码重复,同时为游戏Bitfighter实现Lua接口.以下代码工作正常:
// Fn name Valid param profiles Profile count
# define TELEPORTER_LUA_METHOD_TABLE \
TELEPORTER_LUA_METHOD_ITEM(addDest, ARRAYDEF({{ PT, END }}), 1 ) \
TELEPORTER_LUA_METHOD_ITEM(delDest, ARRAYDEF({{ INT, END }}), 1 ) \
TELEPORTER_LUA_METHOD_ITEM(clearDests, ARRAYDEF({{ END }}), 1 ) \
// BLOCK A Start
const luaL_reg Teleporter::luaMethods[] =
{
# define TELEPORTER_LUA_METHOD_ITEM(name, b, c) { #name, luaW_doMethod<Teleporter, &Teleporter::name > },
TELEPORTER_LUA_METHOD_TABLE
# undef TELEPORTER_LUA_METHOD_ITEM
{ NULL, NULL }
};
// BLOCK A End
/* Generates the following:
const luaL_reg Teleporter::luaMethods[] =
{ …
Run Code Online (Sandbox Code Playgroud) 在我看来,以下是X宏技巧的首选样式:
#define LIST_OF_COLOURS(X) \
X(RED) \
X(GREEN) \
X(BLUE)
#define LIST_OF_FRUIT(X) \
X(APPLE) \
X(ORANGE) \
X(TOMATO)
Run Code Online (Sandbox Code Playgroud)
具体来说,将X
宏传递给列表,而不是在每次实例化列表时都对其进行取消定义和重新定义。这允许:
#define X_LIST(x) x,
#define X_STRING_LIST(x) #x,
#define COMPREHENSIVE_SETUP(n, l) \
enum n { l(X_LIST) }; \
char const* n##Names[] = { l(X_STRING_LIST) };
COMPREHENSIVE_SETUP(Colour, LIST_OF_COLOURS)
COMPREHENSIVE_SETUP(Fruit, LIST_OF_FRUIT)
Run Code Online (Sandbox Code Playgroud)
但是问题是我没有经常在野外看到这种习语,这不是Wikipedia所描述的,即使我每次尝试并觉得更方便的时候它似乎都可以工作。
我的问题是,这实际上合法且已完全定义,还是我依赖未定义的行为?
我试图在我的x-macro中分配一个值,但我真的不明白为什么它不起作用:
#include <stdio.h>
typedef struct
{
int a;
int b;
} struct_t;
#define MY_LIST \
MY_ELEMENT(a) \
MY_ELEMENT(b)
#define MY_ELEMENT(x) struct_t x; \
x.a=33;
MY_LIST
#undef MY_ELEMENT
int main(void)
{
fprintf(stdout, "a: %d\n", a.a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译时我收到以下错误:
test.c:14:2: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘.’ token
x.a=33;
Run Code Online (Sandbox Code Playgroud)
有人可以解释为什么我得到这个错误以及如何解决这个问题?