詳細解析boost中bind的實現

來源:互聯網
上載者:User
寫在前面的話

在C++11之後,std::bind是C++標準庫的一個組件了。一開始想弄個C++11的實現來研究下,發現裡面用到了可變參數模板(代碼變得非常神奇).

http://llvm.org/svn/llvm-project/libcxx/trunk/include/functional

還是弄個原始點的boost的實現來研究下。

話說網上關於boost::bind的實現的文章也有不少,不過大多數都是貼一段代碼,再扯一通,結果到頭來什麼都沒看明白。(起碼LZ是。。)

花了一天的功夫,最終從boost::bind的原始碼中摳出了可編繹啟動並執行代碼。

下面一步一步來解析boost::bind的實現。

標準庫中的fouctor和bind1st的實現

首先從簡單的入手。先看下標準庫中的fouctor和bind1st的實現(為了防止和標準庫的命名衝突,全部都在命名後面加了2)。

#include <iostream>#include <algorithm>using namespace std;template<typename _Arg1, typename _Arg2, typename _Result>struct binary_function2 {typedef _Arg1 first_argument_type;typedef _Arg2 second_argument_type;typedef _Result result_type;};template<typename _Tp>struct equal_to2: public binary_function2<_Tp, _Tp, bool> {bool operator()(const _Tp& __x, const _Tp& __y) const {return __x == __y;}};template<typename _Arg, typename _Result>struct unary_function2 {typedef _Arg argument_type;typedef _Result result_type;};template<typename _Operation>class binder1st2: public unary_function2<typename _Operation::second_argument_type,typename _Operation::result_type> {protected:_Operation op;typename _Operation::first_argument_type value;public:binder1st2(const _Operation& __x,const typename _Operation::first_argument_type& __y) :op(__x), value(__y) {}typename _Operation::result_type operator()(const typename _Operation::second_argument_type& __x) const {return op(value, __x);}typename _Operation::result_type operator()(typename _Operation::second_argument_type& __x) const {return op(value, __x);}};template<typename _Operation, typename _Tp>inline binder1st2<_Operation> bind1st2(const _Operation& __fn, const _Tp& __x) {typedef typename _Operation::first_argument_type _Arg1_type;return binder1st2<_Operation>(__fn, _Arg1_type(__x));}int main() {binder1st2<equal_to2<int> > equal_to_10(equal_to2<int>(), 10);int numbers[] = { 10, 20, 30, 40, 50, 10 };int cx;cx = std::count_if(numbers, numbers + 6, bind1st(equal_to2<int>(), 10));cout << "There are " << cx << " elements that are equal to 10.\n";}

上面的實現還是比較簡單的,希望還沒有看暈。:)

從代碼可以看出binder1st的實現,實制上是把參數儲存起來,等到調用時,再取出來使用。

boost::bind從原理上來說,也是這麼回事,但是要複雜得多。

解析簡化版bind2

下面是從boost::bind源碼中摳出來的,一個簡單的bind的實現(bind改為bind2),主流編譯器應該都可以編統執行。

為了方便理解程式運行過程,代碼中增加了一些cout輸出。

myBind.h:

#ifndef BOOST_BIND_BIND_HPP_INCLUDED__Mybind#define BOOST_BIND_BIND_HPP_INCLUDED__Mybind#include <iostream>using namespace std;namespace boost {template<class T> struct is_placeholder {enum _vt {value = 0};};template<int I> struct arg {arg() {}};template<class T>struct type {};namespace _bi // implementation details{template<class A1> struct storage1 {explicit storage1(A1 a1) :a1_(a1) {cout<<"storage1  storage1(A1 a1)"<<endl;}A1 a1_;};template<int I> struct storage1<boost::arg<I> > {explicit storage1(boost::arg<I>) {cout<<"storage1  storage1(boost::arg<I>)"<<endl;}static boost::arg<I> a1_() {return boost::arg<I>();}};template<int I> struct storage1<boost::arg<I> (*)()> {explicit storage1(boost::arg<I> (*)()) {cout<<"storage1  storage1(boost::arg<I> (*)())"<<endl;}static boost::arg<I> a1_() {return boost::arg<I>();}};// 2template<class A1, class A2> struct storage2: public storage1<A1> {typedef storage1<A1> inherited;storage2(A1 a1, A2 a2) :storage1<A1>(a1), a2_(a2) {cout<<"storage2  storage2(A1 a1, A2 a2)"<<endl;}A2 a2_;};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) {cout<<"storage2  storage2(A1 a1, boost::arg<I>)"<<endl;}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) {cout<<"storage2  storage2(A1 a1, boost::arg<I> (*)())"<<endl;}static boost::arg<I> a2_() {return boost::arg<I>();}};// result_traitstemplate<class R, class F> struct result_traits {typedef R type;};struct unspecified {};template<class F> struct result_traits<unspecified, F> {typedef typename F::result_type type;};// valuetemplate<class T> class value {public:value(T const & t) :t_(t) {}T & get() {return t_;}private:T t_;};// typetemplate<class T> class type {};// unwraptemplate<class F> struct unwrapper {static inline F & unwrap(F & f, long) {return f;}};// listNclass list0 {public:list0() {}template<class T> T & operator[](_bi::value<T> & v) const {cout << "list0  T & operator[](_bi::value<T> & v)" << endl;return v.get();}template<class R, class F, class A> R operator()(type<R>, F & f, A &,long) {cout << "list0  R operator()(type<R>, F & f, A &, long)" << endl;return unwrapper<F>::unwrap(f, 0)();}template<class F, class A> void operator()(type<void>, F & f, A &, int) {cout << "list0  void operator()(type<void>, F & f, A &, int)" << endl;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 R, class F, class A> R operator()(type<R>, F & f, A & a,long) {return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]);}//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 {cout << "list2  A1 operator[](boost::arg<1>)" << endl;return base_type::a1_;}A2 operator[](boost::arg<2>) const {cout << "list2  A1 operator[](boost::arg<2>)" << endl;return base_type::a2_;}A1 operator[](boost::arg<1> (*)()) const {cout << "list2  A1 operator[](boost::arg<1> (*)())" << endl;return base_type::a1_;}A2 operator[](boost::arg<2> (*)()) const {cout << "list2  A1 operator[](boost::arg<2> (*)())" << endl;return base_type::a2_;}template<class T> T & operator[](_bi::value<T> & v) const {cout << "T & operator[](_bi::value<T> & v)" << endl;return v.get();}template<class R, class F, class A> R operator()(type<R>, F & f, A & a,long) {return f(a[base_type::a1_], a[base_type::a2_]);//return 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_]);//}};// bind_ttemplate<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<R, F>::type result_type;result_type operator()() {cout << "bind_t::result_type operator()()" << endl;list0 a;return l_(type<result_type>(), f_, a, 0);}template<class A1> result_type operator()(A1 & a1) {list1<A1 &> a(a1);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);return l_(type<result_type>(), f_, a, 0);}private:F f_;L l_;};template<class T, int I> struct add_value_2 {typedef boost::arg<I> type;};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<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;};// list_av_Ntemplate<class A1> struct list_av_1 {typedef typename add_value<A1>::type B1;typedef list1<B1> 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;};} // namespace _bi// function pointerstemplate<class R>_bi::bind_t<R, R (*)(), _bi::list0> bind2(R (*f)()) {typedef R (*F)();typedef _bi::list0 list_type;return _bi::bind_t<R, F, list_type>(f, list_type());}template<class R, class B1, class A1>_bi::bind_t<R, R (*)(B1), typename _bi::list_av_1<A1>::type> bind2(R (*f)(B1),A1 a1) {typedef R (*F)(B1);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 B1, class B2, class A1, class A2>_bi::bind_t<R, R (*)(B1, B2), typename _bi::list_av_2<A1, A2>::type> bind2(R (*f)(B1, B2), A1 a1, A2 a2) {typedef R (*F)(B1, B2);typedef typename _bi::list_av_2<A1, A2>::type list_type;return _bi::bind_t<R, F, list_type>(f, list_type(a1, a2));}} // namespace boostnamespace {boost::arg<1> _1;boost::arg<2> _2;}#endif // #ifndef BOOST_BIND_BIND_HPP_INCLUDED__Mybind

main.cpp:

#include <iostream>#include "myBind.h"using namespace std;void tow_arguments(int i1, int i2) {std::cout << i1 << i2 << '\n';}class Test{};void testClass(Test t, int i){cout<<"testClass,Test,int"<<endl;}int main() {int i1 = 1, i2 = 2;(boost::bind2(&tow_arguments, 123, _1))(i1, i2);(boost::bind2(&tow_arguments, _1, _2))(i1, i2);(boost::bind2(&tow_arguments, _2, _1))(i1, i2);(boost::bind2(&tow_arguments, _1, _1))(i1, i2);(boost::bind2(&tow_arguments, 222, 666))(i1, i2);Test t;(boost::bind2(&testClass, _1, 666))(t, i2);(boost::bind2(&testClass, _1, 666))(t);(boost::bind2(&testClass, t, 666))();}

在上面的代碼中有很多個類,有幾個是比較重要的any,storage,list和bind_t。

先來看類any:

template<int I> struct arg {arg() {}};namespace {boost::arg<1> _1;boost::arg<2> _2;}

在上面的代碼中,我們看到了_1和_2,沒錯,所謂的預留位置就是這麼個東東,在匿名名字空間中定義的空的struct。

接下來是storage1,storage2類:

template<class A1> struct storage1 {explicit storage1(A1 a1) :a1_(a1) {}A1 a1_;};...// 2template<class A1, class A2> struct storage2: public storage1<A1> {typedef storage1<A1> inherited;storage2(A1 a1, A2 a2) :storage1<A1>(a1), a2_(a2) {}A2 a2_;};...

可以看出storage類只是簡單的繼承關係,實際上storage類是用來儲存bind函數所傳遞進來的參數的值的,之所以採用繼承而不用組合,其實有精妙的用處。

接著看類list1和list2,它倆分別是storage1和storage2的子類(即參數是存放在listN類中的)

template<class A1> class list1: private storage1<A1> {...}template<class A1, class A2> class list2: private storage2<A1, A2> {...}

仔細看代碼,可以看到list1和list2重載了operator [ ] 和operator () 函數,這些重載函數很關鍵,下面會談到。

再看類bind_t:

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<R, F>::type result_type;result_type operator()() {cout<<"bind_t::result_type operator()()"<<endl;list0 a;return l_(type<result_type>(), f_, a, 0);}...private:F f_;   //bind所綁定的函數L l_;   //實際是是listN類,存放bind傳綁定的參數};

同樣,我們可以看到bind_t類重載了operator ()函數,實際上,bind_t類是bind函數的傳回型別,bind_t實際上是一個stl意義上的funtor。

注意bind_t的兩個成員F f_ 和 L l_,這裡是關鍵之處。

介紹完關鍵類,下面以main.cpp中下面部分代碼為例,詳細說明它的工作流程。

int i1 = 1, i2 = 2;(boost::bind2(&tow_arguments, 123, _1))(i1, i2);

首先bind2函數返回一個bind_t類,這個類中的F成員,儲存了tow_arguments函數指標,L成員(即list2類),儲存了參數123 和 _1。

bind_t類採用的是以下的特化:

template<class R, class B1, class B2, class A1, class A2>_bi::bind_t<R, R (*)(B1, B2), typename _bi::list_av_2<A1, A2>::type> bind2(R (*f)(B1, B2), A1 a1, A2 a2)

其中storage2類(list2的父類)和storage1類,分別採用的是以下的特化:

template<class A1, int I> struct storage2<A1, boost::arg<I> > : public storage1<A1>
template<class A1> struct storage1

(這裡可以試下把123和_1互換位置,就能發現storage系列類用繼承的精妙之處了,這裡不展開了)

當bind_t調用operator (i1, i2)函數時,即以下函數:

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

再次產生一個list2,這個list2中儲存的是i1 和 i2的值。接著調用l_.operator() (type<result_type>(), f_, a, 0)函數(還記得l_是什麼東東不?)

l_實際是上剛才儲存了123和_1的list2!而f_是由bind函資料繫結的函數的指標!

接著看list2的operator() 函數的實現:

template<class R, class F, class A> R operator()(type<R>, F & f, A & a,long) {return f(a[base_type::a1_], a[base_type::a2_]);//return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); //本是這句的,簡化了下,無關要緊}

可以看到f是bind所綁定的函數的指標,即這裡是調用了我們所綁定的函數。那麼參數是從哪裡來的?

注意A &a實際上是儲存了i1和i2的list2!,所以這裡又調用了list2的operator[ ]函數!

再看下list2的operator[] 函數的實現:

A1 operator[](boost::arg<1>) const {cout << "list2  A1 operator[](boost::arg<1>)" << endl;return base_type::a1_;}A2 operator[](boost::arg<2>) const {cout << "list2  A1 operator[](boost::arg<2>)" << endl;return base_type::a2_;}A1 operator[](boost::arg<1> (*)()) const {cout << "list2  A1 operator[](boost::arg<1> (*)())" << endl;return base_type::a1_;}A2 operator[](boost::arg<2> (*)()) const {cout << "list2  A1 operator[](boost::arg<2> (*)())" << endl;return base_type::a2_;}template<class T> T & operator[](_bi::value<T> & v) const {cout << "T & operator[](_bi::value<T> & v)" << endl;return v.get();}

貌似問題都解決了,其實這裡還有一個關鍵要素,兩個list2是怎麼合并起來,組成正確的參數傳遞給函數f的?(第一個list2存放了123和_1,第二個list2存放了i1和i2)

傳遞給tow_arguments函數的參數實際是是(123, 1)!

這裡要仔細研究這些operator[]函數的實現,才能真正理解其過程。

注意,上面的的代碼中bind2隻能綁定普通的函數,不能綁定類成員函數,functor,智能指標等。不過其實區別不大,只是增加一些特化的代碼而已。

還有一些const相關的函數刪掉了。

後記:

從代碼可以看到boost::bind和原生的函數指標相比,會損失效率(兩次的定址,函數參數的拷貝,有一些可能沒有inline的函數調用)。

不得不吐槽下boost的代碼中那些神奇的宏,如果不是IDE有提示,我想真心弄不明白到底哪段是有意義的。。有時候還很神奇地include一部分代碼進來(不是標頭檔,只是實現的一部分!)。

不得不吐槽下模板編程中的const,為了一個函數,比如int sum(int a, int b); 就得寫四個重載函數來對應不同參數是或者不是const的情況。所以大家可以想像bind最多支援9個參數,那麼有多少種情況了。

boost::bind的實現的確非常精巧,隱藏了很多複雜性。在《C++沉思錄》中作者說到世界是複雜的,可以通過複雜性擷取簡單性。也許這個是對的,但是對於無數的後來的程式員總會有人想要看看黑盒子裡到底是什麼東東,結果總要花大量的時間才能理解,才能獲得這種“簡單性”。

C++的模板代碼中最痛苦的是到處都是typedef,一個typedef就把所有的類型資訊都幹掉了!而這東東又是必須的。

也許C++給程式設計語言技術帶來的最大貢獻就是牛B的編譯器了!

C++11中貌似部分的支援concept,希望這個能簡化模板編程。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.