/* Template for Signa1, Signal2, ... classes that support signals with 1, 2, ... parameters Begin: 2007-01-23 */ // Copyright Frank Mori Hess 2007-2008 // // Use, modification and // distribution is subject to the Boost Software License, Version // 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // This file is included iteratively, and should not be protected from multiple inclusion #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES #define BOOST_SIGNALS2_NUM_ARGS BOOST_PP_ITERATION() #else #define BOOST_SIGNALS2_NUM_ARGS 1 #endif // R, T1, T2, ..., TN, Combiner, Group, GroupCompare, SlotFunction, ExtendedSlotFunction, Mutex #define BOOST_SIGNALS2_SIGNAL_TEMPLATE_INSTANTIATION \ BOOST_SIGNALS2_SIGNATURE_TEMPLATE_INSTANTIATION(BOOST_SIGNALS2_NUM_ARGS), \ Combiner, Group, GroupCompare, SlotFunction, ExtendedSlotFunction, Mutex namespace boost { namespace signals2 { namespace detail { // helper for bound_extended_slot_function that handles specialization for void return template class BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_INVOKER_N(BOOST_SIGNALS2_NUM_ARGS) { public: typedef R result_type; template result_type operator()(ExtendedSlotFunction &func, const connection &conn BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_FULL_REF_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const { return func(conn BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); } }; #ifdef BOOST_NO_VOID_RETURNS template<> class BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_INVOKER_N(BOOST_SIGNALS2_NUM_ARGS) { public: typedef result_type_wrapper::type result_type; template result_type operator()(ExtendedSlotFunction &func, const connection &conn BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_FULL_REF_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const { func(conn BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); return result_type(); } }; #endif // wrapper around an signalN::extended_slot_function which binds the // connection argument so it looks like a normal // signalN::slot_function template class BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_N(BOOST_SIGNALS2_NUM_ARGS) { public: typedef typename result_type_wrapper::type result_type; BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_N(BOOST_SIGNALS2_NUM_ARGS)(const ExtendedSlotFunction &fun): _fun(fun), _connection(new connection) {} void set_connection(const connection &conn) { *_connection = conn; } #if BOOST_SIGNALS2_NUM_ARGS > 0 template #endif // BOOST_SIGNALS2_NUM_ARGS > 0 result_type operator()(BOOST_SIGNALS2_FULL_REF_ARGS(BOOST_SIGNALS2_NUM_ARGS)) { return BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_INVOKER_N(BOOST_SIGNALS2_NUM_ARGS) () (_fun, *_connection BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); } // const overload #if BOOST_SIGNALS2_NUM_ARGS > 0 template #endif // BOOST_SIGNALS2_NUM_ARGS > 0 result_type operator()(BOOST_SIGNALS2_FULL_REF_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const { return BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_INVOKER_N(BOOST_SIGNALS2_NUM_ARGS) () (_fun, *_connection BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); } template bool operator==(const T &other) const { return _fun == other; } private: BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_N(BOOST_SIGNALS2_NUM_ARGS)() {} ExtendedSlotFunction _fun; boost::shared_ptr _connection; }; template class BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS); template class BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_SIGNAL_TEMPLATE_SPECIALIZATION { public: typedef SlotFunction slot_function_type; // typedef slotN slot_type; typedef BOOST_SIGNALS2_SLOT_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) slot_type; typedef ExtendedSlotFunction extended_slot_function_type; // typedef slotN+1 extended_slot_type; typedef BOOST_SIGNALS2_EXTENDED_SLOT_TYPE(BOOST_SIGNALS2_NUM_ARGS) extended_slot_type; typedef typename nonvoid::type nonvoid_slot_result_type; private: #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES class slot_invoker; #else // BOOST_NO_CXX11_VARIADIC_TEMPLATES typedef variadic_slot_invoker slot_invoker; #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES typedef slot_call_iterator_cache slot_call_iterator_cache_type; typedef typename group_key::type group_key_type; typedef shared_ptr > connection_body_type; typedef grouped_list connection_list_type; typedef BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_N(BOOST_SIGNALS2_NUM_ARGS) bound_extended_slot_function_type; public: typedef Combiner combiner_type; typedef typename result_type_wrapper::type result_type; typedef Group group_type; typedef GroupCompare group_compare_type; typedef typename detail::slot_call_iterator_t > slot_call_iterator; BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)(const combiner_type &combiner_arg, const group_compare_type &group_compare): _shared_state(new invocation_state(connection_list_type(group_compare), combiner_arg)), _garbage_collector_it(_shared_state->connection_bodies().end()) {} // connect slot connection connect(const slot_type &slot, connect_position position = at_back) { unique_lock lock(_mutex); return nolock_connect(slot, position); } connection connect(const group_type &group, const slot_type &slot, connect_position position = at_back) { unique_lock lock(_mutex); return nolock_connect(group, slot, position); } // connect extended slot connection connect_extended(const extended_slot_type &ext_slot, connect_position position = at_back) { unique_lock lock(_mutex); bound_extended_slot_function_type bound_slot(ext_slot.slot_function()); slot_type slot = replace_slot_function(ext_slot, bound_slot); connection conn = nolock_connect(slot, position); bound_slot.set_connection(conn); return conn; } connection connect_extended(const group_type &group, const extended_slot_type &ext_slot, connect_position position = at_back) { unique_lock lock(_mutex); bound_extended_slot_function_type bound_slot(ext_slot.slot_function()); slot_type slot = replace_slot_function(ext_slot, bound_slot); connection conn = nolock_connect(group, slot, position); bound_slot.set_connection(conn); return conn; } // disconnect slot(s) void disconnect_all_slots() { shared_ptr local_state = get_readable_state(); typename connection_list_type::iterator it; for(it = local_state->connection_bodies().begin(); it != local_state->connection_bodies().end(); ++it) { (*it)->disconnect(); } } void disconnect(const group_type &group) { shared_ptr local_state = get_readable_state(); group_key_type group_key(grouped_slots, group); typename connection_list_type::iterator it; typename connection_list_type::iterator end_it = local_state->connection_bodies().upper_bound(group_key); for(it = local_state->connection_bodies().lower_bound(group_key); it != end_it; ++it) { (*it)->disconnect(); } } template void disconnect(const T &slot) { typedef mpl::bool_<(is_convertible::value)> is_group; do_disconnect(slot, is_group()); } // emit signal result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) { shared_ptr local_state; typename connection_list_type::iterator it; { unique_lock list_lock(_mutex); // only clean up if it is safe to do so if(_shared_state.unique()) nolock_cleanup_connections(false, 1); /* Make a local copy of _shared_state while holding mutex, so we are thread safe against the combiner or connection list getting modified during invocation. */ local_state = _shared_state; } slot_invoker invoker = slot_invoker(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); slot_call_iterator_cache_type cache(invoker); invocation_janitor janitor(cache, *this, &local_state->connection_bodies()); return detail::combiner_invoker() ( local_state->combiner(), slot_call_iterator(local_state->connection_bodies().begin(), local_state->connection_bodies().end(), cache), slot_call_iterator(local_state->connection_bodies().end(), local_state->connection_bodies().end(), cache) ); } result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const { shared_ptr local_state; typename connection_list_type::iterator it; { unique_lock list_lock(_mutex); // only clean up if it is safe to do so if(_shared_state.unique()) nolock_cleanup_connections(false, 1); /* Make a local copy of _shared_state while holding mutex, so we are thread safe against the combiner or connection list getting modified during invocation. */ local_state = _shared_state; } slot_invoker invoker = slot_invoker(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); slot_call_iterator_cache_type cache(invoker); invocation_janitor janitor(cache, *this, &local_state->connection_bodies()); return detail::combiner_invoker() ( local_state->combiner(), slot_call_iterator(local_state->connection_bodies().begin(), local_state->connection_bodies().end(), cache), slot_call_iterator(local_state->connection_bodies().end(), local_state->connection_bodies().end(), cache) ); } std::size_t num_slots() const { shared_ptr local_state = get_readable_state(); typename connection_list_type::iterator it; std::size_t count = 0; for(it = local_state->connection_bodies().begin(); it != local_state->connection_bodies().end(); ++it) { if((*it)->connected()) ++count; } return count; } bool empty() const { shared_ptr local_state = get_readable_state(); typename connection_list_type::iterator it; for(it = local_state->connection_bodies().begin(); it != local_state->connection_bodies().end(); ++it) { if((*it)->connected()) return false; } return true; } combiner_type combiner() const { unique_lock lock(_mutex); return _shared_state->combiner(); } void set_combiner(const combiner_type &combiner_arg) { unique_lock lock(_mutex); if(_shared_state.unique()) _shared_state->combiner() = combiner_arg; else _shared_state.reset(new invocation_state(*_shared_state, combiner_arg)); } private: typedef Mutex mutex_type; // slot_invoker is passed to slot_call_iterator_t to run slots #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES class slot_invoker { public: typedef nonvoid_slot_result_type result_type; // typename add_reference::type #define BOOST_SIGNALS2_ADD_REF_TYPE(z, n, data) \ typename add_reference::type // typename add_reference::type argn #define BOOST_SIGNALS2_ADD_REF_ARG(z, n, data) \ BOOST_SIGNALS2_ADD_REF_TYPE(~, n, ~) \ BOOST_SIGNALS2_SIGNATURE_ARG_NAME(~, n, ~) // typename add_reference::type arg1, typename add_reference::type arg2, ..., typename add_reference::type argn #define BOOST_SIGNALS2_ADD_REF_ARGS(arity) \ BOOST_PP_ENUM(arity, BOOST_SIGNALS2_ADD_REF_ARG, ~) slot_invoker(BOOST_SIGNALS2_ADD_REF_ARGS(BOOST_SIGNALS2_NUM_ARGS)) BOOST_PP_EXPR_IF(BOOST_SIGNALS2_NUM_ARGS, :) #undef BOOST_SIGNALS2_ADD_REF_ARGS // m_argn #define BOOST_SIGNALS2_M_ARG_NAME(z, n, data) BOOST_PP_CAT(m_arg, BOOST_PP_INC(n)) // m_argn ( argn ) #define BOOST_SIGNALS2_MISC_STATEMENT(z, n, data) \ BOOST_SIGNALS2_M_ARG_NAME(~, n, ~) ( BOOST_SIGNALS2_SIGNATURE_ARG_NAME(~, n, ~) ) // m_arg1(arg1), m_arg2(arg2), ..., m_argn(argn) BOOST_PP_ENUM(BOOST_SIGNALS2_NUM_ARGS, BOOST_SIGNALS2_MISC_STATEMENT, ~) #undef BOOST_SIGNALS2_MISC_STATEMENT {} result_type operator ()(const connection_body_type &connectionBody) const { result_type *resolver = 0; return m_invoke(connectionBody, resolver); } private: // declare assignment operator private since this class might have reference or const members slot_invoker & operator=(const slot_invoker &); #define BOOST_SIGNALS2_ADD_REF_M_ARG_STATEMENT(z, n, data) \ BOOST_SIGNALS2_ADD_REF_TYPE(~, n, ~) BOOST_SIGNALS2_M_ARG_NAME(~, n, ~) ; BOOST_PP_REPEAT(BOOST_SIGNALS2_NUM_ARGS, BOOST_SIGNALS2_ADD_REF_M_ARG_STATEMENT, ~) #undef BOOST_SIGNALS2_ADD_REF_M_ARG_STATEMENT #undef BOOST_SIGNALS2_ADD_REF_ARG #undef BOOST_SIGNALS2_ADD_REF_TYPE // m_arg1, m_arg2, ..., m_argn #define BOOST_SIGNALS2_M_ARG_NAMES(arity) BOOST_PP_ENUM(arity, BOOST_SIGNALS2_M_ARG_NAME, ~) result_type m_invoke(const connection_body_type &connectionBody, const void_type *) const { connectionBody->slot.slot_function()(BOOST_SIGNALS2_M_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); return void_type(); } result_type m_invoke(const connection_body_type &connectionBody, ...) const { return connectionBody->slot.slot_function()(BOOST_SIGNALS2_M_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); } }; #undef BOOST_SIGNALS2_M_ARG_NAMES #undef BOOST_SIGNALS2_M_ARG_NAME #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES // a struct used to optimize (minimize) the number of shared_ptrs that need to be created // inside operator() class invocation_state { public: invocation_state(const connection_list_type &connections_in, const combiner_type &combiner_in): _connection_bodies(new connection_list_type(connections_in)), _combiner(new combiner_type(combiner_in)) {} invocation_state(const invocation_state &other, const connection_list_type &connections_in): _connection_bodies(new connection_list_type(connections_in)), _combiner(other._combiner) {} invocation_state(const invocation_state &other, const combiner_type &combiner_in): _connection_bodies(other._connection_bodies), _combiner(new combiner_type(combiner_in)) {} connection_list_type & connection_bodies() { return *_connection_bodies; } const connection_list_type & connection_bodies() const { return *_connection_bodies; } combiner_type & combiner() { return *_combiner; } const combiner_type & combiner() const { return *_combiner; } private: invocation_state(const invocation_state &); shared_ptr _connection_bodies; shared_ptr _combiner; }; // Destructor of invocation_janitor does some cleanup when a signal invocation completes. // Code can't be put directly in signal's operator() due to complications from void return types. class invocation_janitor { public: typedef BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) signal_type; invocation_janitor ( const slot_call_iterator_cache_type &cache, const signal_type &sig, const connection_list_type *connection_bodies ):_cache(cache), _sig(sig), _connection_bodies(connection_bodies) {} ~invocation_janitor() { // force a full cleanup of disconnected slots if there are too many if(_cache.disconnected_slot_count > _cache.connected_slot_count) { _sig.force_cleanup_connections(_connection_bodies); } } private: const slot_call_iterator_cache_type &_cache; const signal_type &_sig; const connection_list_type *_connection_bodies; }; // clean up disconnected connections void nolock_cleanup_connections_from(bool grab_tracked, const typename connection_list_type::iterator &begin, unsigned count = 0) const { BOOST_ASSERT(_shared_state.unique()); typename connection_list_type::iterator it; unsigned i; for(it = begin, i = 0; it != _shared_state->connection_bodies().end() && (count == 0 || i < count); ++i) { bool connected; { unique_lock lock(**it); if(grab_tracked) (*it)->nolock_slot_expired(); connected = (*it)->nolock_nograb_connected(); }// scoped lock destructs here, safe to erase now if(connected == false) { it = _shared_state->connection_bodies().erase((*it)->group_key(), it); }else { ++it; } } _garbage_collector_it = it; } // clean up a few connections in constant time void nolock_cleanup_connections(bool grab_tracked, unsigned count) const { BOOST_ASSERT(_shared_state.unique()); typename connection_list_type::iterator begin; if(_garbage_collector_it == _shared_state->connection_bodies().end()) { begin = _shared_state->connection_bodies().begin(); }else { begin = _garbage_collector_it; } nolock_cleanup_connections_from(grab_tracked, begin, count); } /* Make a new copy of the slot list if it is currently being read somewhere else */ void nolock_force_unique_connection_list() { if(_shared_state.unique() == false) { _shared_state.reset(new invocation_state(*_shared_state, _shared_state->connection_bodies())); nolock_cleanup_connections_from(true, _shared_state->connection_bodies().begin()); }else { /* We need to try and check more than just 1 connection here to avoid corner cases where certain repeated connect/disconnect patterns cause the slot list to grow without limit. */ nolock_cleanup_connections(true, 2); } } // force a full cleanup of the connection list void force_cleanup_connections(const connection_list_type *connection_bodies) const { unique_lock list_lock(_mutex); // if the connection list passed in as a parameter is no longer in use, // we don't need to do any cleanup. if(&_shared_state->connection_bodies() != connection_bodies) { return; } if(_shared_state.unique() == false) { _shared_state.reset(new invocation_state(*_shared_state, _shared_state->connection_bodies())); } nolock_cleanup_connections_from(false, _shared_state->connection_bodies().begin()); } shared_ptr get_readable_state() const { unique_lock list_lock(_mutex); return _shared_state; } connection_body_type create_new_connection(const slot_type &slot) { nolock_force_unique_connection_list(); return connection_body_type(new connection_body(slot)); } void do_disconnect(const group_type &group, mpl::bool_ /* is_group */) { disconnect(group); } template void do_disconnect(const T &slot, mpl::bool_ /* is_group */) { shared_ptr local_state = get_readable_state(); typename connection_list_type::iterator it; for(it = local_state->connection_bodies().begin(); it != local_state->connection_bodies().end(); ++it) { unique_lock lock(**it); if((*it)->slot.slot_function() == slot) { (*it)->nolock_disconnect(); }else { // check for wrapped extended slot bound_extended_slot_function_type *fp; fp = (*it)->slot.slot_function().template target(); if(fp && *fp == slot) { (*it)->nolock_disconnect(); } } } } // connect slot connection nolock_connect(const slot_type &slot, connect_position position) { connection_body_type newConnectionBody = create_new_connection(slot); group_key_type group_key; if(position == at_back) { group_key.first = back_ungrouped_slots; _shared_state->connection_bodies().push_back(group_key, newConnectionBody); }else { group_key.first = front_ungrouped_slots; _shared_state->connection_bodies().push_front(group_key, newConnectionBody); } newConnectionBody->set_group_key(group_key); return connection(newConnectionBody); } connection nolock_connect(const group_type &group, const slot_type &slot, connect_position position) { connection_body_type newConnectionBody = create_new_connection(slot); // update map to first connection body in group if needed group_key_type group_key(grouped_slots, group); newConnectionBody->set_group_key(group_key); if(position == at_back) { _shared_state->connection_bodies().push_back(group_key, newConnectionBody); }else // at_front { _shared_state->connection_bodies().push_front(group_key, newConnectionBody); } return connection(newConnectionBody); } // _shared_state is mutable so we can do force_cleanup_connections during a const invocation mutable shared_ptr _shared_state; mutable typename connection_list_type::iterator _garbage_collector_it; // connection list mutex must never be locked when attempting a blocking lock on a slot, // or you could deadlock. mutable mutex_type _mutex; }; template class BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS); } template class BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS); template class BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_SIGNAL_TEMPLATE_SPECIALIZATION: public signal_base, public detail::BOOST_SIGNALS2_STD_FUNCTIONAL_BASE (typename detail::result_type_wrapper::type) { typedef detail::BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) impl_class; public: typedef detail::BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) weak_signal_type; friend class detail::BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) ; typedef SlotFunction slot_function_type; // typedef slotN slot_type; typedef typename impl_class::slot_type slot_type; typedef typename impl_class::extended_slot_function_type extended_slot_function_type; typedef typename impl_class::extended_slot_type extended_slot_type; typedef typename slot_function_type::result_type slot_result_type; typedef Combiner combiner_type; typedef typename impl_class::result_type result_type; typedef Group group_type; typedef GroupCompare group_compare_type; typedef typename impl_class::slot_call_iterator slot_call_iterator; typedef typename mpl::identity::type signature_type; #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES // typedef Tn argn_type; #define BOOST_SIGNALS2_MISC_STATEMENT(z, n, data) \ typedef BOOST_PP_CAT(T, BOOST_PP_INC(n)) BOOST_PP_CAT(BOOST_PP_CAT(arg, BOOST_PP_INC(n)), _type); BOOST_PP_REPEAT(BOOST_SIGNALS2_NUM_ARGS, BOOST_SIGNALS2_MISC_STATEMENT, ~) #undef BOOST_SIGNALS2_MISC_STATEMENT #if BOOST_SIGNALS2_NUM_ARGS == 1 typedef arg1_type argument_type; #elif BOOST_SIGNALS2_NUM_ARGS == 2 typedef arg1_type first_argument_type; typedef arg2_type second_argument_type; #endif template class arg : public detail::BOOST_SIGNALS2_PREPROCESSED_ARG_N_TYPE_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) {}; BOOST_STATIC_CONSTANT(int, arity = BOOST_SIGNALS2_NUM_ARGS); #else // BOOST_NO_CXX11_VARIADIC_TEMPLATES template class arg { public: typedef typename detail::variadic_arg_type::type type; }; BOOST_STATIC_CONSTANT(int, arity = sizeof...(Args)); #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)(const combiner_type &combiner_arg = combiner_type(), const group_compare_type &group_compare = group_compare_type()): _pimpl(new impl_class(combiner_arg, group_compare)) {}; virtual ~BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)() { } //move support #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)( BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) && other) { using std::swap; swap(_pimpl, other._pimpl); }; BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) & operator=(BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) && rhs) { if(this == &rhs) { return *this; } _pimpl.reset(); using std::swap; swap(_pimpl, rhs._pimpl); return *this; } #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) connection connect(const slot_type &slot, connect_position position = at_back) { return (*_pimpl).connect(slot, position); } connection connect(const group_type &group, const slot_type &slot, connect_position position = at_back) { return (*_pimpl).connect(group, slot, position); } connection connect_extended(const extended_slot_type &slot, connect_position position = at_back) { return (*_pimpl).connect_extended(slot, position); } connection connect_extended(const group_type &group, const extended_slot_type &slot, connect_position position = at_back) { return (*_pimpl).connect_extended(group, slot, position); } void disconnect_all_slots() { (*_pimpl).disconnect_all_slots(); } void disconnect(const group_type &group) { (*_pimpl).disconnect(group); } template void disconnect(const T &slot) { (*_pimpl).disconnect(slot); } result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) { return (*_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); } result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const { return (*_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); } std::size_t num_slots() const { return (*_pimpl).num_slots(); } bool empty() const { return (*_pimpl).empty(); } combiner_type combiner() const { return (*_pimpl).combiner(); } void set_combiner(const combiner_type &combiner_arg) { return (*_pimpl).set_combiner(combiner_arg); } void swap(BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) & other) { using std::swap; swap(_pimpl, other._pimpl); } protected: virtual shared_ptr lock_pimpl() const { return _pimpl; } private: shared_ptr _pimpl; }; #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES // free swap function for signalN classes, findable by ADL template void swap( BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) &sig1, BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) &sig2 ) { sig1.swap(sig2); } #endif namespace detail { // wrapper class for storing other signals as slots with automatic lifetime tracking template class BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS); template class BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_SIGNAL_TEMPLATE_SPECIALIZATION { public: typedef typename BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) ::result_type result_type; BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) (const BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) &signal): _weak_pimpl(signal._pimpl) {} result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) { shared_ptr > shared_pimpl(_weak_pimpl.lock()); if(shared_pimpl == 0) boost::throw_exception(expired_slot()); return (*shared_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); } result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const { shared_ptr > shared_pimpl(_weak_pimpl.lock()); if(shared_pimpl == 0) boost::throw_exception(expired_slot()); return (*shared_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); } private: boost::weak_ptr > _weak_pimpl; }; #ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES template class extended_signature: public variadic_extended_signature {}; #else // BOOST_NO_CXX11_VARIADIC_TEMPLATES template class extended_signature; // partial template specialization template class extended_signature { public: // typename function_traits::result_type ( // const boost::signals2::connection &, // typename function_traits::arg1_type, // typename function_traits::arg2_type, // ..., // typename function_traits::argn_type) #define BOOST_SIGNALS2_EXT_SIGNATURE(arity, Signature) \ typename function_traits::result_type ( \ const boost::signals2::connection & BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) \ BOOST_PP_ENUM(arity, BOOST_SIGNALS2_SIGNATURE_TO_ARGN_TYPE, Signature) ) typedef function function_type; #undef BOOST_SIGNALS2_EXT_SIGNATURE }; template class signalN; // partial template specialization template class signalN { public: typedef BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)< BOOST_SIGNALS2_PORTABLE_SIGNATURE(BOOST_SIGNALS2_NUM_ARGS, Signature), Combiner, Group, GroupCompare, SlotFunction, ExtendedSlotFunction, Mutex> type; }; #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES } // namespace detail } // namespace signals2 } // namespace boost #undef BOOST_SIGNALS2_NUM_ARGS #undef BOOST_SIGNALS2_SIGNAL_TEMPLATE_INSTANTIATION