Search code examples
c++cgccpreprocessor

Remove trailing comma of macro expansion


I have a 'system' to generate classes using macros (there is unfortunately no other way).

In some cases, for example for an initializer list, this leads to a trailing comma that I'd like to get rid of:

#define REMOVE_TRAILING_COMMA(...) ?what to put here?
#define FOO a, b, c, d, e, f, g, h,

REMOVE_TRAILING_COMMA(FOO) --> expands to a, b, c, d, e, f, g, h

If it just removes the last argument that would be fine too, of course. Aka if REMOVE_TRAILING_COMMA(a, b, c) expands to a, b.

The macro will always get at least two arguments, so the minimal case would be: REMOVE_TRAILING_COMMA(a,) --> expands to a.

Is this even possible? I searched for a few hours with Google, but nothing comes up.


Solution

  • I would be nice to use #define REMOVE_FIRST(a, ...) __VA_ARGS__, but that would require reversing macro arguments order. I do not see a way to do it (other then overloading it anyway). So overload the macro on count of arguments and enumerate each overload to remove trailing argument:

    // example macro overoad for up to 9 args
    // TODO: write REMOVE_TRAILING_COMMA_{1,2,3,4,5....} for each case
    #define REMOVE_TRAILING_COMMA_8(_1,_2,_3,_4,_5,_6,_7,_8)  \
                                    _1,_2,_3,_4,_5,_6,_7
    #define REMOVE_TRAILING_COMMA_9(_1,_2,_3,_4,_5,_6,_7,_8,_9)  \
                                    _1,_2,_3,_4,_5,_6,_7,_8
    #define REMOVE_TRAILING_COMMA_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...)  \
            REMOVE_TRAILING_COMMA_##N
    #define REMOVE_TRAILING_COMMA(...)  \
            REMOVE_TRAILING_COMMA_N(__VA_ARGS__,9,8,7,6,5,4,3,2,1)(__VA_ARGS__)
    
    #define FOO a, b, c, d, e, f, g, h,
    
    REMOVE_TRAILING_COMMA(FOO) --> expands to a, b, c, d, e, f, g, h
    

    The following was added by Carlo Wood:

    Thanks! I wrote a small program to generate the above, so it is easy to increase the maximum number of arguments that this still works with:

    #include <string>
    #include <iostream>
    #include <sstream>
    
    std::string args1_N(int n)
    {
      std::ostringstream ss;
      char const* prefix = "_";
      for (int a = 1; a <= n; ++a)
      {
        ss << prefix << a;
        prefix = ",_";
      }
      return ss.str();
    }
    
    int main()
    {
      int const max_args = 9;
    
      for (int n = 1; n <= max_args + 1; ++n)
        std::cout << "#define REMOVE_TRAILING_COMMA_" << n << "(" << args1_N(n) <<
          ") \\\n                                " << args1_N(n - 1) << std::endl;
      std::cout << "#define REMOVE_TRAILING_COMMA_N(" << args1_N(max_args + 1) << ",N,...) \\\n" <<
        "        REMOVE_TRAILING_COMMA_##N\n";
      std::cout << "#define REMOVE_TRAILING_COMMA(...) \\\n" <<
        "        REMOVE_TRAILING_COMMA_N(__VA_ARGS__";
      for (int a = max_args + 1; a > 0; --a)
        std::cout << "," << a;
      std::cout << ")(__VA_ARGS__)" << std::endl;
    }