-#ifndef __DALI_BASE_SIGNAL_H__
-#define __DALI_BASE_SIGNAL_H__
+#ifndef DALI_BASE_SIGNAL_H
+#define DALI_BASE_SIGNAL_H
/*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
*/
+// EXTERNAL INCLUDES
+#include <stdint.h> // for uint32_t
+
// INTERNAL INCLUDES
#include <dali/public-api/common/dali-common.h>
-#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/common/list-wrapper.h>
#include <dali/public-api/signals/callback.h>
#include <dali/public-api/signals/connection-tracker-interface.h>
#include <dali/public-api/signals/signal-slot-connections.h>
namespace Dali
{
+/**
+ * @addtogroup dali_core_signals
+ * @{
+ */
/**
* @brief Implementation class for Dali::Signal.
* <tr><td> %Callback 2 </td><td> Slot Observer 2 </td></tr>
* </table>
*
- * When the connection tracker is destroyed, SlotDisconnected() is called on every slot observer ( signal )
- * Signals implement the Slot Observer interface, to be told when a slot has disconnected
- * Connection tracker implements the Signal Observer interface, to be told when a signal has disconnected (died)
+ * When the connection tracker is destroyed, SlotDisconnected() is called on every slot observer ( signal ).
+ * Signals implement the Slot Observer interface, to be told when a slot has disconnected.
+ * Connection tracker implements the Signal Observer interface, to be told when a signal has disconnected (died).
*
+ * @SINCE_1_0.0
*/
-class DALI_IMPORT_API BaseSignal : public SlotObserver
+class DALI_CORE_API BaseSignal : public SlotObserver
{
public:
-
/**
* @brief Constructor.
+ * @SINCE_1_0.0
*/
BaseSignal();
/**
* @brief Virtual destructor.
+ * @SINCE_1_0.0
*/
- virtual ~BaseSignal();
+ ~BaseSignal() override;
/**
- * @brief Query whether there are any connected slots.
+ * @brief Queries whether there are any connected slots.
*
+ * @SINCE_1_0.0
* @return True if there are any slots connected to the signal.
*/
- bool Empty() const;
+ bool Empty() const
+ {
+ return (0 == GetConnectionCount());
+ }
/**
- * @brief Query the number of slots.
+ * @brief Queries the number of slots.
*
- * @return The number of slots connected to this signal.
+ * @SINCE_1_0.0
+ * @return The number of slots connected to this signal
*/
- std::size_t GetConnectionCount() const;
+ std::size_t GetConnectionCount() const
+ {
+ return mSignalConnections.size() - mNullConnections;
+ }
// Templated Emit functions for the Signal implementations
/**
* @brief Used to guard against nested Emit() calls.
+ * @SINCE_1_0.0
*/
- struct EmitGuard
+ struct DALI_CORE_API EmitGuard
{
/**
- * @brief Create the guard.
+ * @brief Creates the guard.
*
- * @param[in,out] flag This flag will be set to true during Emit() calls.
+ * @SINCE_1_0.0
+ * @param[in,out] flag This flag will be set to true during Emit() calls
*/
- EmitGuard( bool& flag );
+ EmitGuard(bool& flag);
/**
* @brief Non-virtual destructor.
*
+ * @SINCE_1_0.0
*/
~EmitGuard();
/**
- * @brief Determine if an error occured.
+ * @brief Determines if an error occurred.
*
+ * @SINCE_1_0.0
* @return True if an error occurred i.e. if Emit() was called during Emit()
*/
bool ErrorOccurred();
};
/**
- * @brief Emit a signal with no parameters.
- *
- * @pre Cannot be called from inside the same Signal's Emit methods.
- */
- void Emit();
-
- /**
- * @brief Emit a signal with no parameters.
+ * @brief Emits a signal with parameter pack.
*
+ * @SINCE_1_9.33
+ * @param[in] args The parameter pack
+ * @return The value returned by the last callback
* @pre Cannot be called from inside the same Signal's Emit methods.
- * @return The value returned by the last callback.
*/
- template< typename Ret >
- Ret EmitReturn()
+ template<typename Ret, typename... Args>
+ Ret EmitReturn(Args... args)
{
Ret returnVal = Ret();
// Guards against nested Emit() calls
- EmitGuard guard( mEmittingFlag );
- if( guard.ErrorOccurred() )
+ EmitGuard guard(mEmittingFlag);
+ if(guard.ErrorOccurred())
{
return returnVal;
}
- // If more connections are added by callbacks, these are ignore until the next Emit()
- // Note that count cannot be reduced while iterating
- const std::size_t initialCount( mSignalConnections.Count() );
-
- for( std::size_t i = 0; i < initialCount; ++i )
- {
- CallbackBase* callback( GetCallback(i) );
-
- // Note that connections will be set to NULL when disconnected
- // This is preferable to reducing the connection count while iterating
- if( callback )
- {
- returnVal = CallbackBase::ExecuteReturn<Ret>( *callback );
- }
- }
-
- // Cleanup NULL values from Connection container
- CleanupConnections();
-
- return returnVal;
- }
-
- /**
- * @brief Emit a signal with 1 parameter.
- *
- * @pre Cannot be called from inside the same Signal's Emit methods.
- * @param[in] arg0 The first parameter.
- */
- template< typename Arg0 >
- void Emit( Arg0 arg0 )
- {
- // Guards against nested Emit() calls
- EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
- if( guard.ErrorOccurred() )
- {
- return;
- }
+ // Guards against calling CleanupConnections if the signal is deleted during emission
+ bool signalDeleted{false};
+ mSignalDeleted = &signalDeleted;
// If more connections are added by callbacks, these are ignore until the next Emit()
// Note that count cannot be reduced while iterating
- const std::size_t initialCount( mSignalConnections.Count() );
-
- for( std::size_t i = 0; i < initialCount; ++i )
+ auto count = mSignalConnections.size();
+ auto iter = mSignalConnections.begin();
+ while(count--)
{
- CallbackBase* callback( GetCallback(i) );
-
- // Note that connections will be set to NULL when disconnected
- // This is preferable to reducing the connection count while iterating
- if( callback )
+ CallbackBase* callback((*iter) ? iter->GetCallback() : nullptr);
+ ++iter;
+ if(callback)
{
- CallbackBase::Execute<Arg0 >( *callback, arg0 );
+ returnVal = CallbackBase::ExecuteReturn<Ret, Args...>(*callback, args...);
}
}
- // Cleanup NULL values from Connection container
- CleanupConnections();
- }
-
- /**
- * @brief Emit a signal with 1 parameter.
- *
- * @pre Cannot be called from inside the same Signal's Emit methods.
- * @param[in] arg0 The first parameter.
- * @return The value returned by the last callback.
- */
- template< typename Ret, typename Arg0 >
- Ret EmitReturn( Arg0 arg0 )
- {
- Ret returnVal = Ret();
-
- // Guards against nested Emit() calls
- EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
- if( guard.ErrorOccurred() )
- {
- return returnVal;
- }
-
- // If more connections are added by callbacks, these are ignore until the next Emit()
- // Note that count cannot be reduced while iterating
- const std::size_t initialCount( mSignalConnections.Count() );
-
- for( std::size_t i = 0; i < initialCount; ++i )
+ if(!signalDeleted)
{
- CallbackBase* callback( GetCallback(i) );
-
- // Note that connections will be set to NULL when disconnected
- // This is preferable to reducing the connection count while iterating
- if( callback )
- {
- returnVal = CallbackBase::ExecuteReturn<Ret,Arg0>( *callback, arg0 );
- }
+ // Cleanup NULL values from Connection container
+ CleanupConnections();
+ mSignalDeleted = nullptr;
}
- // Cleanup NULL values from Connection container
- CleanupConnections();
-
return returnVal;
}
/**
- * @brief Emit a signal with 2 parameters.
+ * @brief Emits a signal with parameter pack.
*
+ * @SINCE_1_9.33
+ * @param[in] args The parameter pack
* @pre Cannot be called from inside the same Signal's Emit methods.
- * @param[in] arg0 The first parameter.
- * @param[in] arg1 The second parameter.
*/
- template< typename Arg0, typename Arg1 >
- void Emit( Arg0 arg0, Arg1 arg1 )
+ template<typename... Args>
+ void Emit(Args... args)
{
// Guards against nested Emit() calls
- EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
- if( guard.ErrorOccurred() )
+ EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
+ if(guard.ErrorOccurred())
{
return;
}
- // If more connections are added by callbacks, these are ignore until the next Emit()
- // Note that count cannot be reduced while iterating
- const std::size_t initialCount( mSignalConnections.Count() );
-
- for( std::size_t i = 0; i < initialCount; ++i )
- {
- CallbackBase* callback( GetCallback(i) );
-
- // Note that connections will be set to NULL when disconnected
- // This is preferable to reducing the connection count while iterating
- if( callback )
- {
- CallbackBase::Execute<Arg0,Arg1>( *callback, arg0, arg1 );
- }
- }
-
- // Cleanup NULL values from Connection container
- CleanupConnections();
- }
-
- /**
- * @brief Emit a signal with 2 parameters.
- *
- * @pre Cannot be called from inside the same Signal's Emit methods.
- * @param[in] arg0 The first parameter.
- * @param[in] arg1 The second parameter.
- * @return The value returned by the last callback.
- */
- template< typename Ret, typename Arg0, typename Arg1 >
- Ret EmitReturn( Arg0 arg0, Arg1 arg1 )
- {
- Ret returnVal = Ret();
-
- // Guards against nested Emit() calls
- EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
- if( guard.ErrorOccurred() )
- {
- return returnVal;
- }
-
- // If more connections are added by callbacks, these are ignore until the next Emit()
- // Note that count cannot be reduced while iterating
- const std::size_t initialCount( mSignalConnections.Count() );
-
- for( std::size_t i = 0; i < initialCount; ++i )
- {
- CallbackBase* callback( GetCallback(i) );
-
- // Note that connections will be set to NULL when disconnected
- // This is preferable to reducing the connection count while iterating
- if( callback )
- {
- returnVal = CallbackBase::ExecuteReturn<Ret,Arg0,Arg1>( *callback, arg0, arg1 );
- }
- }
-
- // Cleanup NULL values from Connection container
- CleanupConnections();
-
- return returnVal;
- }
-
- /**
- * @brief Emit a signal with 3 parameters.
- *
- * @pre Cannot be called from inside the same Signal's Emit methods.
- * @param[in] arg0 The first parameter.
- * @param[in] arg1 The second parameter.
- * @param[in] arg2 The third parameter.
- */
- template< typename Arg0, typename Arg1, typename Arg2 >
- void Emit( Arg0 arg0, Arg1 arg1, Arg2 arg2 )
- {
- // Guards against nested Emit() calls
- EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
- if( guard.ErrorOccurred() )
- {
- return;
- }
+ // Guards against calling CleanupConnections if the signal is deleted during emission
+ bool signalDeleted{false};
+ mSignalDeleted = &signalDeleted;
// If more connections are added by callbacks, these are ignore until the next Emit()
// Note that count cannot be reduced while iterating
- const std::size_t initialCount( mSignalConnections.Count() );
-
- for( std::size_t i = 0; i < initialCount; ++i )
+ auto count = mSignalConnections.size();
+ auto iter = mSignalConnections.begin();
+ while(count--)
{
- CallbackBase* callback( GetCallback(i) );
-
- // Note that connections will be set to NULL when disconnected
- // This is preferable to reducing the connection count while iterating
- if( callback )
+ CallbackBase* callback((*iter) ? iter->GetCallback() : nullptr);
+ ++iter;
+ if(callback)
{
- CallbackBase::Execute<Arg0,Arg1,Arg2>( *callback, arg0, arg1, arg2 );
+ CallbackBase::Execute<Args...>(*callback, args...);
}
}
- // Cleanup NULL values from Connection container
- CleanupConnections();
- }
-
- /**
- * @brief Emit a signal with 3 parameters.
- *
- * @pre Cannot be called from inside the same Signal's Emit methods.
- * @param[in] arg0 The first parameter.
- * @param[in] arg1 The second parameter.
- * @param[in] arg2 The third parameter.
- * @return The value returned by the last callback.
- */
- template< typename Ret, typename Arg0, typename Arg1, typename Arg2 >
- Ret EmitReturn( Arg0 arg0, Arg1 arg1, Arg2 arg2 )
- {
- Ret returnVal = Ret();
-
- // Guards against nested Emit() calls
- EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
- if( guard.ErrorOccurred() )
- {
- return returnVal;
- }
-
- // If more connections are added by callbacks, these are ignore until the next Emit()
- // Note that count cannot be reduced while iterating
- const std::size_t initialCount( mSignalConnections.Count() );
-
- for( std::size_t i = 0; i < initialCount; ++i )
+ if(!signalDeleted)
{
- CallbackBase* callback( GetCallback(i) );
-
- // Note that connections will be set to NULL when disconnected
- // This is preferable to reducing the connection count while iterating
- if( callback )
- {
- returnVal = CallbackBase::ExecuteReturn<Ret,Arg0,Arg1,Arg2>( *callback, arg0, arg1, arg2 );
- }
+ // Cleanup NULL values from Connection container
+ CleanupConnections();
+ mSignalDeleted = nullptr;
}
-
- // Cleanup NULL values from Connection container
- CleanupConnections();
-
- return returnVal;
}
// Connect / Disconnect function for use by Signal implementations
/**
- * @brief Called by Signal implementations, when the user calls Signal.Connect( ... )
+ * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
*
- * @param[in] callback A newly allocated callback object (takes ownership).
+ * @SINCE_1_0.0
+ * @param[in] callback A newly allocated callback object (takes ownership)
*/
- void OnConnect( CallbackBase* callback );
+ void OnConnect(CallbackBase* callback);
/**
- * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... )
+ * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
*
- * @param[in] callback A newly allocated callback object (takes ownership).
+ * @SINCE_1_0.0
+ * @param[in] callback A newly allocated callback object (takes ownership)
*/
- void OnDisconnect( CallbackBase* callback );
+ void OnDisconnect(CallbackBase* callback);
/**
- * @brief Called by Signal implementations, when the user calls Signal.Connect( ... )
+ * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
*
- * @param[in] tracker The connection tracker.
- * @param[in] callback A newly allocated callback object (takes ownership).
+ * @SINCE_1_0.0
+ * @param[in] tracker The connection tracker
+ * @param[in] callback A newly allocated callback object (takes ownership)
*/
- void OnConnect( ConnectionTrackerInterface* tracker, CallbackBase* callback );
+ void OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
/**
- * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... )
+ * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
*
- * @param[in] tracker The connection tracker.
- * @param[in] callback A newly allocated callback object (takes ownership).
+ * @SINCE_1_0.0
+ * @param[in] tracker The connection tracker
+ * @param[in] callback A newly allocated callback object (takes ownership)
*/
- void OnDisconnect( ConnectionTrackerInterface* tracker, CallbackBase* callback );
+ void OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
private: // SlotObserver interface, to be told when a slot disconnects
-
/**
* @copydoc SlotObserver::SlotDisconnected
*/
- virtual void SlotDisconnected( CallbackBase* callback );
+ void SlotDisconnected(CallbackBase* callback) override;
private:
-
- /**
- * @brief Returns a callback given an index in to the connection array.
- *
- * @param[in] connectionIndex The index of the callback.
- * @return The callback, or NULL if the connection has been deleted.
- */
- CallbackBase* GetCallback( std::size_t connectionIndex ) const;
-
/**
* @brief Helper to find whether a callback is connected.
*
- * @param[in] callback The call back object.
- * @return A valid index if the callback is connected.
+ * @SINCE_2_1.22
+ * @param[in] callback The call back object
+ * @return A valid index if the callback is connected
*/
- int FindCallback( CallbackBase* callback );
+ std::list<SignalConnection>::iterator FindCallback(CallbackBase* callback) noexcept;
/**
* @brief Deletes a connection object from the list of connections.
*
- * @param[in] connectionIndex The index of the callback.
+ * @SINCE_2_1.22
+ * @param[in] iter The index of the callback
*/
- void DeleteConnection( std::size_t connectionIndex );
+ void DeleteConnection(std::list<SignalConnection>::iterator iter);
/**
* @brief Helper to remove NULL items from mSignalConnections, which is only safe at the end of Emit()
* i.e. not from methods which can be called during a signal Emit(), such as Disconnect().
+ * @SINCE_1_0.0
*/
void CleanupConnections();
- BaseSignal( const BaseSignal& ); ///< undefined copy constructor, signals don't support copying.
- BaseSignal& operator=( const BaseSignal& ); ///< undefined assignment operator
+ BaseSignal(const BaseSignal&) = delete; ///< Deleted copy constructor, signals don't support copying. @SINCE_1_0.0
+ BaseSignal(BaseSignal&&) = delete; ///< Deleted move constructor, signals don't support moving. @SINCE_1_9.25
+ BaseSignal& operator=(const BaseSignal&) = delete; ///< Deleted copy assignment operator. @SINCE_1_0.0
+ BaseSignal& operator=(BaseSignal&&) = delete; ///< Deleted move assignment operator. @SINCE_1_9.25
private:
+ struct DALI_INTERNAL Impl;
+ Impl* mCacheImpl; ///< Private internal extra data.
- Dali::Vector< SignalConnection* > mSignalConnections; ///< Array of connections
-
- bool mEmittingFlag; ///< Used to guard against nested Emit() calls
+private:
+ std::list<SignalConnection> mSignalConnections{}; ///< List of connections
+ uint32_t mNullConnections{0}; ///< Empty Connections in the array.
+ bool mEmittingFlag{false}; ///< Used to guard against nested Emit() calls.
+ bool* mSignalDeleted{nullptr}; ///< Used to guard against deletion during Emit() calls.
};
+/**
+ * @}
+ */
} // namespace Dali
-#endif // __DALI_BASE_SIGNAL_H__
+#endif // DALI_BASE_SIGNAL_H