Home > database >  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?

Time:12-09

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.

CodePudding user response:

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.

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"

CodePudding user response:

Two solutions.

1.

Based on the usual COUNT pattern, which is described in the answer on the other question (in comments) from Jarod42:

First, you define argument counting macros. You might add arbitrary number of arguments (up to compiler limit) to COUNT_N and numbers in the definition of COUNT. It'll have the numbers in decreasing order after __VA_ARGS__, thus it returns the argument count:

#define COUNT_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N
#define COUNT(...)   COUNT_N(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
// Warning: COUNT() return 1 (as COUNT(A)) :-/

Then you'll need the usual IDENTITY and APPLY definitions:

#define IDENTITY(N) N
#define APPLY(macro, ...) IDENTITY(macro(__VA_ARGS__))

Finaly, you add a dispatcher based on arg count. Unfortunately, you have to create a case for each number of arguments; if you like, you might generate that code (yes, even with preprocessor, but in a distinct run):

#define CONCAT_STRINGIFY_DISPATCH(N) CONCAT_STRINGIFY ## N

#define TO_STRING(X) #X
#define CONCAT_STRINGIFY1(A) TO_STRING(A)
#define CONCAT_STRINGIFY2(A, B) TO_STRING(A ## B)
#define CONCAT_STRINGIFY3(A, B, C) TO_STRING(A ## B ## C)
#define CONCAT_STRINGIFY4(A, B, C, D) TO_STRING(A ## B ## C ## D)
// ...

#define CONCAT_STRINGIFY(...) IDENTITY(APPLY(CONCAT_STRINGIFY_DISPATCH, COUNT(__VA_ARGS__)))(__VA_ARGS__)

CONCAT_STRINGIFY(AAA, BBB, CCC)

Perhaps a little more 'hacky', but sorter solution is to concatenate many arguments and pass empty arguments as needed:

#define TO_STRING(X) #X
#define CONCAT_STRINGIFY_4(A, B, C, D, ...) TO_STRING(A ## B ## C ## D)
#define CS(...) CONCAT_STRINGIFY_4(__VA_ARGS__,,,,)

This works for up to 4 args, you might add more as needed.

  • Related