c - 如何在编译时为我的结构动态创建枚举
问题描述
我在下面有这个结构
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
调用的宏。
注意:每个宏调用后没有逗号或分号。在这种情况下,也可以使用逗号实现(从宏中删除它们)(因为只涉及枚举项和数组元素)。
包含生成的结构/枚举的文件:
这可以是一个.h
文件。在我的示例中,它是包含虚拟演示的 C 文件main()
。我刚刚将 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;
}
如您所见,实现了以下策略:
- 取消任何先前的
FOO_CREATE()
定义 - 为第一个任务定义
FOO_CREATE()
宏(枚举生成) - 在枚举中包含
.def
文件。s的序列将用于根据刚刚定义的宏生成枚举项FOO_CREATE()
- 再次取消宏定义,并为第二个任务重新定义它(结构体定义的数组)
- 在数组定义中包含
.def
文件。s的序列将用于根据刚刚定义的宏生成数组元素FOO_CREATE()
--
输出
我使用仅预处理器选项编译,在我的情况下
gcc PreprocessorTypePopulation.c -E -P
(-P
选项从输出中删除线标记)然后我获得了以下输出(我刚刚删除了与包含的标准头相关的所有内容):
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;
}
--
总之,它肯定不是一个好看的解决方案。但它可以工作,并且它可以防止很多人为错误将多个定义集中在一个文件中。在一个长期的大型项目中,这可以节省数周的工作。
推荐阅读
- android - 临时禁用 WorkManager 的 Workers
- ruby - 使用 Ruby 将 CSV 负载上传到 Google 存储
- firebase - 在 Firestore 中使用身份验证限制特定对象键值
- c# - 如何删除当前用户的 localappdata 中的目录
- c++ - 如何获取双精度的小数部分并将其转换为整数?(c++)
- scripting - Netsuite 已在预定脚本中保存搜索 无结果
- php - laravel 单个帖子未在 livewire 中显示
- ethereum - Solidity ERC-721 错误:返回错误:执行已恢复
- swift - PHImageManager 请求精确大小的图像并不总是有效
- postgresql - 如何使用 database-rider 将 DataSet 插入表中,其列定义为 GENERATED ALWAYS?