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 EYIConnectionType::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. For connections without a signal handler, the thread affinity can be passed as an argument and, by default, is the thread that established the connection. You can also force the connection to be always synchronous by setting the connection type to EYIConnectionType::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 EYIConnectionType::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.
It is highly recommended to leave the connection type as EYIConnectionType::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 EYIConnectionType::Auto but the effective connection type is EYIConnectionType::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:
Disconnecting or Connecting during Emit
Disconnecting a slot from a signal while that signal is being emitted is supported. If the slot being disconnected has not been called yet, calling it will be skipped. Disconnecting the slot that is currently being called is also supported. However, deleting the object containing the slot that is currently being called is not supported.
Connecting to a signal while that signal is being emitted is supported. However be aware that the newly-connected slot will be called after the connection has been established (and before the signal's emit function call completes).
#include <signal/YiSignal.h>
Public Member Functions | |
virtual | ~CYISignal () |
template<typename HandlerType , typename SlotReturnType , typename... SlotTypes> | |
bool | IsConnected (const HandlerType &signalHandler, SlotReturnType(HandlerType::*const pSlot)(SlotTypes...)) const |
template<typename HandlerType , typename SlotReturnType , typename... SlotTypes> | |
bool | IsConnected (const HandlerType &signalHandler, 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 &signalHandler, SlotReturnType(SlotHandlerType::*const pSlot)(SlotTypes...), EYIConnectionType type=EYIConnectionType::Auto) |
template<typename HandlerType , typename SlotHandlerType , typename SlotReturnType , typename... SlotTypes> | |
CYISignalConnectionID | Connect (const HandlerType &signalHandler, SlotReturnType(SlotHandlerType::*const pSlot)(SlotTypes...) const, EYIConnectionType type=EYIConnectionType::Auto) |
template<typename SlotReturnType , typename... SlotTypes> | |
CYISignalConnectionID | Connect (SlotReturnType(*const pSlot)(SlotTypes...), EYIConnectionType type=EYIConnectionType::Auto, CYIThreadHandle threadAffinity=CYIThread::GetCurrentThreadId()) |
template<typename HandlerType , typename SlotHandlerType , typename SlotReturnType , typename... SlotTypes> | |
CYISignalConnectionID | Connect (HandlerType &signalHandler, std::function< SlotReturnType(SlotHandlerType &, SlotTypes...)> slot, EYIConnectionType type=EYIConnectionType::Auto) |
template<typename CallableType > | |
CYISignalConnectionID | Connect (CallableType &&callable, EYIConnectionType type=EYIConnectionType::Auto, CYIThreadHandle threadAffinity=CYIThread::GetCurrentThreadId()) |
template<typename CallableType > | |
CYISignalConnectionID | Connect (const CYISignalHandler &signalHandler, CallableType &&callable, EYIConnectionType type=EYIConnectionType::Auto) |
template<typename... OtherSignalTypes> | |
CYISignalConnectionID | Connect (CYISignal< OtherSignalTypes... > &slotSignal, EYIConnectionType type=EYIConnectionType::Auto) |
void | operator() (const typename std::decay< SignalTypes >::type &... param) const |
void | Emit (const typename std::decay< SignalTypes >::type &... params) const |
template<class SlotReturnType , typename SlotHandlerType , typename... SlotTypes> | |
void | Disconnect (CYISignalHandler &signalHandler, SlotReturnType(SlotHandlerType::*const pSlot)(SlotTypes...)) |
template<class SlotReturnType , typename SlotHandlerType , typename... SlotTypes> | |
void | Disconnect (CYISignalHandler &signalHandler, SlotReturnType(SlotHandlerType::*const pSlot)(SlotTypes...) const) |
template<typename SlotReturnType , typename... SlotTypes> | |
void | Disconnect (SlotReturnType(*const pSlot)(SlotTypes...)) |
![]() | |
virtual bool | IsConnected () const override |
virtual bool | IsConnected (const CYISignalBase &signal) const override |
bool | IsConnected (const CYISignalHandler &signalHandler) const |
bool | IsConnected (CYISignalConnectionID connectionID) const |
void | Disconnect (CYISignalHandler &signalHandler) |
void | Disconnect (CYISignalConnectionID connectionID) |
void | Disconnect (CYISignalBase &signal) |
![]() | |
CYISignalHandler () | |
CYISignalHandler (const CYISignalHandler &signalHandler) | |
virtual | ~CYISignalHandler () |
CYISignalHandler & | operator= (const CYISignalHandler &signalHandler) |
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 &threadAffinity) |
void | Disconnect (CYISignalBase &signal) |
void | DisconnectFromAllSignals () |
![]() | |
Listener () | |
virtual | ~Listener () |
virtual void | OnThreadStarted (CYIThread *) |
virtual void | OnThreadTerminated (CYIThread *) |
virtual void | OnThreadFinished (CYIThread *) |
Protected Member Functions | |
void | EmitForConnection (CYISignalConnectionWrapper &connection, const CYIThreadHandle ¤tThreadID, EYIConnectionType connectionType, const typename std::decay< SignalTypes >::type &... params) |
virtual void | Emitted () |
virtual void | ConnectionAdded (CYISignalConnectionWrapper &connection) |
![]() | |
CYISignalBase () | |
CYISignalBase (const CYISignalBase &signal) | |
virtual | ~CYISignalBase () |
CYISignalBase & | operator= (const CYISignalBase &signal) |
void | RemoveConnection (CYISignalHandler &signalHandler, NotifyFlag notifyHandler) |
void | RemoveAllConnections (NotifyFlag notifyHandler) |
void | RegisterToSignalHandler (const CYISignalHandler &signalHandler) |
void | UnregisterFromSignalHandler (CYISignalHandler &signalHandler) |
void | ExclusiveLock (CYIRecursiveSpinLock &signalMutex) const |
void | ExclusiveLock (const CYISignalHandler &signalHandler, CYIRecursiveSpinLock &signalMutex) const |
void | ExclusiveUnlock (CYIRecursiveSpinLock &signalMutex) const |
void | ExclusiveUnlock (const CYISignalHandler &signalHandler, CYIRecursiveSpinLock &signalMutex) const |
bool | HasConnection (const CYISignalHandler &signalHandler) const |
void | DestroySignalHandler () |
void | CloneConnectionAndConnectTo (CYISignalBase &signal, const CYISignalConnectionWrapper &connection) |
Additional Inherited Members | |
![]() | |
CYILazy< SignalObjects > | m_signalObjects |
CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | HandlerType & | signalHandler, |
SlotReturnType(SlotHandlerType::*)(SlotTypes...) | pSlot, | ||
EYIConnectionType | type = EYIConnectionType::Auto |
||
) |
Establish a connection between this CYISignal and the member function slot pSlot using the signal handler signalHandler.
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 signalHandler.
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, |
EYIConnectionType | type = EYIConnectionType::Auto , |
||
CYIThreadHandle | threadAffinity = CYIThread::GetCurrentThreadId() |
||
) |
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.
By default, the thread affinity of the connection is that of the thread from which the connection is established.
CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | HandlerType & | signalHandler, |
std::function< SlotReturnType(SlotHandlerType &, SlotTypes...)> | slot, | ||
EYIConnectionType | type = EYIConnectionType::Auto |
||
) |
Establish a connection between this CYISignal and the std::function slot slot using the signal handler signalHandler. 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 | ( | CallableType && | callable, |
EYIConnectionType | type = EYIConnectionType::Auto , |
||
CYIThreadHandle | threadAffinity = CYIThread::GetCurrentThreadId() |
||
) |
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.
By default, the thread affinity of the connection is that of the thread from which the connection is established.
CYISignalConnectionID CYISignal< SignalTypes >::Connect | ( | const CYISignalHandler & | signalHandler, |
CallableType && | callable, | ||
EYIConnectionType | type = EYIConnectionType::Auto |
||
) |
Establish a connection between this CYISignal and the Callable slot slot, tying the lifetime of the connection to the lifetime of the signalHandler.
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, |
EYIConnectionType | type = EYIConnectionType::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.
|
protectedvirtual |
A handler called when a connection added to this signal object.
void CYISignal< SignalTypes >::Disconnect | ( | CYISignalHandler & | signalHandler, |
SlotReturnType(SlotHandlerType::*)(SlotTypes...) | pSlot | ||
) |
Destroy a signal/slot connection between this signal and the member function pSlot (using the signal handler signalHandler).
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 signalHandler).
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.
|
protected |
Emits for connection pConnection with the given parameters params.
|
protectedvirtual |
A handler called after a signal has been emitted.
bool CYISignal< SignalTypes >::IsConnected | ( | const HandlerType & | signalHandler, |
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.