mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-14 03:37:16 +01:00
1718 lines
55 KiB
C++
1718 lines
55 KiB
C++
// ------------------------------------------------------------------------------------------------
|
|
#include "Misc/Signal.hpp"
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
namespace SqMod {
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQMODE_DECL_TYPENAME(Typename, _SC("SqSignalImpl"))
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::SignalPool Signal::s_Signals;
|
|
Signal::FreeSignals Signal::s_FreeSignals;
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Class used to control the signal emitter.
|
|
*/
|
|
struct SignalWrapper
|
|
{
|
|
// --------------------------------------------------------------------------------------------
|
|
using Slot = Signal::Slot;
|
|
// --------------------------------------------------------------------------------------------
|
|
Signal * mSignal; // A raw pointer to the signal instance.
|
|
// --------------------------------------------------------------------------------------------
|
|
Slot mSlot; // The specified slot.
|
|
HSQUIRRELVM mVM; // The specified virtual machine.
|
|
SQInteger mRes; // The result of the operation.
|
|
bool mOne; // Limit to one slot in the operation.
|
|
bool mAppend; // Append instead of push when leading or tailing.
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Explicit constructor.
|
|
*/
|
|
explicit SignalWrapper(HSQUIRRELVM vm, bool extra = false)
|
|
: mSignal(nullptr)
|
|
, mSlot()
|
|
, mVM(vm)
|
|
, mRes(Initialize(vm, extra))
|
|
, mOne(false), mAppend(false)
|
|
{
|
|
//...
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Extracts the information from the stack of the specified virtual machine.
|
|
*/
|
|
SQInteger Initialize(HSQUIRRELVM vm, bool extra)
|
|
{
|
|
const SQInteger top = sq_gettop(vm);
|
|
// See if the minimum amount of arguments were specified
|
|
if (top <= 1)
|
|
{
|
|
return sq_throwerror(vm, "Wrong number of parameters");
|
|
}
|
|
// Attempt to extract the signal instance
|
|
try
|
|
{
|
|
mSignal = Var< Signal * >(vm, 1).value;
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
// Do we have a valid signal instance?
|
|
if (!mSignal)
|
|
{
|
|
return sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// By default, extra parameters start after callback
|
|
SQInteger extpos = 3;
|
|
// Is the first parameter a function?
|
|
if (sq_gettype(vm, 2) & (_RT_CLOSURE | _RT_NATIVECLOSURE))
|
|
{
|
|
// Attempt to grab the callback object
|
|
SQRESULT res = sq_getstackobj(vm, 2, &mSlot.mFuncRef);
|
|
// Did we fail to retrieve the callback object?
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
else
|
|
{
|
|
sq_addref(vm, &mSlot.mFuncRef);
|
|
}
|
|
// Grab the hash of the callback object
|
|
mSlot.mFuncHash = sq_gethash(vm, 2);
|
|
// Push the root table on the stack
|
|
sq_pushroottable(vm);
|
|
// Attempt to grab root table as the environment
|
|
res = sq_getstackobj(vm, -1, &mSlot.mThisRef);
|
|
// Did we fail to retrieve the root table?
|
|
if (SQ_FAILED(res))
|
|
{
|
|
// Pop the root table from the stack
|
|
sq_pop(vm, 1);
|
|
// Propagate the error
|
|
return res;
|
|
}
|
|
else
|
|
{
|
|
sq_addref(vm, &mSlot.mThisRef);
|
|
}
|
|
// Grab the hash of the root table
|
|
mSlot.mThisHash = sq_gethash(vm, -1);
|
|
// Pop the root table from the stack
|
|
sq_pop(vm, 1);
|
|
}
|
|
// Should we look for a specific environment?
|
|
else if (top >= 3 && sq_gettype(vm, 3) & (_RT_CLOSURE | _RT_NATIVECLOSURE))
|
|
{
|
|
// Is the first type at suitable to be an environment at least?
|
|
if (!(sq_gettype(vm, 2) & (_RT_TABLE | _RT_CLASS | _RT_INSTANCE)))
|
|
{
|
|
return sq_throwerror(vm, "Invalid environment object");
|
|
}
|
|
// Attempt to grab the environment object
|
|
SQRESULT res = sq_getstackobj(vm, 2, &mSlot.mThisRef);
|
|
// Did we fail to retrieve the environment object?
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
else
|
|
{
|
|
sq_addref(vm, &mSlot.mThisRef);
|
|
}
|
|
// Grab the hash of the environment object
|
|
mSlot.mThisHash = sq_gethash(vm, 2);
|
|
// Attempt to grab the callback object
|
|
res = sq_getstackobj(vm, 3, &mSlot.mFuncRef);
|
|
// Did we fail to retrieve the callback object?
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
else
|
|
{
|
|
sq_addref(vm, &mSlot.mFuncRef);
|
|
}
|
|
// Grab the hash of the callback object
|
|
mSlot.mFuncHash = sq_gethash(vm, 3);
|
|
// The extra parameters start one slot higher
|
|
++extpos;
|
|
}
|
|
else
|
|
{
|
|
return sq_throwerror(vm, "Missing callback function");
|
|
}
|
|
// Should we look for the extra parameters?
|
|
if (extra && top >= extpos)
|
|
{
|
|
SQBool value;
|
|
// Attempt to retrieve the parameter value
|
|
sq_tobool(vm, extpos, &value);
|
|
// Convert the retrieved value
|
|
mOne = static_cast< bool >(value);
|
|
// The next extra parameter starts one slot higher
|
|
++extpos;
|
|
}
|
|
else
|
|
{
|
|
mOne = false;
|
|
}
|
|
// Should we look for the extra parameters?
|
|
if (extra && top >= extpos)
|
|
{
|
|
SQBool value;
|
|
// Attempt to retrieve the parameter value
|
|
sq_tobool(vm, extpos, &value);
|
|
// Convert the retrieved value
|
|
mAppend = static_cast< bool >(value);
|
|
}
|
|
else
|
|
{
|
|
mAppend = true;
|
|
}
|
|
// Initialization was successful
|
|
return SQ_OK;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy constructor. (disabled)
|
|
*/
|
|
SignalWrapper(const SignalWrapper & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move constructor. (disabled)
|
|
*/
|
|
SignalWrapper(SignalWrapper && o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy assignment operator. (disabled)
|
|
*/
|
|
SignalWrapper & operator = (const SignalWrapper & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move assignment operator. (disabled)
|
|
*/
|
|
SignalWrapper & operator = (SignalWrapper && o) = delete;
|
|
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Helper functor to locate specific slots.
|
|
*/
|
|
template < class Slot > struct MatchSlot
|
|
{
|
|
// --------------------------------------------------------------------------------------------
|
|
const SQHash mThisHash; // The environment to search for.
|
|
const SQHash mFuncHash; // The callback to search for.
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Base constructor.
|
|
*/
|
|
MatchSlot(SQHash t, SQHash f)
|
|
: mThisHash(t), mFuncHash(f)
|
|
{
|
|
//...
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Function call operator.
|
|
*/
|
|
inline bool operator () (const Slot & s) const
|
|
{
|
|
return (mThisHash == s.mThisHash) && (mFuncHash == s.mFuncHash);
|
|
}
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Helper functor to locate slots with specific environments.
|
|
*/
|
|
template < class Slot > struct MatchThis
|
|
{
|
|
// --------------------------------------------------------------------------------------------
|
|
const SQHash mThisHash; // The environment to search for.
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Base constructor.
|
|
*/
|
|
explicit MatchThis(SQHash t)
|
|
: mThisHash(t)
|
|
{
|
|
//...
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Function call operator.
|
|
*/
|
|
inline bool operator () (const Slot & s) const
|
|
{
|
|
return (mThisHash == s.mThisHash);
|
|
}
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Helper functor to locate slots with specific callbacks.
|
|
*/
|
|
template < class Slot > struct MatchFunc
|
|
{
|
|
// --------------------------------------------------------------------------------------------
|
|
const SQHash mFuncHash; // The callback to search for.
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Base constructor.
|
|
*/
|
|
explicit MatchFunc(SQHash f)
|
|
: mFuncHash(f)
|
|
{
|
|
//...
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Function call operator.
|
|
*/
|
|
inline bool operator () (const Slot & s) const
|
|
{
|
|
return (mFuncHash == s.mFuncHash);
|
|
}
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* See if a certain slot exists using the provided functor.
|
|
*/
|
|
template < typename F, class Slot > static bool ExistsIf(F func, Slot * itr, Slot * end)
|
|
{
|
|
// Process slots within the given range
|
|
while (itr != end)
|
|
{
|
|
// Does this slot satisfy the functor?
|
|
if (func(*(itr++)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Count the number of slots that the provided functor deems worthy.
|
|
*/
|
|
template < typename F, class Slot > static Signal::SizeType CountIf(F func, Slot * itr, Slot * end)
|
|
{
|
|
Signal::SizeType count = 0;
|
|
// Process slots within the given range
|
|
while (itr != end)
|
|
{
|
|
// Should we count this slot?
|
|
if (func(*(itr++)))
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
// Return the final count
|
|
return count;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Remove all slot from the specified range if the provided functor demands it.
|
|
*/
|
|
template < typename F, class Slot, class Scope >
|
|
static Signal::SizeType RemoveIf(F func, Slot * itr, Slot * end, Scope * scope)
|
|
{
|
|
Slot * dest = itr;
|
|
// Number of removed slots
|
|
Signal::SizeType count = 0;
|
|
// Process slots within the given range
|
|
while (itr != end)
|
|
{
|
|
// Should we remove this slot?
|
|
if (func(*itr))
|
|
{
|
|
// Release the stored references
|
|
itr->Release();
|
|
// Are we currently signaling?
|
|
if (scope != nullptr)
|
|
{
|
|
scope->Descend(itr); // Update iterators
|
|
}
|
|
// Increase the counter
|
|
++count;
|
|
}
|
|
// Were there any slots removed?
|
|
else if (itr != dest)
|
|
{
|
|
// Offset the current scope
|
|
*dest = std::move(*itr);
|
|
// Advance the destination
|
|
++dest;
|
|
}
|
|
else
|
|
{
|
|
++dest; // We avoided a move assignment
|
|
}
|
|
// Advance the iterator
|
|
++itr;
|
|
}
|
|
// Return the final count
|
|
return count;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Move to the front all slots that the provided functor demands.
|
|
*/
|
|
template < typename F, class Slot, class Scope >
|
|
static void LeadIf(F func, Slot * front, Slot * end, bool one, bool append, Scope * scope)
|
|
{
|
|
Slot * itr = front;
|
|
// Process slots within the given range
|
|
while (itr != end)
|
|
{
|
|
// Should this slot become a lead?
|
|
if (!func(*itr))
|
|
{
|
|
++itr;
|
|
// Skip it
|
|
continue;
|
|
}
|
|
// Is this slot right behind the current lead?
|
|
else if ((itr - front) == 1)
|
|
{
|
|
// Swap them
|
|
itr->Swap(*front);
|
|
// Are we currently signaling?
|
|
if (scope != nullptr)
|
|
{
|
|
scope->Lead(itr); // Update iterators
|
|
}
|
|
}
|
|
// Is this not the current lead?
|
|
else if (itr != front)
|
|
{
|
|
// Backup the values of this slot
|
|
Slot tmp(std::move(*itr));
|
|
// Shift back all the slots before it
|
|
for (Slot * dest = itr, * src = (itr - 1); dest != front; dest = src, --src)
|
|
{
|
|
dest->Swap(*src); // Swap with the one bellow it
|
|
}
|
|
// Finally, place the slot into lead
|
|
*front = std::move(tmp);
|
|
// Are we currently signaling?
|
|
if (scope != nullptr)
|
|
{
|
|
scope->Lead(itr); // Update iterators
|
|
}
|
|
}
|
|
// Advance the iterator
|
|
++itr;
|
|
// Are we using the append method?
|
|
if (append)
|
|
{
|
|
++front; // Don't overrule the current lead
|
|
}
|
|
// Should we make only one lead?
|
|
if (one)
|
|
{
|
|
break; // Then stop here!
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Move to the back all slots that the provided functor demands.
|
|
*/
|
|
template < typename F, class Slot, class Scope >
|
|
static void TailIf(F func, Slot * front, Slot * back, bool one, bool append, Scope * scope)
|
|
{
|
|
Slot * itr = back;
|
|
// Process slots within the given range
|
|
while (itr >= front)
|
|
{
|
|
// Should this slot become a tail?
|
|
if (!func(*itr))
|
|
{
|
|
--itr;
|
|
// Skip it
|
|
continue;
|
|
}
|
|
// Is this slot right behind the current tail?
|
|
else if ((back - itr) == 1)
|
|
{
|
|
// Swap them
|
|
itr->Swap(*front);
|
|
// Are we currently signaling?
|
|
if (scope != nullptr)
|
|
{
|
|
scope->Tail(itr); // Update iterators
|
|
}
|
|
}
|
|
// Is this not the current tail?
|
|
else if (itr != back)
|
|
{
|
|
// Backup the values of this slot
|
|
Slot tmp(std::move(*itr));
|
|
// Shift up all the slots before it
|
|
for (Slot * dest = itr, * src = (itr + 1); dest != back; dest = src, ++src)
|
|
{
|
|
dest->Swap(*src); // Swap with the one above it
|
|
}
|
|
// Finally, place the slot into tail
|
|
*back = std::move(tmp);
|
|
// Are we currently signaling?
|
|
if (scope != nullptr)
|
|
{
|
|
scope->Tail(itr); // Update iterators
|
|
}
|
|
}
|
|
// Advance the iterator
|
|
--itr;
|
|
// Are we using the append method?
|
|
if (append)
|
|
{
|
|
--back; // Don't overrule the current tail
|
|
}
|
|
// Should we make only one tail?
|
|
if (one)
|
|
{
|
|
break; // Then stop here!
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Scope::Descend(Pointer ptr)
|
|
{
|
|
// Is the descended pointer withing this scope?
|
|
if (ptr <= mEnd)
|
|
{
|
|
// Is the iterator affected by this?
|
|
if (ptr < mItr || mItr > mEnd)
|
|
{
|
|
--mItr;
|
|
}
|
|
// Update the end iterator
|
|
--mEnd;
|
|
}
|
|
// Is this that last scope?
|
|
if (mChild != nullptr)
|
|
{
|
|
mChild->Descend(ptr); // Let the others know as well
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Scope::Lead(Pointer ptr)
|
|
{
|
|
// Is the descended pointer in our scope?
|
|
if (ptr <= mEnd)
|
|
{
|
|
// Does it affect our iterator?
|
|
if (ptr >= mItr && mItr != mEnd)
|
|
{
|
|
++mItr;
|
|
}
|
|
}
|
|
// Is the descended pointer out of our scope?
|
|
else if (ptr > mEnd)
|
|
{
|
|
++mItr, ++mEnd;
|
|
}
|
|
// Is this that last scope?
|
|
if (mChild != nullptr)
|
|
{
|
|
mChild->Lead(ptr); // Let the others know as well
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Scope::Tail(Pointer ptr)
|
|
{
|
|
// Is the descended pointer in our scope?
|
|
if (ptr <= mEnd)
|
|
{
|
|
// Did this scope already finish processing?
|
|
if (mItr != mEnd)
|
|
{
|
|
// Does it affect our iterator?
|
|
if (ptr < mItr)
|
|
{
|
|
--mItr;
|
|
}
|
|
// Update the end iterator
|
|
--mEnd;
|
|
}
|
|
}
|
|
// Is this that last scope?
|
|
if (mChild != nullptr)
|
|
{
|
|
mChild->Tail(ptr); // Let the others know as well
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Scope::Finish()
|
|
{
|
|
// Forcefully skip all remaining slots
|
|
mItr = mEnd;
|
|
// Is this that last scope?
|
|
if (mChild != nullptr)
|
|
{
|
|
mChild->Finish(); // Let the others know as well
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::Signal()
|
|
: m_Used(0)
|
|
, m_Size(SMB_SIZE)
|
|
, m_Slots(m_SMB)
|
|
, m_Scope(nullptr)
|
|
, m_Name()
|
|
, m_Data()
|
|
{
|
|
s_FreeSignals.push_back(this);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::Signal(String && name)
|
|
: m_Used(0)
|
|
, m_Size(SMB_SIZE)
|
|
, m_Slots(m_SMB)
|
|
, m_Scope(nullptr)
|
|
, m_Name(std::forward< String >(name))
|
|
, m_Data()
|
|
{
|
|
if (m_Name.empty())
|
|
{
|
|
s_FreeSignals.push_back(this);
|
|
}
|
|
}
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::~Signal()
|
|
{
|
|
ClearSlots();
|
|
// Should we erase this from the fee signals list?
|
|
if (m_Name.empty())
|
|
{
|
|
s_FreeSignals.erase(std::remove(s_FreeSignals.begin(), s_FreeSignals.end(), this),
|
|
s_FreeSignals.end());
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Signal::AdjustSlots(SizeType capacity)
|
|
{
|
|
// Is it necessary to resize?
|
|
if (capacity <= m_Size)
|
|
{
|
|
return true; // Already have that memory available!
|
|
}
|
|
// Do not alter the current capacity
|
|
SizeType size = m_Size;
|
|
// Calculate the next optimal size of the buffer
|
|
while (size < capacity)
|
|
{
|
|
size += (size + 1u) >> 1u;
|
|
}
|
|
// Attempt to allocate a memory buffer of the resulted size
|
|
auto slots = reinterpret_cast< Pointer >(new uint8_t[size * sizeof(Slot)]);
|
|
// See if the memory could be allocated
|
|
if (slots == nullptr)
|
|
{
|
|
return false; // Unable to acquire the memory!
|
|
}
|
|
// Do not alter the pointer to the new buffer
|
|
Pointer dest = slots;
|
|
// Are there any existing slots?
|
|
if (m_Used)
|
|
{
|
|
// Grab the range of slots to be transferred
|
|
Pointer src = m_Slots, end = (m_Slots + m_Used);
|
|
// Transfer the existing slots
|
|
while (src != end)
|
|
{
|
|
// Transfer to the new buffer
|
|
new (dest++) Slot(std::move(*(src)));
|
|
// Destroy the old instance
|
|
(src++)->~Slot();
|
|
}
|
|
// Grab the end of the remaining slots
|
|
end = (m_Slots + m_Size);
|
|
// Destroy the remaining slots
|
|
while (src != end)
|
|
{
|
|
(src++)->~Slot();
|
|
}
|
|
}
|
|
// Grab the end of the new buffer
|
|
Pointer end = (slots + size);
|
|
// Initialize the remaining slots
|
|
while (dest != end)
|
|
{
|
|
new (dest++) Slot();
|
|
}
|
|
// Update the iterators from current scopes
|
|
for (Scope * scope = m_Scope; scope != nullptr; scope = scope->mChild)
|
|
{
|
|
scope->mItr = slots + (scope->mItr - m_Slots);
|
|
scope->mEnd = slots + (scope->mEnd - m_Slots);
|
|
}
|
|
// Should we delete the current buffer?
|
|
if (m_Slots != m_SMB)
|
|
{
|
|
delete[] reinterpret_cast< uint8_t * >(m_Slots);
|
|
}
|
|
// Assign the new buffer
|
|
m_Slots = slots;
|
|
// Assign the new capacity
|
|
m_Size = size;
|
|
// The buffer was successfully adjusted
|
|
return true;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::ClearSlots()
|
|
{
|
|
// Release every connected slot
|
|
for (Pointer itr = m_Slots, end = m_Slots + m_Used; itr != end; ++itr)
|
|
{
|
|
itr->Release();
|
|
}
|
|
// Are we currently signaling?
|
|
if (m_Scope != nullptr)
|
|
{
|
|
m_Scope->Finish(); // Update iterators
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Connect(SignalWrapper & w)
|
|
{
|
|
// Make sure we have enough space to store the slot
|
|
if ((m_Used < m_Size) || AdjustSlots(m_Used + 1))
|
|
{
|
|
m_Slots[m_Used++].Swap(w.mSlot); // Connect the slot to the signal
|
|
}
|
|
else
|
|
{
|
|
sq_throwerror(w.mVM, "Unable to acquire enough memory");
|
|
}
|
|
// Specify that we don't return a value
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::ConnectOnce(SignalWrapper & w)
|
|
{
|
|
// Disconnect every occurrence of this slot first
|
|
w.mRes = Eliminate(w);
|
|
// Did we fail to disconnect it?
|
|
if (SQ_FAILED(w.mRes))
|
|
{
|
|
return w.mRes; // Propagate the error
|
|
}
|
|
// Finally, attempt to connect it again
|
|
return Connect(w);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal:: Disconnect(SignalWrapper & w)
|
|
{
|
|
return Eliminate(w);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Exists(SignalWrapper & w)
|
|
{
|
|
// Forward the call to the actual function
|
|
const bool r = ExistsIf(MatchSlot< Slot >(w.mSlot.mThisHash, w.mSlot.mFuncHash),
|
|
m_Slots, m_Slots + m_Used);
|
|
// Push the resulted value on the stack
|
|
sq_pushbool(w.mVM, static_cast< SQBool >(r));
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::ExistsThis(SignalWrapper & w)
|
|
{
|
|
// Forward the call to the actual function
|
|
const bool r = ExistsIf(MatchThis< Slot >(w.mSlot.mThisHash), m_Slots, m_Slots + m_Used);
|
|
// Push the resulted value on the stack
|
|
sq_pushbool(w.mVM, static_cast< SQBool >(r));
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::ExistsFunc(SignalWrapper & w)
|
|
{
|
|
// Forward the call to the actual function
|
|
const bool r = ExistsIf(MatchFunc< Slot >(w.mSlot.mFuncHash), m_Slots, m_Slots + m_Used);
|
|
// Push the resulted value on the stack
|
|
sq_pushbool(w.mVM, static_cast< SQBool >(r));
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Count(SignalWrapper & w)
|
|
{
|
|
// Forward the call to the actual function
|
|
const SizeType r = CountIf(MatchSlot< Slot >(w.mSlot.mThisHash, w.mSlot.mFuncHash),
|
|
m_Slots, m_Slots + m_Used);
|
|
// Push the resulted value on the stack
|
|
sq_pushinteger(w.mVM, static_cast< SQInteger >(r));
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::CountThis(SignalWrapper & w)
|
|
{
|
|
// Forward the call to the actual function
|
|
const SizeType r = CountIf(MatchThis< Slot >(w.mSlot.mThisHash), m_Slots, m_Slots + m_Used);
|
|
// Push the resulted value on the stack
|
|
sq_pushinteger(w.mVM, static_cast< SQInteger >(r));
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::CountFunc(SignalWrapper & w)
|
|
{
|
|
// Forward the call to the actual function
|
|
const SizeType r = CountIf(MatchFunc< Slot >(w.mSlot.mFuncHash), m_Slots, m_Slots + m_Used);
|
|
// Push the resulted value on the stack
|
|
sq_pushinteger(w.mVM, static_cast< SQInteger >(r));
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Lead(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's more than one slot connected
|
|
if (m_Used > 1)
|
|
{
|
|
LeadIf(MatchSlot< Slot >(w.mSlot.mThisHash, w.mSlot.mFuncHash),
|
|
m_Slots, m_Slots + m_Used, w.mOne, w.mAppend, m_Scope);
|
|
}
|
|
// Specify that we don't return a value
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::LeadThis(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's more than one slot connected
|
|
if (m_Used > 1)
|
|
{
|
|
LeadIf(MatchThis< Slot >(w.mSlot.mThisHash),
|
|
m_Slots, m_Slots + m_Used, w.mOne, w.mAppend, m_Scope);
|
|
}
|
|
// Specify that we don't return a value
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::LeadFunc(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's more than one slot connected
|
|
if (m_Used > 1)
|
|
{
|
|
LeadIf(MatchFunc< Slot >(w.mSlot.mFuncHash),
|
|
m_Slots, m_Slots + m_Used, w.mOne, w.mAppend, m_Scope);
|
|
}
|
|
// Specify that we don't return a value
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Tail(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's more than one slot connected
|
|
if (m_Used > 1)
|
|
{
|
|
TailIf(MatchSlot< Slot >(w.mSlot.mThisHash, w.mSlot.mFuncHash),
|
|
m_Slots, m_Slots + m_Used, w.mOne, w.mAppend, m_Scope);
|
|
}
|
|
// Specify that we don't return a value
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::TailThis(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's more than one slot connected
|
|
if (m_Used > 1)
|
|
{
|
|
TailIf(MatchThis< Slot >(w.mSlot.mThisHash),
|
|
m_Slots, m_Slots + m_Used, w.mOne, w.mAppend, m_Scope);
|
|
}
|
|
// Specify that we don't return a value
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::TailFunc(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's more than one slot connected
|
|
if (m_Used > 1)
|
|
{
|
|
TailIf(MatchFunc< Slot >(w.mSlot.mFuncHash),
|
|
m_Slots, m_Slots + m_Used, w.mOne, w.mAppend, m_Scope);
|
|
}
|
|
// Specify that we don't return a value
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Eliminate(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's at least one slot connected
|
|
if (m_Used != 0)
|
|
{
|
|
// Backup the current number of used slots
|
|
const SizeType count = m_Used;
|
|
// Forward the call to the actual function
|
|
m_Used -= RemoveIf(MatchSlot< Slot >(w.mSlot.mThisHash, w.mSlot.mFuncHash),
|
|
m_Slots, m_Slots + m_Used, m_Scope);
|
|
// Push the number of removed slots
|
|
sq_pushinteger(w.mVM, static_cast< SQInteger >(count - m_Used));
|
|
}
|
|
else
|
|
{
|
|
sq_pushinteger(w.mVM, 0);
|
|
}
|
|
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::EliminateThis(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's at least one slot connected
|
|
if (m_Used != 0)
|
|
{
|
|
// Backup the current number of used slots
|
|
const SizeType count = m_Used;
|
|
// Forward the call to the actual function
|
|
m_Used -= RemoveIf(MatchThis< Slot >(w.mSlot.mThisHash), m_Slots, m_Slots + m_Used, m_Scope);
|
|
// Push the number of removed slots
|
|
sq_pushinteger(w.mVM, static_cast< SQInteger >(count - m_Used));
|
|
}
|
|
else
|
|
{
|
|
sq_pushinteger(w.mVM, 0);
|
|
}
|
|
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::EliminateFunc(SignalWrapper & w)
|
|
{
|
|
// Make sure that there's at least one slot connected
|
|
if (m_Used != 0)
|
|
{
|
|
// Backup the current number of used slots
|
|
const SizeType count = m_Used;
|
|
// Forward the call to the actual function
|
|
m_Used -= RemoveIf(MatchFunc< Slot >(w.mSlot.mFuncHash), m_Slots, m_Slots + m_Used, m_Scope);
|
|
// Push the number of removed slots
|
|
sq_pushinteger(w.mVM, static_cast< SQInteger >(count - m_Used));
|
|
}
|
|
else
|
|
{
|
|
sq_pushinteger(w.mVM, 0);
|
|
}
|
|
|
|
// Specify that we returned a value
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
#define SQMOD_SIGNAL_CONTROL_WRAPPER(_f, _e) /*
|
|
*/ SQInteger Signal::Sq##_f(HSQUIRRELVM vm) { /*
|
|
*/ SignalWrapper w(vm, _e); /*
|
|
*/ if (SQ_FAILED(w.mRes)) return w.mRes; /*
|
|
*/ else return w.mSignal->_f(w); /*
|
|
*/ } /*
|
|
*/ /*
|
|
*/
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(Connect, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(ConnectOnce, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(Exists, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(Disconnect, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(ExistsThis, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(ExistsFunc, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(Count, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(CountThis, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(CountFunc, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(Lead, true)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(LeadThis, true)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(LeadFunc, true)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(Tail, true)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(TailThis, true)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(TailFunc, true)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(Eliminate, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(EliminateThis, false)
|
|
SQMOD_SIGNAL_CONTROL_WRAPPER(EliminateFunc, false)
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Emit(HSQUIRRELVM vm, SQInteger top)
|
|
{
|
|
// Are there any slots connected?
|
|
if (!m_Used) return 0;
|
|
// 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);
|
|
// Contains the last received result
|
|
SQRESULT res = SQ_OK;
|
|
// 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);
|
|
}
|
|
// 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
|
|
res = sq_call(vm, top, 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))
|
|
{
|
|
break; // Stop emitting signals
|
|
}
|
|
}
|
|
// Return the last result
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Query(HSQUIRRELVM vm, SQInteger top)
|
|
{
|
|
// Are there any slots connected?
|
|
if (!m_Used) return 0;
|
|
// The collector and the specified environment
|
|
HSQOBJECT cthis, cfunc;
|
|
// Attempt to grab the collector environment
|
|
SQRESULT res = sq_getstackobj(vm, 2, &cthis);
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Was there a valid environment?
|
|
else if (sq_isnull(cthis))
|
|
{
|
|
// Default to the root table
|
|
sq_pushroottable(vm);
|
|
// Try to grab the collector environment again
|
|
res = sq_getstackobj(vm, -1, &cthis);
|
|
// Pop the root table from the stack
|
|
sq_pop(vm, 1);
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
}
|
|
// Grab the collector callback
|
|
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_type(cfunc) & (_RT_CLOSURE | _RT_NATIVECLOSURE)))
|
|
{
|
|
return sq_throwerror(vm, "Invalid collector callback");
|
|
}
|
|
// 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);
|
|
// 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);
|
|
}
|
|
// 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, static_cast< SQBool >(true), static_cast< SQBool >(ErrorHandling::IsEnabled()));
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
// Stop emitting signals
|
|
break;
|
|
}
|
|
// Push the collector onto the stack
|
|
sq_pushobject(vm, cfunc);
|
|
sq_pushobject(vm, cthis);
|
|
// Push the returned value
|
|
sq_push(vm, -3);
|
|
// Make the function call and store the result
|
|
res = sq_call(vm, 2, static_cast< SQBool >(false), static_cast< SQBool >(ErrorHandling::IsEnabled()));
|
|
// Pop the callback object, return value and collector from the stack
|
|
sq_pop(vm, 3);
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
break; // Stop emitting signals
|
|
}
|
|
}
|
|
// Return the last result
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Consume(HSQUIRRELVM vm, SQInteger top)
|
|
{
|
|
// Are there any slots connected?
|
|
if (!m_Used) return 0;
|
|
// 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);
|
|
// Contains the last received result
|
|
SQRESULT res = SQ_OK;
|
|
// Default to not consumed
|
|
SQBool ret = SQFalse;
|
|
// 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);
|
|
}
|
|
// 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
|
|
res = sq_call(vm, top, static_cast< SQBool >(true), static_cast< SQBool >(ErrorHandling::IsEnabled()));
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
// Stop emitting signals
|
|
break;
|
|
}
|
|
// 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)
|
|
{
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
// The slot consumed the signal
|
|
break;
|
|
}
|
|
}
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
}
|
|
// Did we fail to process slots?
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Forward the returned value to the invoker
|
|
sq_pushbool(vm, ret);
|
|
// Specify that we returned something
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Approve(HSQUIRRELVM vm, SQInteger top)
|
|
{
|
|
// Are there any slots connected?
|
|
if (!m_Used) return 0;
|
|
// 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);
|
|
// Contains the last received result
|
|
SQRESULT res = SQ_OK;
|
|
// Default to approved
|
|
SQBool ret = SQTrue;
|
|
// 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);
|
|
}
|
|
// 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
|
|
res = sq_call(vm, top, static_cast< SQBool >(true), static_cast< SQBool >(ErrorHandling::IsEnabled()));
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
// Stop emitting signals
|
|
break;
|
|
}
|
|
// 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)
|
|
{
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
// The slot did not approve the signal
|
|
break;
|
|
}
|
|
}
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
}
|
|
// Did we fail to process slots?
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Forward the returned value to the invoker
|
|
sq_pushbool(vm, ret);
|
|
// Specify that we returned something
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Request(HSQUIRRELVM vm, SQInteger top)
|
|
{
|
|
// Are there any slots connected?
|
|
if (!m_Used) return 0;
|
|
// 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);
|
|
// Contains the last received result
|
|
SQRESULT res = SQ_OK;
|
|
// 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);
|
|
}
|
|
// 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
|
|
res = sq_call(vm, top, static_cast< SQBool >(true), static_cast< SQBool >(ErrorHandling::IsEnabled()));
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
// Stop emitting signals
|
|
break;
|
|
}
|
|
// Is the returned value not null?
|
|
else if (sq_gettype(vm, -1) != OT_NULL)
|
|
{
|
|
// Remove the callback object from the stack
|
|
sq_remove(vm, -2);
|
|
// Specify that we returned something
|
|
res = 1;
|
|
// The slot did not approve the signal
|
|
break;
|
|
}
|
|
// Pop the callback object and return value from the stack
|
|
sq_pop(vm, 2);
|
|
}
|
|
// Return the last result
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqEmit(HSQUIRRELVM vm)
|
|
{
|
|
const SQInteger top = sq_gettop(vm);
|
|
// Contains the last received result
|
|
SQRESULT res;
|
|
// Attempt to forward the call to the signal instance
|
|
try
|
|
{
|
|
// Attempt to grab the signal instance from the stack
|
|
Signal * signal = Var< Signal * >(vm, 1).value;
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
res = sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Forward the call to the signal instance
|
|
else
|
|
{
|
|
res = signal->Emit(vm, top);
|
|
}
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
res = sq_throwerror(vm, e.what());
|
|
}
|
|
// The execution was successful
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqQuery(HSQUIRRELVM vm)
|
|
{
|
|
const SQInteger 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 callback?
|
|
else if (top <= 2)
|
|
{
|
|
return sq_throwerror(vm, "Missing collector callback");
|
|
}
|
|
// Contains the last received result
|
|
SQRESULT res;
|
|
// Attempt to forward the call to the signal instance
|
|
try
|
|
{
|
|
// Attempt to grab the signal instance from the stack
|
|
Signal * signal = Var< Signal * >(vm, 1).value;
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
res = sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Forward the call to the signal instance
|
|
else
|
|
{
|
|
res = signal->Query(vm, top);
|
|
}
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
res = sq_throwerror(vm, e.what());
|
|
}
|
|
// The execution was successful
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqConsume(HSQUIRRELVM vm)
|
|
{
|
|
const SQInteger top = sq_gettop(vm);
|
|
// Contains the last received result
|
|
SQRESULT res;
|
|
// Attempt to forward the call to the signal instance
|
|
try
|
|
{
|
|
// Attempt to grab the signal instance from the stack
|
|
Signal * signal = Var< Signal * >(vm, 1).value;
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
res = sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Forward the call to the signal instance
|
|
else
|
|
{
|
|
res = signal->Consume(vm, top);
|
|
}
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
res = sq_throwerror(vm, e.what());
|
|
}
|
|
// The execution was successful
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqApprove(HSQUIRRELVM vm)
|
|
{
|
|
const SQInteger top = sq_gettop(vm);
|
|
// Contains the last received result
|
|
SQRESULT res;
|
|
// Attempt to forward the call to the signal instance
|
|
try
|
|
{
|
|
// Attempt to grab the signal instance from the stack
|
|
Signal * signal = Var< Signal * >(vm, 1).value;
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
res = sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Forward the call to the signal instance
|
|
else
|
|
{
|
|
res = signal->Approve(vm, top);
|
|
}
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
res = sq_throwerror(vm, e.what());
|
|
}
|
|
// The execution was successful
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqRequest(HSQUIRRELVM vm)
|
|
{
|
|
const SQInteger top = sq_gettop(vm);
|
|
// Contains the last received result
|
|
SQRESULT res;
|
|
// Attempt to forward the call to the signal instance
|
|
try
|
|
{
|
|
// Attempt to grab the signal instance from the stack
|
|
Signal * signal = Var< Signal * >(vm, 1).value;
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
res = sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Forward the call to the signal instance
|
|
else
|
|
{
|
|
res = signal->Request(vm, top);
|
|
}
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
res = sq_throwerror(vm, e.what());
|
|
}
|
|
// The execution was successful
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Terminate()
|
|
{
|
|
// Terminate named signals
|
|
for (const auto & s : s_Signals)
|
|
{
|
|
// Clear slots
|
|
s.second.first->ClearSlots();
|
|
// Release the name
|
|
s.second.first->m_Name.clear();
|
|
// Release whatever is in the user data
|
|
s.second.first->m_Data.Release();
|
|
}
|
|
// 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->ClearSlots();
|
|
// Release whatever is in the user data
|
|
s->m_Data.Release();
|
|
}
|
|
// Finally clear the container itself
|
|
s_FreeSignals.clear();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
LightObj Signal::CreateFree()
|
|
{
|
|
// 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< LightObj >(DefaultVM::Get(), -1).value;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
LightObj Signal::Create(StackStrF & name)
|
|
{
|
|
// Validate the signal name
|
|
if (name.mLen <= 0)
|
|
{
|
|
return CreateFree();
|
|
}
|
|
// Create a copy of the name
|
|
String sname(name.mPtr, static_cast< size_t >(name.mLen));
|
|
// Compute the hash of the specified name
|
|
const std::size_t hash = std::hash< String >{}(sname);
|
|
// See if the signal already exists
|
|
for (const auto & e : s_Signals)
|
|
{
|
|
if (e.first == hash)
|
|
{
|
|
return e.second.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(sname)));
|
|
// 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, SignalPair(ptr, Var< LightObj >(DefaultVM::Get(), -1).value));
|
|
// Return the created signal
|
|
return s_Signals.back().second.second.mObj;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Remove(StackStrF & name)
|
|
{
|
|
// Validate the signal name
|
|
if (name.mLen <= 0)
|
|
{
|
|
STHROWF("Signals without names cannot be removed manually");
|
|
}
|
|
// Create a copy of the name
|
|
const String sname(name.mPtr, static_cast< size_t >(name.mLen));
|
|
// Compute the hash of the specified name
|
|
const std::size_t hash = std::hash< String >{}(sname);
|
|
// Iterator to the existing signal, if any
|
|
auto 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.first->m_Name.clear();
|
|
// Put it on the free list
|
|
s_FreeSignals.push_back(itr->second.first);
|
|
// Finally, remove it from the named list
|
|
s_Signals.erase(itr);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const LightObj & Signal::Fetch(StackStrF & name)
|
|
{
|
|
// Validate the signal name
|
|
if (name.mLen <= 0)
|
|
{
|
|
STHROWF("Signals without names cannot be retrieved manually");
|
|
}
|
|
// Create a copy of the name
|
|
const String sname(name.mPtr, static_cast< size_t >(name.mLen));
|
|
// Compute the hash of the specified name
|
|
const std::size_t hash = std::hash< String >{}(sname);
|
|
// Search for a signal with this name
|
|
for (const auto & e : s_Signals)
|
|
{
|
|
if (e.first == hash)
|
|
{
|
|
return e.second.second; // Found a match so let's return it
|
|
}
|
|
}
|
|
// No such signal exists
|
|
STHROWF("Unknown signal named (%s)", sname.c_str());
|
|
// SHOULD NOT REACH THIS POINT!
|
|
static LightObj slo;
|
|
return slo;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Forward the call to terminate the signals.
|
|
*/
|
|
void TerminateSignals()
|
|
{
|
|
Signal::Terminate();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void InitSignalPair(SignalPair & sp, LightObj & et, const char * name)
|
|
{
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Create the signal instance
|
|
DeleteGuard< Signal > dg(new Signal());
|
|
// Attempt to create the signal instance object
|
|
sp.second = LightObj(dg.Get());
|
|
// Assign the signal instance itself
|
|
sp.first = dg.Get();
|
|
// This is now managed by the script
|
|
dg.Release();
|
|
// Should we bind this to a certain object?
|
|
if (name != nullptr)
|
|
{
|
|
et.Bind(name, sp.second); // Bind the signal to the specified object
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void ResetSignalPair(SignalPair & sp, bool clear)
|
|
{
|
|
// See if the slots must be cleared as well
|
|
if (clear && sp.first != nullptr)
|
|
{
|
|
sp.first->ClearSlots();
|
|
}
|
|
// Reset the signal pair
|
|
sp.first = nullptr;
|
|
sp.second.Release();
|
|
}
|
|
|
|
// ================================================================================================
|
|
void Register_Signal(HSQUIRRELVM vm)
|
|
{
|
|
|
|
RootTable(vm).Bind(Typename::Str,
|
|
Class< Signal, NoConstructor< Signal > >(vm, Typename::Str)
|
|
// Meta-methods
|
|
.SquirrelFunc(_SC("_typename"), &Typename::Fn)
|
|
.Func(_SC("_tostring"), &Signal::ToString)
|
|
// Core Properties
|
|
.Prop(_SC("Data"), &Signal::GetData, &Signal::SetData)
|
|
.Func(_SC("Name"), &Signal::ToString)
|
|
.Prop(_SC("Slots"), &Signal::GetUsed)
|
|
.Prop(_SC("Empty"), &Signal::IsEmpty)
|
|
// Core Methods
|
|
.Func(_SC("Clear"), &Signal::ClearSlots)
|
|
// Squirrel Functions
|
|
.SquirrelFunc(_SC("Connect"), &Signal::SqConnect)
|
|
.SquirrelFunc(_SC("ConnectOnce"), &Signal::SqConnectOnce)
|
|
.SquirrelFunc(_SC("Exists"), &Signal::SqExists)
|
|
.SquirrelFunc(_SC("Disconnect"), &Signal::SqDisconnect)
|
|
.SquirrelFunc(_SC("ExistsThis"), &Signal::SqExistsThis)
|
|
.SquirrelFunc(_SC("ExistsFunc"), &Signal::SqExistsFunc)
|
|
.SquirrelFunc(_SC("Count"), &Signal::SqCount)
|
|
.SquirrelFunc(_SC("CountThis"), &Signal::SqCountThis)
|
|
.SquirrelFunc(_SC("CountFunc"), &Signal::SqCountFunc)
|
|
.SquirrelFunc(_SC("Lead"), &Signal::SqLead)
|
|
.SquirrelFunc(_SC("LeadThis"), &Signal::SqLeadThis)
|
|
.SquirrelFunc(_SC("LeadFunc"), &Signal::SqLeadFunc)
|
|
.SquirrelFunc(_SC("Tail"), &Signal::SqTail)
|
|
.SquirrelFunc(_SC("TailThis"), &Signal::SqTailThis)
|
|
.SquirrelFunc(_SC("TailFunc"), &Signal::SqTailFunc)
|
|
.SquirrelFunc(_SC("Eliminate"), &Signal::SqEliminate)
|
|
.SquirrelFunc(_SC("EliminateThis"), &Signal::SqEliminateThis)
|
|
.SquirrelFunc(_SC("EliminateFunc"), &Signal::SqEliminateFunc)
|
|
.SquirrelFunc(_SC("Emit"), &Signal::SqEmit)
|
|
.SquirrelFunc(_SC("Query"), &Signal::SqQuery)
|
|
.SquirrelFunc(_SC("Consume"), &Signal::SqConsume)
|
|
.SquirrelFunc(_SC("Approve"), &Signal::SqApprove)
|
|
.SquirrelFunc(_SC("Request"), &Signal::SqRequest)
|
|
);
|
|
|
|
RootTable(vm)
|
|
.FmtFunc(_SC("SqSignal"), &Signal::Fetch)
|
|
.FmtFunc(_SC("SqCreateSignal"), &Signal::Create)
|
|
.FmtFunc(_SC("SqRemoveSignal"), &Signal::Remove);
|
|
}
|
|
|
|
} // Namespace:: SqMod
|