diff --git a/include/exec/detail/shared.hpp b/include/exec/detail/shared.hpp index 378b9dfbf..58407d93a 100644 --- a/include/exec/detail/shared.hpp +++ b/include/exec/detail/shared.hpp @@ -27,6 +27,7 @@ #include "../../stdexec/__detail/__optional.hpp" #include "../../stdexec/__detail/__queries.hpp" #include "../../stdexec/__detail/__receivers.hpp" +#include "../../stdexec/__detail/__storage.hpp" #include "../../stdexec/__detail/__transform_completion_signatures.hpp" #include "../../stdexec/__detail/__tuple.hpp" #include "../../stdexec/__detail/__variant.hpp" // IWYU pragma: keep @@ -77,24 +78,6 @@ namespace experimental::execution::__shared template using __env_t = __join_env_t, _Env>; - struct __notify_fn - { - template - constexpr void operator()(_Receiver& __rcvr, _Tag, _Args&&... __args) const noexcept - { - _Tag()(static_cast<_Receiver&&>(__rcvr), static_cast<_Args&&>(__args)...); - } - }; - - struct __notify_visitor - { - template - constexpr void operator()(_Receiver& __rcvr, _Tuple&& __tupl) const noexcept - { - STDEXEC::__apply(__notify_fn(), static_cast<_Tuple&&>(__tupl), __rcvr); - }; - }; - //////////////////////////////////////////////////////////////////////////////////////// template struct __receiver @@ -133,13 +116,8 @@ namespace experimental::execution::__shared //////////////////////////////////////////////////////////////////////////////////////// template using __result_variant_t = - __transform_reduce_completion_signatures_t<__completion_signatures_of_t<_CvSender, _Env>, - __mbind_front_q<__decayed_tuple, set_value_t>::__f, - __mbind_front_q<__decayed_tuple, set_error_t>::__f, - __tuple, - __munique<__qq<__variant>>::__f, - __tuple, - __tuple>; + __mapply<__mbind_front_q<__results_storage, set_stopped_t(), set_error_t(std::exception_ptr)>, + __completion_signatures_of_t<_CvSender, _Env>>; //////////////////////////////////////////////////////////////////////////////////////// template @@ -269,10 +247,7 @@ namespace experimental::execution::__shared using __cv_variant_t = __if_c<__is_split, __variant_t const &, __variant_t>; __on_stop_.reset(); - - STDEXEC::__visit(__notify_visitor(), - static_cast<__cv_variant_t&&>(__sh_state_->__results_), - __rcvr_); + static_cast<__cv_variant_t&&>(__sh_state_->__results_).__complete(__rcvr_); } _Receiver __rcvr_; @@ -353,7 +328,7 @@ namespace experimental::execution::__shared __waiters_list_t __waiters_{}; inplace_stop_source __stop_source_{}; __env_t<_Env> __env_; - _Variant __results_{__no_init}; // Initialized to the "set_stopped" state in the ctor. + _Variant __results_; // Initialized to the "set_stopped" state in the ctor. }; template diff --git a/include/exec/fork_join.hpp b/include/exec/fork_join.hpp index cd58ccc1f..c9aa3421e 100644 --- a/include/exec/fork_join.hpp +++ b/include/exec/fork_join.hpp @@ -16,6 +16,7 @@ #pragma once #include "../stdexec/__detail/__receiver_ref.hpp" +#include "../stdexec/__detail/__storage.hpp" #include "../stdexec/execution.hpp" #include @@ -27,26 +28,6 @@ namespace experimental::execution struct fork_join_impl_t { - struct _dematerialize_fn - { - struct _impl_fn - { - template - STDEXEC_ATTRIBUTE(always_inline, host, device) - constexpr void operator()(Rcvr& rcvr, Tag, Args const &... args) const noexcept - { - Tag{}(static_cast(rcvr), args...); - } - }; - - template - STDEXEC_ATTRIBUTE(always_inline, host, device) - constexpr void operator()(Rcvr& rcvr, Tuple const & tupl) const noexcept - { - STDEXEC::__apply(_impl_fn{}, tupl, rcvr); - } - }; - struct _mk_when_all_fn { template @@ -58,17 +39,7 @@ namespace experimental::execution }; template - using _maybe_eptr_completion_t = - STDEXEC::__if_c::value, - STDEXEC::__mset_nil, - STDEXEC::__tuple>; - - template - using _variant_t = STDEXEC::__mset_insert< - STDEXEC::__for_each_completion_signature_t, - _maybe_eptr_completion_t>::template rebind; + using _variant_t = STDEXEC::__mapply_q; template struct _env_t @@ -102,7 +73,7 @@ namespace experimental::execution STDEXEC_ATTRIBUTE(host, device) void start() noexcept { - STDEXEC::__visit(_dematerialize_fn{}, *_results_, _rcvr_); + std::as_const(*_results_).__complete(_rcvr_); } Rcvr _rcvr_; @@ -230,7 +201,7 @@ namespace experimental::execution } Rcvr _rcvr_; - _variant_t<_child_completions_t> _cache_{STDEXEC::__no_init}; + _variant_t<_child_completions_t> _cache_; STDEXEC::__manual_lifetime<_child_opstate_t> _child_opstate_{}; _fork_opstate_t _fork_opstate_; }; diff --git a/include/exec/just_from.hpp b/include/exec/just_from.hpp index a4339f4ac..cfe33f5f1 100644 --- a/include/exec/just_from.hpp +++ b/include/exec/just_from.hpp @@ -165,7 +165,7 @@ namespace experimental::execution { // Extract the tags from the completion signatures and use them to construct the attributes. return STDEXEC::__mapply< - STDEXEC::__mtransform, + STDEXEC::__mtransform, STDEXEC::__munique>>, completion_signatures>(); } diff --git a/include/exec/sequence/ignore_all_values.hpp b/include/exec/sequence/ignore_all_values.hpp index a971b178a..0ca0e9ddb 100644 --- a/include/exec/sequence/ignore_all_values.hpp +++ b/include/exec/sequence/ignore_all_values.hpp @@ -19,8 +19,8 @@ #include "../../stdexec/execution.hpp" // include these after execution.hpp +#include "../../stdexec/__detail/__senders.hpp" #include "../../stdexec/__detail/__tuple.hpp" -#include "../../stdexec/__detail/__variant.hpp" #include "../sender_for.hpp" #include "../sequence_senders.hpp" @@ -58,7 +58,7 @@ namespace experimental::execution static_cast<_Receiver&&>(__rcvr)); }; - template + template struct __result_type { template @@ -83,31 +83,28 @@ namespace experimental::execution { STDEXEC::set_value(static_cast<_Receiver&&>(__rcvr)); } - else if constexpr (STDEXEC::__mapply::value != 0) + else if constexpr (STDEXEC::__mapply::value != 0) { - STDEXEC_ASSERT(__result_.index() != __variant_npos); - STDEXEC::__visit(__visit_fn, - static_cast<_ResultVariant&&>(__result_), - static_cast<_Receiver&&>(__rcvr)); + static_cast<_ResultsStorage&&>(__result_).__complete(__rcvr); } } - _ResultVariant __result_{STDEXEC::__no_init}; + _ResultsStorage __result_; __std::atomic __emplaced_{0}; }; - template + template struct __item_operation_base { STDEXEC_ATTRIBUTE(no_unique_address) _ItemReceiver __rcvr_; - __result_type<_ResultVariant>* __result_; + __result_type<_ResultsStorage>* __result_; }; - template + template struct __item_receiver { using receiver_concept = STDEXEC::receiver_tag; - __item_operation_base<_ItemReceiver, _ResultVariant>* __op_; + __item_operation_base<_ItemReceiver, _ResultsStorage>* __op_; template void set_value([[maybe_unused]] _Args&&... __args) noexcept @@ -117,7 +114,7 @@ namespace experimental::execution } template - requires __variant_emplaceable<_ResultVariant, + requires __variant_emplaceable<_ResultsStorage, __decayed_tuple, set_error_t, _Error> @@ -130,7 +127,9 @@ namespace experimental::execution } void set_stopped() noexcept - requires __variant_emplaceable<_ResultVariant, __decayed_tuple, set_stopped_t> + requires __variant_emplaceable<_ResultsStorage, + __decayed_tuple, + set_stopped_t> && __callable { // stop without error @@ -144,15 +143,15 @@ namespace experimental::execution } }; - template - struct __item_operation : __item_operation_base<_ItemReceiver, _ResultVariant> + template + struct __item_operation : __item_operation_base<_ItemReceiver, _ResultsStorage> { - using __base_t = __item_operation_base<_ItemReceiver, _ResultVariant>; - using __item_receiver_t = __item_receiver<_ItemReceiver, _ResultVariant>; + using __base_t = __item_operation_base<_ItemReceiver, _ResultsStorage>; + using __item_receiver_t = __item_receiver<_ItemReceiver, _ResultsStorage>; - __item_operation(__result_type<_ResultVariant>* __parent, - _Sender&& __sndr, - _ItemReceiver __rcvr) + __item_operation(__result_type<_ResultsStorage>* __parent, + _Sender&& __sndr, + _ItemReceiver __rcvr) noexcept(__nothrow_decay_copyable<_ItemReceiver> && __nothrow_connectable<_Sender, __item_receiver_t>) : __base_t{static_cast<_ItemReceiver&&>(__rcvr), __parent} @@ -167,7 +166,7 @@ namespace experimental::execution connect_result_t<_Sender, __item_receiver_t> __op_; }; - template + template struct __item_sender { using sender_concept = STDEXEC::sender_tag; @@ -175,10 +174,10 @@ namespace experimental::execution template using __operation_t = - __item_operation<__copy_cvref_t<_Self, _Sender>, _Receiver, _ResultVariant>; + __item_operation<__copy_cvref_t<_Self, _Sender>, _Receiver, _ResultsStorage>; template - using __item_receiver_t = __item_receiver<_Receiver, _ResultVariant>; + using __item_receiver_t = __item_receiver<_Receiver, _ResultsStorage>; template <__decays_to<__item_sender> _Self, STDEXEC::receiver_of _Receiver> @@ -192,23 +191,23 @@ namespace experimental::execution } STDEXEC_EXPLICIT_THIS_END(connect) - _Sender __sender_; - __result_type<_ResultVariant>* __parent_; + _Sender __sender_; + __result_type<_ResultsStorage>* __parent_; }; - template - struct __operation_base : __result_type<_ResultVariant> + template + struct __operation_base : __result_type<_ResultsStorage> { STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Receiver __rcvr_; }; - template + template struct __receiver { using receiver_concept = STDEXEC::receiver_tag; - constexpr explicit __receiver(__operation_base<_Receiver, _ResultVariant>* __op) noexcept + constexpr explicit __receiver(__operation_base<_Receiver, _ResultsStorage>* __op) noexcept : __op_{__op} {} @@ -218,7 +217,7 @@ namespace experimental::execution template [[nodiscard]] auto set_next(_Item&& __item) & noexcept(__nothrow_decay_copyable<_Item>) - -> __item_sender<__decay_t<_Item>, _ResultVariant> + -> __item_sender<__decay_t<_Item>, _ResultsStorage> { return {static_cast<_Item&&>(__item), __op_}; } @@ -245,32 +244,30 @@ namespace experimental::execution } private: - __operation_base<_Receiver, _ResultVariant>* __op_; + __operation_base<_Receiver, _ResultsStorage>* __op_; }; - template - using __result_variant_ = __transform_reduce_completion_signatures_t< - _Sigs, - __mconst<__mlist<>>::__f, - __mcompose_q<__mlist, __mbind_front_q<__decayed_tuple, set_error_t>::__f>::__f, - __mlist<__tuple>, - __mconcat<__qq<__variant>>::__f>; + template + using __is_set_value_signature_t = + __mbool<__same_as>>; + // Storage for the non-set_value completions template using __result_variant_t = - __result_variant_<__sequence_completion_signatures_of_t<_Sender, _Env>>; + __mapply<__mremove_if<__q1<__is_set_value_signature_t>, __qq<__results_storage>>, + __sequence_completion_signatures_of_t<_Sender, _Env>>; template struct __operation : __operation_base<_Receiver, __result_variant_t<_Sender, env_of_t<_Receiver>>> { - using _ResultVariant = __result_variant_t<_Sender, env_of_t<_Receiver>>; - using __base_type = __operation_base<_Receiver, _ResultVariant>; - using __receiver_t = __receiver<_Receiver, _ResultVariant>; + using __variant_t = __result_variant_t<_Sender, env_of_t<_Receiver>>; + using __base_t = __operation_base<_Receiver, __variant_t>; + using __receiver_t = __receiver<_Receiver, __variant_t>; explicit __operation(_Sender&& __sndr, _Receiver __rcvr) noexcept(__nothrow_subscribable<_Sender, __receiver_t>) - : __base_type{{}, static_cast<_Receiver&&>(__rcvr)} + : __base_t{{}, static_cast<_Receiver&&>(__rcvr)} , __op_{exec::subscribe(static_cast<_Sender&&>(__sndr), __receiver_t{this})} {} diff --git a/include/exec/sequence/merge_each.hpp b/include/exec/sequence/merge_each.hpp index 17273523e..ee099255b 100644 --- a/include/exec/sequence/merge_each.hpp +++ b/include/exec/sequence/merge_each.hpp @@ -238,13 +238,8 @@ namespace experimental::execution void start() & noexcept { // emit delayed error into the sequence - STDEXEC::__visit( - [this](auto&& __error) noexcept - { - STDEXEC::set_error(static_cast<_ErrorReceiver&&>(__rcvr_), - static_cast(__error)); - }, - static_cast<_ErrorStorage&&>(*__op_->__error_storage_)); + STDEXEC::__visit(STDEXEC::__mk_completion_fn(STDEXEC::set_error, __rcvr_), + static_cast<_ErrorStorage&&>(*__op_->__error_storage_)); } _ErrorReceiver __rcvr_; diff --git a/include/exec/when_any.hpp b/include/exec/when_any.hpp index 5389a3e9a..5151d1158 100644 --- a/include/exec/when_any.hpp +++ b/include/exec/when_any.hpp @@ -16,6 +16,7 @@ */ #pragma once +#include "../stdexec/__detail/__storage.hpp" #include "../stdexec/execution.hpp" #include "../stdexec/stop_token.hpp" @@ -69,22 +70,8 @@ namespace experimental::execution }; template - using __result_type_t = - __for_each_completion_signature_t<__minvoke<__completions_fn<_Env>, _CvSenders...>, - __decayed_tuple, - __uniqued_variant>; - - template - auto __make_visitor_fn(_Receiver& __rcvr) noexcept - { - return [&__rcvr](_Tuple&& __result) noexcept - { - STDEXEC::__apply( - [&__rcvr](auto __tag, _As&... __args) noexcept - { __tag(static_cast<_Receiver&&>(__rcvr), static_cast<_As&&>(__args)...); }, - __result); - }; - } + using __results_storage_t = + __mapply_q<__results_storage, __minvoke<__completions_fn<_Env>, _CvSenders...>>; template struct __opstate_base @@ -108,7 +95,7 @@ namespace experimental::execution __std::atomic __count_{}; _Receiver __rcvr_; - _ResultVariant __result_{__no_init}; + _ResultVariant __result_; template void notify(_Tag, _Args&&... __args) noexcept @@ -151,9 +138,7 @@ namespace experimental::execution STDEXEC::set_stopped(static_cast<_Receiver&&>(__rcvr_)); return; } - STDEXEC_ASSERT(!__result_.__is_valueless()); - STDEXEC::__visit(__when_any::__make_visitor_fn(__rcvr_), - static_cast<_ResultVariant&&>(__result_)); + std::move(__result_).__complete(__rcvr_); } } }; @@ -197,11 +182,11 @@ namespace experimental::execution template struct __opstate - : __opstate_base<_Receiver, __result_type_t, _CvSenders...>> + : __opstate_base<_Receiver, __results_storage_t, _CvSenders...>> { - using __result_t = __result_type_t, _CvSenders...>; - using __receiver_t = __receiver<_Receiver, __result_t>; - using __op_base_t = __opstate_base<_Receiver, __result_t>; + using __storage_t = __results_storage_t, _CvSenders...>; + using __receiver_t = __receiver<_Receiver, __storage_t>; + using __op_base_t = __opstate_base<_Receiver, __storage_t>; using __opstates_t = __tuple...>; static constexpr bool __nothrow_construct = (__nothrow_connectable<_CvSenders, __receiver_t> @@ -236,12 +221,6 @@ namespace experimental::execution template struct __sender { - template - using __result_t = __result_type_t<_Env, __copy_cvref_t<_Self, _Senders>...>; - - template - using __receiver_t = __receiver<_Receiver, __result_t<_Self, env_of_t<_Receiver>>>; - template using __opstate_t = __opstate<_Receiver, __copy_cvref_t<_Self, _Senders>...>; diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index 6e0baf4e3..e64fa2359 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -27,10 +27,10 @@ #include "__schedulers.hpp" #include "__sender_adaptor_closure.hpp" #include "__senders.hpp" +#include "__storage.hpp" #include "__transform_completion_signatures.hpp" #include "__tuple.hpp" #include "__utility.hpp" -#include "__variant.hpp" // IWYU pragma: keep for __variant #include "__prologue.hpp" @@ -40,24 +40,6 @@ namespace STDEXEC // [execution.senders.adaptors.continues_on] namespace __trnsfr { - // Compute a variant type that is capable of storing the results of the - // input sender when it completes. The variant has type: - // variant< - // monostate, - // tuple, - // tuple...>, - // tuple...>, - // ... - // tuple>, - // tuple>, - // ... - // > - template - using __results_of_t = - __for_each_completion_signature_t<__completion_signatures_of_t<_CvSender, _Env>, - __decayed_tuple, - __munique<__qq>::__f>; - template using __decay_value_sig = set_value_t (*)(__decay_t<_Values>...); @@ -80,26 +62,13 @@ namespace STDEXEC using __completions_t = __completions_impl_t<_Scheduler, __completion_signatures_of_t<_CvSender, _Env...>, _Env...>; - template - STDEXEC_ATTRIBUTE(always_inline) - constexpr auto __make_visitor_fn(_State* __state) noexcept - { - return [__state](_Tup& __tupl) noexcept -> void - { - STDEXEC::__apply( - [&](_Tag, _Args&... __args) noexcept -> void - { _Tag()(static_cast<_State&&>(*__state).__rcvr_, static_cast<_Args&&>(__args)...); }, - __tupl); - }; - } - template struct __state_base { - using __variant_t = __results_of_t<__child_of<_Sexpr>, env_of_t<_Receiver>>; + using __storage_t = __storage_for_t<__child_of<_Sexpr>, env_of_t<_Receiver>>; _Receiver __rcvr_; - __variant_t __data_{__no_init}; + __storage_t __data_; }; // This receiver is to be completed on the execution context associated with the scheduler. When @@ -113,7 +82,7 @@ namespace STDEXEC constexpr void set_value() noexcept { - STDEXEC::__visit(__trnsfr::__make_visitor_fn(__state_), __state_->__data_); + std::move(__state_->__data_).__complete(__state_->__rcvr_); } template @@ -140,7 +109,6 @@ namespace STDEXEC template struct __state : __state_base<_Sexpr, _Receiver> { - using __variant_t = __results_of_t<__child_of<_Sexpr>, env_of_t<_Receiver>>; using __receiver2_t = __receiver2<_Sexpr, _Receiver>; constexpr explicit __state(_Scheduler __sched, _Receiver&& __rcvr) diff --git a/include/stdexec/__detail/__finally.hpp b/include/stdexec/__detail/__finally.hpp index 56b7b487a..43f7d151e 100644 --- a/include/stdexec/__detail/__finally.hpp +++ b/include/stdexec/__detail/__finally.hpp @@ -24,10 +24,10 @@ #include "__schedulers.hpp" #include "__sender_adaptor_closure.hpp" #include "__senders.hpp" +#include "__storage.hpp" #include "__transform_completion_signatures.hpp" #include "__tuple.hpp" #include "__utility.hpp" -#include "__variant.hpp" #include "__prologue.hpp" @@ -77,17 +77,14 @@ namespace STDEXEC struct __result_variant_fn { template - using __f = __for_each_completion_signature_t< - completion_signatures_of_t<_InitialSender, env_of_t<_Receiver>>, - __decayed_tuple, - __variant>; + using __f = __storage_for_t<_InitialSender, env_of_t<_Receiver>>; }; template <> struct __result_variant_fn { template - using __f = __variant<>; + using __f = __results_storage<>; }; // If the final sender has no value completions, then we don't need to store the @@ -98,31 +95,13 @@ namespace STDEXEC _CvInitialSender, _Receiver>; - template + template struct __opstate_base { _Receiver __rcvr_{}; STDEXEC_ATTRIBUTE(no_unique_address) _Env2 const __env2_{}; - _ResultType __result_{__no_init}; // __variant<__tuple, ...> - }; - - struct __applier - { - template - constexpr void operator()(_Receiver& __rcvr, _Tag, _Args&&... __args) noexcept - { - _Tag()(static_cast<_Receiver&&>(__rcvr), static_cast<_Args&&>(__args)...); - } - }; - - struct __visitor - { - template - constexpr void operator()(_Receiver& __rcvr, _Tuple&& __tuple) noexcept - { - __apply(__applier{}, static_cast<_Tuple&&>(__tuple), __rcvr); - } + _Storage __result_; // __variant<__tuple, ...> }; using __mk_secondary_env_t = @@ -135,7 +114,7 @@ namespace STDEXEC template using __final_env_t = __join_env_t<_Env2 const &, __fwd_env_t<_ReceiverEnv>>; - template + template struct __final_receiver { using receiver_concept = receiver_tag; @@ -143,7 +122,7 @@ namespace STDEXEC constexpr void set_value() noexcept { - __visit(__visitor{}, std::move(__opstate_->__result_), __opstate_->__rcvr_); + std::move(__opstate_->__result_).__complete(__opstate_->__rcvr_); } template @@ -164,23 +143,23 @@ namespace STDEXEC return __env::__join(__opstate_->__env2_, __fwd_env(STDEXEC::get_env(__opstate_->__rcvr_))); } - __opstate_base<_ResultType, _Receiver, _Env2>* __opstate_; + __opstate_base<_Storage, _Receiver, _Env2>* __opstate_; }; - template - struct __final_opstate : __opstate_base<_ResultType, _Receiver, _Env2> + template + struct __final_opstate : __opstate_base<_Storage, _Receiver, _Env2> { - using __results_t = _ResultType; + using __results_t = _Storage; using __cleanup_callback_t = void(__final_opstate*) noexcept; - using __final_receiver_t = __final_receiver<_ResultType, _Receiver, _Env2>; + using __final_receiver_t = __final_receiver<_Storage, _Receiver, _Env2>; using __final_opstate_t = connect_result_t<_CvFinalSender, __final_receiver_t>; constexpr explicit __final_opstate(__cleanup_callback_t* __cleanup_callback, _CvFinalSender&& __final, _Receiver&& __rcvr, _Env2 __env2) noexcept - : __opstate_base<_ResultType, _Receiver, _Env2>{static_cast<_Receiver&&>(__rcvr), - static_cast<_Env2&&>(__env2)} + : __opstate_base<_Storage, _Receiver, _Env2>{static_cast<_Receiver&&>(__rcvr), + static_cast<_Env2&&>(__env2)} , __cleanup_callback_{__cleanup_callback} , __final_opstate_( STDEXEC::connect(static_cast<_CvFinalSender&&>(__final), __final_receiver_t{this})) @@ -220,7 +199,7 @@ namespace STDEXEC __final_opstate_t __final_opstate_; }; - template + template struct __initial_receiver; template @@ -270,7 +249,7 @@ namespace STDEXEC __optional<__initial_opstate_t> __initial_opstate_{}; }; - template + template struct __initial_receiver { using receiver_concept = receiver_tag; @@ -300,7 +279,7 @@ namespace STDEXEC return STDEXEC::get_env(__opstate_->__rcvr_); } - __final_opstate<_CvFinalSender, _ResultType, _Receiver, _Env2>* __opstate_; + __final_opstate<_CvFinalSender, _Storage, _Receiver, _Env2>* __opstate_; }; template diff --git a/include/stdexec/__detail/__senders.hpp b/include/stdexec/__detail/__senders.hpp index 51101de60..4cac52f9e 100644 --- a/include/stdexec/__detail/__senders.hpp +++ b/include/stdexec/__detail/__senders.hpp @@ -31,31 +31,30 @@ namespace STDEXEC namespace __detail { template - extern __undefined<_Sig> __tag_of_sig_v; + extern __undefined<_Sig> __signature_tag_v; template - extern _Tag __tag_of_sig_v<_Tag(_Args...)>; + extern _Tag __signature_tag_v<_Tag(_Args...)>; template - extern _Tag __tag_of_sig_v<_Tag (*)(_Args...)>; - - template - using __tag_of_sig_t = decltype(__tag_of_sig_v<_Sig>); + extern _Tag __signature_tag_v<_Tag (*)(_Args...)>; template requires false using __nofail_t = _Error; } // namespace __detail + template + using __signature_tag_t = decltype(__detail::__signature_tag_v<_Sig>); + template concept sender_of = sender_in<_Sender, _Env...> - && __same_as< - __mlist<_SetSig>, - __gather_completions_t<__detail::__tag_of_sig_t<_SetSig>, - __completion_signatures_of_t<_Sender, _Env...>, - __mcompose<__qq<__mlist>, __qf<__detail::__tag_of_sig_t<_SetSig>>>, - __mconcat<__qq<__mlist>>>>; + && __same_as<__mlist<_SetSig>, + __gather_completions_t<__signature_tag_t<_SetSig>, + __completion_signatures_of_t<_Sender, _Env...>, + __mcompose<__qq<__mlist>, __qf<__signature_tag_t<_SetSig>>>, + __mconcat<__qq<__mlist>>>>; template concept __nofail_sender = __never_sends; diff --git a/include/stdexec/__detail/__spawn_future.hpp b/include/stdexec/__detail/__spawn_future.hpp index 9ca00d16d..2f65d8dcc 100644 --- a/include/stdexec/__detail/__spawn_future.hpp +++ b/include/stdexec/__detail/__spawn_future.hpp @@ -27,9 +27,9 @@ #include "__scope_concepts.hpp" #include "__senders.hpp" #include "__spawn_common.hpp" +#include "__storage.hpp" #include "__tuple.hpp" #include "__type_traits.hpp" -#include "__variant.hpp" #include #include @@ -39,54 +39,17 @@ namespace STDEXEC { + template + using __decayed_signature_t = __mapply<__mtransform<__q1<__decay_t>, __q<__fn_t>>, _Signature>; + + template + inline constexpr bool __nothrow_storable_signature = + __mapply<__qq<__nothrow_decay_copyable_t>, _Signature>::value; ///////////////////////////////////////////////////////////////////////////// // [exec.spawn.future] namespace __spawn_future { - - template - struct __future_sig_fns; - - template - struct __future_sig_fns<_Tag(_Args...)> - { - static constexpr bool __is_nothrow_storable = __nothrow_decay_copyable<_Args...>; - - using __decayed_sig = _Tag(__decay_t<_Args>...); - }; - - // [exec.spawn.future] paragraph 4 - template - using __as_tuple = __mapply_q<__decayed_tuple, _Sig>; - - template - using __decayed_sig = __future_sig_fns<_Sig>::__decayed_sig; - - template - inline constexpr bool __sigs_are_nothrow_storable = - (__future_sig_fns<_Sigs>::__is_nothrow_storable && ...); - - template - struct __future_variant - { - // this case handles _NothrowStorable == true - using type = __uniqued_variant<__decayed_tuple, __as_tuple<_Sigs>...>; - }; - - template - struct __future_variant - { - using type = __uniqued_variant<__decayed_tuple, - __decayed_tuple, - __as_tuple<_Sigs>...>; - }; - - template - using __future_variant_t = - // [exec.spawn.future] paragraphs 4.1 and 4.2 - __future_variant<__sigs_are_nothrow_storable<_Sigs...>, _Sigs...>::type; - struct __try_cancelable { explicit __try_cancelable(void (*__try_cancel)(__try_cancelable*) noexcept) noexcept @@ -129,8 +92,9 @@ namespace STDEXEC // stores the results for later consumption by the future; and // - the stop token provided by the future's receiver can no-throw-construct a stop callback // in the future's operation state - using type = - __mcall<__munique<__qq>, set_stopped_t(), __decayed_sig<_Sigs>...>; + using type = __mcall<__munique<__qq>, + set_stopped_t(), + __decayed_signature_t<_Sigs>...>; }; template @@ -139,7 +103,7 @@ namespace STDEXEC using type = __mcall<__munique<__qq>, set_stopped_t(), set_error_t(std::exception_ptr), - __decayed_sig<_Sigs>...>; + __decayed_signature_t<_Sigs>...>; }; template @@ -149,9 +113,10 @@ namespace STDEXEC __future_stop_callback>; template - using __future_completions_t = __future_completions< - __stop_callback_is_nothrow_constructible<_Env> && __sigs_are_nothrow_storable<_Sigs...>, - _Sigs...>::type; + using __future_completions_t = + __future_completions<__stop_callback_is_nothrow_constructible<_Env> + && (__nothrow_storable_signature<_Sigs> && ...), + _Sigs...>::type; // [exec.spawn.future] paragraph 3 template @@ -160,11 +125,11 @@ namespace STDEXEC template struct __spawn_future_state_base> : __try_cancelable { - using __variant_t = __future_variant_t<_Sigs...>; + using __variant_t = __results_storage; template using __completions_t = __future_completions_t<_Env, _Sigs...>; - __variant_t __result_{__no_init}; + __variant_t __result_; explicit __spawn_future_state_base( void (*__try_cancel)(__try_cancelable*) noexcept, @@ -195,10 +160,10 @@ namespace STDEXEC __spawn_future_state_base<_Completions>* __state_; - template - void set_value(_T&&... __t) && noexcept + template + void set_value(_Ts&&... __ts) && noexcept { - __set_complete(static_cast<_T&&>(__t)...); + __set_complete(static_cast<_Ts&&>(__ts)...); } template @@ -213,20 +178,17 @@ namespace STDEXEC } private: - template - void __set_complete(_T&&... __t) noexcept + template + void __set_complete(_Ts&&... __ts) noexcept { - constexpr bool __non_throwing = (__nothrow_constructible_from<__decay_t<_T>, _T> && ...); - STDEXEC_TRY { - __state_->__result_.template emplace<__decayed_tuple<_CPO, _T...>>(_CPO{}, - static_cast<_T&&>( - __t)...); + using __tuple_t = __decayed_tuple<_Tag, _Ts...>; + __state_->__result_.template emplace<__tuple_t>(_Tag{}, static_cast<_Ts&&>(__ts)...); } STDEXEC_CATCH_ALL { - if constexpr (!__non_throwing) + if constexpr (!__nothrow_decay_copyable<_Ts...>) { using tuple_t = __decayed_tuple; __state_->__result_.template emplace(set_error_t{}, std::current_exception()); @@ -419,14 +381,7 @@ namespace STDEXEC // NOTE: __rcvr's type is unconstrained because the thing we pass doesn't satisfy receiver void __do_consume(auto& __rcvr) noexcept { - __visit( - [&__rcvr](auto&& __tuple) noexcept - { - __apply([&__rcvr](auto cpo, auto&&... __vals) - { cpo(std::move(__rcvr), std::move(__vals)...); }, - std::move(__tuple)); // NOLINT(bugprone-move-forwarding-reference) - }, - std::move(this->__result_)); + std::move(this->__result_).__complete(__rcvr); } static void __do_complete(__base* __base_ptr) noexcept @@ -765,11 +720,11 @@ namespace STDEXEC __callback_; }; - template + template void __complete(_Ts&&... __ts) noexcept { std::destroy_at(std::addressof(__callback_)); - _CPO{}(std::move(__rcvr_), static_cast<_Ts&&>(__ts)...); + _Tag{}(std::move(__rcvr_), static_cast<_Ts&&>(__ts)...); } }; diff --git a/include/stdexec/__detail/__storage.hpp b/include/stdexec/__detail/__storage.hpp new file mode 100644 index 000000000..fa83cb490 --- /dev/null +++ b/include/stdexec/__detail/__storage.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-2025 NVIDIA Corporation + * + * Licensed under the Apache License Version 2.0 with LLVM Exceptions + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "__execution_fwd.hpp" + +#include "__get_completion_signatures.hpp" +#include "__meta.hpp" +#include "__sender_concepts.hpp" +#include "__tuple.hpp" +#include "__variant.hpp" + +#include "__prologue.hpp" + +namespace STDEXEC +{ + namespace __detail + { + struct __applier + { + template + constexpr void operator()(_Receiver& __rcvr, _Tag, _Args&&... __args) noexcept + { + _Tag()(static_cast<_Receiver&&>(__rcvr), static_cast<_Args&&>(__args)...); + } + }; + + struct __visitor + { + template + constexpr void operator()(_Receiver& __rcvr, _Tuple&& __tupl) noexcept + { + __apply(__applier(), static_cast<_Tuple&&>(__tupl), __rcvr); + } + }; + + // Add storage for an exception_ptr if the result datums are not all nothrow + // decay-copyable. + template + using __tuples_for_t = + __if<__nothrow_decay_copyable_t<_Args...>, + __mlist<__decayed_tuple<_Tag, _Args...>>, + __mlist<__decayed_tuple<_Tag, _Args...>, __tuple>>; + } // namespace __detail + + // A variant type that is capable of storing the result datums of the specified + // completion signatures. + template + struct __results_storage + : __mcall<__mconcat<__qq<__uniqued_variant>>, + __mapply_q<__detail::__tuples_for_t, _Signatures>...> + { + constexpr __results_storage() noexcept + : __results_storage::__variant(__no_init) + {} + + template + STDEXEC_ATTRIBUTE(host, device) + constexpr STDEXEC_EXPLICIT_THIS_BEGIN(void __complete)(this _Self&& __self, + _Receiver& __rcvr) noexcept + { + __visit(__detail::__visitor(), static_cast<_Self&&>(__self), __rcvr); + } + STDEXEC_EXPLICIT_THIS_END(__complete) + }; + + template + requires sender_in<_CvSender, _Env> + using __storage_for_t = + __mapply_q<__results_storage, __completion_signatures_of_t<_CvSender, _Env>>; +} // namespace STDEXEC + +#include "__epilogue.hpp" diff --git a/include/stdexec/__detail/__task_scheduler.hpp b/include/stdexec/__detail/__task_scheduler.hpp index 81ccf1fe9..f7d6aa2ff 100644 --- a/include/stdexec/__detail/__task_scheduler.hpp +++ b/include/stdexec/__detail/__task_scheduler.hpp @@ -420,28 +420,21 @@ namespace STDEXEC { return [=, &__fn](auto& __args) { - constexpr bool __valid_args = !__same_as; - // runtime assert that we never take this path without valid args from the predecessor: - STDEXEC_ASSERT(__valid_args); - - if constexpr (__valid_args) - { - // If we are not parallelizing, we need to run all the iterations sequentially. - size_t const __increments = _Parallelize ? 1 : __shape; - // Precompose the function with the arguments so we don't have to do it every iteration. - auto __precomposed_fn = __apply( - [&](auto&... __as) - { - return [&](size_t __i) -> void - { - __fn(__i, __as...); - }; - }, - __args); - for (size_t __i = __begin; __i < __begin + __increments; ++__i) + // If we are not parallelizing, we need to run all the iterations sequentially. + size_t const __increments = _Parallelize ? 1 : __shape; + // Precompose the function with the arguments so we don't have to do it every iteration. + auto __precomposed_fn = __apply( + [&](auto&... __as) { - __precomposed_fn(__i); - } + return [&](size_t __i) -> void + { + __fn(__i, __as...); + }; + }, + __args); + for (size_t __i = __begin; __i < __begin + __increments; ++__i) + { + __precomposed_fn(__i); } }; } @@ -480,13 +473,7 @@ namespace STDEXEC { return [=, &__fn](auto& __args) { - constexpr bool __valid_args = !__same_as; - STDEXEC_ASSERT(__valid_args); - - if constexpr (__valid_args) - { - __apply(__apply_bulk_execute<_Parallelize, _Fn>{__begin, __end, __shape, __fn}, __args); - } + __apply(__apply_bulk_execute<_Parallelize, _Fn>{__begin, __end, __shape, __fn}, __args); }; } @@ -513,19 +500,10 @@ namespace STDEXEC constexpr void set_value() noexcept final { // Send the stored values to the downstream receiver. - __visit( - [this](auto& __tupl) - { - constexpr bool __valid_args = __not_same_as; - // runtime assert that we never take this path without valid args from the predecessor: - STDEXEC_ASSERT(__valid_args); - - if constexpr (__valid_args) - { - __apply(STDEXEC::set_value, std::move(__tupl), std::move(this->__rcvr_)); - } - }, - __values_); + __visit([](auto& __rcvr, auto& __tupl) + { __apply(STDEXEC::set_value, std::move(__tupl), std::move(__rcvr)); }, + __values_, + this->__rcvr_); } //! Actually runs the bulk operation over the specified range. @@ -584,10 +562,8 @@ namespace STDEXEC } private: - using __values_t = value_types_of_t<_Sndr, - __fwd_env_t>, - __decayed_tuple, - __mbind_front_q<__variant, __monostate>::__f>; + using __values_t = + value_types_of_t<_Sndr, __fwd_env_t>, __decayed_tuple, __uniqued_variant>; using __rcvr_t = __bulk_receiver<_BulkTag, _Policy, _Fn, _Rcvr, __values_t>; using __opstate1_t = connect_result_t<_Sndr, __rcvr_t>; diff --git a/include/stdexec/__detail/__variant.hpp b/include/stdexec/__detail/__variant.hpp index b78e1e341..cbe5cab04 100644 --- a/include/stdexec/__detail/__variant.hpp +++ b/include/stdexec/__detail/__variant.hpp @@ -341,10 +341,11 @@ namespace STDEXEC } template + requires(__callable<_Fn, _Us..., __copy_cvref_t<_Self, _Ts>> && ...) STDEXEC_ATTRIBUTE(host, device) static constexpr auto __visit(_Fn &&__fn, _Self &&__self, _Us &&...__us) noexcept((__nothrow_callable<_Fn, _Us..., __copy_cvref_t<_Self, _Ts>> && ...)) - -> __call_result_t<_Fn, _Us..., __copy_cvref_t<_Self, __at_t<0>>> + -> decltype(auto) { using __result_t = __call_result_t<_Fn, _Us..., __copy_cvref_t<_Self, __at_t<0>>>; using __visit_fn_t = decltype(&__var::__visit_alt<0, __result_t, _Fn, _Self, _Us...>); diff --git a/test/exec/test_completion_signatures.cpp b/test/exec/test_completion_signatures.cpp index 5931bce6f..1ea84ad1c 100644 --- a/test/exec/test_completion_signatures.cpp +++ b/test/exec/test_completion_signatures.cpp @@ -129,8 +129,7 @@ namespace template constexpr bool operator()(Sig*) const noexcept { - return STDEXEC::__detail::__tag_of_sig_t::__disposition - == STDEXEC::__disposition::__value; + return STDEXEC::__signature_tag_t::__disposition == STDEXEC::__disposition::__value; } };