使用可变宏的结构初始化数组
Array of struct initialization using variadic macro
我有以下类型定义:
typedef struct {
int (*function)(int argc, char *argv[]);
char *name;
} command_t;
成员function
是一个函数指针,成员name
是一个字符串,它将存储函数的名称。
为了初始化command_t
类型的变量,我写了下面的宏:
#define COMMAND(x) (command_t){.function = x, .name = #x}
这是我目前如何初始化 command_t
的数组:
int ls(int argc, char *argv[]);
int echo(int argc, char *argv[]);
int cat(int argc, char *argv[]);
int mkdir(int argc, char *argv[]);
command_t cmd_list[] = {COMMAND(ls), COMMAND(echo), COMMAND(cat), COMMAND(mkdir)};
我希望能够这样初始化一个 command_t
的数组:
command_t cmd_list[] = COMMAND(ls, echo, cat, mkdir);
或
command_t cmd_list[] = {COMMAND(ls, echo, cat, mkdir)};
我知道 COMMAND
必须是可变参数宏才能这样做,但我不知道如何编写它。
//stringification of the argument Arg
#define PP_STRINGIFY(Arg) PP_STRINGIFY_(Arg)
#define PP_STRINGIFY_(Arg) #Arg
//concatenation of the two arguments
#define PP_CAT2(_1, _2) PP_CAT_(_1, _2)
#define PP_CAT_(_1, _2) _1##_2
//enumerate the number of arguments (min:1, max: 8)
#define PP_VA_NUM_ARGS(...) PP_VA_NUM_ARGS_(__VA_ARGS__,8,7,6,5,4,3,2,1)
#define PP_VA_NUM_ARGS_(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
//a single command initializer
#define COMMAND(x) (command_t){.function = x, .name = PP_STRINGIFY(x)}
//command list initializer
#define COMMAND_LST(...) { PP_CAT2(COMMAND_LST_,PP_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) }
//implement as many as you need
//maybe you need to extend PP_VA_NUM_ARGS (current max limit 8)
#define COMMAND_LST_1(_1) COMMAND(_1)
#define COMMAND_LST_2(_1,_2) COMMAND(_1), COMMAND(_2)
#define COMMAND_LST_3(_1,_2,_3) COMMAND_LST_2(_1,_2), COMMAND(_3)
#define COMMAND_LST_4(_1,_2,_3,_4) COMMAND_LST_3(_1,_2,_3), COMMAND(_4)
typedef struct {
int (*function)(int argc, char *argv[]);
char *name;
} command_t;
int ls(int argc, char *argv[]);
int echo(int argc, char *argv[]);
int cat(int argc, char *argv[]);
int mkdir(int argc, char *argv[]);
int main()
{
command_t cmd_lst[] = COMMAND_LST(ls, echo, cat, mkdir);
return 0;
}
CPP 输出通过
gcc -E main.c
给你
command_t cmd_lst[] = {
(command_t){.function = ls, .name = "ls"},
(command_t){.function = echo, .name = "echo"},
(command_t){.function = cat, .name = "cat"},
(command_t){.function = mkdir, .name = "mkdir"}
};
我认为您已经接近可读性最强的表格了。尝试创建尽可能简单的代码。因此,您可以为整个初始化列表创建一个宏,而不是发明一些没有 C 程序员理解它做什么的奇怪宏,而不是通过宏的丛林 - 这是相当常见的做法。
即:command_t cmd_lst[] = COMMAND_INIT;
然后:
#define COMMAND(cmd) { .function = cmd, .name = #cmd }
#define COMMAND_INIT { COMMAND(ls), COMMAND(echo), COMMAND(cat), COMMAND(mkdir) }
还有一个选择是 X 宏。它们不一定更具可读性,但可能会减少其他地方的代码重复:
#define FUNC_LIST(X) \
X(ls) \
X(echo) \
X(cat) \
X(mkdir) \
...
#define COMMAND_INIT(func) { .function = func, .name = #func },
command_t cmd_lst[] = { FUNC_LIST(COMMAND_INIT) };
我有以下类型定义:
typedef struct {
int (*function)(int argc, char *argv[]);
char *name;
} command_t;
成员function
是一个函数指针,成员name
是一个字符串,它将存储函数的名称。
为了初始化command_t
类型的变量,我写了下面的宏:
#define COMMAND(x) (command_t){.function = x, .name = #x}
这是我目前如何初始化 command_t
的数组:
int ls(int argc, char *argv[]);
int echo(int argc, char *argv[]);
int cat(int argc, char *argv[]);
int mkdir(int argc, char *argv[]);
command_t cmd_list[] = {COMMAND(ls), COMMAND(echo), COMMAND(cat), COMMAND(mkdir)};
我希望能够这样初始化一个 command_t
的数组:
command_t cmd_list[] = COMMAND(ls, echo, cat, mkdir);
或
command_t cmd_list[] = {COMMAND(ls, echo, cat, mkdir)};
我知道 COMMAND
必须是可变参数宏才能这样做,但我不知道如何编写它。
//stringification of the argument Arg
#define PP_STRINGIFY(Arg) PP_STRINGIFY_(Arg)
#define PP_STRINGIFY_(Arg) #Arg
//concatenation of the two arguments
#define PP_CAT2(_1, _2) PP_CAT_(_1, _2)
#define PP_CAT_(_1, _2) _1##_2
//enumerate the number of arguments (min:1, max: 8)
#define PP_VA_NUM_ARGS(...) PP_VA_NUM_ARGS_(__VA_ARGS__,8,7,6,5,4,3,2,1)
#define PP_VA_NUM_ARGS_(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
//a single command initializer
#define COMMAND(x) (command_t){.function = x, .name = PP_STRINGIFY(x)}
//command list initializer
#define COMMAND_LST(...) { PP_CAT2(COMMAND_LST_,PP_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) }
//implement as many as you need
//maybe you need to extend PP_VA_NUM_ARGS (current max limit 8)
#define COMMAND_LST_1(_1) COMMAND(_1)
#define COMMAND_LST_2(_1,_2) COMMAND(_1), COMMAND(_2)
#define COMMAND_LST_3(_1,_2,_3) COMMAND_LST_2(_1,_2), COMMAND(_3)
#define COMMAND_LST_4(_1,_2,_3,_4) COMMAND_LST_3(_1,_2,_3), COMMAND(_4)
typedef struct {
int (*function)(int argc, char *argv[]);
char *name;
} command_t;
int ls(int argc, char *argv[]);
int echo(int argc, char *argv[]);
int cat(int argc, char *argv[]);
int mkdir(int argc, char *argv[]);
int main()
{
command_t cmd_lst[] = COMMAND_LST(ls, echo, cat, mkdir);
return 0;
}
CPP 输出通过
gcc -E main.c
给你
command_t cmd_lst[] = {
(command_t){.function = ls, .name = "ls"},
(command_t){.function = echo, .name = "echo"},
(command_t){.function = cat, .name = "cat"},
(command_t){.function = mkdir, .name = "mkdir"}
};
我认为您已经接近可读性最强的表格了。尝试创建尽可能简单的代码。因此,您可以为整个初始化列表创建一个宏,而不是发明一些没有 C 程序员理解它做什么的奇怪宏,而不是通过宏的丛林 - 这是相当常见的做法。
即:command_t cmd_lst[] = COMMAND_INIT;
然后:
#define COMMAND(cmd) { .function = cmd, .name = #cmd }
#define COMMAND_INIT { COMMAND(ls), COMMAND(echo), COMMAND(cat), COMMAND(mkdir) }
还有一个选择是 X 宏。它们不一定更具可读性,但可能会减少其他地方的代码重复:
#define FUNC_LIST(X) \
X(ls) \
X(echo) \
X(cat) \
X(mkdir) \
...
#define COMMAND_INIT(func) { .function = func, .name = #func },
command_t cmd_lst[] = { FUNC_LIST(COMMAND_INIT) };