add base type of enum to reduce class size.
[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) 2020 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 // INTERNAL INCLUDES
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>
27
28 namespace Dali
29 {
30 /**
31  * @addtogroup dali_core_signals
32  * @{
33  */
34
35 /**
36  * @brief Implementation class for Dali::Signal.
37  *
38  * A slot can be connected to many signals
39  * A signal can be connected to many slots
40  *
41  * To provide automatic disconnection when either a signal or the object owning the slot dies,
42  * observers are used.
43  *
44  * A signal is an object with state. It holds a list of SignalConnection%s.
45  *
46  * E.g.
47  *  Signal OnTouch. mSignalConnections contains
48  * <table>
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>
52  * </table>
53  *
54  * OnTouch.Emit() will run callbacks 0, 1 and 2.
55  *
56  * When the signal is destroyed. SignalDisconnected() is called on each Signal Observer.
57  *
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.
62  *
63  * To keep track of connections between slots and signals, a Connection tracker is used.
64  * It holds a list of SlotConnections.
65  *
66  * <table>
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>
70  * </table>
71  *
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).
75  *
76  * @SINCE_1_0.0
77  */
78 class DALI_CORE_API BaseSignal : public SlotObserver
79 {
80 public:
81   /**
82    * @brief Constructor.
83    * @SINCE_1_0.0
84    */
85   BaseSignal();
86
87   /**
88    * @brief Virtual destructor.
89    * @SINCE_1_0.0
90    */
91   ~BaseSignal() override;
92
93   /**
94    * @brief Queries whether there are any connected slots.
95    *
96    * @SINCE_1_0.0
97    * @return True if there are any slots connected to the signal.
98    */
99   bool Empty() const;
100
101   /**
102    * @brief Queries the number of slots.
103    *
104    * @SINCE_1_0.0
105    * @return The number of slots connected to this signal
106    */
107   std::size_t GetConnectionCount() const;
108
109   // Templated Emit functions for the Signal implementations
110
111   /**
112    * @brief Used to guard against nested Emit() calls.
113    * @SINCE_1_0.0
114    */
115   struct DALI_CORE_API EmitGuard
116   {
117     /**
118      * @brief Creates the guard.
119      *
120      * @SINCE_1_0.0
121      * @param[in,out] flag This flag will be set to true during Emit() calls
122      */
123     EmitGuard(bool& flag);
124
125     /**
126      * @brief Non-virtual destructor.
127      *
128      * @SINCE_1_0.0
129      */
130     ~EmitGuard();
131
132     /**
133      * @brief Determines if an error occurred.
134      *
135      * @SINCE_1_0.0
136      * @return True if an error occurred i.e. if Emit() was called during Emit()
137      */
138     bool ErrorOccurred();
139
140     bool* mFlag; ///< Pointer to the emit guard flag.
141   };
142
143   /**
144    * @brief Emits a signal with no parameters.
145    *
146    * @SINCE_1_0.0
147    * @pre Cannot be called from inside the same Signal's Emit methods.
148    */
149   void Emit();
150
151   /**
152    * @brief Emits a signal with no parameters.
153    *
154    * @SINCE_1_0.0
155    * @return The value returned by the last callback
156    * @pre Cannot be called from inside the same Signal's Emit methods.
157    */
158   template<typename Ret>
159   Ret EmitReturn()
160   {
161     Ret returnVal = Ret();
162
163     // Guards against nested Emit() calls
164     EmitGuard guard(mEmittingFlag);
165     if(guard.ErrorOccurred())
166     {
167       return returnVal;
168     }
169
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());
173
174     for(std::size_t i = 0; i < initialCount; ++i)
175     {
176       CallbackBase* callback(GetCallback(i));
177
178       // Note that connections will be set to NULL when disconnected
179       // This is preferable to reducing the connection count while iterating
180       if(callback)
181       {
182         returnVal = CallbackBase::ExecuteReturn<Ret>(*callback);
183       }
184     }
185
186     // Cleanup NULL values from Connection container
187     CleanupConnections();
188
189     return returnVal;
190   }
191
192   /**
193    * @brief Emits a signal with 1 parameter.
194    *
195    * @SINCE_1_0.0
196    * @param[in] arg0 The first parameter
197    * @pre Cannot be called from inside the same Signal's Emit methods.
198    */
199   template<typename Arg0>
200   void Emit(Arg0 arg0)
201   {
202     // Guards against nested Emit() calls
203     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
204     if(guard.ErrorOccurred())
205     {
206       return;
207     }
208
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());
212
213     for(std::size_t i = 0; i < initialCount; ++i)
214     {
215       CallbackBase* callback(GetCallback(i));
216
217       // Note that connections will be set to NULL when disconnected
218       // This is preferable to reducing the connection count while iterating
219       if(callback)
220       {
221         CallbackBase::Execute<Arg0>(*callback, arg0);
222       }
223     }
224
225     // Cleanup NULL values from Connection container
226     CleanupConnections();
227   }
228
229   /**
230    * @brief Emits a signal with 1 parameter.
231    *
232    * @SINCE_1_0.0
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.
236    */
237   template<typename Ret, typename Arg0>
238   Ret EmitReturn(Arg0 arg0)
239   {
240     Ret returnVal = Ret();
241
242     // Guards against nested Emit() calls
243     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
244     if(guard.ErrorOccurred())
245     {
246       return returnVal;
247     }
248
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());
252
253     for(std::size_t i = 0; i < initialCount; ++i)
254     {
255       CallbackBase* callback(GetCallback(i));
256
257       // Note that connections will be set to NULL when disconnected
258       // This is preferable to reducing the connection count while iterating
259       if(callback)
260       {
261         returnVal = CallbackBase::ExecuteReturn<Ret, Arg0>(*callback, arg0);
262       }
263     }
264
265     // Cleanup NULL values from Connection container
266     CleanupConnections();
267
268     return returnVal;
269   }
270
271   /**
272    * @brief Emits a signal with 2 parameters.
273    *
274    * @SINCE_1_0.0
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.
278    */
279   template<typename Arg0, typename Arg1>
280   void Emit(Arg0 arg0, Arg1 arg1)
281   {
282     // Guards against nested Emit() calls
283     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
284     if(guard.ErrorOccurred())
285     {
286       return;
287     }
288
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());
292
293     for(std::size_t i = 0; i < initialCount; ++i)
294     {
295       CallbackBase* callback(GetCallback(i));
296
297       // Note that connections will be set to NULL when disconnected
298       // This is preferable to reducing the connection count while iterating
299       if(callback)
300       {
301         CallbackBase::Execute<Arg0, Arg1>(*callback, arg0, arg1);
302       }
303     }
304
305     // Cleanup NULL values from Connection container
306     CleanupConnections();
307   }
308
309   /**
310    * @brief Emits a signal with 2 parameters.
311    *
312    * @SINCE_1_0.0
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.
317    */
318   template<typename Ret, typename Arg0, typename Arg1>
319   Ret EmitReturn(Arg0 arg0, Arg1 arg1)
320   {
321     Ret returnVal = Ret();
322
323     // Guards against nested Emit() calls
324     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
325     if(guard.ErrorOccurred())
326     {
327       return returnVal;
328     }
329
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());
333
334     for(std::size_t i = 0; i < initialCount; ++i)
335     {
336       CallbackBase* callback(GetCallback(i));
337
338       // Note that connections will be set to NULL when disconnected
339       // This is preferable to reducing the connection count while iterating
340       if(callback)
341       {
342         returnVal = CallbackBase::ExecuteReturn<Ret, Arg0, Arg1>(*callback, arg0, arg1);
343       }
344     }
345
346     // Cleanup NULL values from Connection container
347     CleanupConnections();
348
349     return returnVal;
350   }
351
352   /**
353    * @brief Emits a signal with 3 parameters.
354    *
355    * @SINCE_1_0.0
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.
360    */
361   template<typename Arg0, typename Arg1, typename Arg2>
362   void Emit(Arg0 arg0, Arg1 arg1, Arg2 arg2)
363   {
364     // Guards against nested Emit() calls
365     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
366     if(guard.ErrorOccurred())
367     {
368       return;
369     }
370
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());
374
375     for(std::size_t i = 0; i < initialCount; ++i)
376     {
377       CallbackBase* callback(GetCallback(i));
378
379       // Note that connections will be set to NULL when disconnected
380       // This is preferable to reducing the connection count while iterating
381       if(callback)
382       {
383         CallbackBase::Execute<Arg0, Arg1, Arg2>(*callback, arg0, arg1, arg2);
384       }
385     }
386
387     // Cleanup NULL values from Connection container
388     CleanupConnections();
389   }
390
391   /**
392    * @brief Emits a signal with 3 parameters.
393    *
394    * @SINCE_1_0.0
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.
400    */
401   template<typename Ret, typename Arg0, typename Arg1, typename Arg2>
402   Ret EmitReturn(Arg0 arg0, Arg1 arg1, Arg2 arg2)
403   {
404     Ret returnVal = Ret();
405
406     // Guards against nested Emit() calls
407     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
408     if(guard.ErrorOccurred())
409     {
410       return returnVal;
411     }
412
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());
416
417     for(std::size_t i = 0; i < initialCount; ++i)
418     {
419       CallbackBase* callback(GetCallback(i));
420
421       // Note that connections will be set to NULL when disconnected
422       // This is preferable to reducing the connection count while iterating
423       if(callback)
424       {
425         returnVal = CallbackBase::ExecuteReturn<Ret, Arg0, Arg1, Arg2>(*callback, arg0, arg1, arg2);
426       }
427     }
428
429     // Cleanup NULL values from Connection container
430     CleanupConnections();
431
432     return returnVal;
433   }
434
435   // Connect / Disconnect function for use by Signal implementations
436
437   /**
438    * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
439    *
440    * @SINCE_1_0.0
441    * @param[in] callback A newly allocated callback object (takes ownership)
442    */
443   void OnConnect(CallbackBase* callback);
444
445   /**
446    * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
447    *
448    * @SINCE_1_0.0
449    * @param[in] callback A newly allocated callback object (takes ownership)
450    */
451   void OnDisconnect(CallbackBase* callback);
452
453   /**
454    * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
455    *
456    * @SINCE_1_0.0
457    * @param[in] tracker The connection tracker
458    * @param[in] callback A newly allocated callback object (takes ownership)
459    */
460   void OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
461
462   /**
463    * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
464    *
465    * @SINCE_1_0.0
466    * @param[in] tracker The connection tracker
467    * @param[in] callback A newly allocated callback object (takes ownership)
468    */
469   void OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
470
471 private: // SlotObserver interface, to be told when a slot disconnects
472   /**
473    * @copydoc SlotObserver::SlotDisconnected
474    */
475   void SlotDisconnected(CallbackBase* callback) override;
476
477 private:
478   /**
479    * @brief Returns a callback given an index in to the connection array.
480    *
481    * @SINCE_1_0.0
482    * @param[in] connectionIndex The index of the callback
483    * @return The callback, or NULL if the connection has been deleted
484    */
485   CallbackBase* GetCallback(std::size_t connectionIndex) const;
486
487   /**
488    * @brief Helper to find whether a callback is connected.
489    *
490    * @SINCE_1_0.0
491    * @param[in] callback The call back object
492    * @return A valid index if the callback is connected
493    */
494   int32_t FindCallback(CallbackBase* callback);
495
496   /**
497    * @brief Deletes a connection object from the list of connections.
498    *
499    * @SINCE_1_0.0
500    * @param[in] connectionIndex The index of the callback
501    */
502   void DeleteConnection(std::size_t connectionIndex);
503
504   /**
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().
507    * @SINCE_1_0.0
508    */
509   void CleanupConnections();
510
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
515
516 private:
517   Dali::Vector<SignalConnection*> mSignalConnections; ///< Array of connections
518
519   bool mEmittingFlag; ///< Used to guard against nested Emit() calls
520 };
521
522 /**
523  * @}
524  */
525 } // namespace Dali
526
527 #endif // DALI_BASE_SIGNAL_H