本帖最后由 雪千渔 于 2021-5-30 11:40 编辑
先看看如何使用的
[C++] 纯文本查看 复制代码 #include "CoreLib/Events.hpp"
class EventTest
{
public:
int cb_index;
Action<int> e;
void lambda_inst(int c) {
std::cout << "lambda_inst callback" << std::endl;
}
void bind_inst(int c) {
std::cout << "bind_inst callback" << std::endl;
}
static void static_method(int c) {
std::cout << "static_method callback" << std::endl;
}
EventTest()
{
//静态lambda两种方式
e.AddListener([](int c) { std::cout << "static lambda1 callback" << std::endl; });
e += [](int c) { std::cout << "static lambda2 callback" << std::endl; };
//静态函数
e += static_method; //或 e.AddListener(static_method);
//e -= static_method; 或 e.RemoveListener(static_method);
//添加与移除闭包lambda方法,可以把lambda托管给this,然后最后按实例移除
this->cb_index = e.AddListener(this, [this](int c) { this->lambda_inst(c); });
//e.RemoveListenerByIndex(this->cb_index);
//添加与移除成员方法
e.AddListener(this, &EventTest::bind_inst);
//e.RemoveListener(this, &EventTest::bind_inst);
//执行
e.Invoke(3);
//移除实例中的所有事件
e.RemoveByInstance(this);
//移除全部事件
e.RemoveAllListener();
}
};
在程序中使用批量事件通知是很常用的场景,该事件分发器可以绑定多个静态与非静态函数,内部使用标准库的list和function实现,可变模板参数可以拓展到任意长度。
模仿C#中的委托,分为Events、Delegate、Action和Function。以下是继承的结构。- Events (添加移除静态与实例事件)
- Delegate (执行事件、按实例移除或全部移除)
- Action (Delegate特化版本)
- Function (继承Delegate,实现带返回值列表的执行事件)
- Function<bool> (Function偏特化版本,实现返回值验证是否存在false)
- ActionEvents (Event特化版本)
- FunctionEvents (Event特化版本)
Events作为模板需要传入返回值与形参类型,同时Events作为基类,只能使用添加事件和移除事件,并不可以执行。而执行函数是在Delegate层以上的,因为ActionEvents和FunctionEvents只是Events的具体化,Action只是Delegate的具体化,所以就可以有以下的继承关系。- ActionEvents (Events)
- FunctionEvents (Events)
Events类应该实现以下几个功能:- 添加与移除函数指针的事件(普通函数、成员静态函数与无捕获的lambda表达式)
- 添加与移除实例事件(成员实例函数)
- 通过索引来移除闭包lambda
- 提供+=与-=运算符重载(仅函数指针类型)
Delegate类功能实现:- Invoke执行所有事件
- 按实例对象移除事件
- 移除所有事件
普通的函数指针因为在内存中仅存在一份,所以在移除时可以方便的直接对保存的函数指针进行对比。
带捕获的lambda表达式可以直接转换成std::function类型,采用索引的方式来控制:将函数保存后,返回一个自增的索引,外部可以通过这个索引来移除。或者在创建时传入一个实例对象,最后按实例移除事件。
普通的实例函数就需要使用std::bind对实例进行绑定,但是绑定后就没办法对实例和成员指针进行判断,所以使用成员指针来比较,C++的成员指针必须要求指定的类型,所以使用类模板,并通过继承来做类型擦除,类型擦除后就可以将它们保存到一起,同时取值函数也是一个模板函数,在取值时对实例进行基类至子类的转换。
所有事件的绑定绑定都会返回一个索引值,用来标示该事件,主要用于没有名字函数的移除,在单独移除事件时需要该索引值,在不需要移除单事件或移除全部的情况下该索引可以丢弃。
该源码你可以在 https://github.com/Jayshonyves/JxCode.CoreLib/blob/main/CoreLib/Events.hpp 找到。
博客文章页:http://www.imxqy.com/code/cpp/events.html
[C++] 纯文本查看 复制代码 #ifndef CORELIB_EVENTS_HPP
#define CORELIB_EVENTS_HPP
#include <list>
#include <functional>
template<typename TReturn, typename... TArgs>
class Events
{
public:
using FunctionType = std::function<TReturn(TArgs...)>;
using FunctionPointer = TReturn(*)(TArgs...);
protected:
enum class FunctionInfoType
{
Static,
Lambda,
Member
};
class FunctionInfo
{
public:
unsigned int index;
FunctionInfoType type;
public:
FunctionInfo(const unsigned int& index, const FunctionInfoType& type)
: index(index), type(type)
{
}
public:
bool operator ==(const FunctionInfo& r) {
return this->index == r.index;
}
virtual TReturn Invoke(TArgs...) = 0;
};
class StaticFunctionInfo : public FunctionInfo
{
public:
FunctionPointer ptr;
StaticFunctionInfo(const unsigned int& index, FunctionPointer ptr)
: FunctionInfo(index, FunctionInfoType::Static), ptr(ptr)
{
}
virtual TReturn Invoke(TArgs... args) override {
return ptr(args...);
}
};
class LambdaFunctionInfo : public FunctionInfo
{
public:
FunctionType func;
void* instance;
LambdaFunctionInfo(
const unsigned int& index,
void* instance,
const FunctionType& func)
: FunctionInfo(index, FunctionInfoType::Lambda), instance(instance), func(func)
{
}
virtual TReturn Invoke(TArgs... args) override {
return func(args...);
}
};
template<typename TObj>
class MemberFunctionInfo : public FunctionInfo
{
public:
TObj* instance;
TReturn(TObj::* ptr)(TArgs...);
MemberFunctionInfo(
const unsigned int& index,
TObj* instance,
TReturn(TObj::* ptr)(TArgs...))
: FunctionInfo(index, FunctionInfoType::Member), instance(instance), ptr(ptr)
{
}
virtual TReturn Invoke(TArgs... args) override {
return (instance->*ptr)(args...);
}
};
protected:
unsigned int index;
std::list<FunctionInfo*> eventList;
public:
int Count() const {
return this->eventList.size();
}
public:
Events() : index(0) {}
Events(const Events& right) = delete;
Events(Events&& right) = delete;
~Events() {
this->RemoveAllListener();
}
protected:
void RemoveAllListener() {
for (auto it = this->eventList.begin(); it != this->eventList.end(); it++) {
delete* it;
}
this->eventList.clear();
}
public:
//static
unsigned int AddListener(FunctionPointer funcPtr) {
if (funcPtr == nullptr) {
return 0;
}
this->eventList.push_back(new StaticFunctionInfo(++this->index, funcPtr));
return this->index;
}
//member
template<typename TObj>
unsigned int AddListener(TObj* obj, TReturn(TObj::* ptr)(TArgs...)) {
if (obj == nullptr) {
return 0;
}
this->eventList.push_back(new MemberFunctionInfo<TObj>(++this->index, obj, ptr));
return this->index;
}
//lambda
template<typename TObj>
unsigned int AddListener(TObj* obj, const FunctionType& func) {
this->eventList.push_back(new LambdaFunctionInfo(++this->index, obj, func));
return this->index;
}
//static
unsigned int RemoveListener(FunctionPointer funcPtr) {
for (auto it = this->eventList.begin(); it != this->eventList.end(); it++) {
if ((*it)->type == FunctionInfoType::Static
&& static_cast<StaticFunctionInfo*>(*it)->ptr == funcPtr) {
auto index = (*it)->index;
delete* it;
this->eventList.erase(it);
return index;
}
}
return 0;
}
//member
template<typename TObj>
unsigned int RemoveListener(TObj* obj, TReturn(TObj::* ptr)(TArgs...)) {
for (auto it = this->eventList.begin(); it != this->eventList.end(); it++)
{
if ((*it)->type == FunctionInfoType::Member
&& static_cast<MemberFunctionInfo<TObj>*>(*it)->instance == obj
&& static_cast<MemberFunctionInfo<TObj>*>(*it)->ptr == ptr)
{
auto index = (*it)->index;
delete* it;
this->eventList.erase(it);
return index;
}
}
return 0;
}
unsigned int RemoveListenerByIndex(unsigned int index) {
if (index <= 0) {
return 0;
}
for (auto it = this->eventList.begin(); it != this->eventList.end(); it++) {
if ((*it)->index == index) {
delete* it;
this->eventList.erase(it);
return index;
}
}
return 0;
}
unsigned int operator+=(FunctionPointer ptr) {
return this->AddListener(ptr);
}
unsigned int operator-=(FunctionPointer ptr) {
return this->RemoveListener(ptr);
}
};
template<typename TReturn, typename... TArgs>
class Delegate : public Events<TReturn, TArgs...>
{
using base = Events<TReturn, TArgs...>;
public:
void Invoke(TArgs... t) {
for (auto& item : this->eventList) {
item->Invoke(t...);
}
}
void RemoveAllListener() {
base::RemoveAllListener();
}
//member lambda
template<typename TObj>
void RemoveByInstance(TObj* obj) {
using MemberFunInfo = typename Events<TReturn, TArgs...>::template MemberFunctionInfo<TObj>;
using LambdaFunInfo = typename Events<TReturn, TArgs...>::LambdaFunctionInfo;
for (auto it = this->eventList.begin(); it != this->eventList.end(); ) {
if ((
(*it)->type == base::FunctionInfoType::Member
&& (static_cast<MemberFunInfo*>(*it))->instance == obj
) || (
(*it)->type == base::FunctionInfoType::Lambda
&& static_cast<LambdaFunInfo*>(*it)->instance == obj
)
)
{
delete* it;
it = this->eventList.erase(it);
}
else {
it++;
}
}
}
};
template<typename... TArgs>
using ActionEvents = Events<void, TArgs...>;
template<typename... TArgs>
using Action = Delegate<void, TArgs...>;
template<typename TReturn, typename... TArgs>
using FunctionEvents = Events<TReturn, TArgs...>;
template<typename TReturn, typename... TArgs>
class Function : public Delegate<TReturn, TArgs...>
{
public:
std::vector<TReturn> InvokeResult(TArgs... args) {
std::vector<TReturn> retList;
for (auto& item : this->eventList) {
retList.push_back(item->Invoke(args...));
}
return retList;
}
};
template<>
class Function<bool> : public Delegate<bool>
{
public:
std::vector<bool> InvokeResult() {
std::vector<bool> retList;
for (auto& item : this->eventList) {
retList.push_back(item->Invoke());
}
return retList;
}
bool IsValidReturnInvoke() {
for (const bool& item : this->InvokeResult()) {
if (!item) return false;
}
return true;
}
};
#endif // !CORELIB_EVENTS_HPP |