Search code examples
cmacros

How can I combine two macros to initialize an array of strings in C?


I'm working with two macros in C and would like to combine them to initialize an array of strings. Here are the macros in question:

#define FOREACH_T(ACTION) \
ACTION("T1", 1) \
ACTION("T2", 2) \
ACTION("T3", 3) 

#define FOREACH_K(ACTION) \
ACTION("K1", 17) \
ACTION("K2", 20) \
ACTION("K3", 23) 

My goal is to combine these macros in such a way as to obtain a value array such that, for example, the element at index [1 + 17] is "T1_K1".

I'm a bit at a loss as to how to combine these macros to achieve the desired result. I'd like to get something like this:

const char* value[] = { [1 + 17] = "T1_K1", [1 + 20] = "T1_K2", [1 + 23] = "T1_K3",
                        [2 + 17] = "T2_K1", [2 + 20] = "T2_K2", [2 + 23] = "T2_K3",
                        [3 + 17] = "T3_K1", [3 + 20] = "T3_K2", [3 + 23] = "T3_K3"};
};

Solution

  • If you can change the list macros, then it's simple. Doing it without changing the macros should also be possible, but harder.

    #define FOREACH_T(ACTION, ...) \
        ACTION("T1", 1, __VA_ARGS__) \
        ACTION("T2", 2, __VA_ARGS__) \
        ACTION("T3", 3, __VA_ARGS__) 
    
    #define FOREACH_K(ACTION, ...) \
        ACTION("K1", 17, __VA_ARGS__) \
        ACTION("K2", 18, __VA_ARGS__) \
        ACTION("K3", 19, __VA_ARGS__) 
    
    #define FOO(s, x, s2, x2) [x + x2] = s "_" s2,
    #define FOO2(s, x, ...) FOREACH_T(FOO, s, x)
    
    const char* value[] = {
        FOREACH_K(FOO2,)
    };
    

    Note that this emits warning: initializer overrides prior initialization of this subobject, because e.g. 1+18 == 2+17, and so on.


    Here's the same thing without changing the macro. This uses macro_sequence_for, a library I made for writing preprocessor loops that would otherwise be too tricky.

    Note that with this, you might as well simplify the macros to lists like ("T1",1)("T2",2)("T3",3), no need to accept another macro as the argument. This solution ends up converting the lists to this form first.

    run on gcc.godbolt.org

    #include <macro_sequence_for.h>
    
    #define FOREACH_T(ACTION) \
        ACTION("T1", 1) \
        ACTION("T2", 2) \
        ACTION("T3", 3)
    
    #define FOREACH_K(ACTION) \
        ACTION("K1", 17) \
        ACTION("K2", 18) \
        ACTION("K3", 19)
    
    #define IDENTITY(...) __VA_ARGS__
    #define PARENS(...) (__VA_ARGS__)
    
    #define BODY(n, d, name, value) BODY2(IDENTITY d, name, value)
    #define BODY2(...) BODY3(__VA_ARGS__)
    #define BODY3(name1, value1, name2, value2) [value1 + value2] = name1 "_" name2,
    
    #define OUTER_BODY(n, d, name, value) SF_FOR_EACH0(BODY, SF_STATE, SF_NULL, (name, value), FOREACH_K(PARENS))
    
    const char *value[] = {
        SF_FOR_EACH(OUTER_BODY, SF_NULL, SF_NULL,, FOREACH_T(PARENS))
    };