Introduction
Although we have many ways to implement an observer pattern, inherited listener is used in most C++ OOP. Some programmers using C# (or Java, etc.) may be familiar with the non-inherited delegate solution, that is the most effective way in those programming languages. I'll show you a simple way to mime a C# style multicast event system with C++ in this tip. My implementation requires the fastdelegate library.
The Implementation
#ifndef __EVENT_H__
#define __EVENT_H__
#include <set>
#include "FastDelegate.h"
#include "FastDelegateBind.h"
namespace kks {
template<typename HandlerT>
class _Event : public std::set<fastdelegate::FastDelegate<HandlerT> > {
public:
typedef std::set<fastdelegate::FastDelegate<HandlerT> > BaseType;
typedef fastdelegate::FastDelegate<HandlerT> Handler;
public:
bool operator += (const Handler &handler) {
if(BaseType::find(handler) != BaseType::end())
return false;
BaseType::insert(handler);
return true;
}
bool operator -= (const Handler &handler) {
if(BaseType::find(handler) == BaseType::end())
return false;
BaseType::erase(handler);
return true;
}
};
template<typename Signature>
class Event;
template<>
class Event<void(void)> : public _Event<void(void)> {
public:
typedef _Event<void(void)> BaseType;
public:
Event() {
}
Event(const Event::Handler &handler) {
this->operator += (handler);
}
void operator() (void) const {
for(const_iterator it = begin(); it != end(); ++it) {
Handler d = *it;
d();
}
}
};
template<typename Arg1T>
class Event<void(Arg1T)> : public _Event<void(Arg1T)> {
public:
typedef _Event<void(Arg1T)> BaseType;
public:
Event() {
}
Event(const typename Event::Handler &handler) {
this->operator += (handler);
}
void operator() (Arg1T &arg1) {
for(typename BaseType::iterator it = BaseType::begin(); it != BaseType::end(); ++it) {
typename BaseType::Handler d = *it;
d(arg1);
}
}
void operator() (Arg1T arg1) const {
for(typename BaseType::const_iterator it = BaseType::begin(); it != BaseType::end(); ++it) {
typename BaseType::Handler d = *it;
d(arg1);
}
}
};
template<typename Arg1T, typename Arg2T>
class Event<void(Arg1T, Arg2T)> : public _Event<void(Arg1T, Arg2T)> {
public:
typedef _Event<void(Arg1T, Arg2T)> BaseType;
public:
Event() {
}
Event(const typename Event::Handler &handler) {
this->operator += (handler);
}
void operator() (Arg1T &arg1, Arg2T &arg2) {
for(typename BaseType::iterator it = BaseType::begin(); it != BaseType::end(); ++it) {
typename BaseType::Handler d = *it;
d(arg1, arg2);
}
}
void operator() (Arg1T arg1, Arg2T arg2) const {
for(typename BaseType::const_iterator it = BaseType::begin(); it != BaseType::end(); ++it) {
typename BaseType::Handler d = *it;
d(arg1, arg2);
}
}
};
template<typename Arg1T, typename Arg2T, typename Arg3T>
class Event<void(Arg1T, Arg2T, Arg3T)> : public _Event<void(Arg1T, Arg2T, Arg3T)> {
public:
typedef _Event<void(Arg1T, Arg2T, Arg3T)> BaseType;
public:
Event() {
}
Event(const typename Event::Handler &handler) {
this->operator += (handler);
}
void operator() (Arg1T &arg1, Arg2T &arg2, Arg3T &arg3) {
for(typename BaseType::iterator it = BaseType::begin(); it != BaseType::end(); ++it) {
typename BaseType::Handler d = *it;
d(arg1, arg2, arg3);
}
}
void operator() (Arg1T arg1, Arg2T arg2, Arg3T arg3) const {
for(typename BaseType::const_iterator it = BaseType::begin(); it != BaseType::end(); ++it) {
typename BaseType::Handler d = *it;
d(arg1, arg2, arg3);
}
}
};
}
#endif // __EVENT_H__
Using the Code
Follow these steps to use this page of code:
- You maybe should make an event type definition, because it eases further coding and maintaining:
typedef Event<void(Arg1T, Arg2T, ...)> SomeEvent;
- You can use that definition to declare an event:
SomeEvent OnSomeEvent;
- If something occurred, raise an event using:
OnSomeEvent(Arg1, Arg2, ...);
- To register an event handler observing it, write
obj->SomeEvent += SomeEvent::Handler(Class*, &Class::Method);
- Don't forget to unregister a handler after someone doesn't observe that event any more by:
obj->SomeEvent -= SomeEvent::Handler(Class*, &Class::Method);
- A habitual pattern to define an event is some sort like:
Event<void(Sender*, EventArgsStruct*)>
in which "Sender
" is an event raiser, whilst "EventArgsStruct
" is a common structure which encapsulates detail event arguments; this is a usage derived from C#.