Jump to content

  • Log In with Google      Sign In   
  • Create Account






Varadic templates and signals/slots

Posted by Aardvajk, 03 June 2014 · 71 views

Been playing around with a typesafe signal/slot system (yes another one) using varadic templates tonight. Its so much cleaner than the existing approaches pre-C++0x. It's a real demonstration of the usefulness of this awesome new feature:
#include <iostream>
#include <vector>
#include <string>

template<class... Args> class Signal;

template<class... Args> class AbstractSlot
{
public:
    virtual ~AbstractSlot();

    virtual void add(Signal<Args...> *s){ v.push_back(s); }
    virtual void remove(Signal<Args...> *s){ auto i = v.begin(); while(i != v.end()){ if(*i == s) i = v.erase(i); else ++i; } }

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

private:
    std::vector<Signal<Args...>*> v;
};

template<class T, class... Args> class Slot : public AbstractSlot<Args...>
{
public:
    Slot(T *t, void(T::*f)(Args...)) : t(t), f(f) { }

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

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

template<class... Args> class Signal
{
public:
    ~Signal(){ for(auto i = v.begin(); i != v.end(); ++i) (*i)->remove(this); }

    void connect(AbstractSlot<Args...> &s){ v.push_back(&s); s.add(this); }
    void disconnect(AbstractSlot<Args...> *s){ auto i = v.begin(); while(i != v.end()){ if(*i == s) i = v.erase(i); else ++i; } }

    void operator()(Args... args){ for(auto i = v.begin(); i != v.end(); ++i) (*i)->call(args...); }

private:
    std::vector<AbstractSlot<Args...>*> v;
};

template<class... Args> AbstractSlot<Args...>::~AbstractSlot()
{
    for(auto i = v.begin(); i != v.end(); ++i) (*i)->disconnect(this);
}
Some usage
class Thing
{
public:
    Thing(const std::string &name) : name(name) { }

    void f1(){ std::cout << name << " f1\n"; }
    void f2(int a){ std::cout << name << " f2 " << a << "\n"; }
    void f3(float f, char c){ std::cout << name << " f3 " << f << " " << c << "\n"; }

    std::string name;
};

int main()
{
    Thing f("One");
    Thing g("Two");

    Slot<Thing, int> s1(&f, &Thing::f2);
    Slot<Thing, int> s2(&g, &Thing::f2);

    Signal<int> x1;

    x1.connect(s1);
    x1.connect(s2);

    Signal<float, char> x2;

    Slot<Thing, float, char> s4(&f, &Thing::f3);
    x2.connect(s4);

    x2(123.4f, 'x');

    if(true)
    {
        Thing h("Three");
        Slot<Thing, int> s3(&h, &Thing::f2);

        x1.connect(s3);

        x1(100);

        std::cout << "END OF SCOPE\n";
    }

    x1(230375);
}

You can even do this:
    Signal<> x3;
    Slot<Thing> s5(&f, &Thing::f1);
    x3.connect(s5);

    x3();
So you have a completely uniform way of defining and calling things, without any of the hackery we used to have to rely on for this stuff.

Excellent. Jolly good.

EDIT:

Also, it seems if you make a non-type-dependant base class for Slot, you can do this:
class Slots
{
public:
    Slots(){ }
    ~Slots(){ for(auto i = v.begin(); i != v.end(); ++i) delete *i; }

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

private:
    std::vector<BaseSlot*> v;
};
Then rather than have a separate object for each slot in the owning class, you can just do:
class SomeProvider
{
public:
    Signal<int, const std::string&> signal;
    Signal<char> other;
};

class Thing
{
public:
    Thing(SomeProvider &provider);

private:
    void f(int i, const std::string &s){ }
    void g(char c){ }

    Slots slots;
};

Thing::Thing(SomeProvider &provider)
{
    slots.connect(this, &Thing::f, provider.signal);
    slots.connect(this, &Thing::g, provider.other);
}

So you only have one object, and the compiler (GCC in my case) appears to be able to deduce all of the types in the statement so no need for any template specification at the point of use, which is nice.

(I'm aware I need to implement the rule of three for these classes by the way, just an example).




August 2014 »

S M T W T F S
     1 2
3456789
10111213141516
17181920212223
24252627282930
31      

Recent Comments

Recent Comments

PARTNERS