sdbus-c++: include/sdbus-c++/ConvenienceApiClasses.inl Source File

sdbus-c++ 1.4.0 High-level C++ D-Bus library based on systemd D-Bus implementation

Loading...
Searching...
No Matches
ConvenienceApiClasses.inl
Go to the documentation of this file.
1
27#ifndef SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
28#define SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
29
30#include <sdbus-c++/IObject.h>
31#include <sdbus-c++/IProxy.h>
32#include <sdbus-c++/Message.h>
33#include <sdbus-c++/MethodResult.h>
34#include <sdbus-c++/Types.h>
35#include <sdbus-c++/TypeTraits.h>
36#include <sdbus-c++/Error.h>
37#include <string>
38#include <tuple>
39#include <exception>
40#include <cassert>
41
42namespace sdbus {
43
44 /*** ----------------- ***/
45 /*** MethodRegistrator ***/
46 /*** ----------------- ***/
47
48 inline MethodRegistrator::MethodRegistrator(IObject& object, std::string methodName)
49 : object_(object)
50 , methodName_(std::move(methodName))
51 , exceptions_(std::uncaught_exceptions())
52 {
53 }
54
55 inline MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must
56 { // explicitly be allowed to throw
57 // Don't register the method if MethodRegistrator threw an exception in one of its methods
58 if (std::uncaught_exceptions() != exceptions_)
59 return;
60
61 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
62 assert(methodCallback_); // implementedAs() must be placed/called prior to this function
63
64 // registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
65 // temporary object, i.e. not as a stack-allocated object, the double-exception situation
66 // shall never happen. I.e. it should not happen that this destructor is directly called
67 // in the stack-unwinding process of another flying exception (which would lead to immediate
68 // termination). It can be called indirectly in the destructor of another object, but that's
69 // fine and safe provided that the caller catches exceptions thrown from here.
70 // Therefore, we can allow registerMethod() to throw even if we are in the destructor.
71 // Bottomline is, to be on the safe side, the caller must take care of catching and reacting
72 // to the exception thrown from here if the caller is a destructor itself.
73 object_.registerMethod( interfaceName_
74 , std::move(methodName_)
75 , std::move(inputSignature_)
76 , inputParamNames_
77 , std::move(outputSignature_)
78 , outputParamNames_
79 , std::move(methodCallback_)
80 , std::move(flags_));
81 }
82
83 inline MethodRegistrator& MethodRegistrator::onInterface(std::string interfaceName)
84 {
85 interfaceName_ = std::move(interfaceName);
86
87 return *this;
88 }
89
90 template <typename _Function>
91 MethodRegistrator& MethodRegistrator::implementedAs(_Function&& callback)
92 {
93 inputSignature_ = signature_of_function_input_arguments<_Function>::str();
94 outputSignature_ = signature_of_function_output_arguments<_Function>::str();
95 methodCallback_ = [callback = std::forward<_Function>(callback)](MethodCall call)
96 {
97 // Create a tuple of callback input arguments' types, which will be used
98 // as a storage for the argument values deserialized from the message.
99 tuple_of_function_input_arg_types_t<_Function> inputArgs;
100
101 // Deserialize input arguments from the message into the tuple.
102 call >> inputArgs;
103
104 if constexpr (!is_async_method_v<_Function>)
105 {
106 // Invoke callback with input arguments from the tuple.
107 auto ret = sdbus::apply(callback, inputArgs);
108
109 // Store output arguments to the reply message and send it back.
110 auto reply = call.createReply();
111 reply << ret;
112 reply.send();
113 }
114 else
115 {
116 // Invoke callback with input arguments from the tuple and with result object to be set later
117 using AsyncResult = typename function_traits<_Function>::async_result_t;
118 sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs));
119 }
120 };
121
122 return *this;
123 }
124
125 inline MethodRegistrator& MethodRegistrator::withInputParamNames(std::vector<std::string> paramNames)
126 {
127 inputParamNames_ = std::move(paramNames);
128
129 return *this;
130 }
131
132 template <typename... _String>
133 inline MethodRegistrator& MethodRegistrator::withInputParamNames(_String... paramNames)
134 {
135 static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
136
137 return withInputParamNames({paramNames...});
138 }
139
140 inline MethodRegistrator& MethodRegistrator::withOutputParamNames(std::vector<std::string> paramNames)
141 {
142 outputParamNames_ = std::move(paramNames);
143
144 return *this;
145 }
146
147 template <typename... _String>
148 inline MethodRegistrator& MethodRegistrator::withOutputParamNames(_String... paramNames)
149 {
150 static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
151
152 return withOutputParamNames({paramNames...});
153 }
154
155 inline MethodRegistrator& MethodRegistrator::markAsDeprecated()
156 {
157 flags_.set(Flags::DEPRECATED);
158
159 return *this;
160 }
161
162 inline MethodRegistrator& MethodRegistrator::markAsPrivileged()
163 {
164 flags_.set(Flags::PRIVILEGED);
165
166 return *this;
167 }
168
169 inline MethodRegistrator& MethodRegistrator::withNoReply()
170 {
171 flags_.set(Flags::METHOD_NO_REPLY);
172
173 return *this;
174 }
175
176 /*** ----------------- ***/
177 /*** SignalRegistrator ***/
178 /*** ----------------- ***/
179
180 inline SignalRegistrator::SignalRegistrator(IObject& object, std::string signalName)
181 : object_(object)
182 , signalName_(std::move(signalName))
183 , exceptions_(std::uncaught_exceptions())
184 {
185 }
186
187 inline SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
188 { // explicitly be allowed to throw
189 // Don't register the signal if SignalRegistrator threw an exception in one of its methods
190 if (std::uncaught_exceptions() != exceptions_)
191 return;
192
193 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
194
195 // registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
196 // temporary object, i.e. not as a stack-allocated object, the double-exception situation
197 // shall never happen. I.e. it should not happen that this destructor is directly called
198 // in the stack-unwinding process of another flying exception (which would lead to immediate
199 // termination). It can be called indirectly in the destructor of another object, but that's
200 // fine and safe provided that the caller catches exceptions thrown from here.
201 // Therefore, we can allow registerSignal() to throw even if we are in the destructor.
202 // Bottomline is, to be on the safe side, the caller must take care of catching and reacting
203 // to the exception thrown from here if the caller is a destructor itself.
204 object_.registerSignal( interfaceName_
205 , std::move(signalName_)
206 , std::move(signalSignature_)
207 , paramNames_
208 , std::move(flags_) );
209 }
210
211 inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
212 {
213 interfaceName_ = std::move(interfaceName);
214
215 return *this;
216 }
217
218 template <typename... _Args>
219 inline SignalRegistrator& SignalRegistrator::withParameters()
220 {
221 signalSignature_ = signature_of_function_input_arguments<void(_Args...)>::str();
222
223 return *this;
224 }
225
226 template <typename... _Args>
227 inline SignalRegistrator& SignalRegistrator::withParameters(std::vector<std::string> paramNames)
228 {
229 paramNames_ = std::move(paramNames);
230
231 return withParameters<_Args...>();
232 }
233
234 template <typename... _Args, typename... _String>
235 inline SignalRegistrator& SignalRegistrator::withParameters(_String... paramNames)
236 {
237 static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
238 static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match");
239
240 return withParameters<_Args...>({paramNames...});
241 }
242
243 inline SignalRegistrator& SignalRegistrator::markAsDeprecated()
244 {
245 flags_.set(Flags::DEPRECATED);
246
247 return *this;
248 }
249
250 /*** ------------------- ***/
251 /*** PropertyRegistrator ***/
252 /*** ------------------- ***/
253
254 inline PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
255 : object_(object)
256 , propertyName_(propertyName)
257 , exceptions_(std::uncaught_exceptions())
258 {
259 }
260
261 inline PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
262 { // explicitly be allowed to throw
263 // Don't register the property if PropertyRegistrator threw an exception in one of its methods
264 if (std::uncaught_exceptions() != exceptions_)
265 return;
266
267 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
268
269 // registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
270 // temporary object, i.e. not as a stack-allocated object, the double-exception situation
271 // shall never happen. I.e. it should not happen that this destructor is directly called
272 // in the stack-unwinding process of another flying exception (which would lead to immediate
273 // termination). It can be called indirectly in the destructor of another object, but that's
274 // fine and safe provided that the caller catches exceptions thrown from here.
275 // Therefore, we can allow registerProperty() to throw even if we are in the destructor.
276 // Bottomline is, to be on the safe side, the caller must take care of catching and reacting
277 // to the exception thrown from here if the caller is a destructor itself.
278 object_.registerProperty( interfaceName_
279 , propertyName_
280 , propertySignature_
281 , std::move(getter_)
282 , std::move(setter_)
283 , flags_ );
284 }
285
286 inline PropertyRegistrator& PropertyRegistrator::onInterface(std::string interfaceName)
287 {
288 interfaceName_ = std::move(interfaceName);
289
290 return *this;
291 }
292
293 template <typename _Function>
294 inline PropertyRegistrator& PropertyRegistrator::withGetter(_Function&& callback)
295 {
296 static_assert(function_argument_count_v<_Function> == 0, "Property getter function must not take any arguments");
297 static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
298
299 if (propertySignature_.empty())
300 propertySignature_ = signature_of_function_output_arguments<_Function>::str();
301
302 getter_ = [callback = std::forward<_Function>(callback)](PropertyGetReply& reply)
303 {
304 // Get the propety value and serialize it into the pre-constructed reply message
305 reply << callback();
306 };
307
308 return *this;
309 }
310
311 template <typename _Function>
312 inline PropertyRegistrator& PropertyRegistrator::withSetter(_Function&& callback)
313 {
314 static_assert(function_argument_count_v<_Function> == 1, "Property setter function must take one parameter - the property value");
315 static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
316
317 if (propertySignature_.empty())
318 propertySignature_ = signature_of_function_input_arguments<_Function>::str();
319
320 setter_ = [callback = std::forward<_Function>(callback)](PropertySetCall& call)
321 {
322 // Default-construct property value
323 using property_type = function_argument_t<_Function, 0>;
324 std::decay_t<property_type> property;
325
326 // Deserialize property value from the incoming call message
327 call >> property;
328
329 // Invoke setter with the value
330 callback(property);
331 };
332
333 return *this;
334 }
335
336 inline PropertyRegistrator& PropertyRegistrator::markAsDeprecated()
337 {
338 flags_.set(Flags::DEPRECATED);
339
340 return *this;
341 }
342
343 inline PropertyRegistrator& PropertyRegistrator::markAsPrivileged()
344 {
345 flags_.set(Flags::PRIVILEGED);
346
347 return *this;
348 }
349
350 inline PropertyRegistrator& PropertyRegistrator::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
351 {
352 flags_.set(behavior);
353
354 return *this;
355 }
356
357 /*** -------------------- ***/
358 /*** InterfaceFlagsSetter ***/
359 /*** -------------------- ***/
360
361 inline InterfaceFlagsSetter::InterfaceFlagsSetter(IObject& object, const std::string& interfaceName)
362 : object_(object)
363 , interfaceName_(interfaceName)
364 , exceptions_(std::uncaught_exceptions())
365 {
366 }
367
368 inline InterfaceFlagsSetter::~InterfaceFlagsSetter() noexcept(false) // since C++11, destructors must
369 { // explicitly be allowed to throw
370 // Don't set any flags if InterfaceFlagsSetter threw an exception in one of its methods
371 if (std::uncaught_exceptions() != exceptions_)
372 return;
373
374 // setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
375 // temporary object, i.e. not as a stack-allocated object, the double-exception situation
376 // shall never happen. I.e. it should not happen that this destructor is directly called
377 // in the stack-unwinding process of another flying exception (which would lead to immediate
378 // termination). It can be called indirectly in the destructor of another object, but that's
379 // fine and safe provided that the caller catches exceptions thrown from here.
380 // Therefore, we can allow setInterfaceFlags() to throw even if we are in the destructor.
381 // Bottomline is, to be on the safe side, the caller must take care of catching and reacting
382 // to the exception thrown from here if the caller is a destructor itself.
383 object_.setInterfaceFlags(interfaceName_, std::move(flags_));
384 }
385
386 inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsDeprecated()
387 {
388 flags_.set(Flags::DEPRECATED);
389
390 return *this;
391 }
392
393 inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsPrivileged()
394 {
395 flags_.set(Flags::PRIVILEGED);
396
397 return *this;
398 }
399
400 inline InterfaceFlagsSetter& InterfaceFlagsSetter::withNoReplyMethods()
401 {
402 flags_.set(Flags::METHOD_NO_REPLY);
403
404 return *this;
405 }
406
407 inline InterfaceFlagsSetter& InterfaceFlagsSetter::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
408 {
409 flags_.set(behavior);
410
411 return *this;
412 }
413
414 /*** ------------- ***/
415 /*** SignalEmitter ***/
416 /*** ------------- ***/
417
418 inline SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
419 : object_(object)
420 , signalName_(signalName)
421 , exceptions_(std::uncaught_exceptions())
422 {
423 }
424
425 inline SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must
426 { // explicitly be allowed to throw
427 // Don't emit the signal if SignalEmitter threw an exception in one of its methods
428 if (std::uncaught_exceptions() != exceptions_)
429 return;
430
431 // emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
432 // temporary object, i.e. not as a stack-allocated object, the double-exception situation
433 // shall never happen. I.e. it should not happen that this destructor is directly called
434 // in the stack-unwinding process of another flying exception (which would lead to immediate
435 // termination). It can be called indirectly in the destructor of another object, but that's
436 // fine and safe provided that the caller catches exceptions thrown from here.
437 // Therefore, we can allow emitSignal() to throw even if we are in the destructor.
438 // Bottomline is, to be on the safe side, the caller must take care of catching and reacting
439 // to the exception thrown from here if the caller is a destructor itself.
440 object_.emitSignal(signal_);
441 }
442
443 inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
444 {
445 signal_ = object_.createSignal(interfaceName, signalName_);
446
447 return *this;
448 }
449
450 template <typename... _Args>
451 inline void SignalEmitter::withArguments(_Args&&... args)
452 {
453 assert(signal_.isValid()); // onInterface() must be placed/called prior to withArguments()
454
455 detail::serialize_pack(signal_, std::forward<_Args>(args)...);
456 }
457
458 /*** ------------- ***/
459 /*** MethodInvoker ***/
460 /*** ------------- ***/
461
462 inline MethodInvoker::MethodInvoker(IProxy& proxy, const std::string& methodName)
463 : proxy_(proxy)
464 , methodName_(methodName)
465 , exceptions_(std::uncaught_exceptions())
466 {
467 }
468
469 inline MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
470 { // explicitly be allowed to throw
471 // Don't call the method if it has been called already or if MethodInvoker
472 // threw an exception in one of its methods
473 if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
474 return;
475
476 // callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
477 // temporary object, i.e. not as a stack-allocated object, the double-exception situation
478 // shall never happen. I.e. it should not happen that this destructor is directly called
479 // in the stack-unwinding process of another flying exception (which would lead to immediate
480 // termination). It can be called indirectly in the destructor of another object, but that's
481 // fine and safe provided that the caller catches exceptions thrown from here.
482 // Therefore, we can allow callMethod() to throw even if we are in the destructor.
483 // Bottomline is, to be on the safe side, the caller must take care of catching and reacting
484 // to the exception thrown from here if the caller is a destructor itself.
485 proxy_.callMethod(method_, timeout_);
486 }
487
488 inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
489 {
490 method_ = proxy_.createMethodCall(interfaceName, methodName_);
491
492 return *this;
493 }
494
495 inline MethodInvoker& MethodInvoker::withTimeout(uint64_t usec)
496 {
497 timeout_ = usec;
498
499 return *this;
500 }
501
502 template <typename _Rep, typename _Period>
503 inline MethodInvoker& MethodInvoker::withTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
504 {
505 auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
506 return withTimeout(microsecs.count());
507 }
508
509 template <typename... _Args>
510 inline MethodInvoker& MethodInvoker::withArguments(_Args&&... args)
511 {
512 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
513
514 detail::serialize_pack(method_, std::forward<_Args>(args)...);
515
516 return *this;
517 }
518
519 template <typename... _Args>
520 inline void MethodInvoker::storeResultsTo(_Args&... args)
521 {
522 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
523
524 auto reply = proxy_.callMethod(method_, timeout_);
525 methodCalled_ = true;
526
527 detail::deserialize_pack(reply, args...);
528 }
529
530 inline void MethodInvoker::dontExpectReply()
531 {
532 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
533
534 method_.dontExpectReply();
535 }
536
537 /*** ------------------ ***/
538 /*** AsyncMethodInvoker ***/
539 /*** ------------------ ***/
540
541 inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const std::string& methodName)
542 : proxy_(proxy)
543 , methodName_(methodName)
544 {
545 }
546
547 inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const std::string& interfaceName)
548 {
549 method_ = proxy_.createMethodCall(interfaceName, methodName_);
550
551 return *this;
552 }
553
554 inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(uint64_t usec)
555 {
556 timeout_ = usec;
557
558 return *this;
559 }
560
561 template <typename _Rep, typename _Period>
562 inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
563 {
564 auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
565 return withTimeout(microsecs.count());
566 }
567
568 template <typename... _Args>
569 inline AsyncMethodInvoker& AsyncMethodInvoker::withArguments(_Args&&... args)
570 {
571 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
572
573 detail::serialize_pack(method_, std::forward<_Args>(args)...);
574
575 return *this;
576 }
577
578 template <typename _Function>
579 PendingAsyncCall AsyncMethodInvoker::uponReplyInvoke(_Function&& callback)
580 {
581 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
582
583 auto asyncReplyHandler = [callback = std::forward<_Function>(callback)](MethodReply& reply, const Error* error)
584 {
585 // Create a tuple of callback input arguments' types, which will be used
586 // as a storage for the argument values deserialized from the message.
587 tuple_of_function_input_arg_types_t<_Function> args;
588
589 // Deserialize input arguments from the message into the tuple (if no error occurred).
590 if (error == nullptr)
591 {
592 try
593 {
594 reply >> args;
595 }
596 catch (const Error& e)
597 {
598 // Catch message unpack exceptions and pass them to the callback
599 // in the expected manner to avoid propagating them up the call
600 // stack to the event loop.
601 sdbus::apply(callback, &e, args);
602 return;
603 }
604 }
605
606 // Invoke callback with input arguments from the tuple.
607 sdbus::apply(callback, error, args);
608 };
609
610 return proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
611 }
612
613 template <typename... _Args>
614 std::future<future_return_t<_Args...>> AsyncMethodInvoker::getResultAsFuture()
615 {
616 auto promise = std::make_shared<std::promise<future_return_t<_Args...>>>();
617 auto future = promise->get_future();
618
619 uponReplyInvoke([promise = std::move(promise)](const Error* error, _Args... args)
620 {
621 if (error == nullptr)
622 if constexpr (!std::is_void_v<future_return_t<_Args...>>)
623 promise->set_value({std::move(args)...});
624 else
625 promise->set_value();
626 else
627 promise->set_exception(std::make_exception_ptr(*error));
628 });
629
630 // Will be std::future<void> for no D-Bus method return value
631 // or std::future<T> for single D-Bus method return value
632 // or std::future<std::tuple<...>> for multiple method return values
633 return future;
634 }
635
636 /*** ---------------- ***/
637 /*** SignalSubscriber ***/
638 /*** ---------------- ***/
639
640 inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const std::string& signalName)
641 : proxy_(proxy)
642 , signalName_(signalName)
643 {
644 }
645
646 inline SignalSubscriber& SignalSubscriber::onInterface(std::string interfaceName)
647 {
648 interfaceName_ = std::move(interfaceName);
649
650 return *this;
651 }
652
653 template <typename _Function>
654 inline void SignalSubscriber::call(_Function&& callback)
655 {
656 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
657
658 proxy_.registerSignalHandler( interfaceName_
659 , signalName_
660 , [callback = std::forward<_Function>(callback)](Signal& signal)
661 {
662 // Create a tuple of callback input arguments' types, which will be used
663 // as a storage for the argument values deserialized from the signal message.
664 tuple_of_function_input_arg_types_t<_Function> signalArgs;
665
666 // The signal handler can take pure signal parameters only, or an additional `const Error*` as its first
667 // parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch),
668 // the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure
669 // will be communicated as a non-zero Error pointer to the client's signal handler.
670 if constexpr (has_error_param_v<_Function>)
671 {
672 // Deserialize input arguments from the signal message into the tuple
673 try
674 {
675 signal >> signalArgs;
676 }
677 catch (const sdbus::Error& e)
678 {
679 // Invoke callback with error argument and input arguments from the tuple.
680 sdbus::apply(callback, &e, signalArgs);
681 }
682
683 // Invoke callback with no error and input arguments from the tuple.
684 sdbus::apply(callback, nullptr, signalArgs);
685 }
686 else
687 {
688 // Deserialize input arguments from the signal message into the tuple
689 signal >> signalArgs;
690
691 // Invoke callback with input arguments from the tuple.
692 sdbus::apply(callback, signalArgs);
693 }
694 });
695 }
696
697 /*** ------------------ ***/
698 /*** SignalUnsubscriber ***/
699 /*** ------------------ ***/
700
701 inline SignalUnsubscriber::SignalUnsubscriber(IProxy& proxy, const std::string& signalName)
702 : proxy_(proxy)
703 , signalName_(signalName)
704 {
705 }
706
707 inline void SignalUnsubscriber::onInterface(const std::string& interfaceName)
708 {
709 proxy_.unregisterSignalHandler(interfaceName, signalName_);
710 }
711
712 /*** -------------- ***/
713 /*** PropertyGetter ***/
714 /*** -------------- ***/
715
716 inline PropertyGetter::PropertyGetter(IProxy& proxy, const std::string& propertyName)
717 : proxy_(proxy)
718 , propertyName_(propertyName)
719 {
720 }
721
722 inline Variant PropertyGetter::onInterface(const std::string& interfaceName)
723 {
724 Variant var;
725 proxy_.callMethod("Get")
726 .onInterface("org.freedesktop.DBus.Properties")
727 .withArguments(interfaceName, propertyName_)
728 .storeResultsTo(var);
729 return var;
730 }
731
732 /*** ------------------- ***/
733 /*** AsyncPropertyGetter ***/
734 /*** ------------------- ***/
735
736 inline AsyncPropertyGetter::AsyncPropertyGetter(IProxy& proxy, const std::string& propertyName)
737 : proxy_(proxy)
738 , propertyName_(propertyName)
739 {
740 }
741
742 inline AsyncPropertyGetter& AsyncPropertyGetter::onInterface(const std::string& interfaceName)
743 {
744 interfaceName_ = &interfaceName;
745
746 return *this;
747 }
748
749 template <typename _Function>
750 PendingAsyncCall AsyncPropertyGetter::uponReplyInvoke(_Function&& callback)
751 {
752 static_assert(std::is_invocable_r_v<void, _Function, const Error*, Variant>, "Property get callback function must accept Error* and property value as Variant");
753
754 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
755
756 return proxy_.callMethodAsync("Get")
757 .onInterface("org.freedesktop.DBus.Properties")
758 .withArguments(*interfaceName_, propertyName_)
759 .uponReplyInvoke(std::forward<_Function>(callback));
760 }
761
762 inline std::future<Variant> AsyncPropertyGetter::getResultAsFuture()
763 {
764 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
765
766 return proxy_.callMethodAsync("Get")
767 .onInterface("org.freedesktop.DBus.Properties")
768 .withArguments(*interfaceName_, propertyName_)
769 .getResultAsFuture<Variant>();
770 }
771
772 /*** -------------- ***/
773 /*** PropertySetter ***/
774 /*** -------------- ***/
775
776 inline PropertySetter::PropertySetter(IProxy& proxy, const std::string& propertyName)
777 : proxy_(proxy)
778 , propertyName_(propertyName)
779 {
780 }
781
782 inline PropertySetter& PropertySetter::onInterface(const std::string& interfaceName)
783 {
784 interfaceName_ = &interfaceName;
785
786 return *this;
787 }
788
789 template <typename _Value>
790 inline void PropertySetter::toValue(const _Value& value)
791 {
792 PropertySetter::toValue(Variant{value});
793 }
794
795 template <typename _Value>
796 inline void PropertySetter::toValue(const _Value& value, dont_expect_reply_t)
797 {
798 PropertySetter::toValue(Variant{value}, dont_expect_reply);
799 }
800
801 inline void PropertySetter::toValue(const Variant& value)
802 {
803 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
804
805 proxy_.callMethod("Set")
806 .onInterface("org.freedesktop.DBus.Properties")
807 .withArguments(*interfaceName_, propertyName_, value);
808 }
809
810 inline void PropertySetter::toValue(const Variant& value, dont_expect_reply_t)
811 {
812 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
813
814 proxy_.callMethod("Set")
815 .onInterface("org.freedesktop.DBus.Properties")
816 .withArguments(*interfaceName_, propertyName_, value)
817 .dontExpectReply();
818 }
819
820 /*** ------------------- ***/
821 /*** AsyncPropertySetter ***/
822 /*** ------------------- ***/
823
824 inline AsyncPropertySetter::AsyncPropertySetter(IProxy& proxy, const std::string& propertyName)
825 : proxy_(proxy)
826 , propertyName_(propertyName)
827 {
828 }
829
830 inline AsyncPropertySetter& AsyncPropertySetter::onInterface(const std::string& interfaceName)
831 {
832 interfaceName_ = &interfaceName;
833
834 return *this;
835 }
836
837 template <typename _Value>
838 inline AsyncPropertySetter& AsyncPropertySetter::toValue(_Value&& value)
839 {
840 return AsyncPropertySetter::toValue(Variant{std::forward<_Value>(value)});
841 }
842
843 inline AsyncPropertySetter& AsyncPropertySetter::toValue(Variant value)
844 {
845 value_ = std::move(value);
846
847 return *this;
848 }
849
850 template <typename _Function>
851 PendingAsyncCall AsyncPropertySetter::uponReplyInvoke(_Function&& callback)
852 {
853 static_assert(std::is_invocable_r_v<void, _Function, const Error*>, "Property set callback function must accept Error* only");
854
855 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
856
857 return proxy_.callMethodAsync("Set")
858 .onInterface("org.freedesktop.DBus.Properties")
859 .withArguments(*interfaceName_, propertyName_, std::move(value_))
860 .uponReplyInvoke(std::forward<_Function>(callback));
861 }
862
863 inline std::future<void> AsyncPropertySetter::getResultAsFuture()
864 {
865 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
866
867 return proxy_.callMethodAsync("Set")
868 .onInterface("org.freedesktop.DBus.Properties")
869 .withArguments(*interfaceName_, propertyName_, std::move(value_))
870 .getResultAsFuture<>();
871 }
872
873 /*** ------------------- ***/
874 /*** AllPropertiesGetter ***/
875 /*** ------------------- ***/
876
877 inline AllPropertiesGetter::AllPropertiesGetter(IProxy& proxy)
878 : proxy_(proxy)
879 {
880 }
881
882 inline std::map<std::string, Variant> AllPropertiesGetter::onInterface(const std::string& interfaceName)
883 {
884 std::map<std::string, Variant> props;
885 proxy_.callMethod("GetAll")
886 .onInterface("org.freedesktop.DBus.Properties")
887 .withArguments(interfaceName)
888 .storeResultsTo(props);
889 return props;
890 }
891
892 /*** ------------------------ ***/
893 /*** AsyncAllPropertiesGetter ***/
894 /*** ------------------------ ***/
895
896 inline AsyncAllPropertiesGetter::AsyncAllPropertiesGetter(IProxy& proxy)
897 : proxy_(proxy)
898 {
899 }
900
901 inline AsyncAllPropertiesGetter& AsyncAllPropertiesGetter::onInterface(const std::string& interfaceName)
902 {
903 interfaceName_ = &interfaceName;
904
905 return *this;
906 }
907
908 template <typename _Function>
909 PendingAsyncCall AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback)
910 {
911 static_assert( std::is_invocable_r_v<void, _Function, const Error*, std::map<std::string, Variant>>
912 , "All properties get callback function must accept Error* and a map of property names to their values" );
913
914 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
915
916 return proxy_.callMethodAsync("GetAll")
917 .onInterface("org.freedesktop.DBus.Properties")
918 .withArguments(*interfaceName_)
919 .uponReplyInvoke(std::forward<_Function>(callback));
920 }
921
922 inline std::future<std::map<std::string, Variant>> AsyncAllPropertiesGetter::getResultAsFuture()
923 {
924 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
925
926 return proxy_.callMethodAsync("GetAll")
927 .onInterface("org.freedesktop.DBus.Properties")
928 .withArguments(*interfaceName_)
929 .getResultAsFuture<std::map<std::string, Variant>>();
930 }
931
932}
933
934#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */
Error.h
IObject.h
IProxy.h
Message.h
MethodResult.h
TypeTraits.h
Types.h
sdbus::Error
Definition Error.h:44