c++cmacrosvariadic-macros

What are options to get multiple variable lists in a variadic macro?


I have been trying to use variadic macros to reduce redundant code in some SFINAE patterns I am using. Specifically, I would like to generate function definitions using variadic macros. (This question is not about SFINAE). For my application, I would like write a wrapper function that calls an existing member function. The actual application requires that I produce a templated struct with SFINAE specialization so there is some repetitive stuff that would be great to just have a macro take care of generating the struct and function. OK that is the motivation.

The following example dispenses with all of the SFINAE stuff and simply defines a struct with three static functions (f0, f1, f2) with different parameter lists, and tries to call these functions through global macro generated function. Why would you want to do this?? You wouldn't. But this is just to illustrate the problem that I was having for the SFINAE application.

#include<iostream>

struct Foo
{
  static void f0()
  {
    std::cout<<"f0() called"<<std::endl;
  }

  static void f1(int a)
  {
    std::cout<<"f1("<<a<<") called"<<std::endl;
  }

  static double f2(int a, double b)
  {
    std::cout<<"f2("<<a<<","<<b<<") called"<<std::endl;
    return a*b;
  }

};

#define VA_LIST(...) __VA_ARGS__

#define FUNCTION_WRAPPER(NAME,RTYPE,PARAMS,ARGS)\
RTYPE NAME(PARAMS)\
{\
  return Foo::NAME(ARGS);\
}

FUNCTION_WRAPPER(f0,void,VA_LIST(),VA_LIST())
FUNCTION_WRAPPER(f1,void,VA_LIST(int a),VA_LIST(a))
FUNCTION_WRAPPER(f2,double,VA_LIST(int a, double b), VA_LIST(a, b))

int main()
{
  f0();
  f1(1);
  f2(1,4.2);
  return 0;
}

OK. So the VA_LIST(...) __VA_ARGS__ enables the creation of a single variable list. The call:

FUNCTION_WRAPPER(f2,double,VA_LIST(int a, double b), VA_LIST(a, b))

utilizes to variable lists as argument 3 and 3. Note that the Parameter list and the Argument list have to be consistent with each other (i.e. variables passed in the Argument list better be declared in the Parameter list.

This seems to work for this example, although there are issues if we were to try to add an fixed argument to beginning of the call (e.g. Foo::NAME(fixedArg, ARGS) ). It appears that the ##__VA_ARGS trick to swallow the comma if the variable list is empty doesn't work with this approach.

So the questions I have is:

  1. While this approach seems to work properly on both gcc and clang, I can't find any similar examples that uses multiple variable lists in this manner...which makes me a little nervous. Is this a valid approach?
  2. So what is actually happening in this approach? How is the FUNCTION_WRAPPER macro able to handle two variable lists? Are the PARAMS and ARGS macro arguments simply being expanded in the body of the macro, or is there something more complex happening here?
  3. Is there a way to use the ## trick to swallow preceding commas? Placing the ## preceding the PARAMS and ARGS values in the macro body result in a compilation error, as does placing the ## in the VA_LIST.

Sample code for this last case is:

#define VA_LIST2(...) __VA_ARGS__
#define FUNCTION_WRAPPER2(NAME,RTYPE,PARAMS,ARGS)\
RTYPE NAME##_2(PARAMS)\
{\
  return Foo::NAME(1,ARGS);\
}
FUNCTION_WRAPPER2(f2,double,VA_LIST2(double b), VA_LIST2(b))

The error for the insertion of ## into the FUNCTION_WRAPPER body is

 error: pasting "," and "VA_LIST" does not give a valid preprocessing token
   return Foo::NAME(1,##ARGS);\

The compilation error for the insertion of ## into the VA_LIST macro body is:

error: '##' cannot appear at either end of a macro expansion
 #define VA_LIST2(...) ##__VA_ARGS__
                     ^

Solution

  • How is the FUNCTION_WRAPPER macro able to handle two variable lists?

    C preprocessor first recognizes arguments of a macro, then expands the arguments, then replaces the body of the macro with the expansion of arguments.

    Because macro arguments are first "recognized" before expanded, macro expansion inside arguments resulting in a comma doesn't change the count of arguments.

    Is there a way to use the ## trick to swallow preceding commas?

    Be aware that it is still a GNU extension. Standard alternative to GCC's ##__VA_ARGS__ trick?

    Yes, just make them to VA_ARGS so you can ## them.

    #define VA_LIST2(...) __VA_ARGS__
    #define ADD_ONE(...)  1,##__VA_ARGS__
    #define FUNCTION_WRAPPER2(NAME,RTYPE,PARAMS,ARGS)\
      RTYPE NAME##_2(PARAMS)\
      {\
        return Foo::NAME(ADD_ONE(ARGS));\
      }
    FUNCTION_WRAPPER2(f2,double,VA_LIST2(), VA_LIST2())
    FUNCTION_WRAPPER2(f2,double,VA_LIST2(double b), VA_LIST2(b))
    

    Is this a valid approach?

    Sure, it's great. I would drop VA_LIST from the call. Lists in preprocessor are just done with (...). Then, just apply VA_LIST on the (...) argument.

    #define VA_LIST(...) __VA_ARGS__
    #define FUNCTION_WRAPPER(NAME,RTYPE,PARAMS,ARGS)\
      RTYPE NAME(VA_LIST PARAMS) \
      { \
        return Foo::NAME(VA_LIST ARGS); \
      }
    
    FUNCTION_WRAPPER(f0,void,(),())
    FUNCTION_WRAPPER(f1,void,(int a),(a))
    FUNCTION_WRAPPER(f2,double,(int a, double b), (a, b))
    
    #define ADD_ONE(...)  1,##__VA_ARGS__
    #define FUNCTION_WRAPPER2(NAME,RTYPE,PARAMS,ARGS)\
      RTYPE NAME##_2(VA_LIST PARAMS) \
      { \
        return Foo::NAME(ADD_ONE ARGS); \
      }
    FUNCTION_WRAPPER2(f2,double,(), ())
    FUNCTION_WRAPPER2(f2,double,(double b), (b))
    

    Anyway, double b .... b you are repeating yourself, however it is very clean and very flexible. You might want to see How to use variadic macro arguments in both a function definition and a function call? , c++ variadic macro: how to retrieve arguments values where I'm passing types and variable names as separate tokens and splitting them later differently depending on if it's function parameter list or call parameters.