diff --git a/docs/using-sdbus-c++.md b/docs/using-sdbus-c++.md index 496de97..7470f83 100644 --- a/docs/using-sdbus-c++.md +++ b/docs/using-sdbus-c++.md @@ -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` 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 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`-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 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`. 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 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 `onReply` pure virtual function, where `` 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 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 /*error*/, sdbus::Variant value) { std::cout << "Got property value: " << value.get() << std::endl; }; @@ -1335,7 +1335,7 @@ std::future statusFuture = object.getPropertyAsync("status").onI std::cout << "Got property value: " << statusFuture.get().get() << 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 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 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 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(), error); }); + return object_->getPropertyAsync("status").onInterface(INTERFACE_NAME).uponReplyInvoke([this](std::optional error, const sdbus::Variant& value){ this->onStatusPropertyGetReply(value.get(), std::move(error)); }); } // setting the property value diff --git a/include/sdbus-c++/ConvenienceApiClasses.inl b/include/sdbus-c++/ConvenienceApiClasses.inl index 7a8e946..3cbb163 100644 --- a/include/sdbus-c++/ConvenienceApiClasses.inl +++ b/include/sdbus-c++/ConvenienceApiClasses.inl @@ -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) { // 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>>(); auto future = promise->get_future(); - uponReplyInvoke([promise = std::move(promise)](const Error* error, _Args... args) + uponReplyInvoke([promise = std::move(promise)](std::optional error, _Args... args) { - if (error == nullptr) + if (!error) if constexpr (!std::is_void_v>) 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 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` 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 PendingAsyncCall AsyncPropertyGetter::uponReplyInvoke(_Function&& callback) { - static_assert(std::is_invocable_r_v, "Property get callback function must accept Error* and property value as Variant"); + static_assert(std::is_invocable_r_v, Variant>, "Property get callback function must accept std::optional and property value as Variant"); assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function @@ -505,7 +505,7 @@ namespace sdbus { template PendingAsyncCall AsyncPropertySetter::uponReplyInvoke(_Function&& callback) { - static_assert(std::is_invocable_r_v, "Property set callback function must accept Error* only"); + static_assert(std::is_invocable_r_v>, "Property set callback function must accept std::optional only"); assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function @@ -563,8 +563,8 @@ namespace sdbus { template PendingAsyncCall AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback) { - static_assert( std::is_invocable_r_v> - , "All properties get callback function must accept Error* and a map of property names to their values" ); + static_assert( std::is_invocable_r_v, std::map> + , "All properties get callback function must accept std::optional state = object.getPropertyAsync("state").onInterface("com.kistler.foo").getResultAsFuture(); - * auto callback = [](const sdbus::Error* err, const sdbus::Variant& value){ ... }; + * auto callback = [](std::optional 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>& properties){ ... }; + * auto callback = [](std::optional err, const std::map>& properties){ ... }; * auto props = object.getAllPropertiesAsync().onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback)); * @endcode * diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index c5f3b69..800472e 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -27,11 +27,14 @@ #ifndef SDBUS_CXX_TYPETRAITS_H_ #define SDBUS_CXX_TYPETRAITS_H_ +#include + #include #include #include #include #include +#include #if __cplusplus >= 202002L #include #endif @@ -63,7 +66,7 @@ namespace sdbus { // Callbacks from sdbus-c++ using method_callback = std::function; - using async_reply_handler = std::function; + using async_reply_handler = std::function error)>; using signal_handler = std::function; using message_handler = std::function; using property_set_callback = std::function; @@ -490,7 +493,7 @@ namespace sdbus { }; template - struct function_traits + struct function_traits, _Args...)> : public function_traits_base { static constexpr bool has_error_param = true; @@ -665,12 +668,12 @@ namespace sdbus { } template - constexpr decltype(auto) apply_impl( _Function&& f - , const Error* e - , _Tuple&& t - , std::index_sequence<_I...> ) + decltype(auto) apply_impl( _Function&& f + , std::optional 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). @@ -711,10 +714,10 @@ namespace sdbus { // Convert tuple `t' of values into a list of arguments // and invoke function `f' with those arguments. template - constexpr decltype(auto) apply(_Function&& f, const Error* e, _Tuple&& t) + decltype(auto) apply(_Function&& f, std::optional e, _Tuple&& t) { return detail::apply_impl( std::forward<_Function>(f) - , e + , std::move(e) , std::forward<_Tuple>(t) , std::make_index_sequence>::value>{} ); } diff --git a/src/Proxy.cpp b/src/Proxy.cpp index c20a94a..fb19c01 100644 --- a/src/Proxy.cpp +++ b/src/Proxy.cpp @@ -121,12 +121,12 @@ std::future Proxy::callMethodAsync(const MethodCall& message, uint6 auto promise = std::make_shared>(); 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) 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); diff --git a/tests/integrationtests/DBusAsyncMethodsTests.cpp b/tests/integrationtests/DBusAsyncMethodsTests.cpp index d0a9862..7d96f90 100644 --- a/tests/integrationtests/DBusAsyncMethodsTests.cpp +++ b/tests/integrationtests/DBusAsyncMethodsTests.cpp @@ -60,12 +60,12 @@ TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTime { std::promise 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 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 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 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 /*err*/){}); auto call = this->m_proxy->doOperationClientSideAsync(100); @@ -190,7 +190,7 @@ TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSide) { std::promise 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 /*err*/){ promise.set_value(1); }); auto call = this->m_proxy->doOperationClientSideAsync(100); call.cancel(); @@ -202,7 +202,7 @@ TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenC { std::promise 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 /*err*/){ promise.set_value(1); }); auto call = this->m_proxy->doOperationClientSideAsync(100); call.cancel(); @@ -214,7 +214,7 @@ TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenC { std::promise 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 /*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 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 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(); diff --git a/tests/integrationtests/DBusStandardInterfacesTests.cpp b/tests/integrationtests/DBusStandardInterfacesTests.cpp index 57e9340..36a2bc0 100644 --- a/tests/integrationtests/DBusStandardInterfacesTests.cpp +++ b/tests/integrationtests/DBusStandardInterfacesTests.cpp @@ -82,12 +82,12 @@ TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface) std::promise 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 err, sdbus::Variant value) { - if (err == nullptr) + if (!err) promise.set_value(value.get()); 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 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 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> promise; auto future = promise.get_future(); - this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](const sdbus::Error* err, std::map value) + this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](std::optional err, std::map 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(); diff --git a/tests/integrationtests/TestProxy.cpp b/tests/integrationtests/TestProxy.cpp index 33d4fa2..6e4f46d 100644 --- a/tests/integrationtests/TestProxy.cpp +++ b/tests/integrationtests/TestProxy.cpp @@ -85,7 +85,7 @@ void TestProxy::onSignalWithoutRegistration(const sdbus::Struct 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 handler) +void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function 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 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 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 error, uint32_t returnValue) { - this->onDoOperationReply(returnValue, error); + this->onDoOperationReply(returnValue, std::move(error)); }); } diff --git a/tests/integrationtests/TestProxy.h b/tests/integrationtests/TestProxy.h index d428e92..521a8ac 100644 --- a/tests/integrationtests/TestProxy.h +++ b/tests/integrationtests/TestProxy.h @@ -85,7 +85,7 @@ protected: void onSignalWithVariant(const sdbus::Variant& aVariant) override; void onSignalWithoutRegistration(const sdbus::Struct>& s); - void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error); + void onDoOperationReply(uint32_t returnValue, std::optional error); // Signals of standard D-Bus interfaces void onPropertiesChanged( const std::string& interfaceName @@ -93,7 +93,7 @@ protected: , const std::vector& invalidatedProperties ) override; public: - void installDoOperationClientSideAsyncReplyHandler(std::function handler); + void installDoOperationClientSideAsyncReplyHandler(std::function err)> handler); uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param); sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param); std::future doOperationClientSideAsync(uint32_t param, with_future_t); @@ -116,7 +116,7 @@ public: // for tests std::atomic m_gotSignalWithSignature{false}; std::map m_signatureFromSignal; - std::function m_DoOperationClientSideAsyncReplyHandler; + std::function err)> m_DoOperationClientSideAsyncReplyHandler; std::function&, const std::vector&)> m_onPropertiesChangedHandler; std::unique_ptr m_signalMsg; @@ -140,7 +140,7 @@ protected: void onSignalWithVariant(const sdbus::Variant&) override {} void onSignalWithoutRegistration(const sdbus::Struct>&) {} - void onDoOperationReply(uint32_t, const sdbus::Error*) {} + void onDoOperationReply(uint32_t, std::optional) {} // Signals of standard D-Bus interfaces void onPropertiesChanged( const std::string&, const std::map&, const std::vector& ) override {} diff --git a/tests/stresstests/concatenator-proxy.h b/tests/stresstests/concatenator-proxy.h index 8aa14be..2383dc7 100644 --- a/tests/stresstests/concatenator-proxy.h +++ b/tests/stresstests/concatenator-proxy.h @@ -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 error) = 0; public: sdbus::PendingAsyncCall concatenate(const std::map& 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 error, const std::string& result){ this->onConcatenateReply(result, std::move(error)); }); } private: diff --git a/tests/stresstests/sdbus-c++-stress-tests.cpp b/tests/stresstests/sdbus-c++-stress-tests.cpp index 2a4b1d3..98d89c3 100644 --- a/tests/stresstests/sdbus-c++-stress-tests.cpp +++ b/tests/stresstests/sdbus-c++-stress-tests.cpp @@ -309,9 +309,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 error) override { - assert(error == nullptr); + assert(error == std::nullopt); std::stringstream str(result); std::string aString; diff --git a/tools/xml2cpp-codegen/ProxyGenerator.cpp b/tools/xml2cpp-codegen/ProxyGenerator.cpp index 580feb1..7606e88 100644 --- a/tools/xml2cpp-codegen/ProxyGenerator.cpp +++ b/tools/xml2cpp-codegen/ProxyGenerator.cpp @@ -251,11 +251,11 @@ std::tuple 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 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 error) = 0;" << endl; } } else if (outArgs.size() > 0) @@ -359,11 +359,11 @@ std::tuple 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 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 error) = 0;" << endl; } } propertySS << ";" << endl << tab << "}" << endl << endl; @@ -391,11 +391,11 @@ std::tuple 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 error)" + "{ this->on" << nameBigFirst << "PropertySetReply(std::move(error)); })"; asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "PropertySetReply(" - << "const sdbus::Error* error) = 0;" << endl; + << "std::optional error) = 0;" << endl; } }