如何在 C 中创建结构文字的数组文字?
How to create an array literal of struct literals in C?
我的目标是自动生成一个老式的基于知识的系统的控制逻辑,它本质上是一系列数百个结构相似的 if-then 语句。我们还想以一种反射的方式了解这些语句的属性,数组文字有助于生成这些语句。
我在这里查看了很多关于 SO 的答案,但其中大部分是关于字符串文字数组的,这不是我想要完成的。
我正在尝试制作一个 X 宏,其中包含一个结构文字数组作为其参数之一,可用于初始化堆栈分配的数组变量。
下面是一些无法编译的代码,显示了我的尝试:
#include <stdio.h>
typedef enum qty_t {
qty_None, qty_Some, qty_Many
} qty_t;
typedef enum arg_t {
arg_X, arg_Y, arg_Z
} arg_t;
typedef struct action_t {
int id;
qty_t quantity;
arg_t argument;
} action_t;
/* a change is a sequence of actions, and a change identifier */
/* the outermost {} are the array, the inner ones being the structs */
#define CHANGES \
CHANGE(1, ({{.id=1,.quantity=qty_None,.argument=arg_X}, \
{.id=2,.quantity=qty_Many,.argument=arg_Z}\
}\
)\
)\
CHANGE(2, ({{1,qty_Some,arg_Y}}))
int main(void) {
// your code goes here
#define CHANGE(change_id, actions) \
action_t current_actions ## change_id[] = actions;
CHANGES
#undef CHANGE
return 0;
}
如何将结构文字的数组文字放入 CHANGE 宏中,特别是可用于初始化 current_actions1 变量的数组文字?
那个呢:
#define CHANGES \
CHANGE(1, {{.id=1,.quantity=qty_None,.argument=arg_X}, \
{.id=2,.quantity=qty_Many,.argument=arg_Z}\
}\
\
)\
CHANGE(2, {{.id=1,.quantity=qty_Some,.argument=arg_Y}})
int main(void) {
// your code goes here
#define CHANGE(change_id, actions ...) \
action_t current_actions ## change_id[] = actions;
CHANGES
#undef CHANGE
return 0;
}
我删除了 CHANGES 中的括号并在 CHANGE
中添加了 ...
编译和 gcc -E foo.c
给出:
int main(void) {
action_t current_actions1[] = {{.id=1,.quantity=qty_None,.argument=arg_X}, {.id=2,.quantity=qty_Many,.argument=arg_Z} }; action_t current_actions2[] = {{.id=1,.quantity=qty_Some,.argument=arg_Y}};
return 0;
}
没有 "an array of struct literals" 这样的东西,不管它本身是不是文字。数组的元素可以是结构,您可以使用结构文字来初始化结构数组的元素(糟糕),包括在数组文字中,但数组的元素本身不是文字。这听起来可能——实际上,可能是——迂腐,但使用一致和正确的术语可以提高思路和沟通的清晰度。
事实上,您根本没有使用结构文字或数组文字。您只是在结构数组的初始化程序中使用 struct initializers 。这已经稍微简化了问题。
您的代码的第一个问题是您在宏定义 CHANGE
中放置的括号,围绕着数组初始值设定项。这些是每次调用 CHANGE
宏的第二个参数的一部分,因此,它们作为宏 CHANGES
的替换文本的一部分发出。但它们不是初始化程序允许的语法的一部分,因此生成的代码无效。
我假设括号首先是为了避免初始化器中的逗号被解释为分隔宏参数。至少在这种情况下,解决该问题的另一种方法是使 CHANGE
宏可变参数的定义:
#define CHANGE(change_id, ...) \
action_t current_actions ## change_id[] = __VA_ARGS__;
这解决了我的问题,使用 C99 中引入的可变参数宏的标准形式。
另一种更结构化的替代方法是为结构初始值设定项添加宏,这样内部和之间的逗号就不会影响宏参数识别。例如,
#define FIRST_ACTION(i,q,a) {.id=i,.quantity=q,.argument=a}
#define ACTION(i,q,a) ,FIRST_ACTION(i,q,a)
#define CHANGES \
CHANGE(1, {\
FIRST_ACTION(1,qty_None,arg_X)\
ACTION(2,qty_Many,arg_Z)\
})\
CHANGE(2, {\
FIRST_ACTION(1,qty_Some,arg_Y)\
})
// ...
#define CHANGE(change_id, actions) \
action_t current_actions ## change_id[] = actions;
CHANGES
当然,所有这些都假设在您的案例中可以从 X 宏方法中获得一些好的价值。这至少需要在代码的其他地方再次使用 CHANGES
宏。 None 这在您展示的内容中很明显,但可以省略。但是,我并不清楚您的特定 CHANGES
宏是否提供了其他方法无法更好地提供的其他用途。
我的目标是自动生成一个老式的基于知识的系统的控制逻辑,它本质上是一系列数百个结构相似的 if-then 语句。我们还想以一种反射的方式了解这些语句的属性,数组文字有助于生成这些语句。
我在这里查看了很多关于 SO 的答案,但其中大部分是关于字符串文字数组的,这不是我想要完成的。
我正在尝试制作一个 X 宏,其中包含一个结构文字数组作为其参数之一,可用于初始化堆栈分配的数组变量。
下面是一些无法编译的代码,显示了我的尝试:
#include <stdio.h>
typedef enum qty_t {
qty_None, qty_Some, qty_Many
} qty_t;
typedef enum arg_t {
arg_X, arg_Y, arg_Z
} arg_t;
typedef struct action_t {
int id;
qty_t quantity;
arg_t argument;
} action_t;
/* a change is a sequence of actions, and a change identifier */
/* the outermost {} are the array, the inner ones being the structs */
#define CHANGES \
CHANGE(1, ({{.id=1,.quantity=qty_None,.argument=arg_X}, \
{.id=2,.quantity=qty_Many,.argument=arg_Z}\
}\
)\
)\
CHANGE(2, ({{1,qty_Some,arg_Y}}))
int main(void) {
// your code goes here
#define CHANGE(change_id, actions) \
action_t current_actions ## change_id[] = actions;
CHANGES
#undef CHANGE
return 0;
}
如何将结构文字的数组文字放入 CHANGE 宏中,特别是可用于初始化 current_actions1 变量的数组文字?
那个呢:
#define CHANGES \
CHANGE(1, {{.id=1,.quantity=qty_None,.argument=arg_X}, \
{.id=2,.quantity=qty_Many,.argument=arg_Z}\
}\
\
)\
CHANGE(2, {{.id=1,.quantity=qty_Some,.argument=arg_Y}})
int main(void) {
// your code goes here
#define CHANGE(change_id, actions ...) \
action_t current_actions ## change_id[] = actions;
CHANGES
#undef CHANGE
return 0;
}
我删除了 CHANGES 中的括号并在 CHANGE
中添加了...
编译和 gcc -E foo.c
给出:
int main(void) {
action_t current_actions1[] = {{.id=1,.quantity=qty_None,.argument=arg_X}, {.id=2,.quantity=qty_Many,.argument=arg_Z} }; action_t current_actions2[] = {{.id=1,.quantity=qty_Some,.argument=arg_Y}};
return 0;
}
没有 "an array of struct literals" 这样的东西,不管它本身是不是文字。数组的元素可以是结构,您可以使用结构文字来初始化结构数组的元素(糟糕),包括在数组文字中,但数组的元素本身不是文字。这听起来可能——实际上,可能是——迂腐,但使用一致和正确的术语可以提高思路和沟通的清晰度。
事实上,您根本没有使用结构文字或数组文字。您只是在结构数组的初始化程序中使用 struct initializers 。这已经稍微简化了问题。
您的代码的第一个问题是您在宏定义 CHANGE
中放置的括号,围绕着数组初始值设定项。这些是每次调用 CHANGE
宏的第二个参数的一部分,因此,它们作为宏 CHANGES
的替换文本的一部分发出。但它们不是初始化程序允许的语法的一部分,因此生成的代码无效。
我假设括号首先是为了避免初始化器中的逗号被解释为分隔宏参数。至少在这种情况下,解决该问题的另一种方法是使 CHANGE
宏可变参数的定义:
#define CHANGE(change_id, ...) \
action_t current_actions ## change_id[] = __VA_ARGS__;
这解决了我的问题,使用 C99 中引入的可变参数宏的标准形式。
另一种更结构化的替代方法是为结构初始值设定项添加宏,这样内部和之间的逗号就不会影响宏参数识别。例如,
#define FIRST_ACTION(i,q,a) {.id=i,.quantity=q,.argument=a}
#define ACTION(i,q,a) ,FIRST_ACTION(i,q,a)
#define CHANGES \
CHANGE(1, {\
FIRST_ACTION(1,qty_None,arg_X)\
ACTION(2,qty_Many,arg_Z)\
})\
CHANGE(2, {\
FIRST_ACTION(1,qty_Some,arg_Y)\
})
// ...
#define CHANGE(change_id, actions) \
action_t current_actions ## change_id[] = actions;
CHANGES
当然,所有这些都假设在您的案例中可以从 X 宏方法中获得一些好的价值。这至少需要在代码的其他地方再次使用 CHANGES
宏。 None 这在您展示的内容中很明显,但可以省略。但是,我并不清楚您的特定 CHANGES
宏是否提供了其他方法无法更好地提供的其他用途。