如何在没有函数调用和宏的情况下填充指针数组?
How to fill a pointer array without a function call and from a macro?
我有一个小的递归 AST 如下:
enum ABC
{
A,
B,
C
};
typedef struct abc_t
{
enum ABC kind;
union {
struct a_
{
int x;
} a_;
struct b_
{
int y;
} b_;
struct c_
{
struct abc_t **array;
} c_;
} u;
} abc_t;
为了简化构造,我定义了填充宏:
#define ABC_SET_A(n) (abc_t) { .kind = A, .u.a_ = { n } }
#define ABC_SET_B(n) (abc_t) { .kind = B, .u.b_ = { n } }
const abc_t bar = ABC_SET_A(5);
const abc_t foo = ABC_SET_B(2);
但是,我不知道如何用宏填充我的 AST 的 "c_" 分支。我想得出这样的结果:
const abc_t test = ABC_SET_C({ABC_SET_B(4), ABC_SET_A(3), ABC_SET_B(2)});
// or
const abc_t test = ABC_SET_C(ABC_SET_B(4), ABC_SET_A(3), ABC_SET_B(2)); // With variadics arguments
目标是拥有一个 abc_t
的不可变数组,代表一组预构建的结构,例如:
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C({ABC_SET_A(8), ABC_SET_B(4)}), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
...
};
我不知道如何处理 such 宏的递归结构,尤其是在这种情况下的数组。
我该怎么做?
首先修正你的 ABC_SET_A
和 ABC_SET_B
。你不用像 int var = { 1 };
这样的大括号初始化 int
,你像 int var = 1;
:
这样初始化 int
#define ABC_SET_A(n) (abc_t){ .kind = A, .u.a_ = n }
#define ABC_SET_B(n) (abc_t){ .kind = B, .u.b_ = n }
ABC_SET_C
很容易。当你的结构中有指针数组时,你可以这样做:
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) __VA_ARGS__ }
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
// note - as this is array of pointers, I added & to get the pointer
ABC_SET_C({&ABC_SET_A(8), &ABC_SET_B(4)}), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
无论如何,{
}
大括号对预处理器来说并不特殊,也不会被它解析。我更喜欢:
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) { __VA_ARGS__ } }
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(&ABC_SET_A(8), &ABC_SET_B(4)), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
如果你想摆脱在每个 ABC_SET_*
前面写 &
,那么你可以在参数数量上重载宏,并为每个参数插入它们。我发现它不值得付出努力,但它最多可以为 3 个参数实现:
#define ABC_SET_A(n) ((abc_t){ .kind = A, .u.a_ = n })
#define ABC_SET_B(n) ((abc_t){ .kind = B, .u.b_ = n })
#define ABC_SET_C_1(_1) &_1
#define ABC_SET_C_2(_1,_2) &_1, &_2
#define ABC_SET_C_3(_1,_2,_3) &_1, &_2, &_3
#define ABC_SET_C_N(_1,_2,_3,N,...) ABC_SET_C##N
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) { \
ABC_SET_C_N(__VA_ARGS__,_3,_2,_1)(__VA_ARGS__) \
} }
const abc_t pre_builds[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(ABC_SET_A(8), ABC_SET_B(4)), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
我个人不喜欢在宏变量初始化中写(abc_t)
复合文字。他们让 abc_t *arr = (abc_t[]){
ABC_SET_A(8) }
之类的事情变得不可能。如果用户需要,我更喜欢写 &(abc_t)
,这样其他人就会知道什么是指针,什么不是指针,这样他们就知道何时以及如何分配内存,内存的存储持续时间是多少内存等。它还使宏易于在 C++ 和 C99 中工作。像这样:
#define ABC_SET_A(n) { .kind = A, .u.a_ = n }
#define ABC_SET_B(n) { .kind = B, .u.b_ = n }
#define ABC_SET_C(...) { .kind = C, .u.c_ = __VA_ARGS__ }
const abc_t pre_builds[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C((abc_t*[]){ &(abc_t)ABC_SET_A(8), &(abc_t)ABC_SET_B(4) }), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
// it also allows for something like the following naturally to work:
abc_t *make_me_visible_in_debugger[] = {
&(abc_t)ABC_SET_A(8),
&(abc_t)ABC_SET_B(4)
};
const abc_t pre_builds2[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(make_me_visible_in_debugger), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
我有一个小的递归 AST 如下:
enum ABC
{
A,
B,
C
};
typedef struct abc_t
{
enum ABC kind;
union {
struct a_
{
int x;
} a_;
struct b_
{
int y;
} b_;
struct c_
{
struct abc_t **array;
} c_;
} u;
} abc_t;
为了简化构造,我定义了填充宏:
#define ABC_SET_A(n) (abc_t) { .kind = A, .u.a_ = { n } }
#define ABC_SET_B(n) (abc_t) { .kind = B, .u.b_ = { n } }
const abc_t bar = ABC_SET_A(5);
const abc_t foo = ABC_SET_B(2);
但是,我不知道如何用宏填充我的 AST 的 "c_" 分支。我想得出这样的结果:
const abc_t test = ABC_SET_C({ABC_SET_B(4), ABC_SET_A(3), ABC_SET_B(2)});
// or
const abc_t test = ABC_SET_C(ABC_SET_B(4), ABC_SET_A(3), ABC_SET_B(2)); // With variadics arguments
目标是拥有一个 abc_t
的不可变数组,代表一组预构建的结构,例如:
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C({ABC_SET_A(8), ABC_SET_B(4)}), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
...
};
我不知道如何处理 such 宏的递归结构,尤其是在这种情况下的数组。 我该怎么做?
首先修正你的 ABC_SET_A
和 ABC_SET_B
。你不用像 int var = { 1 };
这样的大括号初始化 int
,你像 int var = 1;
:
int
#define ABC_SET_A(n) (abc_t){ .kind = A, .u.a_ = n }
#define ABC_SET_B(n) (abc_t){ .kind = B, .u.b_ = n }
ABC_SET_C
很容易。当你的结构中有指针数组时,你可以这样做:
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) __VA_ARGS__ }
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
// note - as this is array of pointers, I added & to get the pointer
ABC_SET_C({&ABC_SET_A(8), &ABC_SET_B(4)}), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
无论如何,{
}
大括号对预处理器来说并不特殊,也不会被它解析。我更喜欢:
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) { __VA_ARGS__ } }
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(&ABC_SET_A(8), &ABC_SET_B(4)), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
如果你想摆脱在每个 ABC_SET_*
前面写 &
,那么你可以在参数数量上重载宏,并为每个参数插入它们。我发现它不值得付出努力,但它最多可以为 3 个参数实现:
#define ABC_SET_A(n) ((abc_t){ .kind = A, .u.a_ = n })
#define ABC_SET_B(n) ((abc_t){ .kind = B, .u.b_ = n })
#define ABC_SET_C_1(_1) &_1
#define ABC_SET_C_2(_1,_2) &_1, &_2
#define ABC_SET_C_3(_1,_2,_3) &_1, &_2, &_3
#define ABC_SET_C_N(_1,_2,_3,N,...) ABC_SET_C##N
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) { \
ABC_SET_C_N(__VA_ARGS__,_3,_2,_1)(__VA_ARGS__) \
} }
const abc_t pre_builds[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(ABC_SET_A(8), ABC_SET_B(4)), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
我个人不喜欢在宏变量初始化中写(abc_t)
复合文字。他们让 abc_t *arr = (abc_t[]){
ABC_SET_A(8) }
之类的事情变得不可能。如果用户需要,我更喜欢写 &(abc_t)
,这样其他人就会知道什么是指针,什么不是指针,这样他们就知道何时以及如何分配内存,内存的存储持续时间是多少内存等。它还使宏易于在 C++ 和 C99 中工作。像这样:
#define ABC_SET_A(n) { .kind = A, .u.a_ = n }
#define ABC_SET_B(n) { .kind = B, .u.b_ = n }
#define ABC_SET_C(...) { .kind = C, .u.c_ = __VA_ARGS__ }
const abc_t pre_builds[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C((abc_t*[]){ &(abc_t)ABC_SET_A(8), &(abc_t)ABC_SET_B(4) }), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
// it also allows for something like the following naturally to work:
abc_t *make_me_visible_in_debugger[] = {
&(abc_t)ABC_SET_A(8),
&(abc_t)ABC_SET_B(4)
};
const abc_t pre_builds2[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(make_me_visible_in_debugger), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};