程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> boost bind初步探究

boost bind初步探究

編輯:C++入門知識

最近對boost的bind部分比較感興趣,對其背後的機制進行了簡單的分析,和大家分享一下。

 


注,我所看的代碼是boost_1_51_0, 想來各個版本的差異不大。

從一個例子開始
直接上代碼(從官方網站摘取)

 


定義函數


[cpp]
int f(int a, int b) 

    return a + b; 

 
int g(int a, int b, int c) 

    return a + b + c; 

int f(int a, int b)
{
    return a + b;
}

int g(int a, int b, int c)
{
    return a + b + c;
}調用范例:


bind(f, 1, 2)();                  //f(1,2)

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)


_1, _2, ... _9在 boost中被稱為placeholder,是占位符的意思。它表示參數。這種方式,我是只在boost中見過,是個非常神奇的用法。

它們究竟是什麼呢?,且看定義:(boost/bind/placeholders.hpp)


[cpp]
boost::arg<1> _1; 
boost::arg<2> _2; 
boost::arg<3> _3; 
boost::arg<4> _4; 
boost::arg<5> _5; 
boost::arg<6> _6; 
boost::arg<7> _7; 
boost::arg<8> _8; 
boost::arg<9> _9; 

boost::arg<1> _1;
boost::arg<2> _2;
boost::arg<3> _3;
boost::arg<4> _4;
boost::arg<5> _5;
boost::arg<6> _6;
boost::arg<7> _7;
boost::arg<8> _8;
boost::arg<9> _9;
boost::arg也是個模板,至於是什麼樣的模板,留個懸念吧。

 

boost bind的這些功能,顛覆了我對C++的看法,從未想到過,C++還可以這麼玩。那麼,boost究竟是怎麼實現的呢?

 


讀者請注意,bind在這裡涉及了兩個參數表。第一個參數表是被bind綁定的函數(例子中f,g函數)的參數表,另外一個是bind生成的新的函數對象的參數表。

這兩個參數表如何實現?如何轉換是我們後面分析的重點。

bind是什麼?
bind是函數,是非常神奇的函數,不是一個函數,而是一組函數,是一組重載的函數。

翻開代碼 boost/bind/bind.hpp,找到BOOST_BIND字符串,大約在1290行的位置,boost定義了bind(1.51.0):


[cpp]
// bind  
 
#ifndef BOOST_BIND  
#define BOOST_BIND bind  
#endif  
 
// generic function objects  
 
template<class R, class F> 
    _bi::bind_t<R, F, _bi::list0> 
    BOOST_BIND(F f) 

    typedef _bi::list0 list_type; 
    return _bi::bind_t<R, F, list_type> (f, list_type()); 

 
template<class R, class F, class A1> 
    _bi::bind_t<R, F, typename _bi::list_av_1<A1>::type> 
    BOOST_BIND(F f, A1 a1) 

    typedef typename _bi::list_av_1<A1>::type list_type; 
    return _bi::bind_t<R, F, list_type> (f, list_type(a1)); 

 
 
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)); 

.... 

// bind

#ifndef BOOST_BIND
#define BOOST_BIND bind
#endif

// generic function objects

template<class R, class F>
    _bi::bind_t<R, F, _bi::list0>
    BOOST_BIND(F f)
{
    typedef _bi::list0 list_type;
    return _bi::bind_t<R, F, list_type> (f, list_type());
}

template<class R, class F, class A1>
    _bi::bind_t<R, F, typename _bi::list_av_1<A1>::type>
    BOOST_BIND(F f, A1 a1)
{
    typedef typename _bi::list_av_1<A1>::type list_type;
    return _bi::bind_t<R, F, list_type> (f, list_type(a1));
}


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));
}
....
太多了,只貼3個,足以說明問題。

模板參數


R 表示返回類型
F  表示函數指針的類型
A1,A2, .... 這些都是參數的類型
boost將BOOST_BIND定義為bind。所以,你看到的BOOST_BIND就是對bind函數的定義。


bind函數非常簡單,它其實就是返回一個bind_t類的對象。bind_t類也是一個模板類。 如果仔細觀察,你可以發現,這些不同參數的bind函數,其實現上不同在於list_av_N這一系列的輔助類是不同的。list_av_1表示一個參數,list_av_2表示兩個參數, 以此類推。


這裡的list_av_N對象,是第一個參數表,是函數F要求的參數表,這個參數表是可以包含placeholder的。


list_av_N對象其實只是一個包裝對象。我們以list_av_2為例:
[cpp]
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的作用,僅僅是為了包裝而已。list_av_2::type == ist2<A1,A2> == list_type。


bind_t是什麼東東?
奧秘在bind_t中,且看bind_t的定義 (也在boost/bind/bind.hpp中。下面只是bind_t的一種定義方法,但是道理都是一樣的)
[html]

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
   
};
模板參數R代表return type, F代表function type, L表示的是listN(list0,list1,list2,....),這個是關鍵啊。


至於bind_template.hpp,這個源代碼也比較簡單,主要是定義了operator () 的實現。


bind_t重載括號運算符,因此,bind_t可以像函數那樣調用。而且,bind_t的operator()有N多個重載,分別對應的是不同的參數類型和參數個數。這使得我們可以用不同的參數調用bind_t的對象。


我們摘抄一個有一兩個參數的括號重載,看看
[cpp]
..... 
    template<class A1> result_type operator()(A1 & a1) 
    { 
        list1<A1 &> a(a1); 
        BOOST_BIND_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> result_type operator()(A1 & a1)
    {
        list1<A1 &> a(a1);
        BOOST_BIND_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);
    }
......
f_就是函數指針,這個不用多說;l_是 L (listN)對象。


請注意,這裡有兩個listN出現:
一個是l_ 這是針對f_提供的參數列表,其中包含_1,_2,....這樣的placeholder
一個是生成的臨時變量 a, 這個是bind_t函數對象在調用時 的參數列表
之所以一直強調兩個listN,是因為,奧秘就在listN這些個類中的。大家請記住這一點:一直存在兩個listN對象。


listN的奧秘
bind_t對象的operator () 調用的是listN 的operator (),那麼,整個實現,就在listN中,為了方便說明,我們以list2為例。


對list2的分析,我們只看3部分:
1. 類聲明部分:
[cpp]
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 ) {} 

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 ) {}
從這個定義,我們知道,它從storage2繼承,storage2是什麼?
[cpp]
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_;
};
從名字和定義上,我們就可以斷定:storage2就是保存兩個參數的參數列表對象。看來,storageN負責存儲,而listN負責如何使用這些參數了。


2. operator[] 系列重載函數
[cpp]
  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(); } 
..... 

    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(); }
.....

我已經剔除了一些定義,只留下我們關系的定義。


這裡面有兩類定義,
針對 boost::arg<1>和boost::arg<2>定義的。其實就是針對_1, _2的定義,這個定義表明:如果是_1,那麼,list2就返回存儲的參數a1, 如果是_2,那麼就返回存儲的參數a2。這些參數,是上面我說的針對f_函數的參數;
針對_bi::value<T>的定義。_bi::value<T>就是對T進行簡單封裝的類型。這個定義僅僅是將value的值再取出來。
這兩類定義,就是關鍵所在了。


3. operator()系列重載函數
[html]
.... 
    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_]);
    }
....

盡管有多種不同的重載,但是基本形式就是這樣的。
最後一個參數"int"我想沒有直接的意義,可能是為了重載時用於區分不同的重載函數使用的。


unwrap的作用這裡可以忽略。你可以認為就是直接調用f。


下面有兩個不同尋常的語句:[html] view plaincopyprint?a[base_type::a1_], a[base_type::a2_] 

a[base_type::a1_], a[base_type::a2_]a是一個listN對象,這兩句究竟是什麼意思呢?


下面,我們用兩個例子分別說明,
例子1
bind(f, 1, 2)();                  //f(1,2)下面,我們將參數代入:這種情況下,我們得到的bind_t對象,實際上是
[cpp]
bind_t<int, int(*)(int,int), list2<int,int> >  
//l_的類型和值  
list2<int,int> = [ base_type::a1_ = 1, base_type::a2_ = 2] 

bind_t<int, int(*)(int,int), list2<int,int> >
//l_的類型和值
list2<int,int> = [ base_type::a1_ = 1, base_type::a2_ = 2]
而bind(f, 1, 2) (); 中最後一個括號,調用了bind_t的operator () (void) :
[cpp]
result_type operator()() 

    list0 a; 
    BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0); 

    result_type operator()()
    {
        list0 a;
        BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
    }
因此,a = list0。
在這種情況下,
[cpp] v
a[base_type::a1_]  = = 
a.operator[]((_bi::value<int>) 1) 
== 1; 
 a[base_type::a2_] == 
a.operator[](_bi::value<int>) 2) 
== 2; 

a[base_type::a1_]  = =
a.operator[]((_bi::value<int>) 1)
== 1;
 a[base_type::a2_] ==
a.operator[](_bi::value<int>) 2)
== 2;其實listN裡面的operator[](_bi::value<T>)就是打醬油的,目的就是為了在語法上統一。


因此,bind(f, 1, 2) () === f(1,2)的調用。


例子2
bind(f, _2, _1)(x, y);                 // f(y, x)我們再把參數代入,這是,得到的bind_t對象就是
[html]
bind_t<int, int(*)(int, int), list2<boost::arg<2>, boost::arg<1> > > 
//l_的類型和值是 
list2<boost::arg<2>,boost::arg<1> > = [ base_type::a1_ = _2, base_type::a2_ = _1] 

bind_t<int, int(*)(int, int), list2<boost::arg<2>, boost::arg<1> > >
//l_的類型和值是
list2<boost::arg<2>,boost::arg<1> > = [ base_type::a1_ = _2, base_type::a2_ = _1]哈哈,看到了吧,list2的類型不一定要和F的參數類型一樣的哦。


當調用bind(f, _2, _1)(x, y); 中bind_t::operator()(int, int) 的時候,即
[cpp]
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)
    {
        list2<A1 &, A2 &> a(a1, a2);
        BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
    }
此時,a的類型和值是
[cpp] v
list2<int, int> = [ base_type::a1_= x, base_type::a2_ = y] 

list2<int, int> = [ base_type::a1_= x, base_type::a2_ = y]這種情況下,
[html]
a[l_.base_type::a1_] == 
  a [ _2 ] == 
 a.operator[] ( (boost::arg<2>&)_2) == 
 a.base_type::a2_ == 
y; 
 
a[l_.base_type::a2_] == 
  a [ _1 ] == 
 a.operator[] ( (boost::arg<1>&)_1) == 
 a.base_type::a1_ == 
x; 

a[l_.base_type::a1_] ==
  a [ _2 ] ==
 a.operator[] ( (boost::arg<2>&)_2) ==
 a.base_type::a2_ ==
y;

a[l_.base_type::a2_] ==
  a [ _1 ] ==
 a.operator[] ( (boost::arg<1>&)_1) ==
 a.base_type::a1_ ==
x;

即 bind(f, _2, _1)(x, y) === f(y, x);


關於_1,_2,_3,...._9
現在,我們要看看,到底 boost::arg<1>, boost::arg<2> ... boost::arg<9>是什麼了。
[cpp]
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 ]; 
    }    
}; 

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

呵呵,什麼都沒有!的確,什麼都沒有,因為它是placheholder嘛! 它只要表明自己的類型就可以了。這是為什麼在 listN的operator[] 中,boost::arg<N> 沒有定義形慘了。


第二個帶有 T const & 參數的構造函數是什麼?看起來很奇怪,其實,它是拷貝構造函數。


看看is_placeholder的定義吧:
[cpp]
template< int I > struct is_placeholder< arg<I> > 

    enum _vt { value = I }; 
}; 

template< int I > struct is_placeholder< arg<I> >
{
    enum _vt { value = I };
};

它的作用,是防止錯誤的參考構造。
假如,你這樣定義:
[cpp]
boost::arg<2> arg2(_1); 

boost::arg<2> arg2(_1);模板展開後,將是這樣的
[cpp]
struct arg <2> 
{  
..... 
    arg( arg<1> const & /* t */ ) 
    {    
        // static assert I == is_placeholder<T>::value  
        typedef char T_must_be_placeholder[ I == is_placeholder<arg<1> >::value? 1: -1 ]; 
    }    
}; 

struct arg <2>
{
.....
    arg( arg<1> const & /* t */ )
    {  
        // static assert I == is_placeholder<T>::value
        typedef char T_must_be_placeholder[ I == is_placeholder<arg<1> >::value? 1: -1 ];
    }  
};is_placeholder<arg<1> >::value == 1,而I == 2,因此,你會得到一個
[cpp]
typedef char T_must_be_placeholder[ -1 ]; 

typedef char T_must_be_placeholder[ -1 ];因此,你將收到一個編譯錯誤。僅此而已。


其他
到此為止,boost bind的關鍵部分就已經清楚了。boost還有些高級議題,如類的成員函數的綁定、變量引用、綁定嵌套等等。這些議題都是以此為基礎,再增加了一些新模板參數而已,已經不是實現的核心了,有興趣的同學可以自己看看。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved