太强大了!源码面前了无秘密,手把手写个闭包
前两天对协程这一块很感兴趣,于是了解到libco在这方面做的不错,想扒一下源码看看,今天看了闭包,感觉收获了很多,沉淀一下。
libco是一个广泛用于微信后端服务的协同程序库。自2013年以来,它已经在数万台机器上运行。
下载地址:
“https://github.com/Tencent/libco
法:
1.libco闭包实现之co_closure.h解读
使用方法:
#include "co_closure.h"
#include <stdio.h>
int main() {
int a = 10;
co_ref(ref, a);
co_func(f, ref)
{
printf("hello co_func!\n");
}
co_func_end;
f ff(ref);
ff.exec();
}
源码分析:
上面实际上是宏展开的函数调用。
1.1 闭包基础
在libco中,所有的闭包实现是宏,先学习一下给宏函数传递的参数个数如何推断出来,宏怎么可以做到。
(1)获取宏可变参数个数
#define comac_arg_n( _0,_1,_2,_3,_4,_5,_6,_7,N,...) N
#define comac_get_args_cnt( ... ) comac_arg_n( __VA_ARGS__ )
#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())
上述为源码的实现,源码中支持的是最多传递7个参数,超过了的话,就不能推导出到底传了多少个参数了。
先考虑用户这样调用:
conac_argc(arg1,arg2,arg3)
我们期望是返回3。
具体对应到宏展开如下:conac_argc传递一包参数给cnt宏,cnt宏将(0,一包参数,7,6,5,4,3,2,1,0),传递给comac_arg_n,在这个宏中进行替换,我们得到了
0,arg1,arg2,arg3,7,6,5,4,3,2,1,0 // 传递进comac_arg_n
_0,_1,_2,_3,_3,_5,_6,_7,N // 原参数
可以看到N被3替换了,于是返回了3,这样就求出了宏参数个数。
另外一个注意点是,comac_get_args_cnt里面的第一个参数为啥是0?
当comac_argc调用的时候不传递任何参数,返回时1,也就是返回不正确了,我们没传参,怎么会是1,所以这里有个0占位。这样好处是当不传递参数的时候,能够返回正确,这里返回正确还需要靠后面的##,##符号在这里表示去除逗号。
例如:有个宏定义是
#define mac(fmt,...) printf(fmt,__VA_ARGS__)
这里不加##,调用的时候是mac("assd")
会被展开成printf("assd",)
这种形式的时候,会看到多了个逗号,加上##就可以去掉。
(2)类型推断及声明
在co_closure.h
种有下列宏定义:
#if __cplusplus <= 199711L
#define decl_typeof( i,a,... ) typedef typeof( a ) typeof_##a;
#else
#define decl_typeof( i,a,... ) typedef decltype( a ) typeof_##a;
#endif
#define impl_typeof( i,a,... ) typeof_##a & a;
#define impl_typeof_cpy( i,a,... ) typeof_##a a;
#define con_param_typeof( i,a,... ) typeof_##a & a##r,
#define param_init_typeof( i,a,... ) a(a##r),
例如:
decl_typeof(1,a) ==> typedef typeof(a) typeof_a; //主要用于获取变量a的类型
impl_typeof(1,a) ==> typeof_a& a; //主要用于创建一个和变量a的类型相同的引用。
impl_typeof_cpy(1,a) ==> typeof_a a; // 主要用于创建一个和变量a类型相同的变量。
con_param_typeof(1,a) ==> typeof_a& a_r; // 用于生成类构造函数入参。
param_init_typeof(1,a) ==> a(a_r); // 用于生成类构造函数初始化列表。
1.2 co_ref宏展开
co_ref宏展开为:
#define co_ref( name,... )\
repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\
class type_##name\
{\
public:\
repeat( comac_argc(__VA_ARGS__) ,impl_typeof,__VA_ARGS__ )\
int _member_cnt;\
type_##name( \
repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \
repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__)) \
{}\
} name( __VA_ARGS__ ) ;
先看:
repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )
依次调用下面宏:
(1)宏参数个数计算
#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_join_1( x,y ) x##y
#define comac_argc( ... ) comac_get_args_cnt( 0,##__VA_ARGS__,comac_args_seqs() )
#define comac_join( x,y) comac_join_1( x,y )
经过上面这个宏,可以计算出传递给当前宏的参数有多少个。
(2)根据宏参数个数调用对应的repeat_x宏
#define repeat_0( fun,a,... )
#define repeat_1( fun,a,... ) fun( 1,a,__VA_ARGS__ ) repeat_0( fun,__VA_ARGS__ )
#define repeat_2( fun,a,... ) fun( 2,a,__VA_ARGS__ ) repeat_1( fun,__VA_ARGS__ )
#define repeat_3( fun,a,... ) fun( 3,a,__VA_ARGS__ ) repeat_2( fun,__VA_ARGS__ )
#define repeat_4( fun,a,... ) fun( 4,a,__VA_ARGS__ ) repeat_3( fun,__VA_ARGS__ )
#define repeat_5( fun,a,... ) fun( 5,a,__VA_ARGS__ ) repeat_4( fun,__VA_ARGS__ )
#define repeat_6( fun,a,... ) fun( 6,a,__VA_ARGS__ ) repeat_5( fun,__VA_ARGS__ )
#define repeat( n,fun,... ) comac_join( repeat_,n )( fun,__VA_ARGS__)
在这个例子中会调用repeat_1宏,因为只传递了一个参数。
(3)宏中再去调用类型推断
#if __cplusplus <= 199711L
#define decl_typeof( i,a,... ) typedef typeof( a ) typeof_##a;
#else
#define decl_typeof( i,a,... ) typedef decltype( a ) typeof_##a;
#endif
在这个例子中,会展开为:
typedef typeof(a) typeof_a
到此,上述repeat宏展开完毕。
class type_##name
展开为:
class type_ref
再看内部的repeat:
repeat( comac_argc(__VA_ARGS__) ,impl_typeof,__VA_ARGS__ )
分析同上述的repeat,最后调用:
#define impl_typeof( i,a,... ) typeof_##a & a;
这个例子中,会展开为:
typeof_a & a;
继续往下分析,内部声明了一个成员变量:
int _member_cnt;
继续走,看到构造函数:
type_##name( \
repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \
repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__)) \
{}\
先看第一个repeat,展开为:
#define con_param_typeof( i,a,... ) typeof_##a & a##r,
这个例子就是:
typeof_a & a_r,
再看第二个repeat:
repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__)) \
展开为:
#define param_init_typeof( i,a,... ) a(a##r),
这个例子就是:
a(a_r),
后面还有一个:
_member_cnt(comac_argc(__VA_ARGS__))
对应到这个例子就是:
_member_cnt(1)
最后:
name(__VA_ARGS__);
对应到例子就是:
ref(a);
上述的例子完整的宏展开为:
int a = 100;
typedef typeof(a) typeof_a;
class type_ref
{
public:
typeof_a & a;
int _member_cnt;
type_ref(typeof_a & ar,...)
: a(ar), _member_cnt(1)
{
}
} ref(a);
使用的时候:
int main() {
ref._member_cnt;
}
1.3 co_func及co_func_end
co_func展开:
#define co_func(name,...)\
repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\
class name:public stCoClosure_t\
{\
public:\
repeat( comac_argc(__VA_ARGS__) ,impl_typeof_cpy,__VA_ARGS__ )\
int _member_cnt;\
public:\
name( repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \
repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__))\
{}\
void exec()
根据上述例子:
co_ref(ref, a);
co_func(f, ref)
{
printf("hello co_func!\n");
}
同上述co_ref一样,对应到这个例子,被展开成:
//repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\
typedef typeof(ref) typeof_ref;
class f : public stCoClosure_t
{
public:
// repeat( comac_argc(__VA_ARGS__) ,impl_typeof_cpy,__VA_ARGS__ )
typeof_ref ref;
int _member_cnt;
public:
f(typeof_ref & refr, ...) : ref(refr), _member_cnt(1)
{
}
void exec()
{
printf("hello co_func!\n");
}
调用:
cofunc_end;
对应:
#define co_func_end }
展开为:
};
1.4 好理解的说明
上述co_func那个exec怎么展开的呢?
在我们调用的时候是:
co_func(f, ref)
{
printf("hello co_func!\n");
}
先不用管第一行的co_func(f,ref)
,后面:
{
printf("hello co_func!\n");
}
可以看到少个函数名,就构成了函数,也就是跟宏定义里面的:
void exec()
组成一块了,前面co_func展开后跟后面这个花括号拼接到一块,简直碉堡了,又可以深入理解宏展开思想~