Signals and slots are a thread-safe and flexible communication framework that will allow various objects to transfer data synchronously or asynchronously in a highly decoupled manner. The classes that are using signals and slots do not need to share any interfaces to each other of each other. Signals and slots are also highly resilient against short lived objects: if a slot is deleted, the connection will detach automatically even while a CYISignal::Emit() is in progress, in which the deletion of the slot will be delayed.
Here is an example of how to use the CYISignal and CYISignalHandler to mark a class:
Connecting to a signal
As a general rule, signals can be connected to any Callable slot, as defined by the C++ standard. The following is a partial list of the types that signals can be connected to:
The following code example shows how to connect to different slot types.
Connection types
Connect() functions takes as last input an optional connection type. By default, the connection type is YI_CONNECTION_AUTO. Automatic connection means that when CYISignal::Emit() is called, the CYISignal will look at the slot's thread affinity to determine if it should call it directly (synchronously), or if it should be queued on the slot's thread (asynchronously) if a CYIEventDispatcher is running on that given thread. The slot affinity is determined by the thread that instantiated the CYISignalHandler. Please note that the CYISignalHandler can also be moved to a different thread after instantiation using CYISignalHandler::MoveToThread. You can also force the connection to be always synchronous by setting the connection type to YI_CONNECTION_SYNC. In this case, it will always call the slot using the CYISignal's current context, but it is your responsibility to assure thread-safety in your slot's implementation. You can also force the connection to always be asynchronous by setting the connection type to YI_CONNECTION_ASYNC. This connection type will always queue the CYISignal onto the CYIEventDispatcher of the context in which the CYISignalHandler resides. If there are no event dispatcher running on the given context, the application event dispatcher will be used. The last optional connection type is YI_CONNECTION_ASYNC_BLOCK. With this connection type, the CYISignal call will still be asynchronous and will be queued on the slots's thread, but will be blocked until all the slots return. It is an useful connection type that assures that the slot are called by the correct thread while retaining a synchronous behaviour on the CYISignal's caller.
It is highly recommended to leave the connection type as YI_CONNECTION_AUTO unless the other connection types are absolutely necessary. In the automatic mode, the context switching will automatically occur only when it is necessary, and is completely transparent to the user.
Here is an code example on how to make signals asynchronous using a CYIEventDispatcherThread. In this example, the connection type is YI_CONNECTION_AUTO but the effective connection type is YI_CONNECTION_ASYNC because of the different thread affinity.
Checking for existing connections
The IsConnected functions can be used to detect if a signal is connected to a given slot. The function CYISignalBase::IsConnected(const CYISignalConnectionID &) function can be used for any slot type, but the IsConnected functions that take as input slots can only be used with raw function slots.
The following gives an example of how to check for existing connections;
Disconnecting signals and slots
Signals and Slots are both threadsafe and deletion safe. This means that if a handler object is deleted while it is connected to a signal, the connections between the signal and the handler object are automatically severed in a thread-safe manner. Similarly, deleting a signal automatically disconnects that signal from any currently-connected handler object in a thread-safe manner.
In some cases, it is desirable to manually disconnect a slot from its associated signal.
For 'raw' function connections, this can be done using the Disconnect() functions (passing in the function pointer to the slot, as well as the signal handler if necessary).
For all other slot connections (such as connections to lambdas or to std::function objects), the CYISignalConnectionID object must be used. This object is returned when the connection is established, and must be retained by the user.
Here is an example of how connections can be disconnected:
Copying signals
When a signal is copied, all of its slot connections are copied as well. This should be kept in mind when copying objects that have signal members.
Similarily, copying a signal handler also copies all of its signal connections.
Connecting to std::function slots
std::function objects can be connected to from signal objects. As with 'raw' function pointers, connections to std::function objects can be made even if the std::function objects takes less parameters as input than the signal emits. Parameters of std::function objects only need to be convertible to the types of the signal – an exact match is not necessary.
Unlike connections to 'raw' slots, connections to std::function objects can only be disconnected by hanging onto the CYISignalConnectionID object that is returned at connection time. This is because std::function cannot be compared for equality.
Three different Connect types exist for std::function objects:
When using one of the first two Connect types, the signal connection is automatically disconnected when the signal handler is deleted. With the third Connect type, the signal connection is disconnected only when the signal itself is deleted (or when the user manually disconnects from the signal using a CYISignalConnectionID object).
Connecting lambdas, std::bind'ed functions, and generic Callable objects
Lambdas, functors and std::bind'ed functions can all be connected to through an std::function object. With the exception of std::bind'ed functions, connections can be established directly without using an intermediate std::function object. The same limitations exist for lambdas and std::bind'ed functions as for std::function: the same slot can be connected to multiple times, and disconnection can only be done using the CYISignalConnectionID.
Follows is an example of how to 'safely' capture 'this' in a lambda:
#include <signal/YiSignal.h>

Public Member Functions | |
| template<typename HandlerType , typename SlotReturnType , typename... SlotTypes> | |
| bool | IsConnected (const HandlerType &rSignalHandler, SlotReturnType(HandlerType::*const pSlot)(SlotTypes...)) const |
| template<typename HandlerType , typename SlotReturnType , typename... SlotTypes> | |
| bool | IsConnected (const HandlerType &rSignalHandler, SlotReturnType(HandlerType::*const pSlot)(SlotTypes...) const) const |
| template<typename SlotReturnType , typename... SlotTypes> | |
| bool | IsConnected (SlotReturnType(*const pSlot)(SlotTypes...)) const |
| template<typename HandlerType , typename SlotHandlerType , typename SlotReturnType , typename... SlotTypes> | |
| CYISignalConnectionID | Connect (HandlerType &rSignalHandler, SlotReturnType(SlotHandlerType::*const pSlot)(SlotTypes...), YI_CONNECTION_TYPE type=YI_CONNECTION_AUTO) |
| template<typename HandlerType , typename SlotHandlerType , typename SlotReturnType , typename... SlotTypes> | |
| CYISignalConnectionID | Connect (const HandlerType &rSignalHandler, SlotReturnType(SlotHandlerType::*const pSlot)(SlotTypes...) const, YI_CONNECTION_TYPE type=YI_CONNECTION_AUTO) |
| template<typename SlotReturnType , typename... SlotTypes> | |
| CYISignalConnectionID | Connect (SlotReturnType(*const pSlot)(SlotTypes...), YI_CONNECTION_TYPE type=YI_CONNECTION_AUTO) |
| template<typename HandlerType , typename SlotHandlerType , typename SlotReturnType , typename... SlotTypes> | |
| CYISignalConnectionID | Connect (HandlerType &rSignalHandler, const std::function< SlotReturnType(SlotHandlerType &, SlotTypes...)> &slot, YI_CONNECTION_TYPE type=YI_CONNECTION_AUTO) |
| template<typename CallableType > | |
| CYISignalConnectionID | Connect (const CallableType &callable, YI_CONNECTION_TYPE type=YI_CONNECTION_AUTO) |
| template<typename CallableType > | |
| CYISignalConnectionID | Connect (const CYISignalHandler &rSignalHandler, const CallableType &callable, YI_CONNECTION_TYPE type=YI_CONNECTION_AUTO) |
| template<typename... OtherSignalTypes> | |
| CYISignalConnectionID | Connect (CYISignal< OtherSignalTypes... > &slotSignal, YI_CONNECTION_TYPE type=YI_CONNECTION_AUTO) |
| void | operator() (const typename std::decay< SignalTypes >::type &...params) const |
| void | Emit (const typename std::decay< SignalTypes >::type &...params) const |
| template<class SlotReturnType , typename SlotHandlerType , typename... SlotTypes> | |
| void | Disconnect (CYISignalHandler &rSignalHandler, SlotReturnType(SlotHandlerType::*const pSlot)(SlotTypes...)) |
| template<class SlotReturnType , typename SlotHandlerType , typename... SlotTypes> | |
| void | Disconnect (CYISignalHandler &rSignalHandler, SlotReturnType(SlotHandlerType::*const pSlot)(SlotTypes...) const) |
| template<typename SlotReturnType , typename... SlotTypes> | |
| void | Disconnect (SlotReturnType(*const pSlot)(SlotTypes...)) |
Public Member Functions inherited from CYISignalBase | |
| virtual bool | IsConnected () const override |
| virtual bool | IsConnected (const CYISignalBase &rSignal) const override |
| bool | IsConnected (const CYISignalHandler &rSignalHandler) const |
| bool | IsConnected (const CYISignalConnectionID &connectionID) const |
| void | Disconnect (CYISignalHandler &rSignalHandler) |
| void | Disconnect (const CYISignalConnectionID &connectionID) |
| void | Disconnect (CYISignalBase &rSignal) |
Public Member Functions inherited from CYISignalHandler | |
| CYISignalHandler () | |
| CYISignalHandler (const CYISignalHandler &rSignalHandler) | |
| virtual | ~CYISignalHandler () |
| CYISignalHandler & | operator= (const CYISignalHandler &rSignalHandler) |
| void | MoveToThread (CYIThread *pThread) |
| This function allows the user to override the default thread affinity to any CYIThread that may or may not be running. More... | |
| CYIThreadHandle | GetThreadAffinity () const |
| void | SetThreadAffinity (const CYIThreadHandle &rThreadAffinity) |
| void | Disconnect (CYISignalBase &rSignal) |
| void | DisconnectFromAllSignals () |
Public Member Functions inherited from CYIThread::Listener | |
| Listener () | |
| virtual | ~Listener () |
| virtual void | OnThreadStarted (CYIThread *) |
| virtual void | OnThreadTerminated (CYIThread *) |
| virtual void | OnThreadFinished (CYIThread *) |
Additional Inherited Members | |
Protected Member Functions inherited from CYISignalBase | |
| CYISignalBase () | |
| CYISignalBase (const CYISignalBase &rSignal) | |
| virtual | ~CYISignalBase () |
| CYISignalBase & | operator= (const CYISignalBase &rSignal) |
| void | RemoveConnection (CYISignalHandler &rSignalHandler, YI_NOTIFY_FLAG notifyHandler) |
| void | RemoveAllConnections (YI_NOTIFY_FLAG notifyHandler) |
| void | RegisterToSignalHandler (const CYISignalHandler &rSignalHandler) |
| void | UnregisterFromSignalHandler (CYISignalHandler &rSignalHandler) |
| void | ExclusiveLock (CYIRecursiveMutex &signalMutex) const |
| void | ExclusiveLock (const CYISignalHandler &rSignalHandler, CYIRecursiveMutex &signalMutex) const |
| void | ExclusiveUnlock (CYIRecursiveMutex &signalMutex) const |
| void | ExclusiveUnlock (const CYISignalHandler &rSignalHandler, CYIRecursiveMutex &rSignalMutex) const |
| bool | HasConnection (const CYISignalHandler &rSignalHandler) const |
| template<class YI_SIGNAL_EMIT_EVENT > | |
| void | EmitAsync (const CYISignalAbstractConnection *pConnection, YI_CONNECTION_TYPE connectionType, std::unique_ptr< YI_SIGNAL_EMIT_EVENT > pEvent, const std::shared_ptr< CYIEventDispatcher > &pDispatcher) |
Protected Attributes inherited from CYISignalBase | |
| CYILazy< SignalObjects > | m_signalObjects |
| CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | HandlerType & | rSignalHandler, |
| SlotReturnType(SlotHandlerType::*)(SlotTypes...) | pSlot, | ||
| YI_CONNECTION_TYPE | type = YI_CONNECTION_AUTO |
||
| ) |
Establish a connection between this CYISignal and the member function slot pSlot using the signal handler rSignalHandler.
Connections are unique when established using this function. Calling this function a second time using the same slot has no effect.
Note that you can always connect a CYISignal to slots that have fewer parameters than the signal, but not the other way around. When the CYISignal is emitted, the unused parameters will simply be ignored. The parameters of the slot must be convertible to from the signal parameter types. The slot may have any return type. Reference and pointer parameters are allowed.
If the CYISignalHandler goes out-of-scope or is deleted, the connection will be automatically destroyed. You do not need to manually call CYISignal::Disconnect when deleting any instance of a subclass of CYISignalHandler.
|
inline |
Establish a connection between this CYISignal and the const member function slot pSlot using the const signal handler rSignalHandler.
Connections are unique when established using this function. Calling this function a second time using the same slot has no effect.
Note that you can always connect a CYISignal to slots that have fewer parameters than the signal, but not the other way around. When the CYISignal is emitted, the unused parameters will simply be ignored. The parameters of the slot must be convertible to from the signal parameter types. The slot may have any return type. Reference and pointer parameters are allowed.
If the CYISignalHandler goes out-of-scope or is deleted, the connection will be automatically destroyed. You do not need to manually call CYISignal::Disconnect when deleting any instance of a subclass of CYISignalHandler.
| CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | SlotReturnType(*)(SlotTypes...) | pSlot, |
| YI_CONNECTION_TYPE | type = YI_CONNECTION_AUTO |
||
| ) |
Establish a connection between this CYISignal and the static function slot pSlot.
Connections are unique when established using this function. Calling this function a second time using the same slot has no effect.
Note that you can always connect a CYISignal to slots that have fewer parameters than the signal, but not the other way around. When the CYISignal is emitted, the unused parameters will simply be ignored. The parameters of the slot must be convertible to from the signal parameter types. The slot may have any return type. Reference and pointer parameters are allowed.
| CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | HandlerType & | rSignalHandler, |
| const std::function< SlotReturnType(SlotHandlerType &, SlotTypes...)> & | slot, | ||
| YI_CONNECTION_TYPE | type = YI_CONNECTION_AUTO |
||
| ) |
Establish a connection between this CYISignal and the std::function slot slot using the signal handler rSignalHandler. Connecting to const member functions is also supported so long as a const signal handler is provided.
Because std::function objects cannot be compared, the same std::function can be connected to multiple times. To disconnect an std::function connection, the CYISignalConnectionID object returned by this function must be used.
Note that you can always connect a CYISignal to slots that have fewer parameters than the signal, but not the other way around. When the CYISignal is emitted, the unused parameters will simply be ignored. The parameters of the slot must be convertible to from the signal parameter types. The slot may have any return type. Reference and pointer parameters are allowed.
If the CYISignalHandler goes out-of-scope or is deleted, the connection will be automatically destroyed. You do not need to manually call CYISignal::Disconnect when deleting any instance of a subclass of CYISignalHandler.
| CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | const CallableType & | callable, |
| YI_CONNECTION_TYPE | type = YI_CONNECTION_AUTO |
||
| ) |
Establish a connection between this CYISignal and the Callable slot slot. This is typically used to connect to lambda functions.
Because Callable objects cannot be compared, the same Callable can be connected to multiple times. To disconnect a Callable connection, the CYISignalConnectionID object returned by this function must be used.
Note that you can always connect a CYISignal to slots that have fewer parameters than the signal, but not the other way around. When the CYISignal is emitted, the unused parameters will simply be ignored. The parameters of the slot must be convertible to from the signal parameter types. The slot may have any return type. Reference and pointer parameters are allowed.
| CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | const CYISignalHandler & | rSignalHandler, |
| const CallableType & | callable, | ||
| YI_CONNECTION_TYPE | type = YI_CONNECTION_AUTO |
||
| ) |
Establish a connection between this CYISignal and the Callable slot slot, tying the lifetime of the connection to the lifetime of the rSignalHandler.
Because Callable objects cannot be compared, the same Callable can be connected to multiple times. To disconnect a Callable connection, the CYISignalConnectionID object returned by this function must be used.
Note that you can always connect a CYISignal to slots that have fewer parameters than the signal, but not the other way around. When the CYISignal is emitted, the unused parameters will simply be ignored. The parameters of the slot must be convertible to from the signal parameter types. The slot may have any return type. Reference and pointer parameters are allowed.
If the CYISignalHandler goes out-of-scope or is deleted, the connection will be automatically destroyed. You do not need to manually call CYISignal::Disconnect when deleting any instance of a subclass of CYISignalHandler.
| CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | CYISignal< OtherSignalTypes... > & | slotSignal, |
| YI_CONNECTION_TYPE | type = YI_CONNECTION_AUTO |
||
| ) |
Establish a daisy-chain connection between this CYISignal and the slot signal slotSignal.
Connections are unique when established using this function. Calling this function a second time using the same slot has no effect.
Note that you can always connect a CYISignal to slots that have fewer parameters than the signal, but not the other way around. When the CYISignal is emitted, the unused parameters will simply be ignored. The parameters of the slot must be convertible to from the signal parameter types. The slot may have any return type. Reference and pointer parameters are allowed.
If the slot signal goes out-of-scope, or is deleted in the event that it was allocated on the heap, the connection will be automatically destroyed. You do not need to manually call CYISignal::Disconnect when deleting any instance of CYISignal.
| void CYISignal< SignalTypes >::Disconnect | ( | CYISignalHandler & | rSignalHandler, |
| SlotReturnType(SlotHandlerType::*)(SlotTypes...) | pSlot | ||
| ) |
Destroy a signal/slot connection between this signal and the member function pSlot (using the signal handler rSignalHandler).
Once this function has been called, calling CYISignal::Emit() will not result in the disconnected slot receiving the signal, unless the emit is asynchronous and it has already been queued to the CYIEventDispatcher.
|
inline |
Destroy a signal/slot connection between this signal and the const member function pSlot (using the const signal handler rSignalHandler).
Once this function has been called, calling CYISignal::Emit() will not result in the disconnected slot receiving the signal, unless the emit is asynchronous and it has already been queued to the CYIEventDispatcher.
| void CYISignal< SignalTypes >::Disconnect | ( | SlotReturnType(*)(SlotTypes...) | pSlot | ) |
Destroy a signal/slot connection between this signal and the static function pSlot.
Once this function has been called, calling CYISignal::Emit() will not result in the disconnected slot receiving the signal, unless the emit is asynchronous and it has already been queued to the CYIEventDispatcher.
|
inline |
Emits the CYISignal with the given parameters params.
Calling this function in turn calls all slots connected to this signal.
| bool CYISignal< SignalTypes >::IsConnected | ( | const HandlerType & | rSignalHandler, |
| SlotReturnType(HandlerType::*)(SlotTypes...) | pSlot | ||
| ) | const |
Checks if a connection has been established between this CYISignal and the slot pSlot.
|
inline |
Checks if a connection has been established between this CYISignal and the const slot pSlot.
| bool CYISignal< SignalTypes >::IsConnected | ( | SlotReturnType(*)(SlotTypes...) | pSlot | ) | const |
Checks if a connection has been established between this CYISignal and the static function slot pSlot.
|
inline |
Emits the CYISignal with the given parameters params.
This function make it possible to use the Signal as a 'functor'. Calling this function in turn calls all slots connected to this signal.