如何在编译时为我的结构动态创建枚举

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;
}

如您所见,实现了以下策略:

  1. Undef 任何先前的 FOO_CREATE() 定义
  2. 为第一个任务(枚举生成)定义FOO_CREATE()
  3. 包括 .def 文件 INSIDE 枚举。 FOO_CREATE()的序列将用于根据刚刚定义的宏
  4. 生成枚举项
  5. 再次取消定义宏,并为第二个任务(结构定义数组)重新定义它
  6. 包含 .def 文件 INSIDE 数组定义。 FOO_CREATE()的序列将用于根据刚刚定义的宏
  7. 生成数组元素

--

输出

我使用 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;
}

--

总之,这肯定不是一个好看的解决方案。但是 它有效 ,并且它防止了很多将多个定义集中在一个文件中的人为错误。在长期的大项目中,这可以节省数周的工作时间。