From 8ddc7dfda971ec6f5722cd5da09961b352630852 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sun, 15 Oct 2023 14:26:11 -0700 Subject: [PATCH] domain-based customizations of [transfer_]when_all[_with_variant] follows P2999 --- include/exec/any_sender_of.hpp | 2 +- include/exec/async_scope.hpp | 8 +- include/exec/env.hpp | 8 +- include/exec/when_any.hpp | 5 +- include/nvexec/stream/common.cuh | 2 +- include/nvexec/stream/when_all.cuh | 2 +- include/stdexec/execution.hpp | 159 ++++++++++-------- .../algos/adaptors/test_transfer_when_all.cpp | 159 ++++++++++++++++++ test/stdexec/algos/adaptors/test_when_all.cpp | 154 ++++++++++++++++- 9 files changed, 407 insertions(+), 92 deletions(-) diff --git a/include/exec/any_sender_of.hpp b/include/exec/any_sender_of.hpp index 31e853dd3..65ff4585f 100644 --- a/include/exec/any_sender_of.hpp +++ b/include/exec/any_sender_of.hpp @@ -829,7 +829,7 @@ namespace exec { friend __env_t> tag_invoke(_GetEnv, const _Self& __self) noexcept { return __make_env( get_env(__self.__op_->__rcvr_), - __with_(get_stop_token, __self.__op_->__stop_source_.get_token())); + __mkprop(__self.__op_->__stop_source_.get_token(), get_stop_token)); } }; }; diff --git a/include/exec/async_scope.hpp b/include/exec/async_scope.hpp index 4fb039ec1..24ca4dbd4 100644 --- a/include/exec/async_scope.hpp +++ b/include/exec/async_scope.hpp @@ -602,8 +602,8 @@ namespace exec { using __spawn_env_t = __result_of< __join_env, _Env, - __env::__prop, - __env::__prop>; + __env::__prop, + __env::__prop<__inln::__scheduler(get_scheduler_t)>>; template struct __spawn_op_base { @@ -647,8 +647,8 @@ namespace exec { template <__decays_to<_Sender> _Sndr> __spawn_op(_Sndr&& __sndr, _Env __env, const __impl* __scope) : __spawn_op_base<_EnvId>{__join_env((_Env&&) __env, - __mkprop(get_stop_token, __scope->__stop_source_.get_token()), - __mkprop(get_scheduler, __inln::__scheduler{})), + __mkprop(__scope->__stop_source_.get_token(), get_stop_token), + __mkprop(__inln::__scheduler{}, get_scheduler)), [](__spawn_op_base<_EnvId>* __op) { delete static_cast<__spawn_op*>(__op); }} diff --git a/include/exec/env.hpp b/include/exec/env.hpp index 6b95339b3..fc8d3a5c9 100644 --- a/include/exec/env.hpp +++ b/include/exec/env.hpp @@ -23,19 +23,19 @@ #endif namespace exec { - template + template using with_t = stdexec::__with<_Tag, _Value>; namespace __detail { struct __with_t { template - with_t<_Tag, _Value> operator()(_Tag, _Value&& __val) const { - return stdexec::__with_(_Tag(), (_Value&&) __val); + with_t<_Tag, stdexec::__decay_t<_Value>> operator()(_Tag, _Value&& __val) const { + return stdexec::__mkprop((_Value&&) __val, _Tag()); } template with_t<_Tag> operator()(_Tag) const { - return stdexec::__with_(_Tag()); + return stdexec::__mkprop(_Tag()); } }; } // namespace __detail diff --git a/include/exec/when_any.hpp b/include/exec/when_any.hpp index e5af82756..24ffaed5b 100644 --- a/include/exec/when_any.hpp +++ b/include/exec/when_any.hpp @@ -172,9 +172,8 @@ namespace exec { } friend __env_t> tag_invoke(get_env_t, const __t& __self) noexcept { - using __with_token = __with; - auto __token = __with_(get_stop_token, __self.__op_->__stop_source_.get_token()); - return __make_env(get_env(__self.__op_->__receiver_), (__with_token&&) __token); + auto __token = __mkprop(__self.__op_->__stop_source_.get_token(), get_stop_token); + return __make_env(get_env(__self.__op_->__receiver_), std::move(__token)); } }; }; diff --git a/include/nvexec/stream/common.cuh b/include/nvexec/stream/common.cuh index be383faee..94f4b1c7a 100644 --- a/include/nvexec/stream/common.cuh +++ b/include/nvexec/stream/common.cuh @@ -326,7 +326,7 @@ namespace nvexec { template auto make_stream_env(BaseEnv&& base_env, stream_provider_t* stream_provider) noexcept { - return __join_env(__mkprop(get_stream_provider, stream_provider), (BaseEnv&&) base_env); + return __join_env(__mkprop(stream_provider, get_stream_provider), (BaseEnv&&) base_env); } template diff --git a/include/nvexec/stream/when_all.cuh b/include/nvexec/stream/when_all.cuh index a671e83de..e129d4068 100644 --- a/include/nvexec/stream/when_all.cuh +++ b/include/nvexec/stream/when_all.cuh @@ -200,7 +200,7 @@ namespace nvexec::STDEXEC_STREAM_DETAIL_NS { auto env = make_terminal_stream_env( exec::make_env( stdexec::get_env(base()), - __with_(get_stop_token, op_state_->stop_source_.get_token())), + __mkprop(op_state_->stop_source_.get_token(), get_stop_token)), &const_cast(op_state_->stream_providers_[Index])); return env; diff --git a/include/stdexec/execution.hpp b/include/stdexec/execution.hpp index 663b61610..fd8cf2a31 100644 --- a/include/stdexec/execution.hpp +++ b/include/stdexec/execution.hpp @@ -395,38 +395,41 @@ namespace stdexec { using __id = empty_env; }; - template - struct __prop { + template + struct __prop; + + template + struct __prop<_Value(_Tags...)> { using __t = __prop; using __id = __prop; _Value __value_; - template _Key> + template <__one_of<_Tags...> _Key> friend auto tag_invoke(_Key, const __prop& __self) // noexcept(__nothrow_decay_copyable<_Value>) -> _Value { return __self.__value_; } }; - template - struct __prop<_Tag, __none_such> { + template + struct __prop { using __t = __prop; using __id = __prop; - template _Key, class _Self> + template <__one_of<_Tags...> _Key, class _Self> requires(std::is_base_of_v<__prop, __decay_t<_Self>>) friend auto tag_invoke(_Key, _Self&&) noexcept = delete; }; struct __mkprop_t { - template - auto operator()(_Tag, _Value&& __value) const noexcept(__nothrow_decay_copyable<_Value>) - -> __prop<_Tag, __decay_t<_Value>> { + template + auto operator()(_Value&& __value, _Tag, _Tags...) const + noexcept(__nothrow_decay_copyable<_Value>) -> __prop<__decay_t<_Value>(_Tag, _Tags...)> { return {(_Value&&) __value}; } template - auto operator()(_Tag) const -> __prop<_Tag> { + auto operator()(_Tag) const -> __prop { return {}; } }; @@ -488,10 +491,10 @@ namespace stdexec { }; template - struct __joined_env<__prop<_Tag>, _Base> : __env_fwd<_Base> { + struct __joined_env<__prop, _Base> : __env_fwd<_Base> { using __t = __joined_env; using __id = __joined_env; - STDEXEC_ATTRIBUTE((no_unique_address)) __prop<_Tag> __env_; + STDEXEC_ATTRIBUTE((no_unique_address)) __prop __env_; friend void tag_invoke(_Tag, const __joined_env&) noexcept = delete; }; @@ -549,22 +552,6 @@ namespace stdexec { friend auto tag_invoke(get_env_t, const __env_promise&) noexcept -> const _Env&; }; - template - constexpr auto __with_(_Tag, _Value __val) noexcept { - return __env_fn{ - [__val = std::move(__val)](_Tag) noexcept(__nothrow_copy_constructible<_Value>) { - return __val; - }}; - } - - template - __prop<_Tag> __with_(_Tag) noexcept { - return {}; - } - - template - using __with = decltype(__env::__with_(__declval<_Ts>()...)); - // For making an environment from key/value pairs and optionally // another environment. struct __make_env_t { @@ -619,10 +606,9 @@ namespace stdexec { // for making an environment from a single key/value pair inline constexpr __env::__mkprop_t __mkprop{}; - inline constexpr __env::__mkprop_t __with_{}; - template - using __with = __call_result_t<__env::__mkprop_t, _Tag, _Value>; + template + using __with = __env::__prop<_Value(_Tag)>; template using __make_env_t = __call_result_t<__env::__make_env_t, _Ts...>; @@ -2487,7 +2473,7 @@ namespace stdexec { struct __schedule_t { static auto get_env(__ignore) noexcept - -> __env::__prop, __scheduler>; + -> __env::__prop<__scheduler(get_completion_scheduler_t)>; using __compl_sigs = stdexec::completion_signatures; static __compl_sigs get_completion_signatures(__ignore, __ignore); @@ -2516,8 +2502,8 @@ namespace stdexec { }; inline auto __schedule_t::get_env(__ignore) noexcept - -> __env::__prop, __scheduler> { - return __mkprop(get_completion_scheduler, __scheduler{}); + -> __env::__prop<__scheduler(get_completion_scheduler_t)> { + return __mkprop(__scheduler{}, get_completion_scheduler); } } @@ -3866,7 +3852,7 @@ namespace stdexec { connect_result_t<_CvrefSender, __receiver_> __op_state2_; explicit __t(_CvrefSender&& __sndr, _Env __env = {}) - : __env_(__make_env((_Env&&) __env, __with_(get_stop_token, __stop_source_.get_token()))) + : __env_(__make_env((_Env&&) __env, __mkprop(__stop_source_.get_token(), get_stop_token))) , __op_state2_(connect((_CvrefSender&&) __sndr, __receiver_{*this})) { } @@ -4177,7 +4163,7 @@ namespace stdexec { connect_result_t<_CvrefSender, __receiver_t> __op_state2_; explicit __t(_CvrefSender&& __sndr, _Env __env = {}) - : __env_(__make_env((_Env&&) __env, __with_(get_stop_token, __stop_source_.get_token()))) + : __env_(__make_env((_Env&&) __env, __mkprop(__stop_source_.get_token(), get_stop_token))) , __op_state2_(connect((_CvrefSender&&) __sndr, __receiver_t{*this})) { start(__op_state2_); } @@ -4502,7 +4488,7 @@ namespace stdexec { }; inline constexpr auto __get_scheduler_prop = [](auto* __op) noexcept { - return __mkprop(get_scheduler, __op->__sched_); + return __mkprop(__op->__sched_, get_scheduler); }; inline constexpr auto __get_domain_prop = [](auto*) noexcept { @@ -4538,8 +4524,10 @@ namespace stdexec { // then we can use that instead of hard-coding `__inln::__scheduler` here. __one_of<_Scheduler, __none_such, __inln::__scheduler>, _Env, - __env:: - __env_join_t< __env::__prop, __env::__prop, _Env>>; + __env::__env_join_t< + __env::__prop<_Scheduler(get_scheduler_t)>, + __env::__prop, + _Env>>; // A metafunction that computes the type of the resulting operation state for a // given set of argument types. @@ -5371,16 +5359,15 @@ namespace stdexec { template struct __environ { - using _Scheduler = stdexec::__t<_SchedulerId>; - - struct __t { + struct __t + : __env::__prop( + get_completion_scheduler_t, + get_completion_scheduler_t)> { using __id = __environ; - _Scheduler __sched_; - - template <__one_of _Tag> - friend _Scheduler tag_invoke(get_completion_scheduler_t<_Tag>, const __t& __self) noexcept { - return __self.__sched_; + template _Key> + friend auto tag_invoke(_Key, const __t& __self) noexcept { + return query_or(get_domain, __self.__value_, default_domain()); } }; }; @@ -5682,7 +5669,7 @@ namespace stdexec { }; inline constexpr auto __sched_prop = [](auto* __op) noexcept { - return __mkprop(get_scheduler, __op->__scheduler_); + return __mkprop(__op->__scheduler_, get_scheduler); }; inline constexpr auto __domain_prop = [](auto*) noexcept { return __mkprop(get_domain); @@ -6388,7 +6375,21 @@ namespace stdexec { concept __has_common_domain = // __none_of<__none_such, __common_domain_t<_Senders...>>; - struct when_all_t { + template + struct __get_env_common_domain { + template _Self> + static auto get_env(const _Self& __self) noexcept { + using _Domain = __call_result_t; + if constexpr (same_as<_Domain, default_domain>) { + return empty_env(); + } else { + return __mkprop(apply_sender(__self, __common_domain_fn()), get_domain); + } + STDEXEC_UNREACHABLE(); + } + }; + + struct when_all_t : __get_env_common_domain { // Used by the default_domain to find legacy customizations: using _Sender = __1; using __legacy_customizations_t = // @@ -6409,18 +6410,6 @@ namespace stdexec { friend struct stdexec::__sexpr; #endif - template _Self> - static auto get_env(const _Self& __self) noexcept { - using _Domain = __call_result_t; - if constexpr (same_as<_Domain, default_domain>) { - return empty_env(); - } else { - return __env::__env_fn{ - __mkfield(apply_sender(__self, __common_domain_fn()))}; - } - STDEXEC_UNREACHABLE(); - } - template using __error = __mexception< _INVALID_ARGUMENTS_TO_WHEN_ALL_, @@ -6472,7 +6461,7 @@ namespace stdexec { template using __into_variant_result_t = __result_of; - struct when_all_with_variant_t { + struct when_all_with_variant_t : __get_env_common_domain { using _Sender = __1; using __legacy_customizations_t = // __types; @@ -6503,18 +6492,28 @@ namespace stdexec { }; struct transfer_when_all_t { - using _Scheduler = __0; + using _Env = __0; using _Sender = __1; using __legacy_customizations_t = // - __types; + __types(const _Env&), + _Sender...)>; template requires __has_common_domain<_Senders...> auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const { + using _Env = __t<__schedule_from::__environ<__id<__decay_t<_Scheduler>>>>; auto __domain = query_or(get_domain, __sched, default_domain()); return stdexec::transform_sender( __domain, - make_sender_expr((_Scheduler&&) __sched, (_Senders&&) __sndrs...)); + make_sender_expr( + _Env{(_Scheduler&&) __sched}, (_Senders&&) __sndrs...)); + } + + template _Sender> + static __data_of get_env(const _Sender& __self) noexcept { + return apply_sender(__self, __detail::__get_data()); } template @@ -6530,33 +6529,42 @@ namespace stdexec { __domain, transfer( stdexec::transform_sender(__domain, when_all_t()((_Child&&) __child...), __env), - (_Data&&) __data), + get_completion_scheduler(__data)), __env); }); } }; struct transfer_when_all_with_variant_t { - using _Scheduler = __0; + using _Env = __0; using _Sender = __1; using __legacy_customizations_t = // - __types; + __types(const _Env&), + _Sender...)>; template requires __has_common_domain<_Senders...> auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const { + using _Env = __t<__schedule_from::__environ<__id<__decay_t<_Scheduler>>>>; auto __domain = query_or(get_domain, __sched, default_domain()); return stdexec::transform_sender( __domain, make_sender_expr( - (_Scheduler&&) __sched, (_Senders&&) __sndrs...)); + _Env{(_Scheduler&&) __sched}, (_Senders&&) __sndrs...)); + } + + template _Sender> + static __data_of get_env(const _Sender& __self) noexcept { + return apply_sender(__self, __detail::__get_data()); } template static auto transform_sender(_Sender&& __sndr, const _Env& __env) { - // transform the transfer_when_all into a regular transform | when_all - // (looking for early customizations), then transform it again to look for - // late customizations. + // transform the transfer_when_allwith_variant into regular transform_when_all + // and into_variant calls/ (looking for early customizations), then transform it + // again to look for late customizations. return apply_sender( (_Sender&&) __sndr, [&](__ignore, _Data&& __data, _Child&&... __child) { @@ -6564,7 +6572,7 @@ namespace stdexec { return stdexec::transform_sender( __domain, transfer_when_all_t()( - (_Data&&) __data, + get_completion_scheduler((_Data&&) __data), stdexec::transform_sender(__domain, into_variant((_Child&&) __child), __env)...), __env); }); @@ -6574,10 +6582,13 @@ namespace stdexec { using __when_all::when_all_t; inline constexpr when_all_t when_all{}; + using __when_all::when_all_with_variant_t; inline constexpr when_all_with_variant_t when_all_with_variant{}; + using __when_all::transfer_when_all_t; inline constexpr transfer_when_all_t transfer_when_all{}; + using __when_all::transfer_when_all_with_variant_t; inline constexpr transfer_when_all_with_variant_t transfer_when_all_with_variant{}; @@ -6848,7 +6859,7 @@ namespace stdexec { template static auto __mkenv(_Scheduler __sched) { - auto __env = __join_env(__mkprop(get_scheduler, __sched), __mkprop(get_domain)); + auto __env = __join_env(__mkprop(__sched, get_scheduler), __mkprop(get_domain)); using _Env = decltype(__env); struct __env_t : _Env { }; diff --git a/test/stdexec/algos/adaptors/test_transfer_when_all.cpp b/test/stdexec/algos/adaptors/test_transfer_when_all.cpp index c52ba3dcb..3378c9a26 100644 --- a/test/stdexec/algos/adaptors/test_transfer_when_all.cpp +++ b/test/stdexec/algos/adaptors/test_transfer_when_all.cpp @@ -140,3 +140,162 @@ TEST_CASE("transfer_when_all_with_variant can be customized", "[adaptors][transf ); wait_for_value(std::move(snd), std::string{"first program"}); } + +namespace { + enum customize : std::size_t { + early, + late, + none + }; + + template + struct basic_domain { + template Sender, class... Env> + requires(sizeof...(Env) == C) + auto transform_sender(Sender&& sender, const Env&...) const { + return Fun(); + } + }; +} // anonymous namespace + +TEST_CASE("transfer_when_all works with custom domain", "[adaptors][transfer_when_all]") { + constexpr auto hello = [] { + return ex::just(std::string{"hello world"}); + }; + + SECTION("sender has correct domain") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::transfer_when_all( // + scheduler(), + ex::just(3), // + ex::just(0.1415) // + ); + static_assert(ex::sender_expr_for); + [[maybe_unused]] domain dom = ex::get_domain(ex::get_env(snd)); + } + + SECTION("early customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::transfer_when_all( // + scheduler(), // + ex::just(3), // + ex::just(0.1415) // + ); + static_assert(ex::sender_expr_for); + wait_for_value(std::move(snd), std::string{"hello world"}); + } + + SECTION("late customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::on( + scheduler(), + ex::transfer_when_all( // + inline_scheduler(), // + ex::just(3), // + ex::just(0.1415) // + )); + wait_for_value(std::move(snd), std::string{"hello world"}); + } +} + +TEST_CASE( + "transfer_when_all_with_variant works with custom domain", + "[adaptors][transfer_when_all]") { + constexpr auto hello = [] { + return ex::just(std::string{"hello world"}); + }; + + SECTION("sender has correct domain") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::transfer_when_all_with_variant( // + scheduler(), // + ex::just(3), // + ex::just(0.1415) // + ); + static_assert(ex::sender_expr_for); + [[maybe_unused]] domain dom = ex::get_domain(ex::get_env(snd)); + } + + SECTION("early customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::transfer_when_all_with_variant( // + scheduler(), + ex::just(3), // + ex::just(0.1415) // + ); + static_assert(ex::sender_expr_for); + wait_for_value(std::move(snd), std::string{"hello world"}); + } + + SECTION("late customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::on( + scheduler(), + ex::transfer_when_all_with_variant( // + inline_scheduler(), // + ex::just(3), // + ex::just(0.1415) // + )); + wait_for_value(std::move(snd), std::string{"hello world"}); + } +} + +TEST_CASE( + "transfer_when_all_with_variant finds transfer_when_all customizations", + "[adaptors][transfer_when_all]") { + constexpr auto hello = [] { + return ex::just(std::string{"hello world"}); + }; + + SECTION("sender has correct domain") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::transfer_when_all_with_variant( // + scheduler(), // + ex::just(3), // + ex::just(0.1415) // + ); + static_assert(ex::sender_expr_for); + [[maybe_unused]] domain dom = ex::get_domain(ex::get_env(snd)); + } + + SECTION("early customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::transfer_when_all_with_variant( // + scheduler(), // + ex::just(3), // + ex::just(0.1415) // + ); + static_assert(ex::sender_expr_for); + wait_for_value(std::move(snd), std::string{"hello world"}); + } + + SECTION("late customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::on( + scheduler(), + ex::transfer_when_all_with_variant( // + inline_scheduler(), // + ex::just(3), // + ex::just(0.1415) // + )); + wait_for_value(std::move(snd), std::string{"hello world"}); + } +} diff --git a/test/stdexec/algos/adaptors/test_when_all.cpp b/test/stdexec/algos/adaptors/test_when_all.cpp index 5fdb2a83a..5edd31585 100644 --- a/test/stdexec/algos/adaptors/test_when_all.cpp +++ b/test/stdexec/algos/adaptors/test_when_all.cpp @@ -359,10 +359,10 @@ TEST_CASE("when_all_with_variant can be customized", "[adaptors][when_all]") { wait_for_value(std::move(snd), std::string{"first program"}); } -using my_string_variant_sender_t = decltype(ex::into_variant(my_string_sender_t{std::string{}})); - -// There is no way for ADL to find this overload. This test is broken and needs to be rewritten -// using a custom domain. +// There is no way for ADL to find the following overload. This test is broken and needs to be +// rewritten using a custom domain. +// +// using my_string_variant_sender_t = decltype(ex::into_variant(my_string_sender_t{std::string{}})); // // auto tag_invoke(ex::when_all_t, my_string_variant_sender_t, my_string_variant_sender_t) { // // Return a different sender when we invoke this custom defined on implementation @@ -384,3 +384,149 @@ using my_string_variant_sender_t = decltype(ex::into_variant(my_string_sender_t{ TEST_CASE("when_all returns empty env", "[adaptors][when_all]") { check_env_type(ex::when_all(ex::just(), ex::just())); } + +namespace { + enum customize : std::size_t { + early, + late, + none + }; + + template + struct basic_domain { + template Sender, class... Env> + requires(sizeof...(Env) == C) + auto transform_sender(Sender&& sender, const Env&...) const { + return Fun(); + } + }; +} // anonymous namespace + +TEST_CASE("when_all works with custom domain", "[adaptors][when_all]") { + constexpr auto hello = [] { + return ex::just(std::string{"hello world"}); + }; + + SECTION("sender has correct domain") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::when_all( // + ex::transfer_just(scheduler(), 3), // + ex::transfer_just(scheduler(), 0.1415) // + ); + static_assert(ex::sender_expr_for); + [[maybe_unused]] domain dom = ex::get_domain(ex::get_env(snd)); + } + + SECTION("early customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::when_all( // + ex::transfer_just(scheduler(), 3), // + ex::transfer_just(scheduler(), 0.1415) // + ); + static_assert(ex::sender_expr_for); + wait_for_value(std::move(snd), std::string{"hello world"}); + } + + SECTION("late customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::on( + scheduler(), + ex::when_all( // + ex::just(3), // + ex::just(0.1415) // + )); + wait_for_value(std::move(snd), std::string{"hello world"}); + } +} + +TEST_CASE("when_all_with_variant works with custom domain", "[adaptors][when_all]") { + constexpr auto hello = [] { + return ex::just(std::string{"hello world"}); + }; + + SECTION("sender has correct domain") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::when_all_with_variant( // + ex::transfer_just(scheduler(), 3), // + ex::transfer_just(scheduler(), 0.1415) // + ); + static_assert(ex::sender_expr_for); + [[maybe_unused]] domain dom = ex::get_domain(ex::get_env(snd)); + } + + SECTION("early customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::when_all_with_variant( // + ex::transfer_just(scheduler(), 3), // + ex::transfer_just(scheduler(), 0.1415) // + ); + static_assert(ex::sender_expr_for); + wait_for_value(std::move(snd), std::string{"hello world"}); + } + + SECTION("late customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::on( + scheduler(), + ex::when_all_with_variant( // + ex::just(3), // + ex::just(0.1415) // + )); + wait_for_value(std::move(snd), std::string{"hello world"}); + } +} + +TEST_CASE("when_all_with_variant finds when_all customizations", "[adaptors][when_all]") { + constexpr auto hello = [] { + return ex::just(std::string{"hello world"}); + }; + + SECTION("sender has correct domain") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::when_all_with_variant( // + ex::transfer_just(scheduler(), 3), // + ex::transfer_just(scheduler(), 0.1415) // + ); + static_assert(ex::sender_expr_for); + [[maybe_unused]] domain dom = ex::get_domain(ex::get_env(snd)); + } + + SECTION("early customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::when_all_with_variant( // + ex::transfer_just(scheduler(), 3), // + ex::transfer_just(scheduler(), 0.1415) // + ); + static_assert(ex::sender_expr_for); + wait_for_value(std::move(snd), std::string{"hello world"}); + } + + SECTION("late customization") { + using domain = basic_domain; + using scheduler = basic_inline_scheduler; + + auto snd = ex::on( + scheduler(), + ex::when_all_with_variant( // + ex::just(3), // + ex::just(0.1415) // + )); + wait_for_value(std::move(snd), std::string{"hello world"}); + } +}