Search code examples
c++cvisual-c++c-preprocessorpreprocessor

How to concatenate all of the arguments of a variadic macro into a quoted string?


How to concatenate all of the arguments of a variadic macro into a quoted string ?

Below is a definition of non-variadic macro that concatenates 2 arguments into a quoted string:

#define TO_STRING(x) #x
#define CONCAT_STRINGIFY(x,y) TO_STRING(x##y)

Invoking this macro like this:

CONCAT_STRINGIFY(AAA,BBB)

...produces the following output:

"AAABBB"

How to make the macro CONCAT_STRINGIFY(...) variadic, so that it accepts an arbitrary number of arguments ?

P.S.
The solution can use only the C or C++ Preprocessor and no 3rd party libraries
BTW: I am not passing the preprocessor's output to a C/C++ compiler.


Solution

  • Here is a general solution. It works with up to 342 arguments, but can be exponentially expanded to work with more arguments by adding more EVALs.

    #define EVAL1(...) __VA_ARGS__
    #define EVAL2(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__))))
    #define EVAL3(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__))))
    #define EVAL4(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__))))
    #define EVAL5(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__))))
    
    #define EMPTY()
    
    #define TUPLE_AT_1(a,b,...) b
    #define CHECK(...) TUPLE_AT_1(__VA_ARGS__)
    #define CAT_PROBE(...) ,CAT_END,
    
    #define CAT_IND() CAT_
    #define CAT_(x,a,...) CHECK(CAT_PROBE a,CAT_NEXT)(x,a,__VA_ARGS__)
    #define CAT_NEXT(x,a,...) CAT_IND EMPTY()()(x##a,__VA_ARGS__)
    #define CAT_END(x,a,...) #x
    
    #define CAT(...) EVAL5(CAT_(,__VA_ARGS__,()))
    CAT(a,b,c,d,e,f,g,h,i,j,k) // "abcdefghijk"
    

    It uses macro recursion to concatenate all arguments, until an artificially appended "()" argument is reached. I've chosen "()" because it isn't pastable, and easy to detect.

    Since MSVC by default doesn't implement a standard conforming preprocessor, you'll need to enable the /Zc:preprocessor flag for the above code to work on MSVC.

    Edit:

    If you don't care about a general solution, here is a nice and compact one with up to 16 arguments:

    #define CAT_(a,b,c,d,e,f,g,i,j,k,l,m,n,o,p,...) a##b##c##d##e##f##g##i##j##k##l##m##n##o##p
    #define CAT(...) CAT_(__VA_ARGS__,,,,,,,,,,,,,,,,,)
    #define STR(...) #__VA_ARGS__
    #define STRe(...) STR(__VA_ARGS__)
    STRe(CAT(1,2)) // "12"
    STRe(CAT(1,2,3,4,5,6,7)) // "1234567"