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.