Leushenko's answer 真的很酷 - 仅此而已:foo 示例无法使用 GCC 编译,它在 foo(7) 处失败,绊倒了 FIRST 宏和实际的函数调用 ((_1, __VA_ARGS__),剩下一个多余的逗号。另外,如果我们想提供额外的重载,我们就有麻烦了,比如foo(double)。
所以我决定进一步详细说明答案,包括允许 void 重载(foo(void) - 这造成了一些麻烦......)。
现在的想法是:在不同的宏中定义多个泛型,然后根据参数的数量选择正确的一个!
参数数量很简单,基于this answer:
#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)
#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y
很好,我们解析为SELECT_1 或SELECT_2(或更多参数,如果您想要/需要它们),所以我们只需要适当的定义:
#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
int: foo_int, \
char: foo_char, \
double: foo_double \
)
#define SELECT_2(_1, _2) _Generic((_1), \
double: _Generic((_2), \
int: foo_double_int \
) \
)
好的,我已经添加了 void 重载——然而,这个实际上并没有被 C 标准所涵盖,它不允许空的可变参数,即。 e.然后我们依赖编译器扩展!
一开始,一个空的宏调用 (foo()) 仍然会产生一个令牌,但是是一个空的。因此,即使在空宏调用时,计数宏实际上也会返回 1 而不是 0。我们可以“轻松”消除这个问题,如果我们在__VA_ARGS__ 之后放置逗号有条件地,这取决于列表是否为空:
#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)
看起来很简单,但 COMMA 宏却相当沉重;幸运的是,blog of Jens Gustedt 已经涵盖了该主题(感谢 Jens)。基本技巧是,如果没有括号,函数宏不会被扩展,为了进一步解释,请查看 Jens 的博客......我们只需根据需要修改宏(我将使用较短的名称并且为了简洁而减少争论)。
#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)
#define SET_COMMA(...) ,
#define COMMA(...) SELECT_COMMA \
( \
HAS_COMMA(__VA_ARGS__), \
HAS_COMMA(__VA_ARGS__ ()), \
HAS_COMMA(SET_COMMA __VA_ARGS__), \
HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)
#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3
#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,
现在我们很好......
一个块的完整代码:
/*
* demo.c
*
* Created on: 2017-09-14
* Author: sboehler
*/
#include <stdio.h>
void foo_void(void)
{
puts("void");
}
void foo_int(int c)
{
printf("int: %d\n", c);
}
void foo_char(char c)
{
printf("char: %c\n", c);
}
void foo_double(double c)
{
printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
printf("double: %.2f, int: %d\n", c, d);
}
#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)
#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y
#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
int: foo_int, \
char: foo_char, \
double: foo_double \
)
#define SELECT_2(_1, _2) _Generic((_1), \
double: _Generic((_2), \
int: foo_double_int \
) \
)
#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N
#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)
#define SET_COMMA(...) ,
#define COMMA(...) SELECT_COMMA \
( \
HAS_COMMA(__VA_ARGS__), \
HAS_COMMA(__VA_ARGS__ ()), \
HAS_COMMA(SET_COMMA __VA_ARGS__), \
HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)
#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3
#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,
int main(int argc, char** argv)
{
foo();
foo(7);
foo(10.12);
foo(12.10, 7);
foo((char)'s');
return 0;
}