How to create an enum dynamically in compiling time for my struct

ε祈祈猫儿з 提交于 2020-04-16 02:32:05

问题


I have this struct below

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;

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

and in compiling time the result be:

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

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


回答1:


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


A file with all the macros invoked

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.

So, PreprocessorTypePopulation.h can be defined as follow:

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

It contains all the FOO_CREATE macro invoked.

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).

The file containing the generated struct/enums:

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

--

The output

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

gcc PreprocessorTypePopulation.c -E -P 

(-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.



来源:https://stackoverflow.com/questions/60765210/how-to-create-an-enum-dynamically-in-compiling-time-for-my-struct

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!