#ifndef _MISC_SIGNAL_HPP_ #define _MISC_SIGNAL_HPP_ // ------------------------------------------------------------------------------------------------ #include "Base/Shared.hpp" // ------------------------------------------------------------------------------------------------ #include #include #include #include // ------------------------------------------------------------------------------------------------ namespace SqMod { // ------------------------------------------------------------------------------------------------ class Signal; /* ------------------------------------------------------------------------------------------------ * Helper class used to reference and keep track of signal instances. */ struct SignalReference { // -------------------------------------------------------------------------------------------- Signal * mPtr; // A raw pointer to the signal instance. Object mObj; // A managed reference to the signal instance. /* -------------------------------------------------------------------------------------------- * Explicit constructor. */ SignalReference(Signal * ptr, const Object & ref) : mPtr(ptr), mObj(ref) { /* ... */ } /* -------------------------------------------------------------------------------------------- * Copy constructor. */ SignalReference(const SignalReference & o) = default; /* -------------------------------------------------------------------------------------------- * Move constructor. */ SignalReference(SignalReference && o) = default; /* -------------------------------------------------------------------------------------------- * Destructor. */ ~SignalReference() = default; /* -------------------------------------------------------------------------------------------- * Copy assignment operator. */ SignalReference & operator = (const SignalReference & o) = default; /* -------------------------------------------------------------------------------------------- * Move assignment operator. */ SignalReference & operator = (SignalReference && o) = default; }; /* ------------------------------------------------------------------------------------------------ * Utility class for for delivering events to one or more listeners. */ class Signal { private: /* -------------------------------------------------------------------------------------------- * Used to store a script callback and the unique hash associated with it for quick searching. */ struct Slot { // ---------------------------------------------------------------------------------------- SQHash mEnvHash; // The hash of the specified environment. SQHash mFuncHash; // The hash of the specified callback. HSQOBJECT mEnvRef; // The specified script environment. HSQOBJECT mFuncRef; // The specified script callback. // ---------------------------------------------------------------------------------------- Slot * mPrev; // Previous slot in the chain. Slot * mNext; // Next slot in the chain. /* ---------------------------------------------------------------------------------------- * Forwarding constructor. */ Slot(Object & env, Function & func) : Slot(env.GetObject(), func.GetFunc(), nullptr) { /* ... */ } /* ---------------------------------------------------------------------------------------- * Forwarding constructor. */ Slot(Object & env, Function & func, Slot * head) : Slot(env.GetObject(), func.GetFunc(), head) { /* ... */ } /* ---------------------------------------------------------------------------------------- * Base constructor. */ Slot(HSQOBJECT & env, HSQOBJECT & func, Slot * head) : mEnvHash(0) , mFuncHash(0) , mEnvRef(env) , mFuncRef(func) , mPrev(nullptr) , mNext(head) { HSQUIRRELVM vm = DefaultVM::Get(); // Remember the current stack size const StackGuard sg(vm); // Is there an explicit environment? if (!sq_isnull(mEnvRef)) { // Keep a reference to this environment sq_addref(vm, &mEnvRef); // Push the environment on the stack sq_pushobject(vm, mEnvRef); // Grab the hash of the environment object mEnvHash = 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); } // Was there a previous head? if (mNext != nullptr) { // Steal the slot before the head mPrev = mNext->mPrev; // Did that head slot had a slot behind it? if (mPrev != nullptr) { // Tell it we're the next slot now mPrev->mNext = this; } // Tell the previous head that we're behind it now mNext->mPrev = this; } } /* ---------------------------------------------------------------------------------------- * Copy constructor. (disabled) */ Slot(const Slot & o) = delete; /* ---------------------------------------------------------------------------------------- * Move constructor. */ Slot(Slot && o) : mEnvHash(o.mEnvHash) , mFuncHash(o.mFuncHash) , mEnvRef(o.mEnvRef) , mFuncRef(o.mFuncRef) , mPrev(o.mPrev) , mNext(o.mNext) { // Take ownership sq_resetobject(&o.mEnvRef); sq_resetobject(&o.mFuncRef); o.mPrev = nullptr; o.mNext = nullptr; // Attach to the chain Attach(); } /* ---------------------------------------------------------------------------------------- * Destructor. */ ~Slot() { Release(); // Detach from the chain Detach(); } /* ---------------------------------------------------------------------------------------- * Copy assignment operator. (disabled) */ Slot & operator = (const Slot & o) = delete; /* ---------------------------------------------------------------------------------------- * Move assignment operator. */ Slot & operator = (Slot && o) { if (this != &o) { // Release current resources, if any Release(); // Replicate data mEnvHash = o.mEnvHash; mFuncHash = o.mFuncHash; mEnvRef = o.mEnvRef; mFuncRef = o.mFuncRef; mPrev = o.mPrev; mNext = o.mNext; // Take ownership sq_resetobject(&o.mEnvRef); sq_resetobject(&o.mFuncRef); o.mPrev = nullptr; o.mNext = nullptr; // Attach to the chain Attach(); } return *this; } /* ---------------------------------------------------------------------------------------- * Equality comparison operator. */ bool operator == (const Slot & o) const { return (mEnvHash == o.mEnvHash) && (mFuncHash == o.mFuncHash); } /* ---------------------------------------------------------------------------------------- * Inequality comparison operator. */ bool operator != (const Slot & o) const { return (mEnvHash != o.mEnvHash) || (mFuncHash != o.mFuncHash); } /* ---------------------------------------------------------------------------------------- * Release managed script resources. */ void Release() { // Should we release any environment object? if (!sq_isnull(mEnvRef)) { sq_release(DefaultVM::Get(), &mEnvRef); sq_resetobject(&mEnvRef); } // Should we release any callback object? if (!sq_isnull(mFuncRef)) { sq_release(DefaultVM::Get(), &mFuncRef); sq_resetobject(&mFuncRef); } } /* ---------------------------------------------------------------------------------------- * Attach the slot to the chain. */ void Attach() { // Is there a slot after us? if (mNext != nullptr) { // Tell it that we're behind it mNext->mPrev = this; } // Is there a node behind us? if (mPrev != nullptr) { // Tell it that we're ahead of it mPrev->mNext = this; } } /* ---------------------------------------------------------------------------------------- * Detach the slot from the chain. */ void Detach() { // Is there a slot after us? if (mNext != nullptr) { // Tell it we're no longer behind it mNext->mPrev = mPrev; } // Is there a node behind us? if (mPrev != nullptr) { // Tell it we're no longer ahead of it mPrev->mNext = mNext; } mPrev = mNext = nullptr; } /* ---------------------------------------------------------------------------------------- * Attach the slot after another slot. */ void AttachNext(Slot * node) { Detach(); // Assign the slot ahead of us mNext = node; // Is there a node to attach to? if (mNext != nullptr) { // Steal the previous slot mPrev = mNext->mPrev; // Was there a previous slot? if (mPrev != nullptr) { mPrev->mNext = this; // Tell it we're the next slot now } // Place ourself behind mNext->mPrev = this; } } /* ---------------------------------------------------------------------------------------- * Attach the slot before another slot. */ void AttachPrev(Slot * node) { Detach(); // Assign the slot behind us mPrev = node; // Is there a node to attach to? if (mPrev != nullptr) { // Steal the next slot mNext = mPrev->mNext; // Was there a next slot? if (mNext != nullptr) { mNext->mPrev = this; // Tell it we're the previous slot now } // Place ourself ahead mPrev->mNext = this; } } }; // -------------------------------------------------------------------------------------------- Slot * m_Head; // The chain of slots bound to this signal. Object m_Data; // User data associated with this instance. String m_Name; // The name that identifies this signal. /* -------------------------------------------------------------------------------------------- * Default constructor. */ Signal(); /* -------------------------------------------------------------------------------------------- * Base constructor. */ explicit Signal(const CSStr name); /* -------------------------------------------------------------------------------------------- * Base constructor. */ Signal(const String & name); /* -------------------------------------------------------------------------------------------- * Base constructor. */ Signal(String && name); public: /* -------------------------------------------------------------------------------------------- * 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; /* -------------------------------------------------------------------------------------------- * Used by the script engine to convert an instance of this type to a string. */ const String & ToString() const { return m_Name; } /* -------------------------------------------------------------------------------------------- * Retrieve the associated user data. */ Object & GetData() { return m_Data; } /* -------------------------------------------------------------------------------------------- * Modify the associated user data. */ void SetData(Object & data) { m_Data = data; } /* -------------------------------------------------------------------------------------------- * The number of slots connected to the signal. */ SQInteger Count() const; /* -------------------------------------------------------------------------------------------- * Clear all slots connected to the signal. */ void Clear(); /* -------------------------------------------------------------------------------------------- * Connect a function with a specific environment to the signal. */ void Connect(Object & env, Function & func); /* -------------------------------------------------------------------------------------------- * See whether a function with a specific environment is connected to the signal. */ bool Connected(Object & env, Function & func) const; /* -------------------------------------------------------------------------------------------- * Disconnect a function with a specific environment from the signal. */ void Disconnect(Object & env, Function & func); /* -------------------------------------------------------------------------------------------- * Disconnect all functions with a specific environment from the signal. */ void DisconnectThis(Object & env); /* -------------------------------------------------------------------------------------------- * Disconnect all matching functions regardless of the environment from the signal. */ void DisconnectFunc(Function & func); /* -------------------------------------------------------------------------------------------- * Count all functions with a specific environment from the signal. */ Uint32 CountThis(Object & env) const; /* -------------------------------------------------------------------------------------------- * Count all matching functions regardless of the environment from the signal. */ Uint32 CountFunc(Function & func) const; /* -------------------------------------------------------------------------------------------- * Make sure that specified slot is positioned ahead of all other slots. */ void Head(Object & env, Function & func); /* -------------------------------------------------------------------------------------------- * Make sure that specified slot is positioned behind of all other slots. */ void Tail(Object & env, Function & func); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots. */ static SQInteger SqEmit(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots and collect returned values. */ static SQInteger SqQuery(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots and see if they consume it. */ static SQInteger SqConsume(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots and see if they approve it. */ static SQInteger SqApprove(HSQUIRRELVM vm); /* -------------------------------------------------------------------------------------------- * Emit the event to the connected slots and see if they return something. */ static SQInteger SqRequest(HSQUIRRELVM vm); private: // -------------------------------------------------------------------------------------------- typedef std::pair< std::size_t, SignalReference > SignalElement; typedef std::vector< SignalElement > SignalContainer; typedef std::vector< Signal * > FreeSignals; // -------------------------------------------------------------------------------------------- static SignalContainer s_Signals; // List of all created signals. static FreeSignals s_FreeSignals; // List of signals without a name. public: /* -------------------------------------------------------------------------------------------- * Terminate all signal instances and release any script resources. */ static void Terminate(); /* -------------------------------------------------------------------------------------------- * Create a free signal without a specific name. */ static Object CreateFree(); /* -------------------------------------------------------------------------------------------- * Create a new signal with the specified name. */ static Object Create(StackStrF & name); /* -------------------------------------------------------------------------------------------- * Remove the signal with the specified name. */ static void Remove(StackStrF & name); /* -------------------------------------------------------------------------------------------- * Retrieve the signal with the specified name. */ static Object Fetch(StackStrF & name); }; } // Namespace:: SqMod #endif // _MISC_SIGNAL_HPP_