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

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

2.00/5 (6 votes)
29 Mar 2007CPOL 1   286  
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)