前面在作 http server 的时候,须要作一个回调的接口,要求可以绑定类的函数以及普通的函数到这个回调里,对于这种应用要求,选择 boost 的 bind 和 function 是最合适不过了,但如今状况有些不一样,我不许备在如今作的这个东西里加入 boost, 本着以造轮子为乐的精神,如今只能捋起袖子本身来搞一个。ios
使用的时候一直没有太留意它们的实现,如今要作起来,发现也不是想像中那么垂手可得。这个东西作到最后要实现的效果就是设计一个泛型的 function holder,这个 holder 既能包装类的函数,又要能包装通常的函数,换言之就是能像下面同样来使用。git
#include <iostream>
using namespace std; class cs { public: int proc(double d) { cout << "mem func proc:" << d << endl; return (int)d;} }; int Proc(double d) { cout << "normal proc:" << d << endl; return (int)d; } int main() { function fun = &Proc; fun(2.3); cs c; fun = bind(&cs::proc, &c); fun(3.3); return 0; }
简单实现github
一开始你可能会想,a piece of cake! 直接封装一个 function 就好了。web
template<class ret_type, class arg_type>
class function1: public copyable { public: typedef ret_type (* NORM_PROC) (arg_type); function1(NORM_PROC proc = 0): fun_(proc){} ret_type operator() (arg_type arg) { fun_->operator()(arg); } private: NORM_PROC fun_; };
好,这个类能够封装通常的函数了,那类的函数呢?one more!安全
template<class CS, class ret_type, class arg_type>
class function2: public copyable { public:
typedef ret_type (CS::* MEM_PROC)(arg_type); function2(CS* obj, MEM_PROC proc): obj_(obj), proc_(proc) {} ret_type operator() (arg_type arg) { return (obj_->*proc_)(arg); } private: CS* obj_;
MEM_PROC proc_; };
很快咱们就发现有问题了,function1 和 function2 是两不一样的模板类,bind() 的时候无法处理:bind() 返回的应该要是一个统一的类型。怎么办呢?咱们可能想到要抽取出一个基类来,思路是对的!但还有些细节要处理。好比:bind() 返回的是什么类型呢?function1,function2 的基类吗?这好像作不到,不能直接返回 object,因此下面的作法是错的。函数
template<class ret_type, class arg_type>
class function_base: public copyable { public:
virtual ~function_base(){} virtual ret_type operator() (arg_type arg) = 0; }; template<class CS, class ret_type, class arg_type>
class function2: public function_base<ret_type, arg_type> { public: typedef ret_type (CS::* MEM_PROC)(arg_type); function2(CS* obj, MEM_PROC proc): obj_(obj), proc_(proc) {} ret_type operator() (arg_type arg) { return (obj_->*proc_)(arg); } private: CS* obj_;
MEM_PROC proc_; }; template<class CS, class ret_type, class arg_type> function_base<ret_type, arg_type> bind(ret_type (CS::* proc)(arg_type), CS* pc) { function2<CS, ret_type, arg_type> func_holder(pc, proc); return func_holder; // object slicing }
那直接返回指针不就完了!返回指针可行,但很差用,并且容易内存泄漏。解决的办法是对返回的指针再包一层,嗯,RAII。但等等,好像若是再包一层,就已经能直接隔开底下的 function holder 与具体的调用了啊!Perfect!spa
template<class ret_type, class arg_type>
class function_base: public copyable { public:
virtual ~function_base() {} virtual ret_type operator() (arg_type arg) = 0; }; template<class ret_type, class arg_type>
class function1: public function_base<ret_type, arg_type> { public:
typedef ret_type (* NORM_PROC) (arg_type);
function1(NORM_PROC proc = 0): fun_(proc){}
ret_type operator() (arg_type arg) { fun_->operator()(arg); }
private:
NORM_PROC fun_;
}; template<class CS, class ret_type, class arg_type>
class function2: public function_base<ret_type, arg_type> { public: typedef ret_type (CS::* MEM_PROC)(arg_type); function2(CS* obj, MEM_PROC proc): obj_(obj), proc_(proc) {} ret_type operator() (arg_type arg) { return (obj_->*proc_)(arg); } private: CS* obj_;
MEM_PROC proc_; };
template<class ret_type, class arg_type>
class functioin: public copyable
{
public:
function(function_base<ret_type, arg_type>* pf): _obj(pf) {}
ret_type operator()(arg_type arg){obj_->operator()(arg);}
private:
function_base<ret_type, arg_type>* obj_;
};
template<class CS, class ret_type, class arg_type> function<ret_type, arg_type> bind(ret_type (CS::* proc)(arg_type), CS* pc) { return new function2<CS, ret_type, arg_type>(pc, proc); }
通过这样一包装,function 类好像已经可以用来 bind 类的成员函数了, 也没那么难嘛!可是,代码不好劲:线程
1) 没有处理内存释放。设计
2) 没有处理 copy costructor,assignment operator()。指针
3) 普通函数仍是不能直接赋值给 function 类。
再改一下 function 类:
template<class ret_type, class arg_type>
class function { public: typedef ret_type (* NORM_PROC) (arg_type); function(function_base<ret_type, arg_type>* fun): fun_(fun), ref_(new int(1)) {} function(NORM_PROC proc = 0): fun_(new function1<ret_type, arg_type>(proc)), ref_(new int(1)) {} ret_type operator() (arg_type arg) { fun_->operator()(arg); } ~function() { Release(); } void Release() { *ref_ -= 1; if (*ref_ == 0) { delete ref_; delete fun_; } } function(const function& fun) { fun_ = fun.fun_; ref_ = fun.ref_; *ref_ += 1; } void operator=(const function& fun) { Release(); fun_ = fun.fun_; ref_ = fun.ref_; *ref_ += 1; } private: int* ref_; function_base<ret_type, arg_type>* fun_; };
这样一来,终于可以正常使用了,能够看到,为了使得 function 类能被 copy/assign,这里面使用引用计数来控制内存的释放问题,上面的实现比较简单,也不是线程安全的,只是知足了基本的使用需求,具体的代码参看这里。代码写得较快,暂且就这样了,不知道 boost 是怎样实现的?得找个时间研究研究。