探究boost::bind的实现

泛化的bind实用性很强,支持将泛化函数(函数、函数指针、仿函数、与成员函数)与运行期实参的动态绑定;在实际工程中也经常作为基础工具集之一被频繁地使用;我计划投入一定的精力,仔细阅读boost库中的基础工具集的代码实现(bind是第一个);旨在了解这些工具的内部实现机理同时也提高下自己的C++语言泛型编程技术,以备后续的实际工程中使用;

 

2005年,Andrei Alexandrescu就在他的C++巨著《C++设计新思维中》中的5.10节中提出了泛型化的函数绑定;bind的实现目标非常明确,就是在运行期实现针对泛化函数任意类型、任意个数的实参进行绑定;形式如下:

bind(f, _2, _1)(x, y);                  // f(y, x)

bind(g, _1, 9, _1)(x);                 // g(x, 9, x)

bind(g, _3, _3, _3)(x, y, z);        // g(z, z, z)

bind(g, _1, _1, _1)(x, y, z);        // g(x, x, x)

根据上述的形式,假如对泛化函数不考虑的话,至少涉及任意个参数(理论上),任意类型、任意位置的实参与函数绑定;另外,涉及占位符等,值拷贝、引用或者移动参数传递、返回值等细节技术问题。

 

我们一步一步先进行拆解;从上述调用的形式分析,bind函数是function template,其最重要的作用是(个人认为)是进行参数类型推导;function template会根据不同的参数个数,而被重载;实现代码中支持最多9个参数;代码如下:(此处列出的是两个参数的函数模板)

 

template<class R, class F, class A1, class A2>
    _bi::bind_t<R, F, typename _bi::list_av_2<A1, A2>::type>
    BOOST_BIND(F f, A1 a1, A2 a2)
{
    typedef typename _bi::list_av_2<A1, A2>::type list_type;
    return _bi::bind_t<R, F, list_type> (f, list_type(a1, a2));
}
       1、 根据函数重载规则(9个实参范围内, A表示参数,英语Argument,A1, A2, …… A9分表表示9种实参类型,当然9个实参类型可重复,可相同)选择对应参数个数的bind函数模板会别Instantiation。此过程根据函数模板的特性,自然地推导出参数A1,A2;

       2、被推导出的参数类型A1, A2,通过class template _bi::list_av_2 组成了一个由两个类型元素合成的类型列表;新组成的类型列表被typedef成list_type类型

       3、list_type(a1, a2) 被显式地的调用构造函数;

       4、bind的返回类型为 _bi::bind_t<R, F, list_type>; 且实际真正实现是通过调用class template _bi::bind_t的函数调用(operator () .... )的形式实现的。bind_t是被重载了的仿函数!

       5、值得注意的是:F类型可以通过bind的调用,被推导出来;但是返回类型R, 目前还是推导不出来哦!

 

       接下来,我们先看class template _bi::list_av_2的实现代码;

       

template<class T> class value
{
public:
    value(T const & t): t_(t) {}

    T & get() { return t_; }
    T const & get() const { return t_; }

    bool operator==(value const & rhs) const
    {
        return t_ == rhs.t_;
    }

private:
    T t_;
};
namespace _bi
{
// add_value
// 参数类型是占位符时,add_value<T>::type是boost::arg<I>;与类型T无关了
template< class T, int I > struct add_value_2 {
    typedef boost::arg<I> type;
};
//参数类型非占位符时,add_value<T>::type实际就是_bi::value<T>
template< class T > struct add_value_2< T, 0 >{
    typedef _bi::value< T > type;
};
template<class T> struct add_value{
    typedef typename add_value_2< T, boost::is_placeholder< T >::value >::type type;
};
template<class T> struct add_value< value<T> >{
    typedef _bi::value<T> type;
};
template<class T> struct add_value< reference_wrapper<T> >{
    typedef reference_wrapper<T> type;
};
template<int I> struct add_value< arg<I> >{
    typedef boost::arg<I> type;
};
template<int I> struct add_value< arg<I> (*) () >{
    typedef boost::arg<I> (*type) ();
};
template<class R, class F, class L> struct add_value< bind_t<R, F, L> >{
    typedef bind_t<R, F, L> 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;
};

    这段代码中,主要就是理解add_value类模板针对各种情况的特化;单独看代码貌似比较难理解;以例子代入的话,会容易些;调用实例如下述代码:

#include <iostream>

struct sum {
      typedef double result_type;
      double operator()(int a, double b) {
             return a+b;
      }
};
int main(int argc, char *argv[])
{
      // 情况1
      std::cout << boost::bind(sum(), _1, _2)(3, 4.0) << std::endl;
      // 情况2
      std::cout << boost::bind(sum(), 3, 4.0)() << std::endl;
      // 情况3
      std::cout << boost::bind(sum(), _1, 4.0)(3) << std::endl;
      return 0;
}

    情况1:A1: boost::arg<1>, A2: boost::arg<2>; B1: boost::arg<1>, B2: boost::arg<2>; 

                 list_type = list2<B1, B2>

    情况2:A1: int, A2: double; B1: value<int>, B2: value<double>;

                 list_type = list2<B1, B2>

    情况3:A1: boost::arg<1>, A2: double; B1: boost::arg<1>, B2: value<double>; 

                 list_type = list2<B1, B2>

    根据argument deduction的结果进行分析,本次进行参数推导的参数列表是第一对圆括号内的。第二个实参列表并木有丢失!我们把第一对圆括号内的参数,简称第一参数列表;第二对圆括号内的参数,简称第二参数列表;注意,第二参数列表才是真正的实际参数;第一参数列表里可能包含占位符参数;

template< class A1, class A2 > class list2: private storage2< A1, A2 >
{
private:
    typedef storage2< A1, A2 > base_type;
public:
    list2( A1 a1, A2 a2 ): base_type( a1, a2 ) {}

    A1 operator[] (boost::arg<1>) const { return base_type::a1_; }
    A2 operator[] (boost::arg<2>) const { return base_type::a2_; }

    A1 operator[] (boost::arg<1> (*) ()) const { return base_type::a1_; }
    A2 operator[] (boost::arg<2> (*) ()) const { return base_type::a2_; }

    template<class T> T & operator[] (_bi::value<T> & v) const { return v.get(); }
    template<class T> T const & operator[] (_bi::value<T> const & v) const { 
         return v.get(); 
    }

    template<class T> T & operator[] (reference_wrapper<T> const & v) const { 
         return v.get(); 
    }

    template<class R, class F, class L> 
    typename result_traits<R, F>::type operator[] (bind_t<R, F, L> & b) const { 
        return b.eval(*this); 
    }

    template<class R, class F, class L> 
    typename result_traits<R, F>::type operator[] (bind_t<R, F, L> const & b) const { 
        return b.eval(*this); 
    }

    template<class R, class F, class A> R operator()(type<R>, F & f, A & a, long){
        return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]);
    }

    template<class R, class F, class A> 
    R operator()(type<R>, F const & f, A & a, long) const {
        return unwrapper<F const>::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_]);
    }

    template<class F, class A> 
    void operator()(type<void>, F const & f, A & a, int) const{
        unwrapper<F const>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]);
    }

    template<class A> bool operator()( type<bool>, logical_and & /*f*/, A & a, int ){
        return a[ base_type::a1_ ] && a[ base_type::a2_ ];
    }

    template<class A> 
    bool operator()( type<bool>, logical_and const & /*f*/, A & a, int ) const{
        return a[ base_type::a1_ ] && a[ base_type::a2_ ];
    }

    template<class A> bool operator()( type<bool>, logical_or & /*f*/, A & a, int ){
        return a[ base_type::a1_ ] || a[ base_type::a2_ ];
    }

    template<class A> 
    bool operator()( type<bool>, logical_or const & /*f*/, A & a, int ) const{
        return a[ base_type::a1_ ] || a[ base_type::a2_ ];
    }

    template<class V> void accept(V & v) const{
        base_type::accept(v);
    }

    bool operator==(list2 const & rhs) const{
        return ref_compare(base_type::a1_, rhs.a1_, 0) && 
               ref_compare(base_type::a2_, rhs.a2_, 0);
    }
};

    list0, list1, list2, ... ... list9 是不同模板参数个数的listN 类模板实现;上述同样以list2为例说明list的实现;这个listN的类模板实现很重要,因为在接下来的真正被绑定的函数调用体被调用时_bi::bind_t<>::operator()(...),最终还是依赖于这个listN的实现;

    listN因为负责最终存储传入的第二参数列表中的实参,就必然有随机读取任意参数的功能;除此之外,listN还负责最终的绑定函数体的调用;因此listN的实现,主要有三部分功能:

    (1)存储最终调用函数体需要传入的参数;即,存储第二参数列表;

    (2)operator[] 的运算符的重载;【类似随机迭代器,对保存的实参列表进行随机访问】

    (3)operator() 运算符的重载;   【实现最终被绑定的泛化函数体的函数调用】

      功能(1)是listN通过storageN的私有继承得以实现;此处的继承使用了私有继承,私有继承的实现语义是”用……来实现“(“is implemented in terms of ");且子类在其成员函数里调用基类公有成员时,使用base::member_语法; 按照上述调用代码逻辑情况1为例,传入参数a1,a2分别是 _1, _2,被存储在storageN里。

namespace boost
{
namespace _bi
{
// 1

template<class A1> struct storage1
{
    explicit storage1( A1 a1 ): a1_( a1 ) {}

    template<class V> void accept(V & v) const
    {
        BOOST_BIND_VISIT_EACH(v, a1_, 0);
    }

    A1 a1_;
};

#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) && !defined( __BORLANDC__ )

template<int I> struct storage1< boost::arg<I> >
{
    explicit storage1( boost::arg<I> ) {}

    template<class V> void accept(V &) const { }

    static boost::arg<I> a1_() { return boost::arg<I>(); }
};

template<int I> struct storage1< boost::arg<I> (*) () >
{
    explicit storage1( boost::arg<I> (*) () ) {}

    template<class V> void accept(V &) const { }

    static boost::arg<I> a1_() { return boost::arg<I>(); }
};

#endif

// 2

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_;
};

#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION )

template<class A1, int I> struct storage2< A1, boost::arg<I> >: public storage1<A1>
{
    typedef storage1<A1> inherited;

    storage2( A1 a1, boost::arg<I> ): storage1<A1>( a1 ) {}

    template<class V> void accept(V & v) const
    {
        inherited::accept(v);
    }

    static boost::arg<I> a2_() { return boost::arg<I>(); }
};

template<class A1, int I> struct storage2< A1, boost::arg<I> (*) () >: public storage1<A1>
{
    typedef storage1<A1> inherited;

    storage2( A1 a1, boost::arg<I> (*) () ): storage1<A1>( a1 ) {}

    template<class V> void accept(V & v) const
    {
        inherited::accept(v);
    }

    static boost::arg<I> a2_() { return boost::arg<I>(); }
};

#endif

 

   上述截取了部分storageN的类模板代码,可以看出storageN是个单继承体系,N表示了继承体系的总层级,例如storage2则表示整体继承体系一共2层,第一层存储了参数a1, 第二层公有继承于storage1,存储参数a2;至于为什么使用这样的实现方式进行存储,后续再深入研究;另外需要注意的是,是A1,A2的类型决定了参数存储方式;如果const reference&, 实参是以引用的形式存储的。后续值得关注下,A1,A2在经过函数模板的类型推导后,是否存有中间加工的元编程处理过程;

   功能(3)的实现中使用了unwrapper类模板对F的外覆类进行解外覆处理;ref.hpp实际是boost中的另外一个基础核心工具集之一;后续单独研究下;

   接下来,就是重点分析下,如何从占位符到实参的绑定,并且bind_t的对绑定函数的实际调用;这里自然要先粘贴下_bi::bind_t代码的实现摘要;

   

template< class A > struct list_add_cref {
    typedef A const & type;
};

template< class A > struct list_add_cref< A& >{
    typedef A & type;
};

template<class R, class F, class L> class bind_t {
private:

    F f_;
    L l_;

public:

    typedef typename result_traits<R, F>::type result_type;
    typedef bind_t this_type;

    bind_t( F f, L const & l ): f_( f ), l_( l ) {}
    ... ... 
    template<class A1, class A2> result_type operator()( A1 && a1, A2 && a2 )
    {
        list2< typename list_add_cref<A1>::type, typename list_add_cref<A2>::type > a( a1, a2 );
        return l_( type<result_type>(), f_, a, 0 );
    }

    template<class A1, class A2> result_type operator()( A1 && a1, A2 && a2 ) const
    {
        list2< typename list_add_cref<A1>::type, typename list_add_cref<A2>::type > a( a1, a2 );
        return l_( type<result_type>(), f_, a, 0 );
    }
    …… …… 
    
     template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2)
    {
        list2<A1 &, A2 &> a(a1, a2);
        BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
    }

     template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2) const
    {
        list2<A1 &, A2 &> a(a1, a2);
        BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
    }
…… ……

    上述第一参数列表推导后,加工处理后的组成的list_type类型,被推导成L类型;同时,根据第二参数列表的个数及类型,会找对应参数个数的operator()实例化调用;第二参数列表会被推到成类型A1,A2 ....并组成另外一个listN类型且该类型的实例是a;如前所述,在组成listN的过程中,通过list_add_cref<T>元编程处理过程针对A1,A2推导的类型进行add const reference处理,以确保实参以最低成本存储在storageN中。L类型中的多个operator()的重载形式之一被调用时,list_type的operator[]实现了最终的占位符到实参(第二参数列表中的)的绑定;

猜你喜欢

转载自qqsunkist.iteye.com/blog/2205036