c++c-preprocessorvariadic-macros

C/C++ preprocessor: extract every second variadic parameter


Is there a way to extract every second parameter from a list of variadic parameters in C/C++ preprocessor?

I would like to write a macro to generate boilerplate code for interface methods in the following way:

#define INTERFACE_FN(_NAME, _X, _Y, _N_PARAMS, ...) \
    void _NAME(__VA_ARGS__) \
    { \
        processing_function(_X, _Y, _N_PARAMS, EVERY_SECOND(__VA_ARGS__); \
    }

void processing_function(int x, int y, int count, ...)
{
    va_list params;
    va_start(params, count);
    while (count--) {
        do_something(va_arg(params, ParentClass*));
    }
    va_end(params);
    // do something else
}

Which would be used as:

INTERFACE_FN(foo, 1, 2, 0);
INTERFACE_FN(bar, 3, 4, 1, ClassX*, x);
INTERFACE_FN(jar, 5, 6, 2, ClassY*, y, ClassZ*, z);

The classes ClassX, ClassY, ClassZ are derivatives of ParentClass;

I'm looking for the EVERY_SECOND macro.


Solution

  • Here is a working example of what you asked for

    #define EVERY_SECOND0(...)
    
    #define EVERY_SECOND1_(second, ...) , second
    #define EVERY_SECOND1(first, ...) EVERY_SECOND1_(__VA_ARGS__)
    
    #define EVERY_SECOND2_(second, ...) , second EVERY_SECOND1(__VA_ARGS__)
    #define EVERY_SECOND2(first, ...) EVERY_SECOND2_(__VA_ARGS__)
    
    #define EVERY_SECOND3_(second, ...) , second EVERY_SECOND2(__VA_ARGS__)
    #define EVERY_SECOND3(first, ...) EVERY_SECOND3_(__VA_ARGS__)
    
    #define EVERY_SECOND4_(second, ...) , second EVERY_SECOND3(__VA_ARGS__)
    #define EVERY_SECOND4(first, ...) EVERY_SECOND4_(__VA_ARGS__)
    
    #define EVERY_SECOND5_(second, ...) , second EVERY_SECOND4(__VA_ARGS__)
    #define EVERY_SECOND5(first, ...) EVERY_SECOND5_(__VA_ARGS__)
    
    #define COUNT_EVERY_SECOND(_1,__1,_2,__2,_3,__3,_4,__4,_5,__5,num,...) EVERY_SECOND ## num
    #define EVERY_SECOND(...) COUNT_EVERY_SECOND(__VA_ARGS__,5,5,4,4,3,3,2,2,1,0)(__VA_ARGS__)
    
    
    #define INTERFACE_FN(_NAME, _X, _Y, _N_PARAMS, ...) \
        void _NAME(__VA_ARGS__) \
        { \
            processing_function(_X, _Y, _N_PARAMS EVERY_SECOND(__VA_ARGS__)); \
        }
    
    class parentClass {};
    class ClassX {};
    class ClassY {};
    class ClassZ {};
    
    void processing_function(int x, int y, int count, ...)
    {
        va_list params;
        va_start(params, count);
        while (count--) {
            do_something(va_arg(params, ParentClass*));
        }
        va_end(params);
        // do something else
    }
    
    INTERFACE_FN(foo, 1, 2, 0);
    INTERFACE_FN(bar, 3, 4, 1, ClassX*, x);
    INTERFACE_FN(jar, 5, 6, 2, ClassY*, y, ClassZ*, z);
    

    The preprocessor output looks like this

    class parentClass {};
    class ClassX {};
    class ClassY {};
    class ClassZ {};
    
    void processing_function(int x, int y, int count, ...)
    {
     va_list params;
     va_start(params, count);
     while (count--) {
      do_something(va_arg(params, ParentClass*));
     }
     va_end(params);
    
    }
    
    void foo() { processing_function(1, 2, 0 ); };
    void bar(ClassX*, x) { processing_function(3, 4, 1 , x); };
    void jar(ClassY*, y, ClassZ*, z) { processing_function(5, 6, 2 , y , z); };
    

    Though I will give the obligatory warning that there is probably an easier way to do this without macros.

    This will have an extra comma at the end if you put an odd number of arguments, though that shouldn't happen for your use case. This allows up to 5 pairs (10 total arguments) but I think you can see the pattern for how to add support for more. There is no way to do this without explicitly defining for each number of args due to the preprocessors limitations

    Have fun!

    Edit:

    I would also like to note that this can be improved so that you never have to specify _N_PARAMS and let the macros count for you, just add a macro

    #define COUNT_EVERY_SECOND_(_1,__1,_2,__2,_3,__3,_4,__4,_5,__5,num,...) num
    

    and replace INTERFACE_FNs definition with this

    #define INTERFACE_FN(_NAME, _X, _Y, ...) \
        void _NAME(__VA_ARGS__) \
        { \
            processing_function(_X, _Y, COUNT_EVERY_SECOND_(__VA_ARGS__,5,ERROR,4,4,3,3,2,2,1,0) EVERY_SECOND(__VA_ARGS__)); \
        }