Skip to content

Implementing EventEmitter

Daniel Kang edited this page Feb 12, 2012 · 7 revisions

This is the second prototype EventEmitter class code:

template<typename ...F>
struct EventMap
{
    typedef typename utility::tuple_even_elements<F...>::type events;
    typedef typename utility::tuple_odd_elements<F...>::type callbacks;
};

template<typename map>
class EventEmitter
{
    typedef typename map::events events;
    typedef typename map::callbacks callbacks;

    template<typename e>
    struct evt_idx : public std::integral_constant<std::size_t, utility::tuple_index_of<events, e>::value> {};

    template<typename e>
    struct cb_def
    { typedef typename std::tuple_element<evt_idx<e>::value, callbacks>::type type; };

public:
    typedef void* listener_t;

    EventEmitter()
        : set_()
    {}

    virtual ~EventEmitter()
    {
    }

    template<typename event>
    auto addListener(typename cb_def<event>::type callback) -> decltype(&callback)
    {
        auto& entry = std::get<evt_idx<event>::value>(set_);

        auto ptr = new decltype(callback)(callback);
        entry.push_back(std::shared_ptr<decltype(callback)>(ptr));
        return ptr;
    }

    template<typename event>
    bool removeListener(typename cb_def<event>::type* callback_ptr)
    {
        auto& entry = std::get<evt_idx<event>::value>(set_);

        auto it = entry.begin();
        for(;it!=entry.end();++it)
        {
            if(it->get() == callback_ptr)
            {
                entry.erase(it);
                return true;
            }
        }
        return false;
    }

    template<typename event>
    void removeAllListeners()
    {
        auto& entry = std::get<evt_idx<event>::value>(set_);
        entry.clear();
    }

    template<typename event, typename ...A>
    void emit(A&&... args)
    {
        auto& entry = std::get<evt_idx<event>::value>(set_);
        for(auto x : entry) (*x)(std::forward<A>(args)...);
    }

private:
    template<typename>
    struct set_t;

    template<typename ...T>
    struct set_t<std::tuple<T...>>
    { typedef std::tuple<std::list<std::shared_ptr<T>>...> type; };

    typename set_t<callbacks>::type set_;
};

In this implementation, EventEmitter uses compile-time event-callback mapping: EventMap class.

To define your own event-callback mapping:

struct event1 {};
struct event2 {};
struct event3 {};
struct event4 {};   

typedef dev::EventMap<
    event1, std::function<void(int)>,
    event2, std::function<void(const std::string&)>,
    event3, std::function<void()>
> defs;

And them, you can use this mapping whenever you create an EventEmitter (or its inherited) instance.

void test1(int a)
{
    printf("test1(%d)\n", a);
}

void test2()
{
    printf("test2()\n");
}

// ....

int v = 1000;
dev::EventEmitter<defs> bar1;

auto x1 = bar1.addListener<event1>(test1);
auto x2 = bar1.addListener<event1>([&](int n){
    printf("lambda: n=%d v=%d\n", n, v);
});
bar1.emit<event1>(5264);
printf("============================\n");

bar1.addListener<event3>(test2);
bar1.addListener<event3>(test2);
bar1.emit<event3>();
printf("============================\n");

bar1.removeListener<event1>(x1);
bar1.emit<event1>(4432);
printf("============================\n");

bar1.removeAllListeners<event1>();
bar1.emit<event1>(5132);
printf("============================\n");

In the above code, I created an EventEmitter instance named bar1.

  • While the first version required us to explicitly write template parameters for each addListener() call, this second version does not.
  • Still you have to save the return value from addListener() function to remove that listener using removeListener() function.
  • Another advances: incorrect use of event names (template parameters) generate compile-time error. They're all type-safe in run-time.
  • Event names are types, not ints.

Clone this wiki locally