八 月
11
金曜日

libco源码学习

其实C++11已经提供了lambda表达式,索性就梳理一下 co_closure.h 里一些宏定义的用法。

求宏可变参数个数:

#define comac_get_args_cnt( ... ) comac_arg_n( __VA_ARGS__ )
#define comac_arg_n( _0,_1,_2,_3,_4,_5,_6,_7,N,...) N
#define comac_args_seqs() 7,6,5,4,3,2,1,0

#define comac_argc( ... ) comac_get_args_cnt( 0,##__VA_ARGS__,comac_args_seqs() )

例如 comac_argc(total,v2,m) 会有以下的展开过程:

comac_argc(total,v2,m)
/*↓*/
comac_get_args_cnt( 0, total, v2, m, 7, 6, 5, 4, 3, 2, 1, 0)
/*→    comac_arg_n(_0,    _1, _2,_3,_4,_5,_6,_7, N, ...) */
/*↓*/
3

但是对于 comac_argc() 的情况,会有两个因素影响展开的结果。 如果编译时添加 -std=c++11 的选项, comac_argc() 会被展开成1。这是因为在C++11标准下,空的可变参数会被解释成 ""comac_argc() 的展开过程也就变成了下面的情况:

comac_argc()
/*↓*/
comac_get_args_cnt( 0, "",  7, 6, 5, 4, 3, 2, 1, 0)
/*→    comac_arg_n(_0, _1, _2,_3,_4,_5,_6,_7, N, ...) */
/*↓*/
1

另外一个会使 comac_argc() 被展开成1的陷阱是将

#define comac_argc( ... ) comac_get_args_cnt( 0,##__VA_ARGS__,comac_args_seqs() )

写作

#define comac_argc( ... ) comac_get_args_cnt( 0,__VA_ARGS__,comac_args_seqs() )

这是gcc自己实现的一个特性,在gcc的文档中有这样的描述:

Second, the ‘##’ token paste operator has a special meaning when placed between a comma and a variable argument. If you write

#define eprintf(format, …) fprintf (stderr, format, ##__VA_ARGS__)

and the variable argument is left out when the eprintf macro is used, then the comma before the ‘##’ will be deleted. This does not happen if you pass an empty argument, nor does it happen if the token preceding ‘##’ is anything other than a comma.

也就是说如果 ## 写在一个逗号和 __VA_ARGS__ 之间且可变参数为空的时候, ## 前的逗号会被删除。所以 comac_argc() 的展开过程是这样的:

comac_argc()
/*↓*/
comac_get_args_cnt( 0, 7, 6, 5, 4, 3, 2, 1, 0)
/*→    comac_arg_n(_0,_1,_2,_3,_4,_5,_6,_7, N, ...) */
/*↓*/
0

如果删掉了 ## ,展开过程就变为:

comac_argc()
/*↓*/
comac_get_args_cnt( 0,  , 7, 6, 5, 4, 3, 2, 1, 0)
/*→    comac_arg_n(_0,_1,_2,_3,_4,_5,_6,_7, N, ...) */
/*↓*/
1