您可能应该选择@eljay 的答案,但如果您需要支持更多参数,这里有一个支持 22 行约 2000 个参数的方法,并且添加更多行会使这个数字呈指数增长。
#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)
让我试着解释一下。
首先,当LOOP在E4()内部被调用时,它可以递归调用自身,这在LOOP0中完成。最简单的例子是#define LOOP(...) __VA_ARGS__ LOOP_ EMPTY() ()(__VA_ARGS__),它重复参数直到递归限制,由E4 的嵌套限制。 Understanding DEFER and OBSTRUCT macros 已经很好地解释了这种行为,所以我将跳过这部分解释。
现在的想法如下:我们循环遍历每个参数,直到到达最后一个参数,我们在其中插入了END 标记。在这样做的同时,我们构建了一个新的参数列表,但是当我们到达END 标记时,这也会停止。
CAT(LOOP, CHECK(EQ_END_##y)) 分支到 LOOP1,如果参数 y 包含结束标记 END,否则到 LOOP0。
LOOP1 使用(SCAN x, y) 将一个新参数附加到x 中的参数列表中。由于我们从一个空参数列表开始,我们将得到一个前导空参数,我们可以在LOOP0 上轻松删除它。
PS:这个概念可以简单地扩展到E5,E6,...,尽管使用它会有更大的开销,因为一旦递归结束,预处理器仍然需要重新扫描结果直到递归限制。如果你想解决这个问题,你可以使用 order-pp 中的 continuation machine 之类的东西,它实际上可以终止,但大约是 150 loc。
编辑,我刚刚重新审视了这一点,并意识到使用x 构建元组效率非常低,这是一个不这样做的版本:
#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 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 SCAN(...) __VA_ARGS__
#define LPAREN (
#define DTC(...) SCAN((TUPLE_TAIL LPAREN 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)