From c3281d4038df80c728340c4206321450723ede5b Mon Sep 17 00:00:00 2001 From: Benedek Kupper Date: Fri, 24 Jul 2026 17:11:15 +0200 Subject: [PATCH] callback: forward rvalue args Preserve value category in callback::builder::run so services taking T&& receive rvalues instead of lvalues. Add a regression test for callback::service. Signed-off-by: Benedek Kupper --- include/nexus/callback.hpp | 4 +++- test/nexus/callback.cpp | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/include/nexus/callback.hpp b/include/nexus/callback.hpp index 9b4fe704..9bcce0ef 100644 --- a/include/nexus/callback.hpp +++ b/include/nexus/callback.hpp @@ -35,7 +35,9 @@ template , typename... ArgTypes> struct builder { constexpr static void run(ArgTypes... args) { constexpr auto b = BuilderValue::value; [&](std::index_sequence) { - (invoke_with(b.funcs[stdx::index], args...), ...); + (invoke_with(b.funcs[stdx::index], + std::forward(args)...), + ...); }(std::make_index_sequence{}); } diff --git a/test/nexus/callback.cpp b/test/nexus/callback.cpp index 4d4b00eb..b53055cd 100644 --- a/test/nexus/callback.cpp +++ b/test/nexus/callback.cpp @@ -144,6 +144,34 @@ TEST_CASE("callback with args with no extensions", "[callback]") { template static stdx::tuple callback_args{}; +struct MoveOnlyPayload { + int value{}; + + MoveOnlyPayload() = default; + explicit MoveOnlyPayload(int v) : value{v} {} + MoveOnlyPayload(MoveOnlyPayload &&) = default; + MoveOnlyPayload &operator=(MoveOnlyPayload &&) = default; + MoveOnlyPayload(MoveOnlyPayload const &) = delete; + MoveOnlyPayload &operator=(MoveOnlyPayload const &) = delete; +}; + +static int moved_payload_value = 0; + +struct CallbackWithRvalueRefArg { + using service = callback::service; + + static void extension(MoveOnlyPayload &&payload) { + is_callback_invoked<3> = true; + moved_payload_value = payload.value; + payload.value = -1; + } + + constexpr static auto value = []() { + auto const builder = cib::builder_t{}; + return builder.add(extension); + }(); +}; + struct CallbackWithArgsWithMultipleExtensions { using service = callback::service; @@ -204,3 +232,17 @@ TEST_CASE("callback with args with multiple extensions", "[callback]") { built_callback)); } } + +TEST_CASE("callback forwards rvalue reference args", "[callback]") { + constexpr auto built_callback = build(); + + is_callback_invoked<3> = false; + moved_payload_value = 0; + + MoveOnlyPayload payload{42}; + built_callback(std::move(payload)); + + REQUIRE(is_callback_invoked<3>); + REQUIRE(moved_payload_value == 42); + REQUIRE(payload.value == -1); +}