前言
之前的版本:
【项目三】C++实现事件委托_学艺不精的Антон的博客-CSDN博客
前两天因为学设计模式的观察者模式,参考了网上的资料尝试用C++来实现事件委托
当时完成的版本主要有以下两个问题:
1. 对于无返回值无参数的函数委托,在声明事件委托的时候Delegate<void,void>,是模板参数匹配的问题,只能写成Delegate<void>
2. 按照参考的博主写的事件委托在添加引用类型返回值时,由于vector无法存储引用类型会报错,所以我的初始实现索性就省略掉了返回函数返回值的线性表的操作
本文我将一步一步介绍如何解决以上两个问题,本文只给出部分代码,需要完整代码可以克隆我的仓库(建议完整克隆,项目依赖我编写的异常类库):
AntonaStandard: 自定义的库,里面编写了一些常用的工具 (gitee.com)
解决思路
今天在翻C++PrimerPlus的时候发现了C++“新标准”(C++11)的一个机制,包装器function,包装器function也是对函数(类的静态与非静态成员函数,以及普通函数)进行多态封装,于是就查找了相关的资料:
STL源码分析之std::function - 知乎 (zhihu.com)
里面有function的部分声明:
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>
: public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>
, private _Function_base
{
private:
using _Invoker_type = _Res (*)(const _Any_data&, _ArgTypes&&...);
_Invoker_type _M_invoker;
// ... ...
// 还有很多成员函数
};
这个声明就很奇妙,因为它允许我像下面这样声明包装器
function<void(void)> func;
function<void()> func_1;
想必我遗漏了什么知识点,于是查找了一下模板相关的资料,这么写应该是类似显式具体化的写法(本人水平有限,找了一下午也没找到明确说明这叫什么的资料),将一堆子模板参数具体化成一个函数类型。因此第一个问题就得到了解决,只需要将事件委托的特化版本声明出来就可以:
template<typename type_RETURN_VALUE, typename... type_PARAMETERS_PACK>
class Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>{
.........
}
remove_reference也是偶然翻到的,先简单说明一下使用方式和实现原理
使用:
// 使用方式
remove_reference<int&>::type a = 10; // 此时的type表示int类型
// 当然也可以脱去右值引用
remove_reference<int&&>::type b = a; // type为int类型
在完成初版的时候,如何解决将类型引用转化为该类型的值类型困扰了我好久。有时不得不感叹真理往往是简介明了的,下面我截取了remove_reference的源码:
// Reference transformations.
/// remove_reference
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp type; };
可以看到,通过模板参数的表达式,编译器自动选择合适的形式具体化版本,然后在模板的不同实现中,可以根据对应的参数形式推断出值类型。
因此在有返回值的多播委托中将重载运算发()声明成如下的形式就可以返回保存委托的线性表了
virtual std::vector<typename std::remove_reference<type_RETURN_VALUE>::type> operator()(type_PARAMETERS_PACK... parama_args);
注意,要在std::remove_reference<type_RETURN_VALUE>::type添加关键字typename告诉编译器type是一个类型而不是一个静态成员。
这样所有问题就都解决了,只需要把初版的声明修改一下,显式具体化出void返回值版的事件委托类
// 委托类
template<typename type_RETURN_VALUE, typename... type_PARAMETERS_PACK>
class Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>{
// 通过链表存储容器
public:
// 声明一下链表和迭代器,化简表达
using DelegateList = std::list<BaseFuncPointerContainer<type_RETURN_VALUE,type_PARAMETERS_PACK...>* >;
using DelegateListIterator = typename DelegateList::iterator;
using DelegateListConstIterator = typename DelegateList::const_iterator;
protected:
DelegateList delegateList;
public:
// 声明为虚函数,提高可拓展性
// 获取委托个数
inline virtual unsigned long size()const{
return this->delegateList.size();
}
// 判断委托是否为空
inline virtual bool empty()const{
return this->delegateList.empty();
}
// 清空委托
virtual void clear();
virtual Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>& operator+=(BaseFuncPointerContainer<type_RETURN_VALUE,type_PARAMETERS_PACK...>* other_ptr);
virtual Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>& operator-=(BaseFuncPointerContainer<type_RETURN_VALUE,type_PARAMETERS_PACK...>* other_ptr);
// 多播返回值存储到一个vector中
virtual std::vector<typename std::remove_reference<type_RETURN_VALUE>::type> operator()(type_PARAMETERS_PACK... parama_args);
// 返回值如果要存到一个vector中效率较低,这里提供一个无返回值的接口
virtual void call_without_return(type_PARAMETERS_PACK... parama_args);
// 左值赋值构造函数,和赋值函数
Delegate(const Delegate&);
Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>& operator=(const Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>&);
// 右值移动构造函数,和赋值函数
Delegate(Delegate&&);
Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>& operator=(const Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>&&);
// 空构造函数
Delegate(){};
// 虚析构函数
virtual ~Delegate(){this->clear();};
};
// 委托类,返回值为void的具体化版本
template<typename... type_PARAMETERS_PACK>
class Delegate<void(type_PARAMETERS_PACK...)>{
// 通过链表存储容器
public:
// 声明一下链表和迭代器,化简表达
using DelegateList = std::list<BaseFuncPointerContainer<void,type_PARAMETERS_PACK...>* >;
using DelegateListIterator = typename DelegateList::iterator;
using DelegateListConstIterator = typename DelegateList::const_iterator;
protected:
DelegateList delegateList;
public:
// 声明为虚函数,提高可拓展性
// 获取委托个数
inline virtual unsigned long size()const{
return this->delegateList.size();
}
// 判断委托是否为空
inline virtual bool empty()const{
return this->delegateList.empty();
}
// 清空委托
virtual void clear();
virtual Delegate<void(type_PARAMETERS_PACK...)>& operator+=(BaseFuncPointerContainer<void,type_PARAMETERS_PACK...>* other_ptr);
virtual Delegate<void(type_PARAMETERS_PACK...)>& operator-=(BaseFuncPointerContainer<void,type_PARAMETERS_PACK...>* other_ptr);
// 暂时禁用多播返回值,因为如果type_RETURN_VALVE为一个引用类型的话std::vector无法进行存储
virtual void operator()(type_PARAMETERS_PACK... parama_args);
// virtual void operator()(type_PARAMETERS_PACK... parama_args);
// 左值赋值构造函数,和赋值函数
Delegate(const Delegate&);
Delegate<void(type_PARAMETERS_PACK...)>& operator=(const Delegate<void(type_PARAMETERS_PACK...)>&);
// 右值移动构造函数,和赋值函数
Delegate(Delegate&&);
Delegate<void(type_PARAMETERS_PACK...)>& operator=(const Delegate<void(type_PARAMETERS_PACK...)>&&);
// 空构造函数
Delegate(){};
// 虚析构函数
virtual ~Delegate(){this->clear();};
};
另外,由于添加返回值在创建线性表vector的时候必然会消耗运行效率,我为此在通用特化版本中添加了另一个调用接口
virtual void call_without_return(type_PARAMETERS_PACK... parama_args);
该接口由于显式具体化的特性,只有声明了该接口的版本才拥有该接口,因此返回值是void的Delegate不具有该接口。客户端可以根据需要有返回值地调用委托或者无返回值地调用委托
最后给出一个演示示例
#include "Delegate.h"
#include <iostream>
#include <vector>
using namespace std;
using namespace AntonaStandard;
class A{
public:
static int s_i_v_A(){
cout<<"int返回值无参数A的静态函数"<<endl;
return 10;
}
int i_v_A(){
cout<<"int返回值无参数A的非静态函数"<<endl;
return 11;
}
};
int i_v(){
cout<<"int返回值无参数普通函数"<<endl;
return 12;
}
class B{
public:
static void s_v_v_B(){
cout<<"无返回值无参数B的静态函数"<<endl;
}
void v_v_B(){
cout<<"无返回值无参数B的非静态函数"<<endl;
}
};
void v_v(){
cout<<"无返回值无参数的普通函数"<<endl;
}
class C{
public:
static int& s_iq_iq_C(int& v){
cout<<"无返回值无参数C的静态函数 "<<v<<endl;
return v;
}
int& iq_iq_C(int& v){
cout<<"无返回值无参数C的非静态函数 "<<v<<endl;
return v;
}
};
int& iq_iq(int& v){
cout<<"无返回值无参数的普通函数 "<<v<<endl;
return v;
}
int main(){
A a;
Delegate<int(void)> del;
del += newDelegate(A::s_i_v_A);
del += newDelegate(a,a.i_v_A);
del += newDelegate(i_v);
vector<int> vec = del();
cout<<"--------------"<<endl;
del.call_without_return();
for(int i = 0;i<vec.size();++i){
cout<<vec[i]<<" ";
}
cout<<endl<<"--------------"<<endl;
B b;
Delegate<void()> del1;
del1 += newDelegate(B::s_v_v_B);
del1 += newDelegate(b,b.v_v_B);
del1 += newDelegate(v_v);
del1();
cout<<endl<<"--------------"<<endl;
C c;
Delegate<int&(int&)> del2;
del2 += newDelegate(C::s_iq_iq_C);
del2 += newDelegate(c,c.iq_iq_C);
del2 += newDelegate(iq_iq);
int x = 666;
vector<int> vec2 = del2(x);
for(int i = 0;i<vec2.size();++i){
cout<<vec2[i]<<" ";
}
return 0;
}
输出如下:
int返回值无参数A的静态函数
int返回值无参数A的非静态函数
int返回值无参数普通函数
--------------
int返回值无参数A的静态函数
int返回值无参数A的非静态函数
int返回值无参数普通函数
10 11 12
--------------
无返回值无参数B的静态函数
无返回值无参数B的非静态函数
无返回值无参数的普通函数
--------------
无返回值无参数C的静态函数 666
无返回值无参数C的非静态函数 666
无返回值无参数的普通函数 666
666 666 666
总结
整个项目大概搞了两天多,期间改了不少的版本:
现在的事件委托可以是说是比较完善的了,这个项目可以说是一个跨年项目,希望大家多多支持,如果给您了什么思路,求一个赞吧和😜,线上的仓库我会不断更新,用C++实现更多的机制,项目开源,最后祝各位新年快乐!