通用型函数指针

看了 kevinlynx 的一篇文章,而后按本身的理解从新实现一个通用型函数指针。javascript

前言

看了 kevinlynx 的一篇通用型函数指针的文章,发现使用到的技术知识本身都知道,因而想着本身也实现一个来练练手。java

背景

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

这个很差解释,不过能够用例子来让你们看明白。c++

正常类型指针

对于日常的指针,好比整形指针,咱们直接能够像 下面的形式操做 。git

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

这里咱们能够看到整形指针有这么几个性质。github

  1. 普通指针能够在定义时初始化
  2. 普通指针能够在正常赋值
  3. 咱们能够操做指针的值

正常函数指针

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

void testPointFun(int num) {  printf("testPointFun %d\n",num); } void testPointFunTwo(int num, int num2) {  printf("testPointFunTwo %d %d\n",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\n\n"); } 

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

指望的函数指针

因而咱们想,能不能直接定义函数指针呢?spa

好比这样指针

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

固然,根据一个函数名自动推导出对应的函数指针的技术能够实现,可是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\n",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\n",num);  return 0; } struct Func {  int operator() ( int num ) {   printf("Func class %d\n",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\n",num);  return 0; } struct Func {  int operator() ( int num ) {   printf("Func class %d\n",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 .

参考资料

相关文章
相关标签/搜索