-
Notifications
You must be signed in to change notification settings - Fork 146
Implementing EventEmitter
Daniel Kang edited this page Feb 11, 2012
·
7 revisions
This is prototype EventEmitter class code:
namespace internal
{
class sigslot_base
{
public:
sigslot_base() {}
virtual ~sigslot_base() {}
virtual void add_callback(void*) = 0;
virtual void remove_callback(void*) = 0;
virtual void reset() = 0;
};
template<typename ...P>
class sigslot: public sigslot_base
{
public:
typedef std::function<void(P...)> callback_type;
typedef std::shared_ptr<callback_type> callback_ptr;
public:
sigslot()
: callbacks_()
{}
virtual ~sigslot()
{}
public:
virtual void add_callback(void* callback)
{
callbacks_.insert(callback_ptr(reinterpret_cast<callback_type*>(callback)));
}
virtual void remove_callback(void* callback)
{
callbacks_.erase(callback_ptr(reinterpret_cast<callback_type*>(callback)));
}
virtual void reset()
{
callbacks_.clear();
}
void invoke(P&&... args)
{
for(auto c : callbacks_) (*c)(std::forward<P>(args)...);
}
private:
std::set<callback_ptr> callbacks_;
};
}
class EventEmitter
{
protected:
EventEmitter() : events_() {}
public:
virtual ~EventEmitter() {}
typedef void* listener_t;
template<typename ...A>
listener_t addListener(
int event_id,
typename internal::sigslot<A...>::callback_type callback)
{
auto s = events_.find(event_id);
if(s != events_.end())
{
// test if function signature matches or not.
assert(dynamic_cast<typename internal::sigslot<A...>*>(s->second.get()));
auto ptr = new typename internal::sigslot<A...>::callback_type(callback);
s->second->add_callback(ptr);
return ptr;
}
else
{
throw "Not registered event.";
}
}
void removeListener(int event_id, listener_t listener)
{
auto s = events_.find(event_id);
if(s != events_.end())
{
s->second->remove_callback(listener);
}
else
{
throw "Not registered event.";
}
}
void reset(int event_id)
{
auto s = events_.find(event_id);
if(s != events_.end())
{
s->second->reset();
}
else
{
throw "Not registered event.";
}
}
template<typename ...A>
void operator()(int event_id, A&&... args)
{
emit(event_id, std::forward<A>(args)...);
}
template<typename ...A>
void emit(int event_id, A&&... args)
{
auto s = events_.find(event_id);
if(s != events_.end())
{
auto x = dynamic_cast<internal::sigslot<A...>*>(s->second.get());
if(x)
{
x->invoke(std::forward<A>(args)...);
}
else
{
throw "Invalid callback type.";
}
}
else
{
throw "Not registered event.";
}
}
protected:
template<typename ...A>
void registerEvent(int event_id)
{
auto it = events_.find(event_id);
if(it != events_.end())
{
throw "Event is already registered.";
}
events_.insert(std::make_pair(
event_id,
std::shared_ptr<internal::sigslot_base>(new internal::sigslot<A...>())));
}
void unregisterEvent(int event_id)
{
events_.erase(event_id);
}
private:
std::map<int, std::shared_ptr<internal::sigslot_base>> events_;
};
To implement your own EventEmitter, you must inherit from EventEmitter and then register your event ids:
enum
{
event1,
event2,
event3
};
class foo : public dev::EventEmitter
{
public:
foo()
{
registerEvent<int>(event1);
registerEvent<const std::string&>(event2);
registerEvent(event3);
}
virtual ~foo()
{}
};
User of your EventEmitter class can do the following code:
int test1(int n)
{
printf("test1(%d)\n", n);
return 0;
}
// ...
printf("Hello~\n");
int v = 5;
foo f;
// add listeners
auto l1 = f.addListener<int>(event1, test1);
f.addListener<int>(event1, test1);
f.addListener<int>(event1, [&](int t){
printf("lambda: t=%d v=%d\n", t, v);
v++;
});
// invoke #1
printf("----------------------\n");
f.emit(event1, 5264);
// invoke #2 with one listener removed
printf("----------------------\n");
f.removeListener(event1, l1);
f.emit(event1, 5264);
// invoke #3 with all listeners removed
printf("----------------------\n");
f.reset(event1);
f.emit(event1, 5264);
printf("Good-bye~\n");
As you can see in the above code, to remove a specific listener, you must save a return value from addListener() function.
And, one thing to note is that you must provide the parameter types as template parameters of addListener() function. I'm trying to make this implicit:
f.addListener(event1, [&](int t){
printf("lambda: t=%d v=%d\n", t, v);
v++;
});
But, it is not that easy as it seems.