cmacros

Combine X macros


is it possible in C to combine lists of macros to generate an enum with every possible combination? Example:

#define REMAP_LIST R(FOE) R(ANY) R()
#define STATUS_LIST P(TRY) P() P(ABORT)
#define EVENT_LIST X(MOVE) X(HEAL)

Result:

enum e {
    FOE_TRY_MOVE, FOE_TRY_HEAL, FOE_MOVE, FOE_HEAL, FOE_ABORT_MOVE, FOE_ABORT_HEAL,
    ANY_TRY_MOVE, ANY_TRY_HEAL, ANY_MOVE, ANY_HEAL, ANY_ABORT_MOVE, ANY_ABORT_HEAL,
    etc.
};

Defining everything manually would obviously be insane...


Solution

  • As @NateEldredge already mentioned, it would be best to use a bitfield.

    For example:

    struct bitfield {
        unsigned int pov:2;   //2 bits, 2^2 states -> 0: any, 1: foe, 2: me
        unsigned int event:1; //1 bit, 2 states    -> 0: move, 1: heal
        unsigned int step:2;  //2 bits, 2^2 states -> 0: none, 1: try, 2: hold, 3: exec
    };
    

    Then define some constants:

    //character types
    #define ANY 0
    #define FOE 1
    #define ME 2
    
    //actions
    #define MOVE 0
    #define HEAL 1
    
    //state
    #define NONE 0
    #define TRY 1
    #define HOLD 2
    #define EXECUTE 3
    

    Usage:

    //FOE_TRY_MOVE
    struct bitfield b;
    b.pov   = FOE;
    b.event = MOVE;
    b.step  = TRY;
    

    In the comments below you mentioned string maps, here is an example to print a bitfield (similar steps if you want to create a string).

    const char *pov_strings[]   = {"ANY", "FOE", "ME"};
    const char *event_strings[] = {"MOVE", "HEAL"};
    const char *step_strings[]  = {"NONE", "TRY", "HOLD", "EXECUTE"};
    
    void print_bitfield(struct bitfield b)
    {
        printf("%s_%s", pov_strings[b.pov], event_strings[b.event]);
        if (b.step) {
            printf("_%s", step_strings[b.step]);
        }
    }
    

    Of course to answer the question, here the macro version for generating the enumerator list.

    The idea is, that each POV is combined with a set of Events and additional Steps. The assumption is, that each POV has the same Events and Steps.

    //concatenate A and B
    #define CAT_(A, B) A##B
    #define CAT(A, B) CAT_(A, B)
    
    //concat pov and evt with an underscore separator
    //expands to: POV_EVT
    #define POV_EVENT(POV, EVT) \
        CAT(CAT(POV, _), EVT)
    
    //concat pov, evt and step with an underscore separator
    //expands to: POV_EVT_STEP
    #define POV_EVENT_STEP(POV, EVT, STEP) \
        CAT(CAT(POV_EVENT(POV, EVT), _), STEP)
    
    //generates the steps of the combinations of POV_EVT
    #define  EVENT_LIST(POV, EVT) \
        POV_EVENT(POV, EVT), \
        POV_EVENT_STEP(POV, EVT, TRY), \
        POV_EVENT_STEP(POV, EVT, HOLD), \
        POV_EVENT_STEP(POV, EVT, EXECUTE)
    
    //generates the events of POV
    #define POV_LIST(POV) \
        EVENT_LIST(POV, MOVE), \
        EVENT_LIST(POV, HEAL)
    
    //generate the enum
    enum e {
        POV_LIST(ANY),
        POV_LIST(FOE),
        POV_LIST(ME)
    };
    

    which expands to

    enum e {
        ANY_MOVE,
        ANY_MOVE_TRY,
        ANY_MOVE_HOLD,
        ANY_MOVE_EXECUTE,
        ANY_HEAL,
        ANY_HEAL_TRY,
        ANY_HEAL_HOLD,
        ANY_HEAL_EXECUTE,
        FOE_MOVE,
        FOE_MOVE_TRY,
        FOE_MOVE_HOLD,
        FOE_MOVE_EXECUTE,
        FOE_HEAL,
        FOE_HEAL_TRY,
        FOE_HEAL_HOLD,
        FOE_HEAL_EXECUTE,
        ME_MOVE,
        ME_MOVE_TRY,
        ME_MOVE_HOLD,
        ME_MOVE_EXECUTE,
        ME_HEAL,
        ME_HEAL_TRY,
        ME_HEAL_HOLD,
        ME_HEAL_EXECUTE
    };