Events and Delegates: A C++ Implementation


Hi geeks! Hope for you a good coding and debugging day, free of Access Violation and Linkage errors.

First of all, Who should read this article?

  • C++ geek, who dreams of pointers, watches "Bug attack" movies, and prefers to trace the call stack all the night rather than going out with some friends.
  • C# cool dude, sick of Microsoft babysitting, and willing to move to C++, and aware of the trade-offs.
  • Java bean, that never heard of pointer to function concept, and wants to know how C++/C# guys suffer.

I have been using C# for around 2 years. I was totally fascinated by C# object oriented architecture. Events and delegates are of the most C# constructs I liked. However, there is a big trade-off here i.e using delegates in C#, it is flexibility and performance. Delegates in C# are no function to pointers rather they are implemented somehow using reflection. I was shocked when I read an article called Writing Faster Managed Code: Know what things cost, this article is talking about the cost of most common C# operations. You will be surprised when you find that in C# a function call using a delegate is slower 6 times than simply calling a function using its signature. The function call using its signature costs around 6 ns (nano second), while using delegate costs around 40 ns! If you are a normal geek then you should be shocked now. As a consequence to this shock, I don’t know how it happened, but months ago I felt that C++ is calling me. I started to code everything using C++. The geek inside me is controlling! Help me.

Coding C++ again after 1 year of pure C#ing is not easy. I was used to delegates and events heavily in my code to maintain a decoupling between classes. I couldn’t find something like: Click += new Handler(ClickHandler); The only way to achieve something near this in C++ is through function to pointers. A first attempt was done here in this article Meet Functors. The first attempt was about implementing generic delegates, but unfortunately it was over complex, and requires a lot of code to deal with. A second attempt was made, and it is about implementing Events and Delegates in C++ like those in C#. That is what this article is all about.

We are not going to explain the concept here. We will pose the code here, and any explanation required will be in the discussion.

The big goal is the line of code below, i.e approaching C# syntax as much as possible:

p_server->MessageSent += new ClientDelegate(this, &Client::MessageSentHandler);
driver.cpp

class Server;
class Client;

typedef Event<Server> ServerEvent;
typedef Delegate<Client, Server> ClientDelegate;

class Server
{
public:
    ServerEvent MessageSent;

    void SendMessage(char* p_msg)
    {
        MessageSent(this, p_msg);
    }
};

class Client
{
    static int s_id;
    int m_id;
    void MessageSentHandler(const Server* p_sender, void* p_parameter)
    {
        cout << "Client" << m_id << ": received: " << (char*)p_parameter << endl;
    }
public:
    Client(Server* p_server)
    {
        m_id = ++s_id;
        p_server->MessageSent += new ClientDelegate(this, &Client::MessageSentHandler);
    }
};
int Client::s_id = 0;

int main()
{
    Server server;
    Client client1(&server), client2(&server), client3(&server);

    server.SendMessage("Hello");

    return 0;
}

Console Output

Client1: received: Hello 
Client2: received: Hello

Client3: received: Hello

Press any key to continue . . .

Delegate.h

template<class TSender>
class BaseDelegate
{
public:
    virtual bool Equals( const BaseDelegate<TSender>* p_other) = 0;
    virtual void operator()( const TSender* p_sender, void* p_parameter) = 0;
    virtual void Call( const TSender* p_sender, void* p_parameter) = 0;
};

template<class TReciever, class TSender>
class Delegate : public BaseDelegate<TSender>
{
private:
    typedef void (TReciever::*PTF)(const TSender*, void* p_parameter);
    PTF         m_ptr2Func;
    TReciever*  m_ptr2Object;

public:
    Delegate(TReciever* p_ptr2Object, PTF p_ptr2Func)
    {
        m_ptr2Func      = p_ptr2Func;
        m_ptr2Object    = p_ptr2Object;
    }

    bool Equals(const BaseDelegate<TSender>* p_other)
    {
        const Delegate<TReciever, TSender>* other;

        other = static_cast<const Delegate<TReciever, TSender>*>(p_other);

        assert(other != NULL);
        assert(m_ptr2Object != NULL);

        return other->m_ptr2Object == m_ptr2Object && other->m_ptr2Func == m_ptr2Func;
    }

    virtual void operator()(const TSender* p_sender, void* p_parameter)
    {
        assert(p_sender != NULL);
        (m_ptr2Object->*m_ptr2Func)(p_sender, p_parameter);
    }

    virtual void Call(const TSender* p_sender, void* p_parameter)
    {
        assert(p_sender != NULL);
        (m_ptr2Object->*m_ptr2Func)(p_sender, p_parameter);
    }
};

Event.h

template<class TSender>
class Event
{
    list< BaseDelegate<TSender>* > m_observers;
    void Register( const BaseDelegate<TSender>* p_handler);
    void Unregister( const BaseDelegate<TSender>* p_handler);
public:
    void operator += ( const BaseDelegate<TSender>* p_handler);
    void operator -= ( const BaseDelegate<TSender>* p_handler);
    void operator () ( const TSender* p_sender, void* p_parameter);
    void Call( const TSender* p_sender, void* p_parameter);
    ~Event();
};

template<class TSender>
void Event<TSender>::operator += (const BaseDelegate<TSender>* p_handler)
{
    Register(p_handler);
}

template<class TSender>
void Event<TSender>::operator -= (const BaseDelegate<TSender>* p_handler)
{
    Unregister(p_handler);
}

template<class TSender>
void Event<TSender>::operator ()(const TSender* p_sender, void* p_parameter)
{
    Call(p_sender, p_parameter);
}

template<class TSender>
void Event<TSender>::Register(const BaseDelegate<TSender>* p_handler)
{
    assert(p_handler != NULL);

    for(list< BaseDelegate<TSender>* >::iterator itr = m_observers.begin();
       itr != m_observers.end();
       itr++)
    {
        if((*itr)->Equals(p_handler))
            return;
    }

    m_observers.push_back(const_cast<BaseDelegate<TSender>*>(p_handler));
}

template<class TSender>
void Event<TSender>::Unregister(const BaseDelegate<TSender>* p_handler)
{
    assert(p_handler != NULL);

    vector< BaseDelegate<TSender>* >::iterator where;
    for(list< BaseDelegate<TSender>* >::iterator itr = m_observers.begin();
        itr != m_observers.end();
        itr++)
    {
        if((*itr)->Equals(p_handler))
        {
            where = itr;
            break;
        }
    }

    m_observers.erase(where);
}


template<class TSender>
void Event<TSender>::Call(const TSender* p_sender, void* p_parameter)
{
    for(list< BaseDelegate<TSender>* >::iterator itr = m_observers.begin();
        itr != m_observers.end();
        itr++)
    {
        (*itr)->Call(p_sender, p_parameter);
    }
}

template<class TSender>
Event<TSender>::~Event()
{
    for(list< BaseDelegate<TSender>* >::iterator itr = m_observers.begin();
        itr != m_observers.end();
        itr++)
    {
        assert(*itr != NULL);
        delete (*itr);
    }
}

I leave you with the code, feel it and test it. If you detect any bug please don’t hesitate to fire it here. Any questions are very welcomed.

Advertisements

10 Responses to “Events and Delegates: A C++ Implementation”

  1. ZiKaS Says:

    It’s just awesome! (Y) good job

  2. 5olio Says:

    ايه يا عم الحلاوة دى :D:D

  3. ahmadmansour7 Says:

    I Liked it very much!, Great work (Y)

  4. MHesham Says:

    Thanks guyz for this support 😀
    This is just a new era for events in C++!

  5. Mohamed AbdElMonem Says:

    ket3a mn el ganna :D, Good Job (Y)

  6. Anas A. Ismail Says:

    ana kol marra a-read fiha el beta3 da .. btkayf fa7t ba2a : ), Simply Prince.

  7. ofirzeitoun Says:

    Very nice implementation, clean and simple. I like it…
    here you can attach small UML
    http://yuml.me/edit/aff57c96
    or
    http://yuml.me/diagram/scruffy;dir:TB;/class/edit/%5BBaseDelegate%5D^-[Delegate], [Event]++1-*>[BaseDelegate]

  8. Decept Says:

    Very nice! Thank you, you saved my day/year!

  9. Generic Delegates: C++ Implementation | Anas Awad's T-Blog Says:

    […] Simple Delegates Implementation – Geek’s Blog. […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s