Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drop parameters in generator macro

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

like image 703
Kotte Avatar asked Jul 11 '17 07:07

Kotte


1 Answers

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, } */
like image 117
Andre Kampling Avatar answered Sep 28 '22 10:09

Andre Kampling