轉載地址:http://blog.csdn.net/hengyunabc/article/details/7773250
寫在前面的話
在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>();
- }
- };
- // 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) {
- 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_traits
- template<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;
- };
- // value
- template<class T> class value {
- public:
- value(T const & t) :
- t_(t) {
- }
- T & get() {
- return t_;
- }
- private:
- T t_;
- };
- // type
- template<class T> class type {
- };
- // unwrap
- template<class F> struct unwrapper {
- static inline F & unwrap(F & f, long) {
- return f;
- }
- };
- // listN
- class 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_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);
- }
- 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_N
- template<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 pointers
- template<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 boost
- namespace {
- 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;
- }
接下來是storage1,storage2類:
- template<class A1> struct storage1 {
- explicit storage1(A1 a1) :
- a1_(a1) {
- }
- A1 a1_;
- };
- ...
- // 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) {
- }
- 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> {
- ...
- }
再看類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_; <strong>//bind所綁定的函數</strong>
- L l_; <strong> //實際是是listN類,存放bind傳綁定的參數</strong>
- };
注意bind_t的兩個成員F f_ 和 L l_,這裏是關鍵之處。
介紹完關鍵類,下面以main.cpp中下面部分代碼爲例,詳細說明它的工作流程。
- int i1 = 1, i2 = 2;
- (boost::bind2(&tow_arguments, 123, _1))(i1, i2);
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
當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 ()
- }
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_]); //本是這句的,簡化了下,無關要緊
- }
注意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();
- }
傳遞給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,希望這個能簡化模板編程。