You'll probably should go with @eljay's answer, but if you need to support way more arguments, here is one that supports ~2000 arguments in 22 lines and adding more lines grows that number exponentially.
#define E4(...) E3(E3(E3(E3(E3(E3(E3(E3(E3(E3(__VA_ARGS__))))))))))
#define E3(...) E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(__VA_ARGS__))))))))))
#define E2(...) E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(__VA_ARGS__))))))))))
#define E1(...) __VA_ARGS__
#define EMPTY()
#define TUPLE_AT_2(x,y,...) y
#define TUPLE_TAIL(x,...) __VA_ARGS__
#define CHECK(...) TUPLE_AT_2(__VA_ARGS__,0,)
#define EQ_END_END ,1
#define SCAN(...) __VA_ARGS__
#define CAT(a,b) CAT_(a,b)
#define CAT_(a,b) a##b
#define LOOP_() LOOP
#define LOOP(x,y,...) CAT(LOOP, CHECK(EQ_END_##y))(x,y,__VA_ARGS__)
#define LOOP1(x,...) (TUPLE_TAIL x)
#define LOOP0(x,y,...) LOOP_ EMPTY() ()((SCAN x, y),__VA_ARGS__)
#define DTC(...) E4(LOOP((), __VA_ARGS__ END))
DTC (1, 2, 3, 4, 5, 6, 7, 8, 9,) // expands to: (1, 2, 3, 4, 5, 6, 7, 8, 9)
Let me try to explain this.
Firstly, when LOOP
is called inside E4()
it can recursively call itself, which is done in LOOP0. The simplest example for this would be #define LOOP(...) __VA_ARGS__ LOOP_ EMPTY() ()(__VA_ARGS__)
which repeats the argument until the recursion limit, which is bound by the nesting of E4
. Understanding DEFER and OBSTRUCT macros already explains the behavior quite well, so I'll skip that part of the explanation.
Now the idea is the following: We loop over every argument until we reach the last one, where we've inserted an END
mark. Whilst doing that, we construct a new argument list, but this also stops when we reach the END
mark.
CAT(LOOP, CHECK(EQ_END_##y))
branches to LOOP1
if the argument y
holds the end mark END
and otherwise to LOOP0
.
LOOP1
appends a new argument to our argument list in x
using (SCAN x, y)
. Since we start off with an empty argument list, we'll end up with a leading empty argument, which we can trivially remove on LOOP0
.
PS: This concept can be trivially extended to E5
, E6
, ..., although there is a larger overhead using that because once the recursion ends the preprocessor still needs to rescan the result till the recursion limit. If you want to remedy that, you can use something like the continuation machine from order-pp, which can actually terminate, but it's about 150 loc.
Edit, I just revisited this and realized that using x
to build up the tuple is quite inefficient, here is a version that doesn't do that:
#define E4(...) E3(E3(E3(E3(E3(E3(E3(E3(E3(E3(__VA_ARGS__))))))))))
#define E3(...) E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(__VA_ARGS__))))))))))
#define E2(...) E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(__VA_ARGS__))))))))))
#define E1(...) __VA_ARGS__
#define EMPTY()
#define FX(f,x) f(x)
#define TUPLE_AT_2(x,y,...) y
#define TUPLE_TAIL(x,...) __VA_ARGS__
#define CHECK(...) TUPLE_AT_2(__VA_ARGS__,0,)
#define EQ_END_END ,1
#define CAT(a,b) CAT_(a,b)
#define CAT_(a,b) a##b
#define LOOP_() LOOP
#define LOOP(x,...) CAT(LOOP, CHECK(EQ_END_##x))(x,__VA_ARGS__)
#define LOOP1(x,...)
#define LOOP0(x,...) LOOP_ EMPTY() ()(__VA_ARGS__),x
#define DTC(...) (FX(TUPLE_TAIL,E4(LOOP(__VA_ARGS__ END))))
DTC (1, 2, 3, 4, 5, 6, 7, 8, 9,) // expands to: (1, 2, 3, 4, 5, 6, 7, 8, 9)