#pragma once // ------------------------------------------------------------------------------------------------ #include "Core/Utility.hpp" // ------------------------------------------------------------------------------------------------ #include #include #include #include // ------------------------------------------------------------------------------------------------ namespace SqMod { // ------------------------------------------------------------------------------------------------ struct Signal; struct SignalWrapper; /* ------------------------------------------------------------------------------------------------ * Class used to deliver events to one or more listeners. */ struct Signal { friend class SignalWrapper; // -------------------------------------------------------------------------------------------- typedef unsigned int SizeType; // Type of value used to represent sizes and/or indexes. // -------------------------------------------------------------------------------------------- enum { SMB_SIZE = 8 }; /* -------------------------------------------------------------------------------------------- * Default constructor. */ Signal(); /* -------------------------------------------------------------------------------------------- * Default constructor. */ explicit Signal(const char * name) : Signal(String(name)) { //... } /* -------------------------------------------------------------------------------------------- * Default constructor. */ explicit Signal(const String & name) : Signal(String(name)) { //... } /* -------------------------------------------------------------------------------------------- * Default constructor. */ explicit Signal(String && name); /* -------------------------------------------------------------------------------------------- * Copy constructor (disabled). */ Signal(const Signal & o) = delete; /* -------------------------------------------------------------------------------------------- * Move constructor (disabled). */ Signal(Signal && o) = delete; /* -------------------------------------------------------------------------------------------- * Destructor. */ ~Signal(); /* -------------------------------------------------------------------------------------------- * Copy assignment operator (disabled). */ Signal & operator = (const Signal & o) = delete; /* -------------------------------------------------------------------------------------------- * Move assignment operator (disabled). */ Signal & operator = (Signal && o) = delete; protected: /* -------------------------------------------------------------------------------------------- * Adjust the internal buffer size if necessary. */ bool AdjustSlots(SizeType capacity); /* -------------------------------------------------------------------------------------------- * Structure responsible for storing information about a slot. */ struct Slot { SQHash mThisHash; // The hash of the specified environment. SQHash mFuncHash; // The hash of the specified callback. HSQOBJECT mThisRef; // The specified script environment. HSQOBJECT mFuncRef; // The specified script callback. /* ---------------------------------------------------------------------------------------- * Default constructor. */ Slot() : mThisHash(0) , mFuncHash(0) , mThisRef() , mFuncRef() { sq_resetobject(&mThisRef); sq_resetobject(&mFuncRef); } /* ---------------------------------------------------------------------------------------- * Forwarding constructor. */ Slot(Object & env, ::Sqrat::Function & func) : Slot(env.GetObj(), func.GetFunc()) { /* ... */ } /* ---------------------------------------------------------------------------------------- * Base constructor. */ Slot(HSQOBJECT & env, HSQOBJECT & func) : mThisHash(0) , mFuncHash(0) , mThisRef(env) , mFuncRef(func) { HSQUIRRELVM vm = SqVM(); // Remember the current stack size const StackGuard sg(vm); // Is there an explicit environment? if (!sq_isnull(mThisRef)) { // Keep a reference to this environment sq_addref(vm, &mThisRef); // Push the environment on the stack sq_pushobject(vm, mThisRef); // Grab the hash of the environment object mThisHash = sq_gethash(vm, -1); } // Is there an explicit function? if (!sq_isnull(mFuncRef)) { // Keep a reference to this function sq_addref(vm, &mFuncRef); // Push the callback on the stack sq_pushobject(vm, mFuncRef); // Grab the hash of the callback object mFuncHash = sq_gethash(vm, -1); } } /* ---------------------------------------------------------------------------------------- * Base constructor. */ Slot(HSQOBJECT & env, HSQOBJECT & func, SQHash envh, SQHash funch) : mThisHash(envh) , mFuncHash(funch) , mThisRef(env) , mFuncRef(func) { } /* ---------------------------------------------------------------------------------------- * Copy constructor. */ Slot(const Slot & o) : mThisHash(o.mThisHash) , mFuncHash(o.mFuncHash) , mThisRef(o.mThisRef) , mFuncRef(o.mFuncRef) { // Track reference if (mFuncHash != 0) { sq_addref(SqVM(), &mThisRef); sq_addref(SqVM(), &mFuncRef); } } /* ---------------------------------------------------------------------------------------- * Move constructor. */ Slot(Slot && o) noexcept : mThisHash(o.mThisHash) , mFuncHash(o.mFuncHash) , mThisRef(o.mThisRef) , mFuncRef(o.mFuncRef) { // Take ownership sq_resetobject(&o.mThisRef); sq_resetobject(&o.mFuncRef); } /* ---------------------------------------------------------------------------------------- * Destructor. */ ~Slot() { Release(); } /* ---------------------------------------------------------------------------------------- * Copy assignment operator. (disabled) */ Slot & operator = (const Slot & o) { if (this != &o) { // Release current resources, if any Release(); // Replicate data mThisHash = o.mThisHash; mFuncHash = o.mFuncHash; mThisRef = o.mThisRef; mFuncRef = o.mFuncRef; // Track reference sq_addref(SqVM(), &const_cast< HSQOBJECT & >(o.mThisRef)); sq_addref(SqVM(), &const_cast< HSQOBJECT & >(o.mFuncRef)); } return *this; } /* ---------------------------------------------------------------------------------------- * Move assignment operator. */ Slot & operator = (Slot && o) noexcept { if (this != &o) { // Release current resources, if any Release(); // Replicate data mThisHash = o.mThisHash; mFuncHash = o.mFuncHash; mThisRef = o.mThisRef; mFuncRef = o.mFuncRef; // Take ownership sq_resetobject(&o.mThisRef); sq_resetobject(&o.mFuncRef); } return *this; } /* ---------------------------------------------------------------------------------------- * Equality comparison operator. */ bool operator == (const Slot & o) const { return (mThisHash == o.mThisHash) && (mFuncHash == o.mFuncHash); } /* ---------------------------------------------------------------------------------------- * Inequality comparison operator. */ bool operator != (const Slot & o) const { return (mThisHash != o.mThisHash) || (mFuncHash != o.mFuncHash); } /* ---------------------------------------------------------------------------------------- * Release managed script resources. */ SQMOD_NODISCARD bool Available() const { return (mFuncHash == 0); } /* ---------------------------------------------------------------------------------------- * Release managed script resources. */ void Release() { // Should we release any environment object? if (mThisHash != 0) { sq_release(SqVM(), &mThisRef); sq_resetobject(&mThisRef); // Also reset the hash mThisHash = 0; } // Should we release any callback object? if (mFuncHash != 0) { sq_release(SqVM(), &mFuncRef); sq_resetobject(&mFuncRef); // Also reset the hash mFuncHash = 0; } } /* ---------------------------------------------------------------------------------------- * Swap the values of two slots. */ void Swap(Slot & s) { // Swap the environment hash SQHash h = mThisHash; mThisHash = s.mThisHash; s.mThisHash = h; // Swap the callback hash h = mFuncHash; mFuncHash = s.mFuncHash; s.mFuncHash = h; // Swap the environment object HSQOBJECT o = mThisRef; mThisRef = s.mThisRef; s.mThisRef = o; // Swap the callback object o = mFuncRef; mFuncRef = s.mFuncRef; s.mFuncRef = o; } }; // -------------------------------------------------------------------------------------------- typedef Slot ValueType; // Value type used to represent a slot. typedef ValueType & Reference; // Reference to the stored value type typedef const ValueType & ConstReference; // Constant reference to the stored value type. typedef ValueType * Pointer; // Pointer to the stored value type typedef const ValueType * ConstPointer; // Constant pointer to the stored value type. // -------------------------------------------------------------------------------------------- /// Execution scope used to adjust iterators when removing slots or adjusting the buffer. struct Scope { // ---------------------------------------------------------------------------------------- Pointer mItr; ///< Currently executed slot. Pointer mEnd; ///< Where the execution ends. Scope * mParent; ///< Previous execution scope. Scope * mChild; ///< Next execution scope. // ---------------------------------------------------------------------------------------- /// Default constructor. Scope(Scope * parent, Pointer begin, Pointer end) : mItr(begin), mEnd(end), mParent(parent), mChild(nullptr) { if (mParent != nullptr) mParent->mChild = this; } // ---------------------------------------------------------------------------------------- /// Destructor. ~Scope() { if (mParent != nullptr) mParent->mChild = nullptr; } // ---------------------------------------------------------------------------------------- /// Adjust the iterators to account for the fact that the specified slot was removed. void Descend(Pointer ptr); /// Adjust the iterators to account for the fact that the specified slot is now leading. void Lead(Pointer ptr); /// Adjust the iterators to account for the fact that the specified slot is now tailing. void Tail(Pointer ptr); /// Adjust the iterators to finish the execution abruptly. void Finish(); }; private: // -------------------------------------------------------------------------------------------- SizeType m_Used; // The number of stored slots that are valid. SizeType m_Size; // The size of the memory allocated for slots. Pointer m_Slots; // Pointer to the memory containing the slots. // -------------------------------------------------------------------------------------------- Scope * m_Scope; // Current execution state. // -------------------------------------------------------------------------------------------- String m_Name; // The name that identifies this signal. LightObj m_Data; // User data associated with this instance. // -------------------------------------------------------------------------------------------- ValueType m_SMB[SMB_SIZE]{}; // Small buffer optimization. public: /* -------------------------------------------------------------------------------------------- * Used by the script engine to convert an instance of this type to a string. */ SQMOD_NODISCARD const String & ToString() const { return m_Name; } /* -------------------------------------------------------------------------------------------- * Retrieve the associated user data. */ SQMOD_NODISCARD LightObj & GetData() { return m_Data; } /* -------------------------------------------------------------------------------------------- * Modify the associated user data. */ void SetData(LightObj & data) { m_Data = data; } /* -------------------------------------------------------------------------------------------- * The number of slots connected to the signal. */ SQMOD_NODISCARD SQInteger GetUsed() const { return static_cast< SQInteger >(m_Used); } /* -------------------------------------------------------------------------------------------- * Clear all slots connected to the signal. */ void ClearSlots(); /* -------------------------------------------------------------------------------------------- * See if there are any slots connected. */ SQMOD_NODISCARD bool IsEmpty() const { return (m_Used == 0); } protected: /* -------------------------------------------------------------------------------------------- * Connect the specified slot to the signal. */ SQInteger Connect(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Connect the specified slot but not before disconnecting all other occurrences. */ SQInteger ConnectOnce(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Disconnect all occurrences of the specified slot from the signal. */ SQInteger Disconnect(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * See if the specified slot is connected to the signal. */ SQInteger Exists(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * See if the specified slot environment is connected to the signal. */ SQInteger ExistsThis(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * See if the specified slot callback is connected to the signal. */ SQInteger ExistsFunc(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Count all occurrences of the specified slot. */ SQInteger Count(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Count all occurrences of the specified slot environment. */ SQInteger CountThis(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Count all occurrences of the specified slot callback. */ SQInteger CountFunc(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Move all occurrences of the specified slot to the front. */ SQInteger Lead(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Move all occurrences of the specified slot environment to the front. */ SQInteger LeadThis(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Move all occurrences of the specified slot callback to the front. */ SQInteger LeadFunc(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Move all occurrences of the specified slot to the back. */ SQInteger Tail(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Move all occurrences of the specified slot environment to the back. */ SQInteger TailThis(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Move all occurrences of the specified slot callback to the back. */ SQInteger TailFunc(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Remove all occurrences of the specified slot. */ SQInteger Eliminate(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Remove all occurrences of the specified slot environment. */ SQInteger EliminateThis(SignalWrapper & w); /* -------------------------------------------------------------------------------------------- * Remove all occurrences of the specified slot callback. */ SQInteger EliminateFunc(SignalWrapper & w); public: /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Connect` method of this class. */ static SQInteger SqConnect(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `ConnectOnce` method of this class. */ static SQInteger SqConnectOnce(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Exists` method of this class. */ static SQInteger SqExists(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Disconnect` method of this class. */ static SQInteger SqDisconnect(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `ExistsThis` method of this class. */ static SQInteger SqExistsThis(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `ExistsFunc` method of this class. */ static SQInteger SqExistsFunc(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Count` method of this class. */ static SQInteger SqCount(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `CountThis` method of this class. */ static SQInteger SqCountThis(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `CountFunc` method of this class. */ static SQInteger SqCountFunc(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Lead` method of this class. */ static SQInteger SqLead(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `LeadThis` method of this class. */ static SQInteger SqLeadThis(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `LeadFunc` method of this class. */ static SQInteger SqLeadFunc(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Tail` method of this class. */ static SQInteger SqTail(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `TailThis` method of this class. */ static SQInteger SqTailThis(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `TailFunc` method of this class. */ static SQInteger SqTailFunc(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Eliminate` method of this class. */ static SQInteger SqEliminate(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `EliminateThis` method of this class. */ static SQInteger SqEliminateThis(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `EliminateFunc` method of this class. */ static SQInteger SqEliminateFunc(HSQUIRRELVM vm); protected: /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots. */ SQInteger Emit(HSQUIRRELVM vm, SQInteger top); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots and collect returned values. */ SQInteger Query(HSQUIRRELVM vm, SQInteger top); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots and see if they consume it. */ SQInteger Consume(HSQUIRRELVM vm, SQInteger top); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots and see if they approve it. */ SQInteger Approve(HSQUIRRELVM vm, SQInteger top); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots and see if they return something. */ SQInteger Request(HSQUIRRELVM vm, SQInteger top); public: /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Emit` method of this class. */ static SQInteger SqEmit(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Query` method of this class. */ static SQInteger SqQuery(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Consume` method of this class. */ static SQInteger SqConsume(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Approve` method of this class. */ static SQInteger SqApprove(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Squirrel wrapper for the `Request` method of this class. */ static SQInteger SqRequest(HSQUIRRELVM vm); protected: // -------------------------------------------------------------------------------------------- typedef std::pair< std::size_t, SignalPair > SignalElement; typedef std::vector< SignalElement > SignalPool; typedef std::vector< Signal * > FreeSignals; // -------------------------------------------------------------------------------------------- static SignalPool s_Signals; // List of all created signals. static FreeSignals s_FreeSignals; // List of signals without a name. /* -------------------------------------------------------------------------------------------- * Specialization for when there are no arguments given. */ void PushParameters() { //... } /* -------------------------------------------------------------------------------------------- * Specialization for when there's only one argument given/remaining. */ template < typename T > void PushParameters(T v) { Var< T >::push(SqVM(), v); } /* -------------------------------------------------------------------------------------------- * Specialization for when there's more than one argument given. */ template < typename T, typename... Args > void PushParameters(T v, Args... args) { Var< T >::push(SqVM(), v); PushParameters(args...); } public: /* -------------------------------------------------------------------------------------------- * Terminate all signal instances and release any script resources. */ static void Terminate(); /* -------------------------------------------------------------------------------------------- * Create a free signal without a specific name. */ SQMOD_NODISCARD static LightObj CreateFree(); /* -------------------------------------------------------------------------------------------- * Create a new signal with the specified name. */ SQMOD_NODISCARD static LightObj Create(StackStrF & name); /* -------------------------------------------------------------------------------------------- * Remove the signal with the specified name. */ static void Remove(StackStrF & name); /* -------------------------------------------------------------------------------------------- * Retrieve the signal with the specified name. */ SQMOD_NODISCARD static const LightObj & Fetch(StackStrF & name); /* -------------------------------------------------------------------------------------------- * Emit a signal from the module. */ template < typename... Args > void operator () (Args&&... args) { // Are there any slots connected? if (!m_Used) return; // Enter a new execution scope Scope scope(m_Scope, m_Slots, m_Slots + m_Used); // Activate the current scope and create a guard to restore it const AutoAssign< Scope * > aa(m_Scope, scope.mParent, &scope); // Grab the default virtual machine HSQUIRRELVM vm = SqVM(); // Process the slots from this scope while (scope.mItr != scope.mEnd) { // Grab a reference to the current slot const Slot & slot = *(scope.mItr++); // Push the callback object sq_pushobject(vm, slot.mFuncRef); // Is there an explicit environment? if (slot.mThisHash == 0) { sq_pushroottable(vm); } else { sq_pushobject(vm, slot.mThisRef); } // Push the given parameters on the stack PushParameters(args...); // Make the function call and store the result const SQRESULT res = sq_call(vm, 1 + sizeof...(Args), static_cast< SQBool >(false), static_cast< SQBool >(ErrorHandling::IsEnabled())); // Pop the callback object from the stack sq_pop(vm, 1); // Validate the result if (SQ_FAILED(res)) { SQTHROW(vm, LastErrorString(vm)); // Stop emitting signals NOLINT(hicpp-exception-baseclass,cert-err60-cpp) } } } }; } // Namespace:: SqMod