当前访客身份:游客 [ 登录 | 加入开源中国 ]
当前访客身份: 游客 [ 登录 | 加入开源中国 ]
yellia_c yellia_c
.
最新评论
访客统计
  • 9
  • 1
  • 9
  • 9
  • 1228

Boost::Bind 源码实现简化版

发表于1年前(2012-11-21 10:37)   阅读(379) | 评论(1 0人收藏此文章,
0

本着看懂boost::bind整体流程的角度出发,将boost::bind的源码精简贴出,去除了所有多余部分,只在说明问题,错误在所难免,希望大家勿喷。

bind_t对象就是boost::bind方法的返回值,它保存了F和L的值。其中F为函数指针类型,L为listN类型(N的范围为0-9),它将保存用户调用boost::bind方法时传入的绑定参数。例如: void fun_1(int left, int right){//dosomething} boost::bind(&fun_1, 100, 200); 此时,将产生一个list2对象保存在生成的bind_t对象中,list2中包含100,200. 不是listN吗?怎么变成了list2呢,这就是大牛们写了那么一大堆泛型代码的作用了,它是在编译阶段,由强大的编译器通过类型检查推断出来的。下面展示bind_t源码:主要是由bind.hpp和bind_template.hpp提取出来的。

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) {}
    
    typedef typename result_traits::type result_type;

    result_type operator()()
    {
        list0 a;
        BOOST_BIND_RETURN l_(type(), f_, a, 0);
    }

    templateresult_type operator()(A1 & a1)
    {
        list1 a(a1);
        BOOST_BIND_RETURN l_(type(), f_, a, 0);
    }

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

    ...

    template result_type operator()(A1 & a1, A2 & a2, A3 & a3, A4 & a4, A5 & a5, A6 & a6, A7 & a7, A8 & a8)
    {
        list8 a(a1, a2, a3, a4, a5, a6, a7, a8);
        BOOST_BIND_RETURN l_(type(), f_, a, 0);
    }
};


大家可以清晰的看到bind_t对象中只有一个构造函数,和多个operator()(**)重载函数,这意味着什么?这意味着,每个bind_t都有多个operator()(**)方法。所以你可以这样:
boost::bind(&fun_1, 100, 200)();
boost::bind(&fun_1, 100, 200)(100,200);
boost::bind(&fun_1, 100, 200)(100,200,300,400);
boost::bind(&fun_1, 100, 200)(100,200,300,400,..,900);
都不会报错,但是如果你这样:
boost::bind(&fun_1, 100, _1)();//报错,
会提示找不到list0的operator[](boost::arg)方重载
boost::bind(&fun_1, 100, _1)(100);//正确
boost::bind(&fun_1, 100, 200)();//正确

??为什么呢?大家可以先看看对应的bind_t中operator()(**)方法,相信大家可能只能那么一点点了,不过要想真正的想通,我们还得看另外一组类,listN. 我只列举list0,list1,list2, 其余的list3-list9相信大家可以举一反三。

class list0
{
public:

    list0() {}

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

    template<class R, class F, class A> R operator()(type<R>, F & f, A &, long)
    {
        return unwrapper<F>::unwrap(f, 0)();
    }
};
template< class A1 > class list1: private storage1< A1 >
{
private:

    typedef storage1< A1 > base_type;

public:

    explicit list1( A1 a1 ): base_type( a1 ) {}

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

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

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

    template<class F, class A> void operator()(type<void>, F & f, A & a, int)
    {
        unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]);
    }
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 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_]);
    }
};

大家可以看到每个listN类里都有一组operator[] 和 operator()方法,其中operator()就是在bind_t的operator()中被调用的方法(红色背景标注的),而在listN的operator()中只有一句非常重要,就是他完成了用户绑定参数和函数调用时实参之间的转化,下面我们还是拿上面那个错误的例子分析一下:

boost::bind(&fun_1, 100, _1)();//报错,

经上面的分析大家可以知道如下信息:

F = void (*)(int,int)

L = list2<int,boost::arg<1> >//这个不准确,大家看源代码,意思知道就行了

list2<int,int>::base_type::a1_ = 100

list2<int,int>::base_type::a2_ = _1;

将会调用bind_t对象的方法,如下:

result_type operator()()
{
        list0 a;
        BOOST_BIND_RETURN l_(type(), f_, a, 0);
}
接着调用list2对象的方法,如下:
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_]);
}

此时 f_  = &fun_1, a = list0

用实参替换,简化可得:

f(a[100], a[_1]);

再进一步, 调用list0的operator[]的方法:

a[100]调用的方法声明如下:

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

因此返回100,进一步简化:

f(100, a[_1]);

这时问题出现了,list0中没有对应于a[_1],也就是类似于下面方法的重载,当然会报错了!!

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

至此,问题解答完毕,再来总结一下:

boost::bind(fun, arg1, arg2, ...,argN)(a1, a2,...,aM);

1、会产生两个listN对象,第一个listN对应于arg1,arg2实参个数,arg1,arg2的个数必须和fun函数声明一直,否则报错。第二个listN对象由a1,a2的个数决定。

2、如果arg1,arg2为占位符,那么必须保证 对应于a1,a2生成的listN对象中必须存在其对应的operator[](boost::argv<N>)的重载函数,否则肯定报错!如何保证呢, 其实很简单。M的个数>= arg1-argN中占位符的最大值. 

举个例子:

    boost::bind(fun, 100, _1)(/*注意此时,至少要生成list1,就是至少要传一个参数*/);

    boost::bind(fun, 100, _1, _9)(/*注意此时,至少要生成list9,就是至少要传9个参数*/);

完结,希望对大家有帮助。

分享到: 0
声明:OSCHINA 博客文章版权属于作者,受法律保护。未经作者同意不得转载。

评论

yellia_c

插入: 表情 开源软件

关闭插入表情