1.引言:
上一篇文章已经介绍了如何构建一个无参数无返回值的函数指针的委托,这篇文章将对上一文章所述委托进行扩展,使得可以注册任意函数指针,不过再讲篇内容之前先要介绍一下实现这个功能所需要了解的c 11的一个新特性———可变参数模板。
2.可变参数模板:
template(模板)是源于将类型和实现(算法)分离开的思想,为了让写好的算法适用于更多类型使得出现了模板,模板使得参数类别任意化,如果再加上“参数个数的任意化”,那么在参数方面的设计手段就基本上齐备了,有了variadic template 显然可以让设计出来的函数或是类有更大的复用性。因为有很多处理都是与“处理对象的个数”关系不大的,比如说打屏(printf),比如说比较大小(max,min),比如函数绑定子(bind,function要对应各种可能的函数就要能“任意”参数个数和类型)。如果不能对应任意个参数,那么就总会有人无法重用已有的实现,而不得不再重复地写一个自己需要的处理,而共通库的实现者为了尽可能地让自己写的类(函数)能复用在更多的场景,也不得不重复地写很多的代码或是用诡异的技巧,宏之类的去实现有限个“任意参数”的对应。
所以在c 11中引入了这个新语法,下面我们先看一个例子,了解基本的语法:
template class myclass;
myclass a;
在上面这个例子我们可以看到,在定义和申明中用 typename... 表示一个参数包,这样我们使用的时候,传入一个
递归展开!!!我们先来看一个函数的例子,假如我想写一个max函数求传入所有参数的最大值,这个参数支持(int,double,float),返回值为double.那么函数原型你觉得大概可能是这样子的:
template
double max(params... _params)
{
double maxnum;
//求解过程
return maxnum;
}
但是这样写你会发现你无法获取到任何传入参数的类型和值。恩,所以我们来看下如何使用递归来获得每个参数的类型和值。
template
double max(head first,tail... rest)
{
double maxnum;
maxnum = max(rest...);
if (maxnum < first)
maxnum = first;
return maxnum;
}
可以看到,我将函数参数分离了一个出来作为head,这样我就可以每次处理一个变量,使用递归下降,每次递归减少一个参数。注意:上面的rest...表示参数展开,也就是说将这个参数包展开然后当做参数传进去。但是这样做还是不够的,大家都知道递归需要一个出口。所以说我们需要一个重载版本(因为模板函数不能偏特化)。
template
double max(head first)
{
return first;
}
这样就有了一个递归出口了。好,我们结合以上的代码写成一个完整的程序,看下如何使用的:
#include
using namespace std;
template
double max(head first, tail... rest)
{
double maxnum=0;
maxnum = max(rest...);
if (maxnum < first)
maxnum = first;
return maxnum;
}
template
double max(head first)
{
return first;
}
int main()
{
cout << max(1, 3, 3.4, 5.1, 1.5);
}
输出结果是: 5.1,同样的,模板类是通过私有继承来实现递归的。
template
class tuple : private tuple{
head head;
public:
/* implementation */
};
template
class tuple {
/* one-tuple implementation */
};
以上可变参数的基本内容就讲完了,如果还有不懂的可以自行百度
3.任意参数个数、返回值任意的函数指针委托:
同样的,我们还是先对接口进行修改,因为函数指针再不是void(*)(void)所以,接口也需要是一个模板类,而且还需要是一个可变参数模板。
template
class idelegate
{
public:
idelegate(){}
virtual ~idelegate(){}
virtual bool istype(const std::type_info& _type) = 0;
virtual returntype invoke(paramtype ... params) = 0;
virtual bool compare(idelegate *_delegate) const = 0;
};
这样,这个接口的invoke函数将会是根据你的函数类型来动态生成对应的模板了。
同样的,我们把剩下三个类按照如此进行改造:
//staticdelegate 普通函数的委托
template
class cstaticdelegate :
public idelegate
{
public:
typedef returntype(*func)(paramtype...);
cstaticdelegate(func _func) : mfunc(_func) { }
virtual bool istype(const std::type_info& _type) { return typeid(cstaticdelegate) == _type; }
virtual returntype invoke(paramtype ... params) { return mfunc(params...); }
virtual bool compare(idelegate *_delegate)const
{
if (0 == _delegate || !_delegate->istype(typeid(cstaticdelegate))) return false;
cstaticdelegate * cast = static_cast*>(_delegate);
return cast->mfunc == mfunc;
}
virtual ~cstaticdelegate(){}
private:
func mfunc;
};
//成员函数委托
template
class cmethoddelegate :
public idelegate
{
public:
typedef returntype(t::*method)(paramtype...);
cmethoddelegate(t * _object, method _method) : mobject(_object), mmethod(_method) { }
virtual bool istype(const std::type_info& _type) { return typeid(cmethoddelegate) == _type; }
virtual returntype invoke(paramtype...params)
{
(mobject->*mmethod)(params...);
}
virtual bool compare(idelegate *_delegate) const
{
if (0 == _delegate || !_delegate->istype(typeid(cmethoddelegate))) return false;
cmethoddelegate* cast = static_cast*>(_delegate);
return cast->mobject == mobject && cast->mmethod == mmethod;
}
cmethoddelegate(){}
virtual ~cmethoddelegate(){}
private:
t * mobject;
method mmethod;
};
//多播委托
template
class cmultidelegate
{
public:
typedef std::list*> listdelegate;
typedef typename listdelegate::iterator listdelegateiterator;
typedef typename listdelegate::const_iterator constlistdelegateiterator;
cmultidelegate() { }
~cmultidelegate() { clear(); }
bool empty() const
{
for (constlistdelegateiterator iter = mlistdelegates.begin(); iter != mlistdelegates.end(); iter)
{
if (*iter) return false;
}
return true;
}
void clear()
{
for (listdelegateiterator iter = mlistdelegates.begin(); iter != mlistdelegates.end(); iter)
{
if (*iter)
{
delete (*iter);
(*iter) = nullptr;
}
}
}
cmultidelegate& operator =(idelegate* _delegate)
{
for (listdelegateiterator iter = mlistdelegates.begin(); iter != mlistdelegates.end(); iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
delete _delegate;
return *this;
}
}
mlistdelegates.push_back(_delegate);
return *this;
}
cmultidelegate& operator-=(idelegate* _delegate)
{
for (listdelegateiterator iter = mlistdelegates.begin(); iter != mlistdelegates.end(); iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
if ((*iter) != _delegate) delete (*iter); //避免同一个地址被delete两次
(*iter) = 0;
break;
}
}
delete _delegate;
return *this;
}
std::vector operator()(paramtype... params)
{
listdelegateiterator iter = mlistdelegates.begin();
std::vector _results;
while (iter != mlistdelegates.end())
{
if (0 == (*iter))
{
iter = mlistdelegates.erase(iter);
}
else
{
_results.push_back((*iter)->invoke(params...));
iter;
}
}
return _results;
}
private:
cmultidelegate(const cmultidelegate& _event);
cmultidelegate& operator=(const cmultidelegate& _event);
private:
listdelegate mlistdelegates;
};
但是这样写你会发现你在newdelegate里面对于传来的函数指针进行new cstaticdelegate或者new cmethoddelegate的时候需要制定函数返回值、参数的个数和类型,这显然不满足动态类型演化。这时候我们想,能不能给定一个函数指针,让代码自动去识别这个函数的返回值和参数呢?答案是可以的,我们只需要对上面的cstaticdelegate和cmethoddelegate特化一个版本即可。 这里使用了一个小技巧:通过函数指针去得到函数返回值、参数个数类型。详情可以看看这里:http://bbs.csdn.net/topics/390652170?page=1
这里我就直接贴我写好的代码:
//普通函数的委托特化版本
template
class cstaticdelegate :
public idelegate
{
public:
//定义 func 为 void (void) 函数类型指针。
typedef returntype(*func)(paramtype...);
cstaticdelegate(func _func) : mfunc(_func) { }
virtual bool istype(const std::type_info& _type) { return typeid(cstaticdelegate) == _type; }
virtual returntype invoke(paramtype ... params) { return mfunc(params...); }
virtual bool compare(idelegate *_delegate)const
{
if (0 == _delegate || !_delegate->istype(typeid(cstaticdelegate))) return false;
cstaticdelegate * cast = static_cast*>(_delegate);
return cast->mfunc == mfunc;
}
virtual ~cstaticdelegate(){}
private:
func mfunc;
};
//成员函数委托特化
template
class cmethoddelegate :
public idelegate
{
public:
typedef returntype(t::*method)(paramtype...);
cmethoddelegate(t * _object, method _method) : mobject(_object), mmethod(_method) { }
virtual bool istype(const std::type_info& _type) { return typeid(cmethoddelegate) == _type; }
virtual returntype invoke(paramtype...params)
{
return (mobject->*mmethod)(params...);
}
virtual bool compare(idelegate *_delegate) const
{
if (0 == _delegate || !_delegate->istype(typeid(cmethoddelegate))) return false;
cmethoddelegate* cast = static_cast*>(_delegate);
return cast->mobject == mobject && cast->mmethod == mmethod;
}
cmethoddelegate(){}
virtual ~cmethoddelegate(){}
private:
t * mobject;
method mmethod;
};
这样我生成的时候只需要new cstaticdelegate
template< typename t>
cstaticdelegate* newdelegate(t func)
{
return new cstaticdelegate(func);
}
template< typename t,typename f>
cmethoddelegate* newdelegate(t * _object, f func)
{
return new cmethoddelegate(_object, func);
}
写到这里基本上可变参数的委托就完成了,不过还需要注意一点就是void无返回值类型多播委托需要特殊处理。所以我们还需要一个多播委托对于returntype为void这个情况的特化。
template< typename ...paramtype>
class cmultidelegate
{
public:
typedef std::list*> listdelegate;
typedef typename listdelegate::iterator listdelegateiterator;
typedef typename listdelegate::const_iterator constlistdelegateiterator;
cmultidelegate() { }
~cmultidelegate() { clear(); }
bool empty() const
{
for (constlistdelegateiterator iter = mlistdelegates.begin(); iter != mlistdelegates.end(); iter)
{
if (*iter) return false;
}
return true;
}
void clear()
{
for (listdelegateiterator iter = mlistdelegates.begin(); iter != mlistdelegates.end(); iter)
{
if (*iter)
{
delete (*iter);
(*iter) = nullptr;
}
}
}
cmultidelegate& operator =(idelegate* _delegate)
{
for (listdelegateiterator iter = mlistdelegates.begin(); iter != mlistdelegates.end(); iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
delete _delegate;
return *this;
}
}
mlistdelegates.push_back(_delegate);
return *this;
}
cmultidelegate& operator-=(idelegate* _delegate)
{
for (listdelegateiterator iter = mlistdelegates.begin(); iter != mlistdelegates.end(); iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
if ((*iter) != _delegate) delete (*iter); //避免同一个地址被delete两次
(*iter) = 0;
break;
}
}
delete _delegate;
return *this;
}
void operator()(paramtype... params)
{
listdelegateiterator iter = mlistdelegates.begin();
while (iter != mlistdelegates.end())
{
if (0 == (*iter))
{
iter = mlistdelegates.erase(iter);
}
else
{
(*iter)->invoke(params...);
iter;
}
}
}
private:
cmultidelegate(const cmultidelegate& _event);
cmultidelegate& operator=(const cmultidelegate& _event);
private:
listdelegate mlistdelegates;
};
所有代码都已经贴出来了,我把这些模板全部放到了mydelegate.h头文件中。下面给一个使用的例子:
#include "mydelegate.h"
using namespace delegate;
void normalfunc(int a)
{
printf("这里是普通函数 :%d\n", a);
}
class a
{
public:
static void staticfunc(int a)
{
printf("这里是成员静态函数 : %d\n", a);
}
void memberfunc(int a)
{
printf("这里是成员非静态函数 : %d\n", a);
}
};
int _tmain(int argc, _tchar* argv[])
{
//首先创建了一个返回值为 void ,参数为int 的一个委托。
cmultidelegate e;
//将三个函数注册到该委托中
e = newdelegate(normalfunc);
e = newdelegate(a::staticfunc);
e = newdelegate(&a(), &a::memberfunc);
//调用
e(1);
return 0;
}