]> git.lyx.org Git - lyx.git/blob - boost/libs/signals/src/signal_base.cpp
Boost 1.31.0
[lyx.git] / boost / libs / signals / src / signal_base.cpp
1 // Boost.Signals library
2
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)
7
8 // For more information, see http://www.boost.org
9
10 #define BOOST_SIGNALS_SOURCE
11
12 #include <boost/signals/detail/signal_base.hpp>
13 #include <cassert>
14
15 namespace boost {
16   namespace BOOST_SIGNALS_NAMESPACE {
17     namespace detail {
18       signal_base_impl::signal_base_impl(const compare_type& comp) :
19         call_depth(0),
20         slots_(comp)
21       {
22         flags.delayed_disconnect = false;
23         flags.clearing = false;
24       }
25
26       signal_base_impl::~signal_base_impl()
27       {
28         // Set the "clearing" flag to ignore extraneous disconnect requests,
29         // because all slots will be disconnected on destruction anyway.
30         flags.clearing = true;
31       }
32
33       void signal_base_impl::disconnect_all_slots()
34       {
35         // Do nothing if we're already clearing the slot list
36         if (flags.clearing)
37           return;
38
39         if (call_depth == 0) {
40           // Clearing the slot list will disconnect all slots automatically
41           temporarily_set_clearing set_clearing(this);
42           slots_.clear();
43         }
44         else {
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();
54           }
55         }
56       }
57
58       connection
59       signal_base_impl::
60         connect_slot(const any& slot,
61                      const any& name,
62                      const std::vector<const trackable*>& bound_objects)
63       {
64         // Allocate storage for a new basic_connection object to represent the
65         // connection
66         basic_connection* con = new basic_connection();
67
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);
73
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());
78
79         // Add the slot to the list.
80
81         slot_iterator pos =
82           slots_.insert(stored_slot_type(name,
83                                         connection_slot_pair(slot_connection,
84                                                              slot)));
85
86         // Make the copy of the connection in the list disconnect when it is
87         // destroyed
88         pos->second.first.set_controlling();
89
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.
94         *saved_iter = pos;
95
96         // Fill out the connection object appropriately. None of these
97         // operations can throw
98         con->signal = this;
99         con->signal_data = saved_iter.release();
100         con->signal_disconnect = &signal_base_impl::slot_disconnected;
101
102         // If an exception is thrown the connection will automatically be
103         // disconnected.
104         scoped_connection safe_connection = slot_connection;
105
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();
110             ++i) {
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);
117
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);
122
123           // Add the binding to the list of bindings for the connection.
124           con->bound_objects.push_back(binding);
125
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();
130         }
131
132         // No exceptions will be thrown past this point, and we must not
133         // disconnect the connection now
134         safe_connection.release();
135
136         return slot_connection;
137       }
138
139       bool signal_base_impl::empty() const
140       {
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())
146             return false;
147         }
148
149         return true;
150       }
151
152       std::size_t signal_base_impl::num_slots() const
153       {
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())
160             ++count;
161         }
162         return count;
163       }
164
165       void signal_base_impl::disconnect(const any& group)
166       {
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;
171           ++next;
172
173           group_slots.first->second.first.disconnect();
174           group_slots.first = next;
175         }
176       }
177
178       void signal_base_impl::slot_disconnected(void* obj, void* data)
179       {
180         signal_base_impl* self = reinterpret_cast<signal_base_impl*>(obj);
181
182         // We won't need the slot iterator after this
183         std::auto_ptr<slot_iterator> slot(
184                                       reinterpret_cast<slot_iterator*>(data));
185
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;
192           }
193           else {
194             // Just remove the slot now, it's safe
195             self->slots_.erase(*slot);
196           }
197         }
198       }
199
200       void signal_base_impl::remove_disconnected_slots() const
201       {
202         // Remove any disconnected slots
203         for (slot_iterator i = slots_.begin(); i != slots_.end(); /* none */) {
204           if (!i->second.first.connected())
205             slots_.erase(i++);
206           else
207             ++i;
208         }
209       }
210
211       call_notification::
212         call_notification(const shared_ptr<signal_base_impl>& b) :
213           impl(b)
214       {
215         // A call will be made, so increment the call depth as a notification
216         impl->call_depth++;
217       }
218
219       call_notification::~call_notification()
220       {
221         impl->call_depth--;
222
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;
229         }
230       }
231
232     signal_base::signal_base(const compare_type& comp) : impl()
233     {
234       impl.reset(new signal_base_impl(comp));
235     }
236
237     signal_base::~signal_base()
238     {
239     }
240
241     } // namespace detail
242   } // namespace BOOST_SIGNALS_NAMESPACE
243 } // namespace boost
244
245 #ifndef BOOST_MSVC
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> >;
251 #endif