-
Notifications
You must be signed in to change notification settings - Fork 147
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.