Jump to content
  • Advertisement
  • entries
  • comments
  • views

Varadic templates and signals/slots

Sign in to follow this  


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 #include #include template class Signal;template class AbstractSlot{public: virtual ~AbstractSlot(); virtual void add(Signal *s){ v.push_back(s); } virtual void remove(Signal *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*> v;};template class Slot : public AbstractSlot{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 Signal{public: ~Signal(){ for(auto i = v.begin(); i != v.end(); ++i) (*i)->remove(this); } void connect(AbstractSlot &s){ v.push_back(&s); s.add(this); } void disconnect(AbstractSlot *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*> v;};template AbstractSlot::~AbstractSlot(){ for(auto i = v.begin(); i != v.end(); ++i) (*i)->disconnect(this);}
Some usageclass 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 s1(&f, &Thing::f2); Slot s2(&g, &Thing::f2); Signal x1; x1.connect(s1); x1.connect(s2); Signal x2; Slot s4(&f, &Thing::f3); x2.connect(s4); x2(123.4f, 'x'); if(true) { Thing h("Three"); Slot 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 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.


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 void connect(T *t, void(T::*f)(Args...), Signal &s){ v.push_back(new Slot(t, f, s)); }private: std::vector v;};
Then rather than have a separate object for each slot in the owning class, you can just do:class SomeProvider{public: Signal signal; Signal 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).
Sign in to follow this  


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!