1 #ifndef DALI_BASE_SIGNAL_H
2 #define DALI_BASE_SIGNAL_H
5 * Copyright (c) 2020 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
88 * @brief Virtual destructor.
91 ~BaseSignal() override;
94 * @brief Queries whether there are any connected slots.
97 * @return True if there are any slots connected to the signal.
102 * @brief Queries the number of slots.
105 * @return The number of slots connected to this signal
107 std::size_t GetConnectionCount() const;
109 // Templated Emit functions for the Signal implementations
112 * @brief Used to guard against nested Emit() calls.
115 struct DALI_CORE_API EmitGuard
118 * @brief Creates the guard.
121 * @param[in,out] flag This flag will be set to true during Emit() calls
123 EmitGuard(bool& flag);
126 * @brief Non-virtual destructor.
133 * @brief Determines if an error occurred.
136 * @return True if an error occurred i.e. if Emit() was called during Emit()
138 bool ErrorOccurred();
140 bool* mFlag; ///< Pointer to the emit guard flag.
144 * @brief Emits a signal with no parameters.
147 * @pre Cannot be called from inside the same Signal's Emit methods.
152 * @brief Emits a signal with no parameters.
155 * @return The value returned by the last callback
156 * @pre Cannot be called from inside the same Signal's Emit methods.
158 template<typename Ret>
161 Ret returnVal = Ret();
163 // Guards against nested Emit() calls
164 EmitGuard guard(mEmittingFlag);
165 if(guard.ErrorOccurred())
170 // If more connections are added by callbacks, these are ignore until the next Emit()
171 // Note that count cannot be reduced while iterating
172 const std::size_t initialCount(mSignalConnections.Count());
174 for(std::size_t i = 0; i < initialCount; ++i)
176 CallbackBase* callback(GetCallback(i));
178 // Note that connections will be set to NULL when disconnected
179 // This is preferable to reducing the connection count while iterating
182 returnVal = CallbackBase::ExecuteReturn<Ret>(*callback);
186 // Cleanup NULL values from Connection container
187 CleanupConnections();
193 * @brief Emits a signal with 1 parameter.
196 * @param[in] arg0 The first parameter
197 * @pre Cannot be called from inside the same Signal's Emit methods.
199 template<typename Arg0>
202 // Guards against nested Emit() calls
203 EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
204 if(guard.ErrorOccurred())
209 // If more connections are added by callbacks, these are ignore until the next Emit()
210 // Note that count cannot be reduced while iterating
211 const std::size_t initialCount(mSignalConnections.Count());
213 for(std::size_t i = 0; i < initialCount; ++i)
215 CallbackBase* callback(GetCallback(i));
217 // Note that connections will be set to NULL when disconnected
218 // This is preferable to reducing the connection count while iterating
221 CallbackBase::Execute<Arg0>(*callback, arg0);
225 // Cleanup NULL values from Connection container
226 CleanupConnections();
230 * @brief Emits a signal with 1 parameter.
233 * @param[in] arg0 The first parameter
234 * @return The value returned by the last callback
235 * @pre Cannot be called from inside the same Signal's Emit methods.
237 template<typename Ret, typename Arg0>
238 Ret EmitReturn(Arg0 arg0)
240 Ret returnVal = Ret();
242 // Guards against nested Emit() calls
243 EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
244 if(guard.ErrorOccurred())
249 // If more connections are added by callbacks, these are ignore until the next Emit()
250 // Note that count cannot be reduced while iterating
251 const std::size_t initialCount(mSignalConnections.Count());
253 for(std::size_t i = 0; i < initialCount; ++i)
255 CallbackBase* callback(GetCallback(i));
257 // Note that connections will be set to NULL when disconnected
258 // This is preferable to reducing the connection count while iterating
261 returnVal = CallbackBase::ExecuteReturn<Ret, Arg0>(*callback, arg0);
265 // Cleanup NULL values from Connection container
266 CleanupConnections();
272 * @brief Emits a signal with 2 parameters.
275 * @param[in] arg0 The first parameter
276 * @param[in] arg1 The second parameter
277 * @pre Cannot be called from inside the same Signal's Emit methods.
279 template<typename Arg0, typename Arg1>
280 void Emit(Arg0 arg0, Arg1 arg1)
282 // Guards against nested Emit() calls
283 EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
284 if(guard.ErrorOccurred())
289 // If more connections are added by callbacks, these are ignore until the next Emit()
290 // Note that count cannot be reduced while iterating
291 const std::size_t initialCount(mSignalConnections.Count());
293 for(std::size_t i = 0; i < initialCount; ++i)
295 CallbackBase* callback(GetCallback(i));
297 // Note that connections will be set to NULL when disconnected
298 // This is preferable to reducing the connection count while iterating
301 CallbackBase::Execute<Arg0, Arg1>(*callback, arg0, arg1);
305 // Cleanup NULL values from Connection container
306 CleanupConnections();
310 * @brief Emits a signal with 2 parameters.
313 * @param[in] arg0 The first parameter
314 * @param[in] arg1 The second parameter
315 * @return The value returned by the last callback
316 * @pre Cannot be called from inside the same Signal's Emit methods.
318 template<typename Ret, typename Arg0, typename Arg1>
319 Ret EmitReturn(Arg0 arg0, Arg1 arg1)
321 Ret returnVal = Ret();
323 // Guards against nested Emit() calls
324 EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
325 if(guard.ErrorOccurred())
330 // If more connections are added by callbacks, these are ignore until the next Emit()
331 // Note that count cannot be reduced while iterating
332 const std::size_t initialCount(mSignalConnections.Count());
334 for(std::size_t i = 0; i < initialCount; ++i)
336 CallbackBase* callback(GetCallback(i));
338 // Note that connections will be set to NULL when disconnected
339 // This is preferable to reducing the connection count while iterating
342 returnVal = CallbackBase::ExecuteReturn<Ret, Arg0, Arg1>(*callback, arg0, arg1);
346 // Cleanup NULL values from Connection container
347 CleanupConnections();
353 * @brief Emits a signal with 3 parameters.
356 * @param[in] arg0 The first parameter
357 * @param[in] arg1 The second parameter
358 * @param[in] arg2 The third parameter
359 * @pre Cannot be called from inside the same Signal's Emit methods.
361 template<typename Arg0, typename Arg1, typename Arg2>
362 void Emit(Arg0 arg0, Arg1 arg1, Arg2 arg2)
364 // Guards against nested Emit() calls
365 EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
366 if(guard.ErrorOccurred())
371 // If more connections are added by callbacks, these are ignore until the next Emit()
372 // Note that count cannot be reduced while iterating
373 const std::size_t initialCount(mSignalConnections.Count());
375 for(std::size_t i = 0; i < initialCount; ++i)
377 CallbackBase* callback(GetCallback(i));
379 // Note that connections will be set to NULL when disconnected
380 // This is preferable to reducing the connection count while iterating
383 CallbackBase::Execute<Arg0, Arg1, Arg2>(*callback, arg0, arg1, arg2);
387 // Cleanup NULL values from Connection container
388 CleanupConnections();
392 * @brief Emits a signal with 3 parameters.
395 * @param[in] arg0 The first parameter
396 * @param[in] arg1 The second parameter
397 * @param[in] arg2 The third parameter
398 * @return The value returned by the last callback
399 * @pre Cannot be called from inside the same Signal's Emit methods.
401 template<typename Ret, typename Arg0, typename Arg1, typename Arg2>
402 Ret EmitReturn(Arg0 arg0, Arg1 arg1, Arg2 arg2)
404 Ret returnVal = Ret();
406 // Guards against nested Emit() calls
407 EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
408 if(guard.ErrorOccurred())
413 // If more connections are added by callbacks, these are ignore until the next Emit()
414 // Note that count cannot be reduced while iterating
415 const std::size_t initialCount(mSignalConnections.Count());
417 for(std::size_t i = 0; i < initialCount; ++i)
419 CallbackBase* callback(GetCallback(i));
421 // Note that connections will be set to NULL when disconnected
422 // This is preferable to reducing the connection count while iterating
425 returnVal = CallbackBase::ExecuteReturn<Ret, Arg0, Arg1, Arg2>(*callback, arg0, arg1, arg2);
429 // Cleanup NULL values from Connection container
430 CleanupConnections();
435 // Connect / Disconnect function for use by Signal implementations
438 * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
441 * @param[in] callback A newly allocated callback object (takes ownership)
443 void OnConnect(CallbackBase* callback);
446 * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
449 * @param[in] callback A newly allocated callback object (takes ownership)
451 void OnDisconnect(CallbackBase* callback);
454 * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
457 * @param[in] tracker The connection tracker
458 * @param[in] callback A newly allocated callback object (takes ownership)
460 void OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
463 * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
466 * @param[in] tracker The connection tracker
467 * @param[in] callback A newly allocated callback object (takes ownership)
469 void OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
471 private: // SlotObserver interface, to be told when a slot disconnects
473 * @copydoc SlotObserver::SlotDisconnected
475 void SlotDisconnected(CallbackBase* callback) override;
479 * @brief Returns a callback given an index in to the connection array.
482 * @param[in] connectionIndex The index of the callback
483 * @return The callback, or NULL if the connection has been deleted
485 CallbackBase* GetCallback(std::size_t connectionIndex) const;
488 * @brief Helper to find whether a callback is connected.
491 * @param[in] callback The call back object
492 * @return A valid index if the callback is connected
494 int32_t FindCallback(CallbackBase* callback);
497 * @brief Deletes a connection object from the list of connections.
500 * @param[in] connectionIndex The index of the callback
502 void DeleteConnection(std::size_t connectionIndex);
505 * @brief Helper to remove NULL items from mSignalConnections, which is only safe at the end of Emit()
506 * i.e. not from methods which can be called during a signal Emit(), such as Disconnect().
509 void CleanupConnections();
511 BaseSignal(const BaseSignal&) = delete; ///< Deleted copy constructor, signals don't support copying. @SINCE_1_0.0
512 BaseSignal(BaseSignal&&) = delete; ///< Deleted move constructor, signals don't support moving. @SINCE_1_9.25
513 BaseSignal& operator=(const BaseSignal&) = delete; ///< Deleted copy assignment operator. @SINCE_1_0.0
514 BaseSignal& operator=(BaseSignal&&) = delete; ///< Deleted move assignment operator. @SINCE_1_9.25
517 Dali::Vector<SignalConnection*> mSignalConnections; ///< Array of connections
519 bool mEmittingFlag; ///< Used to guard against nested Emit() calls
527 #endif // DALI_BASE_SIGNAL_H