Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.






Varadic templates and signals/slots

Posted by Aardvajk, 03 June 2014 · 179 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).




December 2014 »

S M T W T F S
 123456
78910111213
14151617 18 1920
21222324252627
28293031   

Recent Entries

Recent Comments

Recent Entries

Recent Comments

PARTNERS