Click here to Skip to main content
15,867,973 members
Articles / Programming Languages / C++

An event mechanism for C++ using an interface based delegate

Rate me:
Please Sign up or sign in to vote.
2.00/5 (6 votes)
29 Mar 2007CPOL 33.4K   285   12   4
Introducing a simple event mechanism for C++ using a C# style delegate.

Introduction

This article introduces a very simple and comfortable event mechanism in C++. In many cases, a delegate in C++ is hard to understand because of its complexity. The attached code is a sample that uses the combination of the C# delegation style and Java's inner class callback style. It can show you an example of an easy event mechanism.

Event Processing

C++
// delegate
struct ISampleEventHandler : public IEventHandler
{
    virtual void callback( IUnknown* object, const SampleEventArgs& e ) = 0;
    typedef SampleEventArgs _EventArgs; // passive polymorphism using typedef
};

// event
template<typename T>
class Event
{
public:
    void invoke(IUnknown* object, const EventArgs& e = EventArgs::Empty)
    {
        std::set<T*>::iterator iter;
        for (iter = m_EventHandler.begin();
            iter != m_EventHandler.end();
            ++iter)
        {
            // this code can promise the type-safe callback because
            // of the polymorphism using typedef in delegate
            (*iter)->callback(object, static_cast<const T::_EventArgs&>(e));
        }
    }
    void operator+=(T* eventHandler)
    {
        m_EventHandler.insert(eventHandler);
    }

    void operator-=(T* eventHandler)
    {
        m_EventHandler.erase(eventHandler);
    }

protected:
    std::set<T*> m_EventHandler;
};

Using the Code

We need an abstract parent class to identify the event sender.

C++
class IUnknown
{
public:
 virtual ~IUnknown() {}
 virtual std::string name() = 0;
};

Here is the event sending class:

C++
class Callee : public IUnknown
{
public:
// declare the event.
 Event<ISampleEventHandler> SampleEvent;
public:
 void test()
 {
  SampleEventArgs e;
  e.sampleData = 10;
  e.sampleData2 = 20;

// event occur.
  SampleEvent.invoke(this, e);
 }
 std::string name() { return "callee"; }
};

Here is the event receiving class:

C++
class Caller : public IUnknown
{
public:
 Caller()
 {
// register Caller's inner class object to Callee.
  m_Callee.SampleEvent += &sampleEventHandler;
 }
 ~Caller()
 {
// unregister Caller's inner class object from Callee.
  m_Callee.SampleEvent -= &sampleEventHandler;
 }
 void test()
 {
  m_Callee.test();
 }
private:
 void print(IUnknown* object, const SampleEventArgs& e)
 {
  std::cout << object->name().c_str() << std::endl;
  std::cout << e.sampleData << std::endl;
  std::cout << e.sampleData2 << std::endl;
 }
 std::string name() { return "caller"; }
private:
 class SampleEventHandler : public ISampleEventHandler
 {
  void callback( IUnknown* object, const SampleEventArgs& e )
  {
    // to access outer class's member function.
    Caller* outer = reinterpret_cast<Caller*>((char*)this - 
                       offsetof(Caller, sampleEventHandler));

    outer->print(object, e);
  }
 } sampleEventHandler;
private:
 Callee m_Callee;
};

Points of Interest

You can easily find the reason why using the delegate is nice. It can break the cyclic dependency of each class - the 'Caller' depends on the 'Callee' but the 'Callee' does not have dependency with any classes.

History

  • 2007. 03. 29 - Initially released.

License

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


Written By
Korea (Republic of) Korea (Republic of)
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionstd::set ?!? Pin
Emilio Garavaglia31-Mar-07 3:28
Emilio Garavaglia31-Mar-07 3:28 
AnswerRe: std::set ?!? Pin
choiday31-Mar-07 5:25
choiday31-Mar-07 5:25 
GeneralSome Suggestions Pin
Stuart Dootson29-Mar-07 23:13
professionalStuart Dootson29-Mar-07 23:13 
  1. You could do with more explanation about why you've got the different classes and how they interact.
  2. You ought to mention the Event class really, as that's kind of key to the whole thing.
  3. Is an inner class callback really the best thing to use - it means you have to have abominations like:
C++
Caller* outer = reinterpret_cast<Caller*>((char*)this - offsetof(Caller, sampleEventHandler));
C++ is a multi-paradigm programming language - you can use function pointers, so use a member function pointer to call a method of a class rather than having to use a class.
My final comment (and the one I use whenever I see a delegate system in C++) - look at Boost - the Signals[^] library is (IMO) a good delegate style library. The Function[^], Bind[^] and Lambda[^] libraries are also useful - Boost.Function can be used to implement single-sink delegates, while Boost.Lambda and Boost.Bind are very useful for constructing callbacks from arbitrary C++ functions, methods or function objects.



GeneralRe: Some Suggestions Pin
choiday30-Mar-07 14:57
choiday30-Mar-07 14:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.