问题
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:
- Undef any previous
FOO_CREATE()
definition - Define the
FOO_CREATE()
macro for the first task (the enumerative generation) - Include the
.def
file INSIDE the enum. The sequence ofFOO_CREATE()
s will be used to generate the enum items according to the just defined macro - Undef the macro again, and redefine it for the second task (the array of structs definition)
- Include the
.def
file INSIDE the array definition. The sequence ofFOO_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