1 // Boost.Signals library
3 // Copyright Doug Gregor 2001-2003. Use, modification and
4 // distribution is subject to the Boost Software License, Version
5 // 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
8 // For more information, see http://www.boost.org
10 #define BOOST_SIGNALS_SOURCE
12 #include <boost/signals/detail/signal_base.hpp>
16 namespace BOOST_SIGNALS_NAMESPACE {
18 signal_base_impl::signal_base_impl(const compare_type& comp) :
22 flags.delayed_disconnect = false;
23 flags.clearing = false;
26 signal_base_impl::~signal_base_impl()
28 // Set the "clearing" flag to ignore extraneous disconnect requests,
29 // because all slots will be disconnected on destruction anyway.
30 flags.clearing = true;
33 void signal_base_impl::disconnect_all_slots()
35 // Do nothing if we're already clearing the slot list
39 if (call_depth == 0) {
40 // Clearing the slot list will disconnect all slots automatically
41 temporarily_set_clearing set_clearing(this);
45 // We can't actually remove elements from the slot list because there
46 // are still iterators into the slot list that must not be
47 // invalidated by this operation. So just disconnect each slot
48 // without removing it from the slot list. When the call depth does
49 // reach zero, the call list will be cleared.
50 flags.delayed_disconnect = true;
51 temporarily_set_clearing set_clearing(this);
52 for (slot_iterator i = slots_.begin(); i != slots_.end(); ++i) {
53 i->second.first.disconnect();
60 connect_slot(const any& slot,
62 const std::vector<const trackable*>& bound_objects)
64 // Allocate storage for a new basic_connection object to represent the
66 basic_connection* con = new basic_connection();
68 // Create a new connection handle object and place the basic_connection
69 // object we just created under its control. Note that the "reset"
70 // routine will delete con if allocation throws.
71 connection slot_connection;
72 slot_connection.reset(con);
74 // Allocate storage for an iterator that will hold the point of
75 // insertion of the slot into the list. This is used to later remove
76 // the slot when it is disconnected.
77 std::auto_ptr<slot_iterator> saved_iter(new slot_iterator());
79 // Add the slot to the list.
82 slots_.insert(stored_slot_type(name,
83 connection_slot_pair(slot_connection,
86 // Make the copy of the connection in the list disconnect when it is
88 pos->second.first.set_controlling();
90 // The assignment operation here absolutely must not throw, which
91 // intuitively makes sense (because any container's insert method
92 // becomes impossible to use in an exception-safe manner without this
93 // assumption), but doesn't appear to be mentioned in the standard.
96 // Fill out the connection object appropriately. None of these
97 // operations can throw
99 con->signal_data = saved_iter.release();
100 con->signal_disconnect = &signal_base_impl::slot_disconnected;
102 // If an exception is thrown the connection will automatically be
104 scoped_connection safe_connection = slot_connection;
106 // Connect each of the bound objects
107 for(std::vector<const trackable*>::const_iterator i =
108 bound_objects.begin();
109 i != bound_objects.end();
111 // Notify the object that the signal is connecting to it by passing
112 // it a copy of the connection. If the connection
113 // should throw, the scoped connection safe_connection will
114 // disconnect the connection completely.
115 bound_object binding;
116 (*i)->signal_connected(slot_connection, binding);
118 // This will notify the bound object that the connection just made
119 // should be disconnected if an exception is thrown before the
120 // end of this iteration
121 auto_disconnect_bound_object disconnector(binding);
123 // Add the binding to the list of bindings for the connection.
124 con->bound_objects.push_back(binding);
126 // The connection object now knows about the bound object, so if an
127 // exception is thrown later the connection object will notify the
128 // bound object of the disconnection automatically
129 disconnector.release();
132 // No exceptions will be thrown past this point, and we must not
133 // disconnect the connection now
134 safe_connection.release();
136 return slot_connection;
139 bool signal_base_impl::empty() const
141 // Disconnected slots may still be in the list of slots if
142 // a) this is called while slots are being invoked (call_depth > 0)
143 // b) an exception was thrown in remove_disconnected_slots
144 for (slot_iterator i = slots_.begin(); i != slots_.end(); ++i) {
145 if (i->second.first.connected())
152 std::size_t signal_base_impl::num_slots() const
154 // Disconnected slots may still be in the list of slots if
155 // a) this is called while slots are being invoked (call_depth > 0)
156 // b) an exception was thrown in remove_disconnected_slots
157 std::size_t count = 0;
158 for (slot_iterator i = slots_.begin(); i != slots_.end(); ++i) {
159 if (i->second.first.connected())
165 void signal_base_impl::disconnect(const any& group)
167 std::pair<slot_iterator, slot_iterator> group_slots =
168 slots_.equal_range(group);
169 while (group_slots.first != group_slots.second) {
170 slot_iterator next = group_slots.first;
173 group_slots.first->second.first.disconnect();
174 group_slots.first = next;
178 void signal_base_impl::slot_disconnected(void* obj, void* data)
180 signal_base_impl* self = reinterpret_cast<signal_base_impl*>(obj);
182 // We won't need the slot iterator after this
183 std::auto_ptr<slot_iterator> slot(
184 reinterpret_cast<slot_iterator*>(data));
186 // If we're flags.clearing, we don't bother updating the list of slots
187 if (!self->flags.clearing) {
188 // If we're in a call, note the fact that a slot has been deleted so
189 // we can come back later to remove the iterator
190 if (self->call_depth > 0) {
191 self->flags.delayed_disconnect = true;
194 // Just remove the slot now, it's safe
195 self->slots_.erase(*slot);
200 void signal_base_impl::remove_disconnected_slots() const
202 // Remove any disconnected slots
203 for (slot_iterator i = slots_.begin(); i != slots_.end(); /* none */) {
204 if (!i->second.first.connected())
212 call_notification(const shared_ptr<signal_base_impl>& b) :
215 // A call will be made, so increment the call depth as a notification
219 call_notification::~call_notification()
223 // If the call depth is zero and we have some slots that have been
224 // disconnected during the calls, remove those slots from the list
225 if (impl->call_depth == 0 &&
226 impl->flags.delayed_disconnect) {
227 impl->remove_disconnected_slots();
228 impl->flags.delayed_disconnect = false;
232 signal_base::signal_base(const compare_type& comp) : impl()
234 impl.reset(new signal_base_impl(comp));
237 signal_base::~signal_base()
241 } // namespace detail
242 } // namespace BOOST_SIGNALS_NAMESPACE
246 // Explicit instantiations to keep in the library
247 template class boost::function2<bool, boost::any, boost::any>;
248 template class std::multimap<boost::any,
249 boost::BOOST_SIGNALS_NAMESPACE::detail::connection_slot_pair,
250 boost::function2<bool, boost::any, boost::any> >;