前面在作 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 是怎樣實現的?得找個時間研究研究。