From b828823fc0dbe6ebee517a9ea26a067e2fea67c0 Mon Sep 17 00:00:00 2001 From: Cra3z Date: Mon, 21 Sep 2026 13:27:45 +0800 Subject: [PATCH 1/5] Fix `on` and `continues_on` --- .../beman/execution/detail/continues_on.hpp | 17 ++- include/beman/execution/detail/on.hpp | 100 ++++++++++-------- 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/include/beman/execution/detail/continues_on.hpp b/include/beman/execution/detail/continues_on.hpp index 30242eb4..39477c2a 100644 --- a/include/beman/execution/detail/continues_on.hpp +++ b/include/beman/execution/detail/continues_on.hpp @@ -50,6 +50,7 @@ import beman.execution.detail.sender; import beman.execution.detail.sender_adaptor_closure; import beman.execution.detail.sender_for; import beman.execution.detail.sender_in; +import beman.execution.detail.sends_stopped; import beman.execution.detail.set_error; import beman.execution.detail.set_stopped; import beman.execution.detail.set_value; @@ -84,6 +85,7 @@ import beman.execution.detail.start; #include #include #include +#include #include #include #include @@ -119,13 +121,18 @@ struct continues_on_t { struct get_signatures< ::beman::execution::detail::basic_sender<::beman::execution::detail::continues_on_t, Scheduler, Sender>, Env> { - using scheduler_sender = ::beman::execution::schedule_result_t; + using scheduler_sender = ::beman::execution::schedule_result_t; + using additional_signatures = ::std::conditional_t< + ::beman::execution::sends_stopped, + ::beman::execution::completion_signatures<::beman::execution::set_error_t(::std::exception_ptr), + ::beman::execution::set_stopped_t()>, + ::beman::execution::completion_signatures<::beman::execution::set_error_t(::std::exception_ptr)>>; template using as_set_error = ::beman::execution::completion_signatures<::beman::execution::set_error_t(E)...>; - using type = ::beman::execution::detail::meta::combine< - decltype(::beman::execution::get_completion_signatures()), - ::beman::execution::error_types_of_t, - ::beman::execution::completion_signatures<::beman::execution::set_error_t(::std::exception_ptr)>>; + using type = ::beman::execution::detail::meta::unique<::beman::execution::detail::meta::combine< + decltype(::beman::execution::get_completion_signatures()), + ::beman::execution::error_types_of_t, + additional_signatures>>; }; template diff --git a/include/beman/execution/detail/on.hpp b/include/beman/execution/detail/on.hpp index fec57ba4..2724da98 100644 --- a/include/beman/execution/detail/on.hpp +++ b/include/beman/execution/detail/on.hpp @@ -60,49 +60,27 @@ import beman.execution.detail.transform_sender; namespace beman::execution::detail { struct on_t : ::beman::execution::sender_adaptor_closure { - template - struct env_needs_get_start_scheduler { - using sender_concept = ::beman::execution::sender_tag; - template - static constexpr auto get_completion_signatures() { - return env_needs_get_start_scheduler{}; - } - }; - template <::beman::execution::detail::sender_for OutSndr, typename Env> - auto transform_sender(::beman::execution::set_value_t, OutSndr&& out_sndr, const Env& env) const + static auto transform_sender(::beman::execution::set_value_t, OutSndr&& out_sndr, const Env& env) -> decltype(auto) { - struct not_a_scheduler_t {}; auto&& data = out_sndr.template get<1>(); auto&& child = out_sndr.template get<2>(); if constexpr (::beman::execution::scheduler) { - auto sch{::beman::execution::detail::query_with_default( - ::beman::execution::get_start_scheduler, env, not_a_scheduler_t{})}; - if constexpr (::std::same_as) { - return env_needs_get_start_scheduler{}; - } else { - return ::beman::execution::continues_on( - ::beman::execution::starts_on(::beman::execution::detail::forward_like(data), - ::beman::execution::detail::forward_like(child)), - ::std::move(sch)); - } + auto start_sch = ::beman::execution::get_start_scheduler(env); + return ::beman::execution::continues_on( + ::beman::execution::starts_on(::beman::execution::detail::forward_like(data), + ::beman::execution::detail::forward_like(child)), + ::std::move(start_sch)); } else { auto& [sch, closure] = data; - auto orig_sch{::beman::execution::detail::call_with_default( - ::beman::execution::get_completion_scheduler<::beman::execution::set_value_t>, - not_a_scheduler_t{}, - ::beman::execution::get_env(child), - env)}; - - if constexpr (::std::same_as) { - return env_needs_get_start_scheduler{}; - } else { - return ::beman::execution::continues_on( - ::beman::execution::detail::forward_like(closure)(::beman::execution::continues_on( - ::beman::execution::detail::forward_like(child), sch)), - orig_sch); - } + auto orig_sch = ::beman::execution::get_completion_scheduler<::beman::execution::set_value_t>( + ::beman::execution::get_env(child), env); + + return ::beman::execution::continues_on( + ::beman::execution::detail::forward_like(closure)( + ::beman::execution::continues_on(::beman::execution::detail::forward_like(child), sch)), + orig_sch); } } @@ -116,7 +94,7 @@ struct on_t : ::beman::execution::sender_adaptor_closure { ::beman::execution::detail::is_sender_adaptor_closure Closure> requires ::beman::execution::detail::is_sender_adaptor_closure auto operator()(Sndr&&, Sch&&, Closure&&) const -> void = - BEMAN_EXECUTION_DELETE("on(sch, sndr) requires that sndr isn't both a sender and sender adaptor closure"); + BEMAN_EXECUTION_DELETE("on(sndr, sch, closure) requires that sndr isn't both a sender and sender adaptor closure"); template <::beman::execution::scheduler Sch, ::beman::execution::sender Sndr> auto operator()(Sch&& sch, Sndr&& sndr) const { @@ -138,15 +116,51 @@ struct on_t : ::beman::execution::sender_adaptor_closure { } template struct get_signatures; - template - struct get_signatures<::beman::execution::detail::basic_sender<::beman::execution::detail::on_t, Data, Sndr>, - Env...> { - using type = ::beman::execution::completion_signatures_of_t; + + /// for `on(scheduler, sender)` + template + struct get_signatures<::beman::execution::detail::basic_sender<::beman::execution::detail::on_t, Sched, Child>, + Env> { + + static consteval auto get() noexcept + requires ::std::invocable<::beman::execution::get_start_scheduler_t, Env> + { + using transformed_sndr = decltype(on_t::transform_sender( + ::beman::execution::set_value, + ::std::declval< + ::beman::execution::detail::basic_sender<::beman::execution::detail::on_t, Sched, Child>>(), + ::std::declval())); + return ::beman::execution::get_completion_signatures(); + } + }; + + /// for `on(sender, scheduler, closure)` + template + struct get_signatures< + ::beman::execution::detail::basic_sender<::beman::execution::detail::on_t, + ::beman::execution::detail::product_type, + Child>, + Env> { + static constexpr auto get() noexcept + requires ::std::invocable<::beman::execution::get_completion_scheduler_t<::beman::execution::set_value_t>, + ::beman::execution::env_of_t, + Env> + { + using transformed_sndr = decltype(on_t::transform_sender( + ::beman::execution::set_value, + ::std::declval< + ::beman::execution::detail::basic_sender<::beman::execution::detail::on_t, + ::beman::execution::detail::product_type, + Child>>(), + ::std::declval())); + return ::beman::execution::get_completion_signatures(); + } }; - template - static consteval auto get_completion_signatures() { - return typename get_signatures, Env...>::type{}; + template + requires requires { get_signatures, Env>::get(); } + static consteval auto get_completion_signatures() noexcept { + return get_signatures, Env>::get(); } }; From 802c9ccdf6f6a8711ab490138e1a262cae0dc659 Mon Sep 17 00:00:00 2001 From: Cra3z Date: Mon, 21 Sep 2026 18:13:28 +0800 Subject: [PATCH 2/5] Fix some errors --- include/beman/execution/detail/on.hpp | 135 +++++++++++++++++- include/beman/execution/detail/starts_on.hpp | 34 ++--- .../execution/exec-inline-scheduler.test.cpp | 3 +- tests/beman/execution/exec-on.test.cpp | 77 ++++++++-- .../exec-parallel-scheduler.test.cpp | 11 ++ 5 files changed, 220 insertions(+), 40 deletions(-) diff --git a/include/beman/execution/detail/on.hpp b/include/beman/execution/detail/on.hpp index 2724da98..17cfef6f 100644 --- a/include/beman/execution/detail/on.hpp +++ b/include/beman/execution/detail/on.hpp @@ -8,6 +8,7 @@ #ifdef BEMAN_HAS_IMPORT_STD import std; #else +#include #include #endif #ifdef BEMAN_HAS_MODULES @@ -15,9 +16,13 @@ import beman.execution.detail.basic_sender; import beman.execution.detail.call_with_default; import beman.execution.detail.completion_signatures_of_t; import beman.execution.detail.continues_on; +import beman.execution.detail.default_impls; import beman.execution.detail.default_domain; +import beman.execution.detail.env_of_t; import beman.execution.detail.forward_like; import beman.execution.detail.fwd_env; +import beman.execution.detail.get_completion_domain; +import beman.execution.detail.get_completion_signatures; import beman.execution.detail.get_completion_scheduler; import beman.execution.detail.get_domain; import beman.execution.detail.get_env; @@ -38,10 +43,16 @@ import beman.execution.detail.transform_sender; #include #include #include +#include #include +#include #include #include +#include +#include +#include #include +#include #include #include #include @@ -60,7 +71,21 @@ import beman.execution.detail.transform_sender; namespace beman::execution::detail { struct on_t : ::beman::execution::sender_adaptor_closure { + template + static auto dry_run(OutSndr&& out_sndr) noexcept { + auto& [_, data, child] = out_sndr; + if constexpr (::beman::execution::scheduler) { + return ::std::bool_constant<::std::invocable<::beman::execution::get_start_scheduler_t, Env>>{}; + } else { + ::std::bool_constant< + ::std::invocable<::beman::execution::get_completion_scheduler_t<::beman::execution::set_value_t>, + ::beman::execution::env_of_t, + Env>>{}; + } + } + template <::beman::execution::detail::sender_for OutSndr, typename Env> + requires(decltype(on_t::dry_run(::std::declval()))::value) static auto transform_sender(::beman::execution::set_value_t, OutSndr&& out_sndr, const Env& env) -> decltype(auto) { auto&& data = out_sndr.template get<1>(); @@ -84,6 +109,25 @@ struct on_t : ::beman::execution::sender_adaptor_closure { } } + struct impls_for : ::beman::execution::detail::default_impls { + struct get_attrs_impl { + template + auto operator()(const Sched&, const Child& child) const noexcept { + using ChildAttrs = ::std::remove_cvref_t<::beman::execution::env_of_t>; + return attrs1{::beman::execution::get_env(child)}; + } + + template + auto operator()(const ::beman::execution::detail::product_type&, + const Child& child) const noexcept { + using ChildAttrs = ::std::remove_cvref_t<::beman::execution::env_of_t>; + return attrs2{::beman::execution::get_env(child)}; + } + }; + + static constexpr auto get_attrs{get_attrs_impl{}}; + }; + template <::beman::execution::scheduler Sch, ::beman::execution::sender Sndr> requires ::beman::execution::detail::is_sender_adaptor_closure auto operator()(Sch&&, Sndr&&) const -> void = @@ -93,8 +137,8 @@ struct on_t : ::beman::execution::sender_adaptor_closure { ::beman::execution::sender Sndr, ::beman::execution::detail::is_sender_adaptor_closure Closure> requires ::beman::execution::detail::is_sender_adaptor_closure - auto operator()(Sndr&&, Sch&&, Closure&&) const -> void = - BEMAN_EXECUTION_DELETE("on(sndr, sch, closure) requires that sndr isn't both a sender and sender adaptor closure"); + auto operator()(Sndr&&, Sch&&, Closure&&) const -> void = BEMAN_EXECUTION_DELETE( + "on(sndr, sch, closure) requires that sndr isn't both a sender and sender adaptor closure"); template <::beman::execution::scheduler Sch, ::beman::execution::sender Sndr> auto operator()(Sch&& sch, Sndr&& sndr) const { @@ -114,6 +158,87 @@ struct on_t : ::beman::execution::sender_adaptor_closure { return ::beman::execution::detail::make_sender_adaptor( *this, ::std::forward(sch), ::std::forward(closure)); } + + private: + template + struct is_completion_query : ::std::false_type {}; + + template + struct is_completion_query<::beman::execution::get_completion_scheduler_t> : ::std::true_type {}; + + template + struct is_completion_query<::beman::execution::get_completion_domain_t> : ::std::true_type {}; + + template + struct attrs_base { + template + requires(!is_completion_query<::std::remove_cvref_t>::value) && + requires(const ChildAttrs& child_attrs, Query query, Args&&... args) { + query(::beman::execution::detail::fwd_env(child_attrs), ::std::forward(args)...); + } + auto query(Query query, Args&&... args) const noexcept { + return query(::beman::execution::detail::fwd_env(this->child_attrs), ::std::forward(args)...); + } + + ChildAttrs child_attrs; + }; + + template + struct attrs1 : attrs_base { + using attrs_base::query; + + template + requires requires(const Env& env) { + ::beman::execution::get_completion_scheduler(::beman::execution::get_start_scheduler(env), + ::beman::execution::detail::fwd_env(env)); + } + auto query(::beman::execution::get_completion_scheduler_t, const Env& env) const noexcept { + auto sched = ::beman::execution::get_start_scheduler(env); + return ::beman::execution::get_completion_scheduler(sched, ::beman::execution::detail::fwd_env(env)); + } + + template + requires requires(const Env& env) { + ::beman::execution::get_completion_domain(::beman::execution::get_start_scheduler(env), + ::beman::execution::detail::fwd_env(env)); + } + auto query(::beman::execution::get_completion_domain_t, const Env& env) const noexcept { + auto sched = ::beman::execution::get_start_scheduler(env); + return ::beman::execution::get_completion_domain(sched, ::beman::execution::detail::fwd_env(env)); + } + }; + + template + struct attrs2 : attrs_base { + using attrs_base::query; + + template + auto original_sched(const Env& env) const noexcept { + return ::beman::execution::get_completion_scheduler<::beman::execution::set_value_t>(this->child_attrs, + env); + } + + template + requires requires(const attrs2& self, const Env& env) { + ::beman::execution::get_completion_scheduler(self.original_sched(env), + ::beman::execution::detail::fwd_env(env)); + } + auto query(::beman::execution::get_completion_scheduler_t, const Env& env) const noexcept { + return ::beman::execution::get_completion_scheduler(this->original_sched(env), + ::beman::execution::detail::fwd_env(env)); + } + + template + requires requires(const attrs2& self, const Env& env) { + ::beman::execution::get_completion_domain(self.original_sched(env), + ::beman::execution::detail::fwd_env(env)); + } + auto query(::beman::execution::get_completion_domain_t, const Env& env) const noexcept { + return ::beman::execution::get_completion_domain(this->original_sched(env), + ::beman::execution::detail::fwd_env(env)); + } + }; + template struct get_signatures; @@ -121,8 +246,7 @@ struct on_t : ::beman::execution::sender_adaptor_closure { template struct get_signatures<::beman::execution::detail::basic_sender<::beman::execution::detail::on_t, Sched, Child>, Env> { - - static consteval auto get() noexcept + static consteval auto get() requires ::std::invocable<::beman::execution::get_start_scheduler_t, Env> { using transformed_sndr = decltype(on_t::transform_sender( @@ -141,7 +265,7 @@ struct on_t : ::beman::execution::sender_adaptor_closure { ::beman::execution::detail::product_type, Child>, Env> { - static constexpr auto get() noexcept + static consteval auto get() requires ::std::invocable<::beman::execution::get_completion_scheduler_t<::beman::execution::set_value_t>, ::beman::execution::env_of_t, Env> @@ -157,6 +281,7 @@ struct on_t : ::beman::execution::sender_adaptor_closure { } }; + public: template requires requires { get_signatures, Env>::get(); } static consteval auto get_completion_signatures() noexcept { diff --git a/include/beman/execution/detail/starts_on.hpp b/include/beman/execution/detail/starts_on.hpp index c94dc1a0..c497e4a2 100644 --- a/include/beman/execution/detail/starts_on.hpp +++ b/include/beman/execution/detail/starts_on.hpp @@ -30,6 +30,7 @@ import beman.execution.detail.just; import beman.execution.detail.let; import beman.execution.detail.make_sender; import beman.execution.detail.meta.combine; +import beman.execution.detail.meta.unique; import beman.execution.detail.query_with_default; import beman.execution.detail.sched_env; import beman.execution.detail.schedule_result_t; @@ -40,7 +41,6 @@ import beman.execution.detail.sends_stopped; import beman.execution.detail.set_error; import beman.execution.detail.set_stopped; import beman.execution.detail.set_value; -import beman.execution.detail.write_env; #else #include #include @@ -59,6 +59,7 @@ import beman.execution.detail.write_env; #include #include #include +#include #include #include #include @@ -67,7 +68,6 @@ import beman.execution.detail.write_env; #include #include #include -#include #endif // ---------------------------------------------------------------------------- @@ -75,7 +75,7 @@ import beman.execution.detail.write_env; namespace beman::execution::detail { struct starts_on_t { template <::beman::execution::detail::sender_for<::beman::execution::detail::starts_on_t> Sender, typename Env> - auto transform_sender(::beman::execution::set_value_t, Sender&& sender, const Env&) const noexcept { + static auto transform_sender(::beman::execution::set_value_t, Sender&& sender, const Env&) noexcept { auto&& sched{sender.template get<1>()}; auto&& child{sender.template get<2>()}; constexpr bool is_nothrow = ::std::is_nothrow_move_constructible_v<::std::remove_cvref_t>; @@ -95,22 +95,14 @@ struct starts_on_t { struct get_signatures< ::beman::execution::detail::basic_sender<::beman::execution::detail::starts_on_t, Scheduler, Child>, Env> { - using scheduler_sender = ::beman::execution::schedule_result_t; - using child_env = decltype(::beman::execution::detail::join_env( - ::beman::execution::detail::sched_env(::std::declval()), - ::beman::execution::detail::fwd_env(::std::declval()))); - - template - using as_set_error = ::beman::execution::completion_signatures<::beman::execution::set_error_t(E)...>; - - using value_and_error_types = ::beman::execution::detail::meta::combine< - ::beman::execution::completion_signatures_of_t, - ::beman::execution::error_types_of_t>; - using stopped_types = - ::std::conditional_t<::beman::execution::sends_stopped, - ::beman::execution::completion_signatures<::beman::execution::set_stopped_t()>, - ::beman::execution::completion_signatures<>>; - using type = ::beman::execution::detail::meta::combine; + static consteval auto get() { + using transformed_sndr = decltype(starts_on_t::transform_sender( + ::beman::execution::set_value, + ::std::declval<::beman::execution::detail:: + basic_sender<::beman::execution::detail::starts_on_t, Scheduler, Child>>(), + ::std::declval())); + return ::beman::execution::get_completion_signatures(); + } }; template @@ -153,8 +145,8 @@ struct starts_on_t { public: template - static consteval auto get_completion_signatures() noexcept { - return typename get_signatures<::std::remove_cvref_t, Env...>::type{}; + static consteval auto get_completion_signatures() { + return get_signatures<::std::remove_cvref_t, Env...>::get(); } struct impls_for : ::beman::execution::detail::default_impls { diff --git a/tests/beman/execution/exec-inline-scheduler.test.cpp b/tests/beman/execution/exec-inline-scheduler.test.cpp index 33289808..7a8765e3 100644 --- a/tests/beman/execution/exec-inline-scheduler.test.cpp +++ b/tests/beman/execution/exec-inline-scheduler.test.cpp @@ -76,7 +76,8 @@ struct sched_with_domain { }; struct sender { using sender_concept = test_std::sender_tag; - static consteval auto get_completion_signatures() { + template + static consteval auto get_completion_signatures() noexcept { return test_std::completion_signatures(); } template diff --git a/tests/beman/execution/exec-on.test.cpp b/tests/beman/execution/exec-on.test.cpp index 9b9db35c..60de28d7 100644 --- a/tests/beman/execution/exec-on.test.cpp +++ b/tests/beman/execution/exec-on.test.cpp @@ -30,6 +30,47 @@ import beman.execution.detail; // ---------------------------------------------------------------------------- namespace { +struct custom_domain : test_std::default_domain {}; + +struct custom_scheduler { + using scheduler_concept = test_std::scheduler_tag; + + static auto query(test_std::get_forward_progress_guarantee_t) noexcept { + return test_std::forward_progress_guarantee::weakly_parallel; + } + + static auto query(test_std::get_completion_scheduler_t) noexcept -> custom_scheduler { + return {}; + } + + static auto query(test_std::get_completion_domain_t) noexcept -> custom_domain { + return {}; + } + + struct env { + static auto query(test_std::get_completion_scheduler_t) noexcept -> custom_scheduler { + return {}; + } + }; + + struct sender { + using sender_concept = test_std::sender_tag; + + template + static consteval auto get_completion_signatures() noexcept { + return test_std::completion_signatures{}; + } + + auto connect(auto rcvr) const noexcept { return test_std::connect(test_std::just(), std::move(rcvr)); } + + auto get_env() const noexcept -> env { return {}; } + }; + + static auto schedule() noexcept { return sender{}; } + + auto operator==(const custom_scheduler&) const -> bool = default; +}; + template auto test_interface(Sch sch, Sndr sndr, Closure closure) -> void { static_assert(requires { @@ -48,14 +89,6 @@ auto test_interface(Sch sch, Sndr sndr, Closure closure) -> void { test::use(sndr1, sndr2, sndr3); } -template OutSndr> -auto test_transform_sender(OutSndr out_sndr) -> void { - auto s{test_std::on.transform_sender(test_std::set_value, std::move(out_sndr), test_std::env<>{})}; - static_assert(test_std::sender); - auto ts{std::move(s) | test_std::then([](auto&&...) {})}; - static_assert(test_std::sender); -} - struct on_receiver { using receiver_concept = test_std::receiver_tag; test::thread_pool& pool; @@ -68,7 +101,11 @@ static_assert(test_std::receiver); auto test_on_attributes() { test_std::run_loop loop{}; - test::sender_env s{42}; + static_assert(test_std::dependent_sender); + + test::sender_env s{42}; + auto recv_env = test_detail::make_env(test_std::get_start_scheduler, custom_scheduler{}); + test::test_sender_env(42, test::test_forwardable_attr{}, s); test::test_sender_env(84, test::test_non_forwardable_attr{}, s); test::test_sender_env(42, test::test_forwardable_attr{}, test_std::on(loop.get_scheduler(), s)); @@ -77,6 +114,24 @@ auto test_on_attributes() { 42, test::test_forwardable_attr{}, test_std::on(s, loop.get_scheduler(), test_std::then([]() {}))); test::test_sender_env( 84, test::test_non_forwardable_attr{}, test_std::on(s, loop.get_scheduler(), test_std::then([]() {}))); + + auto on_sndr1 = test_std::on(loop.get_scheduler(), test_std::just()); + static_assert(test_std::sender_in); + static_assert(not test_std::sender_in>); + + auto attrs1 = test_std::get_env(on_sndr1); + + ASSERT(test_std::get_completion_scheduler(attrs1, recv_env) == custom_scheduler{}); + + static_assert(std::same_as(attrs1, recv_env)), + custom_domain>); + + auto attrs2 = test_std::get_env( + test_std::on(test_std::schedule(custom_scheduler{}), loop.get_scheduler(), test_std::then([]() {}))); + ASSERT(test_std::get_completion_scheduler(attrs2, test_std::env<>{}) == custom_scheduler{}); + static_assert( + std::same_as(attrs2, test_std::env<>{})), + custom_domain>); } } // namespace @@ -88,10 +143,6 @@ TEST(exec_on) { static_assert(not test_detail::is_sender_adaptor_closure); test_interface(pool.get_scheduler(), test_std::just(), test_std::then([] {})); - test_transform_sender(test_detail::make_sender(test_std::on, pool.get_scheduler(), test_std::just())); - test_transform_sender(test_detail::make_sender( - test_std::on, test_detail::product_type{pool.get_scheduler(), test_std::then([] {})}, test_std::just())); - std::thread::id on_id{}; std::thread::id pool_id{}; std::thread::id cont_id{}; diff --git a/tests/beman/execution/exec-parallel-scheduler.test.cpp b/tests/beman/execution/exec-parallel-scheduler.test.cpp index 4b816b05..5340b29f 100644 --- a/tests/beman/execution/exec-parallel-scheduler.test.cpp +++ b/tests/beman/execution/exec-parallel-scheduler.test.cpp @@ -99,6 +99,17 @@ auto test_parallel_scheduler_schedule() -> void { test_std::sync_wait(test_std::schedule(sch) | test_std::then([&i]() noexcept { i = 114514; })); ASSERT(i == 114514); } + { + static constexpr auto square = [](int i) noexcept { return i * i; }; + auto work = test_std::when_all(test_std::on(sch, test_std::just(0) | test_std::then(square)), + test_std::on(sch, test_std::just(1) | test_std::then(square)), + test_std::on(sch, test_std::just(2) | test_std::then(square))); + + if (auto result = test_std::sync_wait(std::move(work))) { + auto [i, j, k] = result.value(); + ASSERT(i == 0 && j == 1 && k == 4); + } + } { bool invoked = false; test_std::sync_wait(test_std::schedule(sch) | From ff2a0e2b24d98baea55bac2c63835677f61316bc Mon Sep 17 00:00:00 2001 From: Cra3z <3324654761@qq.com> Date: Mon, 21 Sep 2026 21:44:09 +0800 Subject: [PATCH 3/5] Add missing headers --- include/beman/execution/detail/continues_on.hpp | 6 +++--- include/beman/execution/detail/starts_on.hpp | 2 ++ tests/beman/execution/exec-parallel-scheduler.test.cpp | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/beman/execution/detail/continues_on.hpp b/include/beman/execution/detail/continues_on.hpp index 39477c2a..98266d80 100644 --- a/include/beman/execution/detail/continues_on.hpp +++ b/include/beman/execution/detail/continues_on.hpp @@ -130,9 +130,9 @@ struct continues_on_t { template using as_set_error = ::beman::execution::completion_signatures<::beman::execution::set_error_t(E)...>; using type = ::beman::execution::detail::meta::unique<::beman::execution::detail::meta::combine< - decltype(::beman::execution::get_completion_signatures()), - ::beman::execution::error_types_of_t, - additional_signatures>>; + decltype(::beman::execution::get_completion_signatures()), + ::beman::execution::error_types_of_t, + additional_signatures>>; }; template diff --git a/include/beman/execution/detail/starts_on.hpp b/include/beman/execution/detail/starts_on.hpp index c497e4a2..de520929 100644 --- a/include/beman/execution/detail/starts_on.hpp +++ b/include/beman/execution/detail/starts_on.hpp @@ -24,6 +24,7 @@ import beman.execution.detail.forward_like; import beman.execution.detail.fwd_env; import beman.execution.detail.get_completion_domain; import beman.execution.detail.get_completion_scheduler; +import beman.execution.detail.get_completion_signatures; import beman.execution.detail.get_env; import beman.execution.detail.join_env; import beman.execution.detail.just; @@ -53,6 +54,7 @@ import beman.execution.detail.set_value; #include #include #include +#include #include #include #include diff --git a/tests/beman/execution/exec-parallel-scheduler.test.cpp b/tests/beman/execution/exec-parallel-scheduler.test.cpp index 5340b29f..beb4daf5 100644 --- a/tests/beman/execution/exec-parallel-scheduler.test.cpp +++ b/tests/beman/execution/exec-parallel-scheduler.test.cpp @@ -102,8 +102,8 @@ auto test_parallel_scheduler_schedule() -> void { { static constexpr auto square = [](int i) noexcept { return i * i; }; auto work = test_std::when_all(test_std::on(sch, test_std::just(0) | test_std::then(square)), - test_std::on(sch, test_std::just(1) | test_std::then(square)), - test_std::on(sch, test_std::just(2) | test_std::then(square))); + test_std::on(sch, test_std::just(1) | test_std::then(square)), + test_std::on(sch, test_std::just(2) | test_std::then(square))); if (auto result = test_std::sync_wait(std::move(work))) { auto [i, j, k] = result.value(); From d53fe641858d8058b234d5dcfa589f0457f503c5 Mon Sep 17 00:00:00 2001 From: Cra3z Date: Tue, 22 Sep 2026 13:55:25 +0800 Subject: [PATCH 4/5] gcc 15 workaround --- examples/intro_1_hello_world.cpp | 2 -- examples/intro_2_hello_async.cpp | 2 -- examples/playground.cpp | 2 -- examples/sender_demo.cpp | 2 -- examples/when_all_cancel.cpp | 2 -- include/beman/execution/detail/when_all.hpp | 3 ++- 6 files changed, 2 insertions(+), 11 deletions(-) diff --git a/examples/intro_1_hello_world.cpp b/examples/intro_1_hello_world.cpp index 0dd16121..5a427a70 100644 --- a/examples/intro_1_hello_world.cpp +++ b/examples/intro_1_hello_world.cpp @@ -25,7 +25,6 @@ using namespace std::string_literals; int main() { // clang-format off -#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ > 15) || !defined(BEMAN_HAS_MODULES) auto [result] = ex::sync_wait( ex::when_all( @@ -34,7 +33,6 @@ int main() { ) | ex::then([](auto const& s1, auto const& s2) { return s1 + s2; }) ).value_or(std::tuple(""s)) ; -#endif // clang-format on // std::cout << std::get<0>(v) << '\n'; diff --git a/examples/intro_2_hello_async.cpp b/examples/intro_2_hello_async.cpp index 4ff53c6b..d46041b0 100644 --- a/examples/intro_2_hello_async.cpp +++ b/examples/intro_2_hello_async.cpp @@ -28,7 +28,6 @@ using namespace std::chrono_literals; int main() { std::cout << std::unitbuf; -#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ > 15) || !defined(BEMAN_HAS_MODULES) intro::timer timer; // clang-format off @@ -48,5 +47,4 @@ int main() { // clang-format on std::cout << result << "\n"; -#endif } diff --git a/examples/playground.cpp b/examples/playground.cpp index 186e1fff..0bfe0709 100644 --- a/examples/playground.cpp +++ b/examples/playground.cpp @@ -21,10 +21,8 @@ namespace ex = ::beman::execution; // ---------------------------------------------------------------------------- int main() { -#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ > 15) || !defined(BEMAN_HAS_MODULES) auto [result] = ex::sync_wait(ex::when_all(ex::just(std::string("hello, ")), ex::just(std::string("world"))) | ex::then([](const auto& s1, const auto& s2) { return s1 + s2; })) .value_or(std::tuple(std::string("oops"))); std::cout << "result='" << result << "'\n"; -#endif } diff --git a/examples/sender_demo.cpp b/examples/sender_demo.cpp index aa1ff8f9..255b8560 100644 --- a/examples/sender_demo.cpp +++ b/examples/sender_demo.cpp @@ -72,14 +72,12 @@ int main() { ex::write_env(std::move(w), ex::detail::make_env(ex::get_allocator, std::pmr::polymorphic_allocator<>())); std::cout << "before start\n"; -#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ > 15) || !defined(BEMAN_HAS_MODULES) auto r = ex::sync_wait(std::move(e)); if (r) { auto [v] = *r; std::cout << "produced='" << v << "'\n"; } else std::cout << "operation was cancelled\n"; -#endif std::cout << "after start\n"; } catch (const std::exception& ex) { std::cout << "ERROR: " << ex.what() << "\n"; diff --git a/examples/when_all_cancel.cpp b/examples/when_all_cancel.cpp index cba9096f..f107d7a9 100644 --- a/examples/when_all_cancel.cpp +++ b/examples/when_all_cancel.cpp @@ -150,7 +150,6 @@ eager(Sender&&) -> eager>; } // namespace auto main() -> int { -#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ > 15) || !defined(BEMAN_HAS_MODULES) auto s{eager{ex::when_all(await_stop{})}}; ex::inplace_stop_source source{}; @@ -160,5 +159,4 @@ auto main() -> int { std::cout << "started\n"; source.request_stop(); std::cout << "done\n"; -#endif } diff --git a/include/beman/execution/detail/when_all.hpp b/include/beman/execution/detail/when_all.hpp index c7323415..03c2ee1d 100644 --- a/include/beman/execution/detail/when_all.hpp +++ b/include/beman/execution/detail/when_all.hpp @@ -241,6 +241,8 @@ struct when_all_t { }; static constexpr auto get_env{get_env_impl{}}; + struct nonesuch {}; + enum class disposition : unsigned char { started, error, stopped }; template @@ -249,7 +251,6 @@ struct when_all_t { template struct state_type { - struct nonesuch {}; using env_t = when_all_env<::beman::execution::env_of_t>; using copy_fail = ::std::conditional_t< (... && ::beman::execution::value_types_of_t Date: Tue, 22 Sep 2026 18:11:24 +0800 Subject: [PATCH 5/5] Refactor `continues_on` --- include/beman/execution/detail/continues_on.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/beman/execution/detail/continues_on.hpp b/include/beman/execution/detail/continues_on.hpp index 98266d80..c5105e04 100644 --- a/include/beman/execution/detail/continues_on.hpp +++ b/include/beman/execution/detail/continues_on.hpp @@ -123,7 +123,7 @@ struct continues_on_t { Env> { using scheduler_sender = ::beman::execution::schedule_result_t; using additional_signatures = ::std::conditional_t< - ::beman::execution::sends_stopped, + ::beman::execution::sends_stopped, ::beman::execution::completion_signatures<::beman::execution::set_error_t(::std::exception_ptr), ::beman::execution::set_stopped_t()>, ::beman::execution::completion_signatures<::beman::execution::set_error_t(::std::exception_ptr)>>;