A simple EventEmitter in C++

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
EventEmitter

最后就是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 */
 

Leave a Reply

Your email address will not be published. Required fields are marked *

nine − 8 =