forked from aya/aya
774 lines
23 KiB
C++
774 lines
23 KiB
C++
|
|
#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 |