sdbus-c++ 2.3.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
26
27#ifndef SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
28#define SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
29
30#include <sdbus-c++/Error.h>
31#include <sdbus-c++/IObject.h> // NOLINT(misc-header-include-cycle)
32#include <sdbus-c++/IProxy.h> // NOLINT(misc-header-include-cycle)
33#include <sdbus-c++/Message.h>
36#include <sdbus-c++/Types.h>
37
38#include <cassert>
39#include <exception>
40#include <string>
41#include <tuple>
42#include <type_traits>
43
44namespace sdbus {
45
46 /*** ------------- ***/
47 /*** VTableAdder ***/
48 /*** ------------- ***/
49
50 inline VTableAdder::VTableAdder(IObject& object, std::vector<VTableItem> vtable)
51 : object_(object)
52 , vtable_(std::move(vtable))
53 {
54 }
55
56 inline void VTableAdder::forInterface(InterfaceName interfaceName)
57 {
58 object_.addVTable(std::move(interfaceName), std::move(vtable_));
59 }
60
61 inline void VTableAdder::forInterface(std::string interfaceName)
62 {
63 forInterface(InterfaceName{std::move(interfaceName)});
64 }
65
66 [[nodiscard]] inline Slot VTableAdder::forInterface(InterfaceName interfaceName, return_slot_t)
67 {
68 return object_.addVTable(std::move(interfaceName), std::move(vtable_), return_slot);
69 }
70
71 [[nodiscard]] inline Slot VTableAdder::forInterface(std::string interfaceName, return_slot_t)
72 {
73 return forInterface(InterfaceName{std::move(interfaceName)}, return_slot);
74 }
75
76 /*** ------------- ***/
77 /*** SignalEmitter ***/
78 /*** ------------- ***/
79
80 inline SignalEmitter::SignalEmitter(IObject& object, const SignalName& signalName)
81 : SignalEmitter(object, signalName.c_str())
82 {
83 }
84
85 inline SignalEmitter::SignalEmitter(IObject& object, const char* signalName)
86 : object_(object)
87 , signalName_(signalName)
88 , exceptions_(std::uncaught_exceptions())
89 {
90 }
91
92 inline SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must
93 { // explicitly be allowed to throw
94 // Don't emit the signal if SignalEmitter threw an exception in one of its methods
95 if (std::uncaught_exceptions() != exceptions_)
96 return;
97
98 // emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
99 // temporary object, i.e. not as a stack-allocated object, the double-exception situation
100 // shall never happen. I.e. it should not happen that this destructor is directly called
101 // in the stack-unwinding process of another flying exception (which would lead to immediate
102 // termination). It can be called indirectly in the destructor of another object, but that's
103 // fine and safe provided that the caller catches exceptions thrown from here.
104 // Therefore, we can allow emitSignal() to throw even if we are in the destructor.
105 // Bottomline is, to be on the safe side, the caller must take care of catching and reacting
106 // to the exception thrown from here if the caller is a destructor itself.
107 object_.emitSignal(signal_);
108 }
109
110 inline SignalEmitter& SignalEmitter::onInterface(const InterfaceName& interfaceName)
111 {
112 return onInterface(interfaceName.c_str());
113 }
114
115 inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
116 {
117 return onInterface(interfaceName.c_str());
118 }
119
120 inline SignalEmitter& SignalEmitter::onInterface(const char* interfaceName)
121 {
122 signal_ = object_.createSignal(interfaceName, signalName_);
123
124 return *this;
125 }
126
127 template <typename... Args>
128 inline void SignalEmitter::withArguments(Args&&... args)
129 {
130 assert(signal_.isValid()); // onInterface() must be placed/called prior to withArguments()
131
132 detail::serialize_pack(signal_, std::forward<Args>(args)...);
133 }
134
135 /*** ------------- ***/
136 /*** MethodInvoker ***/
137 /*** ------------- ***/
138
139 inline MethodInvoker::MethodInvoker(IProxy& proxy, const MethodName& methodName)
140 : MethodInvoker(proxy, methodName.c_str())
141 {
142 }
143
144 inline MethodInvoker::MethodInvoker(IProxy& proxy, const char* methodName)
145 : proxy_(proxy)
146 , methodName_(methodName)
147 , exceptions_(std::uncaught_exceptions())
148 {
149 }
150
151 inline MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
152 { // explicitly be allowed to throw
153 // Don't call the method if it has been called already or if MethodInvoker
154 // threw an exception in one of its methods
155 if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
156 return;
157
158 // callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
159 // temporary object, i.e. not as a stack-allocated object, the double-exception situation
160 // shall never happen. I.e. it should not happen that this destructor is directly called
161 // in the stack-unwinding process of another flying exception (which would lead to immediate
162 // termination). It can be called indirectly in the destructor of another object, but that's
163 // fine and safe provided that the caller catches exceptions thrown from here.
164 // Therefore, we can allow callMethod() to throw even if we are in the destructor.
165 // Bottomline is, to be on the safe side, the caller must take care of catching and reacting
166 // to the exception thrown from here if the caller is a destructor itself.
167 proxy_.callMethod(method_, timeout_);
168 }
169
170 inline MethodInvoker& MethodInvoker::onInterface(const InterfaceName& interfaceName)
171 {
172 return onInterface(interfaceName.c_str());
173 }
174
175 inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
176 {
177 return onInterface(interfaceName.c_str());
178 }
179
180 inline MethodInvoker& MethodInvoker::onInterface(const char* interfaceName)
181 {
182 method_ = proxy_.createMethodCall(interfaceName, methodName_);
183
184 return *this;
185 }
186
187 inline MethodInvoker& MethodInvoker::withTimeout(uint64_t usec)
188 {
189 timeout_ = usec;
190
191 return *this;
192 }
193
194 template <typename Rep, typename Period>
195 inline MethodInvoker& MethodInvoker::withTimeout(const std::chrono::duration<Rep, Period>& timeout)
196 {
197 auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
198 return withTimeout(microsecs.count());
199 }
200
201 template <typename... Args>
202 inline MethodInvoker& MethodInvoker::withArguments(Args&&... args)
203 {
204 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
205
206 detail::serialize_pack(method_, std::forward<Args>(args)...);
207
208 return *this;
209 }
210
211 template <typename... Args>
212 inline void MethodInvoker::storeResultsTo(Args&... args)
213 {
214 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
215
216 auto reply = proxy_.callMethod(method_, timeout_);
217 methodCalled_ = true;
218
219 detail::deserialize_pack(reply, args...);
220 }
221
222 inline void MethodInvoker::dontExpectReply()
223 {
224 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
225
226 method_.dontExpectReply();
227 }
228
229 /*** ------------------ ***/
230 /*** AsyncMethodInvoker ***/
231 /*** ------------------ ***/
232
233 inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const MethodName& methodName)
234 : AsyncMethodInvoker(proxy, methodName.c_str())
235 {
236 }
237
238 inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const char* methodName)
239 : proxy_(proxy)
240 , methodName_(methodName)
241 {
242 }
243
244 inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const InterfaceName& interfaceName)
245 {
246 return onInterface(interfaceName.c_str());
247 }
248
249 inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const std::string& interfaceName)
250 {
251 return onInterface(interfaceName.c_str());
252 }
253
254 inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const char* interfaceName)
255 {
256 method_ = proxy_.createMethodCall(interfaceName, methodName_);
257
258 return *this;
259 }
260
261 inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(uint64_t usec)
262 {
263 timeout_ = usec;
264
265 return *this;
266 }
267
268 template <typename Rep, typename Period>
269 inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(const std::chrono::duration<Rep, Period>& timeout)
270 {
271 auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
272 return withTimeout(microsecs.count());
273 }
274
275 template <typename... Args>
276 inline AsyncMethodInvoker& AsyncMethodInvoker::withArguments(Args&&... args)
277 {
278 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
279
280 detail::serialize_pack(method_, std::forward<Args>(args)...);
281
282 return *this;
283 }
284
285 template <typename Function>
286 PendingAsyncCall AsyncMethodInvoker::uponReplyInvoke(Function&& callback)
287 {
288 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
289
290 return proxy_.callMethodAsync(method_, makeAsyncReplyHandler(std::forward<Function>(callback)), timeout_);
291 }
292
293 template <typename Function>
294 [[nodiscard]] Slot AsyncMethodInvoker::uponReplyInvoke(Function&& callback, return_slot_t)
295 {
296 assert(method_.isValid()); // onInterface() must be placed/called prior to this function
297
298 return proxy_.callMethodAsync( method_
299 , makeAsyncReplyHandler(std::forward<Function>(callback))
300 , timeout_
301 , return_slot );
302 }
303
304 template <typename Function>
305 inline async_reply_handler AsyncMethodInvoker::makeAsyncReplyHandler(Function&& callback)
306 {
307 return [callback = std::forward<Function>(callback)](MethodReply reply, std::optional<Error> error)
308 {
309 // Create a tuple of callback input arguments' types, which will be used
310 // as a storage for the argument values deserialized from the message.
311 tuple_of_function_input_arg_types_t<Function> args;
312
313 // Deserialize input arguments from the message into the tuple (if no error occurred).
314 if (!error)
315 {
316 try
317 {
318 reply >> args;
319 }
320 catch (const Error& e)
321 {
322 // Pass message deserialization exceptions to the client via callback error parameter,
323 // instead of propagating them up the message loop call stack.
324 sdbus::apply(callback, e, args);
325 return;
326 }
327 }
328
329 // Invoke callback with input arguments from the tuple.
330 sdbus::apply(callback, std::move(error), args);
331 };
332 }
333
334 template <typename... Args>
335 std::future<future_return_t<Args...>> AsyncMethodInvoker::getResultAsFuture()
336 {
337 auto promise = std::make_shared<std::promise<future_return_t<Args...>>>();
338 auto future = promise->get_future();
339
340 uponReplyInvoke([promise = std::move(promise)](std::optional<Error> error, Args... args)
341 {
342 if (!error)
343 if constexpr (!std::is_void_v<future_return_t<Args...>>)
344 promise->set_value({std::move(args)...});
345 else
346 promise->set_value();
347 else
348 promise->set_exception(std::make_exception_ptr(*std::move(error)));
349 });
350
351 // Will be std::future<void> for no D-Bus method return value
352 // or std::future<T> for single D-Bus method return value
353 // or std::future<std::tuple<...>> for multiple method return values
354 return future;
355 }
356
357 template <typename... Args>
358 Awaitable<awaitable_return_t<Args...>> AsyncMethodInvoker::getResultAsAwaitable()
359 {
360 // awaitable_return_t<Args...> will be void for no D-Bus method return value
361 // or T for single D-Bus method return value
362 // or std::tuple<...> for multiple method return values
363 auto data = std::make_shared<AwaitableData<awaitable_return_t<Args...>>>();
364
365 uponReplyInvoke([data](std::optional<Error> error, Args... args)
366 {
367 if (!error)
368 if constexpr (!std::is_void_v<awaitable_return_t<Args...>>)
369 data->result = {std::move(args)...};
370 else
371 data->result = std::monostate{};
372 else
373 data->result = std::make_exception_ptr(*std::move(error));
374
375 auto previous = data->status.exchange(AwaitableState::Completed, std::memory_order_acq_rel);
376 if (previous == AwaitableState::Waiting)
377 data->resumeCoroutine();
378 });
379
380 return Awaitable(data);
381 }
382
383 /*** ---------------- ***/
384 /*** SignalSubscriber ***/
385 /*** ---------------- ***/
386
387 inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const SignalName& signalName)
388 : SignalSubscriber(proxy, signalName.c_str())
389 {
390 }
391
392 inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const char* signalName)
393 : proxy_(proxy)
394 , signalName_(signalName)
395 {
396 }
397
398 inline SignalSubscriber& SignalSubscriber::onInterface(const InterfaceName& interfaceName)
399 {
400 return onInterface(interfaceName.c_str());
401 }
402
403 inline SignalSubscriber& SignalSubscriber::onInterface(const std::string& interfaceName)
404 {
405 return onInterface(interfaceName.c_str());
406 }
407
408 inline SignalSubscriber& SignalSubscriber::onInterface(const char* interfaceName)
409 {
410 interfaceName_ = std::move(interfaceName);
411
412 return *this;
413 }
414
415 template <typename Function>
416 inline void SignalSubscriber::call(Function&& callback)
417 {
418 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
419
420 proxy_.registerSignalHandler( interfaceName_
421 , signalName_
422 , makeSignalHandler(std::forward<Function>(callback)) );
423 }
424
425 template <typename Function>
426 [[nodiscard]] inline Slot SignalSubscriber::call(Function&& callback, return_slot_t)
427 {
428 assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
429
430 return proxy_.registerSignalHandler( interfaceName_
431 , signalName_
432 , makeSignalHandler(std::forward<Function>(callback))
433 , return_slot );
434 }
435
436 template <typename Function>
437 inline signal_handler SignalSubscriber::makeSignalHandler(Function&& callback)
438 {
439 return [callback = std::forward<Function>(callback)](Signal signal)
440 {
441 // Create a tuple of callback input arguments' types, which will be used
442 // as a storage for the argument values deserialized from the signal message.
443 tuple_of_function_input_arg_types_t<Function> signalArgs;
444
445 // The signal handler can take pure signal parameters only, or an additional `std::optional<Error>` as its first
446 // parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch),
447 // the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure
448 // will be communicated to the client's signal handler as a valid Error object inside the std::optional parameter.
449 if constexpr (has_error_param_v<Function>)
450 {
451 // Deserialize input arguments from the signal message into the tuple
452 try
453 {
454 signal >> signalArgs;
455 }
456 catch (const Error& e)
457 {
458 // Pass message deserialization exceptions to the client via callback error parameter,
459 // instead of propagating them up the message loop call stack.
460 sdbus::apply(callback, e, signalArgs);
461 return;
462 }
463
464 // Invoke callback with no error and input arguments from the tuple.
465 sdbus::apply(callback, {}, signalArgs);
466 }
467 else
468 {
469 // Deserialize input arguments from the signal message into the tuple
470 signal >> signalArgs;
471
472 // Invoke callback with input arguments from the tuple.
473 sdbus::apply(callback, signalArgs);
474 }
475 };
476 }
477
478 /*** -------------- ***/
479 /*** PropertyGetter ***/
480 /*** -------------- ***/
481
482 inline PropertyGetter::PropertyGetter(IProxy& proxy, std::string_view propertyName)
483 : proxy_(proxy)
484 , propertyName_(std::move(propertyName))
485 {
486 }
487
488 inline Variant PropertyGetter::onInterface(std::string_view interfaceName)
489 {
490 Variant var;
491 proxy_.callMethod("Get")
492 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
493 .withArguments(interfaceName, propertyName_)
494 .storeResultsTo(var);
495 return var;
496 }
497
498 /*** ------------------- ***/
499 /*** AsyncPropertyGetter ***/
500 /*** ------------------- ***/
501
502 inline AsyncPropertyGetter::AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName)
503 : proxy_(proxy)
504 , propertyName_(std::move(propertyName))
505 {
506 }
507
508 inline AsyncPropertyGetter& AsyncPropertyGetter::onInterface(std::string_view interfaceName)
509 {
510 interfaceName_ = std::move(interfaceName);
511
512 return *this;
513 }
514
515 template <typename Function>
516 PendingAsyncCall AsyncPropertyGetter::uponReplyInvoke(Function&& callback)
517 {
518 static_assert( std::is_invocable_r_v<void, Function, std::optional<Error>, Variant>
519 , "Property get callback function must accept std::optional<Error> and property value as Variant" );
520
521 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
522
523 return proxy_.callMethodAsync("Get")
524 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
525 .withArguments(interfaceName_, propertyName_)
526 .uponReplyInvoke(std::forward<Function>(callback));
527 }
528
529 template <typename Function>
530 [[nodiscard]] Slot AsyncPropertyGetter::uponReplyInvoke(Function&& callback, return_slot_t)
531 {
532 static_assert( std::is_invocable_r_v<void, Function, std::optional<Error>, Variant>
533 , "Property get callback function must accept std::optional<Error> and property value as Variant" );
534
535 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
536
537 return proxy_.callMethodAsync("Get")
538 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
539 .withArguments(interfaceName_, propertyName_)
540 .uponReplyInvoke(std::forward<Function>(callback), return_slot);
541 }
542
543 inline std::future<Variant> AsyncPropertyGetter::getResultAsFuture()
544 {
545 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
546
547 return proxy_.callMethodAsync("Get")
548 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
549 .withArguments(interfaceName_, propertyName_)
550 .getResultAsFuture<Variant>();
551 }
552
553 inline Awaitable<Variant> AsyncPropertyGetter::getResultAsAwaitable()
554 {
555 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
556
557 return proxy_.callMethodAsync("Get")
558 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
559 .withArguments(interfaceName_, propertyName_)
560 .getResultAsAwaitable<Variant>();
561 }
562
563 /*** -------------- ***/
564 /*** PropertySetter ***/
565 /*** -------------- ***/
566
567 inline PropertySetter::PropertySetter(IProxy& proxy, std::string_view propertyName)
568 : proxy_(proxy)
569 , propertyName_(std::move(propertyName))
570 {
571 }
572
573 inline PropertySetter& PropertySetter::onInterface(std::string_view interfaceName)
574 {
575 interfaceName_ = std::move(interfaceName);
576
577 return *this;
578 }
579
580 template <typename Value>
581 inline void PropertySetter::toValue(const Value& value)
582 {
583 PropertySetter::toValue(Variant{value});
584 }
585
586 template <typename Value>
587 inline void PropertySetter::toValue(const Value& value, dont_expect_reply_t)
588 {
589 PropertySetter::toValue(Variant{value}, dont_expect_reply);
590 }
591
592 inline void PropertySetter::toValue(const Variant& value)
593 {
594 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
595
596 proxy_.callMethod("Set")
597 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
598 .withArguments(interfaceName_, propertyName_, value);
599 }
600
601 inline void PropertySetter::toValue(const Variant& value, dont_expect_reply_t)
602 {
603 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
604
605 proxy_.callMethod("Set")
606 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
607 .withArguments(interfaceName_, propertyName_, value)
608 .dontExpectReply();
609 }
610
611 /*** ------------------- ***/
612 /*** AsyncPropertySetter ***/
613 /*** ------------------- ***/
614
615 inline AsyncPropertySetter::AsyncPropertySetter(IProxy& proxy, std::string_view propertyName)
616 : proxy_(proxy)
617 , propertyName_(propertyName)
618 {
619 }
620
621 inline AsyncPropertySetter& AsyncPropertySetter::onInterface(std::string_view interfaceName)
622 {
623 interfaceName_ = std::move(interfaceName);
624
625 return *this;
626 }
627
628 template <typename Value>
629 inline AsyncPropertySetter& AsyncPropertySetter::toValue(Value&& value)
630 {
631 return AsyncPropertySetter::toValue(Variant{std::forward<Value>(value)});
632 }
633
634 inline AsyncPropertySetter& AsyncPropertySetter::toValue(Variant value)
635 {
636 value_ = std::move(value);
637
638 return *this;
639 }
640
641 template <typename Function>
642 PendingAsyncCall AsyncPropertySetter::uponReplyInvoke(Function&& callback)
643 {
644 static_assert( std::is_invocable_r_v<void, Function, std::optional<Error>>
645 , "Property set callback function must accept std::optional<Error> only" );
646
647 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
648
649 return proxy_.callMethodAsync("Set")
650 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
651 .withArguments(interfaceName_, propertyName_, std::move(value_))
652 .uponReplyInvoke(std::forward<Function>(callback));
653 }
654
655 template <typename Function>
656 [[nodiscard]] Slot AsyncPropertySetter::uponReplyInvoke(Function&& callback, return_slot_t)
657 {
658 static_assert( std::is_invocable_r_v<void, Function, std::optional<Error>>
659 , "Property set callback function must accept std::optional<Error> only" );
660
661 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
662
663 return proxy_.callMethodAsync("Set")
664 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
665 .withArguments(interfaceName_, propertyName_, std::move(value_))
666 .uponReplyInvoke(std::forward<Function>(callback), return_slot);
667 }
668
669 inline std::future<void> AsyncPropertySetter::getResultAsFuture()
670 {
671 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
672
673 return proxy_.callMethodAsync("Set")
674 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
675 .withArguments(interfaceName_, propertyName_, std::move(value_))
676 .getResultAsFuture<>();
677 }
678
679 inline Awaitable<void> AsyncPropertySetter::getResultAsAwaitable()
680 {
681 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
682
683 return proxy_.callMethodAsync("Set")
684 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
685 .withArguments(interfaceName_, propertyName_, std::move(value_))
686 .getResultAsAwaitable<>();
687 }
688
689 /*** ------------------- ***/
690 /*** AllPropertiesGetter ***/
691 /*** ------------------- ***/
692
693 inline AllPropertiesGetter::AllPropertiesGetter(IProxy& proxy)
694 : proxy_(proxy)
695 {
696 }
697
698 inline std::map<PropertyName, Variant> AllPropertiesGetter::onInterface(std::string_view interfaceName)
699 {
700 std::map<PropertyName, Variant> props;
701 proxy_.callMethod("GetAll")
702 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
703 .withArguments(std::move(interfaceName))
704 .storeResultsTo(props);
705 return props;
706 }
707
708 /*** ------------------------ ***/
709 /*** AsyncAllPropertiesGetter ***/
710 /*** ------------------------ ***/
711
712 inline AsyncAllPropertiesGetter::AsyncAllPropertiesGetter(IProxy& proxy)
713 : proxy_(proxy)
714 {
715 }
716
717 inline AsyncAllPropertiesGetter& AsyncAllPropertiesGetter::onInterface(std::string_view interfaceName)
718 {
719 interfaceName_ = std::move(interfaceName);
720
721 return *this;
722 }
723
724 template <typename Function>
725 PendingAsyncCall AsyncAllPropertiesGetter::uponReplyInvoke(Function&& callback)
726 {
727 static_assert( std::is_invocable_r_v<void, Function, std::optional<Error>, std::map<PropertyName, Variant>>
728 , "All properties get callback function must accept std::optional<Error> and a map of property names to their values" );
729
730 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
731
732 return proxy_.callMethodAsync("GetAll")
733 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
734 .withArguments(interfaceName_)
735 .uponReplyInvoke(std::forward<Function>(callback));
736 }
737
738 template <typename Function>
739 [[nodiscard]] Slot AsyncAllPropertiesGetter::uponReplyInvoke(Function&& callback, return_slot_t)
740 {
741 static_assert( std::is_invocable_r_v<void, Function, std::optional<Error>, std::map<PropertyName, Variant>>
742 , "All properties get callback function must accept std::optional<Error> and a map of property names to their values" );
743
744 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
745
746 return proxy_.callMethodAsync("GetAll")
747 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
748 .withArguments(interfaceName_)
749 .uponReplyInvoke(std::forward<Function>(callback), return_slot);
750 }
751
752 inline std::future<std::map<PropertyName, Variant>> AsyncAllPropertiesGetter::getResultAsFuture()
753 {
754 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
755
756 return proxy_.callMethodAsync("GetAll")
757 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
758 .withArguments(interfaceName_)
759 .getResultAsFuture<std::map<PropertyName, Variant>>();
760 }
761
762 inline Awaitable<std::map<PropertyName, Variant>> AsyncAllPropertiesGetter::getResultAsAwaitable()
763 {
764 assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
765
766 return proxy_.callMethodAsync("GetAll")
767 .onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
768 .withArguments(interfaceName_)
769 .getResultAsAwaitable<std::map<PropertyName, Variant>>();
770 }
771
772} // namespace sdbus
773
774#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */
Definition IObject.h:63