2 * Copyright © 2013 Canonical Ltd.
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
18 #ifndef COM_UBUNTU_SIGNAL_H_
19 #define COM_UBUNTU_SIGNAL_H_
21 #include <core/connection.h>
32 * @brief A signal class that observers can subscribe to.
33 * @tparam Arguments List of argument types passed on to observers when the signal is emitted.
35 template<typename ...Arguments>
40 * @brief Slot is the function type that observers have to provide to connect to this signal.
42 typedef std::function<void(Arguments...)> Slot;
47 void operator()(Arguments... args)
49 dispatcher(std::bind(slot, args...));
53 Connection::Dispatcher dispatcher;
54 Connection connection;
59 * @brief Signal constructs a new instance. Never throws.
61 inline Signal() noexcept(true) : d(new Private())
67 std::lock_guard<std::mutex> lg(d->guard);
68 for (auto slot : d->slot_list)
69 slot.connection.reset();
72 // Copy construction, assignment and equality comparison are disabled.
73 Signal(const Signal&) = delete;
74 Signal& operator=(const Signal&) = delete;
75 bool operator==(const Signal&) const = delete;
78 * @brief Connects the provided slot to this signal instance.
80 * Calling this method is thread-safe and synchronized with any
81 * other connect, signal emission or disconnect calls.
83 * @param slot The function to be called when the signal is emitted.
84 * @return A connection object corresponding to the signal-slot connection.
86 inline Connection connect(const Slot& slot) const
88 // Helpers to initialize an invalid connection.
89 static const Connection::Disconnector empty_disconnector{};
90 static const Connection::DispatcherInstaller empty_dispatcher_installer{};
92 // The default dispatcher immediately executes the function object
93 // provided as argument on whatever thread is currently running.
94 static const Connection::Dispatcher default_dispatcher
95 = [](const std::function<void()>& handler) { handler(); };
97 Connection conn{empty_disconnector, empty_dispatcher_installer};
99 std::lock_guard<std::mutex> lg(d->guard);
101 auto result = d->slot_list.insert(
103 SlotWrapper{slot, default_dispatcher, conn});
105 // We implicitly share our internal state with the connection here
106 // by passing in our private bits contained in 'd' to the std::bind call.
107 // This admittedly uncommon approach allows us to cleanly manage connection
108 // and signal lifetimes without the need to mark everything as mutable.
109 conn.d->disconnector = std::bind(
110 &Private::disconnect_slot_for_iterator,
113 conn.d->dispatcher_installer = std::bind(
114 &Private::install_dispatcher_for_iterator,
116 std::placeholders::_1,
123 * @brief operator () emits the signal with the provided parameters.
125 * Please note that signal emissions might not be delivered immediately to
126 * registered slots, depending on whether the respective connection is dispatched
127 * via a queueing dispatcher. For that reason, the lifetime of the arguments has to
128 * exceed the scope of the call to this operator and its surrounding scope.
130 * @param args The arguments to be passed on to registered slots.
132 inline void operator()(Arguments... args)
134 std::lock_guard<std::mutex> lg(d->guard);
135 for(auto slot : d->slot_list)
144 typedef std::list<SlotWrapper> SlotList;
146 inline void disconnect_slot_for_iterator(typename SlotList::iterator it)
148 std::lock_guard<std::mutex> lg(guard);
152 inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher,
153 typename SlotList::iterator it)
155 std::lock_guard<std::mutex> lg(guard);
156 it->dispatcher = dispatcher;
162 std::shared_ptr<Private> d;
166 * @brief A signal class that observers can subscribe to,
167 * template specialization for signals without arguments.
174 * @brief Slot is the function type that observers have to provide to connect to this signal.
176 typedef std::function<void()> Slot;
187 Connection::Dispatcher dispatcher;
188 Connection connection;
193 * @brief Signal constructs a new instance. Never throws.
195 inline Signal() noexcept(true) : d(new Private())
201 std::lock_guard<std::mutex> lg(d->guard);
202 for (auto slot : d->slot_list)
203 slot.connection.reset();
206 // Copy construction, assignment and equality comparison are disabled.
207 Signal(const Signal&) = delete;
208 Signal& operator=(const Signal&) = delete;
209 bool operator==(const Signal&) const = delete;
212 * @brief Connects the provided slot to this signal instance.
214 * Calling this method is thread-safe and synchronized with any
215 * other connect, signal emission or disconnect calls.
217 * @param slot The function to be called when the signal is emitted.
218 * @return A connection object corresponding to the signal-slot connection.
220 inline Connection connect(const Slot& slot) const
222 // Helpers to initialize an invalid connection.
223 static const Connection::Disconnector empty_disconnector{};
224 static const Connection::DispatcherInstaller empty_dispatcher_installer{};
226 // The default dispatcher immediately executes the function object
227 // provided as argument on whatever thread is currently running.
228 static const Connection::Dispatcher default_dispatcher
229 = [](const std::function<void()>& handler) { handler(); };
231 Connection conn{empty_disconnector, empty_dispatcher_installer};
233 std::lock_guard<std::mutex> lg(d->guard);
235 auto result = d->slot_list.insert(
237 SlotWrapper{slot, default_dispatcher, conn});
239 // We implicitly share our internal state with the connection here
240 // by passing in our private bits contained in 'd' to the std::bind call.
241 // This admittedly uncommon approach allows us to cleanly manage connection
242 // and signal lifetimes without the need to mark everything as mutable.
243 conn.d->disconnector = std::bind(
244 &Private::disconnect_slot_for_iterator,
247 conn.d->dispatcher_installer = std::bind(
248 &Private::install_dispatcher_for_iterator,
250 std::placeholders::_1,
257 * @brief operator () emits the signal.
259 * Please note that signal emissions might not be delivered immediately to
260 * registered slots, depending on whether the respective connection is dispatched
261 * via a queueing dispatcher.
263 inline void operator()()
265 std::lock_guard<std::mutex> lg(d->guard);
266 for(auto slot : d->slot_list)
275 typedef std::list<SlotWrapper> SlotList;
277 inline void disconnect_slot_for_iterator(typename SlotList::iterator it)
279 std::lock_guard<std::mutex> lg(guard);
283 inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher,
284 typename SlotList::iterator it)
286 std::lock_guard<std::mutex> lg(guard);
287 it->dispatcher = dispatcher;
293 std::shared_ptr<Private> d;
297 #endif // COM_UBUNTU_SIGNAL_H_