boost::bind 源代码分析

boost::bind库绝对是最有用,最有价值的库之一,已被纳入tr1。bind库的出现,替代了stl中的mem_fun,ptr_fun,bind1st,bin2nd等函数
本文并不介绍bind的使用,而是从bind的源代码(boost1.38.0)中分析出它的实现原理

bind执行大致可分为2个过程 1:构造函数对象 2:调用函数对象

下面我们以以下代码为列,抽丝剥茧,分析bind的内部机制。

以上代码在vc9下编译通过,演示了用bind执行了带1个参数的成员函数调用。bind大量使用了函数模板的重载机制,编译器根据参数信息找到合适的函数模板,在下面的分析过程中,我们只分析上面例子的相关代码,其他大同小异。


bind支持最多9个参数的函数调用(由于成员函数第一个参数是this指针,所以成员函数最多只支持8个参数),其实现原理是函数重载机制,以下是对应的bind源代码(bind_mf_cc.hpp)

该模板函数包含5个模板参数,R是返回类型,T是成员函数的类型信息,B1是成员函数的参数类型,A1表示第一个参数,A2表示第2个参数。R T B1确定了函数指针的类型——R (BOOST_BIND_MF_CC T::*f) (B1).在编译器编译boost::bind(&test_class::fun,test,_1)(11);这段代码时进行类型推导,推导结果如下
R = void
T = test_class
B1 = int
A1 = test_class
A2 = arg<1>(这是个占位符,我们将在后面详细解释)
了解了函数定义之后,我们继续分析函数的实现:
首先是2个类型定义:
typedef _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1> F;
typedef typename _bi::list_av_2<A1, A2>::type list_type;
F是函数类型,list_type是参数列表类型。最后一行保存函数指针和参数信息
return _bi::bind_t<R, F, list_type>(F(f), list_type(a1, a2));
这里需要注意的是list_type,即list_av_*::type(*表示1-9).list_av_2的定义如下(bind.hpp):

list_av_2<A1, A2>::type 类型为list2<B1, B2>。list*是一个关键的模板类,它负责存储参数信息,并调用函数。
参数信息是保存在storage*中的,一个listn(n:1-9)的继承关系如下(storage.hpp):
listn : private storagen : public storage(n-1):... public storage1
每个storage增加一个参数存储

现在,函数指针和参数类型都已经保存到bind_t函数对象中了,剩下的就只需要传人参数
函数调用部分,编译器会做以下几件事情:
1:推导相关模板参数,类型检查
2:根据类型信息找到最匹配函数模板,上面的例子会导致编译器找到以下的bind_t::operator()函数

list1<A1 const &> a(a1);将传入的参数数据保存到list1中。
对于BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);初看语法比较怪异,这里解释一下,l_的定义在bind_template.hpp中,在bind.hpp中bind_t的类定义中包含了 bind_template.hpp:

结合上面bind函数的定义:

根据bind_t的构造来看,这里l_即list_type(a1, a2),类型为list2。
由于是成员函数指针,最终导致调用list2::operator()(如果是普通函数指针,则调用list1::operator())

其中base_type即storage2,a1_即成员函数的第一个参数——this指针(该参数在构造bind_t函数对象的时候就保存下来了),a2_为该成员函数的第2个参数,在上面的例子中,即我们传人的参数11


最后再来看看占位符(_1,_2)的实现,在placeholders.hpp中定义了9个占位符:

arg在arg.hpp中定义:

除了一个模板构造函数之外,好像它什么都不做,那么占位符有什么用呢?
1:它改变了参数的个数,编译器可根据参数信息选择重载函数
2:它确定了类型信息,在上述例子中list2中a2的类型为arg<1>,arg的模板构造函数会做类型检查
3:它可以改变参数调用的顺序:bind(f,_2,_1)(11,22) = f(22,11) 其实现是在list*::operator()函数里做了一次间接转换:

再看看storage2的定义:

 

当编译bind(f,_2,_1)(11,22)时,编译器推导出base_type::a1_类型为arg<2>,base_type::a2_类型为arg<1>,再看list的[]重载实现:
    A1 operator[] (boost::arg<1>) const { return base_type::a1_; }
    A2 operator[] (boost::arg<2>) const { return base_type::a2_; }
第一个参数a[base_type::a1_] -> a[arg<2>] -> a2_
第二个参数a[base_type::a2_] -> a[arg<1>] -> a1_
这样函数调用就变成了
unwrapper<F>::unwrap(f, 0)(a2_, a1_);

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值