Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

A Mimetic C# Style Multicast Event Implementation with C++

5.00/5 (1 vote)
26 Aug 2013CPOL 16.7K  
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

C++
#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:

  1. You maybe should make an event type definition, because it eases further coding and maintaining:
    C#
    typedef Event<void(Arg1T, Arg2T, ...)> SomeEvent;
  2. You can use that definition to declare an event:
    C#
    SomeEvent OnSomeEvent;
  3. If something occurred, raise an event using:
    C#
    OnSomeEvent(Arg1, Arg2, ...);
  4. To register an event handler observing it, write
    C#
    obj->SomeEvent += SomeEvent::Handler(Class*, &Class::Method);
  5. Don't forget to unregister a handler after someone doesn't observe that event any more by:
    C#
    obj->SomeEvent -= SomeEvent::Handler(Class*, &Class::Method);
  6. A habitual pattern to define an event is some sort like:
    C#
    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#.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)