diff --git a/include/exec/__detail/__basic_sequence.hpp b/include/exec/__detail/__basic_sequence.hpp index b91d906c1..04f006cc3 100644 --- a/include/exec/__detail/__basic_sequence.hpp +++ b/include/exec/__detail/__basic_sequence.hpp @@ -31,76 +31,53 @@ namespace exec { using __t = __basic_sequence_sender; }; - template - struct __seqexpr { - using sender_concept = sequence_sender_t; - using __t = __seqexpr; - using __id = __seqexpr; - using __desc_t = decltype(_DescriptorFn()); - using __tag_t = __desc_t::__tag; - using __captures_t = - STDEXEC::__minvoke<__desc_t, STDEXEC::__q>; - - static constexpr auto __tag() noexcept -> __tag_t { - return {}; - } - - mutable __captures_t __impl_; + namespace { + template + struct __seqexpr + : STDEXEC::__minvoke> { + using sender_concept = sequence_sender_t; + using __t = __seqexpr; + using __id = __seqexpr; + using __desc_t = decltype(_DescriptorFn()); + using __tag_t = __desc_t::__tag; + + static constexpr auto __tag() noexcept -> __tag_t { + return {}; + } + + template + auto get_env() const noexcept -> decltype(_Self::__tag().get_env(*this)) { + static_assert(noexcept(_Self::__tag().get_env(*this))); + return _Self::__tag().get_env(*this); + } + + template + static consteval auto get_completion_signatures() { + static_assert(STDEXEC::__decays_to_derived_from<_Self, __seqexpr>); + return __tag_t::template get_completion_signatures<_Self, _Env...>(); + } + + template + static consteval auto get_item_types() { + static_assert(STDEXEC::__decays_to_derived_from<_Self, __seqexpr>); + return __tag_t::template get_item_types<_Self, _Env...>(); + } + + template + static auto subscribe(_Self&& __self, _Receiver&& __rcvr) noexcept(noexcept( + __self.__tag().subscribe(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)))) + -> decltype(__self.__tag() + .subscribe(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr))) { + static_assert(STDEXEC::__decays_to_derived_from<_Self, __seqexpr>); + return __tag_t::subscribe(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); + } + }; template STDEXEC_ATTRIBUTE(host, device) - explicit __seqexpr(_Tag, _Data&& __data, _Child&&... __child) - : __impl_( - STDEXEC::__detail::__captures( - _Tag(), - static_cast<_Data&&>(__data), - static_cast<_Child&&>(__child)...)) { - } - - template _Self = __seqexpr> - auto get_env() const noexcept -> decltype(_Self::__tag().get_env(*this)) { - static_assert(noexcept(_Self::__tag().get_env(*this))); - return _Self::__tag().get_env(*this); - } - - template _Self, class... _Env> - static consteval auto get_completion_signatures() { - return __tag_t::template get_completion_signatures<_Self, _Env...>(); - } - - template _Self, class... _Env> - static consteval auto get_item_types() { - return __tag_t::template get_item_types<_Self, _Env...>(); - } - - template _Self, STDEXEC::receiver _Receiver> - static auto subscribe(_Self&& __self, _Receiver&& __rcvr) noexcept(noexcept( - __self.__tag().subscribe(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)))) - -> decltype(__self.__tag() - .subscribe(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr))) { - return __tag_t::subscribe(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); - } - - template - static auto apply(_Sequence&& __sequence, _ApplyFn&& __fun) - noexcept(STDEXEC::__nothrow_callable< - STDEXEC::__detail::__impl_of<_Sequence>, - STDEXEC::__copy_cvref_fn<_Sequence>, - _ApplyFn - >) - -> STDEXEC::__call_result_t< - STDEXEC::__detail::__impl_of<_Sequence>, - STDEXEC::__copy_cvref_fn<_Sequence>, - _ApplyFn - > { - return static_cast<_Sequence&&>(__sequence) - .__impl_(STDEXEC::__copy_cvref_fn<_Sequence>(), static_cast<_ApplyFn&&>(__fun)); - } - }; - - template - STDEXEC_ATTRIBUTE(host, device) - __seqexpr(_Tag, _Data, _Child...) -> __seqexpr; + __seqexpr(_Tag, _Data, _Child...) + -> __seqexpr; + } // namespace template using __seqexpr_t = __seqexpr; diff --git a/include/exec/finally.hpp b/include/exec/finally.hpp index c0cdfffa5..621491105 100644 --- a/include/exec/finally.hpp +++ b/include/exec/finally.hpp @@ -313,15 +313,15 @@ namespace exec { template static auto transform_sender(STDEXEC::set_value_t, _Sender&& __sndr, __ignore) { - return __sexpr_apply( - static_cast<_Sender&&>(__sndr), + return __apply( []( __ignore, __ignore, _Initial&& __initial, _Final&& __final) { using __result_sndr_t = __t<__sender<__id<__decay_t<_Initial>>, __id<__decay_t<_Final>>>>; return __result_sndr_t{ static_cast<_Initial&&>(__initial), static_cast<_Final&&>(__final)}; - }); + }, + static_cast<_Sender&&>(__sndr)); } }; } // namespace __final diff --git a/include/exec/libdispatch_queue.hpp b/include/exec/libdispatch_queue.hpp index 977b4da6e..a755343e6 100644 --- a/include/exec/libdispatch_queue.hpp +++ b/include/exec/libdispatch_queue.hpp @@ -120,8 +120,8 @@ namespace exec { auto sched = STDEXEC::get_completion_scheduler(STDEXEC::get_env(sndr), env); static_assert(std::is_same_v); - return STDEXEC::__sexpr_apply( - std::forward(sndr), __libdispatch_bulk::transform_bulk{*sched.queue_}); + return STDEXEC::__apply( + __libdispatch_bulk::transform_bulk{*sched.queue_}, std::forward(sndr)); } else { return STDEXEC::__not_a_sender< STDEXEC::_WHAT_<>(CANNOT_DISPATCH_THE_BULK_ALGORITHM_TO_THE_LIBDISPATCH_SCHEDULER), diff --git a/include/exec/repeat_effect_until.hpp b/include/exec/repeat_effect_until.hpp index 0466ad41d..d203335b0 100644 --- a/include/exec/repeat_effect_until.hpp +++ b/include/exec/repeat_effect_until.hpp @@ -89,8 +89,7 @@ namespace exec { trampoline_scheduler __sched_; __repeat_effect_state(_Sender &&__sndr, _Receiver &) - : __child_( - __sexpr_apply(static_cast<_Sender &&>(__sndr), STDEXEC::__detail::__get_data())) { + : __child_(STDEXEC::__get<1>(static_cast<_Sender &&>(__sndr))) { __connect(); } @@ -227,10 +226,11 @@ namespace exec { template auto transform_sender(STDEXEC::set_value_t, _Sender &&__sndr, __ignore) { - return __sexpr_apply( - static_cast<_Sender &&>(__sndr), [](__ignore, __ignore, _Child __child) { + return STDEXEC::__apply( + [](__ignore, __ignore, _Child __child) { return __make_sexpr<__repeat_effect_until_tag>(std::move(__child)); - }); + }, + static_cast<_Sender &&>(__sndr)); } }; @@ -255,9 +255,9 @@ namespace exec { template auto transform_sender(STDEXEC::set_value_t, _Sender &&__sndr, __ignore) { - return __sexpr_apply(static_cast<_Sender &&>(__sndr), [](__ignore, __ignore, auto __child) { + return STDEXEC::__apply([](__ignore, __ignore, auto __child) { return repeat_effect_until_t{}(STDEXEC::then(std::move(__child), _never{})); - }); + }, static_cast<_Sender &&>(__sndr)); } }; } // namespace __repeat_effect diff --git a/include/exec/repeat_n.hpp b/include/exec/repeat_n.hpp index 706ff0796..ad657851c 100644 --- a/include/exec/repeat_n.hpp +++ b/include/exec/repeat_n.hpp @@ -92,7 +92,7 @@ namespace exec { using __child_op_t = STDEXEC::connect_result_t<__child_on_sched_sender_t, __receiver_t>; __repeat_n_state(_Sender &&__sndr, _Receiver &) - : __pair_(__sexpr_apply(static_cast<_Sender &&>(__sndr), STDEXEC::__detail::__get_data())) { + : __pair_(STDEXEC::__get<1>(static_cast<_Sender &&>(__sndr))) { // Q: should we skip __connect() if __count_ == 0? __connect(); } @@ -208,11 +208,11 @@ namespace exec { template > auto transform_sender(set_value_t, _Sender &&__sndr, __ignore) noexcept(_NoThrow) { - return __sexpr_apply( - static_cast<_Sender &&>(__sndr), + return __apply( [](__ignore, std::size_t __count, _Child __child) noexcept(_NoThrow) { return __make_sexpr<__repeat_n_tag>(__child_count_pair{std::move(__child), __count}); - }); + }, + static_cast<_Sender &&>(__sndr)); } }; } // namespace __repeat_n diff --git a/include/exec/sequence/ignore_all_values.hpp b/include/exec/sequence/ignore_all_values.hpp index 8115d909c..d518472e3 100644 --- a/include/exec/sequence/ignore_all_values.hpp +++ b/include/exec/sequence/ignore_all_values.hpp @@ -330,24 +330,12 @@ namespace exec { template using __receiver_t = __t<__receiver<__id<_Receiver>, _ResultVariant<_Child, _Receiver>>>; - // static constexpr auto get_state = - // [](_Sender&& __sndr, _Receiver& __rcvr) noexcept( - // __nothrow_callable<__sexpr_apply_t, _Sender, __connect_fn<__rcvr_ref_t<_Receiver>>>) - // -> __call_result_t<__sexpr_apply_t, _Sender, __connect_fn<__rcvr_ref_t<_Receiver>>> { - // static_assert(sender_expr_for<_Sender, ignore_all_values_t>); - // return __sexpr_apply(static_cast<_Sender&&>(__sndr), __connect_fn{__ref_rcvr(__rcvr)}); - // }; - - // static constexpr auto start = [](auto& __state, __ignore, __ignore) noexcept -> void { - // STDEXEC::start(__state); - // }; - static constexpr auto connect = [](_Sender&& __sndr, _Receiver __rcvr) noexcept( - __nothrow_callable<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>) - -> __call_result_t<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>> { + __nothrow_applicable<__connect_fn<_Receiver>, _Sender>) + -> __apply_result_t<__connect_fn<_Receiver>, _Sender> { static_assert(sender_expr_for<_Sender, ignore_all_values_t>); - return __sexpr_apply(static_cast<_Sender&&>(__sndr), __connect_fn<_Receiver>{__rcvr}); + return __apply(__connect_fn<_Receiver>{__rcvr}, static_cast<_Sender&&>(__sndr)); }; }; } // namespace __ignore_all_values diff --git a/include/exec/sequence/iterate.hpp b/include/exec/sequence/iterate.hpp index 586fe153e..9d2a57564 100644 --- a/include/exec/sequence/iterate.hpp +++ b/include/exec/sequence/iterate.hpp @@ -190,9 +190,9 @@ namespace exec { > requires sender_to<_NextSender<_SeqExpr, _Receiver>, _NextReceiver<_SeqExpr, _Receiver>> static auto subscribe(_SeqExpr&& __seq, _Receiver __rcvr) - noexcept(__nothrow_callable<__sexpr_apply_t, _SeqExpr, __subscribe_fn<_Receiver>>) - -> __call_result_t<__sexpr_apply_t, _SeqExpr, __subscribe_fn<_Receiver>> { - return __sexpr_apply(static_cast<_SeqExpr&&>(__seq), __subscribe_fn<_Receiver>{__rcvr}); + noexcept(__nothrow_applicable<__subscribe_fn<_Receiver>, _SeqExpr>) + -> __apply_result_t<__subscribe_fn<_Receiver>, _SeqExpr> { + return __apply(__subscribe_fn<_Receiver>{__rcvr}, static_cast<_SeqExpr&&>(__seq)); } template diff --git a/include/exec/sequence/merge.hpp b/include/exec/sequence/merge.hpp index b0625703a..ec6b9b27c 100644 --- a/include/exec/sequence/merge.hpp +++ b/include/exec/sequence/merge.hpp @@ -214,9 +214,9 @@ namespace exec { template _Self, receiver _Receiver> static auto subscribe(_Self&& __self, _Receiver __rcvr) - noexcept(__nothrow_callable<__sexpr_apply_t, _Self, __subscribe_fn<_Receiver>>) - -> __sexpr_apply_result_t<_Self, __subscribe_fn<_Receiver>> { - return __sexpr_apply(static_cast<_Self&&>(__self), __subscribe_fn<_Receiver>{__rcvr}); + noexcept(__nothrow_applicable<__subscribe_fn<_Receiver>, _Self>) + -> __apply_result_t<__subscribe_fn<_Receiver>, _Self> { + return __apply(__subscribe_fn<_Receiver>{__rcvr}, static_cast<_Self&&>(__self)); } }; } // namespace __merge diff --git a/include/exec/sequence/merge_each.hpp b/include/exec/sequence/merge_each.hpp index 137eb766d..258ae4289 100644 --- a/include/exec/sequence/merge_each.hpp +++ b/include/exec/sequence/merge_each.hpp @@ -1288,10 +1288,10 @@ namespace exec { static constexpr auto subscribe = [](_Sequence&& __sndr, _Receiver __rcvr) noexcept( - __nothrow_callable<__sexpr_apply_t, _Sequence, __subscribe_fn<_Receiver>>) - -> __sexpr_apply_result_t<_Sequence, __subscribe_fn<_Receiver>> { + __nothrow_applicable<__subscribe_fn<_Receiver>, _Sequence>) + -> __apply_result_t<__subscribe_fn<_Receiver>, _Sequence> { static_assert(sender_expr_for<_Sequence, merge_each_t>); - return __sexpr_apply(static_cast<_Sequence&&>(__sndr), __subscribe_fn<_Receiver>{__rcvr}); + return __apply(__subscribe_fn<_Receiver>{__rcvr}, static_cast<_Sequence&&>(__sndr)); }; }; } // namespace __merge_each diff --git a/include/exec/sequence/transform_each.hpp b/include/exec/sequence/transform_each.hpp index 51c6fb8f3..429b6f796 100644 --- a/include/exec/sequence/transform_each.hpp +++ b/include/exec/sequence/transform_each.hpp @@ -259,16 +259,16 @@ namespace exec { template _Self, receiver _Receiver> static auto subscribe(_Self&& __self, _Receiver __rcvr) - noexcept(__nothrow_callable<__sexpr_apply_t, _Self, __subscribe_fn<_Receiver>>) - -> __call_result_t<__sexpr_apply_t, _Self, __subscribe_fn<_Receiver>> { - return __sexpr_apply(static_cast<_Self&&>(__self), __subscribe_fn<_Receiver>{__rcvr}); + noexcept(__nothrow_applicable<__subscribe_fn<_Receiver>, _Self>) + -> __apply_result_t<__subscribe_fn<_Receiver>, _Self> { + return __apply(__subscribe_fn<_Receiver>{__rcvr}, static_cast<_Self&&>(__self)); } template _Sexpr> static auto get_env(const _Sexpr& __sexpr) noexcept -> env_of_t<__child_of<_Sexpr>> { - return __sexpr_apply(__sexpr, [](__ignore, __ignore, const _Child& __child) { + return __apply([](__ignore, __ignore, const _Child& __child) { return STDEXEC::get_env(__child); - }); + }, __sexpr); } }; } // namespace __transform_each diff --git a/include/exec/static_thread_pool.hpp b/include/exec/static_thread_pool.hpp index a45c732a3..7cc8e9311 100644 --- a/include/exec/static_thread_pool.hpp +++ b/include/exec/static_thread_pool.hpp @@ -298,7 +298,7 @@ namespace exec { if constexpr (__completes_on) { auto sched = STDEXEC::get_completion_scheduler(get_env(sndr), env); static_assert(std::is_same_v); - return __sexpr_apply(static_cast(sndr), _transform_bulk{*sched.pool_}); + return __apply(_transform_bulk{*sched.pool_}, static_cast(sndr)); } else { return STDEXEC::__not_a_sender< STDEXEC::_WHAT_<>( @@ -318,7 +318,7 @@ namespace exec { auto transform_sender(STDEXEC::set_value_t, Sender&& sndr, const Env& env) const noexcept { if constexpr (__completes_on) { auto sched = STDEXEC::get_scheduler(env); - return __sexpr_apply(static_cast(sndr), _transform_iterate{*sched.pool_}); + return __apply(_transform_iterate{*sched.pool_}, static_cast(sndr)); } else { return STDEXEC::__not_a_sender< STDEXEC::_WHAT_<>( diff --git a/include/exec/system_context.hpp b/include/exec/system_context.hpp index 1154f071a..133ba9287 100644 --- a/include/exec/system_context.hpp +++ b/include/exec/system_context.hpp @@ -712,8 +712,8 @@ namespace exec { const _Env& __env) const noexcept { if constexpr (STDEXEC::__completes_on<_Sender, parallel_scheduler, _Env>) { auto __sched = STDEXEC::get_scheduler(__env); - return STDEXEC::__sexpr_apply( - static_cast<_Sender&&>(__sndr), __transform_parallel_bulk_sender{__sched}); + return STDEXEC::__apply( + __transform_parallel_bulk_sender{__sched}, static_cast<_Sender&&>(__sndr)); } else { return STDEXEC::__not_a_sender< STDEXEC::_WHAT_<>(CANNOT_DISPATCH_THE_BULK_ALGORITHM_TO_THE_PARALLEL_SCHEDULER), diff --git a/include/exec/unless_stop_requested.hpp b/include/exec/unless_stop_requested.hpp index cf19d64d6..0c463e082 100644 --- a/include/exec/unless_stop_requested.hpp +++ b/include/exec/unless_stop_requested.hpp @@ -46,10 +46,14 @@ namespace exec { constexpr connect_result_t<__child_of<_Sender>, _Receiver> operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept( noexcept(STDEXEC::connect(__declval<__child_of<_Sender>>(), (_Receiver&&) __rcvr))) { - return __sexpr_apply((_Sender&&) __sndr, [&](auto, const auto&, auto&& __child) { - return STDEXEC::connect((decltype(__child)&&) __child, (_Receiver&&) __rcvr); - }); + return __apply( + [&](auto, const auto&, _Child&& __child) { + return STDEXEC::connect( + static_cast<_Child&&>(__child), static_cast<_Receiver&&>(__rcvr)); + }, + static_cast<_Sender&&>(__sndr)); } + template constexpr __op_state<_Sender, _Receiver> operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept(__nothrow_constructible_from<__op_state<_Sender, _Receiver>, _Sender, _Receiver>) { diff --git a/include/nvexec/stream/common.cuh b/include/nvexec/stream/common.cuh index 5e85e0906..c1c0882d0 100644 --- a/include/nvexec/stream/common.cuh +++ b/include/nvexec/stream/common.cuh @@ -87,14 +87,10 @@ namespace nvexec { // algorithms use the current scheduler's domain to transform senders before starting them. struct stream_domain : STDEXEC::default_domain { template , class Env> - requires STDEXEC::__callable< - STDEXEC::__sexpr_apply_t, - Sender, - _strm::transform_sender_for - > + requires STDEXEC::__applicable<_strm::transform_sender_for, Sender> static auto transform_sender(STDEXEC::set_value_t, Sender&& sndr, const Env& env) { - return STDEXEC::__sexpr_apply( - static_cast(sndr), _strm::transform_sender_for{env}); + return STDEXEC::__apply( + _strm::transform_sender_for{env}, static_cast(sndr)); } template diff --git a/include/nvexec/stream/sync_wait.cuh b/include/nvexec/stream/sync_wait.cuh index 363c7aec7..b0384484c 100644 --- a/include/nvexec/stream/sync_wait.cuh +++ b/include/nvexec/stream/sync_wait.cuh @@ -19,6 +19,8 @@ #pragma once #include "../../stdexec/execution.hpp" +#include "common.cuh" + #include #include #include @@ -27,8 +29,6 @@ #include #include -#include "common.cuh" - namespace nvexec::_strm { namespace _sync_wait { struct __env { diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 89ad44546..a8a22cfa8 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -36,10 +36,6 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // Generic __sender type - namespace __detail { - template - using __impl_of = decltype((__declval<_Sender>().__impl_)); - } // namespace __detail #if STDEXEC_EDG() # define STDEXEC_SEXPR_DESCRIPTOR_FN(_Descriptor) \ @@ -68,11 +64,6 @@ namespace STDEXEC { template struct __sexpr_impl; - namespace __detail { - template - struct __connect_fn; - } // namespace __detail - template struct __op_state; @@ -80,11 +71,19 @@ namespace STDEXEC { struct __rcvr; namespace __detail { + template + struct __connect_fn; + // A decay_copyable trait that uses C++17 guaranteed copy elision, so // that __decay_copyable_if is satisfied. template > concept __decay_copyable_if = requires(__declfn_t<_Ty> __val) { _Uy(__val()); }; + template > + concept __nothrow_decay_copyable_if = requires(__declfn_t<_Ty> __val) { + { _Uy(__val()) } noexcept; + }; + template <__decay_copyable_if _Ty> using __decay_if_t = __decay_t<_Ty>; @@ -101,9 +100,8 @@ namespace STDEXEC { >; template - concept __connectable = - __callable<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>, __connect_fn<_Sexpr, _Receiver>> - && __mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>; + concept __connectable = __tup::__applicable_v<__connect_fn<_Sexpr, _Receiver>, _Sexpr> + && __mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>; struct __defaults { static constexpr auto get_attrs = @@ -123,7 +121,7 @@ namespace STDEXEC { static constexpr auto get_state = [](_Sender&& __sndr, __ignore) noexcept -> decltype(auto) { - return __sndr.apply(static_cast<_Sender&&>(__sndr), __get_data()); + return STDEXEC::__get<1>(static_cast<_Sender&&>(__sndr)); }; static constexpr auto connect = @@ -137,6 +135,8 @@ namespace STDEXEC { static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr)}; }; + static constexpr auto submit = []{}; + static constexpr auto start = []( __ignore, __ignore, @@ -236,15 +236,8 @@ namespace STDEXEC { using __tag_t = __decay_t<_Sexpr>::__tag_t; using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>; - STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS - _Receiver __rcvr_; - STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS - __state_t __state_; - - __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) noexcept( - __nothrow_decay_copyable<_Receiver> - && noexcept( - __state_t(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr_)))) + explicit __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) noexcept(noexcept( + __state_t(__sexpr_impl<__tag_t>::get_state(__declval<_Sexpr>(), __declval<_Receiver&>())))) : __rcvr_(static_cast<_Receiver&&>(__rcvr)) , __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr_)) { } @@ -264,11 +257,16 @@ namespace STDEXEC { STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() const & noexcept -> const _Receiver& { return __rcvr_; } + + STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS + _Receiver __rcvr_; + STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS + __state_t __state_; }; template requires __state_uses_receiver<_Sexpr, _Receiver> - struct __op_base<_Sexpr, _Receiver> + struct STDEXEC_ATTRIBUTE(empty_bases) __op_base<_Sexpr, _Receiver> : __receiver_box<_Receiver> , __state_box<_Sexpr, _Receiver> { using __tag_t = __decay_t<_Sexpr>::__tag_t; @@ -277,7 +275,7 @@ namespace STDEXEC { STDEXEC_IMMOVABLE(__op_base); __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) - noexcept(__nothrow_decay_copyable<_Receiver> && __nothrow_move_constructible<__state_t>) + noexcept(__noexcept_of<__sexpr_impl<__tag_t>::get_state, _Sexpr, _Receiver&>) : __receiver_box<_Receiver>{static_cast<_Receiver&&>(__rcvr)} , __state_box<_Sexpr, _Receiver>{static_cast<_Sexpr&&>(__sndr), this->__rcvr_} { // This is necessary to ensure that the state object is pointer-interconvertible @@ -286,9 +284,6 @@ namespace STDEXEC { } }; - STDEXEC_PRAGMA_PUSH() - STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces") - template struct __connect_fn { template @@ -297,18 +292,15 @@ namespace STDEXEC { template using __env_t = __detail::__env_type_t, __msize_t<_Idx>, _Sexpr, _Receiver>; - __op_state<_Sexpr, _Receiver>* __op_; - struct __impl { - __op_state<_Sexpr, _Receiver>* __op_; - template - requires(sender_to<_Child, __receiver_archetype<__env_t<_Is>>> && ...) auto operator()(__indices<_Is...>, _Child&&... __child) const noexcept((__nothrow_connectable<_Child, __receiver_t<_Is>> && ...)) -> __tuple>...> { return __tuple{connect(static_cast<_Child&&>(__child), __receiver_t<_Is>{__op_})...}; } + + __op_state<_Sexpr, _Receiver>* __op_; }; template @@ -321,9 +313,9 @@ namespace STDEXEC { auto operator()(__ignore, __ignore) const noexcept -> __tuple<> { return {}; } - }; - STDEXEC_PRAGMA_POP() + __op_state<_Sexpr, _Receiver>* __op_; + }; inline constexpr auto __drop_front = [](_Fn __fn) noexcept { return [__fn = std::move(__fn)](auto&&, _Rest&&... __rest) noexcept( @@ -332,28 +324,6 @@ namespace STDEXEC { }; }; - template - STDEXEC_ATTRIBUTE(host, device, always_inline) - constexpr auto __captures(_Tag, _Captures&&... __captures2) { - return - [... __captures3 = static_cast<_Captures&&>(__captures2)]( - _Cvref, - _Fun&& - __fun) mutable noexcept(__nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>) - -> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...> - requires __callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...> - { - // The use of decltype(__captures3) here instead of _Captures is a workaround for - // a codegen bug in nvc++. - return static_cast<_Fun&&>( - __fun)(_Tag(), const_cast<__minvoke<_Cvref, decltype(__captures3)>&&>(__captures3)...); - }; - } - - template - using __captures_t = - decltype(__detail::__captures(_Tag(), __declval<_Data>(), __declval<_Child>()...)); - template using __tuple_size_t = char[sizeof...(_Child) + 2]; // NOLINT(modernize-avoid-c-arrays) @@ -380,10 +350,6 @@ namespace STDEXEC { using __parent_op_t = __op_state<_Sexpr, _Receiver>; using __tag_t = tag_of_t<_Sexpr>; - // A pointer to the parent operation state, which contains the one created with - // this receiver. - __parent_op_t* __op_; - template STDEXEC_ATTRIBUTE(always_inline) void set_value(_Args&&... __args) noexcept { @@ -405,6 +371,10 @@ namespace STDEXEC { auto get_env() const noexcept -> __detail::__env_type_t<__tag_t, _Index, _Sexpr, _Receiver> { return __op_->__get_env(__index_t()); } + + // A pointer to the parent operation state, which contains the one created with + // this receiver. + __parent_op_t* __op_; }; }; @@ -414,18 +384,15 @@ namespace STDEXEC { using __tag_t = __desc_t::__tag; using __data_t = __desc_t::__data; using __state_t = __op_state::__op_base::__state_t; - using __inner_ops_t = - __result_of<__sexpr_apply, _Sexpr, __detail::__connect_fn<_Sexpr, _Receiver>>; - - __inner_ops_t __inner_ops_; + using __inner_ops_t = __apply_result_t<__detail::__connect_fn<_Sexpr, _Receiver>, _Sexpr>; explicit __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) noexcept( __nothrow_constructible_from<__detail::__op_base<_Sexpr, _Receiver>, _Sexpr, _Receiver> - && __noexcept_of<__sexpr_apply, _Sexpr, __detail::__connect_fn<_Sexpr, _Receiver>>) + && __nothrow_applicable<__detail::__connect_fn<_Sexpr, _Receiver>, _Sexpr>) : __op_state::__op_base{static_cast<_Sexpr&&>(__sexpr), static_cast<_Receiver&&>(__rcvr)} - , __inner_ops_(__sexpr_apply( - static_cast<_Sexpr&&>(__sexpr), - __detail::__connect_fn<_Sexpr, _Receiver>{this})) { + , __inner_ops_(__apply( + __detail::__connect_fn<_Sexpr, _Receiver>{this}, + static_cast<_Sexpr&&>(__sexpr))) { } STDEXEC_ATTRIBUTE(always_inline) void start() & noexcept { @@ -459,6 +426,8 @@ namespace STDEXEC { const auto& __rcvr = this->__rcvr(); return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state(), __rcvr); } + + __inner_ops_t __inner_ops_; }; template @@ -468,10 +437,6 @@ namespace STDEXEC { using __detail::__enable_receiver_from_this; - template - using __get_attrs_fn = - __result_of<__detail::__drop_front, __mtypeof<__sexpr_impl<_Tag>::get_attrs>>; - //! A dummy type used only for diagnostic purposes. //! See `__sexpr` for the implementation of P2300's _`basic-sender`_. template @@ -487,7 +452,7 @@ namespace STDEXEC { //! but is not an exact implementation. //! Note: The struct named `__basic_sender` is just a dummy type and is also not _`basic-sender`_. template - struct __sexpr { + struct __sexpr : __minvoke> { using sender_concept = sender_t; // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`. @@ -495,31 +460,16 @@ namespace STDEXEC { using __t = __sexpr; using __desc_t = decltype(_DescriptorFn()); using __tag_t = __desc_t::__tag; - using __captures_t = __minvoke<__desc_t, __q<__detail::__captures_t>>; - template - STDEXEC_ATTRIBUTE(host, device, always_inline) - explicit __sexpr(_Tag, _Data&& __data, _Child&&... __child) - : __impl_( - __detail::__captures( - _Tag(), - static_cast<_Data&&>(__data), - static_cast<_Child&&>(__child)...)) { - } - - template - using __impl = __sexpr_impl<__meval<__msecond, _Self, __tag_t>>; - - template - STDEXEC_ATTRIBUTE(always_inline) - auto get_env() const noexcept - -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>> { - return __sexpr_apply(*this, __detail::__drop_front(__impl<_Self>::get_attrs)); + STDEXEC_ATTRIBUTE(nodiscard, always_inline) + constexpr auto get_env() const noexcept -> decltype(auto) { + return __apply(__detail::__drop_front(__sexpr_impl<__tag_t>::get_attrs), *this); } - template <__decays_to_derived_from<__sexpr> _Self, class... _Env> + template static consteval auto get_completion_signatures() { - using __impl_t = __mtypeof<__impl<_Self>::get_completion_signatures>; + static_assert(__decays_to_derived_from<_Self, __sexpr>); + using __impl_t = __mtypeof<__sexpr_impl<__tag_t>::get_completion_signatures>; using __detail::__has_static_consteval_get_completion_signatures; if constexpr (__has_static_consteval_get_completion_signatures<__tag_t, _Self, _Env...>) { @@ -533,51 +483,44 @@ namespace STDEXEC { } } - template <__decays_to_derived_from<__sexpr> _Self, receiver _Receiver> - STDEXEC_ATTRIBUTE(always_inline) - STDEXEC_EXPLICIT_THIS_BEGIN(auto connect)(this _Self&& __self, _Receiver&& __rcvr) - noexcept(__noexcept_of<__impl<_Self>::connect, _Self, _Receiver>) -> __msecond< - __enable_if<__decays_to_derived_from<_Self, __sexpr>>, - __result_of<__impl<_Self>::connect, _Self, _Receiver> - > { - return __impl<_Self>::connect( + // Non-standard extension: + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline) + static constexpr auto static_connect(_Self&& __self, _Receiver __rcvr) + noexcept(__noexcept_of<__sexpr_impl<__tag_t>::connect, _Self, _Receiver>) + -> __result_of<__sexpr_impl<__tag_t>::connect, _Self, _Receiver> { + static_assert(__decays_to_derived_from<_Self, __sexpr>); + return __sexpr_impl<__tag_t>::connect( static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); } - STDEXEC_EXPLICIT_THIS_END(connect) - template <__decays_to_derived_from<__sexpr> _Self, receiver _Receiver> - STDEXEC_ATTRIBUTE(always_inline) - static auto submit(_Self&& __self, _Receiver&& __rcvr) - noexcept(__noexcept_of<__impl<_Self>::submit, _Self, _Receiver>) -> __msecond< - __enable_if<__decays_to_derived_from<_Self, __sexpr>>, - __result_of<__impl<_Self>::submit, _Self, _Receiver> - > { - return __impl<_Self>::submit( - static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline) + constexpr auto connect(_Receiver __rcvr) && noexcept( + __noexcept_of<__sexpr_impl<__tag_t>::connect, __sexpr, _Receiver>) + -> __result_of<__sexpr_impl<__tag_t>::connect, __sexpr, _Receiver> { + return __sexpr_impl<__tag_t>::connect( + static_cast<__sexpr&&>(*this), static_cast<_Receiver&&>(__rcvr)); } - template - STDEXEC_ATTRIBUTE(always_inline) - static auto apply(_Sender&& __sndr, _ApplyFn&& __fun) noexcept( - __nothrow_callable<__detail::__impl_of<_Sender>, __copy_cvref_fn<_Sender>, _ApplyFn>) - -> __call_result_t<__detail::__impl_of<_Sender>, __copy_cvref_fn<_Sender>, _ApplyFn> { - return static_cast<_Sender&&>(__sndr) - .__impl_(__copy_cvref_fn<_Sender>(), static_cast<_ApplyFn&&>(__fun)); + template + requires __std::copy_constructible<__sexpr> + STDEXEC_ATTRIBUTE(nodiscard, always_inline) + constexpr auto connect(_Receiver __rcvr) const & noexcept( + __noexcept_of<__sexpr_impl<__tag_t>::connect, __sexpr const &, _Receiver>) + -> __result_of<__sexpr_impl<__tag_t>::connect, __sexpr const &, _Receiver> { + return __sexpr_impl<__tag_t>::connect(*this, static_cast<_Receiver&&>(__rcvr)); } - template _Self> - STDEXEC_ATTRIBUTE(always_inline) - friend auto get(_Self&& __self) noexcept -> decltype(auto) - requires __detail::__in_range<_Idx, __desc_t> - { - if constexpr (_Idx == 0) { - return __tag_t(); - } else { - return __self.__impl_(__copy_cvref_fn<_Self>(), __nth_pack_element<_Idx>); - } + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline) + static constexpr auto submit(_Self&& __self, _Receiver __rcvr) + noexcept(__noexcept_of<__sexpr_impl<__tag_t>::submit, _Self, _Receiver>) + -> __result_of<__sexpr_impl<__tag_t>::submit, _Self, _Receiver> { + static_assert(__decays_to_derived_from<_Self, __sexpr>); + return __sexpr_impl<__tag_t>::submit( + static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); } - - mutable __captures_t __impl_; }; template @@ -588,6 +531,9 @@ namespace STDEXEC { template using __sexpr_t = __sexpr; + STDEXEC_PRAGMA_PUSH() + STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces") + ////////////////////////////////////////////////////////////////////////////////////////////////// // __make_sexpr //! A tagged function-object @@ -599,7 +545,8 @@ namespace STDEXEC { template constexpr auto operator()(_Data __data = {}, _Child... __child) const { return __sexpr_t<_Tag, _Data, _Child...>{ - _Tag(), static_cast<_Data&&>(__data), static_cast<_Child&&>(__child)...}; + {_Tag(), static_cast<_Data&&>(__data), static_cast<_Child&&>(__child)...} + }; } }; } // namespace __detail @@ -607,6 +554,8 @@ namespace STDEXEC { template inline constexpr __detail::__make_sexpr_t<_Tag> __make_sexpr{}; + STDEXEC_PRAGMA_POP() + // The __demangle_t utility defined below is used to pretty-print the type names of // senders in compiler diagnostics. namespace __detail { @@ -640,21 +589,3 @@ namespace STDEXEC { extern __id_name __demangle_v<_Sender>; } // namespace __detail } // namespace STDEXEC - -namespace std { - template - struct tuple_size> - : integral_constant< - size_t, - STDEXEC::__minvoke, STDEXEC::__msize>::value - > { }; - - template - struct tuple_element<_Idx, STDEXEC::__sexpr<_Descriptor>> { - using type = STDEXEC::__remove_rvalue_reference_t>, - STDEXEC::__cp, - STDEXEC::__nth_pack_element_t<_Idx> - >>; - }; -} // namespace std diff --git a/include/stdexec/__detail/__bulk.hpp b/include/stdexec/__detail/__bulk.hpp index 40323f228..d639d57b1 100644 --- a/include/stdexec/__detail/__bulk.hpp +++ b/include/stdexec/__detail/__bulk.hpp @@ -251,7 +251,7 @@ namespace STDEXEC { template static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { - return __sexpr_apply(static_cast<_Sender&&>(__sndr), __transform_sender_fn()); + return __apply(__transform_sender_fn(), static_cast<_Sender&&>(__sndr)); } }; diff --git a/include/stdexec/__detail/__config.hpp b/include/stdexec/__detail/__config.hpp index 99f81336f..5a7d8a435 100644 --- a/include/stdexec/__detail/__config.hpp +++ b/include/stdexec/__detail/__config.hpp @@ -450,17 +450,17 @@ namespace STDEXEC::__std { #if STDEXEC_HAS_BUILTIN(__remove_reference) namespace STDEXEC { template - using _remove_reference_t = __remove_reference(Ty); + using __unref_t = __remove_reference(Ty); } // namespace STDEXEC -# define STDEXEC_REMOVE_REFERENCE(...) STDEXEC::_remove_reference_t<__VA_ARGS__> +# define STDEXEC_REMOVE_REFERENCE(...) STDEXEC::__unref_t<__VA_ARGS__> #elif STDEXEC_HAS_BUILTIN(__remove_reference_t) namespace STDEXEC { template - using _remove_reference_t = __remove_reference_t(Ty); + using __unref_t = __remove_reference_t(Ty); } // namespace STDEXEC -# define STDEXEC_REMOVE_REFERENCE(...) STDEXEC::_remove_reference_t<__VA_ARGS__> +# define STDEXEC_REMOVE_REFERENCE(...) STDEXEC::__unref_t<__VA_ARGS__> #else # define STDEXEC_REMOVE_REFERENCE(...) ::std::remove_reference_t<__VA_ARGS__> #endif diff --git a/include/stdexec/__detail/__ensure_started.hpp b/include/stdexec/__detail/__ensure_started.hpp index 60965e31a..c19b61135 100644 --- a/include/stdexec/__detail/__ensure_started.hpp +++ b/include/stdexec/__detail/__ensure_started.hpp @@ -61,8 +61,7 @@ namespace STDEXEC { using _Receiver = __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>; static_assert(sender_to<__child_of<_Sender>, _Receiver>); - return __sexpr_apply( - static_cast<_Sender&&>(__sndr), + return __apply( [&](__ignore, _Env2&& __env, _Child&& __child) { // TODO(ericniebler): should we join the env passed to ensure_started with the // env of the receiver? @@ -74,7 +73,8 @@ namespace STDEXEC { __sh_state->__try_start(); // cannot throw return __make_sexpr<__ensure_started_t>(__box{__ensure_started_t(), __sh_state}); - }); + }, + static_cast<_Sender&&>(__sndr)); } template diff --git a/include/stdexec/__detail/__execution_fwd.hpp b/include/stdexec/__detail/__execution_fwd.hpp index be4dbefcc..02450171a 100644 --- a/include/stdexec/__detail/__execution_fwd.hpp +++ b/include/stdexec/__detail/__execution_fwd.hpp @@ -394,12 +394,12 @@ namespace STDEXEC { using __on::on_t; extern const on_t on; - namespace __detail { - struct __sexpr_apply_t; - } // namespace __detail + // namespace __detail { + // struct __sexpr_apply_t; + // } // namespace __detail - using __detail::__sexpr_apply_t; - extern const __sexpr_apply_t __sexpr_apply; + // using __detail::__sexpr_apply_t; + // extern const __sexpr_apply_t __sexpr_apply; } // namespace STDEXEC template diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index 78df86d6b..ade781d8e 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -589,12 +589,12 @@ namespace STDEXEC { if constexpr (!__movable_value<_Sender>) { return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>{}; } else { - return __sexpr_apply( - static_cast<_Sender&&>(__sndr), + return __apply( [](__ignore, _Fun&& __fun, _Child&& __child) { return __make_sexpr<__let_tag<_SetTag>>( __data_t{static_cast<_Child&&>(__child), static_cast<_Fun&&>(__fun)}); - }); + }, + static_cast<_Sender&&>(__sndr)); } } }; diff --git a/include/stdexec/__detail/__on.hpp b/include/stdexec/__detail/__on.hpp index c4ca85e83..a4001721a 100644 --- a/include/stdexec/__detail/__on.hpp +++ b/include/stdexec/__detail/__on.hpp @@ -151,7 +151,7 @@ namespace STDEXEC { template STDEXEC_ATTRIBUTE(always_inline) static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env& __env) { - return __sexpr_apply(static_cast<_Sender&&>(__sndr), __transform_sender_fn<_Sender>(__env)); + return __apply(__transform_sender_fn<_Sender>(__env), static_cast<_Sender&&>(__sndr)); } }; } // namespace __on diff --git a/include/stdexec/__detail/__read_env.hpp b/include/stdexec/__detail/__read_env.hpp index 099590ebd..668a15cc2 100644 --- a/include/stdexec/__detail/__read_env.hpp +++ b/include/stdexec/__detail/__read_env.hpp @@ -18,14 +18,18 @@ #include "__execution_fwd.hpp" // include these after __execution_fwd.hpp +#include "../stop_token.hpp" #include "__basic_sender.hpp" +#include "__completion_behavior.hpp" #include "__completion_signatures.hpp" #include "__concepts.hpp" #include "__diagnostics.hpp" #include "__env.hpp" #include "__meta.hpp" #include "__optional.hpp" +#include "__queries.hpp" #include "__receivers.hpp" +#include "__schedulers.hpp" #include "__submit.hpp" // IWYU pragma: keep #include @@ -163,7 +167,7 @@ namespace STDEXEC { } template - //STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto get_stop_token_t::operator()() const noexcept { return read_env(get_stop_token); } diff --git a/include/stdexec/__detail/__sender_introspection.hpp b/include/stdexec/__detail/__sender_introspection.hpp index 81a501312..87f1ca4bb 100644 --- a/include/stdexec/__detail/__sender_introspection.hpp +++ b/include/stdexec/__detail/__sender_introspection.hpp @@ -16,35 +16,21 @@ #pragma once #include "__execution_fwd.hpp" +#include "__meta.hpp" +#include "__tuple.hpp" +#include "__type_traits.hpp" #include "__utility.hpp" #include #include // IWYU pragma: keep for std::terminate namespace STDEXEC { - namespace __detail { - // Accessor for the "data" field of a sender - struct __get_data { - template - STDEXEC_ATTRIBUTE(always_inline) - auto operator()(__ignore, _Data&& __data, auto&&...) const noexcept -> _Data&& { - return static_cast<_Data&&>(__data); - } - }; - - // A function object that is to senders what std::apply is to tuples: - struct __sexpr_apply_t { - template - STDEXEC_ATTRIBUTE(always_inline) - auto operator()(_Sender&& __sndr, _ApplyFn&& __fun) const - noexcept(noexcept(__sndr - .apply(static_cast<_Sender&&>(__sndr), static_cast<_ApplyFn&&>(__fun)))) - -> decltype(__sndr - .apply(static_cast<_Sender&&>(__sndr), static_cast<_ApplyFn&&>(__fun))) { - return __sndr.apply(static_cast<_Sender&&>(__sndr), static_cast<_ApplyFn&&>(__fun)); - } - }; + namespace { + template + struct __sexpr; + } // namespace + namespace __detail { // A type that describes a sender's metadata template struct __desc { @@ -56,62 +42,53 @@ namespace STDEXEC { return __desc{}; } - template - using __f = __minvoke<_Fn, _Tag, _Data, _Child...>; + template + using __f = __minvoke<_Fn, _Args..., _Tag, _Data, _Child...>; }; - template - struct __sexpr_uncurry_fn { - template - constexpr auto operator()(_Tag, _Data&&, _Child&&...) const noexcept - -> __minvoke<_Fn, _Tag, _Data, _Child...> { - STDEXEC_ASSERT(!"This function should never be called"); - STDEXEC_TERMINATE(); - } - }; - - template - using __sexpr_uncurry = __call_result_t<__sexpr_apply_t, _CvrefSender, __sexpr_uncurry_fn<_Fn>>; + template + using __desc_of = STDEXEC_REMOVE_REFERENCE(_Sender)::__desc_t; template - using __desc_of = __sexpr_uncurry<_Sender, __q<__desc>>; + using __tag_of = __desc_of<_Sender>::__tag; - using __get_desc = __sexpr_uncurry_fn<__q<__desc>>; + template + requires __mvalid<__tag_of, _Sender> + extern __tag_of<_Sender> __tag_of_v; } // namespace __detail - using __detail::__sexpr_apply_t; - inline constexpr __sexpr_apply_t __sexpr_apply{}; - - template - using __sexpr_apply_result_t = __call_result_t<__sexpr_apply_t, _Sender, _ApplyFn>; - template - using tag_of_t = __detail::__desc_of<_Sender>::__tag; + using tag_of_t = decltype(__detail::__tag_of_v<_Sender>); template - using __data_of = __detail::__desc_of<_Sender>::__data; + using __data_of = __tuple_element_t<1, _Sender>; template > - using __children_of = __mapply<_Continuation, typename __detail::__desc_of<_Sender>::__children>; - - template - using __nth_child_of = __children_of<_Sender, __mbind_front_q<__m_at, _Ny>>; + using __children_of = __mapply< + __mtransform<__copy_cvref_fn<_Sender>, _Continuation>, + typename __detail::__desc_of<_Sender>::__children + >; template - using __nth_child_of_c = __children_of<_Sender, __mbind_front_q<__m_at, __msize_t<_Ny>>>; + using __nth_child_of_c = __tuple_element_t<_Ny + 2, _Sender>; + + template + using __nth_child_of = __nth_child_of_c<_Ny::value, _Sender>; template - using __child_of = __children_of<_Sender, __q<__mfront>>; + using __child_of = __tuple_element_t<2, _Sender>; template - inline constexpr std::size_t __nbr_children_of = __children_of<_Sender, __msize>::value; - - template - requires __mvalid - struct __muncurry_<_Tp> { - template - using __f = __detail::__sexpr_uncurry<_Tp, _Fn>; - }; + inline constexpr std::size_t __nbr_children_of = __tuple_size_v<_Sender> - 2; + + template + struct __muncurry_<__sexpr<_Descriptor>> : decltype(_Descriptor()){}; + + template + struct __muncurry_<__sexpr<_Descriptor> &> : decltype(_Descriptor()){}; + + template + struct __muncurry_<__sexpr<_Descriptor> const &> : decltype(_Descriptor()){}; template concept sender_expr = __mvalid; diff --git a/include/stdexec/__detail/__shared.hpp b/include/stdexec/__detail/__shared.hpp index 3ef7cfc60..80b464e33 100644 --- a/include/stdexec/__detail/__shared.hpp +++ b/include/stdexec/__detail/__shared.hpp @@ -162,7 +162,7 @@ namespace STDEXEC::__shared { } static auto __get_sh_state(_CvrefSender& __sndr) noexcept { - auto __box = __sndr.apply(static_cast<_CvrefSender&&>(__sndr), __detail::__get_data()); + auto __box = STDEXEC::__get<1>(static_cast<_CvrefSender&&>(__sndr)); return std::exchange(__box.__sh_state_, nullptr); } diff --git a/include/stdexec/__detail/__split.hpp b/include/stdexec/__detail/__split.hpp index 5859fd27b..c98180bd5 100644 --- a/include/stdexec/__detail/__split.hpp +++ b/include/stdexec/__detail/__split.hpp @@ -56,15 +56,15 @@ namespace STDEXEC { using _Receiver = __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>; static_assert(sender_to<__child_of<_Sender>, _Receiver>); - return __sexpr_apply( - static_cast<_Sender&&>(__sndr), + return __apply( [&](__ignore, _Env2&& __env, _Child&& __child) { // The shared state starts life with a ref-count of one. auto* __sh_state = new __shared_state{static_cast<_Child&&>(__child), static_cast<_Env2&&>(__env)}; return __make_sexpr<__split_t>(__box{__split_t(), __sh_state}); - }); + }, + static_cast<_Sender&&>(__sndr)); } template diff --git a/include/stdexec/__detail/__starts_on.hpp b/include/stdexec/__detail/__starts_on.hpp index 54bd47637..1c71ec848 100644 --- a/include/stdexec/__detail/__starts_on.hpp +++ b/include/stdexec/__detail/__starts_on.hpp @@ -57,13 +57,13 @@ namespace STDEXEC { template <__decay_copyable _Sender, class _Env> static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { - return __sexpr_apply( - static_cast<_Sender&&>(__sndr), + return __apply( [](__ignore, _Data&& __data, _Child&& __child) -> auto { // This is the heart of starts_on: It uses `let_value` to schedule `__child` on the given scheduler: return let_value( continues_on(just(), __data), __detail::__always{static_cast<_Child&&>(__child)}); - }); + }, + static_cast<_Sender&&>(__sndr)); } template diff --git a/include/stdexec/__detail/__sync_wait.hpp b/include/stdexec/__detail/__sync_wait.hpp index 269badfcd..6ac474339 100644 --- a/include/stdexec/__detail/__sync_wait.hpp +++ b/include/stdexec/__detail/__sync_wait.hpp @@ -62,11 +62,6 @@ namespace STDEXEC { constexpr auto query(__root_t) const noexcept -> bool { return true; } - - // static constexpr auto query(__debug::__is_debug_env_t) noexcept -> bool - // { - // return true; - // } }; // What should sync_wait(just_stopped()) return? @@ -99,8 +94,8 @@ namespace STDEXEC { std::optional>* __values_; template - requires __std::constructible_from, _As...> void set_value(_As&&... __as) noexcept { + static_assert(__std::constructible_from, _As...>); STDEXEC_TRY { __values_->emplace(static_cast<_As&&>(__as)...); } @@ -192,60 +187,61 @@ namespace STDEXEC { //////////////////////////////////////////////////////////////////////////// // [execution.senders.consumers.sync_wait] struct sync_wait_t { - template + template _Sender> auto operator()(_Sender&& __sndr) const { - if constexpr (!sender_in<_Sender, __env>) { - STDEXEC::__diagnose_sender_concept_failure<_Sender, __env>(); - } else { - using __domain_t = __completion_domain_of_t; - constexpr auto __success_completion_count = - __count_of::value; - - static_assert( - __success_completion_count != 0, - "The argument to STDEXEC::sync_wait() is a sender that cannot complete successfully. " - "STDEXEC::sync_wait() requires a sender that can complete successfully in exactly one " - "way. In other words, the sender's completion signatures must include exactly one " - "signature of the form `set_value_t(value-types...)`."); - - static_assert( - __success_completion_count <= 1, - "The sender passed to STDEXEC::sync_wait() can complete successfully in " - "more than one way. Use STDEXEC::sync_wait_with_variant() instead."); - - if constexpr (1 == __success_completion_count) { - using __sync_wait_receiver = __receiver_t<_Sender>; - constexpr bool __no_custom_sync_wait = __same_as<__domain_t, default_domain>; - if constexpr (__no_custom_sync_wait && sender_to<_Sender, __sync_wait_receiver>) { - // using __connect_result = connect_result_t<_Sender, __sync_wait_receiver>; - // if constexpr (!operation_state<__connect_result>) { - // static_assert( - // operation_state<__connect_result>, - // "The `connect` member function of the sender passed to STDEXEC::sync_wait() does " - // "not return an operation state. An operation state is required to have a " - // "no-throw .start() member function."); - // } else - { + using __domain_t = __completion_domain_of_t; + constexpr auto __success_completion_count = __count_of::value; + + static_assert( + __success_completion_count != 0, + "The argument to STDEXEC::sync_wait() is a sender that cannot complete successfully. " + "STDEXEC::sync_wait() requires a sender that can complete successfully in exactly one " + "way. In other words, the sender's completion signatures must include exactly one " + "signature of the form `set_value_t(value-types...)`."); + + static_assert( + __success_completion_count <= 1, + "The sender passed to STDEXEC::sync_wait() can complete successfully in " + "more than one way. Use STDEXEC::sync_wait_with_variant() instead."); + + if constexpr (1 == __success_completion_count) { + if constexpr (__same_as<__domain_t, default_domain>) { + if constexpr (sender_to<_Sender, __receiver_t<_Sender>>) { + using __opstate_t = connect_result_t<_Sender, __receiver_t<_Sender>>; + if constexpr (operation_state<__opstate_t>) { // success path, dispatch to the default domain's sync_wait return default_domain().apply_sender(*this, static_cast<_Sender&&>(__sndr)); + } else { + static_assert( + operation_state<__opstate_t>, + "The `connect` member function of the sender passed to STDEXEC::sync_wait() " + "does not return an operation state. An operation state is required to have a " + "no-throw .start() member function."); } - } else if constexpr (__no_custom_sync_wait) { + } else { static_assert( - sender_to<_Sender, __sync_wait_receiver>, + sender_to<_Sender, __receiver_t<_Sender>>, STDEXEC_ERROR_SYNC_WAIT_CANNOT_CONNECT_SENDER_TO_RECEIVER); - } else if constexpr (!__has_implementation_for) { - static_assert( - __has_implementation_for, - "The sender passed to STDEXEC::sync_wait() has a domain that does not provide a " - "usable implementation for sync_wait()."); - } else { - // success path, dispatch to the custom domain's sync_wait - return STDEXEC::apply_sender(__domain_t(), *this, static_cast<_Sender&&>(__sndr)); } + } else if constexpr (!__has_implementation_for) { + static_assert( + __has_implementation_for, + "The sender passed to STDEXEC::sync_wait() has a domain that does not provide a " + "usable implementation for sync_wait()."); + } else { + // success path, dispatch to the custom domain's sync_wait + return STDEXEC::apply_sender(__domain_t(), *this, static_cast<_Sender&&>(__sndr)); } } } + template + auto operator()(_Sender&&) const { + STDEXEC::__diagnose_sender_concept_failure<_Sender, __env>(); + // dummy return type to silence follow-on errors + return std::optional>{}; + } + // clang-format off /// @brief Synchronously wait for the result of a sender, blocking the /// current thread. diff --git a/include/stdexec/__detail/__transfer_just.hpp b/include/stdexec/__detail/__transfer_just.hpp index 529497d5f..7f27a2b6e 100644 --- a/include/stdexec/__detail/__transfer_just.hpp +++ b/include/stdexec/__detail/__transfer_just.hpp @@ -60,7 +60,7 @@ namespace STDEXEC { if constexpr (!__decay_copyable<_Sender>) { return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); } else { - return __sexpr_apply(static_cast<_Sender&&>(__sndr), __transform_sender_fn()); + return __apply(__transform_sender_fn(), static_cast<_Sender&&>(__sndr)); } } }; diff --git a/include/stdexec/__detail/__tuple.hpp b/include/stdexec/__detail/__tuple.hpp index 3cee9722c..30ad891fa 100644 --- a/include/stdexec/__detail/__tuple.hpp +++ b/include/stdexec/__detail/__tuple.hpp @@ -95,24 +95,6 @@ namespace STDEXEC { _Ty __value; }; - template - concept __empty = STDEXEC_IS_EMPTY(_Ty) && STDEXEC_IS_TRIVIALLY_CONSTRUCTIBLE(_Ty) - && STDEXEC_IS_TRIVIALLY_COPYABLE(_Ty); - - template <__empty _Ty> - inline _Ty __value{}; - - // A specialization for empty types so that they don't take up space. - template <__empty _Ty, std::size_t _Idx> - struct __box<_Ty, _Idx> { - __box() = default; - - constexpr __box(__not_decays_to<__box> auto&&) noexcept { - } - - static constexpr _Ty& __value = __tup::__value<_Ty>; - }; - template struct __tupl_base; @@ -326,7 +308,7 @@ namespace STDEXEC { inline constexpr __tup::__apply_t __apply{}; template - using __apply_result_t = __result_of<__apply, _Fn, _Tuple, _Us...>; + using __apply_result_t = __call_result_t<__tup::__apply_t, _Fn, _Tuple, _Us...>; template concept __applicable = __tup::__applicable_v<_Fn, _Tuple, _Us...>; @@ -413,9 +395,18 @@ namespace STDEXEC { // // __tuple_element_t // + namespace __detail { + template + using __tuple_element_t = decltype(__tt::__remove_rvalue_reference_fn( + STDEXEC::__get<_Index::value>(__declval<_Tuple>()))); + + template + requires __mvalid<__tuple_element_t, __msize_t<_Index>, _Tuple> + extern __declfn_t<__tuple_element_t<__msize_t<_Index>, _Tuple>> __tuple_element_v; + } // namespace __detail + template - using __tuple_element_t = decltype(__tt::__remove_rvalue_reference_fn( - STDEXEC::__get<_Index>(__declval<_Tuple>()))); + using __tuple_element_t = decltype(__detail::__tuple_element_v<_Index, _Tuple>()); // // __cat_apply(fn, tups...) diff --git a/include/stdexec/__detail/__when_all.hpp b/include/stdexec/__detail/__when_all.hpp index 9ab98a5cf..00672bef2 100644 --- a/include/stdexec/__detail/__when_all.hpp +++ b/include/stdexec/__detail/__when_all.hpp @@ -370,8 +370,8 @@ namespace STDEXEC { static constexpr auto get_state = [](_Self&& __self, _Receiver& __rcvr) noexcept - -> __sexpr_apply_result_t<_Self, __mk_state_fn_t<_Receiver>> { - return __sexpr_apply(static_cast<_Self&&>(__self), __when_all::__mk_state_fn(__rcvr)); + -> __apply_result_t<__mk_state_fn_t<_Receiver>, _Self> { + return __apply(__when_all::__mk_state_fn(__rcvr), static_cast<_Self&&>(__self)); }; static constexpr auto start = []( @@ -469,11 +469,11 @@ namespace STDEXEC { template static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { // transform when_all_with_variant(sndrs...) into when_all(into_variant(sndrs)...). - return __sexpr_apply( - static_cast<_Sender&&>(__sndr), + return __apply( [&](__ignore, __ignore, _Child&&... __child) { return when_all_t()(into_variant(static_cast<_Child&&>(__child))...); - }); + }, + static_cast<_Sender&&>(__sndr)); } }; @@ -501,12 +501,12 @@ namespace STDEXEC { static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { // transform transfer_when_all(sch, sndrs...) into // continues_on(when_all(sndrs...), sch). - return __sexpr_apply( - static_cast<_Sender&&>(__sndr), + return __apply( [&](__ignore, _Data&& __data, _Child&&... __child) { return continues_on( when_all_t()(static_cast<_Child&&>(__child)...), static_cast<_Data&&>(__data)); - }); + }, + static_cast<_Sender&&>(__sndr)); } }; @@ -537,12 +537,12 @@ namespace STDEXEC { static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { // transform the transfer_when_all_with_variant(sch, sndrs...) into // transfer_when_all(sch, into_variant(sndrs...)) - return __sexpr_apply( - static_cast<_Sender&&>(__sndr), + return __apply( [&](__ignore, _Data&& __data, _Child&&... __child) { return transfer_when_all_t()( static_cast<_Data&&>(__data), into_variant(static_cast<_Child&&>(__child))...); - }); + }, + static_cast<_Sender&&>(__sndr)); } }; diff --git a/test/exec/sequence/test_iterate.cpp b/test/exec/sequence/test_iterate.cpp index 1efeb85b6..c85b693f7 100644 --- a/test/exec/sequence/test_iterate.cpp +++ b/test/exec/sequence/test_iterate.cpp @@ -114,8 +114,7 @@ namespace { struct my_domain { template Sender, class _Env> auto transform_sender(STDEXEC::start_t, Sender&& sender, _Env&&) const noexcept { - auto range = - STDEXEC::__sexpr_apply(std::forward(sender), STDEXEC::__detail::__get_data{}); + auto range = STDEXEC::__get<1>(std::forward(sender)); auto sum = std::accumulate(std::ranges::begin(range), std::ranges::end(range), 0); return STDEXEC::just(sum + 1); }