Merge branch 'devel/master' into devel/graphics
[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) 2021 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 <vector>
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/common/dali-common.h>
26 #include <dali/public-api/common/dali-vector.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     const std::size_t initialCount(mSignalConnections.size());
179
180     for(std::size_t i = 0; i < initialCount; ++i)
181     {
182       CallbackBase* callback(GetCallback(i));
183
184       // Note that connections will be set to NULL when disconnected
185       // This is preferable to reducing the connection count while iterating
186       if(callback)
187       {
188         returnVal = CallbackBase::ExecuteReturn<Ret, Args...>(*callback, args...);
189       }
190     }
191
192     if(!signalDeleted)
193     {
194       // Cleanup NULL values from Connection container
195       CleanupConnections();
196       mSignalDeleted = nullptr;
197     }
198
199     return returnVal;
200   }
201
202   /**
203    * @brief Emits a signal with  parameter pack.
204    *
205    * @SINCE_1_9.33
206    * @param[in] args The parameter pack
207    * @pre Cannot be called from inside the same Signal's Emit methods.
208    */
209   template<typename... Args>
210   void Emit(Args... args)
211   {
212     // Guards against nested Emit() calls
213     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
214     if(guard.ErrorOccurred())
215     {
216       return;
217     }
218
219     // Guards against calling CleanupConnections if the signal is deleted during emission
220     bool signalDeleted{false};
221     mSignalDeleted = &signalDeleted;
222
223     // If more connections are added by callbacks, these are ignore until the next Emit()
224     // Note that count cannot be reduced while iterating
225     const std::size_t initialCount(mSignalConnections.size());
226
227     for(std::size_t i = 0; i < initialCount; ++i)
228     {
229       CallbackBase* callback(GetCallback(i));
230
231       // Note that connections will be set to NULL when disconnected
232       // This is preferable to reducing the connection count while iterating
233       if(callback)
234       {
235         CallbackBase::Execute<Args...>(*callback, args...);
236       }
237     }
238
239     if(!signalDeleted)
240     {
241       // Cleanup NULL values from Connection container
242       CleanupConnections();
243       mSignalDeleted = nullptr;
244     }
245   }
246
247   // Connect / Disconnect function for use by Signal implementations
248
249   /**
250    * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
251    *
252    * @SINCE_1_0.0
253    * @param[in] callback A newly allocated callback object (takes ownership)
254    */
255   void OnConnect(CallbackBase* callback);
256
257   /**
258    * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
259    *
260    * @SINCE_1_0.0
261    * @param[in] callback A newly allocated callback object (takes ownership)
262    */
263   void OnDisconnect(CallbackBase* callback);
264
265   /**
266    * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
267    *
268    * @SINCE_1_0.0
269    * @param[in] tracker The connection tracker
270    * @param[in] callback A newly allocated callback object (takes ownership)
271    */
272   void OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
273
274   /**
275    * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
276    *
277    * @SINCE_1_0.0
278    * @param[in] tracker The connection tracker
279    * @param[in] callback A newly allocated callback object (takes ownership)
280    */
281   void OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
282
283 private: // SlotObserver interface, to be told when a slot disconnects
284   /**
285    * @copydoc SlotObserver::SlotDisconnected
286    */
287   void SlotDisconnected(CallbackBase* callback) override;
288
289 private:
290   /**
291    * @brief Returns a callback given an index in to the connection array.
292    *
293    * @SINCE_1_0.0
294    * @param[in] connectionIndex The index of the callback
295    * @return The callback, or NULL if the connection has been deleted
296    */
297   CallbackBase* GetCallback(std::size_t connectionIndex) const noexcept
298   {
299     return mSignalConnections[connectionIndex].GetCallback();
300   }
301
302   /**
303    * @brief Helper to find whether a callback is connected.
304    *
305    * @SINCE_1_0.0
306    * @param[in] callback The call back object
307    * @return A valid index if the callback is connected
308    */
309   int32_t FindCallback(CallbackBase* callback) const noexcept;
310
311   /**
312    * @brief Deletes a connection object from the list of connections.
313    *
314    * @SINCE_1_0.0
315    * @param[in] connectionIndex The index of the callback
316    */
317   void DeleteConnection(std::size_t connectionIndex);
318
319   /**
320    * @brief Helper to remove NULL items from mSignalConnections, which is only safe at the end of Emit()
321    * i.e. not from methods which can be called during a signal Emit(), such as Disconnect().
322    * @SINCE_1_0.0
323    */
324   void CleanupConnections();
325
326   BaseSignal(const BaseSignal&) = delete;            ///< Deleted copy constructor, signals don't support copying. @SINCE_1_0.0
327   BaseSignal(BaseSignal&&)      = delete;            ///< Deleted move constructor, signals don't support moving. @SINCE_1_9.25
328   BaseSignal& operator=(const BaseSignal&) = delete; ///< Deleted copy assignment operator. @SINCE_1_0.0
329   BaseSignal& operator=(BaseSignal&&) = delete;      ///< Deleted move assignment operator. @SINCE_1_9.25
330
331 private:
332   std::vector<SignalConnection> mSignalConnections;      ///< Array of connections
333   uint32_t                      mNullConnections{0};     ///< Empty Connections in the array.
334   bool                          mEmittingFlag{false};    ///< Used to guard against nested Emit() calls.
335   bool*                         mSignalDeleted{nullptr}; ///< Used to guard against deletion during Emit() calls.
336 };
337
338 /**
339  * @}
340  */
341 } // namespace Dali
342
343 #endif // DALI_BASE_SIGNAL_H