Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ event system design [closed]

I need to use an event system in C++. I have mainly four requirements :

  1. Speed
  2. Ease of use
  3. Type Safety
  4. Friendly Destruction

By "Friendly Destruction" I mean the event and the subscriber need to manage their disconnection when one of them get destroyed :

  • An event shouldn't call a subscriber that has been destroyed.
  • A subscriber shouldn't be able to unregister from an event that has been destroyed.

I wonder if anyone would have a nicer design than what I came up with :

User point of view :

struct ClickEventArg {};

//-------------------------------------------------------------------------
class Button
{
public:
    Event<ClickEventArg&> Clicked;

    void SendClick(ClickEventArg& arg)
    {
        Clicked(this, arg);
    }
};

//-------------------------------------------------------------------------
class User
{
public:
    void Register(Button* button)
    {
        button->Clicked.Subscribe(m_suscriber, this, &User::OnButtonClicked);
    }

    void OnButtonClicked(void* sender, ClickEventArg& arg)
    {
        std::cout << "button clicked";
    }

private:
    EventSubscriber m_suscriber;
};

//-------------------------------------------------------------------------
void Test()
{
    Button* button = new Button();
    User* user = new User();
    user->Register(button);
    button->SendClick(ClickEventArg());
    delete user;
    button->SendClick(ClickEventArg()); 
}

Internal code :

class EventSubscriber;

//-------------------------------------------------------------------------
class BaseEvent
{
public:
    virtual void UnSubscribe(EventSubscriber& subscriber) = 0;
};

//-------------------------------------------------------------------------
template <class TEventArgs>
class Event : public BaseEvent 
{
public:
    ~Event()
    {
        UnSubscribeAll();
    }

    typedef fastdelegate::FastDelegate2<void*, TEventArgs> EventHandler;

    inline void operator() (void* sender, TEventArgs args) const 
    {
        for (SubscriberMap::const_iterator it = m_subscribers.begin(); it != m_subscribers.end(); ++it)
            it->second(sender, args);
    }

    template <class TClass, class TEventArgs>
    void Subscribe(EventSubscriber& subscriber, TClass* object, void (TClass::*methodAddress)(void* sender, TEventArgs e))
    {
        subscriber.Subscribe(this);
        m_subscribers[&subscriber] = fastdelegate::MakeDelegate(object, methodAddress);
    }

    void UnSubscribe(EventSubscriber& subscriber)
    {
        subscriber.UnSubscribe(this);
        m_subscribers.erase(&subscriber);
    }

    void UnSubscribeAll()
    {
        for (SubscriberMap::const_iterator it = m_subscribers.begin(); it != m_subscribers.end(); ++it)
            it->first->UnSubscribe(this);
        m_subscribers.clear();
    }

private:
    typedef std::map<EventSubscriber*, EventHandler> SubscriberMap;
    SubscriberMap m_subscribers;
};

//-------------------------------------------------------------------------
class EventSubscriber
{
    template <class TEventArgs> friend class Event;

public:
    ~EventSubscriber()
    {
        UnSubscribeAll();
    }

private:
    void Subscribe(BaseEvent* e)
    {
        m_subscriptions.insert(e);
    }

    void UnSubscribe(BaseEvent* e) 
    {
        m_subscriptions.erase(e);
    }

    void UnSubscribeAll()
    {
        EventSet copy(m_subscriptions);
        for (EventSet::iterator it = copy.begin(); it != copy.end(); ++it)
        {
            (*it)->UnSubscribe(*this);
        }
    }

    typedef std::set<BaseEvent*> EventSet;
    EventSet m_subscriptions;
};

You may notice I used the fast delegates from this article http://www.codeproject.com/KB/cpp/FastDelegate.aspx.

The EventSubscriber class is needed for the "Friendly Destruction" aspect. (Otherwise I would have used a prettier design like in C#)

Any feedback is welcome.

Thanks.

like image 737
noon Avatar asked Dec 22 '22 21:12

noon


1 Answers

Use boost::signals or boost::signals2 if you want thread safety.

like image 53
Alexandre C. Avatar answered Dec 24 '22 12:12

Alexandre C.