//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #ifndef _TSIGNAL_H_ #define _TSIGNAL_H_ #ifndef _UTIL_DELEGATE_H_ #include "core/util/delegate.h" #endif #ifndef _TVECTOR_H_ #include "core/util/tVector.h" #endif /// Signals (Multi-cast Delegates) /// /// Signals are used throughout this engine to allow subscribers to listen /// for generated events for various things. /// /// Signals are called according to their order parameter (lower /// numbers first). /// /// Signal functions can return bool or void. If bool then returning false /// from a signal function will cause entries in the ordered signal list after /// that one to not be called. /// /// This allows handlers of a given event to say to the signal generator, "I handled this message, and /// it is no longer appropriate for other listeners to handle it" class SignalBase { public: SignalBase() { mList.next = mList.prev = &mList; mList.mOrder = 0.5f; } ~SignalBase(); /// Removes all the delegates from the signal. void removeAll(); /// Returns true if the delegate list is empty. bool isEmpty() const { return mList.next == &mList; } protected: struct DelegateLink { DelegateLink *next,*prev; F32 mOrder; void insert(DelegateLink* node, F32 order); void unlink(); DelegateLink() :next(NULL), prev(NULL), mOrder(0) {} virtual ~DelegateLink() {} }; DelegateLink mList; /// We need to protect the delegate list against removal of the currently /// triggering node as well removal of the next node in the list. When not /// handling these two cases correctly, removing delegates from a signal /// while it is triggering will lead to crashes. /// /// So this field stores the next node of each active traversal so that when /// we unlink a node, we can check it against this field and move the traversal /// along if needed. Vector mTriggerNext; }; template class SignalBaseT; /// Class for handle automatic diconnect form Signal when destroyed template< typename Signature > class SignalSlot { public: typedef Delegate< Signature > DelegateSig; typedef SignalBaseT< Signature > SignalSig; SignalSlot() : mSignal(NULL) { } ~SignalSlot() { disconnect(); } const DelegateSig& getDelegate() { return mDlg; } /// setDelegate disconect form Signal old delegate and connect new delegate template void setDelegate( const X &fn ) { setDelegate( DelegateSig( fn ) ); } template void setDelegate( const X &ptr, const Y &fn ) { setDelegate( DelegateSig( ptr, fn ) ); } void setDelegate( const DelegateSig &dlg) { SignalSig* signal = mSignal; if( isConnected() ) disconnect(); mDlg = dlg; if( signal && mDlg ) signal->notify( mDlg ); } /// is connected to Signal bool isConnected() const { return mSignal; } /// disconnect from Signal void disconnect() { if( mSignal ) { SignalSig *oldSignal = mSignal; mSignal = NULL; oldSignal->remove( mDlg ); } } protected: friend class SignalBaseT< Signature >; void _setSignal(SignalSig *sig) { mSignal = sig; } SignalSig* _getSignal() const { return mSignal; } DelegateSig mDlg; SignalSig *mSignal; private: SignalSlot( const SignalSlot&); SignalSlot& operator=( const SignalSlot&); }; template class SignalBaseT : public SignalBase { public: /// The delegate signature for this signal. typedef Delegate DelegateSig; SignalBaseT() {} SignalBaseT( const SignalBaseT &base ) { mList.next = mList.prev = &mList; merge( base ); } void operator =( const SignalBaseT &base ) { removeAll(); merge( base ); } void merge( const SignalBaseT &base ) { for ( DelegateLink *ptr = base.mList.next; ptr != &base.mList; ptr = ptr->next ) { DelegateLinkImpl *del = static_cast( ptr ); notify( del->mDelegate, del->mOrder ); } } void notify( const DelegateSig &dlg, F32 order = 0.5f) { mList.insert(new DelegateLinkImpl(dlg), order); } void remove( DelegateSig dlg ) { for( DelegateLink* ptr = mList.next;ptr != &mList; ptr = ptr->next ) { if( DelegateLinkImpl* del = static_cast< DelegateLinkImpl* >( ptr ) ) { if( del->mDelegate == dlg ) { for ( S32 i = 0; i < mTriggerNext.size(); i++ ) { if( mTriggerNext[i] == ptr ) mTriggerNext[i] = ptr->next; } del->unlink(); delete del; return; } } } } template void notify(T obj,U func, F32 order = 0.5f) { DelegateSig dlg(obj, func); notify(dlg, order); } template void notify(T func, F32 order = 0.5f) { DelegateSig dlg(func); notify(dlg, order); } void notify( SignalSlot &slot, F32 order = 0.5f) { if( !slot.getDelegate() ) return; if( slot.isConnected() ) slot.disconnect(); slot._setSignal( this ); mList.insert( new SlotLinkImpl(slot), order ); } template void remove(T obj,U func) { DelegateSig compDelegate(obj, func); remove(compDelegate); } template void remove(T func) { DelegateSig compDelegate(func); remove(compDelegate); } /// Returns true if the signal already contains this delegate. bool contains( const DelegateSig &dlg ) const { for ( DelegateLink *ptr = mList.next; ptr != &mList; ptr = ptr->next ) { DelegateLinkImpl *del = static_cast( ptr ); if ( del->mDelegate == dlg ) return true; } return false; } protected: struct DelegateLinkImpl : public SignalBase::DelegateLink { DelegateSig mDelegate; DelegateLinkImpl(DelegateSig dlg) : mDelegate(dlg) {} }; struct SlotLinkImpl : public DelegateLinkImpl { SlotLinkImpl(SignalSlot& slot) : mSlot( &slot ), DelegateLinkImpl( slot.getDelegate() ) { } ~SlotLinkImpl() { if( mSlot ) mSlot->_setSignal( NULL ); } protected: SignalSlot *mSlot; }; DelegateSig & getDelegate(SignalBase::DelegateLink * link) { return ((DelegateLinkImpl*)link)->mDelegate; } }; //----------------------------------------------------------------------------- template class Signal; // Short-circuit signal implementations template<> class Signal : public SignalBaseT { public: bool trigger() { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )() ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e, F f ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e, f ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e, F f, G g ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e, f, g ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e, F f, G g, H h ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j, k ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j, k, l ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; template class Signal : public SignalBaseT { public: bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l, M m ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j, k, l, m ) ) { this->mTriggerNext.pop_back(); return false; } ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); return true; } }; // Non short-circuit signal implementations template<> class Signal : public SignalBaseT { public: void trigger() { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )(); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e, F f ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e, f ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e, F f, G g ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e, f, g ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e, F f, G g, H h ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e, f, g, h ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j, k ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j, k, l ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; template class Signal : public SignalBaseT { public: void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l, M m ) { this->mTriggerNext.push_back(NULL); for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) { this->mTriggerNext.last() = ptr->next; this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j, k, l, m ); ptr = this->mTriggerNext.last(); } this->mTriggerNext.pop_back(); } }; #endif // _TSIGNAL_H_