• boost bind及function的简单实现


    前面在做 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 是怎样实现的?得找个时间研究研究。

     

  • 相关阅读:
    Spark使用总结与分享【转】
    用实例讲解Spark Sreaming--转
    hbase RowFilter如何根据rowkey查询以及实例实现代码 habase模糊查询【转】
    Android OpenGL ES(十三)通用的矩阵变换指令 .
    Android OpenGL ES(十二):三维坐标系及坐标变换初步 .
    Android OpenGL ES(十一)绘制一个20面体 .
    Android OpenGL ES(十)绘制三角形Triangle .
    Android OpenGL ES(九)绘制线段Line Segment .
    Android OpenGL ES(八)绘制点Point ..
    Android OpenGL ES .介绍
  • 原文地址:https://www.cnblogs.com/catch/p/3490397.html
Copyright © 2020-2023  润新知