properties-cpp 0.0.3
A very simple convenience library for handling properties and signals in C++11.
signal.h
Go to the documentation of this file.
1/*
2 * Copyright © 2013 Canonical Ltd.
3 *
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.
7 *
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.
12 *
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/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18#ifndef COM_UBUNTU_SIGNAL_H_
19#define COM_UBUNTU_SIGNAL_H_
20
21#include <core/connection.h>
22
23#include <functional>
24#include <iostream>
25#include <list>
26#include <mutex>
27#include <set>
28
29namespace core
30{
35template<typename ...Arguments>
36class Signal
37{
38public:
42 typedef std::function<void(Arguments...)> Slot;
43
44private:
45 struct SlotWrapper
46 {
47 void operator()(Arguments... args)
48 {
49 dispatcher(std::bind(slot, args...));
50 }
51
52 Slot slot;
53 Connection::Dispatcher dispatcher;
54 Connection connection;
55 };
56
57public:
61 inline Signal() noexcept(true) : d(new Private())
62 {
63 }
64
65 inline ~Signal()
66 {
67 std::lock_guard<std::mutex> lg(d->guard);
68 for (auto slot : d->slot_list)
69 slot.connection.reset();
70 }
71
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;
76
86 inline Connection connect(const Slot& slot) const
87 {
88 // Helpers to initialize an invalid connection.
89 static const Connection::Disconnector empty_disconnector{};
90 static const Connection::DispatcherInstaller empty_dispatcher_installer{};
91
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(); };
96
97 Connection conn{empty_disconnector, empty_dispatcher_installer};
98
99 std::lock_guard<std::mutex> lg(d->guard);
100
101 auto result = d->slot_list.insert(
102 d->slot_list.end(),
103 SlotWrapper{slot, default_dispatcher, conn});
104
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,
111 d,
112 result);
113 conn.d->dispatcher_installer = std::bind(
114 &Private::install_dispatcher_for_iterator,
115 d,
116 std::placeholders::_1,
117 result);
118
119 return conn;
120 }
121
132 inline void operator()(Arguments... args)
133 {
134 std::lock_guard<std::mutex> lg(d->guard);
135 for(auto slot : d->slot_list)
136 {
137 slot(args...);
138 }
139 }
140
141private:
142 struct Private
143 {
144 typedef std::list<SlotWrapper> SlotList;
145
146 inline void disconnect_slot_for_iterator(typename SlotList::iterator it)
147 {
148 std::lock_guard<std::mutex> lg(guard);
149 slot_list.erase(it);
150 }
151
152 inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher,
153 typename SlotList::iterator it)
154 {
155 std::lock_guard<std::mutex> lg(guard);
156 it->dispatcher = dispatcher;
157 }
158
159 std::mutex guard;
160 SlotList slot_list;
161 };
162 std::shared_ptr<Private> d;
163};
164
169template<>
170class Signal<void>
171{
172public:
176 typedef std::function<void()> Slot;
177
178private:
179 struct SlotWrapper
180 {
181 void operator()()
182 {
183 dispatcher(slot);
184 }
185
186 Slot slot;
187 Connection::Dispatcher dispatcher;
188 Connection connection;
189 };
190
191public:
195 inline Signal() noexcept(true) : d(new Private())
196 {
197 }
198
199 inline ~Signal()
200 {
201 std::lock_guard<std::mutex> lg(d->guard);
202 for (auto slot : d->slot_list)
203 slot.connection.reset();
204 }
205
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;
210
220 inline Connection connect(const Slot& slot) const
221 {
222 // Helpers to initialize an invalid connection.
223 static const Connection::Disconnector empty_disconnector{};
224 static const Connection::DispatcherInstaller empty_dispatcher_installer{};
225
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(); };
230
231 Connection conn{empty_disconnector, empty_dispatcher_installer};
232
233 std::lock_guard<std::mutex> lg(d->guard);
234
235 auto result = d->slot_list.insert(
236 d->slot_list.end(),
237 SlotWrapper{slot, default_dispatcher, conn});
238
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,
245 d,
246 result);
247 conn.d->dispatcher_installer = std::bind(
248 &Private::install_dispatcher_for_iterator,
249 d,
250 std::placeholders::_1,
251 result);
252
253 return conn;
254 }
255
263 inline void operator()()
264 {
265 std::lock_guard<std::mutex> lg(d->guard);
266 for(auto slot : d->slot_list)
267 {
268 slot();
269 }
270 }
271
272private:
273 struct Private
274 {
275 typedef std::list<SlotWrapper> SlotList;
276
277 inline void disconnect_slot_for_iterator(typename SlotList::iterator it)
278 {
279 std::lock_guard<std::mutex> lg(guard);
280 slot_list.erase(it);
281 }
282
283 inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher,
284 typename SlotList::iterator it)
285 {
286 std::lock_guard<std::mutex> lg(guard);
287 it->dispatcher = dispatcher;
288 }
289
290 std::mutex guard;
291 SlotList slot_list;
292 };
293 std::shared_ptr<Private> d;
294};
295}
296
297#endif // COM_UBUNTU_SIGNAL_H_
The Connection class models a signal-slot connection.
Definition: connection.h:32
std::function< void(const std::function< void()> &)> Dispatcher
Definition: connection.h:34
Signal() noexcept(true)
Signal constructs a new instance. Never throws.
Definition: signal.h:195
bool operator==(const Signal &) const =delete
Signal & operator=(const Signal &)=delete
void operator()()
operator () emits the signal.
Definition: signal.h:263
std::function< void()> Slot
Slot is the function type that observers have to provide to connect to this signal.
Definition: signal.h:176
Signal(const Signal &)=delete
Connection connect(const Slot &slot) const
Connects the provided slot to this signal instance.
Definition: signal.h:220
A signal class that observers can subscribe to.
Definition: signal.h:37
Signal(const Signal &)=delete
Signal & operator=(const Signal &)=delete
Connection connect(const Slot &slot) const
Connects the provided slot to this signal instance.
Definition: signal.h:86
void operator()(Arguments... args)
operator () emits the signal with the provided parameters.
Definition: signal.h:132
std::function< void(Arguments...)> Slot
Slot is the function type that observers have to provide to connect to this signal.
Definition: signal.h:42
bool operator==(const Signal &) const =delete
Signal() noexcept(true)
Signal constructs a new instance. Never throws.
Definition: signal.h:61