Jump to content

  • Log In with Google      Sign In   
  • Create Account





C++11 variable args applied to signals/slots system

Posted by Aardvajk, 31 July 2014 · 605 views

Quick snippetty post today. Been looking at making a clean signals/slots system using varadic templates. This is only tested with GCC 4.7.2
#ifndef DELEGATE_H
#define DELEGATE_H

#include <vector>
#include <algorithm>

template<class... Args> class Event;

class BaseDelegate
{
public:
    virtual ~BaseDelegate(){ }
};

template<class... Args> class AbstractDelegate : public BaseDelegate
{
protected:
    virtual ~AbstractDelegate();

    friend class Event<Args...>;

    virtual void add(Event<Args...> *s){ v.push_back(s); }
    virtual void remove(Event<Args...> *s){ v.erase(std::remove(v.begin(), v.end(), s), v.end()); }

    virtual void call(Args... args) = 0;

    std::vector<Event<Args...>*> v;
};

template<class T, class... Args> class ConcreteDelegate : public AbstractDelegate<Args...>
{
public:
    ConcreteDelegate(T *t, void(T::*f)(Args...), Event<Args...> &s);

private:
    ConcreteDelegate(const ConcreteDelegate&);
    void operator=(const ConcreteDelegate&);

    friend class Event<Args...>;

    virtual void call(Args... args){ (t->*f)(args...); }

    T *t;
    void(T::*f)(Args...);
};

template<class... Args> class Event
{
public:
    Event(){ }
    ~Event(){ for(auto i: v) i->remove(this); }

    void connect(AbstractDelegate<Args...> &s){ v.push_back(&s); s.add(this); }
    void disconnect(AbstractDelegate<Args...> &s){ v.erase(std::remove(v.begin(), v.end(), &s), v.end()); }

    void operator()(Args... args){ for(auto i: v) i->call(args...); }

private:
    Event(const Event&);
    void operator=(const Event&);

    std::vector<AbstractDelegate<Args...>*> v;
};

template<class... Args> AbstractDelegate<Args...>::~AbstractDelegate()
{
    for(auto i : v) i->disconnect(*this);
}

template<class T, class... Args> ConcreteDelegate<T, Args...>::ConcreteDelegate(T *t, void(T::*f)(Args...), Event<Args...> &s) : t(t), f(f)
{
    s.connect(*this);
}

class Delegate
{
public:
    Delegate(){ }
    ~Delegate(){ for(auto i: v) delete i; }

    template<class T, class... Args> void connect(T *t, void(T::*f)(Args...), Event<Args...> &s){ v.push_back(new ConcreteDelegate<T, Args...>(t, f, s)); }

private:
    Delegate(const Delegate&);
    void operator=(const Delegate&);

    std::vector<BaseDelegate*> v;
};

#endif // DELEGATE_H

Everything is non-copyable, can't quite work out sensible copying strategies for events or slots.

I haven't used Signal and Slot terminology because I use QtCreator and it highlights words like "slots" as they are sort of Qt defined keywords which was annoying.

Some usage:
class SomeObject
{
public:
   Event<int, float> someEvent;

   void f(){ someEvent(23, 45.0f); }
};

class AnotherObject
{
public:
    AnotherObject(SomeObject *o);

private:
    void handle(int i, float f){ /* ... */ }

    Delegate delegate;
};

AnotherObject::AnotherObject(SomeObject *o)
{
    delegate.connect(this, &AnotherObject::handle, o->someEvent);
}

So defining an event in an emitter is just a case of adding a an Event<> object to its public interface. You can then call it using function syntax.

A listener just needs a Delegate object and can then connect events to any member function using the standard address-of syntax. This is compile-time type safe and will only allow connection to a member function with the correct parameter types.

The destructors of the Event and Delegate classes take care of doing auto-disconnects when the owning objects get destroyed.

A nice example of how varadic templates are taking traditionally complex and ugly code and making it almost trivial. I also like the way that you don't have to use any template syntax in the actual usage, apart from when you define the Event itself.

Thanks for reading.

If anyone wants to see an article on this, explaining step by step how it works, just let me know. Not something I'd do without some interest first but happy to contribute if anyone wants it.




I think an article would be great - you will reach many more people if you have a more distilled description of the system.  Nice work!

Thanks Jason, will look at throwing something together.

Looks like you're already on it, but I would also love to see an article on this! :)

Cheers jb. Stretching the truth a bit if I claimed to be "on it" but will try to make time. Nothing like writing an article to make you discover you don't understand something as well as you think you do :)

October 2014 »

S M T W T F S
   1234
567891011
12131415161718
192021 22 232425
262728293031 

Recent Entries

Recent Comments

Recent Entries

Recent Comments

PARTNERS