node.js里面的EventEmitter非常好用,于是上次写完Functor之后,就顺理成章的写了这个EventEmitter,或者说,就是为了实现这个EventEmitter才写的Functor(后文会提到为什么要Functor,毕竟如果只看Functor本身的话,还不如直接用lamdba方便)。
在node.js中,使用EventEmitter非常简单,
const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('event', () => { console.log('an event occurred!'); }); myEmitter.emit('event');
——https://nodejs.org/api/events.html
那么现在这个C++版的EventEmitter也必须做到这样简单易用,事实上,它也的确和node.js的使用方式类似。
#include <iostream> #include <sstream> #include <thread> #include <vector> #include "EventEmitter.hpp" using namespace std; class emitter : public EventEmitter { }; int main(int argc, const char * argv[]) { emitter emitter; emitter.on("event", [&emitter](int data) { ostringstream osstream; osstream << "data: " << data << '\n'; std::cout << osstream.str(); }); vector<thread> threads; for (int i = 0; i < 10; i++) { threads.emplace_back([&emitter, i]() { emitter.emit("event", i); }); } for (auto &t : threads) t.join(); }
其实EventEmitter本身的实现很简单,通过std::map
做一个event name到Functor *的映射,然后在emit
时取出Functor *,判断一下指针,不是nullptr
就调用。
这里用Functor的原因就是,因为根据不同的需求,回调的lambda也不尽相同,若是lambda接受的参数都一样,兴许还可以用std::function
来wrap,但这并不是一个通用的方案。
直接存放lambda也是不可行的,
auto lambda = []() { }; typedef std::map<std::string, decltype(lambda)> function_map; function_map event{"event", [](){}}; // won't compile function_map event; event["event"] = lambda; // won't compile, too
所以必须要一个lambda wrapper来做这件事,所以有了Functor。
上面样例的运行结果如下图(实际中会因线程执行完的先后而有所不同)
最后就是EventEmitter.hpp了,最新的版本在我的Github上,EventEmitter
// // EventEmitter.hpp // EventEmitter // // Created by Ryza 2016/8/6. // Copyright © 2016[data deleted]. All rights reserved. // #ifndef EVENTEMITTER_HPP #define EVENTEMITTER_HPP #include <map> #include <string> #include "Functor.hpp" class EventEmitter { public: /** * @brief Deconstructor */ ~EventEmitter() { std::for_each(events.begin(), events.end(), [](std::pair<std::string, Functor *> pair) { delete pair.second; }); 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) { events[event] = new Functor{std::forward<Function>(lambda)}; } /** * @brief Event emitter * * @param event Event name */ template <typename ... Arg> void emit(const std::string& event, Arg&& ... args) { Functor * on = events[event]; if (on) (*on)(std::forward<Arg>(args)...); } /** * @brief Event name - Callback function */ std::map<std::string, Functor *> events; protected: /** * @brief Constructor */ EventEmitter() { }; }; #endif /* EVENTEMITTER_HPP */