From 556885b3bc3af950a8ad16e9dc652e75691122ff Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Fri, 10 Apr 2026 19:29:52 +0200 Subject: [PATCH 01/14] Disable clang-format There is no rules-file, and having clang-format enabled will have it use whatever the default rule-set is (I think the rules used for clang itself), causing massive changes on every save. --- .clang-format | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..68924642 --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +# TODO: Introduce common clang-format rule set +DisableFormat: true +SortIncludes: false From aa668c60f586955c9a2c74dca6313d5abd30a4aa Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Fri, 10 Apr 2026 19:43:55 +0200 Subject: [PATCH 02/14] Use a move-only bus factory in Connection::openBus --- src/Connection.cpp | 6 +++--- src/Connection.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Connection.cpp b/src/Connection.cpp index 694a69dc..3c3deb4a 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -58,9 +58,9 @@ namespace sdbus::internal { -Connection::Connection(std::unique_ptr&& interface, const BusFactory& busFactory) +Connection::Connection(std::unique_ptr&& interface, BusFactory&& busFactory) : sdbus_(std::move(interface)) - , bus_(openBus(busFactory)) + , bus_(openBus(std::move(busFactory))) { assert(sdbus_ != nullptr); } @@ -739,7 +739,7 @@ sd_bus_message* Connection::createErrorReplyMessage(sd_bus_message* sdbusMsg, co return sdbusErrorReply; } -Connection::BusPtr Connection::openBus(const BusFactory& busFactory) +Connection::BusPtr Connection::openBus(BusFactory&& busFactory) { sd_bus* bus{}; const int r = busFactory(&bus); diff --git a/src/Connection.h b/src/Connection.h index 0e63aa6b..70ecbb2b 100644 --- a/src/Connection.h +++ b/src/Connection.h @@ -181,11 +181,11 @@ namespace sdbus::internal { sd_bus_message* createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error) override; private: - using BusFactory = std::function; + using BusFactory = std::move_only_function; using BusPtr = std::unique_ptr>; - Connection(std::unique_ptr&& interface, const BusFactory& busFactory); + Connection(std::unique_ptr&& interface, BusFactory&& busFactory); - BusPtr openBus(const std::function& busFactory); + BusPtr openBus(std::move_only_function&& busFactory); BusPtr openPseudoBus(); void finishHandshake(sd_bus* bus); bool waitForNextEvent(); From 3e884016b22248e0bcd08ded987efc3ed5b91291 Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Fri, 10 Apr 2026 19:52:46 +0200 Subject: [PATCH 03/14] BusPtr deleters can also be move-only --- src/Connection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Connection.h b/src/Connection.h index 70ecbb2b..27722082 100644 --- a/src/Connection.h +++ b/src/Connection.h @@ -182,7 +182,7 @@ namespace sdbus::internal { private: using BusFactory = std::move_only_function; - using BusPtr = std::unique_ptr>; + using BusPtr = std::unique_ptr>; Connection(std::unique_ptr&& interface, BusFactory&& busFactory); BusPtr openBus(std::move_only_function&& busFactory); From 9791697b385398e7d565b240629c3f6c82fd89bd Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Fri, 10 Apr 2026 19:57:04 +0200 Subject: [PATCH 04/14] Proxy deleter can be move-only too --- src/Proxy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Proxy.h b/src/Proxy.h index 0d496413..83e95ee5 100644 --- a/src/Proxy.h +++ b/src/Proxy.h @@ -104,7 +104,7 @@ namespace sdbus::internal { friend PendingAsyncCall; - std::unique_ptr> connection_; + std::unique_ptr> connection_; ServiceName destination_; ObjectPath objectPath_; From d34d6385a300b04027c72fe6e4e0280261c68665 Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Fri, 10 Apr 2026 21:33:34 +0200 Subject: [PATCH 05/14] Slots take a move-only callback for cleanup --- include/sdbus-c++/TypeTraits.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index 5e6e9af0..164d20d3 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -82,7 +82,7 @@ namespace sdbus { using property_get_callback = std::function; // Type-erased RAII-style handle to callbacks/subscriptions registered to sdbus-c++ - using Slot = std::unique_ptr>; + using Slot = std::unique_ptr>; // Tag specifying that an owning handle (so-called slot) of the logical resource shall be provided to the client struct return_slot_t { explicit return_slot_t() = default; }; From d1f4c3566a6eb715a607dedb53f48acb8e1a5525 Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Fri, 10 Apr 2026 21:55:14 +0200 Subject: [PATCH 06/14] Convert async_reply_handler, signal_handler and message_handler --- include/sdbus-c++/ConvenienceApiClasses.inl | 2 +- include/sdbus-c++/TypeTraits.h | 9 ++++++++- src/Object.cpp | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/sdbus-c++/ConvenienceApiClasses.inl b/include/sdbus-c++/ConvenienceApiClasses.inl index 4ce3f436..2f5c4894 100644 --- a/include/sdbus-c++/ConvenienceApiClasses.inl +++ b/include/sdbus-c++/ConvenienceApiClasses.inl @@ -304,7 +304,7 @@ namespace sdbus { template inline async_reply_handler AsyncMethodInvoker::makeAsyncReplyHandler(Function&& callback) { - return [callback = std::forward(callback)](MethodReply reply, std::optional error) + return [callback = std::forward(callback)](MethodReply reply, std::optional error) mutable { // Create a tuple of callback input arguments' types, which will be used // as a storage for the argument values deserialized from the message. diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index 164d20d3..7cd42393 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -75,7 +75,7 @@ namespace sdbus { // Callbacks from sdbus-c++ using method_callback = std::function; - using async_reply_handler = std::function error)>; + using async_reply_handler = std::move_only_function error)>; using signal_handler = std::function; using message_handler = std::function; using property_set_callback = std::function; @@ -380,6 +380,9 @@ namespace sdbus { static constexpr bool is_trivial_dbus_type = false; }; + template + concept valid_signature = signature_of::is_valid; + // To simplify conversions of arrays to C strings template constexpr auto as_null_terminated(std::array arr) @@ -508,6 +511,10 @@ namespace sdbus { struct function_traits> : function_traits {}; + template + struct function_traits> : function_traits + {}; + template constexpr auto is_async_method_v = function_traits::is_async; diff --git a/src/Object.cpp b/src/Object.cpp index 7ddd1857..0092006d 100644 --- a/src/Object.cpp +++ b/src/Object.cpp @@ -340,7 +340,7 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, assert(methodItem != nullptr); assert(methodItem->callback); - auto ok = invokeHandlerAndCatchErrors([&](){ methodItem->callback(std::move(message)); }, retError); + auto ok = invokeHandlerAndCatchErrors([&] mutable { methodItem->callback(std::move(message)); }, retError); return ok ? 1 : -1; } @@ -392,7 +392,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/ auto value = Message::Factory::create(sdbusValue, &vtable->object->connection_); - auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->setCallback(std::move(value)); }, retError); + auto ok = invokeHandlerAndCatchErrors([&] mutable { propertyItem->setCallback(std::move(value)); }, retError); return ok ? 1 : -1; } From 6725a548cfe35e4f34cb6086720f6b2e549bfde2 Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Sat, 11 Apr 2026 11:20:21 +0200 Subject: [PATCH 07/14] No longer need shared_ptr for std::future either --- include/sdbus-c++/ConvenienceApiClasses.inl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/sdbus-c++/ConvenienceApiClasses.inl b/include/sdbus-c++/ConvenienceApiClasses.inl index 2f5c4894..25e061e2 100644 --- a/include/sdbus-c++/ConvenienceApiClasses.inl +++ b/include/sdbus-c++/ConvenienceApiClasses.inl @@ -334,18 +334,18 @@ namespace sdbus { template std::future> AsyncMethodInvoker::getResultAsFuture() { - auto promise = std::make_shared>>(); - auto future = promise->get_future(); + std::promise> promise{}; + auto future = promise.get_future(); - uponReplyInvoke([promise = std::move(promise)](std::optional error, Args... args) + uponReplyInvoke([promise = std::move(promise)](std::optional error, Args... args) mutable { if (!error) if constexpr (!std::is_void_v>) - promise->set_value({std::move(args)...}); + promise.set_value({std::move(args)...}); else - promise->set_value(); + promise.set_value(); else - promise->set_exception(std::make_exception_ptr(*std::move(error))); + promise.set_exception(std::make_exception_ptr(*std::move(error))); }); // Will be std::future for no D-Bus method return value From 523dc645230c3a8d0c8633984727238bd8aa9aeb Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Sun, 19 Apr 2026 21:34:11 +0200 Subject: [PATCH 08/14] Make all callbacks std::move_only_function, one-shot is rvalue-invocable All callbacks are now std::move_only_functions. Internally, we allow them to be `const` where possible, so they can be invoked from const-qualified member functions. One-shot callbacks are marked so they can only be invoked on rvalue functions, which more clearly signifies that they have to be moved out to be invoked (and thus cannot be invoked again). --- include/sdbus-c++/ConvenienceApiClasses.inl | 2 +- include/sdbus-c++/IConnection.h | 14 ++-- include/sdbus-c++/IObject.h | 14 +++- include/sdbus-c++/IProxy.h | 24 +++--- include/sdbus-c++/TypeTraits.h | 22 ++++-- include/sdbus-c++/VTableItems.h | 42 +++++------ include/sdbus-c++/VTableItems.inl | 84 ++++++++++----------- src/Connection.cpp | 12 +-- src/Connection.h | 14 ++-- src/Proxy.cpp | 20 ++--- src/Proxy.h | 16 ++-- tests/integrationtests/DBusMethodsTests.cpp | 16 ++-- 12 files changed, 150 insertions(+), 130 deletions(-) diff --git a/include/sdbus-c++/ConvenienceApiClasses.inl b/include/sdbus-c++/ConvenienceApiClasses.inl index 25e061e2..d94ab309 100644 --- a/include/sdbus-c++/ConvenienceApiClasses.inl +++ b/include/sdbus-c++/ConvenienceApiClasses.inl @@ -436,7 +436,7 @@ namespace sdbus { template inline signal_handler SignalSubscriber::makeSignalHandler(Function&& callback) { - return [callback = std::forward(callback)](Signal signal) + return [callback = std::forward(callback)](Signal signal) mutable { // Create a tuple of callback input arguments' types, which will be used // as a storage for the argument values deserialized from the signal message. diff --git a/include/sdbus-c++/IConnection.h b/include/sdbus-c++/IConnection.h index f9758c71..02c591f6 100644 --- a/include/sdbus-c++/IConnection.h +++ b/include/sdbus-c++/IConnection.h @@ -277,7 +277,7 @@ namespace sdbus { * * @throws sdbus::Error in case of failure */ - virtual void addMatch(const std::string& match, message_handler callback) = 0; + virtual void addMatch(const std::string& match, message_handler&& callback) = 0; /*! * @brief Installs a match rule for messages received on this bus connection @@ -300,14 +300,14 @@ namespace sdbus { * * @throws sdbus::Error in case of failure */ - [[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback, return_slot_t) = 0; + [[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler&& callback, return_slot_t) = 0; /*! * @brief Asynchronously installs a floating match rule for messages received on this bus connection * * @param[in] match Match expression to filter incoming D-Bus message * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule - * @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule + * @param[in] installCallback One-shot handler invoked once with the broker's response to the install request * * This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously, * in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response. @@ -323,14 +323,14 @@ namespace sdbus { * * @throws sdbus::Error in case of failure */ - virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0; + virtual void addMatchAsync(const std::string& match, message_handler&& callback, match_install_handler&& installCallback) = 0; /*! * @brief Asynchronously installs a match rule for messages received on this bus connection * * @param[in] match Match expression to filter incoming D-Bus message * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule - * @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule + * @param[in] installCallback One-shot handler invoked once with the broker's response to the install request * @return RAII-style slot handle representing the ownership of the subscription * * This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously, @@ -347,8 +347,8 @@ namespace sdbus { * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual Slot addMatchAsync( const std::string& match - , message_handler callback - , message_handler installCallback + , message_handler&& callback + , match_install_handler&& installCallback , return_slot_t ) = 0; /*! diff --git a/include/sdbus-c++/IObject.h b/include/sdbus-c++/IObject.h index d2f3002c..94e874ec 100644 --- a/include/sdbus-c++/IObject.h +++ b/include/sdbus-c++/IObject.h @@ -442,13 +442,23 @@ namespace sdbus { template void IObject::addVTable(InterfaceName interfaceName, VTableItems&&... items) { - addVTable(std::move(interfaceName), {std::forward(items)...}); + // Built via emplace_back rather than a braced initializer_list: items hold move-only + // callbacks, and initializer_list elements are const (copy-only). + std::vector vtable; + vtable.reserve(sizeof...(items)); + (vtable.emplace_back(std::forward(items)), ...); + addVTable(std::move(interfaceName), std::move(vtable)); } template VTableAdder IObject::addVTable(VTableItems&&... items) { - return addVTable(std::vector{std::forward(items)...}); + // Built via emplace_back rather than a braced initializer_list: items hold move-only + // callbacks, and initializer_list elements are const (copy-only). + std::vector vtable; + vtable.reserve(sizeof...(items)); + (vtable.emplace_back(std::forward(items)), ...); + return addVTable(std::move(vtable)); } inline VTableAdder IObject::addVTable(std::vector vtable) diff --git a/include/sdbus-c++/IProxy.h b/include/sdbus-c++/IProxy.h index ce00c890..c4c0f71a 100644 --- a/include/sdbus-c++/IProxy.h +++ b/include/sdbus-c++/IProxy.h @@ -455,7 +455,7 @@ namespace sdbus { * * @throws sdbus::Error in case of failure */ - virtual PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) = 0; + virtual PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler&& asyncReplyCallback) = 0; /*! * @brief Calls method on the D-Bus object asynchronously @@ -480,7 +480,7 @@ namespace sdbus { * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , return_slot_t ) = 0; /*! @@ -506,7 +506,7 @@ namespace sdbus { * @throws sdbus::Error in case of failure */ virtual PendingAsyncCall callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , uint64_t timeout ) = 0; /*! @@ -533,7 +533,7 @@ namespace sdbus { * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , uint64_t timeout , return_slot_t ) = 0; @@ -542,7 +542,7 @@ namespace sdbus { */ template PendingAsyncCall callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , const std::chrono::duration& timeout ); /*! @@ -550,7 +550,7 @@ namespace sdbus { */ template [[nodiscard]] Slot callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , const std::chrono::duration& timeout , return_slot_t ); @@ -669,7 +669,7 @@ namespace sdbus { */ virtual void registerSignalHandler( const InterfaceName& interfaceName , const SignalName& signalName - , signal_handler signalHandler ) = 0; + , signal_handler&& signalHandler ) = 0; /*! * @brief Registers a handler for the desired signal emitted by the D-Bus object @@ -689,7 +689,7 @@ namespace sdbus { */ [[nodiscard]] virtual Slot registerSignalHandler( const InterfaceName& interfaceName , const SignalName& signalName - , signal_handler signalHandler + , signal_handler&& signalHandler , return_slot_t ) = 0; protected: // Internal API for efficiency reasons used by high-level API helper classes @@ -700,10 +700,10 @@ namespace sdbus { [[nodiscard]] virtual MethodCall createMethodCall(const char* interfaceName, const char* methodName) const = 0; virtual void registerSignalHandler( const char* interfaceName , const char* signalName - , signal_handler signalHandler ) = 0; + , signal_handler&& signalHandler ) = 0; [[nodiscard]] virtual Slot registerSignalHandler( const char* interfaceName , const char* signalName - , signal_handler signalHandler + , signal_handler&& signalHandler , return_slot_t ) = 0; }; @@ -759,7 +759,7 @@ namespace sdbus { template inline PendingAsyncCall IProxy::callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , const std::chrono::duration& timeout ) { auto microsecs = std::chrono::duration_cast(timeout); @@ -768,7 +768,7 @@ namespace sdbus { template inline Slot IProxy::callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , const std::chrono::duration& timeout , return_slot_t ) { diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index 7cd42393..5569c392 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -74,12 +74,22 @@ namespace sdbus { namespace sdbus { // Callbacks from sdbus-c++ - using method_callback = std::function; - using async_reply_handler = std::move_only_function error)>; - using signal_handler = std::function; - using message_handler = std::function; - using property_set_callback = std::function; - using property_get_callback = std::function; + // Server-side vtable entries: looked up through a const vtable, so invocation is const. + // Handlers should be pure w.r.t. their own state (capture shared state by ref/shared_ptr). + using method_callback = std::move_only_function; + using property_set_callback = std::move_only_function; + using property_get_callback = std::move_only_function; + + // Client-side reactive handlers: stored non-const, stateful captures are permitted + // (counters, rolling averages, debouncers). + using signal_handler = std::move_only_function; + using message_handler = std::move_only_function; + + // One-shot callbacks: invoked exactly once (async reply arrives / match rule installed). + // Rvalue-qualified so the library must std::move to invoke, making the call-once contract + // visible at the call site and letting handlers move-consume captured resources. + using async_reply_handler = std::move_only_function error) &&>; + using match_install_handler = std::move_only_function; // Type-erased RAII-style handle to callbacks/subscriptions registered to sdbus-c++ using Slot = std::unique_ptr>; diff --git a/include/sdbus-c++/VTableItems.h b/include/sdbus-c++/VTableItems.h index ae57a00a..c1976cf1 100644 --- a/include/sdbus-c++/VTableItems.h +++ b/include/sdbus-c++/VTableItems.h @@ -38,14 +38,14 @@ namespace sdbus { struct MethodVTableItem { - template MethodVTableItem& implementedAs(Function&& callback); - MethodVTableItem& withInputParamNames(std::vector names); - template MethodVTableItem& withInputParamNames(String... names); - MethodVTableItem& withOutputParamNames(std::vector names); - template MethodVTableItem& withOutputParamNames(String... names); - MethodVTableItem& markAsDeprecated(); - MethodVTableItem& markAsPrivileged(); - MethodVTableItem& withNoReply(); + template MethodVTableItem&& implementedAs(Function&& callback) &&; + MethodVTableItem&& withInputParamNames(std::vector names) &&; + template MethodVTableItem&& withInputParamNames(String... names) &&; + MethodVTableItem&& withOutputParamNames(std::vector names) &&; + template MethodVTableItem&& withOutputParamNames(String... names) &&; + MethodVTableItem&& markAsDeprecated() &&; + MethodVTableItem&& markAsPrivileged() &&; + MethodVTableItem&& withNoReply() &&; MethodName name; Signature inputSignature; @@ -61,10 +61,10 @@ namespace sdbus { struct SignalVTableItem { - template SignalVTableItem& withParameters(); - template SignalVTableItem& withParameters(std::vector names); - template SignalVTableItem& withParameters(String... names); - SignalVTableItem& markAsDeprecated(); + template SignalVTableItem&& withParameters() &&; + template SignalVTableItem&& withParameters(std::vector names) &&; + template SignalVTableItem&& withParameters(String... names) &&; + SignalVTableItem&& markAsDeprecated() &&; SignalName name; Signature signature; @@ -77,11 +77,11 @@ namespace sdbus { struct PropertyVTableItem { - template PropertyVTableItem& withGetter(Function&& callback); - template PropertyVTableItem& withSetter(Function&& callback); - PropertyVTableItem& markAsDeprecated(); - PropertyVTableItem& markAsPrivileged(); - PropertyVTableItem& withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior); + template PropertyVTableItem&& withGetter(Function&& callback) &&; + template PropertyVTableItem&& withSetter(Function&& callback) &&; + PropertyVTableItem&& markAsDeprecated() &&; + PropertyVTableItem&& markAsPrivileged() &&; + PropertyVTableItem&& withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior) &&; PropertyName name; Signature signature; @@ -95,10 +95,10 @@ namespace sdbus { struct InterfaceFlagsVTableItem { - InterfaceFlagsVTableItem& markAsDeprecated(); - InterfaceFlagsVTableItem& markAsPrivileged(); - InterfaceFlagsVTableItem& withNoReplyMethods(); - InterfaceFlagsVTableItem& withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior); + InterfaceFlagsVTableItem&& markAsDeprecated() &&; + InterfaceFlagsVTableItem&& markAsPrivileged() &&; + InterfaceFlagsVTableItem&& withNoReplyMethods() &&; + InterfaceFlagsVTableItem&& withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior) &&; Flags flags; }; diff --git a/include/sdbus-c++/VTableItems.inl b/include/sdbus-c++/VTableItems.inl index ee4e4e32..4fde1d68 100644 --- a/include/sdbus-c++/VTableItems.inl +++ b/include/sdbus-c++/VTableItems.inl @@ -40,7 +40,7 @@ namespace sdbus { /*** -------------------- ***/ template - MethodVTableItem& MethodVTableItem::implementedAs(Function&& callback) + MethodVTableItem&& MethodVTableItem::implementedAs(Function&& callback) && { inputSignature = signature_of_function_input_arguments_v; outputSignature = signature_of_function_output_arguments_v; @@ -71,58 +71,58 @@ namespace sdbus { } }; - return *this; + return std::move(*this); } - inline MethodVTableItem& MethodVTableItem::withInputParamNames(std::vector names) + inline MethodVTableItem&& MethodVTableItem::withInputParamNames(std::vector names) && { inputParamNames = std::move(names); - return *this; + return std::move(*this); } template - inline MethodVTableItem& MethodVTableItem::withInputParamNames(String... names) + inline MethodVTableItem&& MethodVTableItem::withInputParamNames(String... names) && { static_assert(std::conjunction_v...>, "Parameter names must be (convertible to) strings"); - return withInputParamNames({names...}); + return std::move(*this).withInputParamNames({names...}); } - inline MethodVTableItem& MethodVTableItem::withOutputParamNames(std::vector names) + inline MethodVTableItem&& MethodVTableItem::withOutputParamNames(std::vector names) && { outputParamNames = std::move(names); - return *this; + return std::move(*this); } template - inline MethodVTableItem& MethodVTableItem::withOutputParamNames(String... names) + inline MethodVTableItem&& MethodVTableItem::withOutputParamNames(String... names) && { static_assert(std::conjunction_v...>, "Parameter names must be (convertible to) strings"); - return withOutputParamNames({names...}); + return std::move(*this).withOutputParamNames({names...}); } - inline MethodVTableItem& MethodVTableItem::markAsDeprecated() + inline MethodVTableItem&& MethodVTableItem::markAsDeprecated() && { flags.set(Flags::DEPRECATED); - return *this; + return std::move(*this); } - inline MethodVTableItem& MethodVTableItem::markAsPrivileged() + inline MethodVTableItem&& MethodVTableItem::markAsPrivileged() && { flags.set(Flags::PRIVILEGED); - return *this; + return std::move(*this); } - inline MethodVTableItem& MethodVTableItem::withNoReply() + inline MethodVTableItem&& MethodVTableItem::withNoReply() && { flags.set(Flags::METHOD_NO_REPLY); - return *this; + return std::move(*this); } inline MethodVTableItem registerMethod(MethodName methodName) @@ -140,35 +140,35 @@ namespace sdbus { /*** -------------------- ***/ template - inline SignalVTableItem& SignalVTableItem::withParameters() + inline SignalVTableItem&& SignalVTableItem::withParameters() && { signature = signature_of_function_input_arguments_v; - return *this; + return std::move(*this); } template - inline SignalVTableItem& SignalVTableItem::withParameters(std::vector names) + inline SignalVTableItem&& SignalVTableItem::withParameters(std::vector names) && { paramNames = std::move(names); - return withParameters(); + return std::move(*this).template withParameters(); } template - inline SignalVTableItem& SignalVTableItem::withParameters(String... names) + inline SignalVTableItem&& SignalVTableItem::withParameters(String... names) && { static_assert(std::conjunction_v...>, "Parameter names must be (convertible to) strings"); static_assert(sizeof...(Args) == sizeof...(String), "Numbers of signal parameters and their names don't match"); - return withParameters({names...}); + return std::move(*this).template withParameters({names...}); } - inline SignalVTableItem& SignalVTableItem::markAsDeprecated() + inline SignalVTableItem&& SignalVTableItem::markAsDeprecated() && { flags.set(Flags::DEPRECATED); - return *this; + return std::move(*this); } inline SignalVTableItem registerSignal(SignalName signalName) @@ -186,7 +186,7 @@ namespace sdbus { /*** -------------------- ***/ template - inline PropertyVTableItem& PropertyVTableItem::withGetter(Function&& callback) + inline PropertyVTableItem&& PropertyVTableItem::withGetter(Function&& callback) && { static_assert(function_argument_count_v == 0, "Property getter function must not take any arguments"); static_assert(!std::is_void_v>, "Property getter function must return property value"); @@ -200,11 +200,11 @@ namespace sdbus { reply << callback(); }; - return *this; + return std::move(*this); } template - inline PropertyVTableItem& PropertyVTableItem::withSetter(Function&& callback) + inline PropertyVTableItem&& PropertyVTableItem::withSetter(Function&& callback) && { static_assert(function_argument_count_v == 1, "Property setter function must take one parameter - the property value"); static_assert(std::is_void_v>, "Property setter function must not return any value"); @@ -225,28 +225,28 @@ namespace sdbus { callback(property); }; - return *this; + return std::move(*this); } - inline PropertyVTableItem& PropertyVTableItem::markAsDeprecated() + inline PropertyVTableItem&& PropertyVTableItem::markAsDeprecated() && { flags.set(Flags::DEPRECATED); - return *this; + return std::move(*this); } - inline PropertyVTableItem& PropertyVTableItem::markAsPrivileged() + inline PropertyVTableItem&& PropertyVTableItem::markAsPrivileged() && { flags.set(Flags::PRIVILEGED); - return *this; + return std::move(*this); } - inline PropertyVTableItem& PropertyVTableItem::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior) + inline PropertyVTableItem&& PropertyVTableItem::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior) && { flags.set(behavior); - return *this; + return std::move(*this); } inline PropertyVTableItem registerProperty(PropertyName propertyName) @@ -263,32 +263,32 @@ namespace sdbus { /*** Interface Flags VTable Item ***/ /*** --------------------------- ***/ - inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsDeprecated() + inline InterfaceFlagsVTableItem&& InterfaceFlagsVTableItem::markAsDeprecated() && { flags.set(Flags::DEPRECATED); - return *this; + return std::move(*this); } - inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsPrivileged() + inline InterfaceFlagsVTableItem&& InterfaceFlagsVTableItem::markAsPrivileged() && { flags.set(Flags::PRIVILEGED); - return *this; + return std::move(*this); } - inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withNoReplyMethods() + inline InterfaceFlagsVTableItem&& InterfaceFlagsVTableItem::withNoReplyMethods() && { flags.set(Flags::METHOD_NO_REPLY); - return *this; + return std::move(*this); } - inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior) + inline InterfaceFlagsVTableItem&& InterfaceFlagsVTableItem::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior) && { flags.set(behavior); - return *this; + return std::move(*this); } inline InterfaceFlagsVTableItem setInterfaceFlags() diff --git a/src/Connection.cpp b/src/Connection.cpp index 3c3deb4a..7da864c2 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -238,12 +238,12 @@ uint64_t Connection::getMethodCallTimeout() const return timeout; } -void Connection::addMatch(const std::string& match, message_handler callback) +void Connection::addMatch(const std::string& match, message_handler&& callback) { floatingMatchRules_.push_back(addMatch(match, std::move(callback), return_slot)); } -Slot Connection::addMatch(const std::string& match, message_handler callback, return_slot_t) +Slot Connection::addMatch(const std::string& match, message_handler&& callback, return_slot_t) { SDBUS_THROW_ERROR_IF(!callback, "Invalid match callback handler provided", EINVAL); @@ -258,14 +258,14 @@ Slot Connection::addMatch(const std::string& match, message_handler callback, re return {matchInfo.release(), [](void *ptr){ delete static_cast(ptr); }}; // NOLINT(cppcoreguidelines-owning-memory) } -void Connection::addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) +void Connection::addMatchAsync(const std::string& match, message_handler&& callback, match_install_handler&& installCallback) { floatingMatchRules_.push_back(addMatchAsync(match, std::move(callback), std::move(installCallback), return_slot)); } Slot Connection::addMatchAsync( const std::string& match - , message_handler callback - , message_handler installCallback + , message_handler&& callback + , match_install_handler&& installCallback , return_slot_t ) { SDBUS_THROW_ERROR_IF(!callback, "Invalid match callback handler provided", EINVAL); @@ -920,7 +920,7 @@ int Connection::sdbus_match_install_callback(sd_bus_message *sdbusMessage, void auto message = Message::Factory::create(sdbusMessage, &matchInfo->connection); - auto ok = invokeHandlerAndCatchErrors([&](){ matchInfo->installCallback(std::move(message)); }, retError); + auto ok = invokeHandlerAndCatchErrors([&](){ std::move(matchInfo->installCallback)(std::move(message)); }, retError); return ok ? 0 : -1; } diff --git a/src/Connection.h b/src/Connection.h index 27722082..e565752a 100644 --- a/src/Connection.h +++ b/src/Connection.h @@ -111,12 +111,12 @@ namespace sdbus::internal { void setMethodCallTimeout(uint64_t timeout) override; [[nodiscard]] uint64_t getMethodCallTimeout() const override; - void addMatch(const std::string& match, message_handler callback) override; - [[nodiscard]] Slot addMatch(const std::string& match, message_handler callback, return_slot_t) override; - void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) override; + void addMatch(const std::string& match, message_handler&& callback) override; + [[nodiscard]] Slot addMatch(const std::string& match, message_handler&& callback, return_slot_t) override; + void addMatchAsync(const std::string& match, message_handler&& callback, match_install_handler&& installCallback) override; [[nodiscard]] Slot addMatchAsync( const std::string& match - , message_handler callback - , message_handler installCallback + , message_handler&& callback + , match_install_handler&& installCallback , return_slot_t ) override; void attachSdEventLoop(sd_event *event, int priority) override; @@ -233,8 +233,8 @@ namespace sdbus::internal { struct MatchInfo { - message_handler callback; - message_handler installCallback; + message_handler&& callback; + match_install_handler&& installCallback; Connection& connection; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) Slot slot; }; diff --git a/src/Proxy.cpp b/src/Proxy.cpp index 1c5377f3..c4af2a52 100644 --- a/src/Proxy.cpp +++ b/src/Proxy.cpp @@ -117,17 +117,17 @@ MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout) return message.send(timeout); } -PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) +PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler&& asyncReplyCallback) { return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0); } -Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, return_slot_t) +Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler&& asyncReplyCallback, return_slot_t) { return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0, return_slot); } -PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) +PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler&& asyncReplyCallback, uint64_t timeout) { SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL); @@ -145,7 +145,7 @@ PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_h return PendingAsyncCall{asyncCallInfoWeakPtr}; } -Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout, return_slot_t) +Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler&& asyncReplyCallback, uint64_t timeout, return_slot_t) { SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL); @@ -209,14 +209,14 @@ Awaitable Proxy::callMethodAsync(const MethodCall& message, uint64_ void Proxy::registerSignalHandler( const InterfaceName& interfaceName , const SignalName& signalName - , signal_handler signalHandler ) + , signal_handler&& signalHandler ) { Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler)); } void Proxy::registerSignalHandler( const char* interfaceName , const char* signalName - , signal_handler signalHandler ) + , signal_handler&& signalHandler ) { auto slot = Proxy::registerSignalHandler(interfaceName, signalName, std::move(signalHandler), return_slot); @@ -225,7 +225,7 @@ void Proxy::registerSignalHandler( const char* interfaceName Slot Proxy::registerSignalHandler( const InterfaceName& interfaceName , const SignalName& signalName - , signal_handler signalHandler + , signal_handler&& signalHandler , return_slot_t ) { return Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler), return_slot); @@ -233,7 +233,7 @@ Slot Proxy::registerSignalHandler( const InterfaceName& interfaceName Slot Proxy::registerSignalHandler( const char* interfaceName , const char* signalName - , signal_handler signalHandler + , signal_handler&& signalHandler , return_slot_t ) { SDBUS_CHECK_INTERFACE_NAME(interfaceName); @@ -296,12 +296,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) { - asyncCallInfo->callback(std::move(message), {}); + std::move(asyncCallInfo->callback)(std::move(message), {}); } else { Error exception(Error::Name{error->name}, error->message); - asyncCallInfo->callback(std::move(message), std::move(exception)); + std::move(asyncCallInfo->callback)(std::move(message), std::move(exception)); } }, retError); diff --git a/src/Proxy.h b/src/Proxy.h index 83e95ee5..d76c82df 100644 --- a/src/Proxy.h +++ b/src/Proxy.h @@ -60,15 +60,15 @@ namespace sdbus::internal { [[nodiscard]] MethodCall createMethodCall(const char* interfaceName, const char* methodName) const override; MethodReply callMethod(const MethodCall& message) override; MethodReply callMethod(const MethodCall& message, uint64_t timeout) override; - PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) override; + PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler&& asyncReplyCallback) override; Slot callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , return_slot_t ) override; PendingAsyncCall callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , uint64_t timeout ) override; Slot callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback + , async_reply_handler&& asyncReplyCallback , uint64_t timeout , return_slot_t ) override; std::future callMethodAsync(const MethodCall& message, with_future_t) override; @@ -80,17 +80,17 @@ namespace sdbus::internal { void registerSignalHandler( const InterfaceName& interfaceName , const SignalName& signalName - , signal_handler signalHandler ) override; + , signal_handler&& signalHandler ) override; void registerSignalHandler( const char* interfaceName , const char* signalName - , signal_handler signalHandler ) override; + , signal_handler&& signalHandler ) override; Slot registerSignalHandler( const InterfaceName& interfaceName , const SignalName& signalName - , signal_handler signalHandler + , signal_handler&& signalHandler , return_slot_t ) override; Slot registerSignalHandler( const char* interfaceName , const char* signalName - , signal_handler signalHandler + , signal_handler&& signalHandler , return_slot_t ) override; void unregister() override; diff --git a/tests/integrationtests/DBusMethodsTests.cpp b/tests/integrationtests/DBusMethodsTests.cpp index 345cf4e4..23bc0099 100644 --- a/tests/integrationtests/DBusMethodsTests.cpp +++ b/tests/integrationtests/DBusMethodsTests.cpp @@ -350,10 +350,10 @@ TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime) { auto& object = this->m_adaptor->getObject(); sdbus::InterfaceName const interfaceName{"org.sdbuscpp.integrationtests2"}; - auto vtableSlot = object.addVTable( interfaceName - , { sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; }) - , sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; }) } - , sdbus::return_slot ); + std::vector vtable; + vtable.emplace_back(sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; })); + vtable.emplace_back(sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; })); + auto vtableSlot = object.addVTable(interfaceName, std::move(vtable), sdbus::return_slot); // The new remote vtable is registered as long as we keep vtableSlot, so remote method calls now should pass auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH); @@ -368,10 +368,10 @@ TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime) auto& object = this->m_adaptor->getObject(); sdbus::InterfaceName const interfaceName{"org.sdbuscpp.integrationtests2"}; - auto vtableSlot = object.addVTable( interfaceName - , { sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; }) - , sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; }) } - , sdbus::return_slot ); + std::vector vtable; + vtable.emplace_back(sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; })); + vtable.emplace_back(sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; })); + auto vtableSlot = object.addVTable(interfaceName, std::move(vtable), sdbus::return_slot); vtableSlot.reset(); // Letting the slot go means letting go the associated vtable registration // No such remote D-Bus method under given interface exists anymore... From f92865b308d11c253c72be5a5d81de8f881f4e7e Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Mon, 20 Apr 2026 11:31:45 +0200 Subject: [PATCH 09/14] Fix issues found by clang-tidy --- src/Connection.cpp | 2 +- src/Connection.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Connection.cpp b/src/Connection.cpp index 7da864c2..bf3b11b8 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -742,7 +742,7 @@ sd_bus_message* Connection::createErrorReplyMessage(sd_bus_message* sdbusMsg, co Connection::BusPtr Connection::openBus(BusFactory&& busFactory) { sd_bus* bus{}; - const int r = busFactory(&bus); + const int r = std::move(busFactory)(&bus); SDBUS_THROW_ERROR_IF(r < 0, "Failed to open bus", -r); BusPtr busPtr{bus, [this](sd_bus* bus){ return sdbus_->sd_bus_flush_close_unref(bus); }}; diff --git a/src/Connection.h b/src/Connection.h index e565752a..2925f383 100644 --- a/src/Connection.h +++ b/src/Connection.h @@ -181,11 +181,11 @@ namespace sdbus::internal { sd_bus_message* createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error) override; private: - using BusFactory = std::move_only_function; + using BusFactory = std::move_only_function; using BusPtr = std::unique_ptr>; Connection(std::unique_ptr&& interface, BusFactory&& busFactory); - BusPtr openBus(std::move_only_function&& busFactory); + BusPtr openBus(BusFactory&& busFactory); BusPtr openPseudoBus(); void finishHandshake(sd_bus* bus); bool waitForNextEvent(); @@ -233,8 +233,8 @@ namespace sdbus::internal { struct MatchInfo { - message_handler&& callback; - match_install_handler&& installCallback; + message_handler callback; + match_install_handler installCallback; Connection& connection; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) Slot slot; }; From 5ff86cab17beea327e2ccff2606cc201992e3d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Mon, 20 Apr 2026 14:46:30 +0200 Subject: [PATCH 10/14] fix: solve std::move clang-tidy issue --- tests/integrationtests/DBusMethodsTests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integrationtests/DBusMethodsTests.cpp b/tests/integrationtests/DBusMethodsTests.cpp index 23bc0099..3fd30388 100644 --- a/tests/integrationtests/DBusMethodsTests.cpp +++ b/tests/integrationtests/DBusMethodsTests.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include From a031176e1a91968825ec044c3853cd5da50da217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Mon, 20 Apr 2026 15:14:45 +0200 Subject: [PATCH 11/14] refactor: use promise directly with mutable lambda --- src/Proxy.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Proxy.cpp b/src/Proxy.cpp index c4af2a52..0f6e8135 100644 --- a/src/Proxy.cpp +++ b/src/Proxy.cpp @@ -166,15 +166,15 @@ std::future Proxy::callMethodAsync(const MethodCall& message, with_ std::future Proxy::callMethodAsync(const MethodCall& message, uint64_t timeout, with_future_t) { - auto promise = std::make_shared>(); - auto future = promise->get_future(); + std::promise promise; + auto future = promise.get_future(); - async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply reply, std::optional error) noexcept + async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply reply, std::optional error) mutable noexcept { if (!error) - promise->set_value(std::move(reply)); + promise.set_value(std::move(reply)); else - promise->set_exception(std::make_exception_ptr(*std::move(error))); + promise.set_exception(std::make_exception_ptr(*std::move(error))); }; (void)Proxy::callMethodAsync(message, std::move(asyncReplyCallback), timeout); From 122c12185fe63ab59679df653a0d856bd36037ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Mon, 20 Apr 2026 15:29:15 +0200 Subject: [PATCH 12/14] refactor: have invokeHandlerAndCatchErrors() take callable by const ref --- src/Object.cpp | 4 ++-- src/Utils.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Object.cpp b/src/Object.cpp index 0092006d..a5f5f44a 100644 --- a/src/Object.cpp +++ b/src/Object.cpp @@ -340,7 +340,7 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, assert(methodItem != nullptr); assert(methodItem->callback); - auto ok = invokeHandlerAndCatchErrors([&] mutable { methodItem->callback(std::move(message)); }, retError); + auto ok = invokeHandlerAndCatchErrors([&] { methodItem->callback(std::move(message)); }, retError); return ok ? 1 : -1; } @@ -392,7 +392,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/ auto value = Message::Factory::create(sdbusValue, &vtable->object->connection_); - auto ok = invokeHandlerAndCatchErrors([&] mutable { propertyItem->setCallback(std::move(value)); }, retError); + auto ok = invokeHandlerAndCatchErrors([&] { propertyItem->setCallback(std::move(value)); }, retError); return ok ? 1 : -1; } diff --git a/src/Utils.h b/src/Utils.h index 4164d723..923a54a2 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -57,7 +57,7 @@ namespace sdbus::internal { template - bool invokeHandlerAndCatchErrors(Callable callable, sd_bus_error *retError) + bool invokeHandlerAndCatchErrors(const Callable& callable, sd_bus_error *retError) { try { From 61a139a3a08dc477c4dd7fdcec67b4c1aff7b8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Mon, 20 Apr 2026 15:59:35 +0200 Subject: [PATCH 13/14] refactor: use higher-level addVTable in tests --- tests/integrationtests/DBusMethodsTests.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/integrationtests/DBusMethodsTests.cpp b/tests/integrationtests/DBusMethodsTests.cpp index 3fd30388..1f9c66f4 100644 --- a/tests/integrationtests/DBusMethodsTests.cpp +++ b/tests/integrationtests/DBusMethodsTests.cpp @@ -351,10 +351,9 @@ TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime) { auto& object = this->m_adaptor->getObject(); sdbus::InterfaceName const interfaceName{"org.sdbuscpp.integrationtests2"}; - std::vector vtable; - vtable.emplace_back(sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; })); - vtable.emplace_back(sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; })); - auto vtableSlot = object.addVTable(interfaceName, std::move(vtable), sdbus::return_slot); + auto vtableSlot = object.addVTable( sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; }) + , sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; }) + ).forInterface(interfaceName, sdbus::return_slot); // The new remote vtable is registered as long as we keep vtableSlot, so remote method calls now should pass auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH); @@ -368,11 +367,10 @@ TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime) { auto& object = this->m_adaptor->getObject(); sdbus::InterfaceName const interfaceName{"org.sdbuscpp.integrationtests2"}; + auto vtableSlot = object.addVTable( sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; }) + , sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; }) + ).forInterface(interfaceName, sdbus::return_slot); - std::vector vtable; - vtable.emplace_back(sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; })); - vtable.emplace_back(sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; })); - auto vtableSlot = object.addVTable(interfaceName, std::move(vtable), sdbus::return_slot); vtableSlot.reset(); // Letting the slot go means letting go the associated vtable registration // No such remote D-Bus method under given interface exists anymore... From 66bd6f5bb8768755776e57b1af2ce767887fa1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Tue, 28 Apr 2026 10:01:03 +0200 Subject: [PATCH 14/14] refactor: use non-const server-side callbacks --- include/sdbus-c++/TypeTraits.h | 12 ++++-------- include/sdbus-c++/VTableItems.inl | 6 +++--- src/Object.cpp | 12 ++++++------ src/Object.h | 4 ++-- src/Utils.h | 4 ++-- tests/integrationtests/DBusMethodsTests.cpp | 1 - 6 files changed, 17 insertions(+), 22 deletions(-) diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index 5569c392..4735ff5c 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -74,14 +74,10 @@ namespace sdbus { namespace sdbus { // Callbacks from sdbus-c++ - // Server-side vtable entries: looked up through a const vtable, so invocation is const. - // Handlers should be pure w.r.t. their own state (capture shared state by ref/shared_ptr). - using method_callback = std::move_only_function; - using property_set_callback = std::move_only_function; - using property_get_callback = std::move_only_function; - - // Client-side reactive handlers: stored non-const, stateful captures are permitted - // (counters, rolling averages, debouncers). + // Repeatable server-side and client-side callbacks + using method_callback = std::move_only_function; + using property_set_callback = std::move_only_function; + using property_get_callback = std::move_only_function; using signal_handler = std::move_only_function; using message_handler = std::move_only_function; diff --git a/include/sdbus-c++/VTableItems.inl b/include/sdbus-c++/VTableItems.inl index 4fde1d68..05f376e0 100644 --- a/include/sdbus-c++/VTableItems.inl +++ b/include/sdbus-c++/VTableItems.inl @@ -44,7 +44,7 @@ namespace sdbus { { inputSignature = signature_of_function_input_arguments_v; outputSignature = signature_of_function_output_arguments_v; - callbackHandler = [callback = std::forward(callback)](MethodCall call) + callbackHandler = [callback = std::forward(callback)](MethodCall call) mutable { // Create a tuple of callback input arguments' types, which will be used // as a storage for the argument values deserialized from the message. @@ -194,7 +194,7 @@ namespace sdbus { if (signature.empty()) signature = signature_of_function_output_arguments_v; - getter = [callback = std::forward(callback)](PropertyGetReply& reply) + getter = [callback = std::forward(callback)](PropertyGetReply& reply) mutable { // Get the propety value and serialize it into the pre-constructed reply message reply << callback(); @@ -212,7 +212,7 @@ namespace sdbus { if (signature.empty()) signature = signature_of_function_input_arguments_v; - setter = [callback = std::forward(callback)](PropertySetCall call) + setter = [callback = std::forward(callback)](PropertySetCall call) mutable { // Default-construct property value using property_type = function_argument_t; diff --git a/src/Object.cpp b/src/Object.cpp index a5f5f44a..ef4d03cf 100644 --- a/src/Object.cpp +++ b/src/Object.cpp @@ -300,7 +300,7 @@ void Object::finalizeSdBusVTable(std::vector& vtable) vtable.push_back(createSdBusVTableEndItem()); } -const Object::VTable::MethodItem* Object::findMethod(const VTable& vtable, std::string_view methodName) +Object::VTable::MethodItem* Object::findMethod(VTable& vtable, std::string_view methodName) { auto it = std::lower_bound(vtable.methods.begin(), vtable.methods.end(), methodName, [](const auto& methodItem, const auto& methodName) { @@ -310,7 +310,7 @@ const Object::VTable::MethodItem* Object::findMethod(const VTable& vtable, std:: return it != vtable.methods.end() && it->name == methodName ? &*it : nullptr; } -const Object::VTable::PropertyItem* Object::findProperty(const VTable& vtable, std::string_view propertyName) +Object::VTable::PropertyItem* Object::findProperty(VTable& vtable, std::string_view propertyName) { auto it = std::lower_bound(vtable.properties.begin(), vtable.properties.end(), propertyName, [](const auto& propertyItem, const auto& propertyName) { @@ -336,7 +336,7 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, auto message = Message::Factory::create(sdbusMessage, &vtable->object->connection_); - const auto* methodItem = findMethod(*vtable, message.getMemberName()); + auto* methodItem = findMethod(*vtable, message.getMemberName()); assert(methodItem != nullptr); assert(methodItem->callback); @@ -357,7 +357,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/ assert(vtable != nullptr); assert(vtable->object != nullptr); - const auto* propertyItem = findProperty(*vtable, property); + auto* propertyItem = findProperty(*vtable, property); assert(propertyItem != nullptr); // Getter may be empty - the case of "write-only" property @@ -369,7 +369,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/ auto reply = Message::Factory::create(sdbusReply, &vtable->object->connection_); - auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->getCallback(reply); }, retError); + auto ok = invokeHandlerAndCatchErrors([&] { propertyItem->getCallback(reply); }, retError); return ok ? 1 : -1; } @@ -386,7 +386,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/ assert(vtable != nullptr); assert(vtable->object != nullptr); - const auto* propertyItem = findProperty(*vtable, property); + auto* propertyItem = findProperty(*vtable, property); assert(propertyItem != nullptr); assert(propertyItem->setCallback); diff --git a/src/Object.h b/src/Object.h index 4dafa6b3..1dccac45 100644 --- a/src/Object.h +++ b/src/Object.h @@ -145,8 +145,8 @@ namespace sdbus::internal { static void writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector& vtable); static void finalizeSdBusVTable(std::vector& vtable); - static const VTable::MethodItem* findMethod(const VTable& vtable, std::string_view methodName); - static const VTable::PropertyItem* findProperty(const VTable& vtable, std::string_view propertyName); + static VTable::MethodItem* findMethod(VTable& vtable, std::string_view methodName); + static VTable::PropertyItem* findProperty(VTable& vtable, std::string_view propertyName); static std::string paramNamesToString(const std::vector& paramNames); diff --git a/src/Utils.h b/src/Utils.h index 923a54a2..a6bfd523 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -57,11 +57,11 @@ namespace sdbus::internal { template - bool invokeHandlerAndCatchErrors(const Callable& callable, sd_bus_error *retError) + bool invokeHandlerAndCatchErrors(Callable&& callable, sd_bus_error *retError) { try { - callable(); + std::forward(callable)(); } catch (const Error& e) { diff --git a/tests/integrationtests/DBusMethodsTests.cpp b/tests/integrationtests/DBusMethodsTests.cpp index 1f9c66f4..a0fd0846 100644 --- a/tests/integrationtests/DBusMethodsTests.cpp +++ b/tests/integrationtests/DBusMethodsTests.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include