I am working on a project, mainly to wipe out crashes. This project has a concept of signalmanager, that stores signals’ pointers, so that signals can be stored locally. Slots are connected through this management class by name.
The idea is good, it avoids the direct connection between a signal and a slot. however, the design and implement is uncompleted, the signalmanger connects a slot to signal directly when a slot tries to connect.It requires to disconnect a connection before slots disappear. the management class requires a long parameter list when registrating a signal, de-registrating a signal, connecting a slot, disconnecting a slot.
It is implemented based on boost::signals2, add a map to store these signals. Let’s step back to think what is a signal-slot. It is a collection of optional callbacks, which can be described in std::function.
So we have known the limitations, and here is the our new target.
- hide the signal location from slots, the slots no need to care where is the signal.
- simplify the life span control, should allow auto break the connection whenever a signal is gone or a slot is gone.
- simplify the registration/connection.
- use std namespace only. the original one was implemented with boost.
The solution comes with following features:
1. no extra dependency, only std namespace.
2. provides two versions, the namedsigslot version supports
to store signals locally, slots can be connected by signal name.
3. support bidirection disconnection. you can either reset a connection to stop receiving further events,or disconnect explicitly from a sginal
4. supports bidirection enable/disable. that means you can keep a signal or a connection but disable event dispatching
5. header only, each implement is self-contained.
6. an individual connection is stored in a shared_ptr.
7. provides a container class(‘connections’) to hold all connected connections’ shared_ptr. say you may established many connections from ClassA, and you can instanlize a container in classA as a member variable, it will take care all connections.
8. Allow to overwrite the mutex type, by default it is std::recursive_mutex, but you can change it to std::mutex or even a dummy mutex, that is required to implement lock/unlock.
We have a few concepts here:
- slot: a std::function is a slot
- connectionbase: a base class for management, represents for a connection
- connection: a templated version of connection, based on connectionbase. it holds the real connection.
- signalbase: a base class for management, represents for a signal
- signal: a templated version of signal, based on signalbase. it holds the connections.
- connections: it is a collection of all connected connections, used to hold all connections(because our connections are stored in shared_ptr) established from a class(or other certain scope)
- [only in named version]signals: a collection of all signals, used to create new signals, as well as to create new connections.