Merge lp:~thomas-voss/dbus-cpp/fix-signal-subscriptions into lp:dbus-cpp/15.04
- fix-signal-subscriptions
- Merge into 15.04
Proposed by
Thomas Voß
Status: | Merged |
---|---|
Approved by: | Łukasz Zemczak |
Approved revision: | 109 |
Merged at revision: | 102 |
Proposed branch: | lp:~thomas-voss/dbus-cpp/fix-signal-subscriptions |
Merge into: | lp:dbus-cpp/15.04 |
Diff against target: |
363 lines (+137/-44) 6 files modified
include/core/dbus/impl/object.h (+95/-34) include/core/dbus/impl/property.h (+21/-0) include/core/dbus/impl/signal.h (+4/-2) include/core/dbus/object.h (+8/-7) include/core/dbus/property.h (+8/-0) src/core/dbus/service.cpp (+1/-1) |
To merge this branch: | bzr merge lp:~thomas-voss/dbus-cpp/fix-signal-subscriptions |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alberto Aguirre (community) | Approve | ||
Review via email: mp+277101@code.launchpad.net |
Commit message
Ensure that Signal with non-void argument types correctly narrow their match rules.
Description of the change
Ensure that Signal with non-void argument types correctly narrow their match rules.
To post a comment you must log in.
- 103. By Thomas Voß
-
Fix disconnect behavior for stub signals and lifetime issues for Signal<> instances.
- 104. By Thomas Voß
-
Handle o.f.dbus.
Properties. PropertiesChang ed manually to fix circular lifetime dependency. - 105. By Thomas Voß
-
Cache properties on stub object instances.
- 106. By Thomas Voß
-
Ensure that Property:
:about_ to_be_destroyed () is triggered in dtor. - 107. By Thomas Voß
-
Ensure that match rules are correctly removed on property destruction.
- 108. By Thomas Voß
-
Only subscribe to PropertiesChanged once per dbus::Object instance (on demand).
- 109. By Thomas Voß
-
Remove match for PropertiesChanged in Object::~Object.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'include/core/dbus/impl/object.h' | |||
2 | --- include/core/dbus/impl/object.h 2014-10-30 13:22:52 +0000 | |||
3 | +++ include/core/dbus/impl/object.h 2015-11-16 15:33:02 +0000 | |||
4 | @@ -19,7 +19,6 @@ | |||
5 | 19 | #define CORE_DBUS_IMPL_OBJECT_H_ | 19 | #define CORE_DBUS_IMPL_OBJECT_H_ |
6 | 20 | 20 | ||
7 | 21 | #include <core/dbus/bus.h> | 21 | #include <core/dbus/bus.h> |
8 | 22 | #include <core/dbus/lifetime_constrained_cache.h> | ||
9 | 23 | #include <core/dbus/match_rule.h> | 22 | #include <core/dbus/match_rule.h> |
10 | 24 | #include <core/dbus/message_router.h> | 23 | #include <core/dbus/message_router.h> |
11 | 25 | #include <core/dbus/message_streaming_operators.h> | 24 | #include <core/dbus/message_streaming_operators.h> |
12 | @@ -37,6 +36,7 @@ | |||
13 | 37 | 36 | ||
14 | 38 | #include <functional> | 37 | #include <functional> |
15 | 39 | #include <future> | 38 | #include <future> |
16 | 39 | #include <iostream> | ||
17 | 40 | #include <map> | 40 | #include <map> |
18 | 41 | #include <memory> | 41 | #include <memory> |
19 | 42 | #include <string> | 42 | #include <string> |
20 | @@ -70,17 +70,17 @@ | |||
21 | 70 | object_path.as_string(), | 70 | object_path.as_string(), |
22 | 71 | traits::Service<typename Method::Interface>::interface_name().c_str(), | 71 | traits::Service<typename Method::Interface>::interface_name().c_str(), |
23 | 72 | Method::name()); | 72 | Method::name()); |
25 | 73 | 73 | ||
26 | 74 | if (!msg) | 74 | if (!msg) |
27 | 75 | throw std::runtime_error("No memory available to allocate DBus message"); | 75 | throw std::runtime_error("No memory available to allocate DBus message"); |
29 | 76 | 76 | ||
30 | 77 | auto writer = msg->writer(); | 77 | auto writer = msg->writer(); |
31 | 78 | encode_message(writer, args...); | 78 | encode_message(writer, args...); |
33 | 79 | 79 | ||
34 | 80 | auto reply = parent->get_connection()->send_with_reply_and_block_for_at_most( | 80 | auto reply = parent->get_connection()->send_with_reply_and_block_for_at_most( |
35 | 81 | msg, | 81 | msg, |
36 | 82 | Method::default_timeout()); | 82 | Method::default_timeout()); |
38 | 83 | 83 | ||
39 | 84 | return Result<ResultType>::from_message(reply); | 84 | return Result<ResultType>::from_message(reply); |
40 | 85 | } | 85 | } |
41 | 86 | 86 | ||
42 | @@ -153,40 +153,57 @@ | |||
43 | 153 | inline std::shared_ptr<Property<PropertyDescription>> | 153 | inline std::shared_ptr<Property<PropertyDescription>> |
44 | 154 | Object::get_property() | 154 | Object::get_property() |
45 | 155 | { | 155 | { |
46 | 156 | // If this is a proxy object we set up listening for property changes the | ||
47 | 157 | // first time someone accesses properties. | ||
48 | 158 | if (parent->is_stub()) | ||
49 | 159 | { | ||
50 | 160 | if (!signal_properties_changed) | ||
51 | 161 | { | ||
52 | 162 | signal_properties_changed | ||
53 | 163 | = get_signal<interfaces::Properties::Signals::PropertiesChanged>(); | ||
54 | 164 | |||
55 | 165 | signal_properties_changed->connect( | ||
56 | 166 | std::bind( | ||
57 | 167 | &Object::on_properties_changed, | ||
58 | 168 | shared_from_this(), | ||
59 | 169 | std::placeholders::_1)); | ||
60 | 170 | } | ||
61 | 171 | } | ||
62 | 172 | |||
63 | 173 | typedef Property<PropertyDescription> PropertyType; | 156 | typedef Property<PropertyDescription> PropertyType; |
64 | 174 | auto property = | ||
65 | 175 | PropertyType::make_property( | ||
66 | 176 | shared_from_this()); | ||
67 | 177 | 157 | ||
68 | 158 | // Creating a stub property for a remote object/property instance | ||
69 | 159 | // requires the following steps: | ||
70 | 160 | // [1.] Look up if we already have a property instance available in the cache, | ||
71 | 161 | // leveraging the tuple (path, interface, name) as key. | ||
72 | 162 | // [1.1] If yes: return the property. | ||
73 | 163 | // [1.2] If no: Create a new proeprty instance and: | ||
74 | 164 | // [1.2.1] Make it known to the cache. | ||
75 | 165 | // [1.2.2] Wire it up for property_changed signal receiving. | ||
76 | 166 | // [1.2.3] Communicate a new match rule to the dbus daemon to enable reception. | ||
77 | 178 | if (parent->is_stub()) | 167 | if (parent->is_stub()) |
78 | 179 | { | 168 | { |
87 | 180 | auto tuple = std::make_tuple( | 169 | auto itf = traits::Service<typename PropertyDescription::Interface>::interface_name(); |
88 | 181 | traits::Service<typename PropertyDescription::Interface>::interface_name(), | 170 | auto name = PropertyDescription::name(); |
89 | 182 | PropertyDescription::name()); | 171 | auto ekey = std::make_tuple(path(), itf, name); |
90 | 183 | 172 | ||
91 | 184 | property_changed_vtable[tuple] = std::bind( | 173 | auto property = Object::property_cache<PropertyDescription>().retrieve_value_for_key(ekey); |
92 | 185 | &Property<PropertyDescription>::handle_changed, | 174 | if (property) |
93 | 186 | property, | 175 | { |
94 | 187 | std::placeholders::_1); | 176 | return property; |
95 | 177 | } | ||
96 | 178 | |||
97 | 179 | auto mr = MatchRule() | ||
98 | 180 | .type(Message::Type::signal) | ||
99 | 181 | .interface(traits::Service<interfaces::Properties>::interface_name()) | ||
100 | 182 | .member(interfaces::Properties::Signals::PropertiesChanged::name()); | ||
101 | 183 | |||
102 | 184 | property = PropertyType::make_property(shared_from_this()); | ||
103 | 185 | |||
104 | 186 | Object::property_cache<PropertyDescription>().insert_value_for_key(ekey, property); | ||
105 | 187 | |||
106 | 188 | // We only ever do this once per object. | ||
107 | 189 | std::call_once(add_match_once, [&]() | ||
108 | 190 | { | ||
109 | 191 | // [1.2.4] Inform the dbus daemon that we would like to receive the respective signals. | ||
110 | 192 | add_match(mr); | ||
111 | 193 | }); | ||
112 | 194 | |||
113 | 195 | // [1.2.2] Enable dispatching of changes. | ||
114 | 196 | std::weak_ptr<PropertyType> wp{property}; | ||
115 | 197 | property_changed_vtable[std::make_tuple(itf, name)] = [wp](const types::Variant& arg) | ||
116 | 198 | { | ||
117 | 199 | if (auto sp = wp.lock()) | ||
118 | 200 | sp->handle_changed(arg); | ||
119 | 201 | }; | ||
120 | 202 | |||
121 | 203 | return property; | ||
122 | 188 | } | 204 | } |
124 | 189 | return property; | 205 | |
125 | 206 | return PropertyType::make_property(shared_from_this()); | ||
126 | 190 | } | 207 | } |
127 | 191 | 208 | ||
128 | 192 | template<typename Interface> | 209 | template<typename Interface> |
129 | @@ -312,6 +329,23 @@ | |||
130 | 312 | &MessageRouter<PropertyKey>::operator(), | 329 | &MessageRouter<PropertyKey>::operator(), |
131 | 313 | std::ref(set_property_router), | 330 | std::ref(set_property_router), |
132 | 314 | std::placeholders::_1)); | 331 | std::placeholders::_1)); |
133 | 332 | } else | ||
134 | 333 | { | ||
135 | 334 | // We centrally route org.freedesktop.DBus.Properties.PropertiesChanged | ||
136 | 335 | // through the object, which in turn routes via a custom Property cache. | ||
137 | 336 | signal_router.install_route( | ||
138 | 337 | SignalKey{ | ||
139 | 338 | traits::Service<interfaces::Properties>::interface_name(), | ||
140 | 339 | interfaces::Properties::Signals::PropertiesChanged::name() | ||
141 | 340 | }, | ||
142 | 341 | // Passing 'this' is fine as the lifetime of the signal_router is upper limited | ||
143 | 342 | // by the lifetime of 'this'. | ||
144 | 343 | [this](const Message::Ptr& msg) | ||
145 | 344 | { | ||
146 | 345 | interfaces::Properties::Signals::PropertiesChanged::ArgumentType arg; | ||
147 | 346 | msg->reader() >> arg; | ||
148 | 347 | on_properties_changed(arg); | ||
149 | 348 | }); | ||
150 | 315 | } | 349 | } |
151 | 316 | } | 350 | } |
152 | 317 | 351 | ||
153 | @@ -319,6 +353,20 @@ | |||
154 | 319 | { | 353 | { |
155 | 320 | parent->get_connection()->access_signal_router().uninstall_route(object_path); | 354 | parent->get_connection()->access_signal_router().uninstall_route(object_path); |
156 | 321 | parent->get_connection()->unregister_object_path(object_path); | 355 | parent->get_connection()->unregister_object_path(object_path); |
157 | 356 | |||
158 | 357 | auto mr = MatchRule() | ||
159 | 358 | .type(Message::Type::signal) | ||
160 | 359 | .interface(traits::Service<interfaces::Properties>::interface_name()) | ||
161 | 360 | .member(interfaces::Properties::Signals::PropertiesChanged::name()); | ||
162 | 361 | |||
163 | 362 | try | ||
164 | 363 | { | ||
165 | 364 | remove_match(mr); | ||
166 | 365 | } catch(...) | ||
167 | 366 | { | ||
168 | 367 | // We consciously drop all possible exceptions here. There is hardly | ||
169 | 368 | // anything we can do about the error anyway. | ||
170 | 369 | } | ||
171 | 322 | } | 370 | } |
172 | 323 | 371 | ||
173 | 324 | inline void Object::add_match(const MatchRule& rule) | 372 | inline void Object::add_match(const MatchRule& rule) |
174 | @@ -346,6 +394,19 @@ | |||
175 | 346 | } | 394 | } |
176 | 347 | } | 395 | } |
177 | 348 | } | 396 | } |
178 | 397 | |||
179 | 398 | template<typename PropertyDescription> | ||
180 | 399 | inline core::dbus::ThreadSafeLifetimeConstrainedCache< | ||
181 | 400 | core::dbus::Object::CacheKey, | ||
182 | 401 | core::dbus::Property<PropertyDescription>>& | ||
183 | 402 | core::dbus::Object::property_cache() | ||
184 | 403 | { | ||
185 | 404 | static core::dbus::ThreadSafeLifetimeConstrainedCache< | ||
186 | 405 | core::dbus::Object::CacheKey, | ||
187 | 406 | core::dbus::Property<PropertyDescription> | ||
188 | 407 | > cache; | ||
189 | 408 | return cache; | ||
190 | 409 | } | ||
191 | 349 | } | 410 | } |
192 | 350 | } | 411 | } |
193 | 351 | 412 | ||
194 | 352 | 413 | ||
195 | === modified file 'include/core/dbus/impl/property.h' | |||
196 | --- include/core/dbus/impl/property.h 2014-06-10 08:44:17 +0000 | |||
197 | +++ include/core/dbus/impl/property.h 2015-11-16 15:33:02 +0000 | |||
198 | @@ -64,6 +64,13 @@ | |||
199 | 64 | } | 64 | } |
200 | 65 | 65 | ||
201 | 66 | template<typename PropertyType> | 66 | template<typename PropertyType> |
202 | 67 | const core::Signal<void>& | ||
203 | 68 | Property<PropertyType>::about_to_be_destroyed() const | ||
204 | 69 | { | ||
205 | 70 | return signal_about_to_be_destroyed; | ||
206 | 71 | } | ||
207 | 72 | |||
208 | 73 | template<typename PropertyType> | ||
209 | 67 | std::shared_ptr<Property<PropertyType>> | 74 | std::shared_ptr<Property<PropertyType>> |
210 | 68 | Property<PropertyType>::make_property(const std::shared_ptr<Object>& parent) | 75 | Property<PropertyType>::make_property(const std::shared_ptr<Object>& parent) |
211 | 69 | { | 76 | { |
212 | @@ -109,6 +116,20 @@ | |||
213 | 109 | } | 116 | } |
214 | 110 | 117 | ||
215 | 111 | template<typename PropertyType> | 118 | template<typename PropertyType> |
216 | 119 | Property<PropertyType>::~Property() | ||
217 | 120 | { | ||
218 | 121 | try | ||
219 | 122 | { | ||
220 | 123 | signal_about_to_be_destroyed(); | ||
221 | 124 | } catch(...) | ||
222 | 125 | { | ||
223 | 126 | // Consciously dropping all exceptions here. | ||
224 | 127 | // There is hardly anything we can do about it while | ||
225 | 128 | // tearing down the object anyway. | ||
226 | 129 | } | ||
227 | 130 | } | ||
228 | 131 | |||
229 | 132 | template<typename PropertyType> | ||
230 | 112 | void | 133 | void |
231 | 113 | Property<PropertyType>::handle_get(const Message::Ptr& msg) | 134 | Property<PropertyType>::handle_get(const Message::Ptr& msg) |
232 | 114 | { | 135 | { |
233 | 115 | 136 | ||
234 | === modified file 'include/core/dbus/impl/signal.h' | |||
235 | --- include/core/dbus/impl/signal.h 2015-10-21 20:04:04 +0000 | |||
236 | +++ include/core/dbus/impl/signal.h 2015-11-16 15:33:02 +0000 | |||
237 | @@ -121,8 +121,8 @@ | |||
238 | 121 | &Signal<SignalDescription>::operator(), | 121 | &Signal<SignalDescription>::operator(), |
239 | 122 | this, | 122 | this, |
240 | 123 | std::placeholders::_1)); | 123 | std::placeholders::_1)); |
243 | 124 | parent->add_match( | 124 | rule = rule.type(Message::Type::signal).interface(interface).member(name); |
244 | 125 | rule.type(Message::Type::signal).interface(interface).member(name)); | 125 | parent->add_match(rule); |
245 | 126 | } | 126 | } |
246 | 127 | 127 | ||
247 | 128 | template<typename SignalDescription, typename Argument> | 128 | template<typename SignalDescription, typename Argument> |
248 | @@ -309,6 +309,8 @@ | |||
249 | 309 | &Signal<SignalDescription, typename SignalDescription::ArgumentType>::operator(), | 309 | &Signal<SignalDescription, typename SignalDescription::ArgumentType>::operator(), |
250 | 310 | this, | 310 | this, |
251 | 311 | std::placeholders::_1)); | 311 | std::placeholders::_1)); |
252 | 312 | |||
253 | 313 | d->rule = d->rule.type(Message::Type::signal).interface(interface).member(name); | ||
254 | 312 | } | 314 | } |
255 | 313 | 315 | ||
256 | 314 | template<typename SignalDescription> | 316 | template<typename SignalDescription> |
257 | 315 | 317 | ||
258 | === modified file 'include/core/dbus/object.h' | |||
259 | --- include/core/dbus/object.h 2014-10-23 21:09:56 +0000 | |||
260 | +++ include/core/dbus/object.h 2015-11-16 15:33:02 +0000 | |||
261 | @@ -19,12 +19,14 @@ | |||
262 | 19 | #define CORE_DBUS_OBJECT_H_ | 19 | #define CORE_DBUS_OBJECT_H_ |
263 | 20 | 20 | ||
264 | 21 | #include <core/dbus/bus.h> | 21 | #include <core/dbus/bus.h> |
265 | 22 | #include <core/dbus/lifetime_constrained_cache.h> | ||
266 | 22 | #include <core/dbus/service.h> | 23 | #include <core/dbus/service.h> |
267 | 23 | 24 | ||
268 | 24 | #include <functional> | 25 | #include <functional> |
269 | 25 | #include <future> | 26 | #include <future> |
270 | 26 | #include <map> | 27 | #include <map> |
271 | 27 | #include <memory> | 28 | #include <memory> |
272 | 29 | #include <mutex> | ||
273 | 28 | #include <ostream> | 30 | #include <ostream> |
274 | 29 | #include <string> | 31 | #include <string> |
275 | 30 | 32 | ||
276 | @@ -82,10 +84,14 @@ | |||
277 | 82 | class Object : public std::enable_shared_from_this<Object> | 84 | class Object : public std::enable_shared_from_this<Object> |
278 | 83 | { | 85 | { |
279 | 84 | private: | 86 | private: |
280 | 87 | typedef std::tuple<types::ObjectPath, std::string, std::string> CacheKey; | ||
281 | 85 | typedef std::tuple<std::string, std::string> MethodKey; | 88 | typedef std::tuple<std::string, std::string> MethodKey; |
282 | 86 | typedef std::tuple<std::string, std::string> PropertyKey; | 89 | typedef std::tuple<std::string, std::string> PropertyKey; |
283 | 87 | typedef std::tuple<std::string, std::string> SignalKey; | 90 | typedef std::tuple<std::string, std::string> SignalKey; |
284 | 88 | 91 | ||
285 | 92 | template<typename PropertyDescription> | ||
286 | 93 | static ThreadSafeLifetimeConstrainedCache<CacheKey, Property<PropertyDescription>>& property_cache(); | ||
287 | 94 | |||
288 | 89 | public: | 95 | public: |
289 | 90 | typedef std::shared_ptr<Object> Ptr; | 96 | typedef std::shared_ptr<Object> Ptr; |
290 | 91 | typedef std::function<void(const Message::Ptr&)> MethodHandler; | 97 | typedef std::function<void(const Message::Ptr&)> MethodHandler; |
291 | @@ -173,7 +179,7 @@ | |||
292 | 173 | * @param [in] path The path to associate the object with. | 179 | * @param [in] path The path to associate the object with. |
293 | 174 | * @return An object instance or nullptr in case of errors. | 180 | * @return An object instance or nullptr in case of errors. |
294 | 175 | */ | 181 | */ |
296 | 176 | std::shared_ptr<Object> | 182 | std::shared_ptr<Object> |
297 | 177 | inline add_object_for_path(const types::ObjectPath& path); | 183 | inline add_object_for_path(const types::ObjectPath& path); |
298 | 178 | 184 | ||
299 | 179 | /** | 185 | /** |
300 | @@ -227,16 +233,11 @@ | |||
301 | 227 | MessageRouter<MethodKey> method_router; | 233 | MessageRouter<MethodKey> method_router; |
302 | 228 | MessageRouter<PropertyKey> get_property_router; | 234 | MessageRouter<PropertyKey> get_property_router; |
303 | 229 | MessageRouter<PropertyKey> set_property_router; | 235 | MessageRouter<PropertyKey> set_property_router; |
304 | 236 | std::once_flag add_match_once; | ||
305 | 230 | std::map< | 237 | std::map< |
306 | 231 | std::tuple<std::string, std::string>, | 238 | std::tuple<std::string, std::string>, |
307 | 232 | std::function<void(const types::Variant&)> | 239 | std::function<void(const types::Variant&)> |
308 | 233 | > property_changed_vtable; | 240 | > property_changed_vtable; |
309 | 234 | std::shared_ptr< | ||
310 | 235 | Signal< | ||
311 | 236 | interfaces::Properties::Signals::PropertiesChanged, | ||
312 | 237 | interfaces::Properties::Signals::PropertiesChanged::ArgumentType | ||
313 | 238 | > | ||
314 | 239 | > signal_properties_changed; | ||
315 | 240 | }; | 241 | }; |
316 | 241 | } | 242 | } |
317 | 242 | } | 243 | } |
318 | 243 | 244 | ||
319 | === modified file 'include/core/dbus/property.h' | |||
320 | --- include/core/dbus/property.h 2014-01-20 21:22:02 +0000 | |||
321 | +++ include/core/dbus/property.h 2015-11-16 15:33:02 +0000 | |||
322 | @@ -43,6 +43,8 @@ | |||
323 | 43 | typedef typename PropertyType::ValueType ValueType; | 43 | typedef typename PropertyType::ValueType ValueType; |
324 | 44 | typedef core::Property<ValueType> Super; | 44 | typedef core::Property<ValueType> Super; |
325 | 45 | 45 | ||
326 | 46 | inline ~Property(); | ||
327 | 47 | |||
328 | 46 | /** | 48 | /** |
329 | 47 | * @brief Non-mutable access to the contained value. | 49 | * @brief Non-mutable access to the contained value. |
330 | 48 | * @return Non-mutable reference to the contained value. | 50 | * @return Non-mutable reference to the contained value. |
331 | @@ -61,6 +63,11 @@ | |||
332 | 61 | */ | 63 | */ |
333 | 62 | inline bool is_writable() const; | 64 | inline bool is_writable() const; |
334 | 63 | 65 | ||
335 | 66 | /** | ||
336 | 67 | * @brief Emitted during destruction of an object instance. | ||
337 | 68 | */ | ||
338 | 69 | inline const core::Signal<void>& about_to_be_destroyed() const; | ||
339 | 70 | |||
340 | 64 | protected: | 71 | protected: |
341 | 65 | friend class Object; | 72 | friend class Object; |
342 | 66 | 73 | ||
343 | @@ -82,6 +89,7 @@ | |||
344 | 82 | std::string interface; | 89 | std::string interface; |
345 | 83 | std::string name; | 90 | std::string name; |
346 | 84 | bool writable; | 91 | bool writable; |
347 | 92 | core::Signal<void> signal_about_to_be_destroyed; | ||
348 | 85 | }; | 93 | }; |
349 | 86 | } | 94 | } |
350 | 87 | } | 95 | } |
351 | 88 | 96 | ||
352 | === modified file 'src/core/dbus/service.cpp' | |||
353 | --- src/core/dbus/service.cpp 2014-06-30 04:57:08 +0000 | |||
354 | +++ src/core/dbus/service.cpp 2015-11-16 15:33:02 +0000 | |||
355 | @@ -79,7 +79,7 @@ | |||
356 | 79 | 79 | ||
357 | 80 | void Service::remove_match(const MatchRule& rule) | 80 | void Service::remove_match(const MatchRule& rule) |
358 | 81 | { | 81 | { |
360 | 82 | connection->remove_match(rule); | 82 | connection->remove_match(rule.sender(name)); |
361 | 83 | } | 83 | } |
362 | 84 | 84 | ||
363 | 85 | Service::Service(const Bus::Ptr& connection, const std::string& name) | 85 | Service::Service(const Bus::Ptr& connection, const std::string& name) |
LGTM