sdbus-c++ 1.5.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>
34#include <sdbus-c++/Types.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 // Pass message deserialization exceptions to the client via callback error parameter,
599 // instead of propagating them up the message loop call stack.
600 sdbus::apply(callback, &e, args);
601 return;
602 }
603 }
604
605 // Invoke callback with input arguments from the tuple.
606 sdbus::apply(callback, error, args);
607 };
608
609 return proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
610 }
611
612 template <typename... _Args>
613 std::future<future_return_t<_Args...>> AsyncMethodInvoker::getResultAsFuture()
614 {
615 auto promise = std::make_shared<std::promise<future_return_t<_Args...>>>();
616 auto future = promise->get_future();
617
618 uponReplyInvoke([promise = std::move(promise)](const Error* error, _Args... args)
619 {
620 if (error == nullptr)
621 if constexpr (!std::is_void_v<future_return_t<_Args...>>)
622 promise->set_value({std::move(args)...});
623 else
624 promise->set_value();
625 else
626 promise->set_exception(std::make_exception_ptr(*error));
627 });
628
629 // Will be std::future<void> for no D-Bus method return value
630 // or std::future<T> for single D-Bus method return value
631 // or std::future<std::tuple<...>> for multiple method return values
632 return future;
633 }
634
635 /*** ---------------- ***/
636 /*** SignalSubscriber ***/
637 /*** ---------------- ***/
638
639 inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const std::string& signalName)
640 : proxy_(proxy)
641 , signalName_(signalName)
642 {
643 }
644
645 inline SignalSubscriber& SignalSubscriber::onInterface(std::string interfaceName)
646 {
647 interfaceName_ = std::move(interfaceName);
648
649 return *this;
650 }
651
652 template <typename _Function>
653 inline void SignalSubscriber::call(_Function&& callback)
654 {
655 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
656
657 proxy_.registerSignalHandler( interfaceName_
658 , signalName_
659 , [callback = std::forward<_Function>(callback)](Signal& signal)
660 {
661 // Create a tuple of callback input arguments' types, which will be used
662 // as a storage for the argument values deserialized from the signal message.
663 tuple_of_function_input_arg_types_t<_Function> signalArgs;
664
665 // The signal handler can take pure signal parameters only, or an additional `const Error*` as its first
666 // parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch),
667 // the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure
668 // will be communicated as a non-zero Error pointer to the client's signal handler.
669 if constexpr (has_error_param_v<_Function>)
670 {
671 // Deserialize input arguments from the signal message into the tuple
672 try
673 {
674 signal >> signalArgs;
675 }
676 catch (const sdbus::Error& e)
677 {
678 // Pass message deserialization exceptions to the client via callback error parameter,
679 // instead of propagating them up the message loop call stack.
680 sdbus::apply(callback, &e, signalArgs);
681 return;
682 }
683
684 // Invoke callback with no error and input arguments from the tuple.
685 sdbus::apply(callback, nullptr, signalArgs);
686 }
687 else
688 {
689 // Deserialize input arguments from the signal message into the tuple
690 signal >> signalArgs;
691
692 // Invoke callback with input arguments from the tuple.
693 sdbus::apply(callback, signalArgs);
694 }
695 });
696 }
697
698 /*** ------------------ ***/
699 /*** SignalUnsubscriber ***/
700 /*** ------------------ ***/
701
702 inline SignalUnsubscriber::SignalUnsubscriber(IProxy& proxy, const std::string& signalName)
703 : proxy_(proxy)
704 , signalName_(signalName)
705 {
706 }
707
708 inline void SignalUnsubscriber::onInterface(const std::string& interfaceName)
709 {
710 proxy_.unregisterSignalHandler(interfaceName, signalName_);
711 }
712
713 /*** -------------- ***/
714 /*** PropertyGetter ***/
715 /*** -------------- ***/
716
717 inline PropertyGetter::PropertyGetter(IProxy& proxy, const std::string& propertyName)
718 : proxy_(proxy)
719 , propertyName_(propertyName)
720 {
721 }
722
723 inline Variant PropertyGetter::onInterface(const std::string& interfaceName)
724 {
725 Variant var;
726 proxy_.callMethod("Get")
727 .onInterface("org.freedesktop.DBus.Properties")
728 .withArguments(interfaceName, propertyName_)
729 .storeResultsTo(var);
730 return var;
731 }
732
733 /*** ------------------- ***/
734 /*** AsyncPropertyGetter ***/
735 /*** ------------------- ***/
736
737 inline AsyncPropertyGetter::AsyncPropertyGetter(IProxy& proxy, const std::string& propertyName)
738 : proxy_(proxy)
739 , propertyName_(propertyName)
740 {
741 }
742
743 inline AsyncPropertyGetter& AsyncPropertyGetter::onInterface(const std::string& interfaceName)
744 {
745 interfaceName_ = &interfaceName;
746
747 return *this;
748 }
749
750 template <typename _Function>
751 PendingAsyncCall AsyncPropertyGetter::uponReplyInvoke(_Function&& callback)
752 {
753 static_assert(std::is_invocable_r_v<void, _Function, const Error*, Variant>, "Property get callback function must accept Error* and property value as Variant");
754
755 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
756
757 return proxy_.callMethodAsync("Get")
758 .onInterface("org.freedesktop.DBus.Properties")
759 .withArguments(*interfaceName_, propertyName_)
760 .uponReplyInvoke(std::forward<_Function>(callback));
761 }
762
763 inline std::future<Variant> AsyncPropertyGetter::getResultAsFuture()
764 {
765 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
766
767 return proxy_.callMethodAsync("Get")
768 .onInterface("org.freedesktop.DBus.Properties")
769 .withArguments(*interfaceName_, propertyName_)
770 .getResultAsFuture<Variant>();
771 }
772
773 /*** -------------- ***/
774 /*** PropertySetter ***/
775 /*** -------------- ***/
776
777 inline PropertySetter::PropertySetter(IProxy& proxy, const std::string& propertyName)
778 : proxy_(proxy)
779 , propertyName_(propertyName)
780 {
781 }
782
783 inline PropertySetter& PropertySetter::onInterface(const std::string& interfaceName)
784 {
785 interfaceName_ = &interfaceName;
786
787 return *this;
788 }
789
790 template <typename _Value>
791 inline void PropertySetter::toValue(const _Value& value)
792 {
793 PropertySetter::toValue(Variant{value});
794 }
795
796 template <typename _Value>
797 inline void PropertySetter::toValue(const _Value& value, dont_expect_reply_t)
798 {
799 PropertySetter::toValue(Variant{value}, dont_expect_reply);
800 }
801
802 inline void PropertySetter::toValue(const Variant& value)
803 {
804 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
805
806 proxy_.callMethod("Set")
807 .onInterface("org.freedesktop.DBus.Properties")
808 .withArguments(*interfaceName_, propertyName_, value);
809 }
810
811 inline void PropertySetter::toValue(const Variant& value, dont_expect_reply_t)
812 {
813 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
814
815 proxy_.callMethod("Set")
816 .onInterface("org.freedesktop.DBus.Properties")
817 .withArguments(*interfaceName_, propertyName_, value)
818 .dontExpectReply();
819 }
820
821 /*** ------------------- ***/
822 /*** AsyncPropertySetter ***/
823 /*** ------------------- ***/
824
825 inline AsyncPropertySetter::AsyncPropertySetter(IProxy& proxy, const std::string& propertyName)
826 : proxy_(proxy)
827 , propertyName_(propertyName)
828 {
829 }
830
831 inline AsyncPropertySetter& AsyncPropertySetter::onInterface(const std::string& interfaceName)
832 {
833 interfaceName_ = &interfaceName;
834
835 return *this;
836 }
837
838 template <typename _Value>
839 inline AsyncPropertySetter& AsyncPropertySetter::toValue(_Value&& value)
840 {
841 return AsyncPropertySetter::toValue(Variant{std::forward<_Value>(value)});
842 }
843
844 inline AsyncPropertySetter& AsyncPropertySetter::toValue(Variant value)
845 {
846 value_ = std::move(value);
847
848 return *this;
849 }
850
851 template <typename _Function>
852 PendingAsyncCall AsyncPropertySetter::uponReplyInvoke(_Function&& callback)
853 {
854 static_assert(std::is_invocable_r_v<void, _Function, const Error*>, "Property set callback function must accept Error* only");
855
856 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
857
858 return proxy_.callMethodAsync("Set")
859 .onInterface("org.freedesktop.DBus.Properties")
860 .withArguments(*interfaceName_, propertyName_, std::move(value_))
861 .uponReplyInvoke(std::forward<_Function>(callback));
862 }
863
864 inline std::future<void> AsyncPropertySetter::getResultAsFuture()
865 {
866 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
867
868 return proxy_.callMethodAsync("Set")
869 .onInterface("org.freedesktop.DBus.Properties")
870 .withArguments(*interfaceName_, propertyName_, std::move(value_))
871 .getResultAsFuture<>();
872 }
873
874 /*** ------------------- ***/
875 /*** AllPropertiesGetter ***/
876 /*** ------------------- ***/
877
878 inline AllPropertiesGetter::AllPropertiesGetter(IProxy& proxy)
879 : proxy_(proxy)
880 {
881 }
882
883 inline std::map<std::string, Variant> AllPropertiesGetter::onInterface(const std::string& interfaceName)
884 {
885 std::map<std::string, Variant> props;
886 proxy_.callMethod("GetAll")
887 .onInterface("org.freedesktop.DBus.Properties")
888 .withArguments(interfaceName)
889 .storeResultsTo(props);
890 return props;
891 }
892
893 /*** ------------------------ ***/
894 /*** AsyncAllPropertiesGetter ***/
895 /*** ------------------------ ***/
896
897 inline AsyncAllPropertiesGetter::AsyncAllPropertiesGetter(IProxy& proxy)
898 : proxy_(proxy)
899 {
900 }
901
902 inline AsyncAllPropertiesGetter& AsyncAllPropertiesGetter::onInterface(const std::string& interfaceName)
903 {
904 interfaceName_ = &interfaceName;
905
906 return *this;
907 }
908
909 template <typename _Function>
910 PendingAsyncCall AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback)
911 {
912 static_assert( std::is_invocable_r_v<void, _Function, const Error*, std::map<std::string, Variant>>
913 , "All properties get callback function must accept Error* and a map of property names to their values" );
914
915 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
916
917 return proxy_.callMethodAsync("GetAll")
918 .onInterface("org.freedesktop.DBus.Properties")
919 .withArguments(*interfaceName_)
920 .uponReplyInvoke(std::forward<_Function>(callback));
921 }
922
923 inline std::future<std::map<std::string, Variant>> AsyncAllPropertiesGetter::getResultAsFuture()
924 {
925 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
926
927 return proxy_.callMethodAsync("GetAll")
928 .onInterface("org.freedesktop.DBus.Properties")
929 .withArguments(*interfaceName_)
930 .getResultAsFuture<std::map<std::string, Variant>>();
931 }
932
933}
934
935#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */
Definition Error.h:44