mirror of
https://github.com/Kistler-Group/sdbus-cpp.git
synced 2025-07-29 17:47:18 +02:00
refactor: use optional for passing potential call errors (#396)
This switches from a raw pointer to std::optional type to pass prospective call errors to the client (using std::optional was not possible years back when sdbus-c++ was based on C++14). This makes the API a little clearer, safer, idiomatically more expressive, and removes potential confusion associated with raw pointers (like ownership, lifetime questions, etc.).
This commit is contained in:
@ -167,10 +167,10 @@ All public types and functions of sdbus-c++ reside in the `sdbus` namespace.
|
||||
Error signalling and propagation
|
||||
--------------------------------
|
||||
|
||||
`sdbus::Error` exception is used to signal errors in sdbus-c++. There are two types of errors:
|
||||
`sdbus::Error` type is used as an exception to signal errors in sdbus-c++. There are two types of errors:
|
||||
|
||||
* D-Bus related errors, like call timeouts, failed socket allocation, etc. These are raised by the D-Bus library or D-Bus daemon itself.
|
||||
* user-defined errors, i.e. errors signalled and propagated from remote methods back to the caller. So these are issued by sdbus-c++ users.
|
||||
* user-defined errors, i.e. errors signalled and propagated from remote methods back to the caller. So these are issued by sdbus-c++ clients.
|
||||
|
||||
`sdbus::Error` is a carrier for both types of errors, carrying the error name and error message with it.
|
||||
|
||||
@ -605,15 +605,15 @@ When registering methods, calling methods or emitting signals, multiple lines of
|
||||
|
||||
We recommend that sdbus-c++ users prefer the convenience API to the lower level, basic API. When feasible, using generated adaptor and proxy C++ bindings is even better as it provides yet slightly higher abstraction built on top of the convenience API, where remote calls look simply like local, native calls of object methods. They are described in the following section.
|
||||
|
||||
> **_Note_:** By default, signal callback handlers are not invoked (i.e., the signal is silently dropped) if there is a signal signature mismatch. If you want to be informed of such situations, they can prepend `const sdbus::Error*` parameter to their signal callback handler's parameter list. This argument will be `nullptr` in normal cases, and will provide access to the corresponding `sdbus::Error` object in case of deserialization failures. An example of a handler with the signature (`int`) different from the real signal contents (`string`):
|
||||
> **_Note_:** By default, signal callback handlers are not invoked (i.e., the signal is silently dropped) if there is a signal signature mismatch. If you want to be informed of such situations, you can add `std::optional<sdbus::Error>` parameter to the beginning of your signal callback handler's parameter list. When sdbus-c++ invokes the handler, it will set this argument either to be empty (in normal cases), or to carry a corresponding `sdbus::Error` object (in case of deserialization failures, like type mismatches). An example of a handler with the signature (`int`) different from the real signal contents (`string`):
|
||||
> ```c++
|
||||
> void onConcatenated(const sdbus::Error* e, int wrongParameter)
|
||||
> void onConcatenated(std::optional<sdbus::Error> e, int wrongParameter)
|
||||
> {
|
||||
> assert(e);
|
||||
> assert(e.has_value());
|
||||
> assert(e->getMessage() == "Failed to deserialize a int32 value");
|
||||
> }
|
||||
> ```
|
||||
> Signature mismatch in signal handlers is probably the most common reason why signals are not received in the client, while we can see them on the bus with `dbus-monitor`. Use `const sdbus::Error*`-based callback variant and inspect the error to check if that's the cause of such problems.
|
||||
> Signature mismatch in signal handlers is probably the most common reason why signals are not received in the client, while we can see them on the bus with `dbus-monitor`. Use `std::optional<sdbus::Error>`-based callback variant and inspect the error to check if that's the cause of your problems.
|
||||
|
||||
> **_Tip_:** When registering a D-Bus object, we can additionally provide names of input and output parameters of its methods and names of parameters of its signals. When the object is introspected, these names are listed in the resulting introspection XML, which improves the description of object's interfaces:
|
||||
> ```c++
|
||||
@ -1127,15 +1127,15 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
/* ... */
|
||||
|
||||
auto callback = [](MethodReply reply, const sdbus::Error* error)
|
||||
auto callback = [](MethodReply reply, std::optional<sdbus::Error> error)
|
||||
{
|
||||
if (error == nullptr) // No error
|
||||
if (!error) // No error
|
||||
{
|
||||
std::string result;
|
||||
reply >> result;
|
||||
std::cout << "Got concatenate result: " << result << std::endl;
|
||||
}
|
||||
else // We got a D-Bus error...
|
||||
else // We've got a D-Bus error...
|
||||
{
|
||||
std::cerr << "Got concatenate error " << error->getName() << " with message " << error->getMessage() << std::endl;
|
||||
}
|
||||
@ -1163,7 +1163,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
```
|
||||
|
||||
The callback is a void-returning function taking two arguments: a reference to the reply message, and a pointer to the prospective `sdbus::Error` instance. Zero `Error` pointer means that no D-Bus error occurred while making the call, and the reply message contains valid reply. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred. Error name and message can then be read out by the client from that instance.
|
||||
The callback is a void-returning function taking two arguments: a reference to the reply message, and a pointer to the prospective `sdbus::Error` instance. Empty `error` optional argument means that no D-Bus error occurred while making the call, and the reply message contains a valid reply. A non-empty `error` argument means that an error occurred during the call, and we can access the error name and message from the `Error` value inside the argument.
|
||||
|
||||
There is also an overload of this `IProxy::callMethod()` function taking method call timeout argument.
|
||||
|
||||
@ -1192,16 +1192,16 @@ Another option is to use `std::future`-based overload of the `IProxy::callMethod
|
||||
|
||||
### Convenience API
|
||||
|
||||
On the convenience API level, the call statement starts with `callMethodAsync()`, and one option is to finish the statement with `uponReplyInvoke()` that takes a callback handler. The callback is a void-returning function that takes at least one argument: pointer to the `sdbus::Error` instance. All subsequent arguments shall exactly reflect the D-Bus method output arguments. A concatenator example:
|
||||
On the convenience API level, the call statement starts with `callMethodAsync()`, and one option is to finish the statement with `uponReplyInvoke()` that takes a callback handler. The callback is a void-returning function that takes at least one argument: `std::optional<sdbus::Error>`. All subsequent arguments shall exactly reflect the D-Bus method output arguments. A concatenator example:
|
||||
|
||||
```c++
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* ... */
|
||||
|
||||
auto callback = [](const sdbus::Error* error, const std::string& concatenatedString)
|
||||
auto callback = [](std::optional<sdbus::Error> error, const std::string& concatenatedString)
|
||||
{
|
||||
if (error == nullptr) // No error
|
||||
if (!error) // No error
|
||||
std::cout << "Got concatenate result: " << concatenatedString << std::endl;
|
||||
else // We got a D-Bus error...
|
||||
std::cerr << "Got concatenate error " << error->getName() << " with message " << error->getMessage() << std::endl;
|
||||
@ -1225,7 +1225,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
```
|
||||
|
||||
When the `Error` pointer is zero, it means that no D-Bus error occurred while making the call, and subsequent arguments are valid D-Bus method return values. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred during the call (and subsequent arguments are simply default-constructed). Error name and message can then be read out by the client from `Error` instance.
|
||||
Empty `error` parameter means that no D-Bus error occurred while making the call, and subsequent arguments are valid D-Bus method return values. However, `error` parameter containing a value means that an error occurred during the call (and subsequent arguments are simply default-constructed), and the underlying `Error` instance provides us with the error name and message.
|
||||
|
||||
Another option is to finish the async call statement with `getResultAsFuture()`, which is a template function which takes the list of types returned by the D-Bus method (empty list in case of `void`-returning method) which returns a `std::future` object, which will later, when the reply arrives, be set to contain the return value(s). Or if the call returns an error, `sdbus::Error` will be thrown by `std::future::get()`.
|
||||
|
||||
@ -1274,7 +1274,7 @@ An asynchronous method can be generated as a callback-based method or `std::futu
|
||||
|
||||
For each client-side async method, a corresponding `on<MethodName>Reply` pure virtual function, where `<MethodName>` is the capitalized D-Bus method name, is generated in the generated proxy class. This function is the callback invoked when the D-Bus method reply arrives, and must be provided a body by overriding it in the implementation class.
|
||||
|
||||
So in the specific example above, the tool will generate a `Concatenator_proxy` class similar to one shown in a [dedicated section above](#concatenator-client-glueh), with the difference that it will also generate an additional `virtual void onConcatenateReply(const sdbus::Error* error, const std::string& concatenatedString);` method, which we shall override in derived `ConcatenatorProxy`.
|
||||
So in the specific example above, the tool will generate a `Concatenator_proxy` class similar to one shown in a [dedicated section above](#concatenator-client-glueh), with the difference that it will also generate an additional `virtual void onConcatenateReply(std::optional<sdbus::Error> error, const std::string& concatenatedString);` method, which we shall override in the derived `ConcatenatorProxy`.
|
||||
|
||||
#### Generating std:future-based async methods
|
||||
|
||||
@ -1324,7 +1324,7 @@ Getting a property in asynchronous manner is also possible, in both callback-bas
|
||||
|
||||
```c++
|
||||
// Callback-based method:
|
||||
auto callback = [](const sdbus::Error* err, sdbus::Variant value)
|
||||
auto callback = [](std::optional<sdbus::Error> /*error*/, sdbus::Variant value)
|
||||
{
|
||||
std::cout << "Got property value: " << value.get<uint32_t>() << std::endl;
|
||||
};
|
||||
@ -1335,7 +1335,7 @@ std::future<sdbus::Variant> statusFuture = object.getPropertyAsync("status").onI
|
||||
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).
|
||||
More information on an `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
|
||||
|
||||
@ -1350,7 +1350,7 @@ Setting a property in asynchronous manner is also possible, in both callback-bas
|
||||
|
||||
```c++
|
||||
// Callback-based method:
|
||||
auto callback = [](const sdbus::Error* err) { /*... Error handling in case err is non-null...*/ };
|
||||
auto callback = [](std::optional<sdbus::Error> error { /*... Error handling in case error contains a value...*/ };
|
||||
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();
|
||||
@ -1473,13 +1473,13 @@ class PropertyProvider_proxy
|
||||
{
|
||||
/*...*/
|
||||
|
||||
virtual void onStatusPropertyGetReply(const uint32_t& value, const sdbus::Error* error) = 0;
|
||||
virtual void onStatusPropertyGetReply(const uint32_t& value, std::optional<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); });
|
||||
return object_->getPropertyAsync("status").onInterface(INTERFACE_NAME).uponReplyInvoke([this](std::optional<sdbus::Error> error, const sdbus::Variant& value){ this->onStatusPropertyGetReply(value.get<uint32_t>(), std::move(error)); });
|
||||
}
|
||||
|
||||
// setting the property value
|
||||
|
@ -232,14 +232,14 @@ namespace sdbus {
|
||||
{
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
auto asyncReplyHandler = [callback = std::forward<_Function>(callback)](MethodReply reply, const Error* error)
|
||||
auto asyncReplyHandler = [callback = std::forward<_Function>(callback)](MethodReply reply, std::optional<Error> error)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
tuple_of_function_input_arg_types_t<_Function> args;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple (if no error occurred).
|
||||
if (error == nullptr)
|
||||
if (!error)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -249,13 +249,13 @@ namespace sdbus {
|
||||
{
|
||||
// Pass message deserialization exceptions to the client via callback error parameter,
|
||||
// instead of propagating them up the message loop call stack.
|
||||
sdbus::apply(callback, &e, args);
|
||||
sdbus::apply(callback, e, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, error, args);
|
||||
sdbus::apply(callback, std::move(error), args);
|
||||
};
|
||||
|
||||
return proxy_.callMethodAsync(method_, std::move(asyncReplyHandler), timeout_);
|
||||
@ -267,15 +267,15 @@ namespace sdbus {
|
||||
auto promise = std::make_shared<std::promise<future_return_t<_Args...>>>();
|
||||
auto future = promise->get_future();
|
||||
|
||||
uponReplyInvoke([promise = std::move(promise)](const Error* error, _Args... args)
|
||||
uponReplyInvoke([promise = std::move(promise)](std::optional<Error> error, _Args... args)
|
||||
{
|
||||
if (error == nullptr)
|
||||
if (!error)
|
||||
if constexpr (!std::is_void_v<future_return_t<_Args...>>)
|
||||
promise->set_value({std::move(args)...});
|
||||
else
|
||||
promise->set_value();
|
||||
else
|
||||
promise->set_exception(std::make_exception_ptr(*error));
|
||||
promise->set_exception(std::make_exception_ptr(*std::move(error)));
|
||||
});
|
||||
|
||||
// Will be std::future<void> for no D-Bus method return value
|
||||
@ -331,10 +331,10 @@ namespace sdbus {
|
||||
// as a storage for the argument values deserialized from the signal message.
|
||||
tuple_of_function_input_arg_types_t<_Function> signalArgs;
|
||||
|
||||
// The signal handler can take pure signal parameters only, or an additional `const Error*` as its first
|
||||
// The signal handler can take pure signal parameters only, or an additional `std::optional<Error>` as its first
|
||||
// parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch),
|
||||
// the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure
|
||||
// will be communicated as a non-zero Error pointer to the client's signal handler.
|
||||
// will be communicated to the client's signal handler as a valid Error object inside the std::optional parameter.
|
||||
if constexpr (has_error_param_v<_Function>)
|
||||
{
|
||||
// Deserialize input arguments from the signal message into the tuple
|
||||
@ -346,12 +346,12 @@ namespace sdbus {
|
||||
{
|
||||
// Pass message deserialization exceptions to the client via callback error parameter,
|
||||
// instead of propagating them up the message loop call stack.
|
||||
sdbus::apply(callback, &e, signalArgs);
|
||||
sdbus::apply(callback, e, signalArgs);
|
||||
return;
|
||||
}
|
||||
|
||||
// Invoke callback with no error and input arguments from the tuple.
|
||||
sdbus::apply(callback, nullptr, signalArgs);
|
||||
sdbus::apply(callback, {}, signalArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -404,7 +404,7 @@ namespace sdbus {
|
||||
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");
|
||||
static_assert(std::is_invocable_r_v<void, _Function, std::optional<Error>, Variant>, "Property get callback function must accept std::optional<Error> and property value as Variant");
|
||||
|
||||
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||
|
||||
@ -505,7 +505,7 @@ namespace sdbus {
|
||||
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");
|
||||
static_assert(std::is_invocable_r_v<void, _Function, std::optional<Error>>, "Property set callback function must accept std::optional<Error> only");
|
||||
|
||||
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||
|
||||
@ -563,8 +563,8 @@ namespace sdbus {
|
||||
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" );
|
||||
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, std::map<std::string, Variant>>
|
||||
, "All properties get callback function must accept std::optional<Error< and a map of property names to their values" );
|
||||
|
||||
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||
|
||||
|
@ -343,7 +343,7 @@ namespace sdbus {
|
||||
* 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){ ... };
|
||||
* auto callback = [](std::optional<sdbus::Error> err, const sdbus::Variant& value){ ... };
|
||||
* object.getPropertyAsync("state").onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback));
|
||||
* @endcode
|
||||
*
|
||||
@ -420,7 +420,7 @@ namespace sdbus {
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* auto callback = [](const sdbus::Error* err, const std::map<std::string, Variant>>& properties){ ... };
|
||||
* auto callback = [](std::optional<sdbus::Error> err, const std::map<std::string, Variant>>& properties){ ... };
|
||||
* auto props = object.getAllPropertiesAsync().onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback));
|
||||
* @endcode
|
||||
*
|
||||
|
@ -27,11 +27,14 @@
|
||||
#ifndef SDBUS_CXX_TYPETRAITS_H_
|
||||
#define SDBUS_CXX_TYPETRAITS_H_
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#if __cplusplus >= 202002L
|
||||
#include <span>
|
||||
#endif
|
||||
@ -63,7 +66,7 @@ namespace sdbus {
|
||||
|
||||
// Callbacks from sdbus-c++
|
||||
using method_callback = std::function<void(MethodCall msg)>;
|
||||
using async_reply_handler = std::function<void(MethodReply reply, const Error* error)>;
|
||||
using async_reply_handler = std::function<void(MethodReply reply, std::optional<Error> error)>;
|
||||
using signal_handler = std::function<void(Signal signal)>;
|
||||
using message_handler = std::function<void(Message msg)>;
|
||||
using property_set_callback = std::function<void(PropertySetCall msg)>;
|
||||
@ -502,7 +505,7 @@ namespace sdbus {
|
||||
};
|
||||
|
||||
template <typename... _Args>
|
||||
struct function_traits<void(const Error*, _Args...)>
|
||||
struct function_traits<void(std::optional<Error>, _Args...)>
|
||||
: public function_traits_base<void, _Args...>
|
||||
{
|
||||
static constexpr bool has_error_param = true;
|
||||
@ -677,12 +680,12 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, const Error* e
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...> )
|
||||
decltype(auto) apply_impl( _Function&& f
|
||||
, std::optional<Error> e
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...> )
|
||||
{
|
||||
return std::forward<_Function>(f)(e, std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
return std::forward<_Function>(f)(std::move(e), std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
}
|
||||
|
||||
// For non-void returning functions, apply_impl simply returns function return value (a tuple of values).
|
||||
@ -723,10 +726,10 @@ namespace sdbus {
|
||||
// Convert tuple `t' of values into a list of arguments
|
||||
// and invoke function `f' with those arguments.
|
||||
template <class _Function, class _Tuple>
|
||||
constexpr decltype(auto) apply(_Function&& f, const Error* e, _Tuple&& t)
|
||||
decltype(auto) apply(_Function&& f, std::optional<Error> e, _Tuple&& t)
|
||||
{
|
||||
return detail::apply_impl( std::forward<_Function>(f)
|
||||
, e
|
||||
, std::move(e)
|
||||
, std::forward<_Tuple>(t)
|
||||
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
|
||||
}
|
||||
|
@ -121,12 +121,12 @@ std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, uint6
|
||||
auto promise = std::make_shared<std::promise<MethodReply>>();
|
||||
auto future = promise->get_future();
|
||||
|
||||
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply reply, const Error* error) noexcept
|
||||
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply reply, std::optional<Error> error) noexcept
|
||||
{
|
||||
if (error == nullptr)
|
||||
if (!error)
|
||||
promise->set_value(std::move(reply));
|
||||
else
|
||||
promise->set_exception(std::make_exception_ptr(*error));
|
||||
promise->set_exception(std::make_exception_ptr(*std::move(error)));
|
||||
};
|
||||
|
||||
(void)Proxy::callMethodAsync(message, std::move(asyncReplyCallback), timeout);
|
||||
@ -207,12 +207,12 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
|
||||
const auto* error = sd_bus_message_get_error(sdbusMessage);
|
||||
if (error == nullptr)
|
||||
{
|
||||
asyncCallData->callback(std::move(message), nullptr);
|
||||
asyncCallData->callback(std::move(message), {});
|
||||
}
|
||||
else
|
||||
{
|
||||
Error exception(error->name, error->message);
|
||||
asyncCallData->callback(std::move(message), &exception);
|
||||
asyncCallData->callback(std::move(message), std::move(exception));
|
||||
}
|
||||
}, retError);
|
||||
|
||||
|
@ -60,12 +60,12 @@ TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTime
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
@ -146,12 +146,12 @@ TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(std::move(err)));
|
||||
});
|
||||
|
||||
this->m_proxy->doOperationClientSideAsync(100);
|
||||
@ -179,7 +179,7 @@ TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutu
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
|
||||
{
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){});
|
||||
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
@ -190,7 +190,7 @@ TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
call.cancel();
|
||||
@ -202,7 +202,7 @@ TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenC
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
call.cancel();
|
||||
@ -214,7 +214,7 @@ TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenC
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
|
||||
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(0);
|
||||
(void) future.get(); // Wait for the call to finish
|
||||
@ -242,12 +242,12 @@ TYPED_TEST(AsyncSdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFa
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
this->m_proxy->doErroneousOperationClientSideAsync();
|
||||
|
@ -82,12 +82,12 @@ TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
std::promise<std::string> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
this->m_proxy->GetAsync(INTERFACE_NAME, "state", [&](const sdbus::Error* err, sdbus::Variant value)
|
||||
this->m_proxy->GetAsync(INTERFACE_NAME, "state", [&](std::optional<sdbus::Error> err, sdbus::Variant value)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(value.get<std::string>());
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(DEFAULT_STATE_VALUE));
|
||||
@ -115,12 +115,12 @@ TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](const sdbus::Error* err)
|
||||
this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value();
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW(future.get());
|
||||
@ -152,12 +152,12 @@ TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfac
|
||||
std::promise<std::map<std::string, sdbus::Variant>> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](const sdbus::Error* err, std::map<std::string, sdbus::Variant> value)
|
||||
this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](std::optional<sdbus::Error> err, std::map<std::string, sdbus::Variant> value)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(std::move(value));
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
const auto properties = future.get();
|
||||
|
||||
|
@ -85,7 +85,7 @@ void TestProxy::onSignalWithoutRegistration(const sdbus::Struct<std::string, sdb
|
||||
m_gotSignalWithSignature = true;
|
||||
}
|
||||
|
||||
void TestProxy::onDoOperationReply(uint32_t returnValue, const sdbus::Error* error)
|
||||
void TestProxy::onDoOperationReply(uint32_t returnValue, std::optional<sdbus::Error> error)
|
||||
{
|
||||
if (m_DoOperationClientSideAsyncReplyHandler)
|
||||
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
|
||||
@ -99,7 +99,7 @@ void TestProxy::onPropertiesChanged( const std::string& interfaceName
|
||||
m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties);
|
||||
}
|
||||
|
||||
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
|
||||
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, std::optional<sdbus::Error> err)> handler)
|
||||
{
|
||||
m_DoOperationClientSideAsyncReplyHandler = std::move(handler);
|
||||
}
|
||||
@ -117,9 +117,9 @@ sdbus::PendingAsyncCall TestProxy::doOperationClientSideAsync(uint32_t param)
|
||||
return getProxy().callMethodAsync("doOperation")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withArguments(param)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
|
||||
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, error);
|
||||
this->onDoOperationReply(returnValue, std::move(error));
|
||||
});
|
||||
}
|
||||
|
||||
@ -143,9 +143,9 @@ void TestProxy::doErroneousOperationClientSideAsync()
|
||||
{
|
||||
getProxy().callMethodAsync("throwError")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error)
|
||||
.uponReplyInvoke([this](std::optional<sdbus::Error> error)
|
||||
{
|
||||
this->onDoOperationReply(0, error);
|
||||
this->onDoOperationReply(0, std::move(error));
|
||||
});
|
||||
}
|
||||
|
||||
@ -163,9 +163,9 @@ void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microse
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withTimeout(timeout)
|
||||
.withArguments(param)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
|
||||
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, error);
|
||||
this->onDoOperationReply(returnValue, std::move(error));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ protected:
|
||||
void onSignalWithVariant(const sdbus::Variant& aVariant) override;
|
||||
|
||||
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s);
|
||||
void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error);
|
||||
void onDoOperationReply(uint32_t returnValue, std::optional<sdbus::Error> error);
|
||||
|
||||
// Signals of standard D-Bus interfaces
|
||||
void onPropertiesChanged( const std::string& interfaceName
|
||||
@ -93,7 +93,7 @@ protected:
|
||||
, const std::vector<std::string>& invalidatedProperties ) override;
|
||||
|
||||
public:
|
||||
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler);
|
||||
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, std::optional<sdbus::Error> err)> handler);
|
||||
uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
|
||||
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param);
|
||||
std::future<uint32_t> doOperationClientSideAsync(uint32_t param, with_future_t);
|
||||
@ -116,7 +116,7 @@ public: // for tests
|
||||
std::atomic<bool> m_gotSignalWithSignature{false};
|
||||
std::map<std::string, std::string> m_signatureFromSignal;
|
||||
|
||||
std::function<void(uint32_t res, const sdbus::Error* err)> m_DoOperationClientSideAsyncReplyHandler;
|
||||
std::function<void(uint32_t res, std::optional<sdbus::Error> err)> m_DoOperationClientSideAsyncReplyHandler;
|
||||
std::function<void(const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>&)> m_onPropertiesChangedHandler;
|
||||
|
||||
std::unique_ptr<const Message> m_signalMsg;
|
||||
@ -140,7 +140,7 @@ protected:
|
||||
void onSignalWithVariant(const sdbus::Variant&) override {}
|
||||
|
||||
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>&) {}
|
||||
void onDoOperationReply(uint32_t, const sdbus::Error*) {}
|
||||
void onDoOperationReply(uint32_t, std::optional<sdbus::Error>) {}
|
||||
|
||||
// Signals of standard D-Bus interfaces
|
||||
void onPropertiesChanged( const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>& ) override {}
|
||||
|
@ -39,12 +39,12 @@ protected:
|
||||
|
||||
virtual void onConcatenatedSignal(const std::string& concatenatedString) = 0;
|
||||
|
||||
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) = 0;
|
||||
virtual void onConcatenateReply(const std::string& result, std::optional<sdbus::Error> error) = 0;
|
||||
|
||||
public:
|
||||
sdbus::PendingAsyncCall concatenate(const std::map<std::string, sdbus::Variant>& params)
|
||||
{
|
||||
return proxy_->callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
|
||||
return proxy_->callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](std::optional<sdbus::Error> error, const std::string& result){ this->onConcatenateReply(result, std::move(error)); });
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -310,9 +310,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void onConcatenateReply(const std::string& result, [[maybe_unused]] const sdbus::Error* error) override
|
||||
virtual void onConcatenateReply(const std::string& result, [[maybe_unused]] std::optional<sdbus::Error> error) override
|
||||
{
|
||||
assert(error == nullptr);
|
||||
assert(error == std::nullopt);
|
||||
|
||||
std::stringstream str(result);
|
||||
std::string aString;
|
||||
|
@ -251,11 +251,11 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
|
||||
}
|
||||
else // Async methods implemented through callbacks
|
||||
{
|
||||
definitionSS << ".uponReplyInvoke([this](const sdbus::Error* error" << (outArgTypeStr.empty() ? "" : ", ") << outArgTypeStr << ")"
|
||||
"{ this->on" << nameBigFirst << "Reply(" << outArgStr << (outArgStr.empty() ? "" : ", ") << "error); })";
|
||||
definitionSS << ".uponReplyInvoke([this](std::optional<sdbus::Error> error" << (outArgTypeStr.empty() ? "" : ", ") << outArgTypeStr << ")"
|
||||
"{ this->on" << nameBigFirst << "Reply(" << outArgStr << (outArgStr.empty() ? "" : ", ") << "std::move(error)); })";
|
||||
|
||||
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "Reply("
|
||||
<< outArgTypeStr << (outArgTypeStr.empty() ? "" : ", ") << "const sdbus::Error* error) = 0;" << endl;
|
||||
<< outArgTypeStr << (outArgTypeStr.empty() ? "" : ", ") << "std::optional<sdbus::Error> error) = 0;" << endl;
|
||||
}
|
||||
}
|
||||
else if (outArgs.size() > 0)
|
||||
@ -359,11 +359,11 @@ std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nod
|
||||
}
|
||||
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); })";
|
||||
propertySS << ".uponReplyInvoke([this](std::optional<sdbus::Error> error, const sdbus::Variant& value)"
|
||||
"{ this->on" << nameBigFirst << "PropertyGetReply(value.get<" << propertyType << ">(), std::move(error)); })";
|
||||
|
||||
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "PropertyGetReply("
|
||||
<< "const " << propertyType << "& value, const sdbus::Error* error) = 0;" << endl;
|
||||
<< "const " << propertyType << "& value, std::optional<sdbus::Error> error) = 0;" << endl;
|
||||
}
|
||||
}
|
||||
propertySS << ";" << endl << tab << "}" << endl << endl;
|
||||
@ -391,11 +391,11 @@ std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nod
|
||||
}
|
||||
else // Async methods implemented through callbacks
|
||||
{
|
||||
propertySS << ".uponReplyInvoke([this](const sdbus::Error* error)"
|
||||
"{ this->on" << nameBigFirst << "PropertySetReply(error); })";
|
||||
propertySS << ".uponReplyInvoke([this](std::optional<sdbus::Error> error)"
|
||||
"{ this->on" << nameBigFirst << "PropertySetReply(std::move(error)); })";
|
||||
|
||||
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "PropertySetReply("
|
||||
<< "const sdbus::Error* error) = 0;" << endl;
|
||||
<< "std::optional<sdbus::Error> error) = 0;" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user