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

Move some of the signal implementation to source file.

Don't combine the Consume and Approve methods.
The null value does not count towards returned values anymore.
This commit is contained in:
Sandu Liviu Catalin 2016-11-13 14:50:04 +02:00
parent df12603405
commit da76d87a93
2 changed files with 795 additions and 674 deletions

View File

@ -150,6 +150,780 @@ Object Signal::Fetch(String name)
return NullObject();
}
// ------------------------------------------------------------------------------------------------
Signal::Signal()
: m_Head(nullptr), m_Data(), m_Name()
{
s_FreeSignals.push_back(this);
}
// ------------------------------------------------------------------------------------------------
Signal::Signal(const CSStr name)
: Signal(String(name ? name : _SC("")))
{
/* ... */
}
// ------------------------------------------------------------------------------------------------
Signal::Signal(const String & name)
: Signal(String(name))
{
/* ... */
}
// ------------------------------------------------------------------------------------------------
Signal::Signal(String && name)
: m_Head(nullptr), m_Data(), m_Name(std::move(name))
{
if (m_Name.empty())
{
s_FreeSignals.push_back(this);
}
}
// ------------------------------------------------------------------------------------------------
Signal::~Signal()
{
Clear();
if (m_Name.empty())
{
s_FreeSignals.erase(std::remove(s_FreeSignals.begin(), s_FreeSignals.end(), this),
s_FreeSignals.end());
}
}
// ------------------------------------------------------------------------------------------------
SQInteger Signal::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;
}
// ------------------------------------------------------------------------------------------------
void Signal::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;
}
// ------------------------------------------------------------------------------------------------
void Signal::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;
}
const Slot slot{env, func};
// Don't attempt to search anything if there's only one element
if (m_Head->mNext == nullptr)
{
// Is it already inserted?
if (*m_Head != slot)
{
m_Head = new Slot(env, func, m_Head);
}
// We're done here
return;
}
// 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);
}
// ------------------------------------------------------------------------------------------------
bool Signal::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;
}
// ------------------------------------------------------------------------------------------------
void Signal::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;
}
}
}
// ------------------------------------------------------------------------------------------------
void Signal::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;
}
}
}
// ------------------------------------------------------------------------------------------------
void Signal::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;
}
}
}
// ------------------------------------------------------------------------------------------------
Uint32 Signal::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;
}
// ------------------------------------------------------------------------------------------------
Uint32 Signal::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;
}
// ------------------------------------------------------------------------------------------------
void Signal::Head(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;
}
const Slot slot{env, func};
// Don't attempt to search anything if there's only one element
if (m_Head->mNext == nullptr)
{
// Is it already inserted?
if (*m_Head != slot)
{
m_Head = new Slot(env, func, m_Head);
}
// We're done here
return;
}
// Grab the head node
Slot * node = m_Head;
// Walk down the chain and find a matching node
for (; node != nullptr; node = node->mNext)
{
if (*node == slot)
{
break; // Found it
}
}
// Have we found anything?
if (node == nullptr)
{
m_Head = new Slot(env, func, m_Head); // Lead everyone
}
// Is it the head already?
else if (m_Head != node)
{
node->AttachNext(m_Head);
// We're the head now
m_Head = node;
}
}
// ------------------------------------------------------------------------------------------------
void Signal::Tail(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;
}
const Slot slot{env, func};
// Don't attempt to search anything if there's only one element
if (m_Head->mNext == nullptr)
{
// Is it already inserted?
if (*m_Head != slot)
{
m_Head->mNext = new Slot(env, func, nullptr);
// Link with the head
m_Head->mNext->mPrev = m_Head;
}
// We're done here
return;
}
// Grab the head node
Slot * node = m_Head, * prev = nullptr;
// Walk down the chain and find a matching node
for (; node != nullptr; prev = node, node = node->mNext)
{
if (*node == slot)
{
break; // Found it
}
}
// Have we found anything?
if (node == nullptr)
{
// Create the slot now
node = new Slot(env, func, nullptr);
}
else
{
// Walk down the chain until the end
while (prev->mNext != nullptr)
{
prev = prev->mNext;
}
// Finally, detach the node from it's current position
node->Detach();
}
// Knowing 'prev' points to last element
node->AttachPrev(prev);
}
// ------------------------------------------------------------------------------------------------
SQInteger Signal::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;
}
// ------------------------------------------------------------------------------------------------
SQInteger Signal::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;
}
// ------------------------------------------------------------------------------------------------
SQInteger Signal::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");
}
// Default to not consumed
SQBool ret = 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
}
// Is the returned value not null?
else if (sq_gettype(vm, -1) != OT_NULL)
{
// Obtain the returned value
sq_tobool(vm, -1, &ret);
// Should we proceed to the next slot or stop here?
if (ret == SQTrue)
{
break; // The slot consumed the signal
}
}
}
// Forward the returned value to the invoker
sq_pushbool(vm, ret);
// Specify that we returned something
return 1;
}
// ------------------------------------------------------------------------------------------------
SQInteger Signal::SqApprove(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");
}
// Default to approved
SQBool ret = SQTrue;
// 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
}
// Is the returned value not null?
else if (sq_gettype(vm, -1) != OT_NULL)
{
// Obtain the returned value
sq_tobool(vm, -1, &ret);
// Should we proceed to the next slot or stop here?
if (ret == SQFalse)
{
break; // The slot did not approve the signal
}
}
}
// Forward the returned value to the invoker
sq_pushbool(vm, ret);
// Specify that we returned something
return 1;
}
// ------------------------------------------------------------------------------------------------
SQInteger Signal::SqRequest(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, true, ErrorHandling::IsEnabled());
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Is the returned value not null?
else if (sq_gettype(vm, -1) != OT_NULL)
{
// Push back the returned value
sq_push(vm, -1);
// Specify that we returned something
return 1;
}
}
// Specify that we returned nothing
return 0;
}
// ------------------------------------------------------------------------------------------------
static SQInteger SqCreateSignal(HSQUIRRELVM vm)
{

View File

@ -347,41 +347,22 @@ private:
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Signal()
: m_Head(nullptr), m_Data(), m_Name()
{
s_FreeSignals.push_back(this);
}
Signal();
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
Signal(const CSStr name)
: Signal(String(name ? name : _SC("")))
{
/* ... */
}
Signal(const CSStr name);
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
Signal(const String & name)
: Signal(String(name))
{
/* ... */
}
Signal(const String & name);
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
Signal(String && name)
: m_Head(nullptr), m_Data(), m_Name(std::move(name))
{
if (m_Name.empty())
{
s_FreeSignals.push_back(this);
}
}
Signal(String && name);
public:
@ -398,15 +379,7 @@ public:
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~Signal()
{
Clear();
if (m_Name.empty())
{
s_FreeSignals.erase(std::remove(s_FreeSignals.begin(), s_FreeSignals.end(), this),
s_FreeSignals.end());
}
}
~Signal();
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
@ -469,708 +442,82 @@ public:
/* --------------------------------------------------------------------------------------------
* 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;
}
SQInteger Count() const;
/* --------------------------------------------------------------------------------------------
* 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;
}
void Clear();
/* --------------------------------------------------------------------------------------------
* 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;
}
const Slot slot{env, func};
// Don't attempt to search anything if there's only one element
if (m_Head->mNext == nullptr)
{
// Is it already inserted?
if (*m_Head != slot)
{
m_Head = new Slot(env, func, m_Head);
}
// We're done here
return;
}
// 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);
}
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
{
// 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;
}
bool Connected(Object & env, Function & func) const;
/* --------------------------------------------------------------------------------------------
* 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;
}
}
}
void Disconnect(Object & env, Function & func);
/* --------------------------------------------------------------------------------------------
* 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;
}
}
}
void DisconnectThis(Object & env);
/* --------------------------------------------------------------------------------------------
* 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;
}
}
}
void DisconnectFunc(Function & func);
/* --------------------------------------------------------------------------------------------
* 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;
}
Uint32 CountThis(Object & env) const;
/* --------------------------------------------------------------------------------------------
* 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;
}
Uint32 CountFunc(Function & func) const;
/* --------------------------------------------------------------------------------------------
* Make sure that specified slot is positioned ahead of all other slots.
*/
void Head(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;
}
const Slot slot{env, func};
// Don't attempt to search anything if there's only one element
if (m_Head->mNext == nullptr)
{
// Is it already inserted?
if (*m_Head != slot)
{
m_Head = new Slot(env, func, m_Head);
}
// We're done here
return;
}
// Grab the head node
Slot * node = m_Head;
// Walk down the chain and find a matching node
for (; node != nullptr; node = node->mNext)
{
if (*node == slot)
{
break; // Found it
}
}
// Have we found anything?
if (node == nullptr)
{
m_Head = new Slot(env, func, m_Head); // Lead everyone
}
// Is it the head already?
else if (m_Head != node)
{
node->AttachNext(m_Head);
// We're the head now
m_Head = node;
}
}
void Head(Object & env, Function & func);
/* --------------------------------------------------------------------------------------------
* Make sure that specified slot is positioned behind of all other slots.
*/
void Tail(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;
}
const Slot slot{env, func};
// Don't attempt to search anything if there's only one element
if (m_Head->mNext == nullptr)
{
// Is it already inserted?
if (*m_Head != slot)
{
m_Head->mNext = new Slot(env, func, nullptr);
// Link with the head
m_Head->mNext->mPrev = m_Head;
}
// We're done here
return;
}
// Grab the head node
Slot * node = m_Head, * prev = nullptr;
// Walk down the chain and find a matching node
for (; node != nullptr; prev = node, node = node->mNext)
{
if (*node == slot)
{
break; // Found it
}
}
// Have we found anything?
if (node == nullptr)
{
// Create the slot now
node = new Slot(env, func, nullptr);
}
else
{
// Walk down the chain until the end
while (prev->mNext != nullptr)
{
prev = prev->mNext;
}
// Finally, detach the node from it's current position
node->Detach();
}
// Knowing 'prev' points to last element
node->AttachPrev(prev);
}
void Tail(Object & env, Function & func);
/* --------------------------------------------------------------------------------------------
* 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;
}
static SQInteger SqEmit(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* 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;
}
protected:
/* --------------------------------------------------------------------------------------------
* Actual implementation of the consume method.
*/
static SQInteger SqConsumeApproveImpl(HSQUIRRELVM vm, const SQBool rval, bool neg)
{
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");
}
// Return value of each slot
SQBool ret = !rval;
// 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, &ret);
// Should we proceed to the next slot or stop here?
if (ret == rval)
{
// The slot satisfied our criteria
break;
}
}
// Forward the returned value to the invoker
sq_pushbool(vm, neg ? !ret : ret);
// Specify that we returned something
return 1;
}
public:
static SQInteger SqQuery(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and see if they consume it.
*/
static SQInteger SqConsume(HSQUIRRELVM vm)
{
return SqConsumeApproveImpl(vm, SQTrue, false);
}
static SQInteger SqConsume(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and see if they approve it.
*/
static SQInteger SqApprove(HSQUIRRELVM vm)
{
return SqConsumeApproveImpl(vm, SQFalse, true);
}
static SQInteger SqApprove(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and see if they return something.
*/
static SQInteger SqRequest(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, true, ErrorHandling::IsEnabled());
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Is the returned value not null?
else if (sq_gettype(vm, -1) != OT_NULL)
{
// Push back the returned value
sq_push(vm, -1);
// Specify that we returned something
return 1;
}
}
// Specify that we returned nothing
return 0;
}
static SQInteger SqRequest(HSQUIRRELVM vm);
private: