forked from aya/aya
Initial commit
This commit is contained in:
774
engine/core/src/signal.hpp
Normal file
774
engine/core/src/signal.hpp
Normal file
@@ -0,0 +1,774 @@
|
||||
|
||||
#pragma once
|
||||
#include "boost.hpp"
|
||||
#include "intrusive_ptr_target.hpp"
|
||||
#include "Memory.hpp"
|
||||
#include "intrusive_weak_ptr.hpp"
|
||||
#include "boost/any.hpp"
|
||||
#include "boost/type_traits.hpp"
|
||||
#include "threadsafe.hpp"
|
||||
#include "Debug.hpp"
|
||||
#include <limits>
|
||||
#include "callable.hpp"
|
||||
|
||||
// aya - instead of intrusive_ptr, use shared_ptr on unix
|
||||
|
||||
using boost::shared_ptr;
|
||||
using boost::weak_ptr;
|
||||
|
||||
LOGGROUP(ScopedConnection);
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define AYA_SIGNALS_DEBUGGING
|
||||
#endif
|
||||
|
||||
#ifdef AYA_SIGNALS_DEBUGGING
|
||||
#define AYA_SIGNALS_ASSERT AYA_CRASH_ASSERT
|
||||
#pragma optimize("", off)
|
||||
#else
|
||||
#define AYA_SIGNALS_ASSERT AYAASSERT
|
||||
#endif
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
// The classes in this namespace mimic a small fraction of the features contained
|
||||
// in boost signals
|
||||
namespace signals
|
||||
{
|
||||
/*
|
||||
This signal class is similar to boost::signal, but with a few
|
||||
important differences. First, it doesn't implement nearly as
|
||||
much functionality as boost's. It merely implements those
|
||||
portions that Roblox uses.
|
||||
The big advantage to this implementation is its (limited) thread
|
||||
safety. Any thread is allowed to connect new slots to this
|
||||
signal and also disconnect them at any time. The firing of a signal
|
||||
is not thread safe - only one thread is allowed to fire at a time.
|
||||
*/
|
||||
|
||||
// Set this to whatever you want to handle exceptions thrown by a slot
|
||||
extern boost::function<void(std::exception&)> slot_exception_handler;
|
||||
|
||||
class connection
|
||||
{
|
||||
public:
|
||||
class islot
|
||||
: boost::noncopyable
|
||||
#if 0
|
||||
// No need to test maxStrong, since all strong references are internal
|
||||
// However, weak references are external via connection object. It is
|
||||
// expected that the total weak references to a slot are much less than 64000
|
||||
// NOTE: unsigned short is slightly slower than int on Win32. However, it
|
||||
// saves 4 bytes.
|
||||
, public Aya::intrusive_ptr_target<islot, unsigned short, 0, 0>
|
||||
#else
|
||||
, public Aya::intrusive_ptr_target<islot>
|
||||
#endif
|
||||
{
|
||||
protected:
|
||||
islot() {}
|
||||
|
||||
public:
|
||||
virtual ~islot() {}
|
||||
virtual void disconnect() = 0;
|
||||
virtual bool connected() const = 0;
|
||||
};
|
||||
|
||||
inline connection(islot* slot)
|
||||
: weak_slot(slot)
|
||||
{
|
||||
}
|
||||
inline connection(const connection& con)
|
||||
: weak_slot(con.weak_slot)
|
||||
{
|
||||
}
|
||||
inline connection() {}
|
||||
connection& operator=(const connection& con);
|
||||
|
||||
void disconnect() const;
|
||||
bool connected() const;
|
||||
bool operator==(const connection& other) const;
|
||||
bool operator!=(const connection& other) const;
|
||||
|
||||
void flogPrint()
|
||||
{
|
||||
boost::intrusive_ptr<islot> s(weak_slot.lock());
|
||||
FASTLOG2(FLog::Always, "Connection %p, slot %p", this, s.get());
|
||||
}
|
||||
|
||||
private:
|
||||
// to make connections copyable, the data for a connection are shared
|
||||
Aya::intrusive_weak_ptr<islot> weak_slot; // must be weak to avoid memory leaks
|
||||
};
|
||||
|
||||
class scoped_connection : boost::noncopyable
|
||||
{
|
||||
// Has-a instead of Is-a. We do this because demoting scoped_connection reference
|
||||
// to a connection will alter the meaning of the = operator, leading to strange
|
||||
// bugs.
|
||||
connection con;
|
||||
|
||||
public:
|
||||
inline scoped_connection() {}
|
||||
inline scoped_connection(const connection& con)
|
||||
: con(con)
|
||||
{
|
||||
}
|
||||
|
||||
inline scoped_connection& operator=(const connection& con)
|
||||
{
|
||||
if (this->con != con)
|
||||
{
|
||||
disconnect();
|
||||
this->con = con;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline ~scoped_connection()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
// Accessor to underlying connection, if you really want it
|
||||
inline connection& get()
|
||||
{
|
||||
return con;
|
||||
}
|
||||
|
||||
// implementation of connection contract
|
||||
inline void disconnect() const
|
||||
{
|
||||
con.disconnect();
|
||||
}
|
||||
inline bool connected() const
|
||||
{
|
||||
return con.connected();
|
||||
}
|
||||
inline bool operator==(const connection& other) const
|
||||
{
|
||||
return con == other;
|
||||
}
|
||||
inline bool operator!=(const connection& other) const
|
||||
{
|
||||
return con != other;
|
||||
}
|
||||
};
|
||||
|
||||
class scoped_connection_logged : boost::noncopyable
|
||||
{
|
||||
// Has-a instead of Is-a. We do this because demoting scoped_connection reference
|
||||
// to a connection will alter the meaning of the = operator, leading to strange
|
||||
// bugs.
|
||||
connection con;
|
||||
bool logged;
|
||||
|
||||
public:
|
||||
inline scoped_connection_logged()
|
||||
: logged(false)
|
||||
{
|
||||
}
|
||||
inline scoped_connection_logged(bool logged)
|
||||
: logged(logged)
|
||||
{
|
||||
}
|
||||
|
||||
// Helper for using FastLog groups as trigger
|
||||
inline scoped_connection_logged(FLog::Channel channelId)
|
||||
: logged(channelId != 0)
|
||||
{
|
||||
}
|
||||
|
||||
inline scoped_connection_logged(const connection& con)
|
||||
: con(con)
|
||||
{
|
||||
}
|
||||
|
||||
inline scoped_connection_logged& operator=(const connection& con)
|
||||
{
|
||||
if (this->con != con)
|
||||
{
|
||||
disconnect();
|
||||
this->con = con;
|
||||
if (logged)
|
||||
{
|
||||
FASTLOG2(FLog::Always, "Scoped connection %p assign: %p", this, &con);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline ~scoped_connection_logged()
|
||||
{
|
||||
if (logged)
|
||||
FASTLOG1(FLog::Always, "Scoped connection %p destructor", this);
|
||||
disconnect();
|
||||
}
|
||||
|
||||
// Accessor to underlying connection, if you really want it
|
||||
inline connection& get()
|
||||
{
|
||||
return con;
|
||||
}
|
||||
|
||||
inline void setLogged(bool logged)
|
||||
{
|
||||
this->logged = logged;
|
||||
}
|
||||
|
||||
// implementation of connection contract
|
||||
inline void disconnect() const
|
||||
{
|
||||
if (logged)
|
||||
FASTLOG2(FLog::Always, "Scoped connection %p disconnect, previously connected: %u", this, con.connected());
|
||||
con.disconnect();
|
||||
}
|
||||
inline bool connected() const
|
||||
{
|
||||
return con.connected();
|
||||
}
|
||||
inline bool operator==(const connection& other) const
|
||||
{
|
||||
return con == other;
|
||||
}
|
||||
inline bool operator!=(const connection& other) const
|
||||
{
|
||||
return con != other;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal : boost::noncopyable
|
||||
{
|
||||
protected:
|
||||
friend class slot;
|
||||
class slot
|
||||
: public connection::islot
|
||||
, public icallable<boost::function_traits<Signature>::arity, Signature>
|
||||
{
|
||||
public:
|
||||
boost::intrusive_ptr<slot> next;
|
||||
|
||||
signal* sig;
|
||||
|
||||
inline slot(signal* sig)
|
||||
: sig(sig)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool connected() const
|
||||
{
|
||||
return sig != NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
SAFE_HEAP_STATIC(boost::mutex, mutex);
|
||||
|
||||
virtual void disconnect()
|
||||
{
|
||||
if (!sig)
|
||||
return;
|
||||
|
||||
boost::mutex::scoped_lock lock(mutex());
|
||||
|
||||
if (sig)
|
||||
{
|
||||
signal* s = sig;
|
||||
sig = NULL;
|
||||
s->remove(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class Delegate>
|
||||
class callable_slot : public callable<slot, Delegate, boost::function_traits<Signature>::arity, Signature>
|
||||
{
|
||||
public:
|
||||
inline callable_slot(const Delegate& deleg, signal* sig)
|
||||
: callable<slot, Delegate, boost::function_traits<Signature>::arity, Signature>(deleg, sig)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// The slots are stored in a linked list, with "head" as a dummy slot used to anchor the list.
|
||||
|
||||
boost::intrusive_ptr<slot> head;
|
||||
|
||||
// TODO: Avoid contention by using one mutex per signal? Or an array of signals?
|
||||
// TODO: Is boost::mutex the best choice? Does it start up with a spin?
|
||||
// However, at least we have a separate mutex for each signature
|
||||
|
||||
// SAFE_HEAP_STATIC is used instead of SAFE_STATIC to work around global variables using signals (like GameSettings)
|
||||
// If you use SAFE_STATIC, mutex can be destroyed before other global variables using signals,
|
||||
// so signal destructor will fail on mutex access
|
||||
SAFE_HEAP_STATIC(boost::mutex, mutex)
|
||||
|
||||
void remove(slot* item)
|
||||
{
|
||||
// Invariant: the value of item->next does not change
|
||||
AYAASSERT(!boost::intrusive_ptr_expired(item));
|
||||
|
||||
if (item == head)
|
||||
head = item->next;
|
||||
else
|
||||
{
|
||||
// Find "prev". This is O(n)
|
||||
slot* prev = head.get();
|
||||
// TODO: Can we just assert that prev!=NULL?
|
||||
while (prev && prev->next != item)
|
||||
prev = prev->next.get();
|
||||
|
||||
// In theory prev should never be NULL, because for it to be NULL
|
||||
// the slot would be destroyed, in which case remove() can't be
|
||||
// called. Let's play it safe and null-check anyway.
|
||||
AYA_SIGNALS_ASSERT(!prev || prev->next.get() == item);
|
||||
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
}
|
||||
|
||||
AYAASSERT(!boost::intrusive_ptr_expired(item));
|
||||
|
||||
// item is now deletable
|
||||
}
|
||||
|
||||
void insert(slot* item)
|
||||
{
|
||||
AYA_SIGNALS_ASSERT(item);
|
||||
|
||||
boost::mutex::scoped_lock lock(mutex());
|
||||
|
||||
if (!head)
|
||||
{
|
||||
head = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->next = head;
|
||||
head = item;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
inline signal()
|
||||
{
|
||||
mutex();
|
||||
}
|
||||
|
||||
inline ~signal()
|
||||
{
|
||||
disconnectAll();
|
||||
}
|
||||
|
||||
void disconnectAll()
|
||||
{
|
||||
while (head)
|
||||
{
|
||||
boost::intrusive_ptr<slot> node;
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex());
|
||||
|
||||
// See DE131 for a justification of this "chunk" code
|
||||
const int chunkSize = 10;
|
||||
int count = chunkSize;
|
||||
for (node = head; node; node = node->next)
|
||||
{
|
||||
node->sig = NULL;
|
||||
if (count-- == 0)
|
||||
{
|
||||
// After 10 iterations we need to break out and collect
|
||||
// the slots. Otherwise we risk a stack crash
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the next line will cause nodes to be destroyed.
|
||||
// Notice that we want them to be destroyed
|
||||
// outside of the mutex lock because
|
||||
// destruction could have side-effects.
|
||||
head = node;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool empty() const
|
||||
{
|
||||
return !head;
|
||||
}
|
||||
|
||||
template<class Delegate>
|
||||
connection connect(const Delegate& function)
|
||||
{
|
||||
slot* item = new callable_slot<Delegate>(function, this);
|
||||
insert(item);
|
||||
return connection(item);
|
||||
}
|
||||
|
||||
// For debugging:
|
||||
static size_t sizeof_slot()
|
||||
{
|
||||
return sizeof(slot);
|
||||
}
|
||||
|
||||
void flogPrint()
|
||||
{
|
||||
printf("Signal - %p\n", this);
|
||||
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
|
||||
while (this->next(item))
|
||||
printf("Signal slot = %p\n", item.get());
|
||||
// FASTLOG1(FLog::Always, "Signal slot = %p", item.get());
|
||||
}
|
||||
|
||||
protected:
|
||||
void on_error(std::exception& e)
|
||||
{
|
||||
if (slot_exception_handler)
|
||||
slot_exception_handler(e);
|
||||
}
|
||||
|
||||
bool next(boost::intrusive_ptr<slot>& item)
|
||||
{
|
||||
if (!item)
|
||||
{
|
||||
// Start iterating; this is safe to read from
|
||||
// If another thread is in the process of prepending, we can get old head or new head
|
||||
// If we do get the new head it should already have the new next so this is race-free
|
||||
item = this->head;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Advance the iterator; we keep item alive so next is safe to read from
|
||||
// If another thread is in the process of removing the 'item->next' connection we may see
|
||||
// the next pointer either pointing to the element that's being removed or to the next one
|
||||
// Since replacing next is atomic and we can't observe any other values than these two this is
|
||||
// also race-free.
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (!item)
|
||||
{
|
||||
// Done iterating
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Iteration succeeded
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<int arity, typename Signature>
|
||||
class signal_with_args;
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<0, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call();
|
||||
}
|
||||
|
||||
public:
|
||||
void operator()()
|
||||
{
|
||||
if (this->empty())
|
||||
return;
|
||||
|
||||
typedef typename Aya::signals::signal<Signature>::slot slot;
|
||||
boost::intrusive_ptr<slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get());
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
Aya::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<1, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1);
|
||||
}
|
||||
|
||||
public:
|
||||
void operator()(typename boost::function_traits<Signature>::arg1_type arg1)
|
||||
{
|
||||
if (this->empty())
|
||||
return;
|
||||
|
||||
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1);
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
Aya::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<2, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2);
|
||||
}
|
||||
|
||||
public:
|
||||
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2)
|
||||
{
|
||||
if (this->empty())
|
||||
return;
|
||||
|
||||
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2);
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
Aya::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<3, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
public:
|
||||
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3)
|
||||
{
|
||||
if (this->empty())
|
||||
return;
|
||||
|
||||
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
|
||||
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3);
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
Aya::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<4, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
public:
|
||||
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4)
|
||||
{
|
||||
if (this->empty())
|
||||
return;
|
||||
|
||||
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3, arg4);
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
Aya::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<5, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
public:
|
||||
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5)
|
||||
{
|
||||
if (this->empty())
|
||||
return;
|
||||
|
||||
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
Aya::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<6, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5,
|
||||
typename boost::function_traits<Signature>::arg6_type arg6)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
public:
|
||||
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6)
|
||||
{
|
||||
if (this->empty())
|
||||
return;
|
||||
|
||||
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
Aya::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<7, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5,
|
||||
typename boost::function_traits<Signature>::arg6_type arg6, typename boost::function_traits<Signature>::arg7_type arg7)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
|
||||
public:
|
||||
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6,
|
||||
typename boost::function_traits<Signature>::arg7_type arg7)
|
||||
{
|
||||
if (this->empty())
|
||||
return;
|
||||
|
||||
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
Aya::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace signals
|
||||
|
||||
template<typename Signature>
|
||||
class signal : public signals::signal_with_args<boost::function_traits<Signature>::arity, Signature>
|
||||
{
|
||||
};
|
||||
|
||||
// Note that remote signal is *not* virtualized against signal. This only works because the Event class is templatized, and not using polymorphism.
|
||||
// If Event becomes polymorphics, THIS CODE WILL FAIL
|
||||
template<typename Signature>
|
||||
class remote_signal : public signal<Signature>
|
||||
{
|
||||
private:
|
||||
typedef signal<Signature> Super;
|
||||
|
||||
public:
|
||||
signal<void()> connectionSignal;
|
||||
|
||||
remote_signal() {}
|
||||
|
||||
template<typename F>
|
||||
signals::connection connect(const F& function)
|
||||
{
|
||||
connectionSignal();
|
||||
return Super::connect(function);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
|
||||
#ifdef AYA_SIGNALS_DEBUGGING
|
||||
#pragma optimize("", on)
|
||||
#endif
|
||||
Reference in New Issue
Block a user