I'm currently working on a code base with an extreme amount of enums. There is a need to convert enum
values to and from string representations. To do this there is a bunch of helper macros that operates on macros on the form
#define THINGS_MAP(G, P) \
G(LAMP, P) \
G(DESK, P)
And elsewhere in the code there are macros that works roughly like this:
#define MK_ENUM(SYM, P) P##_##SYM,
enum { THINGS_MAP(MK_ENUM, THING) }; /* expands to { THING_LAMP, THING_DESK, } */
I would like to avoid changing MK_ENUM
and the call to THINGS_MAP
inside the enum.
However, I now have one particularly long enum
list, where I also need to add some classification to the entries. So I was thinking about doing something like
#define THINGS_PROP_MAP(G, P) \
G(LAMP, WOODEN, P) \
G(DESK, METALIC, P)
However, since all my helper macros require two arguments passed, not three, I was thinking if it would be possible to map some kind of macro that drops a parameter. Is this possible?
What I am looking for is something along the lines of:
#define THINGS_PROP_MAP(G, P) \
G(LAMP, WOODEN, P) \
G(DESK, METALIC, P)
/* DROP_PROP should create G(LAMP, P) G(DESK, P) */
#define THINGS_MAP(G, P) DROP_PROP(THINGS_PROP_MAP)
One idea I had was to add an extra transformation step, but I didn't get it to work
#define THINGS_PROP_MAP(G, T, P) \
G(T(LAMP, WOODEN, P)) \
G(T(DESK, METALIC, P))
#define DROP_PROP(SYM, _ , P) SYM, P
#define THING_MAP(G, P) THING_PROP_MAP(G, DROP_PROP, P)
And yes, I know this is waaay beyond macro abuse etc :-)
Your macros are fine but your #define MK_ENUM(SYM, P) P##_##SYM,
expects two parameters where this call G(T(LAMP, WOODEN, P))
for example with G = MK_ENUM
invokes G
with just one parameter even though the inner statement would expand to LAMP, P
.
To force the preprocessor to do one additional expansion step and recognize that LAMP, P
is not a single token you must add:
#define MK_ENUM_EXPAND(...) MK_ENUM(__VA_ARGS__)
and then use:
enum { THING_MAP(MK_ENUM_EXPAND, THING) };
Further notice that your macro doesn't expand to enum { THING_LAMP, THING_DESK };
but with a comma at the end to enum { THING_LAMP, THING_DESK, };
.
Full working code:
#define MK_ENUM(SYM, P) P##_##SYM,
#define MK_ENUM_EXPAND(...) MK_ENUM(__VA_ARGS__)
#define THINGS_PROP_MAP(G, T, P) \
G(T(LAMP, WOODEN, P)) \
G(T(DESK, METALIC, P))
#define DROP_PROP(SYM, _ , P) SYM, P
#define THINGS_MAP(G, P) THINGS_PROP_MAP(G, DROP_PROP, P)
enum { THINGS_MAP(MK_ENUM_EXPAND, THING) }; /* expands to { THING_LAMP, THING_DESK, } */
To test macro expansion it is useful to use gcc with the command line argument -E
:
$ gcc -E srcFile.c
because your're getting concrete error messages and understand what's going wrong.
If you don't want to change MK_ENUM
and the call to THINGS_MAP
inside the enum you can do the following where I just concatenate G##S
in THINGS_PROP_MAP
where G = MK_ENUM
and S = _EXPAND
:
#define MK_ENUM(SYM, P) P##_##SYM,
#define MK_ENUM_EXPAND(...) MK_ENUM(__VA_ARGS__)
#define THINGS_PROP_MAP(G, S, T, P) \
G##S(T(LAMP, WOODEN, P)) \
G##S(T(DESK, METALIC, P))
#define DROP_PROP(SYM, _ , P) SYM, P
#define THINGS_MAP(G, P) THINGS_PROP_MAP(G, _EXPAND, DROP_PROP, P)
enum { THINGS_MAP(MK_ENUM, THING) }; /* expands to { THING_LAMP, THING_DESK, } */
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With