虽然我完全同意,如果可能的话,应该使用可变参数函数或函数模板,但这个问题也显示了一些关于宏可以做什么和不能做什么的误解,所以在这个答案中,我将假装函数是不是一个选项。
将定义更改为 FOO(a, ...); 并将 __VA_ARGS__ 变量(代表 ...)拆分为 b,如果存在,则拆分为其余变量。
是的。
所以函数调用看起来像:FOO(5, "4gamma"); 和 FOO(5, "4");
没有。以FOO(5, 4, "gamma"); 和FOO(5, 4); 继续调用。在第一种情况下,__VA_ARGS__ 是 4, "gamma"。在第二种情况下,__VA_ARGS__ 是4。
如果您需要从前者中提取, "gamma",这可以由预处理器完成。它需要参数数量的上限,但您可以将其增加到几乎任何您喜欢的数字。虽然很丑。
如果__VA_ARGS__ 不包含逗号,则提取很简单:
#define COMMA_TRAILING_ARGS_0(a)
如果你知道__VA_ARGS__ 至少包含一个逗号,你可以使用
#define COMMA_TRAILING_ARGS_1(a, ...) , __VA_ARGS__
并且您可以检测到要使用哪一个,直到宏参数的某个上限:
#define ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16
#define HAS_COMMA(...) ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, )
综合:
#define COMMA_TRAILING_ARGS_0(a)
#define COMMA_TRAILING_ARGS_1(a, ...) , __VA_ARGS__
#define ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16
#define HAS_COMMA(...) ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,)
#define CONCAT(a, b) a ## b
#define CONCAT_(a, b) CONCAT(a, b)
#define FOO(a, ...) BAR(a CONCAT_(COMMA_TRAILING_ARGS_, HAS_COMMA(__VA_ARGS__)) (__VA_ARGS__))
FOO(5, 4, x, q); // expands to BAR(5, x, q);
FOO(5, 4, "gamma"); // expands to BAR(5, "gamma");
FOO(5, 4); // expands to BAR(5);