在上一篇blog中簡單的實現了boost::function,支持帶有2個參數的函數/函數指針,函數對象,函數適配器/bind類,以及帶有1個參數的成員函數指針。
本文接著來介紹如何實現一個簡單的boost::bind。
基本目標如下:
1、支持接收0個參數的函數/函數指針,函數對象。
2、支持接收1個參數的函數/函數指針,函數對象。
3、支持接收2個參數的函數/函數指針,函數對象。
首先,解決占位符的問題:
1 namespace 2 { 3 4 struct Placeholders1 5 { 6 } _1; 7 8 struct Placeholders2 9 { 10 } _2; 11 12 }
使用匿名namespace的原因是防止不同編譯單元中的命名沖突, 讓占位符對象只在其所在的編譯單元中可見。
在boost::bind源碼中主要是通過2個list表維持各種相關信息。一個bindlist表維持了bind接收的綁定參數,包括占位符,用戶傳入的變量等。一個calllist維持了調用bind返回的對象時所傳入的參數信息。它們的通過繼承層次的方式來表現的。
下面這個繼承層次的每一個類都要作為對應的bindlist和calllist層次中的基類,它們分別保存了bind接收的綁定參數信息(用戶傳入的變量,占位符),以及調用bind返回的對象時所傳入的參數信息。
1 class Base0 2 { 3 }; 4 5 template<typename T1> 6 class Base1 : public Base0 7 { 8 public: 9 Base1(T1 data1) 10 : data1_(data1) 11 { 12 } 13 14 protected: 15 T1 data1_; 16 }; 17 18 template<typename T1, typename T2> 19 class Base2 : public Base1<T1> 20 { 21 public: 22 Base2(T1 data1, T2 data2) 23 : Base1<T1>(data1), data2_(data2) 24 { 25 } 26 27 protected: 28 T2 data2_; 29 };
接著,就是所謂的calllist的實現了。它們的基類將保存調用bind返回的對象時所傳入的參數信息。
1 class CallList0 : public Base0 2 { 3 public: 4 template<typename _T> 5 _T operator[](_T arg) 6 { 7 return arg; 8 } 9 }; 10 11 template<typename T1> 12 class CallList1 : public Base1<T1> 13 { 14 public: 15 CallList1(T1 data1) 16 : Base1<T1>(data1) 17 { 18 } 19 20 T1 operator[](Placeholders1 arg1) 21 { 22 return Base1<T1>::data1_; 23 } 24 25 template<typename _T> 26 _T operator[](_T arg) 27 { 28 return arg; 29 } 30 31 }; 32 33 template<typename T1, typename T2> 34 class CallList2: public Base2<T1, T2> 35 { 36 public: 37 CallList2(T1 data1, T2 data2) 38 : Base2<T1, T2>(data1, data2) 39 { 40 } 41 42 T1 operator[](Placeholders1 arg1) 43 { 44 return Base2<T1, T2>::data1_; 45 } 46 47 T2 operator[](Placeholders2 arg2) 48 { 49 return Base2<T1, T2>::data2_; 50 } 51 52 template<typename _T> 53 _T operator[](_T arg) 54 { 55 return arg; 56 } 57 };
然後,我們來看看bindlist,它們的基類主要保存了bind接收的占位符、參數信息。
1 class BindLinst0 : public Base0 2 { 3 public: 4 template<typename Func> 5 void operator()(Func func) 6 { 7 func(); 8 } 9 }; 10 11 template<typename T1> 12 class BindList1 : public Base1<T1> 13 { 14 public: 15 BindList1(T1 data1) 16 : Base1<T1>(data1) 17 { 18 } 19 20 template<typename Func, typename Call> 21 void operator()(Func func, Call call) 22 { 23 func(call[Base1<T1>::data1_]); 24 } 25 }; 26 27 template<typename T1, typename T2> 28 class BindList2 : public Base2<T1, T2> 29 { 30 public: 31 BindList2(T1 data1, T2 data2) 32 : Base2<T1, T2>(data1, data2) 33 { 34 } 35 36 template<typename Func, typename Call> 37 void operator()(Func func, Call call) 38 { 39 func(call[Base2<T1, T2>::data1_], call[Base2<T1, T2>::data2_]); 40 } 41 };
接下來就是bind函數所返回的對象了,相信童鞋們可以想象的到,這個對象中應該主要保存的是bind函數接收的參數信息咯,並且他還保存了所注冊的函數。
1 template<typename Func, typename Bind> 2 class BindImpl 3 { 4 public: 5 BindImpl(Func func, Bind bindlist) 6 : func_(func), bindlist_(bindlist) 7 { 8 } 9 10 void operator()() 11 { 12 bindlist_(func_); 13 } 14 15 template<typename T1> 16 void operator()(T1 data1) 17 { 18 bindlist_(func_, CallList1<T1>(data1)); 19 } 20 21 template<typename T1, typename T2> 22 void operator()(T1 data1, T2 data2) 23 { 24 bindlist_(func_, CallList2<T1, T2>(data1, data2)); 25 } 26 27 protected: 28 Func func_; 29 Bind bindlist_; 30 };
如此,基本的輪廓就已經出來了。bind函數返回一個BindImpl對象,裡面保存了注冊的函數和bind接收的占位符、參數信息。當我們調用這個對象的時候,會生成一個calllist對象,它保存了調用BindImpl對象時所傳入的參數,然後在bindlist中調用注冊的函數。
需要的注意的是,在bindlist調用函數時我們轉而調用了calllist的operator[]函數,通過它來判斷傳入的參數是占位符還是用戶傳入的參數,如果是占位符,那麼就返回calllist中保存的之前注冊的用戶傳入的信息。如果不是占位符,operator[]函數就單純的返回他接收的參數,也就是之前用戶調用BindImpl時傳入的參數。
最後,我們通過一組重載的bind函數來實現對接收0個參數、1個參數、2個參數的支持,它們返回的是一個BindImpl對象。
1 template<typename Func> 2 BindImpl<Func, BindLinst0> bind(Func func) 3 { 4 return BindImpl<Func, BindLinst0>(func, BindLinst0()); 5 } 6 7 template<typename Func, typename T1> 8 BindImpl<Func, BindList1<T1> > bind(Func func, T1 data1) 9 { 10 return BindImpl<Func, BindList1<T1> >(func, BindList1<T1>(data1)); 11 } 12 13 template<typename Func, typename T1, typename T2> 14 BindImpl<Func, BindList2<T1, T2> > bind(Func func, T1 data1, T2 data2) 15 { 16 return BindImpl<Func, BindList2<T1, T2> >(func, BindList2<T1, T2>(data1, data2)); 17 }
結果為:
30 Point::operator() called: a = 7
得到的結果正如預期的一樣。
(完)