编写 vargarg C 预处理器宏,它用它的每个参数调用一些其他宏等等
Write vargarg C preprocessor macro which calls some other macro with each of its arguments and more
我需要在我的代码中包含有关结构的元信息。因此,我编写了一些 C 结构(用于存储元信息)和 C 预处理器宏的组合来初始化这些结构,而无需大量样板代码。现在它看起来像这样(真的,我存储了很多关于字段的信息,但这段代码足以解决我的问题):
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
struct meta_field {
const char *name;
size_t offset;
};
struct meta_struct {
const char *name;
size_t size;
struct meta_field fields[];
};
#define META_STRUCT_BEGIN(NAME) \
static struct meta_struct s_meta_##NAME = { \
.name = "" #NAME, \
.size = sizeof(struct NAME), \
.fields = {
#define META_STRUCT_END() ,{NULL, 0}}}
#define META_FIELD(NAME) { .name = "" #NAME, .offset = offsetof(struct CURRENT_META_STRUCT, NAME) }
struct example {
int i;
double d;
};
#define CURRENT_META_STRUCT example
META_STRUCT_BEGIN(CURRENT_META_STRUCT)
META_FIELD(i),
META_FIELD(d)
META_STRUCT_END();
#undef CURRENT_META_STRUCT
有效。但它仍然有很多样板文件:#define CURRENT_META_STRUCT
、CURRENT_META_STRUCT
作为 META_STRUCT_BEGIN()
的参数的用法、CURRENT_META_STRUCT
的 #undef
的用法——所有这些看起来都很丑陋,恕我直言.
我知道,一个宏定义一个宏是不可能的(所以,META_STRUCT_BEGIN()
不能定义CURRENT_META_STRUCT
)。但是看看 C 预处理器和 Boost-PP 中的 Branf*ck 实现,我认为可以实现如下所示的东西:
META_STRUCT(example,
META_FIELD(i),
META_FIELD(d)
)
但我无法理解这个预处理器魔法。
有人可以帮助我吗?
我已经阅读了 https://github.com/orangeduck/CPP_COMPLETE,但没有帮助。
此外,它被标记为 的副本,但问题是我需要向所有生成的宏 "calls" 添加 "default" 参数。
这里有一些草图,我想要实现的目标:
#define META_FIELD_IMPL(STRUCT, NAME) { .name = "" #NAME, .offset = offsetof(struct STRUCT, NAME) }
#define META_FIELD(NAME) NAME
#define META_STRUCT(NAME, ... ) \
static struct meta_struct s_meta_##NAME = { \
.name = "" #NAME, \
.size = sizeof(struct NAME), \
.fields = { \
/* Each ARG from __VA_ARGS__ is META_FIELD(x) and \
* I need to call META_FIELD_IMPL(NAME, <Expansion of META_FIELD>) \
* for each ARG from _VA_ARGS_ here */ \
{NULL, 0} \
}}
现在在这个例子中 META_FIELD()
只有一个参数:NAME
,但在实际系统中 META_FIELD()
有 6 个参数和一些提供 "default" 一般情况下的值,所以 META_FIELD()
是必需的,不能用 NAME
本身代替。
最后一个复杂的问题:这样的 META_STRUCT()
可以作为 META_FIELD()
的参数调用,因为一些字段包含指向嵌套元结构的指针!现在它是通过为所有嵌套子结构声明命名对象来完成的,但我也想避免它!我明白,嵌套的深度可能会受到某个任意常数的限制,没关系。
更新:我添加了这个例子,说明我想输入什么以及我想在预处理器之后得到什么。我不知道如何实现所有这些宏(标记为 /* ??? */
)。而且,是的,我已经检查过,手动编码 "end result" 可以正常编译。
enum meta_type { mt_end, mt_int, mt_string, mt_struct };
struct meta_struct;
struct meta_field {
const char *name;
enum meta_type type;
size_t offset;
struct meta_struct *child;
};
struct meta_struct {
const char *name;
size_t size;
struct meta_field *fields;
};
#define META_STRUCT(NAME, ...) static struct meta_struct s_meta_##name = { .name = #NAME, .size = sizeof(struct NAME), .fileds = (struct meta_field[]){ /* ??? */, {NULL, mt_end, 0, NULL}}}
#define META_FIELD_IMPL0(STRUCT, NAME, TYPE, CHILD) { .name = #NAME, .type = TYPE, .offset = offsetof(STRUCT, NAME), .child = CHILD }
#define META_FIELD_IMPL1(NAME, TYPE, CHILD) /* ??? */
#define META_FIELD(NAME, TYPE) META_FIELD_IMPL1(NAME, TYPE, NULL)
#define META_FIELD_SUB(NAME, CHILD) META_FIELD_IMPL1(NAME, mt_struct, CHILD)
#define META_SUBSTRUCT(NAME, ...) (struct meta_struct){ .name = #NAME, .size = sizeof(struct NAME), .fileds = (struct meta_field[]){ /* ??? */, {NULL, mt_end, 0, NULL}}}
/* Example of "input": */
struct child {
int i;
};
struct parent {
int i;
struct child c;
const char *s;
};
META_STRUCT(parent,
META_FIELD(i, mt_int),
META_FIELD_SUB(c,
META_SUBSTRUCT(child,
META_FIELD(i, mt_int)
)
),
META_FIELD(s, mt_string)
);
/* This should give this */
static struct meta_struct s_meta_parent = {
.name = "parent",
.size = sizeof(struct parent),
.fields = (struct meta_field[]) {
{ .name = "i", .type = mt_int, .offset = offsetof(struct parent, i), .child = NULL },
{ .name = "c", .type = mt_struct, .offset = offsetof(struct parent, c), .child = &(struct meta_struct){
.name = "child",
.size = sizeof(struct child),
.fields = (struct meta_field[]) {
{ .name = "i", .type = mt_int, .offset = offsetof(struct child, i), .child = NULL },
{NULL, mt_end, 0, NULL}
}
}
},
{ .name = "s", .type = mt_string, .offset = offsetof(struct parent, s), .child = NULL },
{NULL, mt_end, 0, NULL}
}
};
使用 __VA_ARGS__
怎么样?下面的 Codesnip 接受最多 4 个字段,但您可以通过复制和粘贴一些代码行来扩展到 5 个、6 个或更多(有关详细信息,请参阅 )。
头文件:
#define META_FIELD1(st,x1) { .name = #x1, .offset = offsetof(struct st, x1) }
#define META_FIELD2(st,x1,x2) META_FIELD1(st,x1), META_FIELD1(st, x2)
#define META_FIELD3(st,x1,x2,x3) META_FIELD1(st,x1), META_FIELD2(st,x2,x3)
#define META_FIELD4(st,x1,x2,x3,x4) META_FIELD1(st,x1), META_FIELD3(st,x2,x3,x4)
#define META_FIELD(st, ...) META_FIELD4(st, ##__VA_ARGS__) // limit to 4
#define META_STRUCT(st, ...) \
static struct meta_struct meta_struct_##st = { \
.name = #st, \
.size = sizeof(struct st), \
.fields = { \
META_FIELD(st, ##__VA_ARGS__) \
} \
} \
C 文件:
struct sample {
int i;
double d;
const char *test;
float t;
};
META_STRUCT(sample, i, d, test, t);
/*
*
*/
int main(int argc, char** argv) {
printf("struct %s { %s, %s, %s, %s } \n",
meta_struct_sample.name,
meta_struct_sample.fields[0].name,
meta_struct_sample.fields[1].name,
meta_struct_sample.fields[2].name,
meta_struct_sample.fields[3].name);
return (EXIT_SUCCESS);
}
输出应该是:struct sample { i, i, test, t }
这是一个方法。
基本实用程序
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define COUNT(...) COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define COUNT_I(V,_9,_8,_7,_6,_5,_4,_3,_2,X,...) X
这是一个间接粘合宏(允许扩展参数)和一个计数宏。这里的 count 宏只支持 9 个参数,但扩展模式很容易。这只是扩展到传递的参数数量。我假设您以前见过这个,但以防万一,这只是通过参数转换起作用的;它间接扩展了参数,并且每个额外的参数移动到 1 以上都会取代数字列表,直到它扩展到 "above" X 为止。
#define FIRST(...) FIRST_I(__VA_ARGS__,)
#define FIRST_I(X,...) X
#define REST(X,...) __VA_ARGS__
#define EXPAND(...) __VA_ARGS__
...这些是对元组的操作。出于此答案的目的,tuple 是括号中的一组标记。 FIRST
和 REST
使用元组作为数据类型(应该很明显;FIRST
间接获取第一个元素,REST
扩展到除此以外的所有元素)。 EXPAND
展开一个元组。
下一个实用程序宏:
#define FOREACH(MACRO_,DATA_,TUPLE_) GLUE(FOREACH_I_,COUNT TUPLE_)(MACRO_,DATA_,TUPLE_)
#define FOREACH_I_1(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_)
#define FOREACH_I_2(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_1(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_3(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_2(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_4(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_3(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_5(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_4(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_6(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_5(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_7(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_6(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_8(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_7(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_9(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_8(MACRO_,DATA_,(REST TUPLE_))
这是关键宏;它实际上用于将结构的类型应用到每个字段的较低级别的宏中。同样,此特定代码每个级别仅适用于 9 个字段,但是如何扩展它应该是显而易见的(您可能希望支持 COUNT
宏可以数到的级别)。
FOREACH
的第三个参数应该是元组; FOREACH
的工作是在 DATA_
和 TUPLE_
的每个元素上调用您的宏。例如,FOREACH(FIELD,parent,(i,j,k))
扩展为 FIELD(parent,i) FIELD(parent,j) FIELD(parent,k)
。
特定实用程序的概念
特定实用程序的核心调用结构使用元组数据类型;当与 FOREACH
宏一起使用时,底层调用会将外部结构类型适当地柯里化到每个调用中。调用结构如下所示:
<b>META_STRUCT(</b>类型<b>,</b> FIELD[<b>,</b> FIELD[ ... ] ] <b>)</b>
...其中每个 FIELD
是一个三元组:
<b>(</b>名称<b>,</b>类型<b>,</b>CHILD <b>)</b>
...并且每个 CHILD
都是一个元组,它被扩展为一个 .child
赋值。 META_SUBSTRUCT
的工作原理类似。所以根据这个调用结构,你的例子实际上会被这样调用:
META_STRUCT(parent, (i, mt_int, (NULL)), (c, mt_struct, META_SUBSTRUCT(child,(i,mt_int,(NULL)))),(s, mt_string, (NULL)));
实际上,META_SUBSTRUCT
实际上会完全展开。它的扩展将是一个 "tuple of tokens" 作为 child 赋值;也就是说,META_SUBSTRUCT
将扩展到用括号包围的子结构中的实际赋值。 FOREACH
宏应用于此构造,将上述 3 元组的外部结构和三个参数柯里化为 4 参数宏 APPLY_UTILI_FIELD
.
用法反映了您的用法并采用了此调用结构。例如,META_FIELD(i, mt_int)
只是一个扩展为 (i, mt_int, (NULL))
.
的宏
特定实用程序
#define META_STRUCT_BEGIN(NAME_) \
static struct meta_struct s_meta_##NAME_ = { \
.name = #NAME_, \
.size = sizeof(struct NAME_), \
.fields = (struct meta_field[]) {
#define META_STRUCT(NAME_,...) \
META_STRUCT_BEGIN(NAME_) \
FOREACH(APPLY_META_STRUCT_MACRO,NAME_,(__VA_ARGS__)) \
META_STRUCT_END()
#define META_STRUCT_END() \
{NULL, mt_end, 0, NULL}}}
#define META_SUBSTRUCT(NAME_,...) \
( META_SUBSTRUCT_BEGIN(NAME_) \
FOREACH(APPLY_META_STRUCT_MACRO,NAME_,(__VA_ARGS__)) \
META_SUBSTRUCT_END() )
#define META_SUBSTRUCT_BEGIN(NAME_) \
&(struct meta_struct) { \
.name = #NAME_, \
.size = sizeof(struct NAME_), \
.fields = (struct meta_field[]) {
#define META_SUBSTRUCT_END() \
{NULL, mt_end, 0, NULL}}}
#define APPLY_META_STRUCT_MACRO(DATA_, ARG_) APPLY_UTIL_FIELD(DATA_, EXPAND ARG_)
#define APPLY_UTIL_FIELD(...) APPLY_UTILI_FIELD(__VA_ARGS__)
#define APPLY_UTILI_FIELD( STRUCT_, FIELD_, TYPE_, CHILD_) \
{ .name = #FIELD_, .type = TYPE_, .offset = offsetof(struct STRUCT_, FIELD_), .child = EXPAND CHILD_ },
#define META_FIELD(NAME_, TYPE_) (NAME_, TYPE_, (NULL))
#define META_FIELD_SUB(NAME_, SUB_) (NAME_, mt_struct, SUB_)
演示
备注
嵌套
这些宏允许在没有 运行 的情况下无限期地嵌套 META_SUBSTRUCT
调用到蓝色油漆中,因为所有这些宏最终都在参数替换阶段被评估(...
参数提到 __VA_ARGS__
导致所有参数扩展;META_SUBSTRUCT
的每个级别在这种情况下都是顶级扩展;这种情况递归)。
你的六个领域
鉴于您展示的示例字段少于您使用的字段,您需要专门调整用户定义的 META_FIELD
、META_FIELD_SUB
等宏以生成 k
-tuples 而不是 3-tuples(其中 k
是你需要的)。然后,只需调整 APPLY_UTILI_FIELD
的参数和扩展以匹配。
微软兼容性
此代码采用标准 C 预处理器。如果您正在专门处理 MSVC(没有),它将无法工作。具体来说,MSVC 未能按预期扩展 __VA_ARGS__
;如果您关心 在 MSVC 上工作,请将这三个对应的宏更改为:
#define COUNT(...) EXPAND(COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,))
#define FIRST(...) EXPAND(FIRST_I(__VA_ARGS__,))
#define APPLY_UTIL_FIELD(...) EXPAND(APPLY_UTILI_FIELD(__VA_ARGS__))
结果也适用于符合标准的 CPP。
我需要在我的代码中包含有关结构的元信息。因此,我编写了一些 C 结构(用于存储元信息)和 C 预处理器宏的组合来初始化这些结构,而无需大量样板代码。现在它看起来像这样(真的,我存储了很多关于字段的信息,但这段代码足以解决我的问题):
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
struct meta_field {
const char *name;
size_t offset;
};
struct meta_struct {
const char *name;
size_t size;
struct meta_field fields[];
};
#define META_STRUCT_BEGIN(NAME) \
static struct meta_struct s_meta_##NAME = { \
.name = "" #NAME, \
.size = sizeof(struct NAME), \
.fields = {
#define META_STRUCT_END() ,{NULL, 0}}}
#define META_FIELD(NAME) { .name = "" #NAME, .offset = offsetof(struct CURRENT_META_STRUCT, NAME) }
struct example {
int i;
double d;
};
#define CURRENT_META_STRUCT example
META_STRUCT_BEGIN(CURRENT_META_STRUCT)
META_FIELD(i),
META_FIELD(d)
META_STRUCT_END();
#undef CURRENT_META_STRUCT
有效。但它仍然有很多样板文件:#define CURRENT_META_STRUCT
、CURRENT_META_STRUCT
作为 META_STRUCT_BEGIN()
的参数的用法、CURRENT_META_STRUCT
的 #undef
的用法——所有这些看起来都很丑陋,恕我直言.
我知道,一个宏定义一个宏是不可能的(所以,META_STRUCT_BEGIN()
不能定义CURRENT_META_STRUCT
)。但是看看 C 预处理器和 Boost-PP 中的 Branf*ck 实现,我认为可以实现如下所示的东西:
META_STRUCT(example,
META_FIELD(i),
META_FIELD(d)
)
但我无法理解这个预处理器魔法。
有人可以帮助我吗?
我已经阅读了 https://github.com/orangeduck/CPP_COMPLETE,但没有帮助。
此外,它被标记为
这里有一些草图,我想要实现的目标:
#define META_FIELD_IMPL(STRUCT, NAME) { .name = "" #NAME, .offset = offsetof(struct STRUCT, NAME) }
#define META_FIELD(NAME) NAME
#define META_STRUCT(NAME, ... ) \
static struct meta_struct s_meta_##NAME = { \
.name = "" #NAME, \
.size = sizeof(struct NAME), \
.fields = { \
/* Each ARG from __VA_ARGS__ is META_FIELD(x) and \
* I need to call META_FIELD_IMPL(NAME, <Expansion of META_FIELD>) \
* for each ARG from _VA_ARGS_ here */ \
{NULL, 0} \
}}
现在在这个例子中 META_FIELD()
只有一个参数:NAME
,但在实际系统中 META_FIELD()
有 6 个参数和一些提供 "default" 一般情况下的值,所以 META_FIELD()
是必需的,不能用 NAME
本身代替。
最后一个复杂的问题:这样的 META_STRUCT()
可以作为 META_FIELD()
的参数调用,因为一些字段包含指向嵌套元结构的指针!现在它是通过为所有嵌套子结构声明命名对象来完成的,但我也想避免它!我明白,嵌套的深度可能会受到某个任意常数的限制,没关系。
更新:我添加了这个例子,说明我想输入什么以及我想在预处理器之后得到什么。我不知道如何实现所有这些宏(标记为 /* ??? */
)。而且,是的,我已经检查过,手动编码 "end result" 可以正常编译。
enum meta_type { mt_end, mt_int, mt_string, mt_struct };
struct meta_struct;
struct meta_field {
const char *name;
enum meta_type type;
size_t offset;
struct meta_struct *child;
};
struct meta_struct {
const char *name;
size_t size;
struct meta_field *fields;
};
#define META_STRUCT(NAME, ...) static struct meta_struct s_meta_##name = { .name = #NAME, .size = sizeof(struct NAME), .fileds = (struct meta_field[]){ /* ??? */, {NULL, mt_end, 0, NULL}}}
#define META_FIELD_IMPL0(STRUCT, NAME, TYPE, CHILD) { .name = #NAME, .type = TYPE, .offset = offsetof(STRUCT, NAME), .child = CHILD }
#define META_FIELD_IMPL1(NAME, TYPE, CHILD) /* ??? */
#define META_FIELD(NAME, TYPE) META_FIELD_IMPL1(NAME, TYPE, NULL)
#define META_FIELD_SUB(NAME, CHILD) META_FIELD_IMPL1(NAME, mt_struct, CHILD)
#define META_SUBSTRUCT(NAME, ...) (struct meta_struct){ .name = #NAME, .size = sizeof(struct NAME), .fileds = (struct meta_field[]){ /* ??? */, {NULL, mt_end, 0, NULL}}}
/* Example of "input": */
struct child {
int i;
};
struct parent {
int i;
struct child c;
const char *s;
};
META_STRUCT(parent,
META_FIELD(i, mt_int),
META_FIELD_SUB(c,
META_SUBSTRUCT(child,
META_FIELD(i, mt_int)
)
),
META_FIELD(s, mt_string)
);
/* This should give this */
static struct meta_struct s_meta_parent = {
.name = "parent",
.size = sizeof(struct parent),
.fields = (struct meta_field[]) {
{ .name = "i", .type = mt_int, .offset = offsetof(struct parent, i), .child = NULL },
{ .name = "c", .type = mt_struct, .offset = offsetof(struct parent, c), .child = &(struct meta_struct){
.name = "child",
.size = sizeof(struct child),
.fields = (struct meta_field[]) {
{ .name = "i", .type = mt_int, .offset = offsetof(struct child, i), .child = NULL },
{NULL, mt_end, 0, NULL}
}
}
},
{ .name = "s", .type = mt_string, .offset = offsetof(struct parent, s), .child = NULL },
{NULL, mt_end, 0, NULL}
}
};
使用 __VA_ARGS__
怎么样?下面的 Codesnip 接受最多 4 个字段,但您可以通过复制和粘贴一些代码行来扩展到 5 个、6 个或更多(有关详细信息,请参阅
头文件:
#define META_FIELD1(st,x1) { .name = #x1, .offset = offsetof(struct st, x1) }
#define META_FIELD2(st,x1,x2) META_FIELD1(st,x1), META_FIELD1(st, x2)
#define META_FIELD3(st,x1,x2,x3) META_FIELD1(st,x1), META_FIELD2(st,x2,x3)
#define META_FIELD4(st,x1,x2,x3,x4) META_FIELD1(st,x1), META_FIELD3(st,x2,x3,x4)
#define META_FIELD(st, ...) META_FIELD4(st, ##__VA_ARGS__) // limit to 4
#define META_STRUCT(st, ...) \
static struct meta_struct meta_struct_##st = { \
.name = #st, \
.size = sizeof(struct st), \
.fields = { \
META_FIELD(st, ##__VA_ARGS__) \
} \
} \
C 文件:
struct sample {
int i;
double d;
const char *test;
float t;
};
META_STRUCT(sample, i, d, test, t);
/*
*
*/
int main(int argc, char** argv) {
printf("struct %s { %s, %s, %s, %s } \n",
meta_struct_sample.name,
meta_struct_sample.fields[0].name,
meta_struct_sample.fields[1].name,
meta_struct_sample.fields[2].name,
meta_struct_sample.fields[3].name);
return (EXIT_SUCCESS);
}
输出应该是:struct sample { i, i, test, t }
这是一个方法。
基本实用程序
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define COUNT(...) COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define COUNT_I(V,_9,_8,_7,_6,_5,_4,_3,_2,X,...) X
这是一个间接粘合宏(允许扩展参数)和一个计数宏。这里的 count 宏只支持 9 个参数,但扩展模式很容易。这只是扩展到传递的参数数量。我假设您以前见过这个,但以防万一,这只是通过参数转换起作用的;它间接扩展了参数,并且每个额外的参数移动到 1 以上都会取代数字列表,直到它扩展到 "above" X 为止。
#define FIRST(...) FIRST_I(__VA_ARGS__,)
#define FIRST_I(X,...) X
#define REST(X,...) __VA_ARGS__
#define EXPAND(...) __VA_ARGS__
...这些是对元组的操作。出于此答案的目的,tuple 是括号中的一组标记。 FIRST
和 REST
使用元组作为数据类型(应该很明显;FIRST
间接获取第一个元素,REST
扩展到除此以外的所有元素)。 EXPAND
展开一个元组。
下一个实用程序宏:
#define FOREACH(MACRO_,DATA_,TUPLE_) GLUE(FOREACH_I_,COUNT TUPLE_)(MACRO_,DATA_,TUPLE_)
#define FOREACH_I_1(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_)
#define FOREACH_I_2(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_1(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_3(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_2(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_4(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_3(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_5(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_4(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_6(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_5(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_7(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_6(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_8(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_7(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_9(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_8(MACRO_,DATA_,(REST TUPLE_))
这是关键宏;它实际上用于将结构的类型应用到每个字段的较低级别的宏中。同样,此特定代码每个级别仅适用于 9 个字段,但是如何扩展它应该是显而易见的(您可能希望支持 COUNT
宏可以数到的级别)。
FOREACH
的第三个参数应该是元组; FOREACH
的工作是在 DATA_
和 TUPLE_
的每个元素上调用您的宏。例如,FOREACH(FIELD,parent,(i,j,k))
扩展为 FIELD(parent,i) FIELD(parent,j) FIELD(parent,k)
。
特定实用程序的概念
特定实用程序的核心调用结构使用元组数据类型;当与 FOREACH
宏一起使用时,底层调用会将外部结构类型适当地柯里化到每个调用中。调用结构如下所示:
<b>META_STRUCT(</b>类型<b>,</b> FIELD[<b>,</b> FIELD[ ... ] ] <b>)</b>
...其中每个 FIELD
是一个三元组:
<b>(</b>名称<b>,</b>类型<b>,</b>CHILD <b>)</b>
...并且每个 CHILD
都是一个元组,它被扩展为一个 .child
赋值。 META_SUBSTRUCT
的工作原理类似。所以根据这个调用结构,你的例子实际上会被这样调用:
META_STRUCT(parent, (i, mt_int, (NULL)), (c, mt_struct, META_SUBSTRUCT(child,(i,mt_int,(NULL)))),(s, mt_string, (NULL)));
实际上,META_SUBSTRUCT
实际上会完全展开。它的扩展将是一个 "tuple of tokens" 作为 child 赋值;也就是说,META_SUBSTRUCT
将扩展到用括号包围的子结构中的实际赋值。 FOREACH
宏应用于此构造,将上述 3 元组的外部结构和三个参数柯里化为 4 参数宏 APPLY_UTILI_FIELD
.
用法反映了您的用法并采用了此调用结构。例如,META_FIELD(i, mt_int)
只是一个扩展为 (i, mt_int, (NULL))
.
特定实用程序
#define META_STRUCT_BEGIN(NAME_) \
static struct meta_struct s_meta_##NAME_ = { \
.name = #NAME_, \
.size = sizeof(struct NAME_), \
.fields = (struct meta_field[]) {
#define META_STRUCT(NAME_,...) \
META_STRUCT_BEGIN(NAME_) \
FOREACH(APPLY_META_STRUCT_MACRO,NAME_,(__VA_ARGS__)) \
META_STRUCT_END()
#define META_STRUCT_END() \
{NULL, mt_end, 0, NULL}}}
#define META_SUBSTRUCT(NAME_,...) \
( META_SUBSTRUCT_BEGIN(NAME_) \
FOREACH(APPLY_META_STRUCT_MACRO,NAME_,(__VA_ARGS__)) \
META_SUBSTRUCT_END() )
#define META_SUBSTRUCT_BEGIN(NAME_) \
&(struct meta_struct) { \
.name = #NAME_, \
.size = sizeof(struct NAME_), \
.fields = (struct meta_field[]) {
#define META_SUBSTRUCT_END() \
{NULL, mt_end, 0, NULL}}}
#define APPLY_META_STRUCT_MACRO(DATA_, ARG_) APPLY_UTIL_FIELD(DATA_, EXPAND ARG_)
#define APPLY_UTIL_FIELD(...) APPLY_UTILI_FIELD(__VA_ARGS__)
#define APPLY_UTILI_FIELD( STRUCT_, FIELD_, TYPE_, CHILD_) \
{ .name = #FIELD_, .type = TYPE_, .offset = offsetof(struct STRUCT_, FIELD_), .child = EXPAND CHILD_ },
#define META_FIELD(NAME_, TYPE_) (NAME_, TYPE_, (NULL))
#define META_FIELD_SUB(NAME_, SUB_) (NAME_, mt_struct, SUB_)
演示
备注
嵌套
这些宏允许在没有 运行 的情况下无限期地嵌套 META_SUBSTRUCT
调用到蓝色油漆中,因为所有这些宏最终都在参数替换阶段被评估(...
参数提到 __VA_ARGS__
导致所有参数扩展;META_SUBSTRUCT
的每个级别在这种情况下都是顶级扩展;这种情况递归)。
你的六个领域
鉴于您展示的示例字段少于您使用的字段,您需要专门调整用户定义的 META_FIELD
、META_FIELD_SUB
等宏以生成 k
-tuples 而不是 3-tuples(其中 k
是你需要的)。然后,只需调整 APPLY_UTILI_FIELD
的参数和扩展以匹配。
微软兼容性
此代码采用标准 C 预处理器。如果您正在专门处理 MSVC(没有),它将无法工作。具体来说,MSVC 未能按预期扩展 __VA_ARGS__
;如果您关心 在 MSVC 上工作,请将这三个对应的宏更改为:
#define COUNT(...) EXPAND(COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,))
#define FIRST(...) EXPAND(FIRST_I(__VA_ARGS__,))
#define APPLY_UTIL_FIELD(...) EXPAND(APPLY_UTILI_FIELD(__VA_ARGS__))
结果也适用于符合标准的 CPP。