首页 > 解决方案 > 如何在编译时为我的结构动态创建枚举

问题描述

我在下面有这个结构

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 是不可能的,那么您对我有什么建议来完成这项工作?

标签: cstructenumscompilationc-preprocessor

解决方案


我要建议你的是我在一个真正的大型生产项目中实际看到的东西。我不得不说,因为我承认这不是一个好看的解决方案。


调用了所有宏的文件

首先,您需要将所有宏调用放在一个文件中。您可以为其指定名称和所需的扩展名:例如经典.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;
}

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

  1. 取消任何先前的FOO_CREATE()定义
  2. 为第一个任务定义FOO_CREATE()宏(枚举生成)
  3. 在枚举中包含.def文件。s的序列将用于根据刚刚定义的宏生成枚举项FOO_CREATE()
  4. 再次取消宏定义,并为第二个任务重新定义它(结构体定义的数组)
  5. 在数组定义中包含.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;
}

--

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


推荐阅读