如何在编译时为我的结构动态创建枚举
How to create an enum dynamically in compiling time for my struct
我下面有这个结构
struct foo {
char *name;
int (*validate)(u8_t *data, size_t size);
u8_t value;
u8_t changed;
foo_id id;
};
typedef struct foo foo_t;
我希望在编译时通过定义创建一个foo_t的数组,像这样:
int my_validate(u8_t *data, size_t size) {...}
FOO_CREATE(my_name, my_validate, 0, 0);
FOO_CREATE(my_name2, NULL, 0, 0);
编译时结果为:
enum {
MY_NAME_FOO = 0,
MY_NAME2_FOO,
FOO_COUNT
} foo_id;
static foo_t foo[FOO_COUNT] = {
{
.name = "my_name",
.validate = my_validate,
.value = 0,
.changed = 0,
.id = MY_NAME_FOO
},
{
.name = "my_name2",
.validate = NULL,
.value = 0,
.changed = 0,
.id = MY_NAME2_FOO
},
}
如果在编译时仅使用 C 和 cmake 是不可能的,你对我有什么建议来完成这项工作?
我要建议你的是我在一个真正的大型制作项目中实际看到的东西。我不得不说,因为我承认这不是一个 漂亮的 解决方案。
一个调用了所有宏的文件
首先,您需要将所有宏调用放在一个文件中。您可以为其指定所需的名称和扩展名:例如经典的 .h
扩展名或带有描述性扩展名的名称,例如 .def
.
所以,PreprocessorTypePopulation.h可以定义如下:
FOO_CREATE(my_name, my_validate, 0, 0)
FOO_CREATE(my_name2, NULL, 0, 0)
它包含调用的所有 FOO_CREATE
宏。
注意:每个宏调用后没有逗号或分号。在这种情况下,使用逗号(从宏中删除它们)的实现也可以工作(因为只涉及枚举项和数组元素)。
包含生成的文件 struct/enums:
这可以是 .h
文件。在我的示例中,它是包含虚拟指示符 main()
的 C 文件。我刚刚将 OP 的 int 类型转换为 stdint.h
.
中包含的类型
#include <stddef.h>
#include <stdint.h>
#ifdef FOO_CREATE
#undef FOO_CREATE
#endif
/* Enum creation macro */
#define FOO_CREATE(nm,func,val,chgd) nm##_FOO,
typedef enum {
#include "PreprocessorTypePopulation.h"
FOO_COUNT
} foo_id;
struct foo {
char *name;
int (*validate)(uint8_t *data, size_t size);
uint8_t value;
uint8_t changed;
foo_id id;
};
typedef struct foo foo_t;
int my_validate(uint8_t *data, size_t size)
{
return 0;
}
#undef FOO_CREATE
/* Array creation macro */
#define FOO_CREATE(nm,func,val,chgd) \
{ \
.name = (char *) #nm, \
.validate = func, \
.value = val, \
.changed = chgd, \
.id = nm##_FOO \
},
static foo_t foo[FOO_COUNT] = {
#include "PreprocessorTypePopulation.h"
};
int main(void)
{
return 0;
}
如您所见,实现了以下策略:
- Undef 任何先前的
FOO_CREATE()
定义
- 为第一个任务(枚举生成)定义
FOO_CREATE()
宏
- 包括
.def
文件 INSIDE 枚举。 FOO_CREATE()
的序列将用于根据刚刚定义的宏 生成枚举项
- 再次取消定义宏,并为第二个任务(结构定义数组)重新定义它
- 包含
.def
文件 INSIDE 数组定义。 FOO_CREATE()
的序列将用于根据刚刚定义的宏 生成数组元素
--
输出
我使用 preprocessor-only 选项编译,在我的例子中使用
gcc PreprocessorTypePopulation.c -E -P
(-P
选项从输出中删除了 linemarkers) 然后我得到了以下输出(我刚刚删除了所有与包含的标准相关的东西 headers):
typedef enum {
my_name_FOO,
my_name2_FOO,
FOO_COUNT
} foo_id;
struct foo {
char *name;
int (*validate)(short *data, int size);
short value;
short changed;
foo_id id;
};
typedef struct foo foo_t;
int my_validate(short *data, int size)
{
return 0;
}
static foo_t foo[FOO_COUNT] = {
{ .name = "my_name", .validate = my_validate, .value = 0, .changed = 0, .id = my_name_FOO },
{ .name = "my_name2", .validate = NULL, .value = 0, .changed = 0, .id = my_name2_FOO },
}
int main(void)
{
return 0;
}
--
总之,这肯定不是一个好看的解决方案。但是 它有效 ,并且它防止了很多将多个定义集中在一个文件中的人为错误。在长期的大项目中,这可以节省数周的工作时间。
我下面有这个结构
struct foo {
char *name;
int (*validate)(u8_t *data, size_t size);
u8_t value;
u8_t changed;
foo_id id;
};
typedef struct foo foo_t;
我希望在编译时通过定义创建一个foo_t的数组,像这样:
int my_validate(u8_t *data, size_t size) {...}
FOO_CREATE(my_name, my_validate, 0, 0);
FOO_CREATE(my_name2, NULL, 0, 0);
编译时结果为:
enum {
MY_NAME_FOO = 0,
MY_NAME2_FOO,
FOO_COUNT
} foo_id;
static foo_t foo[FOO_COUNT] = {
{
.name = "my_name",
.validate = my_validate,
.value = 0,
.changed = 0,
.id = MY_NAME_FOO
},
{
.name = "my_name2",
.validate = NULL,
.value = 0,
.changed = 0,
.id = MY_NAME2_FOO
},
}
如果在编译时仅使用 C 和 cmake 是不可能的,你对我有什么建议来完成这项工作?
我要建议你的是我在一个真正的大型制作项目中实际看到的东西。我不得不说,因为我承认这不是一个 漂亮的 解决方案。
一个调用了所有宏的文件
首先,您需要将所有宏调用放在一个文件中。您可以为其指定所需的名称和扩展名:例如经典的 .h
扩展名或带有描述性扩展名的名称,例如 .def
.
所以,PreprocessorTypePopulation.h可以定义如下:
FOO_CREATE(my_name, my_validate, 0, 0)
FOO_CREATE(my_name2, NULL, 0, 0)
它包含调用的所有 FOO_CREATE
宏。
注意:每个宏调用后没有逗号或分号。在这种情况下,使用逗号(从宏中删除它们)的实现也可以工作(因为只涉及枚举项和数组元素)。
包含生成的文件 struct/enums:
这可以是 .h
文件。在我的示例中,它是包含虚拟指示符 main()
的 C 文件。我刚刚将 OP 的 int 类型转换为 stdint.h
.
#include <stddef.h>
#include <stdint.h>
#ifdef FOO_CREATE
#undef FOO_CREATE
#endif
/* Enum creation macro */
#define FOO_CREATE(nm,func,val,chgd) nm##_FOO,
typedef enum {
#include "PreprocessorTypePopulation.h"
FOO_COUNT
} foo_id;
struct foo {
char *name;
int (*validate)(uint8_t *data, size_t size);
uint8_t value;
uint8_t changed;
foo_id id;
};
typedef struct foo foo_t;
int my_validate(uint8_t *data, size_t size)
{
return 0;
}
#undef FOO_CREATE
/* Array creation macro */
#define FOO_CREATE(nm,func,val,chgd) \
{ \
.name = (char *) #nm, \
.validate = func, \
.value = val, \
.changed = chgd, \
.id = nm##_FOO \
},
static foo_t foo[FOO_COUNT] = {
#include "PreprocessorTypePopulation.h"
};
int main(void)
{
return 0;
}
如您所见,实现了以下策略:
- Undef 任何先前的
FOO_CREATE()
定义 - 为第一个任务(枚举生成)定义
FOO_CREATE()
宏 - 包括
.def
文件 INSIDE 枚举。FOO_CREATE()
的序列将用于根据刚刚定义的宏 生成枚举项
- 再次取消定义宏,并为第二个任务(结构定义数组)重新定义它
- 包含
.def
文件 INSIDE 数组定义。FOO_CREATE()
的序列将用于根据刚刚定义的宏 生成数组元素
--
输出
我使用 preprocessor-only 选项编译,在我的例子中使用
gcc PreprocessorTypePopulation.c -E -P
(-P
选项从输出中删除了 linemarkers) 然后我得到了以下输出(我刚刚删除了所有与包含的标准相关的东西 headers):
typedef enum {
my_name_FOO,
my_name2_FOO,
FOO_COUNT
} foo_id;
struct foo {
char *name;
int (*validate)(short *data, int size);
short value;
short changed;
foo_id id;
};
typedef struct foo foo_t;
int my_validate(short *data, int size)
{
return 0;
}
static foo_t foo[FOO_COUNT] = {
{ .name = "my_name", .validate = my_validate, .value = 0, .changed = 0, .id = my_name_FOO },
{ .name = "my_name2", .validate = NULL, .value = 0, .changed = 0, .id = my_name2_FOO },
}
int main(void)
{
return 0;
}
--
总之,这肯定不是一个好看的解决方案。但是 它有效 ,并且它防止了很多将多个定义集中在一个文件中的人为错误。在长期的大项目中,这可以节省数周的工作时间。