cpp模仿C#事件机制(1) 无参数版实现
起因
- 之前写unity时,感觉c#的事件机制超好用,就想在万能的c++中实现一下
- 看了网上的一些实现,大部分都是只能注册一个回调函数的,这有啥用???
- 要么就是使用时要继承一大堆东西,一点也不方便
设计
- 本身很简单,就是一个观察者模式,麻烦的是对回调函数的管理
- 需要一个容器存储回调函数,set,vector都行
- 要装进容器中,那么你的回调函数就要支持一些符号
- 这里实现了两种事件发布器
- 基于
std::unordered_set
,重复插入会返回false,调用顺序和插入顺序无关 - 基于
std::vector
,允许重复插入,调用顺序和插入顺序相同
- 基于
- 两种事件发布器都提供注册常驻回调函数和一次性回调函数接口(扩展了下,还是有这方面的需求的)
- 多线程只是简单的分类锁了下,有需要的话可以扩展各种
无锁生产者消费者模型
或者由订阅器维护回调函数,发布器维护订阅器引用
- 回调函数过期
- 回调函数已经被释放会导致空指针问题,程序会core,c#中也有这个问题
- 这里尽可能解决下
- 使用
std::shared_ptr
包裹回调函数,订阅器自己必须维护ptr - 发布器使用
std::weak_ptr
来指向回调函数 - 已经过期的直接从队列中删除,报告下
- 使用
- 这样就解决了大部分过期问题
- 现存的问题
- 当回调函数线程已经通过检测了,准备调用,这时订阅器被销毁了,还会有问题
- 如果再引入锁的话开销有些大,先这样吧
- 最终设计
- 订阅器使用
std::weak_ptr<std::function<void()>>
作为回调函数 - 发布器使用
std::shared_ptr<std::function<void()>>
来保存回调函数引用 - 有参数的,类中的方法,仿函数之类的使用std::bind包装为std::function<void()>
- 订阅器使用
用法
不需要任何继承
任何我能想到的c++调用方式都是支持的
实现
- 本身挺简单的,就是一个观察者模式
- 额外实现了
- 多线程安全:使用锁
- 一次性任务:执行完成后删除自身
- 空指针任务抛弃:weak_ptr lock失败的直接删掉
- 只需要给
std::weak_ptr
实现hash和equal函数就行using DelegrateWeakSave = std::weak_ptr<std::function<void()>>; using DelegrateDefine = std::shared_ptr<std::function<void()>>; struct HandleBindHash { std::size_t operator()(DelegrateWeakSave const& ptr) const { if (auto spt = ptr.lock()) { // 使用之前必须复制到 shared_ptr return reinterpret_cast<size_t>(&(*spt)); } else { return 0; } } std::size_t operator()(DelegrateDefine const& ptr) const { return reinterpret_cast<size_t>(&(*ptr)); } }; struct HandleBindEqual { bool operator()(const DelegrateWeakSave& lhs, const DelegrateWeakSave& rhs) const { return HandleBindHash()(lhs) == HandleBindHash()(rhs); } };
- 这里简单使用地址作为hash地址,由于是用shared_ptr来包裹std::function的,所以没问题
- 剩下的就是一个观察者模式了,没啥说的
完整代码
- 太长了,不贴了
- headOnly引用后直接可以使用
- github文件传送门