Search code examples
cmacrosc-preprocessorx-macros

Combine different arity X-macros


I am defining a lexer for a programming language. Part of this involves having a table of keywords and tokens:

#define FOREACH_KEYWORD(V)                                                     \
  V(And, and)                                                                  \
  V(Else, else)                                                                \
  V(False, false)                                                              \
  V(If, if)                                                                    \
  V(Or, or)                                                                    \
  V(True, true)

#define FOREACH_TOKEN(V)                                                       \
  V(Plus)                                                                      \
  V(Minus)                                                                     \
  V(Times)                                                                     \
  V(Div)                                                                       \
  FOREACH_KEYWORD(V)

These macros are intended to be used like so:

const char *kTokenTypeNames[] = {
#define STR(NAME) #NAME,
  FOREACH_TOKEN(STR)
#undef STR
};

// which would ideally expand to

const char *kTokenTypeNames[] = {
 "Plus", "Minus", "Times", "Div", "And", "Else", "False", "If", "Or", "True",
};

And the keywords should be included in the token list. The above pseudocode does not work because the two different V macro parameters have different arities. I would like any macro that has to deal with tokens to only need to take 1 parameter, and any macro that deals with keywords to need to take 2. I'd rather not make everything variadic. I feel like it should be possible to add some interstitial macros to make this combination possible, but I have as of yet not been clever enough to make this happen.

I suppose, in general: I would like to be able to take an X-macro of arity N and reduce it to some M where M is smaller than N.

How can I achieve this?


Solution

  • Given your specs I think this is what you're looking for.

    #define AB_TO_A(A,B) (A)
    #define EVAL(...) __VA_ARGS__
    
    #define FOREACH_KEYWORD(V) \
      V(And, and)     \
      V(Else, else)   \
      V(False, false) \
      V(If, if)       \
      V(Or, or)       \
      V(True, true)
    
    #define FOREACH_TOKEN(V) \
      V(Plus)   \
      V(Minus)  \
      V(Times)  \
      V(Div)    \
      EVAL(FOREACH_KEYWORD(V AB_TO_A))
    
    const char *kTokenTypeNames[] = {
    #define STR(NAME) #NAME,
      FOREACH_TOKEN(STR)
    #undef STR
    };
    

    Coliru demo here.