From 9de803e6622450768ac990c373ca593ef9127434 Mon Sep 17 00:00:00 2001 From: Oliver Facklam Date: Wed, 27 May 2026 18:33:54 +0200 Subject: [PATCH 1/4] feat: add test for receiving type-mismatched signal --- tests/integrationtests/DBusSignalsTests.cpp | 8 ++++++++ tests/integrationtests/TestAdaptor.cpp | 5 +++++ tests/integrationtests/TestAdaptor.h | 1 + tests/integrationtests/TestProxy.cpp | 8 ++++++++ tests/integrationtests/TestProxy.h | 3 +++ 5 files changed, 25 insertions(+) diff --git a/tests/integrationtests/DBusSignalsTests.cpp b/tests/integrationtests/DBusSignalsTests.cpp index a3a016f1..8b9da908 100644 --- a/tests/integrationtests/DBusSignalsTests.cpp +++ b/tests/integrationtests/DBusSignalsTests.cpp @@ -114,6 +114,14 @@ TYPED_TEST(SdbusTestObject, EmitsSignalWithoutRegistrationSuccessfully) ASSERT_THAT(this->m_proxy->m_signatureFromSignal["platform"], Eq(sdbus::Signature{"av"})); } +TYPED_TEST(SdbusTestObject, EmitsSignalWithErrorAndTypeMismatchSuccessfully) +{ + this->m_adaptor->emitSignalWithErrorAndTypeMismatch(); + + ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithTypeMismatch)); + ASSERT_TRUE(this->m_proxy->m_errorFromSignal.has_value()); +} + TYPED_TEST(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler) { this->m_adaptor->emitSimpleSignal(); diff --git a/tests/integrationtests/TestAdaptor.cpp b/tests/integrationtests/TestAdaptor.cpp index 883bde9f..d7a44fc0 100644 --- a/tests/integrationtests/TestAdaptor.cpp +++ b/tests/integrationtests/TestAdaptor.cpp @@ -311,6 +311,11 @@ void TestAdaptor::emitSignalWithoutRegistration(const sdbus::Struct>& strct); + void emitSignalWithErrorAndTypeMismatch(); static std::string getExpectedXmlApiDescription() ; private: diff --git a/tests/integrationtests/TestProxy.cpp b/tests/integrationtests/TestProxy.cpp index 8ca07e10..44000622 100644 --- a/tests/integrationtests/TestProxy.cpp +++ b/tests/integrationtests/TestProxy.cpp @@ -45,6 +45,7 @@ TestProxy::TestProxy(ServiceName destination, ObjectPath objectPath) : ProxyInterfaces(std::move(destination), std::move(objectPath)) { getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct>& strct){ this->onSignalWithoutRegistration(strct); }); + getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional e, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(e, wrongParameter); }); registerProxy(); } @@ -60,6 +61,7 @@ TestProxy::TestProxy(sdbus::IConnection& connection, ServiceName destination, Ob : ProxyInterfaces(connection, std::move(destination), std::move(objectPath)) { getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct>& strct){ this->onSignalWithoutRegistration(strct); }); + getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional e, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(e, wrongParameter); }); registerProxy(); } @@ -96,6 +98,12 @@ void TestProxy::onSignalWithoutRegistration(const sdbus::Struct e, int wrongParameter) +{ + m_errorFromSignal = e; + m_gotSignalWithTypeMismatch = true; +} + void TestProxy::onDoOperationReply(uint32_t returnValue, std::optional error) const { if (m_DoOperationClientSideAsyncReplyHandler) diff --git a/tests/integrationtests/TestProxy.h b/tests/integrationtests/TestProxy.h index ea058f58..2e72a142 100644 --- a/tests/integrationtests/TestProxy.h +++ b/tests/integrationtests/TestProxy.h @@ -94,6 +94,7 @@ class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integratio void onSignalWithVariant(const sdbus::Variant& aVariant) override; void onSignalWithoutRegistration(const sdbus::Struct>& strct); + void onSignalWithErrorAndTypeMismatch(std::optional e, int wrongParameter); void onDoOperationReply(uint32_t returnValue, std::optional error) const; // Signals of standard D-Bus interfaces @@ -130,6 +131,8 @@ class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integratio double m_variantFromSignal{}; std::atomic m_gotSignalWithSignature{false}; std::map m_signatureFromSignal; + std::atomic m_gotSignalWithTypeMismatch{false}; + std::optional m_errorFromSignal; std::function err)> m_DoOperationClientSideAsyncReplyHandler; std::function&, const std::vector&)> m_onPropertiesChangedHandler; From cc6d9936ab1a3a3bfd66c4a57db3e98c1f81c300 Mon Sep 17 00:00:00 2001 From: Oliver Facklam Date: Wed, 27 May 2026 18:36:16 +0200 Subject: [PATCH 2/4] fix: prevent ambiguous call by calling signal handler with std::nullopt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the following compilation error: ```cpp /sdbus-cpp/include/sdbus-c++/ConvenienceApiClasses.inl:465:29: error: call of overloaded ‘apply(const sdbus::test::TestProxy::TestProxy(sdbus::IConnection&, sdbus::ServiceName, sdbus::ObjectPath)::, int)>&, , sdbus::tuple_of_function_input_arg_types_t, int)> >&)’ is ambiguous 465 | sdbus::apply(callback, {}, signalArgs); | ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~ /sdbus-cpp/include/sdbus-c++/TypeTraits.h:682:30: note: candidate: ‘constexpr decltype(auto) sdbus::apply(Function&&, Result&&, Tuple&&) [with Function = const test::TestProxy::TestProxy(sdbus::IConnection&, sdbus::ServiceName, sdbus::ObjectPath)::, int)>&; Tuple = std::tuple&; Args = {}]’ 682 | constexpr decltype(auto) apply(Function&& fun, Result&& res, Tuple&& tuple) | ^~~~~ /sdbus-cpp/include/sdbus-c++/TypeTraits.h:693:20: note: candidate: ‘decltype(auto) sdbus::apply(Function&&, std::optional, Tuple&&) [with Function = const test::TestProxy::TestProxy(sdbus::IConnection&, sdbus::ServiceName, sdbus::ObjectPath)::, int)>&; Tuple = std::tuple&]’ 693 | decltype(auto) apply(Function&& fun, std::optional err, Tuple&& tuple) | ^~~~~ ``` --- include/sdbus-c++/ConvenienceApiClasses.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sdbus-c++/ConvenienceApiClasses.inl b/include/sdbus-c++/ConvenienceApiClasses.inl index 4ce3f436..729bc369 100644 --- a/include/sdbus-c++/ConvenienceApiClasses.inl +++ b/include/sdbus-c++/ConvenienceApiClasses.inl @@ -462,7 +462,7 @@ namespace sdbus { } // Invoke callback with no error and input arguments from the tuple. - sdbus::apply(callback, {}, signalArgs); + sdbus::apply(callback, std::nullopt, signalArgs); } else { From 5310568828d88c5b66c87f204c6431104a270f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Mon, 8 Jun 2026 16:08:06 +0200 Subject: [PATCH 3/4] fix: mark unused parameter --- tests/integrationtests/TestProxy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integrationtests/TestProxy.cpp b/tests/integrationtests/TestProxy.cpp index 44000622..3ae90c93 100644 --- a/tests/integrationtests/TestProxy.cpp +++ b/tests/integrationtests/TestProxy.cpp @@ -98,7 +98,7 @@ void TestProxy::onSignalWithoutRegistration(const sdbus::Struct e, int wrongParameter) +void TestProxy::onSignalWithErrorAndTypeMismatch(std::optional e, [[maybe_unused]] int wrongParameter) { m_errorFromSignal = e; m_gotSignalWithTypeMismatch = true; From 3bc9f96695fd0541e7a183ac6bee057484506bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Mon, 8 Jun 2026 19:54:17 +0200 Subject: [PATCH 4/4] fix: clang-tidy warnings --- docs/using-sdbus-c++.md | 6 +++--- include/sdbus-c++/ConvenienceApiClasses.inl | 8 ++++---- tests/integrationtests/TestProxy.cpp | 8 ++++---- tests/integrationtests/TestProxy.h | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/using-sdbus-c++.md b/docs/using-sdbus-c++.md index 0a1c59d1..373d3244 100644 --- a/docs/using-sdbus-c++.md +++ b/docs/using-sdbus-c++.md @@ -611,10 +611,10 @@ We recommend that sdbus-c++ users prefer the convenience API to the lower level, > **_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(std::optional e, int wrongParameter) +> void onConcatenated(std::optional err, int wrongParameter) > { -> assert(e.has_value()); -> assert(e->getMessage() == "Failed to deserialize a int32 value"); +> assert(err.has_value()); +> assert(err->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 `std::optional`-based callback variant and inspect the error to check if that's the cause of your problems. diff --git a/include/sdbus-c++/ConvenienceApiClasses.inl b/include/sdbus-c++/ConvenienceApiClasses.inl index 729bc369..0f122093 100644 --- a/include/sdbus-c++/ConvenienceApiClasses.inl +++ b/include/sdbus-c++/ConvenienceApiClasses.inl @@ -317,11 +317,11 @@ namespace sdbus { { reply >> args; } - catch (const Error& e) + catch (const Error& err) { // 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, err, args); return; } } @@ -453,11 +453,11 @@ namespace sdbus { { signal >> signalArgs; } - catch (const Error& e) + catch (const Error& err) { // 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, err, signalArgs); return; } diff --git a/tests/integrationtests/TestProxy.cpp b/tests/integrationtests/TestProxy.cpp index 3ae90c93..ba979176 100644 --- a/tests/integrationtests/TestProxy.cpp +++ b/tests/integrationtests/TestProxy.cpp @@ -45,7 +45,7 @@ TestProxy::TestProxy(ServiceName destination, ObjectPath objectPath) : ProxyInterfaces(std::move(destination), std::move(objectPath)) { getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct>& strct){ this->onSignalWithoutRegistration(strct); }); - getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional e, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(e, wrongParameter); }); + getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional err, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(std::move(err), wrongParameter); }); registerProxy(); } @@ -61,7 +61,7 @@ TestProxy::TestProxy(sdbus::IConnection& connection, ServiceName destination, Ob : ProxyInterfaces(connection, std::move(destination), std::move(objectPath)) { getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct>& strct){ this->onSignalWithoutRegistration(strct); }); - getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional e, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(e, wrongParameter); }); + getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional err, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(std::move(err), wrongParameter); }); registerProxy(); } @@ -98,9 +98,9 @@ void TestProxy::onSignalWithoutRegistration(const sdbus::Struct e, [[maybe_unused]] int wrongParameter) +void TestProxy::onSignalWithErrorAndTypeMismatch(std::optional err, [[maybe_unused]] int wrongParameter) { - m_errorFromSignal = e; + m_errorFromSignal = std::move(err); m_gotSignalWithTypeMismatch = true; } diff --git a/tests/integrationtests/TestProxy.h b/tests/integrationtests/TestProxy.h index 2e72a142..d3ce0cd0 100644 --- a/tests/integrationtests/TestProxy.h +++ b/tests/integrationtests/TestProxy.h @@ -94,7 +94,7 @@ class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integratio void onSignalWithVariant(const sdbus::Variant& aVariant) override; void onSignalWithoutRegistration(const sdbus::Struct>& strct); - void onSignalWithErrorAndTypeMismatch(std::optional e, int wrongParameter); + void onSignalWithErrorAndTypeMismatch(std::optional err, int wrongParameter); void onDoOperationReply(uint32_t returnValue, std::optional error) const; // Signals of standard D-Bus interfaces