boost::bind库绝对是最有用,最有价值的库之一,已被纳入tr1。bind库的出现,替代了stl中的mem_fun,ptr_fun,bind1st,bin2nd等函数
本文并不介绍bind的使用,而是从bind的源代码(boost1.38.0)中分析出它的实现原理
bind执行大致可分为2个过程 1:构造函数对象 2:调用函数对象
下面我们以以下代码为列,抽丝剥茧,分析bind的内部机制。
- #include <iostream>
- #include <boost/bind.hpp>
- using namespace std;
- class test_class
- {
- public:
- void fun(int i)
- {
- cout << i << endl;
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- test_class test;
- boost::bind(&test_class::fun,test,_1)(11);
- return 0;
- }
#include <iostream> #include <boost/bind.hpp> using namespace std; class test_class { public: void fun(int i) { cout << i << endl; } }; int _tmain(int argc, _TCHAR* argv[]) { test_class test; boost::bind(&test_class::fun,test,_1)(11); return 0; }
以上代码在vc9下编译通过,演示了用bind执行了带1个参数的成员函数调用。bind大量使用了函数模板的重载机制,编译器根据参数信息找到合适的函数模板,在下面的分析过程中,我们只分析上面例子的相关代码,其他大同小异。
bind支持最多9个参数的函数调用(由于成员函数第一个参数是this指针,所以成员函数最多只支持8个参数),其实现原理是函数重载机制,以下是对应的bind源代码(bind_mf_cc.hpp)
[c-sharp] view plain copy print ?
- template<class R, class T,
- class B1,
- class A1, class A2>
- _bi::bind_t<R, _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1>, typename _bi::list_av_2<A1, A2>::type>
- BOOST_BIND(R (BOOST_BIND_MF_CC T::*f) (B1), A1 a1, A2 a2)
- {
- typedef _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1> F;
- typedef typename _bi::list_av_2<A1, A2>::type list_type;
- return _bi::bind_t<R, F, list_type>(F(f), list_type(a1, a2));
- }
template<class R, class T, class B1, class A1, class A2> _bi::bind_t<R, _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1>, typename _bi::list_av_2<A1, A2>::type> BOOST_BIND(R (BOOST_BIND_MF_CC T::*f) (B1), A1 a1, A2 a2) { typedef _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1> F; typedef typename _bi::list_av_2<A1, A2>::type list_type; return _bi::bind_t<R, F, list_type>(F(f), list_type(a1, a2)); }
该模板函数包含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):
[cpp] view plain copy print ?
- template<class A1, class A2> struct list_av_2
- {
- typedef typename add_value<A1>::type B1;
- typedef typename add_value<A2>::type B2;
- typedef list2<B1, B2> type;
- };
template<class A1, class A2> struct list_av_2 { typedef typename add_value<A1>::type B1; typedef typename add_value<A2>::type B2; typedef list2<B1, B2> type; };
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()函数
[cpp] view plain copy print ?
- template<class A1> result_type operator()(A1 const & a1)
- {
- list1<A1 const &> a(a1);
- BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
- }
template<class A1> result_type operator()(A1 const & a1) { list1<A1 const &> a(a1); BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0); }
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:
[cpp] view plain copy print ?
- template<class R, class F, class L> class bind_t
- {
- public:
- typedef bind_t this_type;
- bind_t(F f, L const & l): f_(f), l_(l) {}
- #define BOOST_BIND_RETURN return
- #include <boost/bind/bind_template.hpp>
- #undef BOOST_BIND_RETURN
- };
template<class R, class F, class L> class bind_t { public: typedef bind_t this_type; bind_t(F f, L const & l): f_(f), l_(l) {} #define BOOST_BIND_RETURN return #include <boost/bind/bind_template.hpp> #undef BOOST_BIND_RETURN };
结合上面bind函数的定义:
[cpp] view plain copy print ?
- template<class R, class T,
- class B1,
- class A1, class A2>
- _bi::bind_t<R, _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1>, typename _bi::list_av_2<A1, A2>::type>
- BOOST_BIND(R (BOOST_BIND_MF_CC T::*f) (B1), A1 a1, A2 a2)
- {
- typedef _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1> F;
- typedef typename _bi::list_av_2<A1, A2>::type list_type;
- return _bi::bind_t<R, F, list_type>(F(f), list_type(a1, a2));
- }
template<class R, class T, class B1, class A1, class A2> _bi::bind_t<R, _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1>, typename _bi::list_av_2<A1, A2>::type> BOOST_BIND(R (BOOST_BIND_MF_CC T::*f) (B1), A1 a1, A2 a2) { typedef _mfi::BOOST_BIND_MF_NAME(mf1)<R, T, B1> F; typedef typename _bi::list_av_2<A1, A2>::type list_type; return _bi::bind_t<R, F, list_type>(F(f), list_type(a1, a2)); }
根据bind_t的构造来看,这里l_即list_type(a1, a2),类型为list2。
由于是成员函数指针,最终导致调用list2::operator()(如果是普通函数指针,则调用list1::operator())
[cpp] view plain copy print ?
- template<class F, class A> void operator()(type<void>, F & f, A & a, int)
- {
- unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]);
- }
template<class F, class A> void operator()(type<void>, F & f, A & a, int) { unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); }
其中base_type即storage2,a1_即成员函数的第一个参数——this指针(该参数在构造bind_t函数对象的时候就保存下来了),a2_为该成员函数的第2个参数,在上面的例子中,即我们传人的参数11
最后再来看看占位符(_1,_2)的实现,在placeholders.hpp中定义了9个占位符:
[cpp] view plain copy print ?
- static boost::arg<1> _1;
- static boost::arg<2> _2;
- static boost::arg<3> _3;
- static boost::arg<4> _4;
- static boost::arg<5> _5;
- static boost::arg<6> _6;
- static boost::arg<7> _7;
- static boost::arg<8> _8;
- static boost::arg<9> _9;
static boost::arg<1> _1; static boost::arg<2> _2; static boost::arg<3> _3; static boost::arg<4> _4; static boost::arg<5> _5; static boost::arg<6> _6; static boost::arg<7> _7; static boost::arg<8> _8; static boost::arg<9> _9;
arg在arg.hpp中定义:
[cpp] view plain copy print ?
- template< int I > struct arg
- {
- arg()
- {
- }
- template< class T > arg( T const & )
- {
-
- typedef char T_must_be_placeholder[ I == is_placeholder<T>::value? 1: -1 ];
- }
- };
template< int I > struct arg { arg() { } template< class T > arg( T const & /* t */ ) { // static assert I == is_placeholder<T>::value typedef char T_must_be_placeholder[ I == is_placeholder<T>::value? 1: -1 ]; } };
除了一个模板构造函数之外,好像它什么都不做,那么占位符有什么用呢?
1:它改变了参数的个数,编译器可根据参数信息选择重载函数
2:它确定了类型信息,在上述例子中list2中a2的类型为arg<1>,arg的模板构造函数会做类型检查
3:它可以改变参数调用的顺序:bind(f,_2,_1)(11,22) = f(22,11) 其实现是在list*::operator()函数里做了一次间接转换:
[cpp] view plain copy print ?
- template<class F, class A> void operator()(type<void>, F & f, A & a, int)
- {
- unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]);
- }
template<class F, class A> void operator()(type<void>, F & f, A & a, int) { unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); }
再看看storage2的定义:
[cpp] view plain copy print ?
- template<class A1, class A2> struct storage2: public storage1<A1>
- {
- typedef storage1<A1> inherited;
- storage2( A1 a1, A2 a2 ): storage1<A1>( a1 ), a2_( a2 ) {}
- template<class V> void accept(V & v) const
- {
- inherited::accept(v);
- BOOST_BIND_VISIT_EACH(v, a2_, 0);
- }
- A2 a2_;
- };
template<class A1, class A2> struct storage2: public storage1<A1> { typedef storage1<A1> inherited; storage2( A1 a1, A2 a2 ): storage1<A1>( a1 ), a2_( a2 ) {} template<class V> void accept(V & v) const { inherited::accept(v); BOOST_BIND_VISIT_EACH(v, a2_, 0); } A2 a2_; };
当编译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_);