在上一篇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 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 template<typename T1, typename T2> 33 class CallList2: public Base2<T1, T2> 34 { 35 public: 36 CallList2(T1 data1, T2 data2) 37 : Base2<T1, T2>(data1, data2) 38 { 39 } 40 41 T1 operator[](Placeholders1 arg1) 42 { 43 return Base2<T1, T2>::data1_; 44 } 45 46 T2 operator[](Placeholders2 arg2) 47 { 48 return Base2<T1, T2>::data2_; 49 } 50 51 template<typename _T> 52 _T operator[](_T arg) 53 { 54 return arg; 55 } 56 };
接下来就是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, 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 }; 31 32 template<typename Func> 33 BindImpl<Func, BindLinst0> bind(Func func) 34 { 35 return BindImpl<Func, BindLinst0>(func, BindLinst0()); 36 } 37 38 template<typename Func, typename T1> 39 BindImpl<Func, BindList1<T1> > bind(Func func, T1 data1) 40 { 41 return BindImpl<Func, BindList1<T1> >(func, BindList1<T1>(data1)); 42 } 43 44 template<typename Func, typename T1, typename T2> 45 BindImpl<Func, BindList2<T1, T2> > bind(Func func, T1 data1, T2 data2) 46 { 47 return BindImpl<Func, BindList2<T1, T2> >(func, BindList2<T1, T2>(data1, data2)); 48 }
这样bind函数的基本功能就实现了,但是需要提到的是,目前的bind并不支持注册成员函数。如果要支持成员函数注册的话,需要萃取函数指针的返回类型,每个参数类型,具体方法在上一篇blog《boost::function的简单实现》中介绍到了,有兴趣的童鞋可以去看看。
下面就来简单的测试一下:
1 int get(int a, int b) 2 { 3 std::cout << a + b << std::endl; 4 return 0; 5 } 6 7 class Point 8 { 9 public: 10 int operator()(int a, int b) 11 { 12 std::cout << "Point::operator() called: a = " << a + b << std::endl; 13 return a + b; 14 } 15 }; 16 17 int main(int argc, char const *argv[]) 18 { 19 bind(get, _1, 10)(20, 1); 20 bind(Point(), _1, _2)(3, 4); 21 return 0; 22 }
结果为:
30 Point::operator() called: a = 7
得到的结果正如预期的一样。
(完)