太强大了!源码面前了无秘密,手把手写个闭包

太强大了!源码面前了无秘密,手把手写个闭包

前两天对协程这一块很感兴趣,于是了解到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展开后跟后面这个花括号拼接到一块,简直碉堡了,又可以深入理解宏展开思想~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值