From 7cdcd44e0ca10ebdc796638190ed1d9b45d99ef0 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 15 Jan 2024 12:31:20 +1100 Subject: Begin migration of remaining screens to Lua --- lib/bindey/include/bindey/binding.h | 47 --- lib/bindey/include/bindey/property.h | 137 ------- lib/bindey/include/nod/nod.hpp | 681 ----------------------------------- 3 files changed, 865 deletions(-) delete mode 100644 lib/bindey/include/bindey/binding.h delete mode 100644 lib/bindey/include/bindey/property.h delete mode 100644 lib/bindey/include/nod/nod.hpp (limited to 'lib/bindey/include') diff --git a/lib/bindey/include/bindey/binding.h b/lib/bindey/include/bindey/binding.h deleted file mode 100644 index 69baa5cf..00000000 --- a/lib/bindey/include/bindey/binding.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "property.h" - -#include - -#include -#include - -namespace bindey -{ - -using binding = nod::connection; -using scoped_binding = nod::scoped_connection; - -/** - * base binding signature - */ -template -binding bind( property& from, To& to ); - -/** - * binds two properties of the same type - */ -template -binding bind( property& from, property& to ) -{ - return from.onChanged( [&]( const auto& newValue ) { to( newValue ); } ); -} - -/** - * binds two properties of differing types using a Converter callable - * @param from property to observe - * @param to property to write to - * @param bindingConverter a callable to invoke to convert between the types - */ -template -binding bind( property& from, property& to, Converter&& bindingConverter ) -{ - static_assert( std::is_convertible>::value, - "Wrong Signature for binding converter!" ); - - return from.onChanged( - [&to, converter = bindingConverter]( const auto& newValue ) { to( converter( newValue ) ); } ); -} - -} // namespace bindey diff --git a/lib/bindey/include/bindey/property.h b/lib/bindey/include/bindey/property.h deleted file mode 100644 index 369f01f0..00000000 --- a/lib/bindey/include/bindey/property.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include - -#include - -namespace bindey -{ - -/** - * Optional always_update policy to notify subscribers everytime the property value is set, not just when it changes - */ -class always_update -{ -public: - template - bool operator()( const T&, const T& ) const - { - return true; - } -}; - -template , - typename Signal = nod::unsafe_signal> -class property -{ -public: - property() - { - } - property( T&& value ) - : mStorage( std::move( value ) ) - { - } - property( const property& ) = delete; - property& operator=(const property&) = delete; - - - /** - * gets the current value - * @return const reference to the value - */ - const T& get() const - { - return mStorage; - } - - /** - * gets the current value - * @return mutable reference to the value - */ - T& get() - { - return mStorage; - } - - const T& operator()() const - { - return get(); - } - - T& operator()() - { - return get(); - } - - /** - * sets the value of the property. - * @param value the new value - * @discussion the value will only be updated if the UpdatePolicy's critera is met. - * if the value is changed, then the @ref changed event will be fired. - */ - void set( const T& value ) - { - if ( UpdatePolicy{}( mStorage, value ) ) - { - mStorage = value; - changed( mStorage ); - } - } - - void set( T&& value ) - { - if ( UpdatePolicy{}( mStorage, value ) ) - { - mStorage = std::move( value ); - changed( mStorage ); - } - } - - void operator()( const T& value ) - { - set( value ); - } - - void operator()( T&& value ) - { - set( std::move( value ) ); - } - - /** - * this signal is invoked whenever the the value changes per the UpdatePolicy - * @discussion nod::unsafe_signal is used here for speed. Take care of your own threading. - */ - Signal changed; - - /** - * convience function to attach a change listener to this property - */ - auto onChanged( typename decltype( changed )::slot_type&& c ) - { - return changed.connect( std::move( c ) ); - } - - /** - * convience function to attach a change listener to this property and call it right away - */ - auto onChangedAndNow( typename decltype( changed )::slot_type&& c ) - { - auto connection = onChanged( std::move( c ) ); - changed( mStorage ); - return connection; - } - - -private: - T mStorage{}; -}; - -/** - * thread safe property type based on nod::signal - */ -template > -using safe_property = property>; - -} // namespace bindey diff --git a/lib/bindey/include/nod/nod.hpp b/lib/bindey/include/nod/nod.hpp deleted file mode 100644 index 68e7b8d4..00000000 --- a/lib/bindey/include/nod/nod.hpp +++ /dev/null @@ -1,681 +0,0 @@ -#ifndef IG_NOD_INCLUDE_NOD_HPP -#define IG_NOD_INCLUDE_NOD_HPP - -#include // std::vector -#include // std::function -#include // std::mutex, std::lock_guard -#include // std::shared_ptr, std::weak_ptr -#include // std::find_if() -#include // assert() -#include // std::this_thread::yield() -#include // std::is_same -#include // std::back_inserter - -namespace nod { - // implementational details - namespace detail { - /// Interface for type erasure when disconnecting slots - struct disconnector { - virtual ~disconnector() {} - virtual void operator()( std::size_t index ) const = 0; - }; - /// Deleter that doesn't delete - inline void no_delete(disconnector*){ - }; - } // namespace detail - - /// Base template for the signal class - template - class signal_type; - - - /// Connection class. - /// - /// This is used to be able to disconnect slots after they have been connected. - /// Used as return type for the connect method of the signals. - /// - /// Connections are default constructible. - /// Connections are not copy constructible or copy assignable. - /// Connections are move constructible and move assignable. - /// - class connection { - public: - /// Default constructor - connection() : - _index() - {} - - // Connection are not copy constructible or copy assignable - connection( connection const& ) = delete; - connection& operator=( connection const& ) = delete; - - /// Move constructor - /// @param other The instance to move from. - connection( connection&& other ) : - _weak_disconnector( std::move(other._weak_disconnector) ), - _index( other._index ) - {} - - /// Move assign operator. - /// @param other The instance to move from. - connection& operator=( connection&& other ) { - _weak_disconnector = std::move( other._weak_disconnector ); - _index = other._index; - return *this; - } - - /// @returns `true` if the connection is connected to a signal object, - /// and `false` otherwise. - bool connected() const { - return !_weak_disconnector.expired(); - } - - /// Disconnect the slot from the connection. - /// - /// If the connection represents a slot that is connected to a signal object, calling - /// this method will disconnect the slot from that object. The result of this operation - /// is that the slot will stop receiving calls when the signal is invoked. - void disconnect(); - - private: - /// The signal template is a friend of the connection, since it is the - /// only one allowed to create instances using the meaningful constructor. - template friend class signal_type; - - /// Create a connection. - /// @param shared_disconnector Disconnector instance that will be used to disconnect - /// the connection when the time comes. A weak pointer - /// to the disconnector will be held within the connection - /// object. - /// @param index The slot index of the connection. - connection( std::shared_ptr const& shared_disconnector, std::size_t index ) : - _weak_disconnector( shared_disconnector ), - _index( index ) - {} - - /// Weak pointer to the current disconnector functor. - std::weak_ptr _weak_disconnector; - /// Slot index of the connected slot. - std::size_t _index; - }; - - /// Scoped connection class. - /// - /// This type of connection is automatically disconnected when - /// the connection object is destructed. - /// - class scoped_connection - { - public: - /// Scoped are default constructible - scoped_connection() = default; - /// Scoped connections are not copy constructible - scoped_connection( scoped_connection const& ) = delete; - /// Scoped connections are not copy assingable - scoped_connection& operator=( scoped_connection const& ) = delete; - - /// Move constructor - scoped_connection( scoped_connection&& other ) : - _connection( std::move(other._connection) ) - {} - - /// Move assign operator. - /// @param other The instance to move from. - scoped_connection& operator=( scoped_connection&& other ) { - reset( std::move( other._connection ) ); - return *this; - } - - /// Construct a scoped connection from a connection object - /// @param connection The connection object to manage - scoped_connection( connection&& c ) : - _connection( std::forward(c) ) - {} - - /// destructor - ~scoped_connection() { - disconnect(); - } - - /// Assignment operator moving a new connection into the instance. - /// @note If the scoped_connection instance already contains a - /// connection, that connection will be disconnected as if - /// the scoped_connection was destroyed. - /// @param c New connection to manage - scoped_connection& operator=( connection&& c ) { - reset( std::forward(c) ); - return *this; - } - - /// Reset the underlying connection to another connection. - /// @note The connection currently managed by the scoped_connection - /// instance will be disconnected when resetting. - /// @param c New connection to manage - void reset( connection&& c = {} ) { - disconnect(); - _connection = std::move(c); - } - - /// Release the underlying connection, without disconnecting it. - /// @returns The newly released connection instance is returned. - connection release() { - connection c = std::move(_connection); - _connection = connection{}; - return c; - } - - /// - /// @returns `true` if the connection is connected to a signal object, - /// and `false` otherwise. - bool connected() const { - return _connection.connected(); - } - - /// Disconnect the slot from the connection. - /// - /// If the connection represents a slot that is connected to a signal object, calling - /// this method will disconnect the slot from that object. The result of this operation - /// is that the slot will stop receiving calls when the signal is invoked. - void disconnect() { - _connection.disconnect(); - } - - private: - /// Underlying connection object - connection _connection; - }; - - /// Policy for multi threaded use of signals. - /// - /// This policy provides mutex and lock types for use in - /// a multithreaded environment, where signals and slots - /// may exists in different threads. - /// - /// This policy is used in the `nod::signal` type provided - /// by the library. - struct multithread_policy - { - using mutex_type = std::mutex; - using mutex_lock_type = std::unique_lock; - /// Function that yields the current thread, allowing - /// the OS to reschedule. - static void yield_thread() { - std::this_thread::yield(); - } - /// Function that defers a lock to a lock function that prevents deadlock - static mutex_lock_type defer_lock(mutex_type & m){ - return mutex_lock_type{m, std::defer_lock}; - } - /// Function that locks two mutexes and prevents deadlock - static void lock(mutex_lock_type & a,mutex_lock_type & b) { - std::lock(a,b); - } - }; - - /// Policy for single threaded use of signals. - /// - /// This policy provides dummy implementations for mutex - /// and lock types, resulting in that no synchronization - /// will take place. - /// - /// This policy is used in the `nod::unsafe_signal` type - /// provided by the library. - struct singlethread_policy - { - /// Dummy mutex type that doesn't do anything - struct mutex_type{}; - /// Dummy lock type, that doesn't do any locking. - struct mutex_lock_type - { - /// A lock type must be constructible from a - /// mutex type from the same thread policy. - explicit mutex_lock_type( mutex_type const& ) { - } - }; - /// Dummy implementation of thread yielding, that - /// doesn't do any actual yielding. - static void yield_thread() { - } - /// Dummy implemention of defer_lock that doesn't - /// do anything - static mutex_lock_type defer_lock(mutex_type &m){ - return mutex_lock_type{m}; - } - /// Dummy implemention of lock that doesn't - /// do anything - static void lock(mutex_lock_type &,mutex_lock_type &) { - } - }; - - /// Signal accumulator class template. - /// - /// This acts sort of as a proxy for triggering a signal and - /// accumulating the slot return values. - /// - /// This class is not really intended to instantiate by client code. - /// Instances are aquired as return values of the method `accumulate()` - /// called on signals. - /// - /// @tparam S Type of signal. The signal_accumulator acts - /// as a type of proxy for a signal instance of - /// this type. - /// @tparam T Type of initial value of the accumulate algorithm. - /// This type must meet the requirements of `CopyAssignable` - /// and `CopyConstructible` - /// @tparam F Type of accumulation function. - /// @tparam A... Argument types of the underlying signal type. - /// - template - class signal_accumulator - { - public: - /// Result type when calling the accumulating function operator. - using result_type = typename std::result_of::type; - - /// Construct a signal_accumulator as a proxy to a given signal - // - /// @param signal Signal instance. - /// @param init Initial value of the accumulate algorithm. - /// @param func Binary operation function object that will be - /// applied to all slot return values. - /// The signature of the function should be - /// equivalent of the following: - /// `R func( T1 const& a, T2 const& b )` - /// - The signature does not need to have `const&`. - /// - The initial value, type `T`, must be implicitly - /// convertible to `R` - /// - The return type `R` must be implicitly convertible - /// to type `T1`. - /// - The type `R` must be `CopyAssignable`. - /// - The type `S::slot_type::result_type` (return type of - /// the signals slots) must be implicitly convertible to - /// type `T2`. - signal_accumulator( S const& signal, T init, F func ) : - _signal( signal ), - _init( init ), - _func( func ) - {} - - /// Function call operator. - /// - /// Calling this will trigger the underlying signal and accumulate - /// all of the connected slots return values with the current - /// initial value and accumulator function. - /// - /// When called, this will invoke the accumulator function will - /// be called for each return value of the slots. The semantics - /// are similar to the `std::accumulate` algorithm. - /// - /// @param args Arguments to propagate to the slots of the - /// underlying when triggering the signal. - result_type operator()( A const& ... args ) const { - return _signal.trigger_with_accumulator( _init, _func, args... ); - } - - private: - - /// Reference to the underlying signal to proxy. - S const& _signal; - /// Initial value of the accumulate algorithm. - T _init; - /// Accumulator function. - F _func; - - }; - - /// Signal template specialization. - /// - /// This is the main signal implementation, and it is used to - /// implement the observer pattern whithout the overhead - /// boilerplate code that typically comes with it. - /// - /// Any function or function object is considered a slot, and - /// can be connected to a signal instance, as long as the signature - /// of the slot matches the signature of the signal. - /// - /// @tparam P Threading policy for the signal. - /// A threading policy must provide two type definitions: - /// - P::mutex_type, this type will be used as a mutex - /// in the signal_type class template. - /// - P::mutex_lock_type, this type must implement a - /// constructor that takes a P::mutex_type as a parameter, - /// and it must have the semantics of a scoped mutex lock - /// like std::lock_guard, i.e. locking in the constructor - /// and unlocking in the destructor. - /// - /// @tparam R Return value type of the slots connected to the signal. - /// @tparam A... Argument types of the slots connected to the signal. - template - class signal_type - { - public: - /// signals are not copy constructible - signal_type( signal_type const& ) = delete; - /// signals are not copy assignable - signal_type& operator=( signal_type const& ) = delete; - /// signals are move constructible - signal_type(signal_type&& other) - { - mutex_lock_type lock{other._mutex}; - _slot_count = std::move(other._slot_count); - _slots = std::move(other._slots); - if(other._shared_disconnector != nullptr) - { - _disconnector = disconnector{ this }; - _shared_disconnector = std::move(other._shared_disconnector); - // replace the disconnector with our own disconnector - *static_cast(_shared_disconnector.get()) = _disconnector; - } - } - /// signals are move assignable - signal_type& operator=(signal_type&& other) - { - auto lock = thread_policy::defer_lock(_mutex); - auto other_lock = thread_policy::defer_lock(other._mutex); - thread_policy::lock(lock,other_lock); - - _slot_count = std::move(other._slot_count); - _slots = std::move(other._slots); - if(other._shared_disconnector != nullptr) - { - _disconnector = disconnector{ this }; - _shared_disconnector = std::move(other._shared_disconnector); - // replace the disconnector with our own disconnector - *static_cast(_shared_disconnector.get()) = _disconnector; - } - return *this; - } - - /// signals are default constructible - signal_type() : - _slot_count(0) - {} - - // Destruct the signal object. - ~signal_type() { - invalidate_disconnector(); - } - - /// Type that will be used to store the slots for this signal type. - using slot_type = std::function; - /// Type that is used for counting the slots connected to this signal. - using size_type = typename std::vector::size_type; - - - /// Connect a new slot to the signal. - /// - /// The connected slot will be called every time the signal - /// is triggered. - /// @param slot The slot to connect. This must be a callable with - /// the same signature as the signal itself. - /// @return A connection object is returned, and can be used to - /// disconnect the slot. - template - connection connect( T&& slot ) { - mutex_lock_type lock{ _mutex }; - _slots.push_back( std::forward(slot) ); - std::size_t index = _slots.size()-1; - if( _shared_disconnector == nullptr ) { - _disconnector = disconnector{ this }; - _shared_disconnector = std::shared_ptr{&_disconnector, detail::no_delete}; - } - ++_slot_count; - return connection{ _shared_disconnector, index }; - } - - /// Function call operator. - /// - /// Calling this is how the signal is triggered and the - /// connected slots are called. - /// - /// @note The slots will be called in the order they were - /// connected to the signal. - /// - /// @param args Arguments that will be propagated to the - /// connected slots when they are called. - void operator()( A const&... args ) const { - for( auto const& slot : copy_slots() ) { - if( slot ) { - slot( args... ); - } - } - } - - /// Construct a accumulator proxy object for the signal. - /// - /// The intended purpose of this function is to create a function - /// object that can be used to trigger the signal and accumulate - /// all the slot return values. - /// - /// The algorithm used to accumulate slot return values is similar - /// to `std::accumulate`. A given binary function is called for - /// each return value with the parameters consisting of the - /// return value of the accumulator function applied to the - /// previous slots return value, and the current slots return value. - /// A initial value must be provided for the first slot return type. - /// - /// @note This can only be used on signals that have slots with - /// non-void return types, since we can't accumulate void - /// values. - /// - /// @tparam T The type of the initial value given to the accumulator. - /// @tparam F The accumulator function type. - /// @param init Initial value given to the accumulator. - /// @param op Binary operator function object to apply by the accumulator. - /// The signature of the function should be - /// equivalent of the following: - /// `R func( T1 const& a, T2 const& b )` - /// - The signature does not need to have `const&`. - /// - The initial value, type `T`, must be implicitly - /// convertible to `R` - /// - The return type `R` must be implicitly convertible - /// to type `T1`. - /// - The type `R` must be `CopyAssignable`. - /// - The type `S::slot_type::result_type` (return type of - /// the signals slots) must be implicitly convertible to - /// type `T2`. - template - signal_accumulator accumulate( T init, F op ) const { - static_assert( std::is_same::value == false, "Unable to accumulate slot return values with 'void' as return type." ); - return { *this, init, op }; - } - - - /// Trigger the signal, calling the slots and aggregate all - /// the slot return values into a container. - /// - /// @tparam C The type of container. This type must be - /// `DefaultConstructible`, and usable with - /// `std::back_insert_iterator`. Additionally it - /// must be either copyable or moveable. - /// @param args The arguments to propagate to the slots. - template - C aggregate( A const&... args ) const { - static_assert( std::is_same::value == false, "Unable to aggregate slot return values with 'void' as return type." ); - C container; - auto iterator = std::back_inserter( container ); - for( auto const& slot : copy_slots() ) { - if( slot ) { - (*iterator) = slot( args... ); - } - } - return container; - } - - /// Count the number of slots connected to this signal - /// @returns The number of connected slots - size_type slot_count() const { - return _slot_count; - } - - /// Determine if the signal is empty, i.e. no slots are connected - /// to it. - /// @returns `true` is returned if the signal has no connected - /// slots, and `false` otherwise. - bool empty() const { - return slot_count() == 0; - } - - /// Disconnects all slots - /// @note This operation invalidates all scoped_connection objects - void disconnect_all_slots() { - mutex_lock_type lock{ _mutex }; - _slots.clear(); - _slot_count = 0; - invalidate_disconnector(); - } - - private: - template friend class signal_accumulator; - /// Thread policy currently in use - using thread_policy = P; - /// Type of mutex, provided by threading policy - using mutex_type = typename thread_policy::mutex_type; - /// Type of mutex lock, provided by threading policy - using mutex_lock_type = typename thread_policy::mutex_lock_type; - - /// Invalidate the internal disconnector object in a way - /// that is safe according to the current thread policy. - /// - /// This will effectively make all current connection objects to - /// to this signal incapable of disconnecting, since they keep a - /// weak pointer to the shared disconnector object. - void invalidate_disconnector() { - // If we are unlucky, some of the connected slots - // might be in the process of disconnecting from other threads. - // If this happens, we are risking to destruct the disconnector - // object managed by our shared pointer before they are done - // disconnecting. This would be bad. To solve this problem, we - // discard the shared pointer (that is pointing to the disconnector - // object within our own instance), but keep a weak pointer to that - // instance. We then stall the destruction until all other weak - // pointers have released their "lock" (indicated by the fact that - // we will get a nullptr when locking our weak pointer). - std::weak_ptr weak{_shared_disconnector}; - _shared_disconnector.reset(); - while( weak.lock() != nullptr ) { - // we just yield here, allowing the OS to reschedule. We do - // this until all threads has released the disconnector object. - thread_policy::yield_thread(); - } - } - - /// Retrieve a copy of the current slots - /// - /// It's useful and necessary to copy the slots so we don't need - /// to hold the lock while calling the slots. If we hold the lock - /// we prevent the called slots from modifying the slots vector. - /// This simple "double buffering" will allow slots to disconnect - /// themself or other slots and connect new slots. - std::vector copy_slots() const - { - mutex_lock_type lock{ _mutex }; - return _slots; - } - - /// Implementation of the signal accumulator function call - template - typename signal_accumulator::result_type trigger_with_accumulator( T value, F& func, A const&... args ) const { - for( auto const& slot : copy_slots() ) { - if( slot ) { - value = func( value, slot( args... ) ); - } - } - return value; - } - - /// Implementation of the disconnection operation. - /// - /// This is private, and only called by the connection - /// objects created when connecting slots to this signal. - /// @param index The slot index of the slot that should - /// be disconnected. - void disconnect( std::size_t index ) { - mutex_lock_type lock( _mutex ); - assert( _slots.size() > index ); - if( _slots[ index ] != nullptr ) { - --_slot_count; - } - _slots[ index ] = slot_type{}; - while( _slots.size()>0 && !_slots.back() ) { - _slots.pop_back(); - } - } - - /// Implementation of the shared disconnection state - /// used by all connection created by signal instances. - /// - /// This inherits the @ref detail::disconnector interface - /// for type erasure. - struct disconnector : - detail::disconnector - { - /// Default constructor, resulting in a no-op disconnector. - disconnector() : - _ptr(nullptr) - {} - - /// Create a disconnector that works with a given signal instance. - /// @param ptr Pointer to the signal instance that the disconnector - /// should work with. - disconnector( signal_type* ptr ) : - _ptr( ptr ) - {} - - /// Disconnect a given slot on the current signal instance. - /// @note If the instance is default constructed, or created - /// with `nullptr` as signal pointer this operation will - /// effectively be a no-op. - /// @param index The index of the slot to disconnect. - void operator()( std::size_t index ) const override { - if( _ptr ) { - _ptr->disconnect( index ); - } - } - - /// Pointer to the current signal. - signal_type* _ptr; - }; - - /// Mutex to synchronize access to the slot vector - mutable mutex_type _mutex; - /// Vector of all connected slots - std::vector _slots; - /// Number of connected slots - size_type _slot_count; - /// Disconnector operation, used for executing disconnection in a - /// type erased manner. - disconnector _disconnector; - /// Shared pointer to the disconnector. All connection objects has a - /// weak pointer to this pointer for performing disconnections. - std::shared_ptr _shared_disconnector; - }; - - // Implementation of the disconnect operation of the connection class - inline void connection::disconnect() { - auto ptr = _weak_disconnector.lock(); - if( ptr ) { - (*ptr)( _index ); - } - _weak_disconnector.reset(); - } - - /// Signal type that is safe to use in multithreaded environments, - /// where the signal and slots exists in different threads. - /// The multithreaded policy provides mutexes and locks to synchronize - /// access to the signals internals. - /// - /// This is the recommended signal type, even for single threaded - /// environments. - template using signal = signal_type; - - /// Signal type that is unsafe in multithreaded environments. - /// No synchronizations are provided to the signal_type for accessing - /// the internals. - /// - /// Only use this signal type if you are sure that your environment is - /// single threaded and performance is of importance. - template using unsafe_signal = signal_type; -} // namespace nod - -#endif // IG_NOD_INCLUDE_NOD_HPP -- cgit v1.2.3