1 #ifndef __DALI_BASE_SIGNAL_H__
2 #define __DALI_BASE_SIGNAL_H__
5 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 #include <dali/public-api/common/dali-common.h>
23 #include <dali/public-api/common/dali-vector.h>
24 #include <dali/public-api/signals/callback.h>
25 #include <dali/public-api/signals/connection-tracker-interface.h>
26 #include <dali/public-api/signals/signal-slot-connections.h>
31 * @addtogroup dali_core_signals
36 * @brief Implementation class for Dali::Signal.
38 * A slot can be connected to many signals
39 * A signal can be connected to many slots
41 * To provide automatic disconnection when either a signal or the object owning the slot dies,
44 * A signal is an object with state. It holds a list of SignalConnection%s.
47 * Signal OnTouch. mSignalConnections contains
49 * <tr><td> %Callback 0 </td><td> Signal Observer 0 </td></tr>
50 * <tr><td> %Callback 1 </td><td> Signal Observer 1 </td></tr>
51 * <tr><td> %Callback 2 </td><td> Signal Observer 2 </td></tr>
54 * OnTouch.Emit() will run callbacks 0, 1 and 2.
56 * When the signal is destroyed. SignalDisconnected() is called on each Signal Observer.
58 * Slots are just static or member functions, so have no state. E.g. they can't keep
59 * track of how many signals they are connected to.
60 * If the object owning a slot dies, it must automatically disconnected from all signals.
61 * If it doesn't disconnect and the signal is emitted, there will be a crash.
63 * To keep track of connections between slots and signals, a Connection tracker is used.
64 * It holds a list of SlotConnections.
67 * <tr><td> %Callback 0 </td><td> Slot Observer 0 </td></tr>
68 * <tr><td> %Callback 1 </td><td> Slot Observer 1 </td></tr>
69 * <tr><td> %Callback 2 </td><td> Slot Observer 2 </td></tr>
72 * When the connection tracker is destroyed, SlotDisconnected() is called on every slot observer ( signal ).
73 * Signals implement the Slot Observer interface, to be told when a slot has disconnected.
74 * Connection tracker implements the Signal Observer interface, to be told when a signal has disconnected (died).
78 class DALI_CORE_API BaseSignal : public SlotObserver
89 * @brief Virtual destructor.
92 virtual ~BaseSignal();
95 * @brief Queries whether there are any connected slots.
98 * @return True if there are any slots connected to the signal.
103 * @brief Queries the number of slots.
106 * @return The number of slots connected to this signal
108 std::size_t GetConnectionCount() const;
110 // Templated Emit functions for the Signal implementations
113 * @brief Used to guard against nested Emit() calls.
119 * @brief Creates the guard.
122 * @param[in,out] flag This flag will be set to true during Emit() calls
124 EmitGuard( bool& flag );
127 * @brief Non-virtual destructor.
134 * @brief Determines if an error occurred.
137 * @return True if an error occurred i.e. if Emit() was called during Emit()
139 bool ErrorOccurred();
141 bool* mFlag; ///< Pointer to the emit guard flag.
145 * @brief Emits a signal with no parameters.
148 * @pre Cannot be called from inside the same Signal's Emit methods.
153 * @brief Emits a signal with no parameters.
156 * @return The value returned by the last callback
157 * @pre Cannot be called from inside the same Signal's Emit methods.
159 template< typename Ret >
162 Ret returnVal = Ret();
164 // Guards against nested Emit() calls
165 EmitGuard guard( mEmittingFlag );
166 if( guard.ErrorOccurred() )
171 // If more connections are added by callbacks, these are ignore until the next Emit()
172 // Note that count cannot be reduced while iterating
173 const std::size_t initialCount( mSignalConnections.Count() );
175 for( std::size_t i = 0; i < initialCount; ++i )
177 CallbackBase* callback( GetCallback(i) );
179 // Note that connections will be set to NULL when disconnected
180 // This is preferable to reducing the connection count while iterating
183 returnVal = CallbackBase::ExecuteReturn<Ret>( *callback );
187 // Cleanup NULL values from Connection container
188 CleanupConnections();
194 * @brief Emits a signal with 1 parameter.
197 * @param[in] arg0 The first parameter
198 * @pre Cannot be called from inside the same Signal's Emit methods.
200 template< typename Arg0 >
201 void Emit( Arg0 arg0 )
203 // Guards against nested Emit() calls
204 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
205 if( guard.ErrorOccurred() )
210 // If more connections are added by callbacks, these are ignore until the next Emit()
211 // Note that count cannot be reduced while iterating
212 const std::size_t initialCount( mSignalConnections.Count() );
214 for( std::size_t i = 0; i < initialCount; ++i )
216 CallbackBase* callback( GetCallback(i) );
218 // Note that connections will be set to NULL when disconnected
219 // This is preferable to reducing the connection count while iterating
222 CallbackBase::Execute<Arg0 >( *callback, arg0 );
226 // Cleanup NULL values from Connection container
227 CleanupConnections();
231 * @brief Emits a signal with 1 parameter.
234 * @param[in] arg0 The first parameter
235 * @return The value returned by the last callback
236 * @pre Cannot be called from inside the same Signal's Emit methods.
238 template< typename Ret, typename Arg0 >
239 Ret EmitReturn( Arg0 arg0 )
241 Ret returnVal = Ret();
243 // Guards against nested Emit() calls
244 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
245 if( guard.ErrorOccurred() )
250 // If more connections are added by callbacks, these are ignore until the next Emit()
251 // Note that count cannot be reduced while iterating
252 const std::size_t initialCount( mSignalConnections.Count() );
254 for( std::size_t i = 0; i < initialCount; ++i )
256 CallbackBase* callback( GetCallback(i) );
258 // Note that connections will be set to NULL when disconnected
259 // This is preferable to reducing the connection count while iterating
262 returnVal = CallbackBase::ExecuteReturn<Ret,Arg0>( *callback, arg0 );
266 // Cleanup NULL values from Connection container
267 CleanupConnections();
273 * @brief Emits a signal with 2 parameters.
276 * @param[in] arg0 The first parameter
277 * @param[in] arg1 The second parameter
278 * @pre Cannot be called from inside the same Signal's Emit methods.
280 template< typename Arg0, typename Arg1 >
281 void Emit( Arg0 arg0, Arg1 arg1 )
283 // Guards against nested Emit() calls
284 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
285 if( guard.ErrorOccurred() )
290 // If more connections are added by callbacks, these are ignore until the next Emit()
291 // Note that count cannot be reduced while iterating
292 const std::size_t initialCount( mSignalConnections.Count() );
294 for( std::size_t i = 0; i < initialCount; ++i )
296 CallbackBase* callback( GetCallback(i) );
298 // Note that connections will be set to NULL when disconnected
299 // This is preferable to reducing the connection count while iterating
302 CallbackBase::Execute<Arg0,Arg1>( *callback, arg0, arg1 );
306 // Cleanup NULL values from Connection container
307 CleanupConnections();
311 * @brief Emits a signal with 2 parameters.
314 * @param[in] arg0 The first parameter
315 * @param[in] arg1 The second parameter
316 * @return The value returned by the last callback
317 * @pre Cannot be called from inside the same Signal's Emit methods.
319 template< typename Ret, typename Arg0, typename Arg1 >
320 Ret EmitReturn( Arg0 arg0, Arg1 arg1 )
322 Ret returnVal = Ret();
324 // Guards against nested Emit() calls
325 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
326 if( guard.ErrorOccurred() )
331 // If more connections are added by callbacks, these are ignore until the next Emit()
332 // Note that count cannot be reduced while iterating
333 const std::size_t initialCount( mSignalConnections.Count() );
335 for( std::size_t i = 0; i < initialCount; ++i )
337 CallbackBase* callback( GetCallback(i) );
339 // Note that connections will be set to NULL when disconnected
340 // This is preferable to reducing the connection count while iterating
343 returnVal = CallbackBase::ExecuteReturn<Ret,Arg0,Arg1>( *callback, arg0, arg1 );
347 // Cleanup NULL values from Connection container
348 CleanupConnections();
354 * @brief Emits a signal with 3 parameters.
357 * @param[in] arg0 The first parameter
358 * @param[in] arg1 The second parameter
359 * @param[in] arg2 The third parameter
360 * @pre Cannot be called from inside the same Signal's Emit methods.
362 template< typename Arg0, typename Arg1, typename Arg2 >
363 void Emit( Arg0 arg0, Arg1 arg1, Arg2 arg2 )
365 // Guards against nested Emit() calls
366 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
367 if( guard.ErrorOccurred() )
372 // If more connections are added by callbacks, these are ignore until the next Emit()
373 // Note that count cannot be reduced while iterating
374 const std::size_t initialCount( mSignalConnections.Count() );
376 for( std::size_t i = 0; i < initialCount; ++i )
378 CallbackBase* callback( GetCallback(i) );
380 // Note that connections will be set to NULL when disconnected
381 // This is preferable to reducing the connection count while iterating
384 CallbackBase::Execute<Arg0,Arg1,Arg2>( *callback, arg0, arg1, arg2 );
388 // Cleanup NULL values from Connection container
389 CleanupConnections();
393 * @brief Emits a signal with 3 parameters.
396 * @param[in] arg0 The first parameter
397 * @param[in] arg1 The second parameter
398 * @param[in] arg2 The third parameter
399 * @return The value returned by the last callback
400 * @pre Cannot be called from inside the same Signal's Emit methods.
402 template< typename Ret, typename Arg0, typename Arg1, typename Arg2 >
403 Ret EmitReturn( Arg0 arg0, Arg1 arg1, Arg2 arg2 )
405 Ret returnVal = Ret();
407 // Guards against nested Emit() calls
408 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
409 if( guard.ErrorOccurred() )
414 // If more connections are added by callbacks, these are ignore until the next Emit()
415 // Note that count cannot be reduced while iterating
416 const std::size_t initialCount( mSignalConnections.Count() );
418 for( std::size_t i = 0; i < initialCount; ++i )
420 CallbackBase* callback( GetCallback(i) );
422 // Note that connections will be set to NULL when disconnected
423 // This is preferable to reducing the connection count while iterating
426 returnVal = CallbackBase::ExecuteReturn<Ret,Arg0,Arg1,Arg2>( *callback, arg0, arg1, arg2 );
430 // Cleanup NULL values from Connection container
431 CleanupConnections();
436 // Connect / Disconnect function for use by Signal implementations
439 * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
442 * @param[in] callback A newly allocated callback object (takes ownership)
444 void OnConnect( CallbackBase* callback );
447 * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
450 * @param[in] callback A newly allocated callback object (takes ownership)
452 void OnDisconnect( CallbackBase* callback );
455 * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
458 * @param[in] tracker The connection tracker
459 * @param[in] callback A newly allocated callback object (takes ownership)
461 void OnConnect( ConnectionTrackerInterface* tracker, CallbackBase* callback );
464 * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
467 * @param[in] tracker The connection tracker
468 * @param[in] callback A newly allocated callback object (takes ownership)
470 void OnDisconnect( ConnectionTrackerInterface* tracker, CallbackBase* callback );
472 private: // SlotObserver interface, to be told when a slot disconnects
475 * @copydoc SlotObserver::SlotDisconnected
477 virtual void SlotDisconnected( CallbackBase* callback );
482 * @brief Returns a callback given an index in to the connection array.
485 * @param[in] connectionIndex The index of the callback
486 * @return The callback, or NULL if the connection has been deleted
488 CallbackBase* GetCallback( std::size_t connectionIndex ) const;
491 * @brief Helper to find whether a callback is connected.
494 * @param[in] callback The call back object
495 * @return A valid index if the callback is connected
497 int FindCallback( CallbackBase* callback );
500 * @brief Deletes a connection object from the list of connections.
503 * @param[in] connectionIndex The index of the callback
505 void DeleteConnection( std::size_t connectionIndex );
508 * @brief Helper to remove NULL items from mSignalConnections, which is only safe at the end of Emit()
509 * i.e. not from methods which can be called during a signal Emit(), such as Disconnect().
512 void CleanupConnections();
514 BaseSignal( const BaseSignal& ); ///< undefined copy constructor, signals don't support copying. @SINCE_1_0.0
515 BaseSignal& operator=( const BaseSignal& ); ///< undefined assignment operator @SINCE_1_0.0
519 Dali::Vector< SignalConnection* > mSignalConnections; ///< Array of connections
521 bool mEmittingFlag; ///< Used to guard against nested Emit() calls
529 #endif // __DALI_BASE_SIGNAL_H__