Updated all code to new format
[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     // If more connections are added by callbacks, these are ignore until the next Emit()
173     // Note that count cannot be reduced while iterating
174     const std::size_t initialCount(mSignalConnections.size());
175
176     for(std::size_t i = 0; i < initialCount; ++i)
177     {
178       CallbackBase* callback(GetCallback(i));
179
180       // Note that connections will be set to NULL when disconnected
181       // This is preferable to reducing the connection count while iterating
182       if(callback)
183       {
184         returnVal = CallbackBase::ExecuteReturn<Ret, Args...>(*callback, args...);
185       }
186     }
187
188     // Cleanup NULL values from Connection container
189     CleanupConnections();
190
191     return returnVal;
192   }
193
194   /**
195    * @brief Emits a signal with  parameter pack.
196    *
197    * @SINCE_1_9.33
198    * @param[in] args The parameter pack
199    * @pre Cannot be called from inside the same Signal's Emit methods.
200    */
201   template<typename... Args>
202   void Emit(Args... args)
203   {
204     // Guards against nested Emit() calls
205     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
206     if(guard.ErrorOccurred())
207     {
208       return;
209     }
210
211     // If more connections are added by callbacks, these are ignore until the next Emit()
212     // Note that count cannot be reduced while iterating
213     const std::size_t initialCount(mSignalConnections.size());
214
215     for(std::size_t i = 0; i < initialCount; ++i)
216     {
217       CallbackBase* callback(GetCallback(i));
218
219       // Note that connections will be set to NULL when disconnected
220       // This is preferable to reducing the connection count while iterating
221       if(callback)
222       {
223         CallbackBase::Execute<Args...>(*callback, args...);
224       }
225     }
226
227     // Cleanup NULL values from Connection container
228     CleanupConnections();
229   }
230
231   // Connect / Disconnect function for use by Signal implementations
232
233   /**
234    * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
235    *
236    * @SINCE_1_0.0
237    * @param[in] callback A newly allocated callback object (takes ownership)
238    */
239   void OnConnect(CallbackBase* callback);
240
241   /**
242    * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
243    *
244    * @SINCE_1_0.0
245    * @param[in] callback A newly allocated callback object (takes ownership)
246    */
247   void OnDisconnect(CallbackBase* callback);
248
249   /**
250    * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
251    *
252    * @SINCE_1_0.0
253    * @param[in] tracker The connection tracker
254    * @param[in] callback A newly allocated callback object (takes ownership)
255    */
256   void OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
257
258   /**
259    * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
260    *
261    * @SINCE_1_0.0
262    * @param[in] tracker The connection tracker
263    * @param[in] callback A newly allocated callback object (takes ownership)
264    */
265   void OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
266
267 private: // SlotObserver interface, to be told when a slot disconnects
268   /**
269    * @copydoc SlotObserver::SlotDisconnected
270    */
271   void SlotDisconnected(CallbackBase* callback) override;
272
273 private:
274   /**
275    * @brief Returns a callback given an index in to the connection array.
276    *
277    * @SINCE_1_0.0
278    * @param[in] connectionIndex The index of the callback
279    * @return The callback, or NULL if the connection has been deleted
280    */
281   CallbackBase* GetCallback(std::size_t connectionIndex) const noexcept
282   {
283     return mSignalConnections[connectionIndex].GetCallback();
284   }
285
286   /**
287    * @brief Helper to find whether a callback is connected.
288    *
289    * @SINCE_1_0.0
290    * @param[in] callback The call back object
291    * @return A valid index if the callback is connected
292    */
293   int32_t FindCallback(CallbackBase* callback) const noexcept;
294
295   /**
296    * @brief Deletes a connection object from the list of connections.
297    *
298    * @SINCE_1_0.0
299    * @param[in] connectionIndex The index of the callback
300    */
301   void DeleteConnection(std::size_t connectionIndex);
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   std::vector<SignalConnection> mSignalConnections;   ///< Array of connections
317   uint32_t                      mNullConnections{0};  ///< Empty Connections in the array.
318   bool                          mEmittingFlag{false}; ///< Used to guard against nested Emit() calls
319 };
320
321 /**
322  * @}
323  */
324 } // namespace Dali
325
326 #endif // DALI_BASE_SIGNAL_H