Wrapper for boost :: signals2 with lifetime control for shared slots

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.

0
source share
2 answers

This question has been answered in a different context here .

In fact, std :: ref or boost :: ref should be used because the boost :: signals2 connect method copies its arguments. But the class slot cannot be copied, since it is an abstract class. Therefore, using boost :: ref is the recommended solution as it makes the slot available.

0
source

I assume you mean warning C4996. This is a feature of the Microsoft C ++ standard library implementation that warns you whenever the standard algorithm compiles with potentially dangerous arguments, for example. in this fragment, the caller is responsible for the sufficient sizes of source and target :

 int source[3] = { 1, 2, 3 }; int target[3]; std::copy(source, source + 3, target); 

You should not duplicate the functionality already provided by Boost.Signals2: the library provides sophisticated time management of the โ€œslotsโ€ associated with the signal. Read the documents , they foresee a very fine control of this aspect. In addition, you lose a very interesting function - thread safety Boost.Signals2: you need to manage thread safe inserts and deletions to and from slotVec_ yourself, which is a nontrivial undertaking ...

I am working on a similar abstraction library called vex , see abstract_multicast_delegate and abstract_signal in vex/functional/contracts . An implementation based on signals2::signal can be found in vex/functional/implementation/bs2_signal.h . This, however, is still a playground, I am also experimenting with alternative implementations ...

EDIT: Sorry, I don't know how to point to the top of the hg repository in codeplex. I had to remove the links as a lot has changed since yesterday ...

+1
source

All Articles