diff --git a/include/exec/at_coroutine_exit.hpp b/include/exec/at_coroutine_exit.hpp index 19c264839..4e0376aa5 100644 --- a/include/exec/at_coroutine_exit.hpp +++ b/include/exec/at_coroutine_exit.hpp @@ -291,7 +291,7 @@ namespace experimental::execution public: template requires __callable<__decay_t<_Action>, __decay_t<_Ts>...> - auto operator()(_Action&& __action, _Ts&&... __ts) const -> __task<_Ts...> + auto operator()(_Action&& __action, _Ts&&... __ts) const -> __task<__decay_t<_Ts>...> { return __co_impl(static_cast<_Action&&>(__action), static_cast<_Ts&&>(__ts)...); } diff --git a/include/exec/on_coro_disposition.hpp b/include/exec/on_coro_disposition.hpp index d5a76c6de..5a0ddd56f 100644 --- a/include/exec/on_coro_disposition.hpp +++ b/include/exec/on_coro_disposition.hpp @@ -226,7 +226,7 @@ namespace experimental::execution public: template requires __callable<__decay_t<_Action>, __decay_t<_Ts>...> - auto operator()(_Action&& __action, _Ts&&... __ts) const -> __task<_Ts...> + auto operator()(_Action&& __action, _Ts&&... __ts) const -> __task<__decay_t<_Ts>...> { return __impl(static_cast<_Action&&>(__action), static_cast<_Ts&&>(__ts)...); } diff --git a/test/exec/test_at_coroutine_exit.cpp b/test/exec/test_at_coroutine_exit.cpp index ca6184cf3..49262b257 100644 --- a/test/exec/test_at_coroutine_exit.cpp +++ b/test/exec/test_at_coroutine_exit.cpp @@ -25,6 +25,10 @@ # include "../test_common/require_terminate.hpp" # include "../test_common/schedulers.hpp" +# include +# include +# include + using namespace exec; using STDEXEC::sync_wait; @@ -271,6 +275,46 @@ namespace i *= i; } + template + auto test_cleanup_action_with_lvalue_arguments(Cleanup cleanup, + int& result, + task_disposition disposition) -> task + { + int argument = 3; + int const const_argument = 4; + auto action = [&result](int&& i, int&& j, std::unique_ptr&& p) -> task + { + result = i + j + *p; + co_return; + }; + + auto payload = std::make_unique(5); + auto&& [i, j, p] = co_await cleanup(action, argument, const_argument, std::move(payload)); + CHECK_FALSE(payload); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v&>); + CHECK(i == 3); + CHECK(j == 4); + CHECK(*p == 5); + CHECK(result == 0); + + argument = 100; + CHECK(i == 3); + i = 6; + j = 7; + *p = 8; + + if (disposition == task_disposition::stopped) + { + co_await stop(); + } + else if (disposition == task_disposition::failed) + { + throw 42; + } + } + auto with_continuation(int& result, task next) -> task { co_await std::move(next); @@ -498,6 +542,45 @@ namespace REQUIRE(result == 10); } + TEMPLATE_TEST_CASE("CoroutineCleanupWithLvalueArguments", + "[task][at_coroutine_exit]", + decltype(at_coroutine_exit), + decltype(on_coroutine_succeeded), + decltype(on_coroutine_stopped), + decltype(on_coroutine_failed)) + { + int result = 0; + constexpr bool always_runs = std::is_same_v; + + SECTION("success") + { + REQUIRE(sync_wait(test_cleanup_action_with_lvalue_arguments(TestType{}, + result, + task_disposition::succeeded))); + constexpr bool runs = always_runs + || std::is_same_v; + CHECK(result == (runs ? 21 : 0)); + } + + SECTION("stop") + { + CHECK_FALSE(sync_wait( + test_cleanup_action_with_lvalue_arguments(TestType{}, result, task_disposition::stopped))); + constexpr bool runs = always_runs || std::is_same_v; + CHECK(result == (runs ? 21 : 0)); + } + + SECTION("error") + { + CHECK_THROWS_AS( + sync_wait( + test_cleanup_action_with_lvalue_arguments(TestType{}, result, task_disposition::failed)), + int); + constexpr bool runs = always_runs || std::is_same_v; + CHECK(result == (runs ? 21 : 0)); + } + } + # ifdef REQUIRE_TERMINATE TEST_CASE("CancelInCleanupActionCallsTerminate", "[task][at_coroutine_exit]")