-
Notifications
You must be signed in to change notification settings - Fork 147
Implementing EventEmitter
Daniel Kang edited this page Feb 13, 2012
·
7 revisions
The third prototype EventEmitter class code:
template<typename ...M>
class EventEmitter
{
typedef typename util::tuple_merge<M...>::type merged_map;
typedef typename util::tuple_even_elements<merged_map>::type events;
typedef typename util::tuple_odd_elements<merged_map>::type callbacks;
template<typename E>
struct evt_idx : public std::integral_constant<std::size_t, util::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 E>
auto addListener(typename cb_def<E>::type callback) -> decltype(&callback)
{
auto& entry = std::get<evt_idx<E>::value>(set_);
auto ptr = new decltype(callback)(callback);
entry.push_back(std::shared_ptr<decltype(callback)>(ptr));
return ptr;
}
template<typename E>
bool removeListener(typename cb_def<E>::type* callback_ptr)
{
auto& entry = std::get<evt_idx<E>::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 E>
void removeAllListeners()
{
auto& entry = std::get<evt_idx<E>::value>(set_);
entry.clear();
}
template<typename E, typename ...A>
void emit(A&&... args)
{
auto& entry = std::get<evt_idx<E>::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_;
};
For template utilities defined in dev::util, please see native/utility.h.
In this implementation, EventEmitter takes any numbers of std::tuple as template parameter.
To define your own event-callback mapping:
struct event1 {};
struct event2 {};
struct event3 {};
struct event4 {};
typedef std::tuple<
event1, std::function<void(int)>
> foo_events;
typedef std::tuple<
event2, std::function<void(const std::string&)>,
event3, std::function<void()>
> bar_events;
And then, you can use this mapping whenever you create an EventEmitter (or its inherited) instance.
template<typename ...maps>
struct foo : public dev::EventEmitter<foo_events, maps...>
{
// ...
};
template<typename ...maps>
struct bar : public foo<bar_events, maps...>
{
// ...
};
void test1(int a)
{
printf("test1(%d)\n", a);
}
void test2()
{
printf("test2()\n");
}
// ....
int v = 1000;
bar<> 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 code above, I defined foo class that takes event1, and bar class that takes event2 and event3 additionally.
- You don't have to specify function signature for each addListener() or emit() calls.
- Still you have to save the return value from addListener() function to remove that listener using removeListener() function.
- They're all type-safe in run-time.
- Event names are types, not ints.