1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00

Initial implementation of the built-in event system based on the signals and slots design.

This commit is contained in:
Sandu Liviu Catalin 2016-10-30 18:26:37 +02:00
parent ad876f6a8f
commit b946e7a9b9
4 changed files with 1260 additions and 0 deletions

View File

@ -46,6 +46,7 @@ extern bool RegisterAPI(HSQUIRRELVM vm);
extern void InitializeRoutines();
extern void TerminateRoutines();
extern void TerminateCommands();
extern void TerminateSignals();
// ------------------------------------------------------------------------------------------------
extern Buffer GetRealFilePath(CSStr path);
@ -481,6 +482,8 @@ void Core::Terminate(bool shutdown)
TerminateRoutines();
// Release all resources from command managers
TerminateCommands();
// Release all resources from signals
TerminateSignals();
// In case there's a payload for reload
m_ReloadPayload.Release();
// Release null objects in case any reference to valid objects is stored in them

280
source/Misc/Signal.cpp Normal file
View File

@ -0,0 +1,280 @@
// ------------------------------------------------------------------------------------------------
#include "Misc/Signal.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
Signal::SignalContainer Signal::s_Signals;
Signal::FreeSignals Signal::s_FreeSignals;
// ------------------------------------------------------------------------------------------------
SQInteger Signal::Typename(HSQUIRRELVM vm)
{
static const SQChar name[] = _SC("SqSignal");
sq_pushstring(vm, name, sizeof(name));
return 1;
}
// ------------------------------------------------------------------------------------------------
void Signal::Terminate()
{
// Terminate named signals
for (const auto & s : s_Signals)
{
// Clear slots
s.second.mPtr->Clear();
// Release the name
s.second.mPtr->m_Name.clear();
}
// Finally clear the container itself
s_Signals.clear();
// Create a copy so we don't invalidate iterators when destructor removes the instances
FreeSignals fsig(s_FreeSignals);
// Terminate anonymous signals
for (const auto & s : fsig)
{
// Clear slots
s->Clear();
}
// Finally clear the container itself
s_FreeSignals.clear();
}
// ------------------------------------------------------------------------------------------------
Object Signal::Create()
{
// Remember the current stack size
const StackGuard sg;
// Create the signal instance
DeleteGuard< Signal > dg(new Signal());
// Attempt to create the signal instance
ClassType< Signal >::PushInstance(DefaultVM::Get(), dg.Get());
// This is now managed by the script
dg.Release();
// Return the created signal
return Var< Object >(DefaultVM::Get(), -1).value;
}
// ------------------------------------------------------------------------------------------------
Object Signal::Create(String name)
{
// Validate the signal name
if (name.empty())
{
STHROWF("Empty signal names are not allowed");
}
// Compute the hash of the specified name
const std::size_t hash = std::hash< String >{}(name);
// See if the signal already exists
for (const auto & e : s_Signals)
{
if (e.first == hash)
{
return e.second.mObj; // Found a match so let's return it
}
}
// Remember the current stack size
const StackGuard sg;
// Create the signal instance
DeleteGuard< Signal > dg(new Signal(std::move(name)));
// Grab the signal instance pointer
Signal * ptr = dg.Get();
// Attempt to create the signal instance
ClassType< Signal >::PushInstance(DefaultVM::Get(), ptr);
// This is now managed by the script
dg.Release();
// Grab a reference to the instance created on the stack
s_Signals.emplace_back(hash, SignalReference(ptr, Var< Object >(DefaultVM::Get(), -1).value));
// Return the created signal
return s_Signals.back().second.mObj;
}
// ------------------------------------------------------------------------------------------------
void Signal::Remove(String name)
{
// Validate the signal name
if (name.empty())
{
STHROWF("Empty signal names are not allowed");
}
// Compute the hash of the specified name
const std::size_t hash = std::hash< String >{}(name);
// Iterator to the existing signal, if any
SignalContainer::const_iterator itr = s_Signals.cbegin();
// Search for a signal with this name
for (; itr != s_Signals.cend(); ++itr)
{
if (itr->first == hash)
{
break;
}
}
// Did we find anything?
if (itr != s_Signals.cend())
{
// Clear the name
itr->second.mPtr->m_Name.clear();
// Put it on the free list
s_FreeSignals.push_back(itr->second.mPtr);
// Finally, remove it from the named list
s_Signals.erase(itr);
}
}
// ------------------------------------------------------------------------------------------------
Object Signal::Fetch(String name)
{
// Validate the signal name
if (name.empty())
{
STHROWF("Empty signal names are not allowed");
}
// Compute the hash of the specified name
const std::size_t hash = std::hash< String >{}(name);
// Search for a signal with this name
for (const auto & e : s_Signals)
{
if (e.first == hash)
{
return e.second.mObj; // Found a match so let's return it
}
}
// No such signal exists
STHROWF("Unknown signal named (%s)", name.c_str());
// SHOULD NOT REACH THIS POINT!
return NullObject();
}
// ------------------------------------------------------------------------------------------------
static SQInteger SqCreateSignal(HSQUIRRELVM vm)
{
const Int32 top = sq_gettop(vm);
// Was the signal name specified?
if (top <= 1)
{
sq_pushobject(DefaultVM::Get(), Signal::Create());
}
else
{
// Attempt to generate the string value
StackStrF val(vm, 2);
// Have we failed to retrieve the string?
if (SQ_FAILED(val.mRes))
{
return val.mRes; // Propagate the error!
}
// Create the signal instance and push it on the stack
try
{
sq_pushobject(DefaultVM::Get(), Signal::Create(String(val.mPtr, val.mLen)));
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
}
// We have an argument on the stack
return 1;
}
// ------------------------------------------------------------------------------------------------
static SQInteger SqRemoveSignal(HSQUIRRELVM vm)
{
const Int32 top = sq_gettop(vm);
// Was the signal name specified?
if (top <= 1)
{
return sq_throwerror(vm, "Missing signal name");
}
// Attempt to generate the string value
StackStrF val(vm, 2);
// Have we failed to retrieve the string?
if (SQ_FAILED(val.mRes))
{
return val.mRes; // Propagate the error!
}
// Create the signal instance and push it on the stack
try
{
Signal::Remove(String(val.mPtr, val.mLen));
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// We have an argument on the stack
return 1;
}
// ------------------------------------------------------------------------------------------------
static SQInteger SqFetchSignal(HSQUIRRELVM vm)
{
const Int32 top = sq_gettop(vm);
// Was the signal name specified?
if (top <= 1)
{
return sq_throwerror(vm, "Missing signal name");
}
// Attempt to generate the string value
StackStrF val(vm, 2);
// Have we failed to retrieve the string?
if (SQ_FAILED(val.mRes))
{
return val.mRes; // Propagate the error!
}
// Create the signal instance and push it on the stack
try
{
sq_pushobject(DefaultVM::Get(), Signal::Fetch(String(val.mPtr, val.mLen)));
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// We have an argument on the stack
return 1;
}
// ================================================================================================
void Register_Signal(HSQUIRRELVM vm)
{
RootTable(vm).Bind(_SC("SqSignalWrapper"),
Class< Signal, NoConstructor< Signal > >(vm, _SC("SqSignalWrapper"))
// Meta-methods
.Func(_SC("_cmp"), &Signal::Cmp)
.SquirrelFunc(_SC("_typename"), &Signal::Typename)
.Func(_SC("_tostring"), &Signal::ToString)
// Core Properties
.Prop(_SC("Slots"), &Signal::Count)
// Core Methods
.Func(_SC("Clear"), &Signal::Clear)
.Func(_SC("Connect"), &Signal::Connect)
.Func(_SC("Connected"), &Signal::Connected)
.Func(_SC("Disconnect"), &Signal::Disconnect)
.Func(_SC("EliminateThis"), &Signal::DisconnectThis)
.Func(_SC("EliminateFunc"), &Signal::DisconnectFunc)
.Func(_SC("CountThis"), &Signal::CountThis)
.Func(_SC("CountFunc"), &Signal::CountFunc)
// Squirrel Functions
.SquirrelFunc(_SC("Emit"), &Signal::SqEmit)
.SquirrelFunc(_SC("Broadcast"), &Signal::SqEmit)
.SquirrelFunc(_SC("Query"), &Signal::SqQuery)
.SquirrelFunc(_SC("Consume"), &Signal::SqConsume)
);
RootTable(vm)
.SquirrelFunc(_SC("SqSignal"), &SqFetchSignal)
.SquirrelFunc(_SC("SqCreateSignal"), &SqCreateSignal)
.SquirrelFunc(_SC("SqRemoveSignal"), &SqRemoveSignal);
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to terminate the signals.
*/
void TerminateSignals()
{
Signal::Terminate();
}
} // Namespace:: SqMod

975
source/Misc/Signal.hpp Normal file
View File

@ -0,0 +1,975 @@
#ifndef _MISC_SIGNAL_HPP_
#define _MISC_SIGNAL_HPP_
// ------------------------------------------------------------------------------------------------
#include "Base/Shared.hpp"
// ------------------------------------------------------------------------------------------------
#include <vector>
#include <utility>
#include <algorithm>
#include <functional>
// ------------------------------------------------------------------------------------------------
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);
}
/* ----------------------------------------------------------------------------------------
* Less than comparison operator.
*/
bool operator < (const Slot & o) const
{
return (mEnvHash < o.mEnvHash) && (mFuncHash < o.mFuncHash);
}
/* ----------------------------------------------------------------------------------------
* Greater than comparison operator.
*/
bool operator > (const Slot & o) const
{
return (mEnvHash > o.mEnvHash) && (mFuncHash > o.mFuncHash);
}
/* ----------------------------------------------------------------------------------------
* Less than or equal comparison operator.
*/
bool operator <= (const Slot & o) const
{
return (mEnvHash <= o.mEnvHash) && (mFuncHash <= o.mFuncHash);
}
/* ----------------------------------------------------------------------------------------
* Greater than or equal 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;
}
}
};
// --------------------------------------------------------------------------------------------
Slot * m_Head; // The chain of slots bound to this signal.
String m_Name; // The name that identifies this signal.
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Signal()
: m_Head(nullptr), m_Name()
{
s_FreeSignals.push_back(this);
}
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
Signal(const CSStr name)
: Signal(String(name ? name : _SC("")))
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
Signal(const String & name)
: Signal(String(name))
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
Signal(String && name)
: m_Head(nullptr), m_Name(std::move(name))
{
if (m_Name.empty())
{
s_FreeSignals.push_back(this);
}
}
public:
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Signal(const Signal & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Signal(Signal && o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~Signal()
{
Clear();
if (m_Name.empty())
{
s_FreeSignals.erase(std::remove(s_FreeSignals.begin(), s_FreeSignals.end(), this),
s_FreeSignals.end());
}
}
/* --------------------------------------------------------------------------------------------
* 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 compare two instances of this type.
*/
Int32 Cmp(const Signal & o) const
{
if (m_Name == o.m_Name)
{
return 0;
}
else if (m_Name > o.m_Name)
{
return 1;
}
else
{
return -1;
}
}
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert an instance of this type to a string.
*/
const String & ToString() const
{
return m_Name;
}
/* --------------------------------------------------------------------------------------------
* Used by the script engine to retrieve the name from instances of this type.
*/
static SQInteger Typename(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* The number of slots connected to the signal.
*/
SQInteger Count() const
{
// Don't attempt to count anything if there's no head
if (m_Head == nullptr)
{
return 0;
}
// Final count
Uint32 count = 0;
// Walk down the chain and count all nodes
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
{
++count;
}
// Return the count
return count;
}
/* --------------------------------------------------------------------------------------------
* Clear all slots connected to the signal.
*/
void Clear()
{
// Don't attempt to clear anything if there's no head
if (m_Head == nullptr)
{
return;
}
// Walk down the chain and delete all nodes
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
{
// Grab the next node upfront
next = node->mNext;
// Delete the node instance
delete node;
}
// Reset the head
m_Head = nullptr;
}
/* --------------------------------------------------------------------------------------------
* Connect a function with a specific environment to the signal.
*/
void Connect(Object & env, Function & func)
{
// Don't attempt to search anything if there's no head
if (m_Head == nullptr)
{
m_Head = new Slot(env, func, nullptr);
// We're done here
return;
}
Slot slot{env, func};
// Walk down the chain and find an already matching node
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
{
if (*node == slot)
{
return; // Already connected
}
}
// Connect now
m_Head = new Slot(env, func, m_Head);
}
/* --------------------------------------------------------------------------------------------
* See whether a function with a specific environment is connected to the signal.
*/
bool Connected(Object & env, Function & func) const
{
// Don't attempt to search anything if there's no head
if (m_Head == nullptr)
{
return false;
}
const Slot slot{env, func};
// Walk down the chain and find a matching node
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
{
if (*node == slot)
{
return true; // Found it
}
}
// No such slot exists
return false;
}
/* --------------------------------------------------------------------------------------------
* Disconnect a function with a specific environment from the signal.
*/
void Disconnect(Object & env, Function & func)
{
// Don't attempt to search anything if there's no head
if (m_Head == nullptr)
{
return;
}
const Slot slot{env, func};
// Walk down the chain and remove the matching nodes
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
{
// Grab the next node upfront
next = node->mNext;
// Is this our node?
if (*node == slot)
{
// Is this the head?
if (node == m_Head)
{
m_Head = next; // Move the head to the next one
}
// Detach it from the chain
node->Detach();
// Delete the node instance
delete node;
}
}
}
/* --------------------------------------------------------------------------------------------
* Disconnect all functions with a specific environment from the signal.
*/
void DisconnectThis(Object & env)
{
// Don't attempt to search anything if there's no head
if (m_Head == nullptr)
{
return;
}
const SQHash hash = Slot{env, NullFunction()}.mEnvHash;
// Walk down the chain and remove the matching nodes
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
{
// Grab the next node upfront
next = node->mNext;
// Is this our node?
if (node->mEnvHash == hash)
{
// Is this the head?
if (node == m_Head)
{
m_Head = next; // Move the head to the next one
}
// Detach it from the chain
node->Detach();
// Delete the node instance
delete node;
}
}
}
/* --------------------------------------------------------------------------------------------
* Disconnect all matching functions regardless of the environment from the signal.
*/
void DisconnectFunc(Function & func)
{
// Don't attempt to search anything if there's no head
if (m_Head == nullptr)
{
return;
}
const SQHash hash = Slot{NullObject(), func}.mFuncHash;
// Walk down the chain and remove the matching nodes
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
{
// Grab the next node upfront
next = node->mNext;
// Is this our node?
if (node->mFuncHash == hash)
{
// Is this the head?
if (node == m_Head)
{
m_Head = next; // Move the head to the next one
}
// Detach it from the chain
node->Detach();
// Delete the node instance
delete node;
}
}
}
/* --------------------------------------------------------------------------------------------
* Count all functions with a specific environment from the signal.
*/
Uint32 CountThis(Object & env) const
{
// Don't attempt to count anything if there's no head
if (m_Head == nullptr)
{
return 0;
}
const SQHash hash = Slot{env, NullFunction()}.mEnvHash;
// Final count
Uint32 count = 0;
// Walk down the chain and count the matching nodes
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
{
// Is this our node?
if (node->mEnvHash == hash)
{
++count;
}
}
// Return the count
return count;
}
/* --------------------------------------------------------------------------------------------
* Count all matching functions regardless of the environment from the signal.
*/
Uint32 CountFunc(Function & func) const
{
// Don't attempt to count anything if there's no head
if (m_Head == nullptr)
{
return 0;
}
const SQHash hash = Slot{NullObject(), func}.mFuncHash;
// Final count
Uint32 count = 0;
// Walk down the chain and count the matching nodes
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
{
// Is this our node?
if (node->mFuncHash == hash)
{
++count;
}
}
// Return the count
return count;
}
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots.
*/
static SQInteger SqEmit(HSQUIRRELVM vm)
{
const Int32 top = sq_gettop(vm);
// The signal instance
Signal * signal = nullptr;
// Attempt to extract the signal instance
try
{
signal = Var< Signal * >(vm, 1).value;
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// Do we have a valid signal instance?
if (!signal)
{
return sq_throwerror(vm, "Invalid signal instance");
}
// Walk down the chain and trigger slots
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
{
// Grab the next node upfront
next = node->mNext;
// Remember the current stack size
const StackGuard sg(vm);
// Push the callback object
sq_pushobject(vm, node->mFuncRef);
// Is there an explicit environment?
if (sq_isnull(node->mEnvRef))
{
sq_pushroottable(vm);
}
else
{
sq_pushobject(vm, node->mEnvRef);
}
// Are there any parameters to forward?
if (top > 1)
{
for (SQInteger i = 2; i <= top; ++i)
{
sq_push(vm, i);
}
}
// Make the function call and store the result
const SQRESULT res = sq_call(vm, top, false, ErrorHandling::IsEnabled());
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
}
// Specify that we don't return anything
return 0;
}
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and collect returned values.
*/
static SQInteger SqQuery(HSQUIRRELVM vm)
{
const Int32 top = sq_gettop(vm);
// Do we have the collector environment?
if (top <= 1)
{
return sq_throwerror(vm, "Missing collector environment");
}
// Do we have the collector function?
else if (top <= 2)
{
return sq_throwerror(vm, "Missing collector callback");
}
// The signal instance
Signal * signal = nullptr;
// Attempt to extract the signal instance and collector
try
{
signal = Var< Signal * >(vm, 1).value;
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// Do we have a valid signal instance?
if (!signal)
{
return sq_throwerror(vm, "Invalid signal instance");
}
// The collector
HSQOBJECT cenv, cfunc;
// Grab the collector environment
SQRESULT res = sq_getstackobj(vm, 2, &cenv);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Was there a valid environment?
else if (sq_isnull(cenv))
{
// Remember the current stack size
const StackGuard sg(vm);
// Default to the root table
sq_pushroottable(vm);
// Try to grab the collector environment again
SQRESULT res = sq_getstackobj(vm, -1, &cenv);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
}
// Grab the collector function
res = sq_getstackobj(vm, 3, &cfunc);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Some dummy checks to make sure the collector is a callable object
else if (!sq_isfunction(cfunc) && !sq_isclosure(cfunc) && !sq_isnativeclosure(cfunc))
{
return sq_throwerror(vm, "Invalid collector");
}
// Walk down the chain and trigger slots
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
{
// Grab the next node upfront
next = node->mNext;
// Remember the current stack size
const StackGuard sg(vm);
// Push the callback object
sq_pushobject(vm, node->mFuncRef);
// Is there an explicit environment?
if (sq_isnull(node->mEnvRef))
{
sq_pushroottable(vm);
}
else
{
sq_pushobject(vm, node->mEnvRef);
}
// Are there any parameters to forward?
if (top > 3)
{
for (SQInteger i = 4; i <= top; ++i)
{
sq_push(vm, i);
}
}
// Make the function call and store the result
res = sq_call(vm, top - 2, true, ErrorHandling::IsEnabled());
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Push the collector onto the stack
sq_pushobject(vm, cfunc);
sq_pushobject(vm, cenv);
// Push the returned value
sq_push(vm, -3);
// Make the function call and store the result
res = sq_call(vm, 2, false, ErrorHandling::IsEnabled());
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
}
// Specify that we don't return anything
return 0;
}
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and see if they consume it.
*/
static SQInteger SqConsume(HSQUIRRELVM vm)
{
const Int32 top = sq_gettop(vm);
// The signal instance
Signal * signal = nullptr;
// Attempt to extract the signal instance
try
{
signal = Var< Signal * >(vm, 1).value;
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// Do we have a valid signal instance?
if (!signal)
{
return sq_throwerror(vm, "Invalid signal instance");
}
// Whether this signal was consumed
SQBool consumed = SQFalse;
// Walk down the chain and trigger slots
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
{
// Grab the next node upfront
next = node->mNext;
// Remember the current stack size
const StackGuard sg(vm);
// Push the callback object
sq_pushobject(vm, node->mFuncRef);
// Is there an explicit environment?
if (sq_isnull(node->mEnvRef))
{
sq_pushroottable(vm);
}
else
{
sq_pushobject(vm, node->mEnvRef);
}
// Are there any parameters to forward?
if (top > 1)
{
for (SQInteger i = 2; i <= top; ++i)
{
sq_push(vm, i);
}
}
// Make the function call and store the result
const SQRESULT res = sq_call(vm, top, true, ErrorHandling::IsEnabled());
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Obtain the returned value
sq_tobool(vm, -1, &consumed);
// Should we proceed to the next slot or stop here?
if (consumed == SQTrue)
{
break; // This signal was consumed
}
}
// Specify that this signal was consumed
sq_pushbool(vm, consumed);
// Specify that we returned something
return 1;
}
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 Create();
/* --------------------------------------------------------------------------------------------
* Create a new signal with the specified name.
*/
static Object Create(String name);
/* --------------------------------------------------------------------------------------------
* Remove the signal with the specified name.
*/
static void Remove(String name);
/* --------------------------------------------------------------------------------------------
* Retrieve the signal with the specified name.
*/
static Object Fetch(String name);
};
} // Namespace:: SqMod
#endif // _MISC_SIGNAL_HPP_

View File

@ -52,6 +52,7 @@ extern void Register_Routine(HSQUIRRELVM vm);
// ------------------------------------------------------------------------------------------------
extern void Register_Misc(HSQUIRRELVM vm);
extern void Register_Signal(HSQUIRRELVM vm);
// ------------------------------------------------------------------------------------------------
bool RegisterAPI(HSQUIRRELVM vm)
@ -92,6 +93,7 @@ bool RegisterAPI(HSQUIRRELVM vm)
Register_Routine(vm);
Register_Misc(vm);
Register_Signal(vm);
return true;
}