// ================================================================================================ // // sigslot.h/sigslot.cpp - Signal/Slot classes: // // Original siglsot implementation written by Sarah Thompson (sarah@telergy.com) 2002 and // published under public domain. // // This version of the sigslot library is heavily modified, C++ compliant, inherently thread-safe, // and offers a manager class that allows to queue and asynchronously dispatch signals. // // Copyright (C) 2012-2014, all rights reserved, // Christian Schulte zu Berge // Chair for Computer Aided Medical Procedures // Technische Universität München // Boltzmannstr. 3, 85748 Garching b. München, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file // except in compliance with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software distributed under the // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing permissions // and limitations under the License. // // ================================================================================================ #ifndef SIGSLOT_H #define SIGSLOT_H #ifdef CAMPVIS_DYNAMIC_LIBS #ifdef SIGSLOT_BUILD_DLL // building library -> export symbols #ifdef WIN32 #define SIGSLOT_API __declspec(dllexport) #else #define SIGSLOT_API #endif #else // including library -> import symbols #ifdef WIN32 #define SIGSLOT_API __declspec(dllimport) #else #define SIGSLOT_API #endif #endif #else // building/including static library -> do nothing #define SIGSLOT_API #endif #include #include #include "tgt/assert.h" #include #define TBB_PREVIEW_MEMORY_POOL 1 #include #include #include #include #include "ext/tgt/runnable.h" #include "ext/tgt/singleton.h" namespace sigslot { /** * List-like container allowing thread-safe bidirectional iteration, insertion and removal of elements. * ATTENTION: Since removed items are internally stored as nullptr, the element type is constrained * to be a pointer type (or at least have pointer semantics) and never be 0. Use with caution! */ template class concurrent_pointer_list { public: /// Typedef for internal storage of elements typedef tbb::concurrent_vector StorageType; /// Iterator for concurrent_pointer_list class concurrent_pointer_list_iterator : public std::iterator { public: concurrent_pointer_list_iterator() {}; concurrent_pointer_list_iterator(StorageType& storage, typename StorageType::iterator position) : _begin(storage.begin()) , _position(position) , _end(storage.end()) { if (_position != _end && *_position == nullptr) operator++(); } concurrent_pointer_list_iterator& operator++() { do { ++_position; } while (_position < _end && *_position == nullptr); return *this; } concurrent_pointer_list_iterator operator++(int) { concurrent_pointer_list_iterator toReturn = *this; operator++(); return toReturn; } concurrent_pointer_list_iterator& operator--() { do { --_position; } while (_position > _begin && *_position == nullptr); return *this; } concurrent_pointer_list_iterator operator--(int) { concurrent_pointer_list_iterator toReturn = *this; operator--(); return toReturn; } T*& operator*() const { return *_position; } T* operator->() const { return &*_position; } bool operator==(const concurrent_pointer_list_iterator& rhs) const { return (_position == rhs._position); } bool operator!=(const concurrent_pointer_list_iterator& rhs) const { return (_position != rhs._position); } protected: typename StorageType::iterator _begin; typename StorageType::iterator _position; typename StorageType::iterator _end; }; // TODO: As I am currently too lazy to correctly implement const_iterator: I hacked this collection to have a standard iterator as const_iterator. typedef concurrent_pointer_list_iterator iterator; ///< iterator typedef typedef concurrent_pointer_list_iterator const_iterator; ///< const_iterator typedef /** * Creates a new concurrent_pointer_list object of default size */ concurrent_pointer_list() {}; /** * Creates a new concurrent_pointer_list object of default size * \param initalSize Initial size fo the internal storage */ concurrent_pointer_list(typename StorageType::size_type initalSize) : _storage(initalSize) {}; /// Return an iterator to the beginning of the collection iterator begin() { return iterator(_storage, _storage.begin()); }; /// Return an iterator to the beginning of the collection const_iterator begin() const { StorageType& s = const_cast(_storage); // const_cast neccessary, because we did not implement const_iterator return const_iterator(s, s.begin()); }; /// Return an iterator to the end of the collection iterator end() { return iterator(_storage, _storage.end()); }; /// Return an iterator to the end of the collection const_iterator end() const { StorageType& s = const_cast(_storage); // const_cast neccessary, because we did not implement const_iterator return const_iterator(s, s.end()); }; void push_back(T* element) { insert(element); }; void insert(T* element); size_t erase(T* element); void erase(const const_iterator& it) { *it = nullptr; }; void erase(const const_iterator& begin, const const_iterator& end); bool empty() const; protected: StorageType _storage; }; // ================================================================================================ template void concurrent_pointer_list::insert(T* element) { iterator it = begin(); iterator itEnd = end(); // to ensure non-degrading performance, we first fill the gaps. for (/* nothing here */; it != itEnd; ++it) { if (*it == nullptr) { *it = element; return; } } // we did not find an empty element -> we add a new one at the end _storage.push_back(element); } template size_t concurrent_pointer_list::erase(T* element) { size_t count = 0; iterator it = begin(); iterator itEnd = end(); for (/* nothing here */; it != itEnd; ++it) { if (*it == element) { *it = nullptr; ++count; } } return count; } template void concurrent_pointer_list::erase(const typename concurrent_pointer_list::const_iterator& begin, const typename concurrent_pointer_list::const_iterator& end) { for (const_iterator it = begin; it != end; ++it) { *it = nullptr; } } template bool sigslot::concurrent_pointer_list::empty() const { iterator it = begin(); iterator itEnd = end(); for (/* nothing here */; it != itEnd; ++it) { if (*it != nullptr) { return false; } } return true; } // ================================================================================================ /// Base class for signal handles that provides an interface to emit the signal. class SIGSLOT_API _signal_handle_base { public: /// Virtual destructor virtual ~_signal_handle_base() {}; /// Emits the signal of this signal handle. virtual void processSignal() const = 0; static void* operator new(std::size_t size) throw(std::bad_alloc); static void operator delete(void* rawMemory, std::size_t size) throw(); #ifdef CAMPVIS_DEBUG // This is debug information only, automatically removed from release builds std::string _callingFunction; ///< Function that emitted this signal std::string _callingFile; ///< File which emitted this signal int _callingLine; ///< Line where this signal was emitted #endif }; // ================================================================================================ /** * Singleton class that takes care of queuing and asynchronously dispatching signals. * * The signal_manager implements the Runnable interface, i.e. runs in it's own thread once it * was launched. The signal_manager takes care of dispatching signals to their connections. * This is either done synchrnously by using triggerSignalImpl() or asynchronously by using * queueSignalImpl(). Furthermore it allows to check, whether the current thread is the * signal_manager thread. This allows the default signal emitting method operator() to * automatically decide on the dispatch type based on the emitting thread. * * signal_manager can be considered as thread-safe. */ class SIGSLOT_API signal_manager : public tgt::Singleton, public tgt::Runnable { friend class tgt::Singleton; friend class _signal_handle_base; public: /// Enumeration of signal handling modes for the signal_manager enum SignalHandlingMode { DEFAULT, ///< Default mode is that all signals are queued unless they are emitted from the signal_manager thread. FORCE_DIRECT, ///< Force all signals being directly handled by the emitting thread. FORCE_QUEUE ///< Force all signals being queued and handled by the signal_manager thread. }; /** * Returns the signal handling mode of this signal_manager. */ SignalHandlingMode getSignalHandlingMode() const; /** * Sets the signal handling mode of this signal_manager to \mode. * \param mode New signal handling mode to set. */ void setSignalHandlingMode(SignalHandlingMode mode); /** * Directly dispatches the signal \a signal to all currently registered listeners. * \note For using threaded signal dispatching use queueSignalImpl() * \param signal signal to dispatch */ void triggerSignalImpl(_signal_handle_base* signal); /** * Enqueue \a signal into the list of signals to be dispatched. * \note Dispatch will be performed in signal_mananger thread. For direct dispatch in * caller thread use triggerSignalImpl(). * \param signal signal to dispatch * \return True, if \a signal was successfully enqueued to signal queue. */ bool queueSignalImpl(_signal_handle_base* signal); /** * Checks whether calling thread is signal_manager thread. * \return std::this_thread::get_id() == _this_thread_id */ bool isCurrentThreadSignalManagerThread() const; /// \see Runnable:run virtual void run(); /// \see Runnable:stop virtual void stop(); private: /// Private constructor only for singleton signal_manager(); /// Private destructor only for singleton ~signal_manager(); /// Typedef for the signal queue typedef tbb::concurrent_queue<_signal_handle_base*> SignalQueue; SignalHandlingMode _handlingMode; ///< Mode for handling signals SignalQueue _signalQueue; ///< Queue for signals to be dispatched std::condition_variable _evaluationCondition; ///< conditional wait to be used when there are currently no jobs to process std::mutex _ecMutex; ///< Mutex for protecting _evaluationCondition std::thread::id _this_thread_id; typedef std::allocator<_signal_handle_base> pool_allocator_t; tbb::memory_pool _signalPool; ///< Memory pool for the signals static const std::string loggerCat_; }; // ================================================================================================ class SIGSLOT_API has_slots; class SIGSLOT_API _connection_base0 { public: virtual ~_connection_base0() {} virtual has_slots* getdest() const = 0; virtual void processSignal() = 0; virtual _connection_base0* clone() = 0; virtual _connection_base0* duplicate(has_slots* pnewdest) = 0; }; template class _connection_base1 { public: virtual ~_connection_base1() {} virtual has_slots* getdest() const = 0; virtual void processSignal(arg1_type) = 0; virtual _connection_base1* clone() = 0; virtual _connection_base1* duplicate(has_slots* pnewdest) = 0; }; template class _connection_base2 { public: virtual ~_connection_base2() {} virtual has_slots* getdest() const = 0; virtual void processSignal(arg1_type, arg2_type) = 0; virtual _connection_base2* clone() = 0; virtual _connection_base2* duplicate(has_slots* pnewdest) = 0; }; template class _connection_base3 { public: virtual ~_connection_base3() {} virtual has_slots* getdest() const = 0; virtual void processSignal(arg1_type, arg2_type, arg3_type) = 0; virtual _connection_base3* clone() = 0; virtual _connection_base3* duplicate(has_slots* pnewdest) = 0; }; template class _connection_base4 { public: virtual ~_connection_base4() {} virtual has_slots* getdest() const = 0; virtual void processSignal(arg1_type, arg2_type, arg3_type, arg4_type) = 0; virtual _connection_base4* clone() = 0; virtual _connection_base4* duplicate(has_slots* pnewdest) = 0; }; template class _connection_base5 { public: virtual ~_connection_base5() {} virtual has_slots* getdest() const = 0; virtual void processSignal(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type) = 0; virtual _connection_base5* clone() = 0; virtual _connection_base5* duplicate(has_slots* pnewdest) = 0; }; // ================================================================================================ class SIGSLOT_API _signal_base { public: virtual void slot_disconnect(has_slots* pslot) = 0; virtual void slot_duplicate(has_slots const* poldslot, has_slots* pnewslot) = 0; #ifdef CAMPVIS_DEBUG protected: // This is debug information only, automatically removed from release builds // As we're using local variables, we have to protect them with the mutex. This is neither // beautiful nor efficient, but as it's for debug-only code, we should be able to live with it. // In a side note: This mutex is sufficient to avoid segfaults or other crashes. However, it // does not guarantee the correct order of execution. Hence, there exist cases // where the debug information stored in the signal handle is not correct. // This is a risk we can take, as the chance that the *very same* signal // gets emitted concurrently form different threads is very low. tbb::spin_mutex _debugMutex; ///< Mutex to protect these three variables std::string _callingFunction; ///< Function that emitted the last signal std::string _callingFile; ///< File which emitted the last signal int _callingLine; ///< Line where the last signal was emitted // In debug mode, this macro writes the caller information to the signal handle #define writeDebugInfoToSignalHandle(_the_signal_handle) \ { \ tbb::spin_mutex::scoped_lock lock(this->_debugMutex); \ _the_signal_handle->_callingFunction = this->_callingFunction; \ _the_signal_handle->_callingFile = this->_callingFile; \ _the_signal_handle->_callingLine = this->_callingLine; \ } #else // In release mode, this macro expands to a NOP. #define writeDebugInfoToSignalHandle(_the_signal_handle) #endif }; class SIGSLOT_API has_slots { private: typedef concurrent_pointer_list<_signal_base> sender_set; typedef sender_set::const_iterator const_iterator; public: has_slots() {} has_slots(has_slots const& hs) { const_iterator it = hs.m_senders.begin(); const_iterator itEnd = hs.m_senders.end(); while (it != itEnd) { (*it)->slot_duplicate(&hs, this); m_senders.insert(*it); ++it; } } void signal_connect(_signal_base* sender) { m_senders.insert(sender); } void signal_disconnect(_signal_base* sender) { m_senders.erase(sender); } virtual ~has_slots() { disconnect_all(); } void disconnect_all() { const_iterator it = m_senders.begin(); const_iterator itEnd = m_senders.end(); while (it != itEnd) { (*it)->slot_disconnect(this); ++it; } m_senders.erase(m_senders.begin(), m_senders.end()); } private: sender_set m_senders; }; class _signal_base0 : public _signal_base { public: typedef concurrent_pointer_list<_connection_base0 > connections_list; _signal_base0() {} _signal_base0(_signal_base0 const& s) : _signal_base(s) { connections_list::const_iterator it = s.m_connected_slots.begin(); connections_list::const_iterator itEnd = s.m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_connect(this); m_connected_slots.push_back((*it)->clone()); ++it; } } ~_signal_base0() { disconnect_all(); } void disconnect_all() { connections_list::const_iterator it = m_connected_slots.begin(); connections_list::const_iterator itEnd = m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_disconnect(this); delete *it; ++it; } m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); } void disconnect(has_slots* pclass) { connections_list::iterator it = m_connected_slots.begin(); connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == pclass) { delete *it; m_connected_slots.erase(it); pclass->signal_disconnect(this); return; } ++it; } } void slot_disconnect(has_slots* pslot) { connections_list::iterator it = m_connected_slots.begin(); connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { connections_list::iterator itNext = it; ++itNext; if ((*it)->getdest() == pslot) { m_connected_slots.erase(it); // delete *it; } it = itNext; } } void slot_duplicate(has_slots const* oldtarget, has_slots* newtarget) { connections_list::iterator it = m_connected_slots.begin(); connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == oldtarget) { m_connected_slots.push_back((*it)->duplicate(newtarget)); } ++it; } } bool has_connections() const { return !m_connected_slots.empty(); } protected: connections_list m_connected_slots; }; template class _signal_base1 : public _signal_base { public: typedef concurrent_pointer_list<_connection_base1 > connections_list; _signal_base1() {} _signal_base1(_signal_base1 const& s) : _signal_base(s) { typename connections_list::const_iterator it = s.m_connected_slots.begin(); typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_connect(this); m_connected_slots.push_back((*it)->clone()); ++it; } } void slot_duplicate(has_slots const* oldtarget, has_slots* newtarget) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == oldtarget) { m_connected_slots.push_back((*it)->duplicate(newtarget)); } ++it; } } ~_signal_base1() { disconnect_all(); } void disconnect_all() { typename connections_list::const_iterator it = m_connected_slots.begin(); typename connections_list::const_iterator itEnd = m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_disconnect(this); delete *it; ++it; } m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); } void disconnect(has_slots* pclass) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == pclass) { delete *it; m_connected_slots.erase(it); pclass->signal_disconnect(this); return; } ++it; } } void slot_disconnect(has_slots* pslot) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { typename connections_list::iterator itNext = it; ++itNext; if ((*it)->getdest() == pslot) { m_connected_slots.erase(it); // delete *it; } it = itNext; } } bool has_connections() const { return !m_connected_slots.empty(); } protected: connections_list m_connected_slots; }; template class _signal_base2 : public _signal_base { public: typedef concurrent_pointer_list<_connection_base2 > connections_list; _signal_base2() {} _signal_base2(_signal_base2 const& s) : _signal_base(s) { typename connections_list::const_iterator it = s.m_connected_slots.begin(); typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_connect(this); m_connected_slots.push_back((*it)->clone()); ++it; } } void slot_duplicate(has_slots const* oldtarget, has_slots* newtarget) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == oldtarget) { m_connected_slots.push_back((*it)->duplicate(newtarget)); } ++it; } } ~_signal_base2() { disconnect_all(); } void disconnect_all() { typename connections_list::const_iterator it = m_connected_slots.begin(); typename connections_list::const_iterator itEnd = m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_disconnect(this); delete *it; ++it; } m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); } void disconnect(has_slots* pclass) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == pclass) { delete *it; m_connected_slots.erase(it); pclass->signal_disconnect(this); return; } ++it; } } void slot_disconnect(has_slots* pslot) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { typename connections_list::iterator itNext = it; ++itNext; if ((*it)->getdest() == pslot) { m_connected_slots.erase(it); // delete *it; } it = itNext; } } bool has_connections() const { return !m_connected_slots.empty(); } protected: connections_list m_connected_slots; }; template class _signal_base3 : public _signal_base { public: typedef concurrent_pointer_list<_connection_base3 > connections_list; _signal_base3() {} _signal_base3(_signal_base3 const& s) : _signal_base(s) { typename connections_list::const_iterator it = s.m_connected_slots.begin(); typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_connect(this); m_connected_slots.push_back((*it)->clone()); ++it; } } void slot_duplicate(has_slots const* oldtarget, has_slots* newtarget) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == oldtarget) { m_connected_slots.push_back((*it)->duplicate(newtarget)); } ++it; } } ~_signal_base3() { disconnect_all(); } void disconnect_all() { typename connections_list::const_iterator it = m_connected_slots.begin(); typename connections_list::const_iterator itEnd = m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_disconnect(this); delete *it; ++it; } m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); } void disconnect(has_slots* pclass) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == pclass) { delete *it; m_connected_slots.erase(it); pclass->signal_disconnect(this); return; } ++it; } } void slot_disconnect(has_slots* pslot) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { typename connections_list::iterator itNext = it; ++itNext; if ((*it)->getdest() == pslot) { m_connected_slots.erase(it); // delete *it; } it = itNext; } } bool has_connections() const { return !m_connected_slots.empty(); } protected: connections_list m_connected_slots; }; template class _signal_base4 : public _signal_base { public: typedef concurrent_pointer_list<_connection_base4 > connections_list; _signal_base4() {} _signal_base4(const _signal_base4& s) : _signal_base(s) { typename connections_list::const_iterator it = s.m_connected_slots.begin(); typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_connect(this); m_connected_slots.push_back((*it)->clone()); ++it; } } void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == oldtarget) { m_connected_slots.push_back((*it)->duplicate(newtarget)); } ++it; } } ~_signal_base4() { disconnect_all(); } void disconnect_all() { typename connections_list::const_iterator it = m_connected_slots.begin(); typename connections_list::const_iterator itEnd = m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_disconnect(this); delete *it; ++it; } m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); } void disconnect(has_slots* pclass) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == pclass) { delete *it; m_connected_slots.erase(it); pclass->signal_disconnect(this); return; } ++it; } } void slot_disconnect(has_slots* pslot) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { typename connections_list::iterator itNext = it; ++itNext; if ((*it)->getdest() == pslot) { m_connected_slots.erase(it); // delete *it; } it = itNext; } } bool has_connections() const { return !m_connected_slots.empty(); } protected: connections_list m_connected_slots; }; template class _signal_base5 : public _signal_base { public: typedef concurrent_pointer_list<_connection_base5 > connections_list; _signal_base5() {} _signal_base5(const _signal_base5& s) : _signal_base(s) { typename connections_list::const_iterator it = s.m_connected_slots.begin(); typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_connect(this); m_connected_slots.push_back((*it)->clone()); ++it; } } void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == oldtarget) { m_connected_slots.push_back((*it)->duplicate(newtarget)); } ++it; } } ~_signal_base5() { disconnect_all(); } void disconnect_all() { typename connections_list::const_iterator it = m_connected_slots.begin(); typename connections_list::const_iterator itEnd = m_connected_slots.end(); while (it != itEnd) { (*it)->getdest()->signal_disconnect(this); delete *it; ++it; } m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); } void disconnect(has_slots* pclass) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { if ((*it)->getdest() == pclass) { delete *it; m_connected_slots.erase(it); pclass->signal_disconnect(this); return; } ++it; } } void slot_disconnect(has_slots* pslot) { typename connections_list::iterator it = m_connected_slots.begin(); typename connections_list::iterator itEnd = m_connected_slots.end(); while (it != itEnd) { typename connections_list::iterator itNext = it; ++itNext; if ((*it)->getdest() == pslot) { m_connected_slots.erase(it); // delete *it; } it = itNext; } } bool has_connections() const { return !m_connected_slots.empty(); } protected: connections_list m_connected_slots; }; // ================================================================================================ template class _connection0 : public _connection_base0 { public: _connection0() { m_pobject = 0; m_pmemfun = 0; } _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) { m_pobject = pobject; m_pmemfun = pmemfun; } virtual _connection_base0* clone() { return new _connection0(*this); } virtual _connection_base0* duplicate(has_slots* pnewdest) { return new _connection0((dest_type *)pnewdest, m_pmemfun); } virtual void processSignal() { (m_pobject->*m_pmemfun)(); } virtual has_slots* getdest() const { return m_pobject; } private: dest_type* m_pobject; void (dest_type::* m_pmemfun)(); }; template class _connection1 : public _connection_base1 { public: _connection1() { m_pobject = 0; m_pmemfun = 0; } _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) { m_pobject = pobject; m_pmemfun = pmemfun; } virtual _connection_base1* clone() { return new _connection1(*this); } virtual _connection_base1* duplicate(has_slots* pnewdest) { return new _connection1((dest_type *)pnewdest, m_pmemfun); } virtual void processSignal(arg1_type a1) { (m_pobject->*m_pmemfun)(a1); } virtual has_slots* getdest() const { return m_pobject; } private: dest_type* m_pobject; void (dest_type::* m_pmemfun)(arg1_type); }; template class _connection2 : public _connection_base2 { public: _connection2() { m_pobject = 0; m_pmemfun = 0; } _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, arg2_type)) { m_pobject = pobject; m_pmemfun = pmemfun; } virtual _connection_base2* clone() { return new _connection2(*this); } virtual _connection_base2* duplicate(has_slots* pnewdest) { return new _connection2((dest_type *)pnewdest, m_pmemfun); } virtual void processSignal(arg1_type a1, arg2_type a2) { (m_pobject->*m_pmemfun)(a1, a2); } virtual has_slots* getdest() const { return m_pobject; } private: dest_type* m_pobject; void (dest_type::* m_pmemfun)(arg1_type, arg2_type); }; template class _connection3 : public _connection_base3 { public: _connection3() { m_pobject = 0; m_pmemfun = 0; } _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, arg2_type, arg3_type)) { m_pobject = pobject; m_pmemfun = pmemfun; } virtual _connection_base3* clone() { return new _connection3(*this); } virtual _connection_base3* duplicate(has_slots* pnewdest) { return new _connection3((dest_type *)pnewdest, m_pmemfun); } virtual void processSignal(arg1_type a1, arg2_type a2, arg3_type a3) { (m_pobject->*m_pmemfun)(a1, a2, a3); } virtual has_slots* getdest() const { return m_pobject; } private: dest_type* m_pobject; void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); }; template class _connection4 : public _connection_base4 { public: _connection4() { m_pobject = 0; m_pmemfun = 0; } _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type)) { m_pobject = pobject; m_pmemfun = pmemfun; } virtual _connection_base4* clone() { return new _connection4(*this); } virtual _connection_base4* duplicate(has_slots* pnewdest) { return new _connection4((dest_type *)pnewdest, m_pmemfun); } virtual void processSignal(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) { (m_pobject->*m_pmemfun)(a1, a2, a3, a4); } virtual has_slots* getdest() const { return m_pobject; } private: dest_type* m_pobject; void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type); }; template class _connection5 : public _connection_base5 { public: _connection5() { m_pobject = 0; m_pmemfun = 0; } _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type)) { m_pobject = pobject; m_pmemfun = pmemfun; } virtual _connection_base5* clone() { return new _connection5(*this); } virtual _connection_base5* duplicate(has_slots* pnewdest) { return new _connection5((dest_type *)pnewdest, m_pmemfun); } virtual void processSignal(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5) { (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); } virtual has_slots* getdest() const { return m_pobject; } private: dest_type* m_pobject; void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type); }; // ================================================================================================ class signal0 : public _signal_base0 { public: typedef _signal_base0 base; typedef base::connections_list connections_list; using base::m_connected_slots; class signal_handle0 : public _signal_handle_base { public: signal_handle0(signal0* sender) : _sender(sender) {}; virtual ~signal_handle0() {}; // override void processSignal() const { connections_list::const_iterator itNext, it = _sender->m_connected_slots.begin(); connections_list::const_iterator itEnd = _sender->m_connected_slots.end(); while (it != itEnd) { itNext = it; ++itNext; (*it)->processSignal(); it = itNext; } }; signal0* _sender; }; signal0() {} signal0(const signal0& s) : _signal_base0(s) {} virtual ~signal0() {} template void connect(desttype* pclass, void (desttype::*pmemfun)()) { _connection0* conn = new _connection0(pclass, pmemfun); m_connected_slots.push_back(conn); pclass->signal_connect(this); } void triggerSignal() { signal_handle0* sh = new signal_handle0(this); writeDebugInfoToSignalHandle(sh); signal_manager::getRef().triggerSignalImpl(sh); } void queueSignal() { signal_handle0* sh = new signal_handle0(this); writeDebugInfoToSignalHandle(sh); signal_manager::getRef().queueSignalImpl(sh); } void emitSignal() { if (signal_manager::getRef().isCurrentThreadSignalManagerThread()) triggerSignal(); else queueSignal(); } void operator()() { emitSignal(); } }; template class signal1 : public _signal_base1 { public: typedef _signal_base1 base; typedef typename base::connections_list connections_list; using base::m_connected_slots; class signal_handle1 : public _signal_handle_base { public: signal_handle1(signal1* sender, arg1_type a1) : _sender(sender) , _a1(a1) {}; virtual ~signal_handle1() {}; // override void processSignal() const { typename connections_list::const_iterator itNext, it = _sender->m_connected_slots.begin(); typename connections_list::const_iterator itEnd = _sender->m_connected_slots.end(); while (it != itEnd) { itNext = it; ++itNext; (*it)->processSignal(_a1); it = itNext; } }; signal1* _sender; arg1_type _a1; }; signal1() {} signal1(const signal1& s) : _signal_base1(s) {} virtual ~signal1() {} template void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) { _connection1* conn = new _connection1(pclass, pmemfun); m_connected_slots.push_back(conn); pclass->signal_connect(this); } void triggerSignal(arg1_type a1) { signal_handle1* sh = new signal_handle1(this, a1); writeDebugInfoToSignalHandle(sh); signal_manager::getRef().triggerSignalImpl(sh); } void queueSignal(arg1_type a1) { signal_handle1* sh = new signal_handle1(this, a1); writeDebugInfoToSignalHandle(sh); signal_manager::getRef().queueSignalImpl(sh); } void emitSignal(arg1_type a1) { if (signal_manager::getRef().isCurrentThreadSignalManagerThread()) triggerSignal(a1); else queueSignal(a1); } void operator()(arg1_type a1) { emitSignal(a1); } }; template class signal2 : public _signal_base2 { public: typedef _signal_base2 base; typedef typename base::connections_list connections_list; using base::m_connected_slots; class signal_handle2 : public _signal_handle_base { public: signal_handle2(signal2* sender, arg1_type a1, arg2_type a2) : _sender(sender) , _a1(a1) , _a2(a2) {}; virtual ~signal_handle2() {}; // override void processSignal() const { typename connections_list::const_iterator itNext, it = _sender->m_connected_slots.begin(); typename connections_list::const_iterator itEnd = _sender->m_connected_slots.end(); while (it != itEnd) { itNext = it; ++itNext; (*it)->processSignal(_a1, _a2); it = itNext; } }; signal2* _sender; arg1_type _a1; arg2_type _a2; }; signal2() {} signal2(const signal2& s) : _signal_base2(s) {} virtual ~signal2() {} template void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, arg2_type)) { _connection2* conn = new _connection2(pclass, pmemfun); m_connected_slots.push_back(conn); pclass->signal_connect(this); } void triggerSignal(arg1_type a1, arg2_type a2) { signal_handle2* sh = new signal_handle2(this, a1, a2); writeDebugInfoToSignalHandle(sh); signal_manager::getRef().triggerSignalImpl(sh); } void queueSignal(arg1_type a1, arg2_type a2) { signal_handle2* sh = new signal_handle2(this, a1, a2); writeDebugInfoToSignalHandle(sh); signal_manager::getRef().queueSignalImpl(sh); } void emitSignal(arg1_type a1, arg2_type a2) { if (signal_manager::getRef().isCurrentThreadSignalManagerThread()) triggerSignal(a1, a2); else queueSignal(a1, a2); } void operator()(arg1_type a1, arg2_type a2) { emitSignal(a1, a2); } }; template class signal3 : public _signal_base3 { public: typedef _signal_base3 base; typedef typename base::connections_list connections_list; using base::m_connected_slots; class signal_handle3 : public _signal_handle_base { public: signal_handle3(signal3* sender, arg1_type a1, arg2_type a2, arg3_type a3) : _sender(sender) , _a1(a1) , _a2(a2) , _a3(a3) {}; virtual ~signal_handle3() {}; // override void processSignal() const { typename connections_list::const_iterator itNext, it = _sender->m_connected_slots.begin(); typename connections_list::const_iterator itEnd = _sender->m_connected_slots.end(); while (it != itEnd) { itNext = it; ++itNext; (*it)->processSignal(_a1, _a2, _a3); it = itNext; } }; signal3* _sender; arg1_type _a1; arg2_type _a2; arg3_type _a3; }; signal3() {} signal3(const signal3& s) : _signal_base3(s) {} virtual ~signal3() {} template void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, arg2_type, arg3_type)) { _connection3* conn = new _connection3(pclass, pmemfun); m_connected_slots.push_back(conn); pclass->signal_connect(this); } void triggerSignal(arg1_type a1, arg2_type a2, arg3_type a3) { signal_handle3* sh = new signal_handle3(this, a1, a2, a3); signal_manager::getRef().triggerSignalImpl(sh); } void queueSignal(arg1_type a1, arg2_type a2, arg3_type a3) { signal_handle3* sh = new signal_handle3(this, a1, a2, a3); signal_manager::getRef().queueSignalImpl(sh); } void emitSignal(arg1_type a1, arg2_type a2, arg3_type a3) { if (signal_manager::getRef().isCurrentThreadSignalManagerThread()) triggerSignal(a1, a2, a3); else queueSignal(a1, a2, a3); } void operator()(arg1_type a1, arg2_type a2, arg3_type a3) { emitSignal(a1, a2, a3); } }; template class signal4 : public _signal_base4 { public: typedef _signal_base4 base; typedef typename base::connections_list connections_list; using base::m_connected_slots; class signal_handle4 : public _signal_handle_base { public: signal_handle4(signal4* sender, arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) : _sender(sender) , _a1(a1) , _a2(a2) , _a3(a3) , _a4(a4) {}; virtual ~signal_handle4() {}; // override void processSignal() const { typename connections_list::const_iterator itNext, it = _sender->m_connected_slots.begin(); typename connections_list::const_iterator itEnd = _sender->m_connected_slots.end(); while (it != itEnd) { itNext = it; ++itNext; (*it)->processSignal(_a1, _a2, _a3, _a4); it = itNext; } }; signal4* _sender; arg1_type _a1; arg2_type _a2; arg3_type _a3; arg4_type _a4; }; signal4() {} signal4(const signal4& s) : _signal_base4(s) {} virtual ~signal4() {} template void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type)) { _connection4* conn = new _connection4(pclass, pmemfun); m_connected_slots.push_back(conn); pclass->signal_connect(this); } void triggerSignal(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) { signal_handle4* sh = new signal_handle4(this, a1, a2, a3, a4); signal_manager::getRef().triggerSignalImpl(sh); } void queueSignal(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) { signal_handle4* sh = new signal_handle4(this, a1, a2, a3, a4); signal_manager::getRef().queueSignalImpl(sh); } void emitSignal(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) { if (signal_manager::getRef().isCurrentThreadSignalManagerThread()) triggerSignal(a1, a2, a3, a4); else queueSignal(a1, a2, a3, a4); } void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) { emitSignal(a1, a2, a3, a4); } }; template class signal5 : public _signal_base5 { public: typedef _signal_base5 base; typedef typename base::connections_list connections_list; using base::m_connected_slots; class signal_handle5 : public _signal_handle_base { public: signal_handle5(signal5* sender, arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5) : _sender(sender) , _a1(a1) , _a2(a2) , _a3(a3) , _a4(a4) , _a5(a5) {}; virtual ~signal_handle5() {}; // override void processSignal() const { typename connections_list::const_iterator itNext, it = _sender->m_connected_slots.begin(); typename connections_list::const_iterator itEnd = _sender->m_connected_slots.end(); while (it != itEnd) { itNext = it; ++itNext; (*it)->processSignal(_a1, _a2, _a3, _a4, _a5); it = itNext; } }; signal5* _sender; arg1_type _a1; arg2_type _a2; arg3_type _a3; arg4_type _a4; arg5_type _a5; }; signal5() {} signal5(const signal5& s) : _signal_base5(s) {} virtual ~signal5() {} template void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type)) { _connection5* conn = new _connection5(pclass, pmemfun); m_connected_slots.push_back(conn); pclass->signal_connect(this); } void triggerSignal(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5) { signal_handle5* sh = new signal_handle5(this, a1, a2, a3, a4, a5); signal_manager::getRef().triggerSignalImpl(sh); } void queueSignal(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5) { signal_handle5* sh = new signal_handle5(this, a1, a2, a3, a4, a5); signal_manager::getRef().queueSignalImpl(sh); } void emitSignal(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5) { if (signal_manager::getRef().isCurrentThreadSignalManagerThread()) triggerSignal(a1, a2, a3, a4, a5); else queueSignal(a1, a2, a3, a4, a5); } void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5) { emitSignal(a1, a2, a3, a4, a5); } }; #ifdef CAMPVIS_DEBUG // These are decorator classes (non strictly speaking) to add debug functionality to signals. // They overload the emitSignal() method with a version that stores calling function, file and line. // Together with the macro magic further down, this provides us with a clean, non-intrusive way // to automatically enhance all client calls to emitSignal with additional debug information. // In debug mode, this macro writes the caller information to the signal handle #define writeDebugInfoToSignal() \ tbb::spin_mutex::scoped_lock lock(this->_debugMutex); \ this->_callingFunction = caller; \ this->_callingFile = file; \ this->_callingLine = line; // // Implementaion inspired by http://stackoverflow.com/questions/353180/how-do-i-find-the-name-of-the-calling-function class signal0_debug_decorator : public signal0 { public: signal0_debug_decorator() {} signal0_debug_decorator(const signal0_debug_decorator& s) : signal0(s) {} virtual ~signal0_debug_decorator() {} signal0& triggerSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal0& queueSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal0& emitSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } }; template class signal1_debug_decorator : public signal1 { public: signal1_debug_decorator() {} signal1_debug_decorator(const signal1_debug_decorator& s) : signal1(s) {} virtual ~signal1_debug_decorator() {} signal1& triggerSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal1& queueSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal1& emitSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } }; template class signal2_debug_decorator : public signal2 { public: signal2_debug_decorator() {} signal2_debug_decorator(const signal2_debug_decorator& s) : signal2(s) {} virtual ~signal2_debug_decorator() {} signal2& triggerSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal2& queueSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal2& emitSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } }; template class signal3_debug_decorator : public signal3 { public: signal3_debug_decorator() {} signal3_debug_decorator(const signal3_debug_decorator& s) : signal3(s) {} virtual ~signal3_debug_decorator() {} signal3& triggerSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal3& queueSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal3& emitSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } }; template class signal4_debug_decorator : public signal4 { public: signal4_debug_decorator() {} signal4_debug_decorator(const signal4_debug_decorator& s) : signal4(s) {} virtual ~signal4_debug_decorator() {} signal4& triggerSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal4& queueSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal4& emitSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } }; template class signal5_debug_decorator : public signal5 { public: signal5_debug_decorator() {} signal5_debug_decorator(const signal5_debug_decorator& s) : signal5(s) {} virtual ~signal5_debug_decorator() {} signal5& triggerSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal5& queueSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } signal5& emitSignal(std::string caller, std::string file, int line) { writeDebugInfoToSignal(); return *this; } }; // redefine all necessary symbols #undef signal0 #define signal0 signal0_debug_decorator #undef signal1 #define signal1 signal1_debug_decorator #undef signal2 #define signal2 signal2_debug_decorator #undef signal3 #define signal3 signal3_debug_decorator #undef signal4 #define signal4 signal4_debug_decorator #undef signal5 #define signal5 signal5_debug_decorator #undef triggerSignal #define triggerSignal triggerSignal(__FUNCTION__, __FILE__, __LINE__).triggerSignal #undef queueSignal #define queueSignal queueSignal(__FUNCTION__, __FILE__, __LINE__).queueSignal #undef emitSignal #define emitSignal emitSignal(__FUNCTION__, __FILE__, __LINE__) #endif } // namespace sigslot #endif // SIGSLOT_H