C++中事件发布订阅的方法总结和EventEmitter分析
最朴素的一种方式简析
C++中发布订阅的方法有很多种,
个人认为最朴素的一种方式,也是我编程生涯中最早遇到的方式,简单说一下,这里以一个websocket的客户端链接为例进行说明
有多个任务比如Renderer(UI渲染)、Logger(日志记录)、事件上报(Reporter)三个任务,这三个任务要做的工作都依赖了websocket的执行情况,这里我们就可以使用发布订阅的方式:
WebSocketImpl类
// WebSocket事件通知接口
class WebSocketEventHandler {
virtual void onJoin() = 0;
virtual void onLeave() = 0;
};
WebSocket 类
class WebSocketImpl {
public:
void registerEventHandler(WebSocketEventHandler* handler);
void unRegistEventHandler(WebSocketEventHandler* handler);
void
private:
std::list<WebSocketEventHandler> event_lists_;
};
Renderer类
class Renderer : public WebSocketEventHandler {
void onJoin() override;
void onLeave() override;
};
Logger类
class Logger : public WebSocketEventHandler {
void onJoin() override;
void onLeave() override;
};
Reporter类
class Reporter : public WebSocketEventHandler {
void onJoin() override;
void onLeave() override;
};
业务逻辑除可能会有类似的代码
int main(void) {
render.registerEventHandler(render);
logger.registerEventHandler(logger);
reporter.registerEventHandler(logger);
whie (true) {
...
}
render.unRegistEventHandler(render);
logger.unRegistEventHandler(logger);
reporter.unRegistEventHandler(logger);
}
总结
代码冗余比较多。现在很少再看到有这种代码了。
EventEmitter 分析
先上源码
//
// EventEmitter.hpp
// EventEmitter
//
// Created by BlueCocoa on 2016/8/6.
// Copyright © 2016 BlueCocoa. All rights reserved.
//
#ifndef EVENTEMITTER_HPP
#define EVENTEMITTER_HPP
#include <map>
#include <mutex>
#include <string>
#include <tuple>
#include <type_traits>
#include <vector>
#include <algorithm>
#include <Functor.hpp>
#define Infinity 0
class EventEmitter {
public:
/**
* @brief EventListener
*
* @note Callable Function, Call once
*/
using EventListener = std::tuple<Functor *, bool>;
/**
* @brief Deconstructor
*/
~EventEmitter() {
std::unique_lock<std::recursive_mutex> locker(_events_mtx);
std::for_each(events.begin(), events.end(), [](std::pair<std::string, std::vector<EventListener>> pair) {
std::vector<EventListener>& listeners = pair.second;
std::for_each(listeners.begin(), listeners.end(), [](EventListener& listener) {
//delete std::get<0>(listener);
//std::get<0>(listener) = nullptr;
});
});
events.clear();
}
/**
* @brief Event setter
*
* @param event Event name
* @param lambda Callback function when event emitted
*/
template <typename Function>
void on(const std::string& event, Function&& lambda) {
std::unique_lock<std::recursive_mutex> locker(_events_mtx);
events[event].emplace_back(new Functor{ std::forward<Function>(lambda) }, false);
}
/**
* @brief Once event
*
* @param event Event name
* @param lambda Callback function when event emitted
*/
template <typename Function>
void once(const std::string& event, Function&& lambda) {
std::unique_lock<std::recursive_mutex> locker(_events_mtx);
events[event].emplace_back(new Functor{ std::forward<Function>(lambda) }, true);
}
/**
* @brief Event emitter
*
* @param event Event name
*/
template <typename ... Arg>
void emit(const std::string& event, Arg&& ... args) {
std::unique_lock<std::recursive_mutex> locker(_events_mtx);
auto event_listeners = events.find(event);
if (event_listeners == events.end()) return;
std::vector<Functor *> functors;
std::vector<EventListener>& listeners = events[event];
for (auto listener = listeners.begin(); listener != listeners.end();) {
Functor * on = std::get<0>(*listener);
bool once = std::get<1>(*listener);
functors.push_back(on);
if (once) {
// delete on;
//std::get<0>(*listener) = nullptr;
listener = listeners.erase(listener);
}
else
{
++listener;
}
}
listeners.shrink_to_fit();
for (auto &it:functors)
{
Functor * on = it;
if (on) {
(*on) (std::forward<Arg>(args)...);
}
}
}
/**
* @brief Number of listeners
*
* @param event Event name
*/
int listenerCount(const std::string& event) {
std::unique_lock<std::recursive_mutex> locker(_events_mtx);
auto event_listeners = events.find(event);
if (event_listeners == events.end()) return 0;
return events[event].size();;
}
/**
* @brief Remove all listeners
*
* @param event Event name
*/
void removeAllListeners(const std::string& event){
std::unique_lock<std::recursive_mutex> locker(_events_mtx);
std::vector<EventListener>& listeners = events[event];
std::for_each(listeners.begin(), listeners.end(), [](EventListener& listener) {
//delete std::get<0>(listener);
//std::get<0>(listener) = nullptr;
});
listeners.clear();
events.erase(event);
}
void setMaxListeners(int listenerCount)
{
}
protected:
/**
* @brief Constructor
*/
EventEmitter() {
};
/**
* @brief Event name - EventListener
*/
std::map<std::string, std::vector<EventListener>> events;
private:
/**
* @brief Mutex for events
*/
std::recursive_mutex _events_mtx;
};
#endif /* EVENTEMITTER_HPP */
解析
- 类中的构造函数是protected,那就表明我们使用的时候无法用new直接构造,我们要通过new一个子类来实现
- emit和on必须是同一个应用实体
- emit和on的操作是线程安全的
- 订阅事件可以大于1个
- 析构函数会主动清理订阅内容,使用时候注意事件处理的生命周期即可。
例子
还是以上面举的例子为例:
有多个任务比如Renderer(UI渲染)、Logger(日志记录)、事件上报(Reporter)三个任务,这三个任务要做的工作都依赖了websocket的执行情况
class WebSocketImpl : public EventEmitter {
void onJoin() {
emit("ws-joined", this);
}
void onLeave() {
emit("ws-leaved", this, ...);
}
};
业务处理部分
int main(void) {
WebSocketImpl* impl = new WebSocketImpl;
impl->run();
/*订阅事件*/
impl.on("ws-joined", [=](WebSocketImpl* impl) {
//report ...
});
impl.on("ws-joined", [=](WebSocketImpl* impl) {
//logger ...
});
impl.on("ws-joined", [=](WebSocketImpl* impl) {
//renderer ...
});
...
impl.on("ws-leaved", [=](WebSocketImpl* impl, ...) {
//report ...
});
impl.on("ws-leaved", [=](WebSocketImpl* impl, ...) {
//logger ...
});
impl.on("ws-leaved", [=](WebSocketImpl* impl, ...) {
//renderer ...
});
}
总结
- 代码更加简洁,冗余较少
- 减少了代码的耦合度,省掉了WebSocketEventHandler这个中间代理
总结再总结
当然还有很多这样的技术可以进一步深入研究
比如
- Qt的Signal/Slot技术
- Boost中的sigslot实现等等,
- 还有一些rxjava、eventbus中的的一些封装更加完善的一用,都值得我们去学习和玩味。
但说了这么多,其实再学习的时候,个人觉得要注意两个问题就可以放心使用了。
- 是否支持线程安全
- 是否是同步调用,上面的两个例子都是同步的