A Mimetic C# Style Multicast Event Implementation with C++
A Mimetic C# Style Multicast Event Implementation with C++
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 {
/**
* @brief Base class of event
*/
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:
/**
* @brief Register an event handler
*
* @param[in] handler - Event handler delegate
* @return - True if registeration succeed
*/
bool operator += (const Handler &handler) {
if(BaseType::find(handler) != BaseType::end())
return false;
BaseType::insert(handler);
return true;
}
/**
* @brief Unregister an event handler
*
* @param[in] handler - Event handler delegate
* @return - True if unregisteration succeed
*/
bool operator -= (const Handler &handler) {
if(BaseType::find(handler) == BaseType::end())
return false;
BaseType::erase(handler);
return true;
}
};
/**
* @brief Event class
*/
template<typename Signature>
class Event;
/**
* @brief Event class without arguments
* @note This is a final class which you cannot derive from it
*/
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);
}
/**
* @brief Raise an event
*/
void operator() (void) const {
for(const_iterator it = begin(); it != end(); ++it) {
Handler d = *it;
d();
}
}
};
/**
* @brief Event class with 1 argument
* @note This is a final class which you cannot derive from it
*/
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);
}
/**
* @brief Raise an event
*
* @param[in] arg1 - 1st argument
*/
void operator() (Arg1T &arg1) {
for(typename BaseType::iterator it = BaseType::begin(); it != BaseType::end(); ++it) {
typename BaseType::Handler d = *it;
d(arg1);
}
}
/**
* @brief Raise an event
*
* @param[in] arg1 - 1st argument
*/
void operator() (Arg1T arg1) const {
for(typename BaseType::const_iterator it = BaseType::begin(); it != BaseType::end(); ++it) {
typename BaseType::Handler d = *it;
d(arg1);
}
}
};
/**
* @brief Event class with 2 arguments
* @note This is a final class which you cannot derive from it
*/
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);
}
/**
* @brief Raise an event
*
* @param[in] arg1 - 1st argument
* @param[in] arg2 - 2nd argument
*/
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);
}
}
/**
* @brief Raise an event
*
* @param[in] arg1 - 1st argument
* @param[in] arg2 - 2nd argument
*/
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);
}
}
};
/**
* @brief Event class with 3 arguments
* @note This is a final class which you cannot derive from it
*/
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);
}
/**
* @brief Raise an event
*
* @param[in] arg1 - 1st argument
* @param[in] arg2 - 2nd argument
* @param[in] arg3 - 3rd argument
*/
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);
}
}
/**
* @brief Raise an event
*
* @param[in] arg1 - 1st argument
* @param[in] arg2 - 2nd argument
* @param[in] arg3 - 3rd argument
*/
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);
}
}
};
// Add more if you wish.
}
#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#.