boost bind及function的簡單實現

twoon發表於2013-12-25

前面在做 http server 的時候,需要做一個回撥的介面,要求能夠繫結類的函式以及普通的函式到這個回撥裡,對於這種應用要求,選擇 boost 的 bind 和 function 是最合適不過了,但現在情況有些不同,我不準備在現在做的這個東西里加入 boost, 本著以造輪子為樂的精神,現在只能捋起袖子自己來搞一個。

大概原型

使用的時候一直沒有太留意它們的實現,現在要做起來,發現也不是想像中那麼輕而易舉。這個東西做到最後要實現的效果就是設計一個泛型的 function holder,這個 holder 既能包裝類的函式,又要能包裝一般的函式,換言之就是能像下面一樣來使用。

#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;
}

簡單實現

一開始你可能會想,a piece of cake! 直接封裝一個 function 就行了。

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!

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

 

相關文章