[dali_2.3.25] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / public-api / signals / base-signal.h
1 #ifndef DALI_BASE_SIGNAL_H
2 #define DALI_BASE_SIGNAL_H
3
4 /*
5  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
6  *
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
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  */
20
21 // EXTERNAL INCLUDES
22 #include <stdint.h> // for uint32_t
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/common/dali-common.h>
26 #include <dali/public-api/common/list-wrapper.h>
27 #include <dali/public-api/signals/callback.h>
28 #include <dali/public-api/signals/connection-tracker-interface.h>
29 #include <dali/public-api/signals/signal-slot-connections.h>
30
31 namespace Dali
32 {
33 /**
34  * @addtogroup dali_core_signals
35  * @{
36  */
37
38 /**
39  * @brief Implementation class for Dali::Signal.
40  *
41  * A slot can be connected to many signals
42  * A signal can be connected to many slots
43  *
44  * To provide automatic disconnection when either a signal or the object owning the slot dies,
45  * observers are used.
46  *
47  * A signal is an object with state. It holds a list of SignalConnection%s.
48  *
49  * E.g.
50  *  Signal OnTouch. mSignalConnections contains
51  * <table>
52  * <tr><td> %Callback 0 </td><td> Signal Observer 0  </td></tr>
53  * <tr><td> %Callback 1 </td><td> Signal Observer 1  </td></tr>
54  * <tr><td> %Callback 2 </td><td> Signal Observer 2  </td></tr>
55  * </table>
56  *
57  * OnTouch.Emit() will run callbacks 0, 1 and 2.
58  *
59  * When the signal is destroyed. SignalDisconnected() is called on each Signal Observer.
60  *
61  * Slots are just static or member functions, so have no state. E.g. they can't keep
62  * track of how many signals they are connected to.
63  * If the object owning a slot dies, it must automatically disconnected from all signals.
64  * If it doesn't disconnect and the signal is emitted, there will be a crash.
65  *
66  * To keep track of connections between slots and signals, a Connection tracker is used.
67  * It holds a list of SlotConnections.
68  *
69  * <table>
70  * <tr><td> %Callback 0 </td><td> Slot Observer 0  </td></tr>
71  * <tr><td> %Callback 1 </td><td> Slot Observer 1  </td></tr>
72  * <tr><td> %Callback 2 </td><td> Slot Observer 2  </td></tr>
73  * </table>
74  *
75  * When the connection tracker is destroyed, SlotDisconnected() is called on every slot observer ( signal ).
76  * Signals implement the Slot Observer interface, to be told when a slot has disconnected.
77  * Connection tracker implements the Signal Observer interface, to be told when a signal has disconnected (died).
78  *
79  * @SINCE_1_0.0
80  */
81 class DALI_CORE_API BaseSignal : public SlotObserver
82 {
83 public:
84   /**
85    * @brief Constructor.
86    * @SINCE_1_0.0
87    */
88   BaseSignal();
89
90   /**
91    * @brief Virtual destructor.
92    * @SINCE_1_0.0
93    */
94   ~BaseSignal() override;
95
96   /**
97    * @brief Queries whether there are any connected slots.
98    *
99    * @SINCE_1_0.0
100    * @return True if there are any slots connected to the signal.
101    */
102   bool Empty() const
103   {
104     return (0 == GetConnectionCount());
105   }
106
107   /**
108    * @brief Queries the number of slots.
109    *
110    * @SINCE_1_0.0
111    * @return The number of slots connected to this signal
112    */
113   std::size_t GetConnectionCount() const
114   {
115     return mSignalConnections.size() - mNullConnections;
116   }
117
118   // Templated Emit functions for the Signal implementations
119
120   /**
121    * @brief Used to guard against nested Emit() calls.
122    * @SINCE_1_0.0
123    */
124   struct DALI_CORE_API EmitGuard
125   {
126     /**
127      * @brief Creates the guard.
128      *
129      * @SINCE_1_0.0
130      * @param[in,out] flag This flag will be set to true during Emit() calls
131      */
132     EmitGuard(bool& flag);
133
134     /**
135      * @brief Non-virtual destructor.
136      *
137      * @SINCE_1_0.0
138      */
139     ~EmitGuard();
140
141     /**
142      * @brief Determines if an error occurred.
143      *
144      * @SINCE_1_0.0
145      * @return True if an error occurred i.e. if Emit() was called during Emit()
146      */
147     bool ErrorOccurred();
148
149     bool* mFlag; ///< Pointer to the emit guard flag.
150   };
151
152   /**
153    * @brief Emits a signal with parameter pack.
154    *
155    * @SINCE_1_9.33
156    * @param[in] args The parameter pack
157    * @return The value returned by the last callback
158    * @pre Cannot be called from inside the same Signal's Emit methods.
159    */
160   template<typename Ret, typename... Args>
161   Ret EmitReturn(Args... args)
162   {
163     Ret returnVal = Ret();
164
165     // Guards against nested Emit() calls
166     EmitGuard guard(mEmittingFlag);
167     if(guard.ErrorOccurred())
168     {
169       return returnVal;
170     }
171
172     // Guards against calling CleanupConnections if the signal is deleted during emission
173     bool signalDeleted{false};
174     mSignalDeleted = &signalDeleted;
175
176     // If more connections are added by callbacks, these are ignore until the next Emit()
177     // Note that count cannot be reduced while iterating
178     auto count = mSignalConnections.size();
179     auto iter  = mSignalConnections.begin();
180     while(count--)
181     {
182       CallbackBase* callback((*iter) ? iter->GetCallback() : nullptr);
183       ++iter;
184       if(callback)
185       {
186         returnVal = CallbackBase::ExecuteReturn<Ret, Args...>(*callback, args...);
187       }
188     }
189
190     if(!signalDeleted)
191     {
192       // Cleanup NULL values from Connection container
193       CleanupConnections();
194       mSignalDeleted = nullptr;
195     }
196
197     return returnVal;
198   }
199
200   /**
201    * @brief Emits a signal with  parameter pack.
202    *
203    * @SINCE_1_9.33
204    * @param[in] args The parameter pack
205    * @pre Cannot be called from inside the same Signal's Emit methods.
206    */
207   template<typename... Args>
208   void Emit(Args... args)
209   {
210     // Guards against nested Emit() calls
211     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
212     if(guard.ErrorOccurred())
213     {
214       return;
215     }
216
217     // Guards against calling CleanupConnections if the signal is deleted during emission
218     bool signalDeleted{false};
219     mSignalDeleted = &signalDeleted;
220
221     // If more connections are added by callbacks, these are ignore until the next Emit()
222     // Note that count cannot be reduced while iterating
223     auto count = mSignalConnections.size();
224     auto iter  = mSignalConnections.begin();
225     while(count--)
226     {
227       CallbackBase* callback((*iter) ? iter->GetCallback() : nullptr);
228       ++iter;
229       if(callback)
230       {
231         CallbackBase::Execute<Args...>(*callback, args...);
232       }
233     }
234
235     if(!signalDeleted)
236     {
237       // Cleanup NULL values from Connection container
238       CleanupConnections();
239       mSignalDeleted = nullptr;
240     }
241   }
242
243   // Connect / Disconnect function for use by Signal implementations
244
245   /**
246    * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
247    *
248    * @SINCE_1_0.0
249    * @param[in] callback A newly allocated callback object (takes ownership)
250    */
251   void OnConnect(CallbackBase* callback);
252
253   /**
254    * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
255    *
256    * @SINCE_1_0.0
257    * @param[in] callback A newly allocated callback object (takes ownership)
258    */
259   void OnDisconnect(CallbackBase* callback);
260
261   /**
262    * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
263    *
264    * @SINCE_1_0.0
265    * @param[in] tracker The connection tracker
266    * @param[in] callback A newly allocated callback object (takes ownership)
267    */
268   void OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
269
270   /**
271    * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
272    *
273    * @SINCE_1_0.0
274    * @param[in] tracker The connection tracker
275    * @param[in] callback A newly allocated callback object (takes ownership)
276    */
277   void OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
278
279 private: // SlotObserver interface, to be told when a slot disconnects
280   /**
281    * @copydoc SlotObserver::SlotDisconnected
282    */
283   void SlotDisconnected(CallbackBase* callback) override;
284
285 private:
286   /**
287    * @brief Helper to find whether a callback is connected.
288    *
289    * @SINCE_2_1.22
290    * @param[in] callback The call back object
291    * @return A valid index if the callback is connected
292    */
293   std::list<SignalConnection>::iterator FindCallback(CallbackBase* callback) noexcept;
294
295   /**
296    * @brief Deletes a connection object from the list of connections.
297    *
298    * @SINCE_2_1.22
299    * @param[in] iter The index of the callback
300    */
301   void DeleteConnection(std::list<SignalConnection>::iterator iter);
302
303   /**
304    * @brief Helper to remove NULL items from mSignalConnections, which is only safe at the end of Emit()
305    * i.e. not from methods which can be called during a signal Emit(), such as Disconnect().
306    * @SINCE_1_0.0
307    */
308   void CleanupConnections();
309
310   BaseSignal(const BaseSignal&) = delete;            ///< Deleted copy constructor, signals don't support copying. @SINCE_1_0.0
311   BaseSignal(BaseSignal&&)      = delete;            ///< Deleted move constructor, signals don't support moving. @SINCE_1_9.25
312   BaseSignal& operator=(const BaseSignal&) = delete; ///< Deleted copy assignment operator. @SINCE_1_0.0
313   BaseSignal& operator=(BaseSignal&&) = delete;      ///< Deleted move assignment operator. @SINCE_1_9.25
314
315 private:
316   struct DALI_INTERNAL Impl;
317   Impl*                mCacheImpl; ///< Private internal extra data.
318
319 private:
320   std::list<SignalConnection> mSignalConnections{};    ///< List of connections
321   uint32_t                    mNullConnections{0};     ///< Empty Connections in the array.
322   bool                        mEmittingFlag{false};    ///< Used to guard against nested Emit() calls.
323   bool*                       mSignalDeleted{nullptr}; ///< Used to guard against deletion during Emit() calls.
324 };
325
326 /**
327  * @}
328  */
329 } // namespace Dali
330
331 #endif // DALI_BASE_SIGNAL_H