I would like to create a wrapper class for boost :: signals2 for modules (threads) that emit signals into slots. That is, the module should get typical simple signaling capabilities (for example, the public connect (...) method), inheriting from my Signal class. I would also like to hide the used implementation of the signal slot.
A particular slot is inherited from the common base class Slot, which has a template parameter that defines its signature. A slot is just a functor with a suitable signature.
This question is somewhat related to this . Slots are stored as shared_ptr, and lifespan control is required. That is, the Signal class must contain a reference to the slot in order to maintain it in memory until the signal itself exits. Therefore, I cannot connect std :: functions or the like. I need to connect shared_ptrs to the base class of the slot.
My current approach, no thread safety (MSVC 2010):
template<class FunSig> class Slot; template<class R> class Slot<R()> { public: typedef R Ret_type; public: virtual ~Slot() {} virtual Ret_type operator()() = 0; }; template<class R, class A1> class Slot<R(A1)> { public: typedef R Ret_type; typedef A1 Arg1_type; public: virtual ~Slot() {} virtual Ret_type operator()(Arg1_type) = 0; }; // and so forth for more arguments /* Signalling class. This class is basically a wrapper for the boost::signals2 class with lifetime management for slots. Slots are connected by a shared_ptr which gets stored in a std::vector to ensure that a slot exists at least as long as the signal. */ template<class FunSig> class Signal { public: typedef Slot<FunSig> Slot_type; typedef boost::signals2::signal<FunSig> BoostSignal; typedef typename BoostSignal::slot_type BoostSlot; public: virtual ~Signal() {} void connectSlot(std::shared_ptr<Slot_type> slot_ptr); protected: //void emitSignal( ... ); //void disconnectAllSlots(); private: BoostSignal sig_; /// vector of shared_ptr to slots for lifetime management std::vector<std::shared_ptr<Slot_type> > slotsVec_; }; template<class FunSig> void Signal<FunSig>::connectSlot(std::shared_ptr<Slot_type> slot_ptr) { sig_.connect(*slot_ptr); // version A: compiler error // OR sig_.connect(boost::ref(*slot_ptr)); // version B: warning, but compiles and runs // add slot pointer to vector of slots slotsVec_.push_back(slot_ptr); }
This code (version A) does not compile. It breaks inside boosts_template.hpp and on the line marked in the connectSlot method:
error C2679: binary '=' : no operator found which takes a right-hand operand of type 'const Slot<FunSig>' (or there is no acceptable conversion) 1> with 1> [ 1> FunSig=void (const float &)
Interestingly, this code compiles and runs if version B is used instead - that is, boost :: ref is passed. Although there is a warning about the compiler "Functional call with parameters that may be unsafe - this call depends on the caller to verify the correctness of the values โโpassed." in boost singals2 auto_buffer.hpp.
So what is the real problem here and how to solve it? Why does this work with boost :: ref and why does it not compile without it?
I'm not even sure that the whole idea of โโdesign is useful. The initial idea was to hide all the signaling / slot stuff in the superclass and focus on the signature (and enable lifecycle management).
Additional question regarding boost2 signals: the singals2 connect () method refers to a slot. How is it done inside. Does he use a link to a connected slot or does she make a copy of the slot? This is important as my slots handle dynamically allocated memory.