看到这个问题, 第一反应就是用参数列表的api,va_start va_arg va_end遍历一遍计算个和,但仔细想想,对于可变参数这个事,在编译前其实就已经确定了,代码里括号里有多少个参数一目了然.
RAC中Racmetamarcos.h中就有一系列宏来完成这件事,硬是在预处理之后就拿到了可变参数个数:
首先看一下定义
#define metamacro_argcount(...) \
metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
这个宏由几个工具宏一层层展开,现在模拟一下展开过程:
- 假如我们要计算的如下:
int count = metamacro_argcount(a, b, c);
- 于是乎第一层展开后:
int count = metamacro_at(20, a, b, c, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
3.再看metamacro_at的定义:
#define metamacro_at(N, ...) metamacro_concat(metamacro_at, N)(__VA_ARGS__)
// 下面是metamacro_concat做的事(简写一层)
#define metamacro_concat_(A, B) A ## B
- 于是乎第二层展开后:
int count = metamacro_at20(a, b, c, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
5.再看metamacro_at20这个宏干的事儿:
#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)
6.于是乎第三层展开后,相当于截断了前20个参数,留下剩下几个:
int count = metamacro_head(3, 2, 1);
7.这个metamacro_head:
#define metamacro_head(...) metamacro_head_(__VA_ARGS__, 0)
#define metamacro_head_(FIRST, ...) FIRST
8.后面加个0,然后取参数列表第一个,于是乎:
int count = 3;
这样带来的好处不止是将计算在预处理时搞定,不拖延到运行时恶心cpu;但更重要的是编译检查。比如某些可变参数的实现要求可以填2个参数,可以填3个参数,其他的都不行,这样,也只有这样的宏的实现,才能在编译前就确定了错误。'
细节很重要。 @Dylan.
原文链接