如何在编译时动态创建结构体枚举 [英] How to create an enum dynamically in compiling time for my struct

查看:136
本文介绍了如何在编译时动态创建结构体枚举的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的结构如下

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数组,像这样:

I wish I to create an array of foo_t in compiling time through defines, like this:

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在编译时是不可能的,那么您对我有什么建议呢?

If this was not possible with just C and cmake in compiling time, what do you suggest for me to make this work?

推荐答案

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

What I'm going to suggest you is something I actually have seen in a real big production project. I have to say it because I admit it is not a nice looking solution.

首先,您需要将所有宏调用放在一个文件中。您可以为其指定名称和扩展名:例如经典的 .h 扩展名或具有描述性扩展名的内容,例如 .def

First of all you need to put all your macro invocations in a single file. You can give it the name and the extension you want: for example the classical .h extension or something with a descriptive extension such as .def.

因此, PreprocessorTypePopulation.h 可以定义如下:

So, PreprocessorTypePopulation.h can be defined as follow:

FOO_CREATE(my_name, my_validate, 0, 0)
FOO_CREATE(my_name2, NULL, 0, 0)

它包含所有调用的 FOO_CREATE 宏。

注意:每次宏调用后都没有逗号或分号。在这种情况下,也可以使用逗号(从宏中删除它们)实现(因为仅涉及枚举项和数组元素)。

Note: no commas or semicolons after each macro invocation. Also an implementation with commas (removing them from macros) would have worked in this case (because only enum items and array elements were involved).

这可以是 .h 文件。在我的示例中,它是C文件,其中包含伪指令 main()。我只是将OP的int类型转换为 stdint.h 中包含的类型。

This can be a .h file. In my example it is the C file containing a dummy demonstrative main(). I just converted OP's int types to those contained in 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;
}

如您所见,已实施以下策略:

As you can see, the following strategy is implemented:


  1. Undef 任何先前的 FOO_CREATE()定义

  2. 定义 FOO_CREATE()宏用于第一个任务(枚举代)

  3. 包括 .def 文件 INSIDE 枚举。 FOO_CREATE() s的序列将用于根据刚刚定义的宏生成枚举项

  4. 再次取消宏定义,并将其重新定义为第二个任务(结构定义的数组)

  5. 包含 .def 文件 INSIDE >数组定义。 FOO_CREATE() s的序列将用于根据刚刚定义的宏生成数组元素

  1. Undef any previous FOO_CREATE() definition
  2. Define the FOO_CREATE() macro for the first task (the enumerative generation)
  3. Include the .def file INSIDE the enum. The sequence of FOO_CREATE()s will be used to generate the enum items according to the just defined macro
  4. Undef the macro again, and redefine it for the second task (the array of structs definition)
  5. Include the .def file INSIDE the array definition. The sequence of FOO_CREATE()s will be used to generate the array elements according to the just defined macro

-

我使用 preprocessor-only 选项进行编译,对于我来说,

I compiled with preprocessor-only option, in my case with

gcc PreprocessorTypePopulation.c -E -P 

-P 选项从输出中删除 linemarkers ),然后获得以下输出(我刚刚删除了与所含标准有关的所有内容标头):

(-P option removes linemarkers from the output) and then I obtained the following output (I just removed all the stuff related to the included standard 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;
}

-

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

In conclusion, it's not for sure a nice looking solution. But it works, and it prevents a lot of human mistakes concentrating several definitions in a single file. In a long term big project this can save weeks of work.

这篇关于如何在编译时动态创建结构体枚举的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆