zoukankan      html  css  js  c++  java
  • 通用型函数指针

    看了 kevinlynx 的一篇文章,然后按自己的理解重新实现一个通用型函数指针。

    前言

    看了 kevinlynx 的一篇通用型函数指针的文章,发现使用到的技术知识自己都知道,于是想着自己也实现一个来练练手。

    背景

    什么是通用型的函数指针呢?

    这个不好解释,不过可以用例子来让大家看明白。

    正常类型指针

    对于平常的指针,比如整形指针,我们直接可以像 下面的形式操作 。

    void normal() {
      int one = 1;
      int* pOne;
      pOne = &one;
      printf("pOne %d
    ", *pOne);
      int two = 2;
      int* pTwo= &two;
      printf("pTwo %d
    ", *pTwo);
      int three = 3;
      int* pThree(&three);
      printf("pThree %d
    ", *pThree);
      printf("end normal
    
    ");
    }
    
    

    这里我们可以看到整形指针有这么几个性质。

    1. 普通指针可以在定义时初始化
    2. 普通指针可以在正常赋值
    3. 我们可以操作指针的值

    正常函数指针

    那 函数指针 是什么样子呢?

    void testPointFun(int num) {
      printf("testPointFun %d
    ",num);
    }
    void testPointFunTwo(int num, int num2) {
      printf("testPointFunTwo %d %d
    ",num, num2);
    }
    void pointFun() {
      void (*pFunOne)(int);
      pFunOne = testPointFun;
      pFunOne(1);
      void (*pFunTwo)(int) = testPointFun;
      pFunTwo(2);
      void (*pFunThree)(int)(testPointFun);
      pFunThree(3);
      typedef void (*PestPointFun)(int);
      PestPointFun pFunFour = testPointFun;
      pFunFour(4);
      typedef void (*PestPointFunTwo)(int, int);
      PestPointFunTwo pFunFive = testPointFunTwo;
      pFunFive(5,5);
      printf("end  pointFun
    
    ");
    }
    
    

    我们发现,普通指针也都可以做这些操作,但是我们需要使用函数指针那么很长很长的定义,即使使用 typedef , 也要为每一种函数声明单独定义新类型的名字。

    期望的函数指针

    于是我们想,能不能直接定义函数指针呢?

    比如这样

    void wantPointFun() {
      PointFun pointFunOne = testPointFun;
      pointFunOne(6);
      PointFun pointFunTwo = testPointFunTwo;
      pointFunTwo(7,7);
      printf("end  wantPointFun
    
    ");
    }
    
    

    当然,根据一个函数名自动推导出对应的函数指针的技术可以实现,但是cpp标准中又没有这样的技术我就不知道了。

    我们就假设cpp中现在没有这样的技术吧。

    正文

    既然目前标准中不支持这种技术,那我们该如何实现呢?

    初级通用函数指针

    于是只好自己指定好类型了。

    例如 这样

    
    template <typename _R, typename _P1>
    class functor {
    public:
      typedef _R (*func_type)( _P1 );
    public:
      explicit functor( const func_type &func ) :
        _func( func ) {
      }
      _R operator() ( _P1 p ) {
        return _func( p );
      }
    private:
      func_type _func;
    };
    int testPointFun(int num) {
      printf("testPointFun %d
    ",num);
      return 0;
    }
    void firstPointFun() {
      functor<int, int> cmd( testPointFun );
      cmd( 1 );
    }
    
    

    于是我们通过重载类的运算符 () 来模拟函数调用就完美的解决问题了。

    加强版通用函数指针

    但是我们既然可以使用类来模拟函数(姑且称为函数对象吧), 那传过来的函数指针会不会就是我们的那个函数对象呢?

    struct Func {
      int operator() ( int i ) {
        return i;
      }
    };
    void secondPointFun() {
      functor<int, int> cmd1( testPointFun );
      cmd1(1);
      Func obj;
      functor<int, int> cmd2(obj);
      cmd2( 2 );
    }
    
    

    我们发现对于函数对象, 编译不通过。提示这个错误

    error: no matching function for call to 'functor<int, int>::functor(Func&)'
    

    报这个错误也正常,我们的通用函数指针式 int (*)(int) 类型, 但是我们传进去的是 Func 类型,当然不匹配了。

    这个时候我们就会意识到需要对这个函数的类型进行抽象了,比如 这样 。

    template <typename _R, typename _P1,typename _FuncType>
    class functor {
    public:
      typedef _FuncType func_type;
    public:
      explicit functor( const func_type &func ) :
        _func( func ) {
      }
      _R operator() ( _P1 p ) {
        return _func( p );
      }
    private:
      func_type _func;
    };
    int testPointFun(int num) {
      printf("testPointFun %d
    ",num);
      return 0;
    }
    struct Func {
      int operator() ( int num ) {
        printf("Func class %d
    ",num);
        return num;
      }
    };
    void threePointFun() {
      functor<int, int, int (*)(int)> cmd1( testPointFun );
      cmd1(1);
      Func obj;
      functor<int, int, Func> cmd2(obj);
      cmd2( 2 );
    }
    
    

    这个时候我们终于编译通过了。

    回头思考人生

    但是,编译通过的代价却是我们手动指定函数指针的类型, 这与直接声明函数指针变量有什么区别呢?

    比如对于上面的,我们直接使用函数指针不是更方便吗?

    void fourPointFun() {
      int (*cmd1)(int) ( testPointFun );
      cmd1(1);
      Func obj;
      Func cmd2(obj);
      cmd2( 2 );
    }
    
    

    那我们为了什么那样这样的寻找所谓的'通用型函数指针'呢?

    答案是为了统一函数指针的定义,对,是统一。

    自动推导类型

    那我们能不能省去函数指针的类型呢?

    貌似使用多态可以省去函数指针的类型,可以让系统自己推导,然后我们只需要调用函数即可。

    例如 这样

    
    template <typename _R, typename _P1>
    struct handler_base {
      virtual _R operator() ( _P1 ) = 0;
    };
    template <typename _R, typename _P1, typename _FuncType>
    class handler : public handler_base<_R, _P1> {
    public:
      typedef _FuncType func_type;
    public:
      handler( const func_type &func ) :
        _func( func ) {
      }
      _R operator() ( _P1 p ) {
        return _func( p );
      }
    public:
      func_type _func;
    };
    template <typename _R, typename _P1>
    class functor {
    public:
      typedef handler_base<_R, _P1> handler_type ;
    public:
      template <typename _FuncType>
      functor( _FuncType func ) :
        _handler( new handler<_R, _P1, _FuncType>( func ) ) {
      }
      ~functor() {
        delete _handler;
      }
      _R operator() ( _P1 p ) {
        return (*_handler)( p );
      }
    private:
      handler_type *_handler;
    };
    int testPointFun(int num) {
      printf("testPointFun %d
    ",num);
      return 0;
    }
    struct Func {
      int operator() ( int num ) {
        printf("Func class %d
    ",num);
        return num;
      }
    };
    void fivePointFun() {
      functor<int, int>cmd1( testPointFun );
      cmd1(1);
      Func obj;
      functor<int, int>cmd2(obj);
      cmd2( 2 );
    }
    
    

    支持任意参数

    我们通过模板和多态实现了指定参数的通用型函数指针。

    由于模板是编译的时候确定类型的,所以参数的个数需要编译的时候确定。

    又由于模板不支持任意类型参数,所以我们只好把不同个数参数的模板都定义了。

    这里有涉及到怎么优雅的定义不同个数参数的模板了。

    去年我去听过一个培训,讲的是就是c++的模板,重点讲了偏特化。

    我们利用偏特化就可以暂时解决这个问题。

    实现代码可以参考我的 github 。

    看了实现代码,发现使用起来还是很不方便。

    functor<int, TYPE_LIST1(int)>cmd1( testPointFun );
    cmd1(1);
    
    Func obj;
    functor<int, TYPE_LIST1(int)>cmd2(obj);
    cmd2( 2 );
    
    functor<int, TYPE_LIST2(int,int)>cmd3( testPointFunTwo );
    cmd3(1,2);
    

    需要我们手动指定参数的个数,以及传进去参数的类型。

    由于我们不能自动推导参数的类型,所以类型必须手动指定,但是个数我们应该可以在编译器期确定吧。

    获得宏的个数

    现在我们的目的是这样的使用函数指针。

    functor<int, TYPE_LIST(int)>cmd1( testPointFun );
    cmd1(1);
    
    Func obj;
    functor<int, TYPE_LIST(int)>cmd2(obj);
    cmd2( 2 );
    
    functor<int, TYPE_LIST(int,int)>cmd3( testPointFunTwo );
    cmd3(1,2);
    

    这个倒是很容易实现。比如 这样

    #define NUM_PARAMS(...) NUM_PARAMS_OUTER(__VA_ARGS__, NUM_PARAMS_EXTEND())
    #define NUM_PARAMS_OUTER(...) NUM_PARAMS_INTER(__VA_ARGS__)
    #define NUM_PARAMS_INTER( _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, _11,_12,_13,_14,_15,_16, N, ...) N
    #define NUM_PARAMS_EXTEND() 16,15,14,13,12,11,10, 9,8,7,6,5,4,3,2,1,0
    
    
    #define TYPE_LIST1( T1 ) type_list<T1, null_type>
    #define TYPE_LIST2( T1, T2 ) type_list<T1, TYPE_LIST1( T2 )>
    #define TYPE_LIST3( T1, T2, T3 ) type_list<T1, TYPE_LIST2( T2, T3 )>
    
    #define TYPE_LIST(...) TYPE_LIST_N(NUM_PARAMS(__VA_ARGS__), __VA_ARGS__)
    #define TYPE_LIST_N(n,...) TYPE_LIST_N_FIX(n, __VA_ARGS__)
    #define TYPE_LIST_N_FIX(n, ...) TYPE_LIST##n(__VA_ARGS__)
    

    这个实现还是有一点不爽: 我们需要写出所有可能的 TYPE_LISTn.

    能不能使用宏来做到这个呢?

    宏中怎么才能判断出到到达最后一个参数或者没有参数了呢?

    还是依靠得到宏个数的技术。

    但是经过嵌套尝试,发现宏时不能递归展开的。

    好吧,既然不能递归展开,那也只能到达这一步了。

    源代码

    源代码可以参考我的 github .

    参考资料

  • 相关阅读:
    Hanoi塔
    采药
    进制转换(大数)
    Load Balancing with NGINX 负载均衡算法
    upstream模块实现反向代理的功能
    epoll
    在nginx启动后,如果我们要操作nginx,要怎么做呢 别增加无谓的上下文切换 异步非阻塞的方式来处理请求 worker的个数为cpu的核数 红黑树
    粘性会话 session affinity sticky session requests from the same client to be passed to the same server in a group of servers
    负载均衡 4层协议 7层协议
    A Secure Cookie Protocol 安全cookie协议 配置服务器Cookie
  • 原文地址:https://www.cnblogs.com/www886/p/4367967.html
Copyright © 2011-2022 走看看