forked from Kistler-Group/sdbus-cpp
feat: add support for async property get/set on client-side (#354)
* feat: add async property get/set convenience support classes * feat: add no-reply and async overloads to Properties_proxy * feat: add convenience functions for GetAll functionality * test: add tests for new functionality * add codegen IDL support and documentation
This commit is contained in:
committed by
GitHub
parent
0eda855745
commit
290078d6af
@ -1256,9 +1256,67 @@ Annotate the element with `org.freedesktop.DBus.Method.Timeout` in order to spec
|
|||||||
Using D-Bus properties
|
Using D-Bus properties
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
sdbus-c++ provides functionality for convenient working with D-Bus properties, on both convenience and generated code API level.
|
||||||
|
|
||||||
|
### Convenience API
|
||||||
|
|
||||||
|
Let's say a remote D-Bus object provides property `status` of type `u` under interface `org.sdbuscpp.Concatenator`.
|
||||||
|
|
||||||
|
#### Reading a property
|
||||||
|
|
||||||
|
We read property value easily through `IProxy::getProperty()` method:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
uint32_t status = proxy->getProperty("status").onInterface("org.sdbuscpp.Concatenator");
|
||||||
|
```
|
||||||
|
|
||||||
|
Getting a property in asynchronous manner is also possible, in both callback-based and future-based way, by calling `IProxy::getPropertyAsync()` method:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// Callback-based method:
|
||||||
|
auto callback = [](const sdbus::Error* err, sdbus::Variant value)
|
||||||
|
{
|
||||||
|
std::cout << "Got property value: " << value.get<uint32_t>() << std::endl;
|
||||||
|
};
|
||||||
|
uint32_t status = proxy->getPropertyAsync("status").onInterface("org.sdbuscpp.Concatenator").uponReplyInvoke(std::move(callback));
|
||||||
|
// Future-based method:
|
||||||
|
std::future<sdbus::Variant> statusFuture = object.getPropertyAsync("status").onInterface("org.sdbuscpp.Concatenator").getResultAsFuture();
|
||||||
|
...
|
||||||
|
std::cout << "Got property value: " << statusFuture.get().get<uint32_t>() << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
More information on `error` callback handler parameter, on behavior of `future` in erroneous situations, can be found in section [Asynchronous client-side methods](#asynchronous-client-side-methods).
|
||||||
|
|
||||||
|
#### Writing a property
|
||||||
|
|
||||||
|
Writing a property is equally simple, through `IProxy::setProperty()`:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
uint32_t status = ...;
|
||||||
|
proxy->setProperty("status").onInterface("org.sdbuscpp.Concatenator").toValue(status);
|
||||||
|
```
|
||||||
|
|
||||||
|
Setting a property in asynchronous manner is also possible, in both callback-based and future-based way, by calling `IProxy::setPropertyAsync()` method:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// Callback-based method:
|
||||||
|
auto callback = [](const sdbus::Error* err) { /*... Error handling in case err is non-null...*/ };
|
||||||
|
uint32_t status = proxy->setPropertyAsync("status").onInterface("org.sdbuscpp.Concatenator").toValue(status).uponReplyInvoke(std::move(callback));
|
||||||
|
// Future-based method:
|
||||||
|
std::future<void> statusFuture = object.setPropertyAsync("status").onInterface("org.sdbuscpp.Concatenator").getResultAsFuture();
|
||||||
|
```
|
||||||
|
|
||||||
|
More information on `error` callback handler parameter, on behavior of `future` in erroneous situations, can be found in section [Asynchronous client-side methods](#asynchronous-client-side-methods).
|
||||||
|
|
||||||
|
#### Getting all properties
|
||||||
|
|
||||||
|
In a very analogous way, with both synchronous and asynchronous options, it's possible to read all properties of an object under given interface at once. `IProxy::getAllProperties()` is what you're looking for.
|
||||||
|
|
||||||
|
### Generated bindings API
|
||||||
|
|
||||||
Defining and working with D-Bus properties using XML description is quite easy.
|
Defining and working with D-Bus properties using XML description is quite easy.
|
||||||
|
|
||||||
### Defining a property in the IDL
|
#### Defining a property in the IDL
|
||||||
|
|
||||||
A property element has no arg child element. It just has the attributes name, type and access, which are all mandatory. The access attribute allows the values ‘readwrite’, ‘read’, and ‘write’.
|
A property element has no arg child element. It just has the attributes name, type and access, which are all mandatory. The access attribute allows the values ‘readwrite’, ‘read’, and ‘write’.
|
||||||
|
|
||||||
@ -1276,7 +1334,9 @@ An example of a read-write property `status`:
|
|||||||
</node>
|
</node>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generated C++ bindings
|
The property may also have annotations. In addition to standard annotations defined in D-Bus specification, there are sdbus-c++-specific ones, discussed further below.
|
||||||
|
|
||||||
|
#### Generated C++ bindings
|
||||||
|
|
||||||
This is how generated adaptor and proxy classes would look like with the read-write `status` property. The adaptor:
|
This is how generated adaptor and proxy classes would look like with the read-write `status` property. The adaptor:
|
||||||
|
|
||||||
@ -1329,7 +1389,60 @@ public:
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
When implementing the adaptor, we simply need to provide the body for `status` getter and setter method by overriding them. Then in the proxy, we just call them.
|
When implementing the adaptor, we simply need to provide the body for the `status` getter and setter methods by overriding them. Then in the proxy, we just call them.
|
||||||
|
|
||||||
|
#### Client-side asynchronous properties
|
||||||
|
|
||||||
|
We can mark the property so that the generator generates either asynchronous variant of getter method, or asynchronous variant of setter method, or both. Annotations names are `org.freedesktop.DBus.Property.Get.Async`, or `org.freedesktop.DBus.Property.Set.Async`, respectively. Their values must be set to `client`.
|
||||||
|
|
||||||
|
In addition, we can choose through annotations `org.freedesktop.DBus.Property.Get.Async.ClientImpl`, or `org.freedesktop.DBus.Property.Set.Async.ClientImpl`, respectively, whether a callback-based or future-based variant will be generated. The concept is analogous to the one for asynchronous D-Bus methods described above in this document.
|
||||||
|
|
||||||
|
The callback-based method will generate a pure virtual function `On<PropertyName>Property[Get|Set]Reply()`, which must be overridden by the derived class.
|
||||||
|
|
||||||
|
For example, this description:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<node name="/org/sdbuscpp/propertyprovider">
|
||||||
|
<interface name="org.sdbuscpp.PropertyProvider">
|
||||||
|
<!--...-->
|
||||||
|
<property name="status" type="u" access="readwrite">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.Get.Async" value="client"/>
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.Get.Async.ClientImpl" value="callback"/>
|
||||||
|
</property>
|
||||||
|
<!--...-->
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
|
```
|
||||||
|
|
||||||
|
will get generated into this C++ code on client side:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class PropertyProvider_proxy
|
||||||
|
{
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
virtual void onStatusPropertyGetReply(const uint32_t& value, const sdbus::Error* error) = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// getting the property value
|
||||||
|
sdbus::PendingAsyncCall status()
|
||||||
|
{
|
||||||
|
return object_->getPropertyAsync("status").onInterface(INTERFACE_NAME).uponReplyInvoke([this](const sdbus::Error* error, const sdbus::Variant& value){ this->onActionPropertyGetReply(value.get<uint32_t>(), error); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// setting the property value
|
||||||
|
void status(const uint32_t& value)
|
||||||
|
{
|
||||||
|
object_->setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*...*/
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition to custom generated code for getting/setting properties, `org.freedesktop.DBus.Properties` standard D-Bus interface, implemented through pre-defined `sdbus::Properties_proxy` in `sdbus-c++/StandardInterfaces.h`, can also be used for reading/writing properties. See next section.
|
||||||
|
|
||||||
Standard D-Bus interfaces
|
Standard D-Bus interfaces
|
||||||
-------------------------
|
-------------------------
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <sdbus-c++/Message.h>
|
#include <sdbus-c++/Message.h>
|
||||||
#include <sdbus-c++/TypeTraits.h>
|
#include <sdbus-c++/TypeTraits.h>
|
||||||
|
#include <sdbus-c++/Types.h>
|
||||||
#include <sdbus-c++/Flags.h>
|
#include <sdbus-c++/Flags.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -41,7 +42,6 @@
|
|||||||
namespace sdbus {
|
namespace sdbus {
|
||||||
class IObject;
|
class IObject;
|
||||||
class IProxy;
|
class IProxy;
|
||||||
class Variant;
|
|
||||||
class Error;
|
class Error;
|
||||||
class PendingAsyncCall;
|
class PendingAsyncCall;
|
||||||
}
|
}
|
||||||
@ -225,7 +225,7 @@ namespace sdbus {
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SignalUnsubscriber(IProxy& proxy, const std::string& signalName);
|
SignalUnsubscriber(IProxy& proxy, const std::string& signalName);
|
||||||
void onInterface(std::string interfaceName);
|
void onInterface(const std::string& interfaceName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IProxy& proxy_;
|
IProxy& proxy_;
|
||||||
@ -236,25 +236,81 @@ namespace sdbus {
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PropertyGetter(IProxy& proxy, const std::string& propertyName);
|
PropertyGetter(IProxy& proxy, const std::string& propertyName);
|
||||||
sdbus::Variant onInterface(const std::string& interfaceName);
|
Variant onInterface(const std::string& interfaceName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IProxy& proxy_;
|
IProxy& proxy_;
|
||||||
const std::string& propertyName_;
|
const std::string& propertyName_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AsyncPropertyGetter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AsyncPropertyGetter(IProxy& proxy, const std::string& propertyName);
|
||||||
|
AsyncPropertyGetter& onInterface(const std::string& interfaceName);
|
||||||
|
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||||
|
std::future<Variant> getResultAsFuture();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IProxy& proxy_;
|
||||||
|
const std::string& propertyName_;
|
||||||
|
const std::string* interfaceName_{};
|
||||||
|
};
|
||||||
|
|
||||||
class PropertySetter
|
class PropertySetter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PropertySetter(IProxy& proxy, const std::string& propertyName);
|
PropertySetter(IProxy& proxy, const std::string& propertyName);
|
||||||
PropertySetter& onInterface(std::string interfaceName);
|
PropertySetter& onInterface(const std::string& interfaceName);
|
||||||
template <typename _Value> void toValue(const _Value& value);
|
template <typename _Value> void toValue(const _Value& value);
|
||||||
void toValue(const sdbus::Variant& value);
|
template <typename _Value> void toValue(const _Value& value, dont_expect_reply_t);
|
||||||
|
void toValue(const Variant& value);
|
||||||
|
void toValue(const Variant& value, dont_expect_reply_t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IProxy& proxy_;
|
IProxy& proxy_;
|
||||||
const std::string& propertyName_;
|
const std::string& propertyName_;
|
||||||
std::string interfaceName_;
|
const std::string* interfaceName_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncPropertySetter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AsyncPropertySetter(IProxy& proxy, const std::string& propertyName);
|
||||||
|
AsyncPropertySetter& onInterface(const std::string& interfaceName);
|
||||||
|
template <typename _Value> AsyncPropertySetter& toValue(_Value&& value);
|
||||||
|
AsyncPropertySetter& toValue(Variant value);
|
||||||
|
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||||
|
std::future<void> getResultAsFuture();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IProxy& proxy_;
|
||||||
|
const std::string& propertyName_;
|
||||||
|
const std::string* interfaceName_{};
|
||||||
|
Variant value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AllPropertiesGetter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AllPropertiesGetter(IProxy& proxy);
|
||||||
|
std::map<std::string, Variant> onInterface(const std::string& interfaceName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
IProxy& proxy_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncAllPropertiesGetter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AsyncAllPropertiesGetter(IProxy& proxy);
|
||||||
|
AsyncAllPropertiesGetter& onInterface(const std::string& interfaceName);
|
||||||
|
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||||
|
std::future<std::map<std::string, Variant>> getResultAsFuture();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IProxy& proxy_;
|
||||||
|
const std::string* interfaceName_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ namespace sdbus {
|
|||||||
template <typename _Function>
|
template <typename _Function>
|
||||||
inline PropertyRegistrator& PropertyRegistrator::withGetter(_Function&& callback)
|
inline PropertyRegistrator& PropertyRegistrator::withGetter(_Function&& callback)
|
||||||
{
|
{
|
||||||
static_assert(function_traits<_Function>::arity == 0, "Property getter function must not take any arguments");
|
static_assert(function_argument_count_v<_Function> == 0, "Property getter function must not take any arguments");
|
||||||
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
|
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
|
||||||
|
|
||||||
if (propertySignature_.empty())
|
if (propertySignature_.empty())
|
||||||
@ -311,7 +311,7 @@ namespace sdbus {
|
|||||||
template <typename _Function>
|
template <typename _Function>
|
||||||
inline PropertyRegistrator& PropertyRegistrator::withSetter(_Function&& callback)
|
inline PropertyRegistrator& PropertyRegistrator::withSetter(_Function&& callback)
|
||||||
{
|
{
|
||||||
static_assert(function_traits<_Function>::arity == 1, "Property setter function must take one parameter - the property value");
|
static_assert(function_argument_count_v<_Function> == 1, "Property setter function must take one parameter - the property value");
|
||||||
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
|
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
|
||||||
|
|
||||||
if (propertySignature_.empty())
|
if (propertySignature_.empty())
|
||||||
@ -704,7 +704,7 @@ namespace sdbus {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void SignalUnsubscriber::onInterface(std::string interfaceName)
|
inline void SignalUnsubscriber::onInterface(const std::string& interfaceName)
|
||||||
{
|
{
|
||||||
proxy_.unregisterSignalHandler(interfaceName, signalName_);
|
proxy_.unregisterSignalHandler(interfaceName, signalName_);
|
||||||
}
|
}
|
||||||
@ -719,17 +719,56 @@ namespace sdbus {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline sdbus::Variant PropertyGetter::onInterface(const std::string& interfaceName)
|
inline Variant PropertyGetter::onInterface(const std::string& interfaceName)
|
||||||
{
|
{
|
||||||
sdbus::Variant var;
|
Variant var;
|
||||||
proxy_
|
proxy_.callMethod("Get")
|
||||||
.callMethod("Get")
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
.onInterface("org.freedesktop.DBus.Properties")
|
.withArguments(interfaceName, propertyName_)
|
||||||
.withArguments(interfaceName, propertyName_)
|
.storeResultsTo(var);
|
||||||
.storeResultsTo(var);
|
|
||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** ------------------- ***/
|
||||||
|
/*** AsyncPropertyGetter ***/
|
||||||
|
/*** ------------------- ***/
|
||||||
|
|
||||||
|
inline AsyncPropertyGetter::AsyncPropertyGetter(IProxy& proxy, const std::string& propertyName)
|
||||||
|
: proxy_(proxy)
|
||||||
|
, propertyName_(propertyName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AsyncPropertyGetter& AsyncPropertyGetter::onInterface(const std::string& interfaceName)
|
||||||
|
{
|
||||||
|
interfaceName_ = &interfaceName;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
PendingAsyncCall AsyncPropertyGetter::uponReplyInvoke(_Function&& callback)
|
||||||
|
{
|
||||||
|
static_assert(std::is_invocable_r_v<void, _Function, const Error*, Variant>, "Property get callback function must accept Error* and property value as Variant");
|
||||||
|
|
||||||
|
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||||
|
|
||||||
|
return proxy_.callMethodAsync("Get")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(*interfaceName_, propertyName_)
|
||||||
|
.uponReplyInvoke(std::forward<_Function>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::future<Variant> AsyncPropertyGetter::getResultAsFuture()
|
||||||
|
{
|
||||||
|
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||||
|
|
||||||
|
return proxy_.callMethodAsync("Get")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(*interfaceName_, propertyName_)
|
||||||
|
.getResultAsFuture<Variant>();
|
||||||
|
}
|
||||||
|
|
||||||
/*** -------------- ***/
|
/*** -------------- ***/
|
||||||
/*** PropertySetter ***/
|
/*** PropertySetter ***/
|
||||||
/*** -------------- ***/
|
/*** -------------- ***/
|
||||||
@ -740,9 +779,9 @@ namespace sdbus {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline PropertySetter& PropertySetter::onInterface(std::string interfaceName)
|
inline PropertySetter& PropertySetter::onInterface(const std::string& interfaceName)
|
||||||
{
|
{
|
||||||
interfaceName_ = std::move(interfaceName);
|
interfaceName_ = &interfaceName;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -750,17 +789,144 @@ namespace sdbus {
|
|||||||
template <typename _Value>
|
template <typename _Value>
|
||||||
inline void PropertySetter::toValue(const _Value& value)
|
inline void PropertySetter::toValue(const _Value& value)
|
||||||
{
|
{
|
||||||
PropertySetter::toValue(sdbus::Variant{value});
|
PropertySetter::toValue(Variant{value});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void PropertySetter::toValue(const sdbus::Variant& value)
|
template <typename _Value>
|
||||||
|
inline void PropertySetter::toValue(const _Value& value, dont_expect_reply_t)
|
||||||
{
|
{
|
||||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
PropertySetter::toValue(Variant{value}, dont_expect_reply);
|
||||||
|
}
|
||||||
|
|
||||||
proxy_
|
inline void PropertySetter::toValue(const Variant& value)
|
||||||
.callMethod("Set")
|
{
|
||||||
.onInterface("org.freedesktop.DBus.Properties")
|
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||||
.withArguments(interfaceName_, propertyName_, value);
|
|
||||||
|
proxy_.callMethod("Set")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(*interfaceName_, propertyName_, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void PropertySetter::toValue(const Variant& value, dont_expect_reply_t)
|
||||||
|
{
|
||||||
|
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||||
|
|
||||||
|
proxy_.callMethod("Set")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(*interfaceName_, propertyName_, value)
|
||||||
|
.dontExpectReply();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** ------------------- ***/
|
||||||
|
/*** AsyncPropertySetter ***/
|
||||||
|
/*** ------------------- ***/
|
||||||
|
|
||||||
|
inline AsyncPropertySetter::AsyncPropertySetter(IProxy& proxy, const std::string& propertyName)
|
||||||
|
: proxy_(proxy)
|
||||||
|
, propertyName_(propertyName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AsyncPropertySetter& AsyncPropertySetter::onInterface(const std::string& interfaceName)
|
||||||
|
{
|
||||||
|
interfaceName_ = &interfaceName;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename _Value>
|
||||||
|
inline AsyncPropertySetter& AsyncPropertySetter::toValue(_Value&& value)
|
||||||
|
{
|
||||||
|
return AsyncPropertySetter::toValue(Variant{std::forward<_Value>(value)});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AsyncPropertySetter& AsyncPropertySetter::toValue(Variant value)
|
||||||
|
{
|
||||||
|
value_ = std::move(value);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
PendingAsyncCall AsyncPropertySetter::uponReplyInvoke(_Function&& callback)
|
||||||
|
{
|
||||||
|
static_assert(std::is_invocable_r_v<void, _Function, const Error*>, "Property set callback function must accept Error* only");
|
||||||
|
|
||||||
|
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||||
|
|
||||||
|
return proxy_.callMethodAsync("Set")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(*interfaceName_, propertyName_, std::move(value_))
|
||||||
|
.uponReplyInvoke(std::forward<_Function>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::future<void> AsyncPropertySetter::getResultAsFuture()
|
||||||
|
{
|
||||||
|
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||||
|
|
||||||
|
return proxy_.callMethodAsync("Set")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(*interfaceName_, propertyName_, std::move(value_))
|
||||||
|
.getResultAsFuture<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** ------------------- ***/
|
||||||
|
/*** AllPropertiesGetter ***/
|
||||||
|
/*** ------------------- ***/
|
||||||
|
|
||||||
|
inline AllPropertiesGetter::AllPropertiesGetter(IProxy& proxy)
|
||||||
|
: proxy_(proxy)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::map<std::string, Variant> AllPropertiesGetter::onInterface(const std::string& interfaceName)
|
||||||
|
{
|
||||||
|
std::map<std::string, Variant> props;
|
||||||
|
proxy_.callMethod("GetAll")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(interfaceName)
|
||||||
|
.storeResultsTo(props);
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** ------------------------ ***/
|
||||||
|
/*** AsyncAllPropertiesGetter ***/
|
||||||
|
/*** ------------------------ ***/
|
||||||
|
|
||||||
|
inline AsyncAllPropertiesGetter::AsyncAllPropertiesGetter(IProxy& proxy)
|
||||||
|
: proxy_(proxy)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AsyncAllPropertiesGetter& AsyncAllPropertiesGetter::onInterface(const std::string& interfaceName)
|
||||||
|
{
|
||||||
|
interfaceName_ = &interfaceName;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
PendingAsyncCall AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback)
|
||||||
|
{
|
||||||
|
static_assert( std::is_invocable_r_v<void, _Function, const Error*, std::map<std::string, Variant>>
|
||||||
|
, "All properties get callback function must accept Error* and a map of property names to their values" );
|
||||||
|
|
||||||
|
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||||
|
|
||||||
|
return proxy_.callMethodAsync("GetAll")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(*interfaceName_)
|
||||||
|
.uponReplyInvoke(std::forward<_Function>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::future<std::map<std::string, Variant>> AsyncAllPropertiesGetter::getResultAsFuture()
|
||||||
|
{
|
||||||
|
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||||
|
|
||||||
|
return proxy_.callMethodAsync("GetAll")
|
||||||
|
.onInterface("org.freedesktop.DBus.Properties")
|
||||||
|
.withArguments(*interfaceName_)
|
||||||
|
.getResultAsFuture<std::map<std::string, Variant>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ namespace sdbus {
|
|||||||
virtual MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
|
virtual MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Calls method on the proxied D-Bus object
|
* @brief Calls method on the D-Bus object
|
||||||
*
|
*
|
||||||
* @param[in] message Message representing a method call
|
* @param[in] message Message representing a method call
|
||||||
* @param[in] timeout Timeout for dbus call in microseconds
|
* @param[in] timeout Timeout for dbus call in microseconds
|
||||||
@ -109,7 +109,7 @@ namespace sdbus {
|
|||||||
MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout);
|
MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Calls method on the proxied D-Bus object asynchronously
|
* @brief Calls method on the D-Bus object asynchronously
|
||||||
*
|
*
|
||||||
* @param[in] message Message representing an async method call
|
* @param[in] message Message representing an async method call
|
||||||
* @param[in] asyncReplyCallback Handler for the async reply
|
* @param[in] asyncReplyCallback Handler for the async reply
|
||||||
@ -133,7 +133,7 @@ namespace sdbus {
|
|||||||
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout);
|
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
|
* @brief Registers a handler for the desired signal emitted by the D-Bus object
|
||||||
*
|
*
|
||||||
* @param[in] interfaceName Name of an interface that the signal belongs to
|
* @param[in] interfaceName Name of an interface that the signal belongs to
|
||||||
* @param[in] signalName Name of the signal
|
* @param[in] signalName Name of the signal
|
||||||
@ -178,7 +178,7 @@ namespace sdbus {
|
|||||||
virtual void unregister() = 0;
|
virtual void unregister() = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Calls method on the proxied D-Bus object
|
* @brief Calls method on the D-Bus object
|
||||||
*
|
*
|
||||||
* @param[in] methodName Name of the method
|
* @param[in] methodName Name of the method
|
||||||
* @return A helper object for convenient invocation of the method
|
* @return A helper object for convenient invocation of the method
|
||||||
@ -199,7 +199,7 @@ namespace sdbus {
|
|||||||
[[nodiscard]] MethodInvoker callMethod(const std::string& methodName);
|
[[nodiscard]] MethodInvoker callMethod(const std::string& methodName);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Calls method on the proxied D-Bus object asynchronously
|
* @brief Calls method on the D-Bus object asynchronously
|
||||||
*
|
*
|
||||||
* @param[in] methodName Name of the method
|
* @param[in] methodName Name of the method
|
||||||
* @return A helper object for convenient asynchronous invocation of the method
|
* @return A helper object for convenient asynchronous invocation of the method
|
||||||
@ -223,7 +223,7 @@ namespace sdbus {
|
|||||||
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const std::string& methodName);
|
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const std::string& methodName);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Registers signal handler for a given signal of the proxied D-Bus object
|
* @brief Registers signal handler for a given signal of the D-Bus object
|
||||||
*
|
*
|
||||||
* @param[in] signalName Name of the signal
|
* @param[in] signalName Name of the signal
|
||||||
* @return A helper object for convenient registration of the signal handler
|
* @return A helper object for convenient registration of the signal handler
|
||||||
@ -243,7 +243,7 @@ namespace sdbus {
|
|||||||
[[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName);
|
[[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Unregisters signal handler of a given signal of the proxied D-Bus object
|
* @brief Unregisters signal handler of a given signal of the D-Bus object
|
||||||
*
|
*
|
||||||
* @param[in] signalName Name of the signal
|
* @param[in] signalName Name of the signal
|
||||||
* @return A helper object for convenient unregistration of the signal handler
|
* @return A helper object for convenient unregistration of the signal handler
|
||||||
@ -260,7 +260,7 @@ namespace sdbus {
|
|||||||
[[nodiscard]] SignalUnsubscriber muteSignal(const std::string& signalName);
|
[[nodiscard]] SignalUnsubscriber muteSignal(const std::string& signalName);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Gets value of a property of the proxied D-Bus object
|
* @brief Gets value of a property of the D-Bus object
|
||||||
*
|
*
|
||||||
* @param[in] propertyName Name of the property
|
* @param[in] propertyName Name of the property
|
||||||
* @return A helper object for convenient getting of property value
|
* @return A helper object for convenient getting of property value
|
||||||
@ -279,23 +279,101 @@ namespace sdbus {
|
|||||||
[[nodiscard]] PropertyGetter getProperty(const std::string& propertyName);
|
[[nodiscard]] PropertyGetter getProperty(const std::string& propertyName);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Sets value of a property of the proxied D-Bus object
|
* @brief Gets value of a property of the D-Bus object asynchronously
|
||||||
|
*
|
||||||
|
* @param[in] propertyName Name of the property
|
||||||
|
* @return A helper object for convenient asynchronous getting of property value
|
||||||
|
*
|
||||||
|
* This is a high-level, convenience way of reading D-Bus property values that abstracts
|
||||||
|
* from the D-Bus message concept.
|
||||||
|
*
|
||||||
|
* Example of use:
|
||||||
|
* @code
|
||||||
|
* std::future<sdbus::Variant> state = object.getPropertyAsync("state").onInterface("com.kistler.foo").getResultAsFuture();
|
||||||
|
* auto callback = [](const sdbus::Error* err, const sdbus::Variant& value){ ... };
|
||||||
|
* object.getPropertyAsync("state").onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback));
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @throws sdbus::Error in case of failure
|
||||||
|
*/
|
||||||
|
[[nodiscard]] AsyncPropertyGetter getPropertyAsync(const std::string& propertyName);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Sets value of a property of the D-Bus object
|
||||||
*
|
*
|
||||||
* @param[in] propertyName Name of the property
|
* @param[in] propertyName Name of the property
|
||||||
* @return A helper object for convenient setting of property value
|
* @return A helper object for convenient setting of property value
|
||||||
*
|
*
|
||||||
* This is a high-level, convenience way of writing D-Bus property values that abstracts
|
* This is a high-level, convenience way of writing D-Bus property values that abstracts
|
||||||
* from the D-Bus message concept.
|
* from the D-Bus message concept.
|
||||||
|
* Setting property value with NoReply flag is also supported.
|
||||||
|
*
|
||||||
|
* Example of use:
|
||||||
|
* @code
|
||||||
|
* int state = ...;
|
||||||
|
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state);
|
||||||
|
* // Or we can just send the set message call without waiting for the reply
|
||||||
|
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state, dont_expect_reply);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @throws sdbus::Error in case of failure
|
||||||
|
*/
|
||||||
|
[[nodiscard]] PropertySetter setProperty(const std::string& propertyName);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Sets value of a property of the D-Bus object asynchronously
|
||||||
|
*
|
||||||
|
* @param[in] propertyName Name of the property
|
||||||
|
* @return A helper object for convenient asynchronous setting of property value
|
||||||
|
*
|
||||||
|
* This is a high-level, convenience way of writing D-Bus property values that abstracts
|
||||||
|
* from the D-Bus message concept.
|
||||||
*
|
*
|
||||||
* Example of use:
|
* Example of use:
|
||||||
* @code
|
* @code
|
||||||
* int state = ...;
|
* int state = ...;
|
||||||
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state);
|
* // We can wait until the set operation finishes by waiting on the future
|
||||||
|
* std::future<void> res = object_.setPropertyAsync("state").onInterface("com.kistler.foo").toValue(state).getResultAsFuture();
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
* @throws sdbus::Error in case of failure
|
* @throws sdbus::Error in case of failure
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] PropertySetter setProperty(const std::string& propertyName);
|
[[nodiscard]] AsyncPropertySetter setPropertyAsync(const std::string& propertyName);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets values of all properties of the D-Bus object
|
||||||
|
*
|
||||||
|
* @return A helper object for convenient getting of properties' values
|
||||||
|
*
|
||||||
|
* This is a high-level, convenience way of reading D-Bus properties' values that abstracts
|
||||||
|
* from the D-Bus message concept.
|
||||||
|
*
|
||||||
|
* Example of use:
|
||||||
|
* @code
|
||||||
|
* auto props = object.getAllProperties().onInterface("com.kistler.foo");
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @throws sdbus::Error in case of failure
|
||||||
|
*/
|
||||||
|
[[nodiscard]] AllPropertiesGetter getAllProperties();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets values of all properties of the D-Bus object asynchronously
|
||||||
|
*
|
||||||
|
* @return A helper object for convenient asynchronous getting of properties' values
|
||||||
|
*
|
||||||
|
* This is a high-level, convenience way of reading D-Bus properties' values that abstracts
|
||||||
|
* from the D-Bus message concept.
|
||||||
|
*
|
||||||
|
* Example of use:
|
||||||
|
* @code
|
||||||
|
* auto callback = [](const sdbus::Error* err, const std::map<std::string, Variant>>& properties){ ... };
|
||||||
|
* auto props = object.getAllPropertiesAsync().onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback));
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @throws sdbus::Error in case of failure
|
||||||
|
*/
|
||||||
|
[[nodiscard]] AsyncAllPropertiesGetter getAllPropertiesAsync();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Provides D-Bus connection used by the proxy
|
* @brief Provides D-Bus connection used by the proxy
|
||||||
@ -326,7 +404,7 @@ namespace sdbus {
|
|||||||
virtual const Message* getCurrentlyProcessedMessage() const = 0;
|
virtual const Message* getCurrentlyProcessedMessage() const = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Calls method on the proxied D-Bus object asynchronously
|
* @brief Calls method on the D-Bus object asynchronously
|
||||||
*
|
*
|
||||||
* @param[in] message Message representing an async method call
|
* @param[in] message Message representing an async method call
|
||||||
* @param[in] asyncReplyCallback Handler for the async reply
|
* @param[in] asyncReplyCallback Handler for the async reply
|
||||||
@ -445,11 +523,31 @@ namespace sdbus {
|
|||||||
return PropertyGetter(*this, propertyName);
|
return PropertyGetter(*this, propertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline AsyncPropertyGetter IProxy::getPropertyAsync(const std::string& propertyName)
|
||||||
|
{
|
||||||
|
return AsyncPropertyGetter(*this, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
inline PropertySetter IProxy::setProperty(const std::string& propertyName)
|
inline PropertySetter IProxy::setProperty(const std::string& propertyName)
|
||||||
{
|
{
|
||||||
return PropertySetter(*this, propertyName);
|
return PropertySetter(*this, propertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline AsyncPropertySetter IProxy::setPropertyAsync(const std::string& propertyName)
|
||||||
|
{
|
||||||
|
return AsyncPropertySetter(*this, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AllPropertiesGetter IProxy::getAllProperties()
|
||||||
|
{
|
||||||
|
return AllPropertiesGetter(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AsyncAllPropertiesGetter IProxy::getAllPropertiesAsync()
|
||||||
|
{
|
||||||
|
return AsyncAllPropertiesGetter(*this);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||||
*
|
*
|
||||||
|
@ -138,16 +138,52 @@ namespace sdbus {
|
|||||||
return proxy_->getProperty(propertyName).onInterface(interfaceName);
|
return proxy_->getProperty(propertyName).onInterface(interfaceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
PendingAsyncCall GetAsync(const std::string& interfaceName, const std::string& propertyName, _Function&& callback)
|
||||||
|
{
|
||||||
|
return proxy_->getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<sdbus::Variant> GetAsync(const std::string& interfaceName, const std::string& propertyName, with_future_t)
|
||||||
|
{
|
||||||
|
return proxy_->getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||||
|
}
|
||||||
|
|
||||||
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value)
|
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value)
|
||||||
{
|
{
|
||||||
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value);
|
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, dont_expect_reply_t)
|
||||||
|
{
|
||||||
|
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
PendingAsyncCall SetAsync(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, _Function&& callback)
|
||||||
|
{
|
||||||
|
return proxy_->setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<void> SetAsync(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, with_future_t)
|
||||||
|
{
|
||||||
|
return proxy_->setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName)
|
std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName)
|
||||||
{
|
{
|
||||||
std::map<std::string, sdbus::Variant> props;
|
return proxy_->getAllProperties().onInterface(interfaceName);
|
||||||
proxy_->callMethod("GetAll").onInterface(INTERFACE_NAME).withArguments(interfaceName).storeResultsTo(props);
|
}
|
||||||
return props;
|
|
||||||
|
template <typename _Function>
|
||||||
|
PendingAsyncCall GetAllAsync(const std::string& interfaceName, _Function&& callback)
|
||||||
|
{
|
||||||
|
return proxy_->getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<std::map<std::string, sdbus::Variant>> GetAllAsync(const std::string& interfaceName, with_future_t)
|
||||||
|
{
|
||||||
|
return proxy_->getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -93,6 +93,9 @@ namespace sdbus {
|
|||||||
// Tag denoting an asynchronous call that returns std::future as a handle
|
// Tag denoting an asynchronous call that returns std::future as a handle
|
||||||
struct with_future_t { explicit with_future_t() = default; };
|
struct with_future_t { explicit with_future_t() = default; };
|
||||||
inline constexpr with_future_t with_future{};
|
inline constexpr with_future_t with_future{};
|
||||||
|
// Tag denoting a call where the reply shouldn't be waited for
|
||||||
|
struct dont_expect_reply_t { explicit dont_expect_reply_t() = default; };
|
||||||
|
inline constexpr dont_expect_reply_t dont_expect_reply{};
|
||||||
|
|
||||||
// Template specializations for getting D-Bus signatures from C++ types
|
// Template specializations for getting D-Bus signatures from C++ types
|
||||||
template <typename _T>
|
template <typename _T>
|
||||||
|
@ -82,6 +82,29 @@ TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
|
|||||||
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||||
|
{
|
||||||
|
std::promise<std::string> promise;
|
||||||
|
auto future = promise.get_future();
|
||||||
|
|
||||||
|
m_proxy->GetAsync(INTERFACE_NAME, "state", [&](const sdbus::Error* err, sdbus::Variant value)
|
||||||
|
{
|
||||||
|
if (err == nullptr)
|
||||||
|
promise.set_value(value.get<std::string>());
|
||||||
|
else
|
||||||
|
promise.set_exception(std::make_exception_ptr(*err));
|
||||||
|
});
|
||||||
|
|
||||||
|
ASSERT_THAT(future.get(), Eq(DEFAULT_STATE_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||||
|
{
|
||||||
|
auto future = m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
|
||||||
|
|
||||||
|
ASSERT_THAT(future.get().get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
|
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
|
||||||
{
|
{
|
||||||
uint32_t newActionValue = 2345;
|
uint32_t newActionValue = 2345;
|
||||||
@ -91,6 +114,34 @@ TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
|
|||||||
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||||
|
{
|
||||||
|
uint32_t newActionValue = 2346;
|
||||||
|
std::promise<void> promise;
|
||||||
|
auto future = promise.get_future();
|
||||||
|
|
||||||
|
m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](const sdbus::Error* err)
|
||||||
|
{
|
||||||
|
if (err == nullptr)
|
||||||
|
promise.set_value();
|
||||||
|
else
|
||||||
|
promise.set_exception(std::make_exception_ptr(*err));
|
||||||
|
});
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(future.get());
|
||||||
|
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||||
|
{
|
||||||
|
uint32_t newActionValue = 2347;
|
||||||
|
|
||||||
|
auto future = m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(future.get());
|
||||||
|
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
|
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
|
||||||
{
|
{
|
||||||
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
|
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
|
||||||
@ -101,6 +152,38 @@ TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
|
|||||||
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
|
||||||
|
{
|
||||||
|
std::promise<std::map<std::string, sdbus::Variant>> promise;
|
||||||
|
auto future = promise.get_future();
|
||||||
|
|
||||||
|
m_proxy->GetAllAsync(INTERFACE_NAME, [&](const sdbus::Error* err, std::map<std::string, sdbus::Variant> value)
|
||||||
|
{
|
||||||
|
if (err == nullptr)
|
||||||
|
promise.set_value(std::move(value));
|
||||||
|
else
|
||||||
|
promise.set_exception(std::make_exception_ptr(*err));
|
||||||
|
});
|
||||||
|
const auto properties = future.get();
|
||||||
|
|
||||||
|
ASSERT_THAT(properties, SizeIs(3));
|
||||||
|
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||||
|
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||||
|
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||||
|
{
|
||||||
|
auto future = m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
|
||||||
|
|
||||||
|
auto properties = future.get();
|
||||||
|
|
||||||
|
ASSERT_THAT(properties, SizeIs(3));
|
||||||
|
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||||
|
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||||
|
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
|
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
|
||||||
{
|
{
|
||||||
std::atomic<bool> signalReceived{false};
|
std::atomic<bool> signalReceived{false};
|
||||||
|
@ -108,12 +108,18 @@ std::string ProxyGenerator::processInterface(Node& interface) const
|
|||||||
if (!declaration.empty())
|
if (!declaration.empty())
|
||||||
body << declaration << endl;
|
body << declaration << endl;
|
||||||
|
|
||||||
std::string methodDefinitions, asyncDeclarations;
|
std::string methodDefinitions, asyncDeclarationsMethods;
|
||||||
std::tie(methodDefinitions, asyncDeclarations) = processMethods(methods);
|
std::tie(methodDefinitions, asyncDeclarationsMethods) = processMethods(methods);
|
||||||
|
std::string propertyDefinitions, asyncDeclarationsProperties;
|
||||||
|
std::tie(propertyDefinitions, asyncDeclarationsProperties) = processProperties(properties);
|
||||||
|
|
||||||
if (!asyncDeclarations.empty())
|
if (!asyncDeclarationsMethods.empty())
|
||||||
{
|
{
|
||||||
body << asyncDeclarations << endl;
|
body << asyncDeclarationsMethods << endl;
|
||||||
|
}
|
||||||
|
if (!asyncDeclarationsProperties.empty())
|
||||||
|
{
|
||||||
|
body << asyncDeclarationsProperties << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!methodDefinitions.empty())
|
if (!methodDefinitions.empty())
|
||||||
@ -121,7 +127,6 @@ std::string ProxyGenerator::processInterface(Node& interface) const
|
|||||||
body << "public:" << endl << methodDefinitions;
|
body << "public:" << endl << methodDefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string propertyDefinitions = processProperties(properties);
|
|
||||||
if (!propertyDefinitions.empty())
|
if (!propertyDefinitions.empty())
|
||||||
{
|
{
|
||||||
body << "public:" << endl << propertyDefinitions;
|
body << "public:" << endl << propertyDefinitions;
|
||||||
@ -293,9 +298,9 @@ std::tuple<std::string, std::string> ProxyGenerator::processSignals(const Nodes&
|
|||||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ProxyGenerator::processProperties(const Nodes& properties) const
|
std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nodes& properties) const
|
||||||
{
|
{
|
||||||
std::ostringstream propertySS;
|
std::ostringstream propertySS, asyncDeclarationSS;
|
||||||
for (const auto& property : properties)
|
for (const auto& property : properties)
|
||||||
{
|
{
|
||||||
auto propertyName = property->get("name");
|
auto propertyName = property->get("name");
|
||||||
@ -307,25 +312,93 @@ std::string ProxyGenerator::processProperties(const Nodes& properties) const
|
|||||||
auto propertyArg = std::string("value");
|
auto propertyArg = std::string("value");
|
||||||
auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
|
auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
|
||||||
|
|
||||||
|
bool asyncGet{false};
|
||||||
|
bool futureGet{false}; // Async property getter implemented by means of either std::future or callbacks
|
||||||
|
bool asyncSet{false};
|
||||||
|
bool futureSet{false}; // Async property setter implemented by means of either std::future or callbacks
|
||||||
|
|
||||||
|
Nodes annotations = (*property)["annotation"];
|
||||||
|
for (const auto& annotation : annotations)
|
||||||
|
{
|
||||||
|
const auto annotationName = annotation->get("name");
|
||||||
|
const auto annotationValue = annotation->get("value");
|
||||||
|
|
||||||
|
if (annotationName == "org.freedesktop.DBus.Property.Get.Async" && annotationValue == "client") // Server-side not supported (may be in the future)
|
||||||
|
asyncGet = true;
|
||||||
|
else if (annotationName == "org.freedesktop.DBus.Property.Get.Async.ClientImpl" && annotationValue == "callback")
|
||||||
|
futureGet = false;
|
||||||
|
else if (annotationName == "org.freedesktop.DBus.Property.Get.Async.ClientImpl" && (annotationValue == "future" || annotationValue == "std::future"))
|
||||||
|
futureGet = true;
|
||||||
|
else if (annotationName == "org.freedesktop.DBus.Property.Set.Async" && annotationValue == "client") // Server-side not supported (may be in the future)
|
||||||
|
asyncSet = true;
|
||||||
|
else if (annotationName == "org.freedesktop.DBus.Property.Set.Async.ClientImpl" && annotationValue == "callback")
|
||||||
|
futureSet = false;
|
||||||
|
else if (annotationName == "org.freedesktop.DBus.Property.Set.Async.ClientImpl" && (annotationValue == "future" || annotationValue == "std::future"))
|
||||||
|
futureSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||||
{
|
{
|
||||||
propertySS << tab << propertyType << " " << propertyNameSafe << "()" << endl
|
const std::string realRetType = (asyncGet ? (futureGet ? "std::future<sdbus::Variant>" : "sdbus::PendingAsyncCall") : propertyType);
|
||||||
|
|
||||||
|
propertySS << tab << realRetType << " " << propertyNameSafe << "()" << endl
|
||||||
<< tab << "{" << endl;
|
<< tab << "{" << endl;
|
||||||
propertySS << tab << tab << "return proxy_->getProperty(\"" << propertyName << "\")"
|
propertySS << tab << tab << "return proxy_->getProperty" << (asyncGet ? "Async" : "") << "(\"" << propertyName << "\")"
|
||||||
".onInterface(INTERFACE_NAME)";
|
".onInterface(INTERFACE_NAME)";
|
||||||
|
if (asyncGet)
|
||||||
|
{
|
||||||
|
auto nameBigFirst = propertyName;
|
||||||
|
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
|
||||||
|
|
||||||
|
if (futureGet) // Async methods implemented through future
|
||||||
|
{
|
||||||
|
propertySS << ".getResultAsFuture()";
|
||||||
|
}
|
||||||
|
else // Async methods implemented through callbacks
|
||||||
|
{
|
||||||
|
propertySS << ".uponReplyInvoke([this](const sdbus::Error* error, const sdbus::Variant& value)"
|
||||||
|
"{ this->on" << nameBigFirst << "PropertyGetReply(value.get<" << propertyType << ">(), error); })";
|
||||||
|
|
||||||
|
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "PropertyGetReply("
|
||||||
|
<< "const " << propertyType << "& value, const sdbus::Error* error) = 0;" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
propertySS << ";" << endl << tab << "}" << endl << endl;
|
propertySS << ";" << endl << tab << "}" << endl << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||||
{
|
{
|
||||||
propertySS << tab << "void " << propertyNameSafe << "(" << propertyTypeArg << ")" << endl
|
const std::string realRetType = (asyncSet ? (futureSet ? "std::future<void>" : "sdbus::PendingAsyncCall") : "void");
|
||||||
<< tab << "{" << endl;
|
|
||||||
propertySS << tab << tab << "proxy_->setProperty(\"" << propertyName << "\")"
|
propertySS << tab << realRetType << " " << propertyNameSafe << "(" << propertyTypeArg << ")" << endl
|
||||||
|
<< tab << "{" << endl;
|
||||||
|
propertySS << tab << tab << (asyncSet ? "return " : "") << "proxy_->setProperty" << (asyncSet ? "Async" : "")
|
||||||
|
<< "(\"" << propertyName << "\")"
|
||||||
".onInterface(INTERFACE_NAME)"
|
".onInterface(INTERFACE_NAME)"
|
||||||
".toValue(" << propertyArg << ")";
|
".toValue(" << propertyArg << ")";
|
||||||
|
|
||||||
|
if (asyncSet)
|
||||||
|
{
|
||||||
|
auto nameBigFirst = propertyName;
|
||||||
|
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
|
||||||
|
|
||||||
|
if (futureSet) // Async methods implemented through future
|
||||||
|
{
|
||||||
|
propertySS << ".getResultAsFuture()";
|
||||||
|
}
|
||||||
|
else // Async methods implemented through callbacks
|
||||||
|
{
|
||||||
|
propertySS << ".uponReplyInvoke([this](const sdbus::Error* error)"
|
||||||
|
"{ this->on" << nameBigFirst << "PropertySetReply(error); })";
|
||||||
|
|
||||||
|
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "PropertySetReply("
|
||||||
|
<< "const sdbus::Error* error) = 0;" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
propertySS << ";" << endl << tab << "}" << endl << endl;
|
propertySS << ";" << endl << tab << "}" << endl << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return propertySS.str();
|
return std::make_tuple(propertySS.str(), asyncDeclarationSS.str());
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ private:
|
|||||||
* @param properties
|
* @param properties
|
||||||
* @return source code
|
* @return source code
|
||||||
*/
|
*/
|
||||||
std::string processProperties(const sdbuscpp::xml::Nodes& properties) const;
|
std::tuple<std::string, std::string> processProperties(const sdbuscpp::xml::Nodes& properties) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user