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 
29 namespace core
30 {
35 template<typename ...Arguments>
36 class Signal
37 {
38 public:
42  typedef std::function<void(Arguments...)> Slot;
43 
44 private:
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 
57 public:
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 
141 private:
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 
169 template<>
170 class Signal<void>
171 {
172 public:
176  typedef std::function<void()> Slot;
177 
178 private:
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 
191 public:
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 
272 private:
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
void operator()()
operator () emits the signal.
Definition: signal.h:263
Signal & operator=(const Signal &)=delete
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
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 & operator=(const Signal &)=delete
Signal() noexcept(true)
Signal constructs a new instance. Never throws.
Definition: signal.h:61