Merge lp:~thomas-voss/properties-cpp/cleanup into lp:properties-cpp

Proposed by Thomas Voß
Status: Merged
Merged at revision: 4
Proposed branch: lp:~thomas-voss/properties-cpp/cleanup
Merge into: lp:properties-cpp
Diff against target: 456 lines (+123/-87)
6 files modified
README.md (+6/-3)
include/com/ubuntu/connection.h (+30/-7)
include/com/ubuntu/property.h (+1/-26)
include/com/ubuntu/signal.h (+64/-27)
tests/properties_test.cpp (+1/-18)
tests/signals_test.cpp (+21/-6)
To merge this branch: bzr merge lp:~thomas-voss/properties-cpp/cleanup
Reviewer Review Type Date Requested Status
Jussi Pakkanen (community) Approve
Review via email: mp+196829@code.launchpad.net

Commit message

Adjusted the introductory code example to always drain the event queue.
Added a test to make sure that a signal resets all registered slots on destruction.
Documented the usage of a shared private state.

Description of the change

Adjusted the introductory code example to always drain the event queue.
Added a test to make sure that a signal resets all registered slots on destruction.
Documented the usage of a shared private state.

To post a comment you must log in.
Revision history for this message
Jussi Pakkanen (jpakkane) wrote :

Looks good. There are a few cases of for(auto foo : ...) which could be for(auto &foo : ...) but the performance impact is probably unnoticeable.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.md'
2--- README.md 2013-11-13 10:41:06 +0000
3+++ README.md 2013-11-27 08:46:56 +0000
4@@ -72,8 +72,11 @@
5 std::chrono::milliseconds{500},
6 [this]() { return handlers.size() > 0; });
7
8- handlers.front()();
9- handlers.pop();
10+ while (handlers.size() > 0)
11+ {
12+ handlers.front()();
13+ handlers.pop();
14+ }
15 }
16 }
17
18@@ -131,4 +134,4 @@
19 if (dispatcher_thread.joinable())
20 dispatcher_thread.join();
21 }
22-~~~~~~~~~~~~~
23\ No newline at end of file
24+~~~~~~~~~~~~~
25
26=== modified file 'include/com/ubuntu/connection.h'
27--- include/com/ubuntu/connection.h 2013-11-13 09:19:50 +0000
28+++ include/com/ubuntu/connection.h 2013-11-27 08:46:56 +0000
29@@ -31,8 +31,7 @@
30 */
31 class Connection
32 {
33-public:
34- typedef std::function<void()> Disconnector;
35+public:
36 typedef std::function<void(const std::function<void()>&)> Dispatcher;
37
38 /**
39@@ -58,10 +57,12 @@
40 */
41 inline void dispatch_via(const Dispatcher& dispatcher)
42 {
43- d->dispatcher_installer(dispatcher);
44+ if (d->dispatcher_installer)
45+ d->dispatcher_installer(dispatcher);
46 }
47
48 private:
49+ typedef std::function<void()> Disconnector;
50 typedef std::function<void(const Dispatcher&)> DispatcherInstaller;
51
52 template<typename ... Arguments> friend class Signal;
53@@ -72,6 +73,11 @@
54 {
55 }
56
57+ inline void reset()
58+ {
59+ d->reset();
60+ }
61+
62 struct Private
63 {
64 Private(const Connection::Disconnector& disconnector,
65@@ -81,23 +87,40 @@
66 {
67 }
68
69+ inline void reset()
70+ {
71+ std::lock_guard<std::mutex> lg(guard);
72+ reset_locked();
73+ }
74+
75+ inline void reset_locked()
76+ {
77+ static const Connection::Disconnector empty_disconnector{};
78+ static const Connection::DispatcherInstaller empty_dispatcher_installer{};
79+
80+ disconnector = empty_disconnector;
81+ dispatcher_installer = empty_dispatcher_installer;
82+ }
83+
84 inline void disconnect()
85 {
86 static const Connection::Disconnector empty_disconnector{};
87
88 std::lock_guard<std::mutex> lg(guard);
89
90- if (!disconnector)
91- return;
92+ if (disconnector)
93+ disconnector();
94
95- disconnector();
96- disconnector = empty_disconnector;
97+ reset_locked();
98 }
99
100 std::mutex guard;
101 Connection::Disconnector disconnector;
102 Connection::DispatcherInstaller dispatcher_installer;
103 };
104+
105+ // The whole class is implicitly shared and we thus forward our complete
106+ // shared state to a private structure that is lifetime-managed by a shared_ptr.
107 std::shared_ptr<Private> d;
108 };
109
110
111=== modified file 'include/com/ubuntu/property.h'
112--- include/com/ubuntu/property.h 2013-11-13 09:19:50 +0000
113+++ include/com/ubuntu/property.h 2013-11-27 08:46:56 +0000
114@@ -33,13 +33,6 @@
115 template<typename T>
116 class Property
117 {
118- private:
119- inline static T& mutable_default_value()
120- {
121- static T default_value = T{};
122- return default_value;
123- }
124-
125 public:
126 /**
127 * @brief ValueType refers to the type of the contained value.
128@@ -47,28 +40,10 @@
129 typedef T ValueType;
130
131 /**
132- * @brief Queries the default value set up for a specific type.
133- * @return A non-mutable reference to the default value.
134- */
135- inline static const T& default_value()
136- {
137- return mutable_default_value();
138- }
139-
140- /**
141- * @brief Adjusts the default value set up for a specific type.
142- * @post default_value() == new_default_value;
143- */
144- inline static void set_default_value(const T& new_default_value)
145- {
146- mutable_default_value() = new_default_value;
147- }
148-
149- /**
150 * @brief Property creates a new instance of property and initializes the contained value.
151 * @param t The initial value, defaults to Property<T>::default_value().
152 */
153- inline explicit Property(const T& t = Property<T>::default_value()) : value{t}
154+ inline explicit Property(const T& t = T{}) : value{t}
155 {
156 }
157
158
159=== modified file 'include/com/ubuntu/signal.h'
160--- include/com/ubuntu/signal.h 2013-11-13 09:19:50 +0000
161+++ include/com/ubuntu/signal.h 2013-11-27 08:46:56 +0000
162@@ -53,6 +53,7 @@
163
164 Slot slot;
165 Connection::Dispatcher dispatcher;
166+ Connection connection;
167 };
168
169 public:
170@@ -63,7 +64,12 @@
171 {
172 }
173
174- inline ~Signal() = default;
175+ inline ~Signal()
176+ {
177+ std::lock_guard<std::mutex> lg(d->guard);
178+ for (auto slot : d->slots)
179+ slot.connection.reset();
180+ }
181
182 // Copy construction, assignment and equality comparison are disabled.
183 Signal(const Signal&) = delete;
184@@ -81,27 +87,38 @@
185 */
186 inline Connection connect(const Slot& slot) const
187 {
188+ // Helpers to initialize an invalid connection.
189+ static const Connection::Disconnector empty_disconnector{};
190+ static const Connection::DispatcherInstaller empty_dispatcher_installer{};
191+
192 // The default dispatcher immediately executes the function object
193 // provided as argument on whatever thread is currently running.
194 static const Connection::Dispatcher default_dispatcher
195 = [](const std::function<void()>& handler) { handler(); };
196
197+ Connection conn{empty_disconnector, empty_dispatcher_installer};
198+
199 std::lock_guard<std::mutex> lg(d->guard);
200
201 auto result = d->slots.insert(
202 d->slots.end(),
203- SlotWrapper{slot, default_dispatcher});
204-
205- return Connection(
206- std::bind(
207- &Private::disconnect_slot_for_iterator,
208- d,
209- result),
210- std::bind(
211- &Private::install_dispatcher_for_iterator,
212- d,
213- std::placeholders::_1,
214- result));
215+ SlotWrapper{slot, default_dispatcher, conn});
216+
217+ // We implicitly share our internal state with the connection here
218+ // by passing in our private bits contained in 'd' to the std::bind call.
219+ // This admittedly uncommon approach allows us to cleanly manage connection
220+ // and signal lifetimes without the need to mark everything as mutable.
221+ conn.d->disconnector = std::bind(
222+ &Private::disconnect_slot_for_iterator,
223+ d,
224+ result);
225+ conn.d->dispatcher_installer = std::bind(
226+ &Private::install_dispatcher_for_iterator,
227+ d,
228+ std::placeholders::_1,
229+ result);
230+
231+ return conn;
232 }
233
234 /**
235@@ -148,7 +165,8 @@
236 };
237
238 /**
239- * @brief A signal class that observers can subscribe to, template specialization for signals without arguments.
240+ * @brief A signal class that observers can subscribe to,
241+ * template specialization for signals without arguments.
242 */
243 template<>
244 class Signal<void>
245@@ -169,6 +187,7 @@
246
247 Slot slot;
248 Connection::Dispatcher dispatcher;
249+ Connection connection;
250 };
251
252 public:
253@@ -179,7 +198,12 @@
254 {
255 }
256
257- inline ~Signal() = default;
258+ inline ~Signal()
259+ {
260+ std::lock_guard<std::mutex> lg(d->guard);
261+ for (auto slot : d->slots)
262+ slot.connection.reset();
263+ }
264
265 // Copy construction, assignment and equality comparison are disabled.
266 Signal(const Signal&) = delete;
267@@ -197,25 +221,38 @@
268 */
269 inline Connection connect(const Slot& slot) const
270 {
271+ // Helpers to initialize an invalid connection.
272+ static const Connection::Disconnector empty_disconnector{};
273+ static const Connection::DispatcherInstaller empty_dispatcher_installer{};
274+
275 // The default dispatcher immediately executes the function object
276 // provided as argument on whatever thread is currently running.
277 static const Connection::Dispatcher default_dispatcher
278 = [](const std::function<void()>& handler) { handler(); };
279+
280+ Connection conn{empty_disconnector, empty_dispatcher_installer};
281+
282 std::lock_guard<std::mutex> lg(d->guard);
283+
284 auto result = d->slots.insert(
285 d->slots.end(),
286- SlotWrapper{slot, default_dispatcher});
287-
288- return Connection(
289- std::bind(
290- &Private::disconnect_slot_for_iterator,
291- d,
292- result),
293- std::bind(
294- &Private::install_dispatcher_for_iterator,
295- d,
296- std::placeholders::_1,
297- result));
298+ SlotWrapper{slot, default_dispatcher, conn});
299+
300+ // We implicitly share our internal state with the connection here
301+ // by passing in our private bits contained in 'd' to the std::bind call.
302+ // This admittedly uncommon approach allows us to cleanly manage connection
303+ // and signal lifetimes without the need to mark everything as mutable.
304+ conn.d->disconnector = std::bind(
305+ &Private::disconnect_slot_for_iterator,
306+ d,
307+ result);
308+ conn.d->dispatcher_installer = std::bind(
309+ &Private::install_dispatcher_for_iterator,
310+ d,
311+ std::placeholders::_1,
312+ result);
313+
314+ return conn;
315 }
316
317 /**
318
319=== modified file 'tests/properties_test.cpp'
320--- tests/properties_test.cpp 2013-11-13 10:41:06 +0000
321+++ tests/properties_test.cpp 2013-11-27 08:46:56 +0000
322@@ -26,16 +26,13 @@
323 EXPECT_EQ(int{}, p1.get());
324
325 static const int new_default_value = 42;
326- com::ubuntu::Property<int>::set_default_value(new_default_value);
327- com::ubuntu::Property<int> p2;
328+ com::ubuntu::Property<int> p2{new_default_value};
329
330 EXPECT_EQ(new_default_value, p2.get());
331 }
332
333 TEST(Property, copy_construction_yields_correct_value)
334 {
335- com::ubuntu::Property<int>::set_default_value(0);
336-
337 static const int default_value = 42;
338 com::ubuntu::Property<int> p1{default_value};
339 com::ubuntu::Property<int> p2{p1};
340@@ -45,8 +42,6 @@
341
342 TEST(Property, assignment_operator_for_properties_works)
343 {
344- com::ubuntu::Property<int>::set_default_value(0);
345-
346 static const int default_value = 42;
347 com::ubuntu::Property<int> p1{default_value};
348 com::ubuntu::Property<int> p2;
349@@ -57,8 +52,6 @@
350
351 TEST(Property, assignment_operator_for_raw_values_works)
352 {
353- com::ubuntu::Property<int>::set_default_value(0);
354-
355 static const int default_value = 42;
356 com::ubuntu::Property<int> p1;
357 p1 = default_value;
358@@ -68,8 +61,6 @@
359
360 TEST(Property, equality_operator_for_properties_works)
361 {
362- com::ubuntu::Property<int>::set_default_value(0);
363-
364 static const int default_value = 42;
365 com::ubuntu::Property<int> p1{default_value};
366 com::ubuntu::Property<int> p2;
367@@ -80,8 +71,6 @@
368
369 TEST(Property, equality_operator_for_raw_values_works)
370 {
371- com::ubuntu::Property<int>::set_default_value(0);
372-
373 static const int default_value = 42;
374 com::ubuntu::Property<int> p1{default_value};
375
376@@ -110,8 +99,6 @@
377
378 TEST(Property, signal_changed_is_emitted_with_correct_value_for_set)
379 {
380- com::ubuntu::Property<int>::set_default_value(0);
381-
382 static const int default_value = 42;
383 com::ubuntu::Property<int> p1;
384 Expectation<int> expectation{default_value};
385@@ -125,8 +112,6 @@
386
387 TEST(Property, signal_changed_is_emitted_with_correct_value_for_assignment)
388 {
389- com::ubuntu::Property<int>::set_default_value(0);
390-
391 static const int default_value = 42;
392 com::ubuntu::Property<int> p1;
393
394@@ -141,8 +126,6 @@
395
396 TEST(Property, signal_changed_is_emitted_with_correct_value_for_update)
397 {
398- com::ubuntu::Property<int>::set_default_value(0);
399-
400 static const int default_value = 42;
401 com::ubuntu::Property<int> p1;
402
403
404=== modified file 'tests/signals_test.cpp'
405--- tests/signals_test.cpp 2013-11-13 09:19:50 +0000
406+++ tests/signals_test.cpp 2013-11-27 08:46:56 +0000
407@@ -93,6 +93,20 @@
408 EXPECT_FALSE(expectation.satisfied());
409 }
410
411+TEST(Signal, a_signal_going_out_of_scope_disconnects_from_slots)
412+{
413+ auto signal = std::make_shared<com::ubuntu::Signal<int>>();
414+
415+ auto connection = signal->connect([](int value) { std::cout << value << std::endl; });
416+
417+ signal.reset();
418+
419+ com::ubuntu::Connection::Dispatcher dispatcher{};
420+
421+ EXPECT_NO_THROW(connection.disconnect());
422+ EXPECT_NO_THROW(connection.dispatch_via(dispatcher));
423+}
424+
425 #include <queue>
426
427 namespace
428@@ -116,8 +130,11 @@
429 std::chrono::milliseconds{500},
430 [this]() { return handlers.size() > 0; });
431
432- handlers.front()();
433- handlers.pop();
434+ while (handlers.size() > 0)
435+ {
436+ handlers.front()();
437+ handlers.pop();
438+ }
439 }
440 }
441
442@@ -150,12 +167,10 @@
443 // thread the handler is being called upon equals the thread that the
444 // event loop is running upon.
445 auto connection = s.connect(
446- [&dispatcher, dispatcher_thread_id](int value, double d)
447+ [&dispatcher, dispatcher_thread_id](int value, double)
448 {
449 EXPECT_EQ(dispatcher_thread_id,
450- std::this_thread::get_id());
451-
452- std::cout << d << std::endl;
453+ std::this_thread::get_id());
454
455 if (value == expected_invocation_count)
456 dispatcher.stop();

Subscribers

People subscribed via source and target branches