| /* |
| * Copyright (c) 2021-2022 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 "__detail/__basic_sender.hpp" |
| #include "__detail/__execution_fwd.hpp" |
| #include "__detail/__intrusive_ptr.hpp" |
| #include "__detail/__meta.hpp" |
| #include "__detail/__scope.hpp" |
| #include "concepts.hpp" |
| #include "coroutine.hpp" |
| #include "functional.hpp" |
| #include "stop_token.hpp" |
| |
| #include <atomic> |
| #include <cassert> |
| #include <concepts> |
| #include <condition_variable> |
| #include <memory> |
| #include <mutex> |
| #include <optional> |
| #include <stdexcept> |
| #include <system_error> |
| #include <tuple> |
| #include <type_traits> |
| #include <variant> |
| |
| STDEXEC_PRAGMA_PUSH() |
| STDEXEC_PRAGMA_IGNORE_GNU("-Wpragmas") |
| STDEXEC_PRAGMA_IGNORE_GNU("-Wunknown-warning-option") |
| STDEXEC_PRAGMA_IGNORE_GNU("-Wundefined-inline") |
| STDEXEC_PRAGMA_IGNORE_GNU("-Wsubobject-linkage") |
| |
| STDEXEC_PRAGMA_IGNORE_EDG(1302) |
| STDEXEC_PRAGMA_IGNORE_EDG(497) |
| STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference) |
| |
| namespace stdexec |
| { |
| // [exec.queries.queryable] |
| template <class T> |
| concept queryable = destructible<T>; |
| |
| template <class Tag> |
| struct __query |
| { |
| template <class Sig> |
| static inline constexpr Tag (*signature)(Sig) = nullptr; |
| }; |
| |
| struct default_domain; |
| struct dependent_domain; |
| |
| template <class _Sender> |
| using tag_of_t = __tag_of<_Sender>; |
| |
| namespace __domain |
| { |
| template <class _Tag> |
| using __legacy_c11n_for = typename _Tag::__legacy_customizations_t; |
| |
| template <class _Tag, class... _Args> |
| using __legacy_c11n_fn = // |
| __make_dispatcher<__legacy_c11n_for<_Tag>, __none_such, _Args...>; |
| |
| template <class _Tag, class... _Args> |
| concept __has_legacy_c11n = // |
| __callable<__legacy_c11n_fn<_Tag, _Args...>, _Args...>; |
| |
| struct __legacy_customization |
| { |
| template <class _Tag, class _Data, class... _Children> |
| requires __has_legacy_c11n<_Tag, _Data, _Children...> |
| decltype(auto) operator()(_Tag, _Data && __data, |
| _Children&&... __children) const |
| { |
| return __legacy_c11n_fn<_Tag, _Data, _Children...>()( |
| static_cast<_Data&&>(__data), |
| static_cast<_Children&&>(__children)...); |
| } |
| }; |
| |
| template <class _DomainOrTag, class _Sender, class... _Env> |
| concept __has_transform_sender = |
| requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) { |
| __tag.transform_sender((_Sender&&)__sender, __env...); |
| }; |
| |
| template <class _Sender, class... _Env> |
| concept __has_default_transform_sender = // |
| sender_expr<_Sender> // |
| && __has_transform_sender<tag_of_t<_Sender>, _Sender, _Env...>; |
| |
| template <class _Type, class _Sender, class _Env> |
| concept __has_transform_env = |
| requires(_Type __obj, _Sender&& __sender, _Env&& __env) { |
| __obj.transform_env((_Sender&&)__sender, (_Env&&)__env); |
| }; |
| |
| template <class _Sender, class _Env> |
| concept __has_default_transform_env = // |
| sender_expr<_Sender> // |
| && __has_transform_env<tag_of_t<_Sender>, _Sender, _Env>; |
| |
| template <class _DomainOrTag, class... _Args> |
| concept __has_apply_sender = requires(_DomainOrTag __tag, _Args&&... __args) { |
| __tag.apply_sender((_Args&&)__args...); |
| }; |
| } // namespace __domain |
| |
| namespace __write_ |
| { |
| struct __write_t; |
| } |
| |
| struct default_domain |
| { |
| default_domain() = default; |
| |
| // Called without the environment during eager customization |
| template <class _Sender> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| decltype(auto) transform_sender(_Sender&& __sndr) const |
| { |
| // Look for a legacy customization for the given tag, and if found, |
| // apply it. |
| if constexpr (__callable<__sexpr_apply_t, _Sender, |
| __domain::__legacy_customization>) |
| { |
| return stdexec::__sexpr_apply((_Sender&&)__sndr, |
| __domain::__legacy_customization()); |
| } |
| else if constexpr (__domain::__has_default_transform_sender<_Sender>) |
| { |
| return tag_of_t<_Sender>().transform_sender((_Sender&&)__sndr); |
| } |
| else |
| { |
| return static_cast<_Sender>((_Sender&&)__sndr); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| // Called with an environment during lazy customization |
| template <class _Sender, class _Env> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) const |
| { |
| if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>) |
| { |
| return tag_of_t<_Sender>().transform_sender((_Sender&&)__sndr, |
| __env); |
| } |
| else |
| { |
| return static_cast<_Sender>((_Sender&&)__sndr); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| template <class _Tag, class _Sender, class... _Args> |
| requires __domain::__has_legacy_c11n<_Tag, _Sender, _Args...> || |
| __domain::__has_apply_sender<_Tag, _Sender, _Args...> |
| STDEXEC_ATTRIBUTE((always_inline)) decltype(auto) |
| apply_sender(_Tag, _Sender&& __sndr, _Args&&... __args) const |
| { |
| // Look for a legacy customization for the given tag, and if found, |
| // apply it. |
| if constexpr (__domain::__has_legacy_c11n<_Tag, _Sender, _Args...>) |
| { |
| return __domain::__legacy_c11n_fn<_Tag, _Sender, _Args...>()( |
| static_cast<_Sender&&>(__sndr), |
| static_cast<_Args&&>(__args)...); |
| } |
| else |
| { |
| return _Tag().apply_sender((_Sender&&)__sndr, (_Args&&)__args...); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| template <class _Sender, class _Env> |
| decltype(auto) transform_env(_Sender&& __sndr, _Env&& __env) const noexcept |
| { |
| if constexpr (__domain::__has_default_transform_env<_Sender, _Env>) |
| { |
| return tag_of_t<_Sender>().transform_env((_Sender&&)__sndr, |
| (_Env&&)__env); |
| } |
| else |
| { |
| return static_cast<_Env>((_Env&&)__env); |
| } |
| } |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| // [exec.queries] |
| namespace __queries |
| { |
| struct forwarding_query_t |
| { |
| template <class _Query> |
| constexpr bool operator()(_Query __query) const noexcept |
| { |
| if constexpr (tag_invocable<forwarding_query_t, _Query>) |
| { |
| return tag_invoke(*this, (_Query&&)__query); |
| } |
| else if constexpr (std::derived_from<_Query, forwarding_query_t>) |
| { |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| }; |
| |
| struct query_or_t |
| { |
| template <class _Query, class _Queryable, class _Default> |
| constexpr auto operator()(_Query, _Queryable&&, _Default&& __default) const |
| noexcept(__nothrow_constructible_from<_Default, _Default&&>) -> _Default |
| { |
| return (_Default&&)__default; |
| } |
| |
| template <class _Query, class _Queryable, class _Default> |
| requires __callable<_Query, _Queryable> |
| constexpr auto operator()(_Query __query, _Queryable&& __queryable, |
| _Default&&) const |
| noexcept(__nothrow_callable<_Query, _Queryable>) |
| -> __call_result_t<_Query, _Queryable> |
| { |
| return ((_Query&&)__query)((_Queryable&&)__queryable); |
| } |
| }; |
| |
| struct execute_may_block_caller_t : __query<execute_may_block_caller_t> |
| { |
| template <class _Tp> |
| requires tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>> |
| constexpr bool operator()(_Tp&& __t) const noexcept |
| { |
| static_assert( |
| same_as<bool, tag_invoke_result_t<execute_may_block_caller_t, |
| __cref_t<_Tp>>>); |
| static_assert( |
| nothrow_tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>); |
| return tag_invoke(execute_may_block_caller_t{}, std::as_const(__t)); |
| } |
| |
| constexpr bool operator()(auto&&) const noexcept |
| { |
| return true; |
| } |
| }; |
| |
| struct get_forward_progress_guarantee_t : |
| __query<get_forward_progress_guarantee_t> |
| { |
| template <class _Tp> |
| requires tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>> |
| constexpr auto operator()(_Tp&& __t) const noexcept( |
| nothrow_tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>) |
| -> tag_invoke_result_t<get_forward_progress_guarantee_t, __cref_t<_Tp>> |
| { |
| return tag_invoke(get_forward_progress_guarantee_t{}, |
| std::as_const(__t)); |
| } |
| |
| constexpr stdexec::forward_progress_guarantee |
| operator()(auto&&) const noexcept |
| { |
| return stdexec::forward_progress_guarantee::weakly_parallel; |
| } |
| }; |
| |
| struct __has_algorithm_customizations_t : |
| __query<__has_algorithm_customizations_t> |
| { |
| template <class _Tp> |
| using __result_t = |
| tag_invoke_result_t<__has_algorithm_customizations_t, __cref_t<_Tp>>; |
| |
| template <class _Tp> |
| requires tag_invocable<__has_algorithm_customizations_t, __cref_t<_Tp>> |
| constexpr __result_t<_Tp> operator()(_Tp&&) const |
| noexcept(noexcept(__result_t<_Tp>{})) |
| { |
| using _Boolean = tag_invoke_result_t<__has_algorithm_customizations_t, |
| __cref_t<_Tp>>; |
| static_assert(_Boolean{} |
| ? true |
| : true); // must be contextually convertible to bool |
| return _Boolean{}; |
| } |
| |
| constexpr std::false_type operator()(auto&&) const noexcept |
| { |
| return {}; |
| } |
| }; |
| |
| // TODO: implement allocator concept |
| template <class _T0> |
| concept __allocator_c = true; |
| |
| struct get_scheduler_t : __query<get_scheduler_t> |
| { |
| friend constexpr bool tag_invoke(forwarding_query_t, |
| const get_scheduler_t&) noexcept |
| { |
| return true; |
| } |
| |
| template <class _Env> |
| requires tag_invocable<get_scheduler_t, const _Env&> |
| auto operator()(const _Env& __env) const noexcept |
| -> tag_invoke_result_t<get_scheduler_t, const _Env&>; |
| |
| auto operator()() const noexcept; |
| }; |
| |
| struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t> |
| { |
| friend constexpr bool tag_invoke(forwarding_query_t, |
| const get_delegatee_scheduler_t&) noexcept |
| { |
| return true; |
| } |
| |
| template <class _Env> |
| requires tag_invocable<get_delegatee_scheduler_t, const _Env&> |
| auto operator()(const _Env& __t) const noexcept |
| -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>; |
| |
| auto operator()() const noexcept; |
| }; |
| |
| struct get_allocator_t : __query<get_allocator_t> |
| { |
| friend constexpr bool tag_invoke(forwarding_query_t, |
| const get_allocator_t&) noexcept |
| { |
| return true; |
| } |
| |
| template <class _Env> |
| requires tag_invocable<get_allocator_t, const _Env&> |
| auto operator()(const _Env& __env) const noexcept |
| -> tag_invoke_result_t<get_allocator_t, const _Env&> |
| { |
| static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>); |
| static_assert( |
| __allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>); |
| return tag_invoke(get_allocator_t{}, __env); |
| } |
| |
| auto operator()() const noexcept; |
| }; |
| |
| struct get_stop_token_t : __query<get_stop_token_t> |
| { |
| friend constexpr bool tag_invoke(forwarding_query_t, |
| const get_stop_token_t&) noexcept |
| { |
| return true; |
| } |
| |
| template <class _Env> |
| never_stop_token operator()(const _Env&) const noexcept |
| { |
| return {}; |
| } |
| |
| template <class _Env> |
| requires tag_invocable<get_stop_token_t, const _Env&> |
| auto operator()(const _Env& __env) const noexcept |
| -> tag_invoke_result_t<get_stop_token_t, const _Env&> |
| { |
| static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>); |
| static_assert(stoppable_token< |
| tag_invoke_result_t<get_stop_token_t, const _Env&>>); |
| return tag_invoke(get_stop_token_t{}, __env); |
| } |
| |
| auto operator()() const noexcept; |
| }; |
| |
| template <class _Queryable, class _CPO> |
| concept __has_completion_scheduler_for = |
| queryable<_Queryable> && // |
| tag_invocable<get_completion_scheduler_t<_CPO>, const _Queryable&>; |
| |
| template <__completion_tag _CPO> |
| struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_CPO>> |
| { |
| friend constexpr bool |
| tag_invoke(forwarding_query_t, |
| const get_completion_scheduler_t<_CPO>&) noexcept |
| { |
| return true; |
| } |
| |
| template <__has_completion_scheduler_for<_CPO> _Queryable> |
| auto operator()(const _Queryable& __queryable) const noexcept |
| -> tag_invoke_result_t<get_completion_scheduler_t<_CPO>, |
| const _Queryable&>; |
| }; |
| |
| struct get_domain_t |
| { |
| template <class _Ty> |
| requires tag_invocable<get_domain_t, const _Ty&> |
| constexpr auto operator()(const _Ty& __ty) const noexcept |
| -> tag_invoke_result_t<get_domain_t, const _Ty&> |
| { |
| static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>, |
| "Customizations of get_domain must be noexcept."); |
| static_assert(__class<tag_invoke_result_t<get_domain_t, const _Ty&>>, |
| "Customizations of get_domain must return a class type."); |
| return tag_invoke(get_domain_t{}, __ty); |
| } |
| |
| friend constexpr bool tag_invoke(forwarding_query_t, get_domain_t) noexcept |
| { |
| return true; |
| } |
| }; |
| } // namespace __queries |
| |
| using __queries::__has_algorithm_customizations_t; |
| using __queries::execute_may_block_caller_t; |
| using __queries::forwarding_query_t; |
| using __queries::get_allocator_t; |
| using __queries::get_completion_scheduler_t; |
| using __queries::get_delegatee_scheduler_t; |
| using __queries::get_domain_t; |
| using __queries::get_forward_progress_guarantee_t; |
| using __queries::get_scheduler_t; |
| using __queries::get_stop_token_t; |
| using __queries::query_or_t; |
| |
| inline constexpr forwarding_query_t forwarding_query{}; |
| inline constexpr query_or_t query_or{}; // NOT TO SPEC |
| inline constexpr execute_may_block_caller_t execute_may_block_caller{}; |
| inline constexpr __has_algorithm_customizations_t |
| __has_algorithm_customizations{}; |
| inline constexpr get_forward_progress_guarantee_t |
| get_forward_progress_guarantee{}; |
| inline constexpr get_scheduler_t get_scheduler{}; |
| inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{}; |
| inline constexpr get_allocator_t get_allocator{}; |
| inline constexpr get_stop_token_t get_stop_token{}; |
| #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__) |
| template <__completion_tag _CPO> |
| inline constexpr get_completion_scheduler_t<_CPO> get_completion_scheduler{}; |
| #else |
| template <> |
| inline constexpr get_completion_scheduler_t<set_value_t> |
| get_completion_scheduler<set_value_t>{}; |
| template <> |
| inline constexpr get_completion_scheduler_t<set_error_t> |
| get_completion_scheduler<set_error_t>{}; |
| template <> |
| inline constexpr get_completion_scheduler_t<set_stopped_t> |
| get_completion_scheduler<set_stopped_t>{}; |
| #endif |
| |
| template <class _Tag> |
| concept __forwarding_query = forwarding_query(_Tag{}); |
| |
| inline constexpr get_domain_t get_domain{}; |
| |
| template <class _Tag, class _Queryable, class _Default> |
| using __query_result_or_t = |
| __call_result_t<query_or_t, _Tag, _Queryable, _Default>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // env_of |
| namespace __env |
| { |
| struct empty_env |
| { |
| using __t = empty_env; |
| using __id = empty_env; |
| }; |
| |
| template <class _Descriptor> |
| struct __prop; |
| |
| template <class _Value, class... _Tags> |
| struct __prop<_Value(_Tags...)> |
| { |
| using __t = __prop; |
| using __id = __prop; |
| _Value __value_; |
| |
| template <__one_of<_Tags...> _Key> |
| friend auto tag_invoke(_Key, const __prop& __self) // |
| noexcept(__nothrow_decay_copyable<_Value>) -> _Value |
| { |
| return __self.__value_; |
| } |
| }; |
| |
| template <class... _Tags> |
| struct __prop<void(_Tags...)> |
| { |
| using __t = __prop; |
| using __id = __prop; |
| |
| 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 <class _Value, class _Tag, class... _Tags> |
| auto operator()(_Value&& __value, _Tag, _Tags...) const |
| noexcept(__nothrow_decay_copyable<_Value>) |
| -> __prop<__decay_t<_Value>(_Tag, _Tags...)> |
| { |
| return {(_Value&&)__value}; |
| } |
| |
| template <class _Tag> |
| auto operator()(_Tag) const -> __prop<void(_Tag)> |
| { |
| return {}; |
| } |
| }; |
| |
| template <__nothrow_move_constructible _Fun> |
| struct __env_fn |
| { |
| using __t = __env_fn; |
| using __id = __env_fn; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_; |
| |
| template <class _Tag> |
| requires __callable<const _Fun&, _Tag> |
| friend auto tag_invoke(_Tag, const __env_fn& __self) // |
| noexcept(__nothrow_callable<const _Fun&, _Tag>) |
| -> __call_result_t<const _Fun&, _Tag> |
| { |
| return __self.__fun_(_Tag()); |
| } |
| }; |
| |
| template <class _Fun> |
| __env_fn(_Fun) -> __env_fn<_Fun>; |
| |
| template <class _Env> |
| struct __env_fwd |
| { |
| static_assert(__nothrow_move_constructible<_Env>); |
| using __t = __env_fwd; |
| using __id = __env_fwd; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_; |
| |
| template <__forwarding_query _Tag> |
| requires tag_invocable<_Tag, const _Env&> |
| friend auto tag_invoke(_Tag, const __env_fwd& __self) // |
| noexcept(nothrow_tag_invocable<_Tag, const _Env&>) |
| -> tag_invoke_result_t<_Tag, const _Env&> |
| { |
| return _Tag()(__self.__env_); |
| } |
| }; |
| |
| template <class _Env> |
| __env_fwd(_Env&&) -> __env_fwd<_Env>; |
| |
| template <class _Env, class _Base = empty_env> |
| struct __joined_env : __env_fwd<_Base> |
| { |
| static_assert(__nothrow_move_constructible<_Env>); |
| using __t = __joined_env; |
| using __id = __joined_env; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_; |
| |
| const _Base& base() const noexcept |
| { |
| return this->__env_fwd<_Base>::__env_; |
| } |
| |
| template <class _Tag> |
| requires tag_invocable<_Tag, const _Env&> |
| friend auto tag_invoke(_Tag, const __joined_env& __self) // |
| noexcept(nothrow_tag_invocable<_Tag, const _Env&>) |
| -> tag_invoke_result_t<_Tag, const _Env&> |
| { |
| return _Tag()(__self.__env_); |
| } |
| }; |
| |
| template <class _Tag, class _Base> |
| struct __joined_env<__prop<void(_Tag)>, _Base> : __env_fwd<_Base> |
| { |
| using __t = __joined_env; |
| using __id = __joined_env; |
| STDEXEC_ATTRIBUTE((no_unique_address)) __prop<void(_Tag)> __env_; |
| |
| friend void tag_invoke(_Tag, const __joined_env&) noexcept = delete; |
| }; |
| |
| struct __join_env_t |
| { |
| template <class _Env> |
| _Env operator()(_Env&& __env) const noexcept |
| { |
| return (_Env&&)__env; |
| } |
| |
| template <class _Env, class _Base> |
| decltype(auto) operator()(_Env && __env, _Base && __base) const noexcept |
| { |
| using __env_t = __decay_t<_Env>; |
| using __base_t = __decay_t<_Base>; |
| if constexpr (__same_as<__env_t, empty_env>) |
| { |
| return _Base((_Base&&)__base); |
| } |
| else if constexpr (__same_as<__base_t, empty_env>) |
| { |
| return _Env((_Env&&)__env); |
| } |
| else |
| { |
| return __joined_env<_Env, _Base>{{(_Base&&)__base}, (_Env&&)__env}; |
| } |
| } |
| |
| template <class _Env0, class _Env1, class _Env2, class... _Envs> |
| decltype(auto) operator()(_Env0 && __env0, _Env1 && __env1, _Env2 && __env2, |
| _Envs&&... __envs) const noexcept |
| { |
| const auto& __join_env = *this; |
| return __join_env( |
| (_Env0&&)__env0, |
| __join_env((_Env1&&)__env1, |
| __join_env((_Env2&&)__env2, (_Envs&&)__envs...))); |
| } |
| }; |
| |
| template <class... _Envs> |
| using __env_join_t = __call_result_t<__join_env_t, _Envs...>; |
| |
| // To be kept in sync with the promise type used in __connect_awaitable |
| template <class _Env> |
| struct __env_promise |
| { |
| template <class _Ty> |
| _Ty&& await_transform(_Ty&& __value) noexcept |
| { |
| return (_Ty&&)__value; |
| } |
| |
| template <class _Ty> |
| requires tag_invocable<as_awaitable_t, _Ty, __env_promise&> |
| auto await_transform(_Ty&& __value) // |
| noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __env_promise&>) |
| -> tag_invoke_result_t<as_awaitable_t, _Ty, __env_promise&> |
| { |
| return tag_invoke(as_awaitable, (_Ty&&)__value, *this); |
| } |
| |
| friend auto tag_invoke(get_env_t, const __env_promise&) noexcept |
| -> const _Env&; |
| }; |
| |
| // For making an environment from key/value pairs and optionally |
| // another environment. |
| struct __make_env_t |
| { |
| template <__nothrow_move_constructible _Base, |
| __nothrow_move_constructible _Env> |
| auto operator()(_Base&& __base, _Env&& __env) const noexcept |
| -> __env_join_t<_Env, _Base> |
| { |
| return __join_env_t()((_Env&&)__env, (_Base&&)__base); |
| } |
| |
| template <__nothrow_move_constructible _Env> |
| _Env operator()(_Env&& __env) const noexcept |
| { |
| return (_Env&&)__env; |
| } |
| }; |
| |
| // For getting an evaluation environment from a receiver |
| struct get_env_t |
| { |
| template <class _EnvProvider> |
| requires tag_invocable<get_env_t, const _EnvProvider&> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| constexpr auto |
| operator()(const _EnvProvider& __with_env) const noexcept |
| -> tag_invoke_result_t<get_env_t, const _EnvProvider&> |
| { |
| static_assert( |
| queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>); |
| static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>); |
| return tag_invoke(*this, __with_env); |
| } |
| |
| template <class _EnvProvider> |
| constexpr empty_env operator()(const _EnvProvider&) const noexcept |
| { |
| return {}; |
| } |
| }; |
| } // namespace __env |
| |
| using __env::empty_env; |
| using __empty_env |
| [[deprecated("Please use stdexec::empty_env now.")]] = empty_env; |
| |
| using __env::__env_promise; |
| |
| inline constexpr __env::__make_env_t __make_env{}; |
| inline constexpr __env::__join_env_t __join_env{}; |
| inline constexpr __env::get_env_t get_env{}; |
| |
| // for making an environment from a single key/value pair |
| inline constexpr __env::__mkprop_t __mkprop{}; |
| |
| template <class _Tag, class _Value = void> |
| using __with = __env::__prop<_Value(_Tag)>; |
| |
| template <class... _Ts> |
| using __make_env_t = __call_result_t<__env::__make_env_t, _Ts...>; |
| |
| using __default_env = empty_env; |
| |
| template <class _EnvProvider> |
| concept environment_provider = // |
| requires(_EnvProvider& __ep) { |
| { |
| get_env(std::as_const(__ep)) |
| } -> queryable; |
| }; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| template <class _Sender, class _Scheduler, class _Tag = set_value_t> |
| concept __completes_on = __decays_to< |
| __call_result_t<get_completion_scheduler_t<_Tag>, env_of_t<_Sender>>, |
| _Scheduler>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| template <class _Sender, class _Scheduler, class _Env> |
| concept __starts_on = |
| __decays_to<__call_result_t<get_scheduler_t, _Env>, _Scheduler>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| namespace __detail |
| { |
| template <class _Env, class _Tag> |
| using __completion_scheduler_for = |
| __meval_or<__call_result_t, __none_such, get_completion_scheduler_t<_Tag>, |
| _Env>; |
| |
| template <class _Env, class _Tag> |
| using __completion_domain_for = |
| __meval_or<__call_result_t, __none_such, get_domain_t, |
| __completion_scheduler_for<_Env, _Tag>>; |
| |
| // Check the value, error, and stopped channels for completion schedulers. |
| // Of the completion schedulers that are known, they must all have compatible |
| // domains. This computes that domain, or else returns __none_such if there |
| // are no completion schedulers or if they don't specify a domain. |
| template <class _Env> |
| struct __completion_domain_or_none_ : |
| __mdefer_<__transform< |
| __mbind_front_q<__completion_domain_for, _Env>, |
| __remove<__none_such, __munique<__msingle_or<__none_such>>>>, |
| set_value_t, set_error_t, set_stopped_t> |
| {}; |
| |
| template <class _Sender> |
| using __completion_domain_or_none = |
| __t<__completion_domain_or_none_<env_of_t<_Sender>>>; |
| |
| template <class _Sender> |
| concept __consistent_completion_domains = |
| __mvalid<__completion_domain_or_none, _Sender>; |
| |
| template <class _Sender> |
| concept __has_completion_domain = |
| (!same_as<__completion_domain_or_none<_Sender>, __none_such>); |
| |
| template <__has_completion_domain _Sender> |
| using __completion_domain_of = __completion_domain_or_none<_Sender>; |
| } // namespace __detail |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| inline constexpr struct __get_early_domain_t |
| { |
| template <class _Sender, class _Default = default_domain> |
| auto operator()(const _Sender&, _Default __def = {}) const noexcept |
| { |
| if constexpr (__callable<get_domain_t, env_of_t<_Sender>>) |
| { |
| return __call_result_t<get_domain_t, env_of_t<_Sender>>(); |
| } |
| else if constexpr (__detail::__has_completion_domain<_Sender>) |
| { |
| return __detail::__completion_domain_of<_Sender>(); |
| } |
| else |
| { |
| return __def; |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| } __get_early_domain{}; |
| |
| template <class _Sender, class _Default = default_domain> |
| using __early_domain_of_t = |
| __call_result_t<__get_early_domain_t, _Sender, _Default>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| inline constexpr struct __get_late_domain_t |
| { |
| // When connect is looking for a customization, it first checks the sender's |
| // domain. If the sender knows the domain in which it completes, then that |
| // is where the subsequent task will execute. Otherwise, look to the |
| // receiver for late-bound information about the current execution context. |
| template <class _Sender, class _Env> |
| auto operator()(const _Sender& __sndr, const _Env& __env) const noexcept |
| { |
| if constexpr (!same_as<dependent_domain, |
| __early_domain_of_t<_Sender, dependent_domain>>) |
| { |
| return __get_early_domain(__sndr); |
| } |
| else if constexpr (__callable<get_domain_t, const _Env&>) |
| { |
| return get_domain(__env); |
| } |
| else if constexpr (__callable<__composed<get_domain_t, get_scheduler_t>, |
| const _Env&>) |
| { |
| return get_domain(get_scheduler(__env)); |
| } |
| else |
| { |
| return default_domain(); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| // The transfer algorithm is the exception to the rule. It ignores the |
| // domain of the predecessor, and dispatches based on the domain of the |
| // scheduler to which execution is being transferred. |
| template <sender_expr_for<transfer_t> _Sender, class _Env> |
| auto operator()(const _Sender& __sndr, const _Env&) const noexcept |
| { |
| return __sexpr_apply(__sndr, |
| [](__ignore, auto& __data, __ignore) noexcept { |
| auto __sched = get_completion_scheduler<set_value_t>(__data); |
| return query_or(get_domain, __sched, default_domain()); |
| }); |
| } |
| } __get_late_domain{}; |
| |
| template <class _Sender, class _Env> |
| using __late_domain_of_t = __call_result_t<__get_late_domain_t, _Sender, _Env>; |
| |
| namespace __domain |
| { |
| struct __common_domain_fn |
| { |
| static default_domain __common_domain() noexcept |
| { |
| return {}; |
| } |
| |
| template <class _Domain, class... _OtherDomains> |
| requires __all_of<_Domain, _OtherDomains...> |
| static _Domain __common_domain(_Domain __domain, _OtherDomains...) noexcept |
| { |
| return (_Domain&&)__domain; |
| } |
| |
| template <class... _Domains> |
| static auto __common_domain(_Domains...) noexcept // |
| -> __if_c<__one_of<dependent_domain, _Domains...>, dependent_domain, |
| __none_such> |
| { |
| return {}; |
| } |
| |
| auto operator()(__ignore, __ignore, const auto&... __sndrs) const noexcept |
| { |
| return __common_domain(__get_early_domain(__sndrs)...); |
| } |
| }; |
| |
| template <class... _Senders> |
| using __common_domain_t = // |
| __call_result_t<__common_domain_fn, int, int, _Senders...>; |
| |
| template <class... _Senders> |
| concept __has_common_domain = // |
| __none_of<__none_such, __common_domain_t<_Senders...>>; |
| |
| template <class _Tag> |
| struct __get_env_common_domain |
| { |
| template <sender_expr_for<_Tag> _Self> |
| static auto get_env(const _Self& __self) noexcept |
| { |
| using _Domain = |
| __call_result_t<__sexpr_apply_t, const _Self&, __common_domain_fn>; |
| if constexpr (same_as<_Domain, default_domain>) |
| { |
| return empty_env(); |
| } |
| else |
| { |
| return __mkprop(__sexpr_apply(__self, __common_domain_fn()), |
| get_domain); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| }; |
| } // namespace __domain |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.receivers] |
| namespace __receivers |
| { |
| struct set_value_t |
| { |
| template <class _Fn, class... _Args> |
| using __f = __minvoke<_Fn, _Args...>; |
| |
| template <class _Receiver, class... _As> |
| requires tag_invocable<set_value_t, _Receiver, _As...> |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| void |
| operator()(_Receiver&& __rcvr, _As&&... __as) const noexcept |
| { |
| static_assert(nothrow_tag_invocable<set_value_t, _Receiver, _As...>); |
| (void)tag_invoke(set_value_t{}, (_Receiver&&)__rcvr, (_As&&)__as...); |
| } |
| }; |
| |
| struct set_error_t |
| { |
| template <class _Fn, class... _Args> |
| requires(sizeof...(_Args) == 1) |
| using __f = __minvoke<_Fn, _Args...>; |
| |
| template <class _Receiver, class _Error> |
| requires tag_invocable<set_error_t, _Receiver, _Error> |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| void |
| operator()(_Receiver&& __rcvr, _Error&& __err) const noexcept |
| { |
| static_assert(nothrow_tag_invocable<set_error_t, _Receiver, _Error>); |
| (void)tag_invoke(set_error_t{}, (_Receiver&&)__rcvr, (_Error&&)__err); |
| } |
| }; |
| |
| struct set_stopped_t |
| { |
| template <class _Fn, class... _Args> |
| requires(sizeof...(_Args) == 0) |
| using __f = __minvoke<_Fn, _Args...>; |
| |
| template <class _Receiver> |
| requires tag_invocable<set_stopped_t, _Receiver> |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| void |
| operator()(_Receiver&& __rcvr) const noexcept |
| { |
| static_assert(nothrow_tag_invocable<set_stopped_t, _Receiver>); |
| (void)tag_invoke(set_stopped_t{}, (_Receiver&&)__rcvr); |
| } |
| }; |
| } // namespace __receivers |
| |
| using __receivers::set_error_t; |
| using __receivers::set_stopped_t; |
| using __receivers::set_value_t; |
| inline constexpr set_value_t set_value{}; |
| inline constexpr set_error_t set_error{}; |
| inline constexpr set_stopped_t set_stopped{}; |
| |
| inline constexpr struct __try_call_t |
| { |
| template <class _Receiver, class _Fun, class... _Args> |
| requires __callable<_Fun, _Args...> |
| void operator()(_Receiver&& __rcvr, _Fun __fun, |
| _Args&&... __args) const noexcept |
| { |
| if constexpr (__nothrow_callable<_Fun, _Args...>) |
| { |
| ((_Fun&&)__fun)((_Args&&)__args...); |
| } |
| else |
| { |
| try |
| { |
| ((_Fun&&)__fun)((_Args&&)__args...); |
| } |
| catch (...) |
| { |
| set_error((_Receiver&&)__rcvr, std::current_exception()); |
| } |
| } |
| } |
| } __try_call{}; |
| |
| namespace __error__ |
| { |
| inline constexpr __mstring __unrecognized_sender_type_diagnostic = |
| "The given type cannot be used as a sender with the given environment " |
| "because the attempt to compute the completion signatures failed."__csz; |
| |
| template <__mstring _Diagnostic = __unrecognized_sender_type_diagnostic> |
| struct _UNRECOGNIZED_SENDER_TYPE_; |
| |
| template <class _Sender> |
| struct _WITH_SENDER_; |
| |
| template <class... _Senders> |
| struct _WITH_SENDERS_; |
| |
| template <class _Env> |
| struct _WITH_ENVIRONMENT_; |
| |
| template <class _Ty> |
| struct _WITH_TYPE_; |
| |
| template <class _Receiver> |
| struct _WITH_RECEIVER_; |
| |
| template <class _Sig> |
| struct _MISSING_COMPLETION_SIGNAL_; |
| } // namespace __error__ |
| |
| using __error__::_MISSING_COMPLETION_SIGNAL_; |
| using __error__::_UNRECOGNIZED_SENDER_TYPE_; |
| using __error__::_WITH_ENVIRONMENT_; |
| using __error__::_WITH_RECEIVER_; |
| using __error__::_WITH_TYPE_; |
| |
| template <class _Sender> |
| using _WITH_SENDER_ = __error__::_WITH_SENDER_<__name_of<_Sender>>; |
| |
| template <class... _Senders> |
| using _WITH_SENDERS_ = __error__::_WITH_SENDERS_<__name_of<_Senders>...>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // completion_signatures |
| namespace __compl_sigs |
| { |
| #if STDEXEC_NVHPC() |
| template <class _Ty = __q<__types>, class... _Args> |
| __types<__minvoke<_Ty, _Args...>> __test(set_value_t (*)(_Args...), |
| set_value_t = {}, _Ty = {}); |
| template <class _Ty = __q<__types>, class _Error> |
| __types<__minvoke<_Ty, _Error>> __test(set_error_t (*)(_Error), |
| set_error_t = {}, _Ty = {}); |
| template <class _Ty = __q<__types>> |
| __types<__minvoke<_Ty>> __test(set_stopped_t (*)(), set_stopped_t = {}, |
| _Ty = {}); |
| __types<> __test(__ignore, __ignore, __ignore = {}); |
| |
| template <class _Sig> |
| inline constexpr bool __is_compl_sig = false; |
| template <class... _Args> |
| inline constexpr bool __is_compl_sig<set_value_t(_Args...)> = true; |
| template <class _Error> |
| inline constexpr bool __is_compl_sig<set_error_t(_Error)> = true; |
| template <> |
| inline constexpr bool __is_compl_sig<set_stopped_t()> = true; |
| |
| #else |
| |
| template <same_as<set_value_t> _Tag, class _Ty = __q<__types>, class... _Args> |
| __types<__minvoke<_Ty, _Args...>> __test(_Tag (*)(_Args...)); |
| template <same_as<set_error_t> _Tag, class _Ty = __q<__types>, class _Error> |
| __types<__minvoke<_Ty, _Error>> __test(_Tag (*)(_Error)); |
| template <same_as<set_stopped_t> _Tag, class _Ty = __q<__types>> |
| __types<__minvoke<_Ty>> __test(_Tag (*)()); |
| template <class, class = void> |
| __types<> __test(...); |
| template <class _Tag, class _Ty = void, class... _Args> |
| void __test(_Tag (*)(_Args...) noexcept) = delete; |
| #endif |
| |
| #if STDEXEC_NVHPC() |
| template <class _Sig> |
| concept __completion_signature = __compl_sigs::__is_compl_sig<_Sig>; |
| |
| template <class _Sig, class _Tag, class _Ty = __q<__types>> |
| using __signal_args_t = |
| decltype(__compl_sigs::__test((_Sig*)nullptr, _Tag{}, _Ty{})); |
| #else |
| template <class _Sig> |
| concept __completion_signature = |
| __typename<decltype(__compl_sigs::__test((_Sig*)nullptr))>; |
| |
| template <class _Sig, class _Tag, class _Ty = __q<__types>> |
| using __signal_args_t = |
| decltype(__compl_sigs::__test<_Tag, _Ty>((_Sig*)nullptr)); |
| #endif |
| } // namespace __compl_sigs |
| |
| using __compl_sigs::__completion_signature; |
| |
| template <__compl_sigs::__completion_signature... _Sigs> |
| struct completion_signatures |
| { |
| // Uncomment this to see where completion_signatures is |
| // erroneously getting instantiated: |
| // static_assert(sizeof...(_Sigs) == -1u); |
| }; |
| |
| namespace __compl_sigs |
| { |
| template <class _TaggedTuple, __completion_tag _Tag, class... _Ts> |
| auto __as_tagged_tuple_(_Tag (*)(_Ts...), _TaggedTuple*) |
| -> __mconst<__minvoke<_TaggedTuple, _Tag, _Ts...>>; |
| |
| template <class _Sig, class _TaggedTuple> |
| using __as_tagged_tuple = decltype(__compl_sigs::__as_tagged_tuple_( |
| (_Sig*)nullptr, (_TaggedTuple*)nullptr)); |
| |
| template <class _TaggedTuple, class _Variant, class... _Sigs> |
| auto __for_all_sigs_(completion_signatures<_Sigs...>*, _TaggedTuple*, _Variant*) |
| -> __mconst<__minvoke< |
| _Variant, __minvoke<__as_tagged_tuple<_Sigs, _TaggedTuple>>...>>; |
| |
| template <class _Completions, class _TaggedTuple, class _Variant> |
| using __for_all_sigs = // |
| __minvoke< // |
| decltype(__compl_sigs::__for_all_sigs_( // |
| (_Completions*)nullptr, (_TaggedTuple*)nullptr, |
| (_Variant*)nullptr))>; |
| |
| template <class _Completions, class _TaggedTuple, class _Variant> |
| using __maybe_for_all_sigs = |
| __meval<__for_all_sigs, _Completions, _TaggedTuple, _Variant>; |
| } // namespace __compl_sigs |
| |
| template <class _Completions> |
| concept __valid_completion_signatures = // |
| __is_instance_of<_Completions, completion_signatures>; |
| |
| template <class _Completions> |
| using __invalid_completion_signatures_t = // |
| __mbool<!__valid_completion_signatures<_Completions>>; |
| |
| template <__mstring _Msg = |
| "Expected an instance of template completion_signatures<>"__csz> |
| struct _INVALID_COMPLETION_SIGNATURES_TYPE_ |
| { |
| template <class... _Completions> |
| using __f = // |
| __mexception< |
| _INVALID_COMPLETION_SIGNATURES_TYPE_<>, |
| __minvoke<__mfind_if<__q<__invalid_completion_signatures_t>, |
| __mcompose<__q<_WITH_TYPE_>, __q<__mfront>>>, |
| _Completions...>>; |
| }; |
| |
| template <class... _Completions> |
| using __concat_completion_signatures_impl_t = // |
| __minvoke<__if_c<(__valid_completion_signatures<_Completions> && ...), |
| __mconcat<__munique<__q<completion_signatures>>>, |
| _INVALID_COMPLETION_SIGNATURES_TYPE_<>>, |
| _Completions...>; |
| |
| template <class... _Completions> |
| struct __concat_completion_signatures_ |
| { |
| using __t = __meval<__concat_completion_signatures_impl_t, _Completions...>; |
| }; |
| |
| template <class... _Completions> |
| using __concat_completion_signatures_t = |
| __t<__concat_completion_signatures_<_Completions...>>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.receivers] |
| template <class _Receiver, class _Tag, class... _Args> |
| auto __try_completion(_Tag (*)(_Args...)) |
| -> __mexception<_MISSING_COMPLETION_SIGNAL_<_Tag(_Args...)>, |
| _WITH_RECEIVER_<_Receiver>>; |
| |
| template <class _Receiver, class _Tag, class... _Args> |
| requires nothrow_tag_invocable<_Tag, _Receiver, _Args...> |
| __msuccess __try_completion(_Tag (*)(_Args...)); |
| |
| template <class _Receiver, class... _Sigs> |
| auto __try_completions(completion_signatures<_Sigs...>*) |
| -> decltype((__msuccess(), ..., |
| stdexec::__try_completion<_Receiver>((_Sigs*)nullptr))); |
| |
| template <class _Sender, class _Env> |
| using __unrecognized_sender_error = // |
| __mexception<_UNRECOGNIZED_SENDER_TYPE_<>, _WITH_SENDER_<_Sender>, |
| _WITH_ENVIRONMENT_<_Env>>; |
| |
| template <class _Sender, class _Env> |
| using __completion_signatures_of_t = |
| __call_result_t<get_completion_signatures_t, _Sender, _Env>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.receivers] |
| struct __receiver_base |
| {}; |
| |
| template <class _Receiver> |
| concept __enable_receiver = // |
| requires { typename _Receiver::is_receiver; } || |
| STDEXEC_IS_BASE_OF(__receiver_base, _Receiver); |
| |
| template <class _Receiver> |
| inline constexpr bool enable_receiver = |
| __enable_receiver<_Receiver>; // NOT TO SPEC |
| |
| template <class _Receiver> |
| concept receiver = enable_receiver<__decay_t<_Receiver>> && // |
| environment_provider<__cref_t<_Receiver>> && // |
| move_constructible<__decay_t<_Receiver>> && // |
| constructible_from<__decay_t<_Receiver>, _Receiver>; |
| |
| template <class _Receiver, class _Completions> |
| concept receiver_of = // |
| receiver<_Receiver> && // |
| requires(_Completions* __completions) { |
| { |
| stdexec::__try_completions<__decay_t<_Receiver>>(__completions) |
| } -> __ok; |
| }; |
| |
| template <class _Receiver, class _Sender> |
| concept __receiver_from = |
| receiver_of<_Receiver, |
| __completion_signatures_of_t<_Sender, env_of_t<_Receiver>>>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // Some utilities for debugging senders |
| namespace __debug |
| { |
| struct __is_debug_env_t |
| { |
| friend constexpr bool tag_invoke(forwarding_query_t, |
| const __is_debug_env_t&) noexcept |
| { |
| return true; |
| } |
| template <class _Env> |
| requires tag_invocable<__is_debug_env_t, const _Env&> |
| auto operator()(const _Env&) const noexcept |
| -> tag_invoke_result_t<__is_debug_env_t, const _Env&>; |
| }; |
| template <class _Env> |
| using __debug_env_t = __make_env_t<_Env, __with<__is_debug_env_t, bool>>; |
| |
| template <class _Env> |
| concept __is_debug_env = tag_invocable<__debug::__is_debug_env_t, _Env>; |
| |
| struct __completion_signatures |
| {}; |
| |
| #if STDEXEC_MSVC() |
| // MSVCBUG |
| // https://developercommunity.visualstudio.com/t/Explicit-variable-template-specialisatio/10360032 |
| // MSVCBUG |
| // https://developercommunity.visualstudio.com/t/Non-function-type-interpreted-as-functio/10447831 |
| |
| template <class _Sig> |
| struct __normalize_sig; |
| |
| template <class _Tag, class... _Args> |
| struct __normalize_sig<_Tag(_Args...)> |
| { |
| using __type = _Tag (*)(_Args&&...); |
| }; |
| |
| template <class _Sig> |
| using __normalize_sig_t = typename __normalize_sig<_Sig>::__type; |
| #else |
| template <class _Sig> |
| extern int __normalize_sig; |
| |
| template <class _Tag, class... _Args> |
| extern _Tag (*__normalize_sig<_Tag(_Args...)>)(_Args&&...); |
| |
| template <class _Sig> |
| using __normalize_sig_t = decltype(__normalize_sig<_Sig>); |
| #endif |
| |
| template <class... _Sigs> |
| struct __valid_completions |
| { |
| template <derived_from<__valid_completions> _Self, class _Tag, |
| class... _Args> |
| requires __one_of<_Tag (*)(_Args&&...), _Sigs...> |
| STDEXEC_ATTRIBUTE((host, |
| device)) friend void tag_invoke(_Tag, _Self&&, |
| _Args&&...) noexcept |
| { |
| STDEXEC_TERMINATE(); |
| } |
| }; |
| |
| template <class _CvrefSenderId, class _Env, class _Completions> |
| struct __debug_receiver |
| { |
| using __t = __debug_receiver; |
| using __id = __debug_receiver; |
| using is_receiver = void; |
| }; |
| |
| template <class _CvrefSenderId, class _Env, class... _Sigs> |
| struct __debug_receiver<_CvrefSenderId, _Env, |
| completion_signatures<_Sigs...>> // |
| : __valid_completions<__normalize_sig_t<_Sigs>...> |
| { |
| using __t = __debug_receiver; |
| using __id = __debug_receiver; |
| using is_receiver = void; |
| |
| template <same_as<get_env_t> _Tag> |
| STDEXEC_ATTRIBUTE((host, device)) |
| friend __debug_env_t<_Env> tag_invoke(_Tag, __debug_receiver) noexcept |
| { |
| STDEXEC_TERMINATE(); |
| } |
| }; |
| |
| struct _COMPLETION_SIGNATURES_MISMATCH_ |
| {}; |
| |
| template <class _Sig> |
| struct _COMPLETION_SIGNATURE_ |
| {}; |
| |
| template <class... _Sigs> |
| struct _IS_NOT_ONE_OF_ |
| {}; |
| |
| template <class _Sender> |
| struct _SIGNAL_SENT_BY_SENDER_ |
| {}; |
| |
| template <class _Warning> |
| [[deprecated( |
| "The sender claims to send a particular set of completions," |
| " but in actual fact it completes with a result that is not" |
| " one of the declared completion signatures.")]] STDEXEC_ATTRIBUTE((host, |
| device)) void _ATTENTION_() noexcept |
| {} |
| |
| template <class _Sig> |
| struct __invalid_completion |
| { |
| struct __t |
| { |
| template <class _CvrefSenderId, class _Env, class... _Sigs> |
| // BUGBUG this works around a recently (aug 2023) introduced regression |
| // in nvc++ |
| requires(!__one_of<_Sig, _Sigs...>) |
| __t(__debug_receiver<_CvrefSenderId, _Env, |
| completion_signatures<_Sigs...>>&&) noexcept |
| { |
| using _SenderId = __decay_t<_CvrefSenderId>; |
| using _Sender = stdexec::__t<_SenderId>; |
| using _What = // |
| _WARNING_< // |
| _COMPLETION_SIGNATURES_MISMATCH_, |
| _COMPLETION_SIGNATURE_<_Sig>, _IS_NOT_ONE_OF_<_Sigs...>, |
| _SIGNAL_SENT_BY_SENDER_<__name_of<_Sender>>>; |
| __debug::_ATTENTION_<_What>(); |
| } |
| }; |
| }; |
| |
| template <__completion_tag _Tag, class... _Args> |
| STDEXEC_ATTRIBUTE((host, device)) |
| void tag_invoke(_Tag, __t<__invalid_completion<_Tag(_Args...)>>, |
| _Args&&...) noexcept |
| {} |
| |
| struct __debug_operation |
| { |
| template <same_as<start_t> _Tag> |
| friend void tag_invoke(_Tag, __debug_operation&) noexcept |
| {} |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // `__debug_sender` |
| // =============== |
| // |
| // Understanding why a particular sender doesn't connect to a particular |
| // receiver is nigh impossible in the current design due to limitations in |
| // how the compiler reports overload resolution failure in the presence of |
| // constraints. `__debug_sender` is a utility to assist with the process. It |
| // gives you the deep template instantiation backtrace that you need to |
| // understand where in a chain of senders the problem is occurring. |
| // |
| // ```c++ |
| // template <class _Sigs, class _Env = empty_env, class _Sender> |
| // void __debug_sender(_Sender&& __sndr, _Env = {}); |
| // |
| // template <class _Env = empty_env, class _Sender> |
| // void __debug_sender(_Sender&& __sndr, _Env = {}); |
| // ``` |
| // |
| // **Usage:** |
| // |
| // To find out where in a chain of senders a sender is failing to connect |
| // to a receiver, pass it to `__debug_sender`, optionally with an |
| // environment argument; e.g. `__debug_sender(sndr [, env])` |
| // |
| // To find out why a sender will not connect to a receiver of a particular |
| // signature, specify the set of completion signatures as an explicit template |
| // argument that names an instantiation of `completion_signatures`; e.g.: |
| // `__debug_sender<completion_signatures<set_value_t(int)>>(sndr [, env])`. |
| // |
| // **How it works:** |
| // |
| // The `__debug_sender` function `connect`'s the sender to a |
| // `__debug_receiver`, whose environment is augmented with a special |
| // `__is_debug_env_t` query. An additional fall-back overload is added to |
| // the `connect` CPO that recognizes receivers whose environments respond to |
| // that query and lets them through. Then in a non-immediate context, it |
| // looks for a `tag_invoke(connect_t...)` overload for the input sender and |
| // receiver. This will recurse until it hits the `tag_invoke` call that is |
| // causing the failure. |
| // |
| // At least with clang, this gives me a nice backtrace, at the bottom of |
| // which is the faulty `tag_invoke` overload with a mention of the |
| // constraint that failed. |
| template <class _Sigs, class _Env = empty_env, class _Sender> |
| void __debug_sender(_Sender&& __sndr, const _Env& = {}) |
| { |
| if constexpr (!__is_debug_env<_Env>) |
| { |
| if (sizeof(_Sender) == ~0) |
| { // never true |
| using _Receiver = |
| __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>; |
| using _Operation = connect_result_t<_Sender, _Receiver>; |
| // static_assert(receiver_of<_Receiver, _Sigs>); |
| if constexpr (!same_as<_Operation, __debug_operation>) |
| { |
| auto __op = connect((_Sender&&)__sndr, _Receiver{}); |
| start(__op); |
| } |
| } |
| } |
| } |
| |
| template <class _Env = empty_env, class _Sender> |
| void __debug_sender(_Sender&& __sndr, const _Env& = {}) |
| { |
| if constexpr (!__is_debug_env<_Env>) |
| { |
| if (sizeof(_Sender) == ~0) |
| { // never true |
| using _Sigs = |
| __completion_signatures_of_t<_Sender, __debug_env_t<_Env>>; |
| if constexpr (!same_as<_Sigs, __debug::__completion_signatures>) |
| { |
| using _Receiver = |
| __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>; |
| using _Operation = connect_result_t<_Sender, _Receiver>; |
| // static_assert(receiver_of<_Receiver, _Sigs>); |
| if constexpr (!same_as<_Operation, __debug_operation>) |
| { |
| auto __op = connect((_Sender&&)__sndr, _Receiver{}); |
| start(__op); |
| } |
| } |
| } |
| } |
| } |
| } // namespace __debug |
| |
| using __debug::__debug_sender; |
| using __debug::__is_debug_env; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // dependent_domain |
| struct dependent_domain |
| { |
| template <sender_expr _Sender, class _Env> |
| requires same_as<__early_domain_of_t<_Sender>, dependent_domain> |
| STDEXEC_ATTRIBUTE((always_inline)) decltype(auto) |
| transform_sender(_Sender&& __sndr, const _Env& __env) const; |
| }; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.transform_sender] |
| namespace __domain |
| { |
| struct __transform_env |
| { |
| template <class _Domain, class _Sender, class _Env> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| /*constexpr*/ decltype(auto) operator()(_Domain __dom, _Sender && __sndr, |
| _Env && __env) const noexcept |
| { |
| if constexpr (__domain::__has_transform_env<_Domain, _Sender, _Env>) |
| { |
| return __dom.transform_env((_Sender&&)__sndr, (_Env&&)__env); |
| } |
| else |
| { |
| return default_domain().transform_env((_Sender&&)__sndr, |
| (_Env&&)__env); |
| } |
| } |
| }; |
| |
| struct __transform_sender_1 |
| { |
| template <class _Domain, class _Sender, class... _Env> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| /*constexpr*/ decltype(auto) operator()(_Domain __dom, _Sender && __sndr, |
| const _Env&... __env) const |
| { |
| if constexpr (__domain::__has_transform_sender<_Domain, _Sender, |
| _Env...>) |
| { |
| return __dom.transform_sender((_Sender&&)__sndr, __env...); |
| } |
| else |
| { |
| return default_domain().transform_sender((_Sender&&)__sndr, |
| __env...); |
| } |
| } |
| }; |
| |
| template <class _Ty, class _Uy> |
| concept __decay_same_as = same_as<__decay_t<_Ty>, __decay_t<_Uy>>; |
| |
| struct __transform_sender |
| { |
| template <class _Self = __transform_sender, class _Domain, class _Sender, |
| class... _Env> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| /*constexpr*/ decltype(auto) operator()(_Domain __dom, _Sender && __sndr, |
| const _Env&... __env) const |
| { |
| using _Sender2 = __call_result_t<__transform_sender_1, _Domain, _Sender, |
| const _Env&...>; |
| // If the transformation doesn't change the sender's type, then do not |
| // apply the transform recursively. |
| if constexpr (__decay_same_as<_Sender, _Sender2>) |
| { |
| return __transform_sender_1()(__dom, (_Sender&&)__sndr, __env...); |
| } |
| else |
| { |
| // We transformed the sender and got back a different sender. |
| // Transform that one too. |
| return _Self()( |
| __dom, |
| __transform_sender_1()(__dom, (_Sender&&)__sndr, __env...), |
| __env...); |
| } |
| } |
| }; |
| |
| struct __transform_dependent_sender |
| { |
| // If we are doing a lazy customization of a type whose domain is |
| // value-dependent (e.g., let_value), first transform the sender to |
| // determine the domain. Then continue transforming the sender with the |
| // requested domain. |
| template <class _Domain, sender_expr _Sender, class _Env> |
| requires same_as<__early_domain_of_t<_Sender>, dependent_domain> |
| /*constexpr*/ decltype(auto) operator()(_Domain __dom, _Sender && __sndr, |
| const _Env & __env) const |
| { |
| static_assert(__none_of<_Domain, dependent_domain>); |
| return __transform_sender()( |
| __dom, |
| dependent_domain().transform_sender((_Sender&&)__sndr, __env), |
| __env); |
| } |
| }; |
| } // namespace __domain |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.transform_sender] |
| inline constexpr struct transform_sender_t : |
| __domain::__transform_sender, |
| __domain::__transform_dependent_sender |
| { |
| using __domain::__transform_sender::operator(); |
| using __domain::__transform_dependent_sender::operator(); |
| } transform_sender{}; |
| |
| template <class _Domain, class _Sender, class... _Env> |
| using transform_sender_result_t = |
| __call_result_t<transform_sender_t, _Domain, _Sender, _Env...>; |
| |
| inline constexpr __domain::__transform_env transform_env{}; |
| |
| struct _CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_ |
| {}; |
| |
| template <sender_expr _Sender, class _Env> |
| requires same_as<__early_domain_of_t<_Sender>, dependent_domain> |
| decltype(auto) dependent_domain::transform_sender(_Sender&& __sndr, |
| const _Env& __env) const |
| { |
| // apply any algorithm-specific transformation to the environment |
| const auto& __env2 = transform_env(*this, (_Sender&&)__sndr, __env); |
| |
| // recursively transform the sender to determine the domain |
| return __sexpr_apply((_Sender&&)__sndr, |
| [&]<class _Tag, class _Data, class... _Childs>( |
| _Tag, _Data&& __data, _Childs&&... __childs) { |
| // TODO: propagate meta-exceptions here: |
| auto __sndr2 = __make_sexpr<_Tag>( |
| (_Data&&)__data, __domain::__transform_sender()( |
| *this, (_Childs&&)__childs, __env2)...); |
| using _Sender2 = decltype(__sndr2); |
| |
| auto __domain2 = __sexpr_apply(__sndr2, __domain::__common_domain_fn()); |
| using _Domain2 = decltype(__domain2); |
| |
| if constexpr (same_as<_Domain2, __none_such>) |
| { |
| return __mexception<_CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_, |
| _WITH_SENDER_<_Sender2>>(); |
| } |
| else |
| { |
| return __domain::__transform_sender()(__domain2, std::move(__sndr2), |
| __env); |
| } |
| STDEXEC_UNREACHABLE(); |
| }); |
| } |
| |
| // A helper for use when building sender trees where each node must be |
| // transformed. |
| template <class _Domain, class _Env> |
| auto __make_transformer(_Domain, const _Env& __env) |
| { |
| return [&]<class _Tag>(_Tag) { |
| return [&]<class... _Args>(_Args&&... __args) -> decltype(auto) { |
| return stdexec::transform_sender(_Domain(), |
| _Tag()((_Args&&)__args...), __env); |
| }; |
| }; |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| template <class _Tag, class _Domain, class _Sender, class... _Args> |
| concept __has_implementation_for = |
| __domain::__has_apply_sender<_Domain, _Tag, _Sender, _Args...> || |
| __domain::__has_apply_sender<default_domain, _Tag, _Sender, _Args...>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.apply_sender] |
| inline constexpr struct apply_sender_t |
| { |
| template <class _Domain, class _Tag, class _Sender, class... _Args> |
| requires __has_implementation_for<_Tag, _Domain, _Sender, _Args...> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| /*constexpr*/ decltype(auto) |
| operator()(_Domain __dom, _Tag, _Sender && __sndr, |
| _Args&&... __args) const |
| { |
| if constexpr (__domain::__has_apply_sender<_Domain, _Tag, _Sender, |
| _Args...>) |
| { |
| return __dom.apply_sender(_Tag(), (_Sender&&)__sndr, |
| (_Args&&)__args...); |
| } |
| else |
| { |
| return default_domain().apply_sender(_Tag(), (_Sender&&)__sndr, |
| (_Args&&)__args...); |
| } |
| } |
| } apply_sender{}; |
| |
| template <class _Domain, class _Tag, class _Sender, class... _Args> |
| using apply_sender_result_t = |
| __call_result_t<apply_sender_t, _Domain, _Tag, _Sender, _Args...>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.sndtraits] |
| namespace __get_completion_signatures |
| { |
| template <class _Sender, class _Env> |
| using __tfx_sender = |
| transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>; |
| |
| template <class _Sender, class _Env> |
| concept __with_tag_invoke = // |
| tag_invocable<get_completion_signatures_t, __tfx_sender<_Sender, _Env>, |
| _Env>; |
| |
| template <class _Sender, class _Env> |
| using __member_alias_t = // |
| typename __decay_t<__tfx_sender<_Sender, _Env>>::completion_signatures; |
| |
| template <class _Sender, class _Env = empty_env> |
| concept __with_member_alias = __mvalid<__member_alias_t, _Sender, _Env>; |
| |
| struct get_completion_signatures_t |
| { |
| template <class _Sender, class _Env> |
| static auto __impl() |
| { |
| static_assert(sizeof(_Sender), |
| "Incomplete type used with get_completion_signatures"); |
| static_assert(sizeof(_Env), |
| "Incomplete type used with get_completion_signatures"); |
| |
| if constexpr (__with_tag_invoke<_Sender, _Env>) |
| { |
| using _TfxSender = __tfx_sender<_Sender, _Env>; |
| using _Result = tag_invoke_result_t<get_completion_signatures_t, |
| _TfxSender, _Env>; |
| return (_Result(*)()) nullptr; |
| } |
| else if constexpr (__with_member_alias<_Sender, _Env>) |
| { |
| using _Result = __member_alias_t<_Sender, _Env>; |
| return (_Result(*)()) nullptr; |
| } |
| else if constexpr (__awaitable<_Sender, __env_promise<_Env>>) |
| { |
| using _Result = __await_result_t<_Sender, __env_promise<_Env>>; |
| return (completion_signatures< |
| // set_value_t() or set_value_t(T) |
| __minvoke<__remove<void, __qf<set_value_t>>, _Result>, |
| set_error_t(std::exception_ptr), |
| set_stopped_t()>(*)()) nullptr; |
| } |
| else if constexpr (__is_debug_env<_Env>) |
| { |
| using __tag_invoke::tag_invoke; |
| // This ought to cause a hard error that indicates where the problem |
| // is. |
| using _Completions [[maybe_unused]] = |
| tag_invoke_result_t<get_completion_signatures_t, _Sender, _Env>; |
| return (__debug::__completion_signatures(*)()) nullptr; |
| } |
| else |
| { |
| using _Result = |
| __mexception<_UNRECOGNIZED_SENDER_TYPE_<>, |
| _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>; |
| return (_Result(*)()) nullptr; |
| } |
| } |
| |
| // NOT TO SPEC: if we're unable to compute the completion signatures, |
| // return an error type instead of SFINAE. |
| template <class _Sender, class _Env = __default_env> |
| constexpr auto operator()(_Sender&&, const _Env&) const noexcept |
| -> decltype(__impl<_Sender, _Env>()()) |
| { |
| return {}; |
| } |
| }; |
| } // namespace __get_completion_signatures |
| |
| using __get_completion_signatures::get_completion_signatures_t; |
| inline constexpr get_completion_signatures_t get_completion_signatures{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders] |
| namespace __detail |
| { |
| template <class _Sender> |
| concept __enable_sender = // |
| requires { typename _Sender::is_sender; } || // |
| __awaitable<_Sender, __env_promise<empty_env>>; |
| } // namespace __detail |
| |
| template <class _Sender> |
| inline constexpr bool enable_sender = __detail::__enable_sender<_Sender>; |
| |
| template <class _Sender, class _Env = empty_env> |
| concept sender = enable_sender<__decay_t<_Sender>> && // |
| environment_provider<__cref_t<_Sender>> && // |
| __detail::__consistent_completion_domains<_Sender> && // |
| move_constructible<__decay_t<_Sender>> && // |
| constructible_from<__decay_t<_Sender>, _Sender>; |
| |
| template <class _Sender, class _Env = empty_env> |
| concept sender_in = // |
| sender<_Sender> && // |
| requires(_Sender&& __sndr, _Env&& __env) { |
| { |
| get_completion_signatures((_Sender&&)__sndr, (_Env&&)__env) |
| } -> __valid_completion_signatures; |
| }; |
| |
| #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() |
| // __checked_completion_signatures is for catching logic bugs in a typed |
| // sender's metadata. If sender<S> and sender_in<S, Ctx> are both true, then |
| // they had better report the same metadata. This completion signatures wrapper |
| // enforces that at compile time. |
| template <class _Sender, class _Env> |
| auto __checked_completion_signatures(_Sender&& __sndr, |
| const _Env& __env) noexcept |
| { |
| using __completions_t = __completion_signatures_of_t<_Sender, _Env>; |
| stdexec::__debug_sender<__completions_t>((_Sender&&)__sndr, __env); |
| return __completions_t{}; |
| } |
| |
| template <class _Sender, class _Env = empty_env> |
| requires sender_in<_Sender, _Env> |
| using completion_signatures_of_t = |
| decltype(stdexec::__checked_completion_signatures(__declval<_Sender>(), |
| __declval<_Env>())); |
| #else |
| template <class _Sender, class _Env = empty_env> |
| requires sender_in<_Sender, _Env> |
| using completion_signatures_of_t = __completion_signatures_of_t<_Sender, _Env>; |
| #endif |
| |
| struct __not_a_variant |
| { |
| __not_a_variant() = delete; |
| }; |
| template <class... _Ts> |
| using __variant = // |
| __minvoke<__if_c<sizeof...(_Ts) != 0, |
| __transform<__q<__decay_t>, __munique<__q<std::variant>>>, |
| __mconst<__not_a_variant>>, |
| _Ts...>; |
| |
| using __nullable_variant_t = |
| __munique<__mbind_front_q<std::variant, std::monostate>>; |
| |
| template <class... _Ts> |
| using __decayed_tuple = __meval<std::tuple, __decay_t<_Ts>...>; |
| |
| template <class _Tag, class _Tuple> |
| struct __select_completions_for |
| { |
| template <same_as<_Tag> _Tag2, class... _Args> |
| using __f = __minvoke<_Tag2, _Tuple, _Args...>; |
| }; |
| |
| template <class _Tuple> |
| struct __invoke_completions |
| { |
| template <class _Tag, class... _Args> |
| using __f = __minvoke<_Tag, _Tuple, _Args...>; |
| }; |
| |
| template <class _Tag, class _Tuple> |
| using __select_completions_for_or = // |
| __with_default<__select_completions_for<_Tag, _Tuple>, __>; |
| |
| template <class _Tag, class _Completions> |
| using __only_gather_signal = // |
| __compl_sigs::__maybe_for_all_sigs< |
| _Completions, __select_completions_for_or<_Tag, __qf<_Tag>>, |
| __remove<__, __q<completion_signatures>>>; |
| |
| template <class _Tag, class _Completions, class _Tuple, class _Variant> |
| using __gather_signal = // |
| __compl_sigs::__maybe_for_all_sigs<__only_gather_signal<_Tag, _Completions>, |
| __invoke_completions<_Tuple>, _Variant>; |
| |
| template <class _Tag, class _Sender, class _Env, class _Tuple, class _Variant> |
| using __gather_completions_for = // |
| __meval< // |
| __gather_signal, _Tag, __completion_signatures_of_t<_Sender, _Env>, |
| _Tuple, _Variant>; |
| |
| template < // |
| class _Sender, // |
| class _Env = __default_env, // |
| class _Tuple = __q<__decayed_tuple>, // |
| class _Variant = __q<__variant>> |
| using __try_value_types_of_t = // |
| __gather_completions_for<set_value_t, _Sender, _Env, _Tuple, _Variant>; |
| |
| template < // |
| class _Sender, // |
| class _Env = __default_env, // |
| class _Tuple = __q<__decayed_tuple>, // |
| class _Variant = __q<__variant>> |
| requires sender_in<_Sender, _Env> |
| using __value_types_of_t = // |
| __msuccess_or_t<__try_value_types_of_t<_Sender, _Env, _Tuple, _Variant>>; |
| |
| template <class _Sender, class _Env = __default_env, |
| class _Variant = __q<__variant>> |
| using __try_error_types_of_t = |
| __gather_completions_for<set_error_t, _Sender, _Env, __q<__midentity>, |
| _Variant>; |
| |
| template <class _Sender, class _Env = __default_env, |
| class _Variant = __q<__variant>> |
| requires sender_in<_Sender, _Env> |
| using __error_types_of_t = |
| __msuccess_or_t<__try_error_types_of_t<_Sender, _Env, _Variant>>; |
| |
| template < // |
| class _Sender, // |
| class _Env = __default_env, // |
| template <class...> class _Tuple = __decayed_tuple, // |
| template <class...> class _Variant = __variant> |
| requires sender_in<_Sender, _Env> |
| using value_types_of_t = |
| __value_types_of_t<_Sender, _Env, __q<_Tuple>, __q<_Variant>>; |
| |
| template <class _Sender, class _Env = __default_env, |
| template <class...> class _Variant = __variant> |
| requires sender_in<_Sender, _Env> |
| using error_types_of_t = __error_types_of_t<_Sender, _Env, __q<_Variant>>; |
| |
| template <class _Tag, class _Sender, class _Env = __default_env> |
| using __try_count_of = // |
| __compl_sigs::__maybe_for_all_sigs< |
| __completion_signatures_of_t<_Sender, _Env>, __q<__mfront>, |
| __mcount<_Tag>>; |
| |
| template <class _Tag, class _Sender, class _Env = __default_env> |
| requires sender_in<_Sender, _Env> |
| using __count_of = __msuccess_or_t<__try_count_of<_Tag, _Sender, _Env>>; |
| |
| template <class _Tag, class _Sender, class _Env = __default_env> |
| requires __mvalid<__count_of, _Tag, _Sender, _Env> |
| inline constexpr bool __sends = (__v<__count_of<_Tag, _Sender, _Env>> != 0); |
| |
| template <class _Sender, class _Env = __default_env> |
| requires __mvalid<__count_of, set_stopped_t, _Sender, _Env> |
| inline constexpr bool sends_stopped = __sends<set_stopped_t, _Sender, _Env>; |
| |
| template <class _Sender, class _Env = __default_env> |
| using __single_sender_value_t = |
| __value_types_of_t<_Sender, _Env, __msingle_or<void>, __q<__msingle>>; |
| |
| template <class _Sender, class _Env = __default_env> |
| using __single_value_variant_sender_t = |
| value_types_of_t<_Sender, _Env, __types, __msingle>; |
| |
| template <class _Sender, class _Env = __default_env> |
| concept __single_typed_sender = |
| sender_in<_Sender, _Env> && |
| __mvalid<__single_sender_value_t, _Sender, _Env>; |
| |
| template <class _Sender, class _Env = __default_env> |
| concept __single_value_variant_sender = |
| sender_in<_Sender, _Env> && |
| __mvalid<__single_value_variant_sender_t, _Sender, _Env>; |
| |
| template <class... Errs> |
| using __nofail = __mbool<sizeof...(Errs) == 0>; |
| |
| template <class _Sender, class _Env = __default_env> |
| concept __nofail_sender = sender_in<_Sender, _Env> && |
| (__v<error_types_of_t<_Sender, _Env, __nofail>>); |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| namespace __compl_sigs |
| { |
| template <class... _Args> |
| using __default_set_value = completion_signatures<set_value_t(_Args...)>; |
| |
| template <class _Error> |
| using __default_set_error = completion_signatures<set_error_t(_Error)>; |
| |
| template <__valid_completion_signatures... _Sigs> |
| using __ensure_concat_ = |
| __minvoke<__mconcat<__q<completion_signatures>>, _Sigs...>; |
| |
| template <class... _Sigs> |
| using __ensure_concat = __mtry_eval<__ensure_concat_, _Sigs...>; |
| |
| template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr, |
| class _SetStp> |
| using __compl_sigs_impl = // |
| __concat_completion_signatures_t< |
| _Sigs, |
| __mtry_eval<__try_value_types_of_t, _Sender, _Env, _SetVal, |
| __q<__ensure_concat>>, |
| __mtry_eval<__try_error_types_of_t, _Sender, _Env, |
| __transform<_SetErr, __q<__ensure_concat>>>, |
| __if<__try_count_of<set_stopped_t, _Sender, _Env>, _SetStp, |
| completion_signatures<>>>; |
| |
| template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr, |
| class _SetStp> |
| requires __mvalid<__compl_sigs_impl, _Sender, _Env, _Sigs, _SetVal, _SetErr, |
| _SetStp> |
| extern __compl_sigs_impl<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp> |
| __compl_sigs_v; |
| |
| template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr, |
| class _SetStp> |
| using __compl_sigs_t = |
| decltype(__compl_sigs_v<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>); |
| |
| template < // |
| class _Sender, // |
| class _Env = __default_env, // |
| class _Sigs = completion_signatures<>, // |
| class _SetValue = __q<__default_set_value>, // |
| class _SetError = __q<__default_set_error>, // |
| class _SetStopped = completion_signatures<set_stopped_t()>> // |
| using __try_make_completion_signatures = // |
| __meval<__compl_sigs_t, _Sender, _Env, _Sigs, _SetValue, _SetError, |
| _SetStopped>; |
| } // namespace __compl_sigs |
| |
| using __compl_sigs::__try_make_completion_signatures; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // NOT TO SPEC |
| // |
| // make_completion_signatures |
| // ========================== |
| // |
| // `make_completion_signatures` takes a sender, and environment, and a bunch |
| // of other template arguments for munging the completion signatures of a |
| // sender in interesting ways. |
| // |
| // ```c++ |
| // template <class... Args> |
| // using __default_set_value = completion_signatures<set_value_t(Args...)>; |
| // |
| // template <class Err> |
| // using __default_set_error = completion_signatures<set_error_t(Err)>; |
| // |
| // template < |
| // sender Sndr, |
| // class Env = __default_env, |
| // class AddlSigs = completion_signatures<>, |
| // template <class...> class SetValue = __default_set_value, |
| // template <class> class SetError = __default_set_error, |
| // class SetStopped = completion_signatures<set_stopped_t()>> |
| // requires sender_in<Sndr, Env> |
| // using make_completion_signatures = |
| // completion_signatures< ... >; |
| // ``` |
| // |
| // * `SetValue` : an alias template that accepts a set of value types and |
| // returns an instance of `completion_signatures`. |
| // * `SetError` : an alias template that accepts an error types and returns a |
| // an instance of `completion_signatures`. |
| // * `SetStopped` : an instantiation of `completion_signatures` with a list |
| // of completion signatures `Sigs...` to the added to the list if the |
| // sender can complete with a stopped signal. |
| // * `AddlSigs` : an instantiation of `completion_signatures` with a list of |
| // completion signatures `Sigs...` to the added to the list |
| // unconditionally. |
| // |
| // `make_completion_signatures` does the following: |
| // * Let `VCs...` be a pack of the `completion_signatures` types in the |
| // `__typelist` named by `value_types_of_t<Sndr, Env, SetValue, |
| // __typelist>`, and let `Vs...` be the concatenation of the packs that are |
| // template arguments to each `completion_signature` in `VCs...`. |
| // * Let `ECs...` be a pack of the `completion_signatures` types in the |
| // `__typelist` named by `error_types_of_t<Sndr, Env, __errorlist>`, where |
| // `__errorlist` is an alias template such that `__errorlist<Ts...>` names |
| // `__typelist<SetError<Ts>...>`, and let `Es...` by the concatenation of |
| // the packs that are the template arguments to each `completion_signature` |
| // in `ECs...`. |
| // * Let `Ss...` be an empty pack if `sends_stopped<Sndr, Env>` is |
| // `false`; otherwise, a pack containing the template arguments of the |
| // `completion_signatures` instantiation named by `SetStopped`. |
| // * Let `MoreSigs...` be a pack of the template arguments of the |
| // `completion_signatures` instantiation named by `AddlSigs`. |
| // |
| // Then `make_completion_signatures<Sndr, Env, AddlSigs, SetValue, SetError, |
| // SendsStopped>` names the type `completion_signatures< Sigs... >` where |
| // `Sigs...` is the unique set of types in `[Vs..., Es..., Ss..., |
| // MoreSigs...]`. |
| // |
| // If any of the above type computations are ill-formed, |
| // `make_completion_signatures<Sndr, Env, AddlSigs, SetValue, SetError, |
| // SendsStopped>` is an alias for an empty struct |
| template < // |
| class _Sender, // |
| class _Env = __default_env, // |
| __valid_completion_signatures _Sigs = completion_signatures<>, // |
| template <class...> class _SetValue = __compl_sigs::__default_set_value, // |
| template <class> class _SetError = __compl_sigs::__default_set_error, // |
| __valid_completion_signatures _SetStopped = |
| completion_signatures<set_stopped_t()>> |
| requires sender_in<_Sender, _Env> |
| using make_completion_signatures = // |
| __msuccess_or_t< // |
| __try_make_completion_signatures<_Sender, _Env, _Sigs, __q<_SetValue>, |
| __q<_SetError>, _SetStopped>>; |
| |
| // Needed fairly often |
| using __with_exception_ptr = |
| completion_signatures<set_error_t(std::exception_ptr)>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.schedule] |
| namespace __schedule |
| { |
| struct schedule_t |
| { |
| template <class _Scheduler> |
| requires tag_invocable<schedule_t, _Scheduler> |
| STDEXEC_ATTRIBUTE((host, device)) auto |
| operator()(_Scheduler&& __sched) const |
| noexcept(nothrow_tag_invocable<schedule_t, _Scheduler>) |
| { |
| static_assert(sender<tag_invoke_result_t<schedule_t, _Scheduler>>); |
| return tag_invoke(schedule_t{}, (_Scheduler&&)__sched); |
| } |
| |
| friend constexpr bool tag_invoke(forwarding_query_t, schedule_t) |
| { |
| return false; |
| } |
| }; |
| } // namespace __schedule |
| |
| using __schedule::schedule_t; |
| inline constexpr schedule_t schedule{}; |
| |
| // NOT TO SPEC |
| template <class _Tag, const auto& _Predicate> |
| concept tag_category = // |
| requires { |
| typename __mbool<bool{_Predicate(_Tag{})}>; |
| requires bool { |
| _Predicate(_Tag{}) |
| }; |
| }; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.schedulers] |
| template <class _Scheduler> |
| concept __has_schedule = // |
| requires(_Scheduler&& __sched) { |
| { |
| schedule((_Scheduler&&)__sched) |
| } -> sender; |
| }; |
| |
| template <class _Scheduler> |
| concept __sender_has_completion_scheduler = |
| requires(_Scheduler&& __sched, |
| get_completion_scheduler_t<set_value_t>&& __tag) { |
| { |
| tag_invoke(std::move(__tag), |
| get_env(schedule((_Scheduler&&)__sched))) |
| } -> same_as<__decay_t<_Scheduler>>; |
| }; |
| |
| template <class _Scheduler> |
| concept scheduler = // |
| __has_schedule<_Scheduler> && // |
| __sender_has_completion_scheduler<_Scheduler> && // |
| equality_comparable<__decay_t<_Scheduler>> && // |
| copy_constructible<__decay_t<_Scheduler>>; |
| |
| template <scheduler _Scheduler> |
| using schedule_result_t = __call_result_t<schedule_t, _Scheduler>; |
| |
| template <receiver _Receiver> |
| using __current_scheduler_t = |
| __call_result_t<get_scheduler_t, env_of_t<_Receiver>>; |
| |
| template <class _SchedulerProvider> |
| concept __scheduler_provider = // |
| requires(const _SchedulerProvider& __sp) { |
| { |
| get_scheduler(__sp) |
| } -> scheduler; |
| }; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.op_state] |
| namespace __start |
| { |
| struct start_t |
| { |
| template <class _Op> |
| requires tag_invocable<start_t, _Op&> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| void |
| operator()(_Op& __op) const noexcept |
| { |
| static_assert(nothrow_tag_invocable<start_t, _Op&>); |
| (void)tag_invoke(start_t{}, __op); |
| } |
| }; |
| } // namespace __start |
| |
| using __start::start_t; |
| inline constexpr start_t start{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.op_state] |
| template <class _Op> |
| concept operation_state = // |
| destructible<_Op> && // |
| std::is_object_v<_Op> && // |
| requires(_Op& __op) { // |
| start(__op); |
| }; |
| |
| #if !STDEXEC_STD_NO_COROUTINES_ |
| ///////////////////////////////////////////////////////////////////////////// |
| // __connect_awaitable_ |
| namespace __connect_awaitable_ |
| { |
| struct __promise_base |
| { |
| __coro::suspend_always initial_suspend() noexcept |
| { |
| return {}; |
| } |
| |
| [[noreturn]] __coro::suspend_always final_suspend() noexcept |
| { |
| std::terminate(); |
| } |
| |
| [[noreturn]] void unhandled_exception() noexcept |
| { |
| std::terminate(); |
| } |
| |
| [[noreturn]] void return_void() noexcept |
| { |
| std::terminate(); |
| } |
| }; |
| |
| struct __operation_base |
| { |
| __coro::coroutine_handle<> __coro_; |
| |
| explicit __operation_base(__coro::coroutine_handle<> __hcoro) noexcept : |
| __coro_(__hcoro) |
| {} |
| |
| __operation_base(__operation_base&& __other) noexcept : |
| __coro_(std::exchange(__other.__coro_, {})) |
| {} |
| |
| ~__operation_base() |
| { |
| if (__coro_) |
| { |
| #if STDEXEC_MSVC() |
| // MSVCBUG |
| // https://developercommunity.visualstudio.com/t/Double-destroy-of-a-local-in-coroutine-d/10456428 |
| |
| // Reassign __coro_ before calling destroy to make the mutation |
| // observable and to hopefully ensure that the compiler does not |
| // eliminate it. |
| auto __coro = __coro_; |
| __coro_ = {}; |
| __coro.destroy(); |
| #else |
| __coro_.destroy(); |
| #endif |
| } |
| } |
| |
| friend void tag_invoke(start_t, __operation_base& __self) noexcept |
| { |
| __self.__coro_.resume(); |
| } |
| }; |
| |
| template <class _ReceiverId> |
| struct __promise; |
| |
| template <class _ReceiverId> |
| struct __operation |
| { |
| struct __t : __operation_base |
| { |
| using promise_type = stdexec::__t<__promise<_ReceiverId>>; |
| using __operation_base::__operation_base; |
| }; |
| }; |
| |
| template <class _ReceiverId> |
| struct __promise |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t : __promise_base |
| { |
| using __id = __promise; |
| |
| explicit __t(auto&, _Receiver& __rcvr) noexcept : __rcvr_(__rcvr) {} |
| |
| __coro::coroutine_handle<> unhandled_stopped() noexcept |
| { |
| set_stopped((_Receiver&&)__rcvr_); |
| // Returning noop_coroutine here causes the __connect_awaitable |
| // coroutine to never resume past the point where it co_await's |
| // the awaitable. |
| return __coro::noop_coroutine(); |
| } |
| |
| stdexec::__t<__operation<_ReceiverId>> get_return_object() noexcept |
| { |
| return stdexec::__t<__operation<_ReceiverId>>{ |
| __coro::coroutine_handle<__t>::from_promise(*this)}; |
| } |
| |
| template <class _Awaitable> |
| _Awaitable&& await_transform(_Awaitable&& __awaitable) noexcept |
| { |
| return (_Awaitable&&)__awaitable; |
| } |
| |
| template <class _Awaitable> |
| requires tag_invocable<as_awaitable_t, _Awaitable, __t&> |
| auto await_transform(_Awaitable&& __awaitable) // |
| noexcept(nothrow_tag_invocable<as_awaitable_t, _Awaitable, __t&>) |
| -> tag_invoke_result_t<as_awaitable_t, _Awaitable, __t&> |
| { |
| return tag_invoke(as_awaitable, (_Awaitable&&)__awaitable, *this); |
| } |
| |
| // Pass through the get_env receiver query |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| -> env_of_t<_Receiver> |
| { |
| return get_env(__self.__rcvr_); |
| } |
| |
| _Receiver& __rcvr_; |
| }; |
| }; |
| |
| template <receiver _Receiver> |
| using __promise_t = __t<__promise<__id<_Receiver>>>; |
| |
| template <receiver _Receiver> |
| using __operation_t = __t<__operation<__id<_Receiver>>>; |
| |
| struct __connect_awaitable_t |
| { |
| private: |
| template <class _Fun, class... _Ts> |
| static auto __co_call(_Fun __fun, _Ts&&... __as) noexcept |
| { |
| auto __fn = [&, __fun]() noexcept { __fun((_Ts&&)__as...); }; |
| |
| struct __awaiter |
| { |
| decltype(__fn) __fn_; |
| |
| static constexpr bool await_ready() noexcept |
| { |
| return false; |
| } |
| |
| void await_suspend(__coro::coroutine_handle<>) noexcept |
| { |
| __fn_(); |
| } |
| |
| [[noreturn]] void await_resume() noexcept |
| { |
| std::terminate(); |
| } |
| }; |
| |
| return __awaiter{__fn}; |
| } |
| |
| template <class _Awaitable, class _Receiver> |
| #if STDEXEC_GCC() && (__GNUC__ > 11) |
| __attribute__((__used__)) |
| #endif |
| static __operation_t<_Receiver> |
| __co_impl(_Awaitable __awaitable, _Receiver __rcvr) |
| { |
| using __result_t = __await_result_t<_Awaitable, __promise_t<_Receiver>>; |
| std::exception_ptr __eptr; |
| try |
| { |
| if constexpr (same_as<__result_t, void>) |
| co_await (co_await (_Awaitable&&) __awaitable, |
| __co_call(set_value, (_Receiver&&)__rcvr)); |
| else |
| co_await __co_call(set_value, (_Receiver&&)__rcvr, |
| co_await (_Awaitable&&) __awaitable); |
| } |
| catch (...) |
| { |
| __eptr = std::current_exception(); |
| } |
| co_await __co_call(set_error, (_Receiver&&)__rcvr, |
| (std::exception_ptr&&)__eptr); |
| } |
| |
| template <receiver _Receiver, class _Awaitable> |
| using __completions_t = // |
| completion_signatures< |
| __minvoke< // set_value_t() or set_value_t(T) |
| __remove<void, __qf<set_value_t>>, |
| __await_result_t<_Awaitable, __promise_t<_Receiver>>>, |
| set_error_t(std::exception_ptr), set_stopped_t()>; |
| |
| public: |
| template <class _Receiver, __awaitable<__promise_t<_Receiver>> _Awaitable> |
| requires receiver_of<_Receiver, __completions_t<_Receiver, _Awaitable>> |
| __operation_t<_Receiver> operator()(_Awaitable&& __awaitable, |
| _Receiver __rcvr) const |
| { |
| return __co_impl((_Awaitable&&)__awaitable, (_Receiver&&)__rcvr); |
| } |
| }; |
| } // namespace __connect_awaitable_ |
| |
| using __connect_awaitable_::__connect_awaitable_t; |
| #else |
| struct __connect_awaitable_t |
| {}; |
| #endif |
| inline constexpr __connect_awaitable_t __connect_awaitable{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.connect] |
| namespace __connect |
| { |
| struct connect_t; |
| |
| template <class _Sender, class _Receiver> |
| using __tfx_sender = // |
| transform_sender_result_t<__late_domain_of_t<_Sender, env_of_t<_Receiver&>>, |
| _Sender, env_of_t<_Receiver&>>; |
| |
| template <class _Sender, class _Receiver> |
| concept __connectable_with_tag_invoke_ = // |
| receiver<_Receiver> && // |
| sender_in<_Sender, env_of_t<_Receiver>> && // |
| __receiver_from<_Receiver, _Sender> && // |
| tag_invocable<connect_t, _Sender, _Receiver>; |
| |
| template <class _Sender, class _Receiver> |
| concept __connectable_with_tag_invoke = // |
| __connectable_with_tag_invoke_<__tfx_sender<_Sender, _Receiver>, _Receiver>; |
| |
| template <class _Sender, class _Receiver> |
| concept __connectable_with_co_await = // |
| __callable<__connect_awaitable_t, __tfx_sender<_Sender, _Receiver>, |
| _Receiver>; |
| |
| struct connect_t |
| { |
| template <class _Sender, class _Env> |
| static constexpr bool __check_signatures() |
| { |
| if constexpr (sender_in<_Sender, _Env>) |
| { |
| // Instantiate __debug_sender via completion_signatures_of_t |
| // to check that the actual completions match the expected |
| // completions. |
| // |
| // Instantiate completion_signatures_of_t only if sender_in |
| // is true to workaround Clang not implementing CWG#2369 yet |
| // (connect() does have a constraint for _Sender satisfying |
| // sender_in). |
| using __checked_signatures |
| [[maybe_unused]] = completion_signatures_of_t<_Sender, _Env>; |
| } |
| return true; |
| } |
| |
| template <class _Sender, class _Receiver> |
| static constexpr auto __select_impl() noexcept |
| { |
| #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() |
| static_assert(__check_signatures<_Sender, env_of_t<_Receiver>>()); |
| #endif |
| |
| using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver&>>; |
| constexpr bool _NothrowTfxSender = |
| __nothrow_callable<get_env_t, _Receiver&> && |
| __nothrow_callable<transform_sender_t, _Domain, _Sender, |
| env_of_t<_Receiver&>>; |
| using _TfxSender = __tfx_sender<_Sender, _Receiver&>; |
| |
| if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>) |
| { |
| using _Result = |
| tag_invoke_result_t<connect_t, _TfxSender, _Receiver>; |
| constexpr bool _Nothrow = // |
| _NothrowTfxSender && |
| nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>; |
| return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); |
| } |
| else if constexpr (__connectable_with_co_await<_Sender, _Receiver>) |
| { |
| using _Result = |
| __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>; |
| return static_cast<_Result (*)()>(nullptr); |
| } |
| else |
| { |
| using _Result = __debug::__debug_operation; |
| return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>( |
| nullptr); |
| } |
| } |
| |
| template <class _Sender, class _Receiver> |
| using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>()); |
| |
| template <sender _Sender, receiver _Receiver> |
| requires __connectable_with_tag_invoke<_Sender, _Receiver> || |
| __connectable_with_co_await<_Sender, _Receiver> || |
| __is_debug_env<env_of_t<_Receiver>> |
| auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const |
| noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>) |
| -> __call_result_t<__select_impl_t<_Sender, _Receiver>> |
| { |
| using _TfxSender = __tfx_sender<_Sender, _Receiver&>; |
| auto&& __env = get_env(__rcvr); |
| auto __domain = __get_late_domain(__sndr, __env); |
| |
| if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>) |
| { |
| static_assert( |
| operation_state< |
| tag_invoke_result_t<connect_t, _TfxSender, _Receiver>>, |
| "stdexec::connect(sender, receiver) must return a type that " |
| "satisfies the operation_state concept"); |
| return tag_invoke( |
| connect_t{}, |
| transform_sender(__domain, (_Sender&&)__sndr, __env), |
| (_Receiver&&)__rcvr); |
| } |
| else if constexpr (__connectable_with_co_await<_Sender, _Receiver>) |
| { |
| return __connect_awaitable( // |
| transform_sender(__domain, (_Sender&&)__sndr, __env), |
| (_Receiver&&)__rcvr); |
| } |
| else |
| { |
| // This should generate an instantiation backtrace that contains |
| // useful debugging information. |
| using __tag_invoke::tag_invoke; |
| tag_invoke(*this, |
| transform_sender(__domain, (_Sender&&)__sndr, __env), |
| (_Receiver&&)__rcvr); |
| } |
| } |
| |
| friend constexpr bool tag_invoke(forwarding_query_t, connect_t) noexcept |
| { |
| return false; |
| } |
| }; |
| } // namespace __connect |
| |
| using __connect::connect_t; |
| inline constexpr __connect::connect_t connect{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [exec.snd] |
| template <class _Sender, class _Receiver> |
| concept sender_to = receiver<_Receiver> && // |
| sender_in<_Sender, env_of_t<_Receiver>> && // |
| __receiver_from<_Receiver, _Sender> && // |
| requires(_Sender&& __sndr, _Receiver&& __rcvr) { |
| connect((_Sender&&)__sndr, (_Receiver&&)__rcvr); |
| }; |
| |
| template <class _Tag, class... _Args> |
| _Tag __tag_of_sig_(_Tag (*)(_Args...)); |
| template <class _Sig> |
| using __tag_of_sig_t = decltype(stdexec::__tag_of_sig_((_Sig*)nullptr)); |
| |
| template <class _Sender, class _SetSig, class _Env = __default_env> |
| concept sender_of = |
| sender_in<_Sender, _Env> && |
| same_as<__types<_SetSig>, __gather_completions_for< |
| __tag_of_sig_t<_SetSig>, _Sender, _Env, |
| __qf<__tag_of_sig_t<_SetSig>>, __q<__types>>>; |
| |
| #if !STDEXEC_STD_NO_COROUTINES_ |
| ///////////////////////////////////////////////////////////////////////////// |
| // stdexec::as_awaitable [execution.coro_utils.as_awaitable] |
| namespace __as_awaitable |
| { |
| struct __void |
| {}; |
| template <class _Value> |
| using __value_or_void_t = __if<std::is_same<_Value, void>, __void, _Value>; |
| template <class _Value> |
| using __expected_t = |
| std::variant<std::monostate, __value_or_void_t<_Value>, std::exception_ptr>; |
| |
| template <class _Value> |
| struct __receiver_base |
| { |
| using is_receiver = void; |
| |
| template <same_as<set_value_t> _Tag, class... _Us> |
| requires constructible_from<__value_or_void_t<_Value>, _Us...> |
| friend void tag_invoke(_Tag, __receiver_base&& __self, |
| _Us&&... __us) noexcept |
| { |
| try |
| { |
| __self.__result_->template emplace<1>((_Us&&)__us...); |
| __self.__continuation_.resume(); |
| } |
| catch (...) |
| { |
| set_error((__receiver_base&&)__self, std::current_exception()); |
| } |
| } |
| |
| template <same_as<set_error_t> _Tag, class _Error> |
| friend void tag_invoke(_Tag, __receiver_base&& __self, |
| _Error&& __err) noexcept |
| { |
| if constexpr (__decays_to<_Error, std::exception_ptr>) |
| __self.__result_->template emplace<2>((_Error&&)__err); |
| else if constexpr (__decays_to<_Error, std::error_code>) |
| __self.__result_->template emplace<2>( |
| std::make_exception_ptr(std::system_error(__err))); |
| else |
| __self.__result_->template emplace<2>( |
| std::make_exception_ptr((_Error&&)__err)); |
| __self.__continuation_.resume(); |
| } |
| |
| __expected_t<_Value>* __result_; |
| __coro::coroutine_handle<> __continuation_; |
| }; |
| |
| template <class _PromiseId, class _Value> |
| struct __receiver |
| { |
| using _Promise = stdexec::__t<_PromiseId>; |
| |
| struct __t : __receiver_base<_Value> |
| { |
| using __id = __receiver; |
| |
| template <same_as<set_stopped_t> _Tag> |
| friend void tag_invoke(_Tag, __t&& __self) noexcept |
| { |
| auto __continuation = |
| __coro::coroutine_handle<_Promise>::from_address( |
| __self.__continuation_.address()); |
| __coro::coroutine_handle<> __stopped_continuation = |
| __continuation.promise().unhandled_stopped(); |
| __stopped_continuation.resume(); |
| } |
| |
| // Forward get_env query to the coroutine promise |
| friend env_of_t<_Promise&> tag_invoke(get_env_t, |
| const __t& __self) noexcept |
| { |
| auto __continuation = |
| __coro::coroutine_handle<_Promise>::from_address( |
| __self.__continuation_.address()); |
| return get_env(__continuation.promise()); |
| } |
| }; |
| }; |
| |
| template <class _Sender, class _Promise> |
| using __value_t = |
| __decay_t<__value_types_of_t<_Sender, env_of_t<_Promise&>, |
| __msingle_or<void>, __msingle_or<void>>>; |
| |
| template <class _Sender, class _Promise> |
| using __receiver_t = |
| __t<__receiver<__id<_Promise>, __value_t<_Sender, _Promise>>>; |
| |
| template <class _Value> |
| struct __sender_awaitable_base |
| { |
| bool await_ready() const noexcept |
| { |
| return false; |
| } |
| |
| _Value await_resume() |
| { |
| switch (__result_.index()) |
| { |
| case 0: // receiver contract not satisfied |
| STDEXEC_ASSERT(!"_Should never get here"); |
| break; |
| case 1: // set_value |
| if constexpr (!std::is_void_v<_Value>) |
| return (_Value&&)std::get<1>(__result_); |
| else |
| return; |
| case 2: // set_error |
| std::rethrow_exception(std::get<2>(__result_)); |
| } |
| std::terminate(); |
| } |
| |
| protected: |
| __expected_t<_Value> __result_; |
| }; |
| |
| template <class _PromiseId, class _SenderId> |
| struct __sender_awaitable |
| { |
| using _Promise = stdexec::__t<_PromiseId>; |
| using _Sender = stdexec::__t<_SenderId>; |
| using __value = __value_t<_Sender, _Promise>; |
| |
| struct __t : __sender_awaitable_base<__value> |
| { |
| __t(_Sender&& sndr, __coro::coroutine_handle<_Promise> __hcoro) // |
| noexcept(__nothrow_connectable<_Sender, __receiver>) : |
| __op_state_(connect((_Sender&&)sndr, |
| __receiver{{&this->__result_, __hcoro}})) |
| {} |
| |
| void await_suspend(__coro::coroutine_handle<_Promise>) noexcept |
| { |
| start(__op_state_); |
| } |
| |
| private: |
| using __receiver = __receiver_t<_Sender, _Promise>; |
| connect_result_t<_Sender, __receiver> __op_state_; |
| }; |
| }; |
| |
| template <class _Promise, class _Sender> |
| using __sender_awaitable_t = |
| __t<__sender_awaitable<__id<_Promise>, __id<_Sender>>>; |
| |
| template <class _Sender, class _Promise> |
| concept __awaitable_sender = |
| sender_in<_Sender, env_of_t<_Promise&>> && // |
| __mvalid<__value_t, _Sender, _Promise> && // |
| sender_to<_Sender, __receiver_t<_Sender, _Promise>> && // |
| requires(_Promise& __promise) { |
| { |
| __promise.unhandled_stopped() |
| } -> convertible_to<__coro::coroutine_handle<>>; |
| }; |
| |
| struct __unspecified |
| { |
| __unspecified get_return_object() noexcept; |
| __unspecified initial_suspend() noexcept; |
| __unspecified final_suspend() noexcept; |
| void unhandled_exception() noexcept; |
| void return_void() noexcept; |
| __coro::coroutine_handle<> unhandled_stopped() noexcept; |
| }; |
| |
| struct as_awaitable_t |
| { |
| template <class _Tp, class _Promise> |
| static constexpr auto __select_impl_() noexcept |
| { |
| if constexpr (tag_invocable<as_awaitable_t, _Tp, _Promise&>) |
| { |
| using _Result = tag_invoke_result_t<as_awaitable_t, _Tp, _Promise&>; |
| constexpr bool _Nothrow = |
| nothrow_tag_invocable<as_awaitable_t, _Tp, _Promise&>; |
| return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); |
| } |
| else if constexpr (__awaitable<_Tp, __unspecified>) |
| { // NOT __awaitable<_Tp, _Promise> !! |
| return static_cast < _Tp && (*)() noexcept > (nullptr); |
| } |
| else if constexpr (__awaitable_sender<_Tp, _Promise>) |
| { |
| using _Result = __sender_awaitable_t<_Promise, _Tp>; |
| constexpr bool _Nothrow = __nothrow_constructible_from< |
| _Result, _Tp, __coro::coroutine_handle<_Promise>>; |
| return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); |
| } |
| else |
| { |
| return static_cast < _Tp && (*)() noexcept > (nullptr); |
| } |
| } |
| template <class _Tp, class _Promise> |
| using __select_impl_t = decltype(__select_impl_<_Tp, _Promise>()); |
| |
| template <class _Tp, class _Promise> |
| auto operator()(_Tp&& __t, _Promise& __promise) const |
| noexcept(__nothrow_callable<__select_impl_t<_Tp, _Promise>>) |
| -> __call_result_t<__select_impl_t<_Tp, _Promise>> |
| { |
| if constexpr (tag_invocable<as_awaitable_t, _Tp, _Promise&>) |
| { |
| using _Result = tag_invoke_result_t<as_awaitable_t, _Tp, _Promise&>; |
| static_assert(__awaitable<_Result, _Promise>); |
| return tag_invoke(*this, (_Tp&&)__t, __promise); |
| } |
| else if constexpr (__awaitable<_Tp, __unspecified>) |
| { // NOT __awaitable<_Tp, _Promise> !! |
| return (_Tp&&)__t; |
| } |
| else if constexpr (__awaitable_sender<_Tp, _Promise>) |
| { |
| auto __hcoro = |
| __coro::coroutine_handle<_Promise>::from_promise(__promise); |
| return __sender_awaitable_t<_Promise, _Tp>{(_Tp&&)__t, __hcoro}; |
| } |
| else |
| { |
| return (_Tp&&)__t; |
| } |
| } |
| }; |
| } // namespace __as_awaitable |
| |
| using __as_awaitable::as_awaitable_t; |
| inline constexpr as_awaitable_t as_awaitable{}; |
| |
| namespace __with_awaitable_senders |
| { |
| |
| template <class _Promise = void> |
| class __continuation_handle; |
| |
| template <> |
| class __continuation_handle<void> |
| { |
| public: |
| __continuation_handle() = default; |
| |
| template <class _Promise> |
| __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept : |
| __coro_(__coro) |
| { |
| if constexpr (requires(_Promise& __promise) { |
| __promise.unhandled_stopped(); |
| }) |
| { |
| __stopped_callback_ = |
| [](void* __address) noexcept -> __coro::coroutine_handle<> { |
| // This causes the rest of the coroutine (the part after the |
| // co_await of the sender) to be skipped and invokes the calling |
| // coroutine's stopped handler. |
| return __coro::coroutine_handle<_Promise>::from_address( |
| __address) |
| .promise() |
| .unhandled_stopped(); |
| }; |
| } |
| // If _Promise doesn't implement unhandled_stopped(), then if a |
| // "stopped" unwind reaches this point, it's considered an unhandled |
| // exception and terminate() is called. |
| } |
| |
| __coro::coroutine_handle<> handle() const noexcept |
| { |
| return __coro_; |
| } |
| |
| __coro::coroutine_handle<> unhandled_stopped() const noexcept |
| { |
| return __stopped_callback_(__coro_.address()); |
| } |
| |
| private: |
| __coro::coroutine_handle<> __coro_{}; |
| using __stopped_callback_t = __coro::coroutine_handle<> (*)(void*) noexcept; |
| __stopped_callback_t __stopped_callback_ = |
| [](void*) noexcept -> __coro::coroutine_handle<> { std::terminate(); }; |
| }; |
| |
| template <class _Promise> |
| class __continuation_handle |
| { |
| public: |
| __continuation_handle() = default; |
| |
| __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept : |
| __continuation_{__coro} |
| {} |
| |
| __coro::coroutine_handle<_Promise> handle() const noexcept |
| { |
| return __coro::coroutine_handle<_Promise>::from_address( |
| __continuation_.handle().address()); |
| } |
| |
| __coro::coroutine_handle<> unhandled_stopped() const noexcept |
| { |
| return __continuation_.unhandled_stopped(); |
| } |
| |
| private: |
| __continuation_handle<> __continuation_{}; |
| }; |
| |
| struct __with_awaitable_senders_base |
| { |
| template <class _OtherPromise> |
| void set_continuation( |
| __coro::coroutine_handle<_OtherPromise> __hcoro) noexcept |
| { |
| static_assert(!std::is_void_v<_OtherPromise>); |
| __continuation_ = __hcoro; |
| } |
| |
| void set_continuation(__continuation_handle<> __continuation) noexcept |
| { |
| __continuation_ = __continuation; |
| } |
| |
| __continuation_handle<> continuation() const noexcept |
| { |
| return __continuation_; |
| } |
| |
| __coro::coroutine_handle<> unhandled_stopped() noexcept |
| { |
| return __continuation_.unhandled_stopped(); |
| } |
| |
| private: |
| __continuation_handle<> __continuation_{}; |
| }; |
| |
| template <class _Promise> |
| struct with_awaitable_senders : __with_awaitable_senders_base |
| { |
| template <class _Value> |
| auto await_transform(_Value&& __val) |
| -> __call_result_t<as_awaitable_t, _Value, _Promise&> |
| { |
| static_assert(derived_from<_Promise, with_awaitable_senders>); |
| return as_awaitable((_Value&&)__val, static_cast<_Promise&>(*this)); |
| } |
| }; |
| } // namespace __with_awaitable_senders |
| |
| using __with_awaitable_senders::__continuation_handle; |
| using __with_awaitable_senders::with_awaitable_senders; |
| #endif |
| |
| namespace |
| { |
| inline constexpr auto __ref = []<class _Ty>(_Ty& __ty) noexcept { |
| return [__ty = &__ty]() noexcept -> decltype(auto) { return (*__ty); }; |
| }; |
| } |
| |
| template <class _Ty> |
| using __ref_t = decltype(__ref(__declval<_Ty&>())); |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // NOT TO SPEC: __submit |
| namespace __submit_ |
| { |
| template <class _OpRef> |
| struct __receiver |
| { |
| using is_receiver = void; |
| using __t = __receiver; |
| using __id = __receiver; |
| |
| using _Operation = __decay_t<__call_result_t<_OpRef>>; |
| using _Receiver = stdexec::__t<__mapply<__q<__msecond>, _Operation>>; |
| |
| _OpRef __opref_; |
| |
| // Forward all the receiver ops, and delete the operation state. |
| template <__completion_tag _Tag, class... _As> |
| requires __callable<_Tag, _Receiver, _As...> |
| friend void tag_invoke(_Tag __tag, __receiver&& __self, |
| _As&&... __as) noexcept |
| { |
| __tag((_Receiver&&)__self.__opref_().__rcvr_, (_As&&)__as...); |
| __self.__delete_op(); |
| } |
| |
| void __delete_op() noexcept |
| { |
| _Operation* __op = &__opref_(); |
| if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>) |
| { |
| auto&& __env = get_env(__op->__rcvr_); |
| auto __alloc = get_allocator(__env); |
| using _Alloc = decltype(__alloc); |
| using _OpAlloc = typename std::allocator_traits< |
| _Alloc>::template rebind_alloc<_Operation>; |
| _OpAlloc __op_alloc{__alloc}; |
| std::allocator_traits<_OpAlloc>::destroy(__op_alloc, __op); |
| std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op, 1); |
| } |
| else |
| { |
| delete __op; |
| } |
| } |
| |
| // Forward all receiever queries. |
| friend auto tag_invoke(get_env_t, const __receiver& __self) noexcept |
| -> env_of_t<_Receiver&> |
| { |
| return get_env(__self.__opref_().__rcvr_); |
| } |
| }; |
| |
| template <class _SenderId, class _ReceiverId> |
| struct __operation |
| { |
| using _Sender = stdexec::__t<_SenderId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using __receiver_t = __receiver<__ref_t<__operation>>; |
| |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_; |
| connect_result_t<_Sender, __receiver_t> __op_state_; |
| |
| __operation(_Sender&& __sndr, _Receiver __rcvr) : |
| __rcvr_((_Receiver&&)__rcvr), |
| __op_state_(connect((_Sender&&)__sndr, __receiver_t{__ref(*this)})) |
| {} |
| }; |
| |
| struct __submit_t |
| { |
| template <receiver _Receiver, sender_to<_Receiver> _Sender> |
| void operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept(false) |
| { |
| if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>) |
| { |
| auto&& __env = get_env(__rcvr); |
| auto __alloc = get_allocator(__env); |
| using _Alloc = decltype(__alloc); |
| using _Op = __operation<__id<_Sender>, __id<_Receiver>>; |
| using _OpAlloc = typename std::allocator_traits< |
| _Alloc>::template rebind_alloc<_Op>; |
| _OpAlloc __op_alloc{__alloc}; |
| auto __op = std::allocator_traits<_OpAlloc>::allocate(__op_alloc, |
| 1); |
| try |
| { |
| std::allocator_traits<_OpAlloc>::construct( |
| __op_alloc, __op, (_Sender&&)__sndr, (_Receiver&&)__rcvr); |
| start(__op->__op_state_); |
| } |
| catch (...) |
| { |
| std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op, |
| 1); |
| throw; |
| } |
| } |
| else |
| { |
| start((new __operation<__id<_Sender>, __id<_Receiver>>{ |
| (_Sender&&)__sndr, (_Receiver&&)__rcvr}) |
| ->__op_state_); |
| } |
| } |
| }; |
| } // namespace __submit_ |
| |
| using __submit_::__submit_t; |
| inline constexpr __submit_t __submit{}; |
| |
| namespace __inln |
| { |
| struct __scheduler; |
| |
| template <class _Receiver> |
| struct __op : __immovable |
| { |
| _Receiver __recv_; |
| |
| friend void tag_invoke(start_t, __op& __self) noexcept |
| { |
| set_value((_Receiver&&)__self.__recv_); |
| } |
| }; |
| |
| struct __schedule_t |
| { |
| static auto get_env(__ignore) noexcept |
| -> __env::__prop<__scheduler(get_completion_scheduler_t<set_value_t>)>; |
| |
| using __compl_sigs = stdexec::completion_signatures<set_value_t()>; |
| static __compl_sigs get_completion_signatures(__ignore, __ignore); |
| |
| template <receiver_of<__compl_sigs> _Receiver> |
| static auto |
| connect(__ignore, |
| _Receiver __rcvr) noexcept(__nothrow_decay_copyable<_Receiver>) |
| { |
| return __op<_Receiver>{{}, (_Receiver&&)__rcvr}; |
| } |
| }; |
| |
| struct __scheduler |
| { |
| using __t = __scheduler; |
| using __id = __scheduler; |
| |
| STDEXEC_ATTRIBUTE((host, device)) |
| friend auto tag_invoke(schedule_t, __scheduler) |
| { |
| return __make_sexpr<__schedule_t>(); |
| } |
| |
| friend forward_progress_guarantee |
| tag_invoke(get_forward_progress_guarantee_t, __scheduler) noexcept |
| { |
| return forward_progress_guarantee::weakly_parallel; |
| } |
| |
| bool operator==(const __scheduler&) const noexcept = default; |
| }; |
| |
| inline auto __schedule_t::get_env(__ignore) noexcept |
| -> __env::__prop<__scheduler(get_completion_scheduler_t<set_value_t>)> |
| { |
| return __mkprop(__scheduler{}, get_completion_scheduler<set_value_t>); |
| } |
| } // namespace __inln |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.consumer.start_detached] |
| namespace __start_detached |
| { |
| template <class _EnvId> |
| struct __detached_receiver |
| { |
| using _Env = stdexec::__t<_EnvId>; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __detached_receiver; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_; |
| |
| template <same_as<set_value_t> _Tag, class... _As> |
| friend void tag_invoke(_Tag, __t&&, _As&&...) noexcept |
| {} |
| |
| template <same_as<set_error_t> _Tag, class _Error> |
| [[noreturn]] friend void tag_invoke(_Tag, __t&&, _Error&&) noexcept |
| { |
| std::terminate(); |
| } |
| |
| template <same_as<set_stopped_t> _Tag> |
| friend void tag_invoke(_Tag, __t&&) noexcept |
| {} |
| |
| friend const _Env& tag_invoke(get_env_t, const __t& __self) noexcept |
| { |
| // BUGBUG NOT TO SPEC |
| return __self.__env_; |
| } |
| }; |
| }; |
| template <class _Env = empty_env> |
| using __detached_receiver_t = __t<__detached_receiver<__id<__decay_t<_Env>>>>; |
| |
| struct start_detached_t |
| { |
| template <sender_in<empty_env> _Sender> |
| requires __callable<apply_sender_t, __early_domain_of_t<_Sender>, |
| start_detached_t, _Sender> |
| void operator()(_Sender&& __sndr) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| stdexec::apply_sender(__domain, *this, (_Sender&&)__sndr); |
| } |
| |
| template <class _Env, sender_in<_Env> _Sender> |
| requires __callable<apply_sender_t, __late_domain_of_t<_Sender, _Env>, |
| start_detached_t, _Sender, _Env> |
| void operator()(_Sender&& __sndr, _Env&& __env) const |
| { |
| auto __domain = __get_late_domain(__sndr, __env); |
| stdexec::apply_sender(__domain, *this, (_Sender&&)__sndr, |
| (_Env&&)__env); |
| } |
| |
| using _Sender = __0; |
| using __legacy_customizations_t = |
| __types<tag_invoke_t(start_detached_t, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(const _Sender&)), |
| _Sender), |
| tag_invoke_t(start_detached_t, _Sender)>; |
| |
| template <class _Sender, class _Env = empty_env> |
| requires sender_to<_Sender, __detached_receiver_t<_Env>> |
| void apply_sender(_Sender&& __sndr, _Env&& __env = {}) const |
| { |
| __submit((_Sender&&)__sndr, __detached_receiver_t<_Env>{(_Env&&)__env}); |
| } |
| }; |
| } // namespace __start_detached |
| |
| using __start_detached::start_detached_t; |
| inline constexpr start_detached_t start_detached{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.factories] |
| namespace __just |
| { |
| template <class _ReceiverId, class _Tag, class _Tuple> |
| struct __operation |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t : __immovable |
| { |
| using __id = __operation; |
| _Tuple __vals_; |
| _Receiver __rcvr_; |
| |
| friend void tag_invoke(start_t, __t& __op_state) noexcept |
| { |
| using __tag_t = _Tag; |
| using __receiver_t = _Receiver; |
| __apply( |
| [&__op_state]<class... _Ts>(_Ts&... __ts) { |
| __tag_t()((__receiver_t&&)__op_state.__rcvr_, (_Ts&&)__ts...); |
| }, |
| __op_state.__vals_); |
| } |
| }; |
| }; |
| |
| template <class _SetTag, class _Receiver> |
| struct __connect_fn |
| { |
| _Receiver& __rcvr_; |
| |
| template <class _Tuple> |
| auto operator()(__ignore, _Tuple&& __tup) const |
| noexcept(__nothrow_decay_copyable<_Tuple>) |
| -> __t<__operation<__id<_Receiver>, _SetTag, __decay_t<_Tuple>>> |
| { |
| return {{}, (_Tuple&&)__tup, (_Receiver&&)__rcvr_}; |
| } |
| }; |
| |
| template <class _JustTag, class _SetTag> |
| struct __just_impl |
| { |
| template <class _Sender> |
| using __compl_sigs = // |
| completion_signatures< // |
| __mapply< // |
| __qf<_SetTag>, __decay_t<__data_of<_Sender>>>>; |
| |
| template <sender_expr_for<_JustTag> _Sender> |
| static __compl_sigs<_Sender> get_completion_signatures(_Sender&&, __ignore) |
| { |
| return {}; |
| } |
| |
| template <sender_expr_for<_JustTag> _Sender, |
| receiver_of<__compl_sigs<_Sender>> _Receiver> |
| static auto connect(_Sender&& __sndr, _Receiver __rcvr) noexcept( |
| __nothrow_callable<__sexpr_apply_t, _Sender, |
| __connect_fn<_SetTag, _Receiver>>) |
| -> __call_result_t<__sexpr_apply_t, _Sender, |
| __connect_fn<_SetTag, _Receiver>> |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| __connect_fn<_SetTag, _Receiver>{__rcvr}); |
| } |
| |
| static empty_env get_env(__ignore) noexcept |
| { |
| return {}; |
| } |
| }; |
| |
| struct just_t : __just_impl<just_t, set_value_t> |
| { |
| template <__movable_value... _Ts> |
| STDEXEC_ATTRIBUTE((host, device)) |
| auto operator()(_Ts&&... __ts) const |
| noexcept((__nothrow_decay_copyable<_Ts> && ...)) |
| { |
| return __make_sexpr<just_t>(__decayed_tuple<_Ts...>{(_Ts&&)__ts...}); |
| } |
| }; |
| |
| struct just_error_t : __just_impl<just_error_t, set_error_t> |
| { |
| template <__movable_value _Error> |
| STDEXEC_ATTRIBUTE((host, device)) |
| auto operator()(_Error&& __err) const |
| noexcept(__nothrow_decay_copyable<_Error>) |
| { |
| return __make_sexpr<just_error_t>( |
| __decayed_tuple<_Error>{(_Error&&)__err}); |
| } |
| }; |
| |
| struct just_stopped_t : __just_impl<just_stopped_t, set_stopped_t> |
| { |
| STDEXEC_ATTRIBUTE((host, device)) auto operator()() const noexcept |
| { |
| return __make_sexpr<just_stopped_t>(__decayed_tuple<>()); |
| } |
| }; |
| } // namespace __just |
| |
| using __just::just_error_t; |
| using __just::just_stopped_t; |
| using __just::just_t; |
| |
| inline constexpr just_t just{}; |
| inline constexpr just_error_t just_error{}; |
| inline constexpr just_stopped_t just_stopped{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.execute] |
| namespace __execute_ |
| { |
| template <class _Fun> |
| struct __as_receiver |
| { |
| using is_receiver = void; |
| _Fun __fun_; |
| |
| template <same_as<set_value_t> _Tag> |
| friend void tag_invoke(_Tag, __as_receiver&& __rcvr) noexcept |
| { |
| try |
| { |
| __rcvr.__fun_(); |
| } |
| catch (...) |
| { |
| set_error((__as_receiver&&)__rcvr, std::exception_ptr()); |
| } |
| } |
| |
| template <same_as<set_error_t> _Tag> |
| [[noreturn]] friend void tag_invoke(_Tag, __as_receiver&&, |
| std::exception_ptr) noexcept |
| { |
| std::terminate(); |
| } |
| |
| template <same_as<set_stopped_t> _Tag> |
| friend void tag_invoke(_Tag, __as_receiver&&) noexcept |
| {} |
| |
| friend empty_env tag_invoke(get_env_t, const __as_receiver&) noexcept |
| { |
| return {}; |
| } |
| }; |
| |
| struct execute_t |
| { |
| template <scheduler _Scheduler, class _Fun> |
| requires __callable<_Fun&> && move_constructible<_Fun> |
| void operator()(_Scheduler&& __sched, _Fun __fun) const noexcept(false) |
| { |
| // Look for a legacy customization |
| if constexpr (tag_invocable<execute_t, _Scheduler, _Fun>) |
| { |
| tag_invoke(execute_t{}, (_Scheduler&&)__sched, (_Fun&&)__fun); |
| } |
| else |
| { |
| auto __domain = query_or(get_domain, __sched, default_domain()); |
| stdexec::apply_sender(__domain, *this, |
| schedule((_Scheduler&&)__sched), |
| (_Fun&&)__fun); |
| } |
| } |
| |
| template <sender_of<set_value_t()> _Sender, class _Fun> |
| requires __callable<_Fun&> && move_constructible<_Fun> |
| void apply_sender(_Sender&& __sndr, _Fun __fun) const noexcept(false) |
| { |
| __submit((_Sender&&)__sndr, __as_receiver<_Fun>{(_Fun&&)__fun}); |
| } |
| }; |
| } // namespace __execute_ |
| |
| using __execute_::execute_t; |
| inline constexpr execute_t execute{}; |
| |
| // NOT TO SPEC: |
| namespace __closure |
| { |
| template <__class _Dp> |
| struct sender_adaptor_closure; |
| } |
| |
| using __closure::sender_adaptor_closure; |
| |
| template <class _Tp> |
| concept __sender_adaptor_closure = |
| derived_from<__decay_t<_Tp>, sender_adaptor_closure<__decay_t<_Tp>>> && |
| move_constructible<__decay_t<_Tp>> && |
| constructible_from<__decay_t<_Tp>, _Tp>; |
| |
| template <class _Tp, class _Sender> |
| concept __sender_adaptor_closure_for = |
| __sender_adaptor_closure<_Tp> && sender<__decay_t<_Sender>> && |
| __callable<_Tp, __decay_t<_Sender>> && |
| sender<__call_result_t<_Tp, __decay_t<_Sender>>>; |
| |
| namespace __closure |
| { |
| template <class _T0, class _T1> |
| struct __compose : sender_adaptor_closure<__compose<_T0, _T1>> |
| { |
| STDEXEC_ATTRIBUTE((no_unique_address)) _T0 __t0_; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _T1 __t1_; |
| |
| template <sender _Sender> |
| requires __callable<_T0, _Sender> && |
| __callable<_T1, __call_result_t<_T0, _Sender>> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __call_result_t<_T1, __call_result_t<_T0, _Sender>> |
| operator()(_Sender&& __sndr) && |
| { |
| return ((_T1&&)__t1_)(((_T0&&)__t0_)((_Sender&&)__sndr)); |
| } |
| |
| template <sender _Sender> |
| requires __callable<const _T0&, _Sender> && |
| __callable<const _T1&, __call_result_t<const _T0&, _Sender>> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __call_result_t<_T1, __call_result_t<_T0, _Sender>> |
| operator()(_Sender&& __sndr) const& |
| { |
| return __t1_(__t0_((_Sender&&)__sndr)); |
| } |
| }; |
| |
| template <__class _Dp> |
| struct sender_adaptor_closure |
| {}; |
| |
| template <sender _Sender, __sender_adaptor_closure_for<_Sender> _Closure> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __call_result_t<_Closure, _Sender> operator|(_Sender&& __sndr, |
| _Closure&& __clsur) |
| { |
| return ((_Closure&&)__clsur)((_Sender&&)__sndr); |
| } |
| |
| template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __compose<__decay_t<_T0>, __decay_t<_T1>> operator|(_T0&& __t0, _T1&& __t1) |
| { |
| return {{}, (_T0&&)__t0, (_T1&&)__t1}; |
| } |
| |
| template <class _Fun, class... _As> |
| struct __binder_back : sender_adaptor_closure<__binder_back<_Fun, _As...>> |
| { |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_; |
| std::tuple<_As...> __as_; |
| |
| template <sender _Sender> |
| requires __callable<_Fun, _Sender, _As...> |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| __call_result_t<_Fun, _Sender, _As...> |
| operator()(_Sender&& __sndr) && noexcept( |
| __nothrow_callable<_Fun, _Sender, _As...>) |
| { |
| return __apply( |
| [&__sndr, |
| this](_As&... __as) -> __call_result_t<_Fun, _Sender, _As...> { |
| return ((_Fun&&)__fun_)((_Sender&&)__sndr, (_As&&)__as...); |
| }, |
| __as_); |
| } |
| |
| template <sender _Sender> |
| requires __callable<const _Fun&, _Sender, const _As&...> |
| STDEXEC_ATTRIBUTE((host, device)) |
| __call_result_t<const _Fun&, _Sender, const _As&...> |
| operator()(_Sender&& __sndr) const& // |
| noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>) |
| { |
| return __apply( |
| [&__sndr, this](const _As&... __as) |
| -> __call_result_t<const _Fun&, _Sender, const _As&...> { |
| return __fun_((_Sender&&)__sndr, __as...); |
| }, |
| __as_); |
| } |
| }; |
| } // namespace __closure |
| |
| using __closure::__binder_back; |
| |
| namespace __adaptors |
| { |
| // A derived-to-base cast that works even when the base is not |
| // accessible from derived. |
| template <class _Tp, class _Up> |
| STDEXEC_ATTRIBUTE((host, device)) |
| __copy_cvref_t<_Up&&, _Tp> __c_cast(_Up&& u) noexcept |
| requires __decays_to<_Tp, _Tp> |
| { |
| static_assert(std::is_reference_v<__copy_cvref_t<_Up&&, _Tp>>); |
| static_assert(STDEXEC_IS_BASE_OF(_Tp, __decay_t<_Up>)); |
| return (__copy_cvref_t<_Up&&, _Tp>)(_Up&&)u; |
| } |
| |
| namespace __no |
| { |
| struct __nope |
| {}; |
| |
| struct __receiver : __nope |
| { |
| using is_receiver = void; |
| }; |
| |
| template <same_as<set_error_t> _Tag> |
| void tag_invoke(_Tag, __receiver, std::exception_ptr) noexcept; |
| template <same_as<set_stopped_t> _Tag> |
| void tag_invoke(_Tag, __receiver) noexcept; |
| empty_env tag_invoke(get_env_t, __receiver) noexcept; |
| } // namespace __no |
| |
| using __not_a_receiver = __no::__receiver; |
| |
| template <class _Base> |
| struct __adaptor |
| { |
| struct __t |
| { |
| template <class _T1> |
| requires constructible_from<_Base, _T1> |
| explicit __t(_T1&& __base) : __base_((_T1&&)__base) |
| {} |
| |
| private: |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Base __base_; |
| |
| protected: |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| _Base& base() & noexcept |
| { |
| return __base_; |
| } |
| |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| const _Base& base() const& noexcept |
| { |
| return __base_; |
| } |
| |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| _Base&& base() && noexcept |
| { |
| return (_Base&&)__base_; |
| } |
| }; |
| }; |
| |
| template <derived_from<__no::__nope> _Base> |
| struct __adaptor<_Base> |
| { |
| struct __t : __no::__nope |
| {}; |
| }; |
| template <class _Base> |
| using __adaptor_base = typename __adaptor<_Base>::__t; |
| |
| // BUGBUG Not to spec: on gcc and nvc++, member functions in derived classes |
| // don't shadow type aliases of the same name in base classes. :-O |
| // On mingw gcc, 'bool(type::existing_member_function)' evaluates to true, |
| // but 'int(type::existing_member_function)' is an error (as desired). |
| #define _DISPATCH_MEMBER(_TAG) \ |
| template <class _Self, class... _Ts> \ |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) \ |
| static auto __call_##_TAG(_Self&& __self, _Ts&&... __ts) noexcept \ |
| -> decltype(((_Self&&)__self)._TAG((_Ts&&)__ts...)) \ |
| { \ |
| static_assert(noexcept(((_Self&&)__self)._TAG((_Ts&&)__ts...))); \ |
| return ((_Self&&)__self)._TAG((_Ts&&)__ts...); \ |
| } /**/ |
| #define _CALL_MEMBER(_TAG, ...) __call_##_TAG(__VA_ARGS__) |
| |
| #if STDEXEC_CLANG() |
| // Only clang gets this right. |
| #define _MISSING_MEMBER(_Dp, _TAG) requires { typename _Dp::_TAG; } |
| #define _DEFINE_MEMBER(_TAG) _DISPATCH_MEMBER(_TAG) using _TAG = void |
| #else |
| #define _MISSING_MEMBER(_Dp, _TAG) (__missing_##_TAG<_Dp>()) |
| #define _DEFINE_MEMBER(_TAG) \ |
| template <class _Dp> \ |
| static constexpr bool __missing_##_TAG() noexcept \ |
| { \ |
| return requires { requires bool(int(_Dp::_TAG)); }; \ |
| } \ |
| _DISPATCH_MEMBER(_TAG) \ |
| static constexpr int _TAG = 1 /**/ |
| #endif |
| |
| template <__class _Derived, class _Base> |
| struct receiver_adaptor |
| { |
| class __t : __adaptor_base<_Base>, __receiver_base |
| { |
| friend _Derived; |
| _DEFINE_MEMBER(set_value); |
| _DEFINE_MEMBER(set_error); |
| _DEFINE_MEMBER(set_stopped); |
| _DEFINE_MEMBER(get_env); |
| |
| static constexpr bool __has_base = !derived_from<_Base, __no::__nope>; |
| |
| template <class _Dp> |
| using __base_from_derived_t = decltype(__declval<_Dp>().base()); |
| |
| using __get_base_t = |
| __if_c<__has_base, __mbind_back_q<__copy_cvref_t, _Base>, |
| __q<__base_from_derived_t>>; |
| |
| template <class _Dp> |
| using __base_t = __minvoke<__get_base_t, _Dp&&>; |
| |
| template <class _Dp> |
| STDEXEC_ATTRIBUTE((host, device)) |
| static __base_t<_Dp> __get_base(_Dp&& __self) noexcept |
| { |
| if constexpr (__has_base) |
| { |
| return __c_cast<__t>((_Dp&&)__self).base(); |
| } |
| else |
| { |
| return ((_Dp&&)__self).base(); |
| } |
| } |
| |
| template <same_as<set_value_t> _SetValue, class... _As> |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) |
| friend auto tag_invoke(_SetValue, _Derived&& __self, |
| _As&&... __as) noexcept // |
| -> __msecond< // |
| __if_c<same_as<set_value_t, _SetValue>>, |
| decltype(_CALL_MEMBER(set_value, (_Derived&&)__self, |
| (_As&&)__as...))> |
| { |
| static_assert(noexcept( |
| _CALL_MEMBER(set_value, (_Derived&&)__self, (_As&&)__as...))); |
| _CALL_MEMBER(set_value, (_Derived&&)__self, (_As&&)__as...); |
| } |
| |
| template <same_as<set_value_t> _SetValue, class _Dp = _Derived, |
| class... _As> |
| requires _MISSING_MEMBER |
| (_Dp, set_value) && |
| tag_invocable<_SetValue, __base_t<_Dp>, _As...> STDEXEC_ATTRIBUTE( |
| (host, device, always_inline)) // |
| friend void tag_invoke(_SetValue, _Derived&& __self, |
| _As&&... __as) noexcept |
| { |
| stdexec::set_value(__get_base((_Dp&&)__self), (_As&&)__as...); |
| } |
| |
| template <same_as<set_error_t> _SetError, class _Error> |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| friend auto tag_invoke(_SetError, _Derived&& __self, |
| _Error&& __err) noexcept // |
| -> __msecond< // |
| __if_c<same_as<set_error_t, _SetError>>, |
| decltype(_CALL_MEMBER(set_error, (_Derived&&)__self, |
| (_Error&&)__err))> |
| { |
| static_assert(noexcept( |
| _CALL_MEMBER(set_error, (_Derived&&)__self, (_Error&&)__err))); |
| _CALL_MEMBER(set_error, (_Derived&&)__self, (_Error&&)__err); |
| } |
| |
| template <same_as<set_error_t> _SetError, class _Error, |
| class _Dp = _Derived> |
| requires _MISSING_MEMBER |
| (_Dp, set_error) && |
| tag_invocable<_SetError, __base_t<_Dp>, _Error> STDEXEC_ATTRIBUTE( |
| (host, device, always_inline)) // |
| friend void tag_invoke(_SetError, _Derived&& __self, |
| _Error&& __err) noexcept |
| { |
| stdexec::set_error(__get_base((_Derived&&)__self), (_Error&&)__err); |
| } |
| |
| template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived> |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| friend auto tag_invoke(_SetStopped, _Derived&& __self) noexcept // |
| -> __msecond< // |
| __if_c<same_as<set_stopped_t, _SetStopped>>, |
| decltype(_CALL_MEMBER(set_stopped, (_Dp&&)__self))> |
| { |
| static_assert( |
| noexcept(_CALL_MEMBER(set_stopped, (_Derived&&)__self))); |
| _CALL_MEMBER(set_stopped, (_Derived&&)__self); |
| } |
| |
| template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived> |
| requires _MISSING_MEMBER |
| (_Dp, set_stopped) && |
| tag_invocable<_SetStopped, __base_t<_Dp>> STDEXEC_ATTRIBUTE( |
| (host, device, always_inline)) // |
| friend void tag_invoke(_SetStopped, _Derived&& __self) noexcept |
| { |
| stdexec::set_stopped(__get_base((_Derived&&)__self)); |
| } |
| |
| // Pass through the get_env receiver query |
| template <same_as<get_env_t> _GetEnv, class _Dp = _Derived> |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| friend auto tag_invoke(_GetEnv, const _Derived& __self) noexcept |
| -> decltype(_CALL_MEMBER(get_env, (const _Dp&)__self)) |
| { |
| static_assert(noexcept(_CALL_MEMBER(get_env, __self))); |
| return _CALL_MEMBER(get_env, __self); |
| } |
| |
| template <same_as<get_env_t> _GetEnv, class _Dp = _Derived> |
| requires _MISSING_MEMBER |
| (_Dp, get_env) STDEXEC_ATTRIBUTE((host, device, always_inline)) // |
| friend auto tag_invoke(_GetEnv, const _Derived& __self) noexcept |
| -> env_of_t<__base_t<const _Dp&>> |
| { |
| return stdexec::get_env(__get_base(__self)); |
| } |
| |
| public: |
| __t() = default; |
| using __adaptor_base<_Base>::__adaptor_base; |
| |
| using is_receiver = void; |
| }; |
| }; |
| } // namespace __adaptors |
| |
| template <__class _Derived, receiver _Base = __adaptors::__not_a_receiver> |
| using receiver_adaptor = |
| typename __adaptors::receiver_adaptor<_Derived, _Base>::__t; |
| |
| template <class _Receiver, class _Fun, class... _As> |
| concept __receiver_of_invoke_result = // |
| receiver_of<_Receiver, completion_signatures< |
| __minvoke<__remove<void, __qf<set_value_t>>, |
| __invoke_result_t<_Fun, _As...>>>>; |
| |
| template <bool _CanThrow = false, class _Receiver, class _Fun, class... _As> |
| void __set_value_invoke(_Receiver&& __rcvr, _Fun&& __fun, |
| _As&&... __as) noexcept(!_CanThrow) |
| { |
| if constexpr (_CanThrow || __nothrow_invocable<_Fun, _As...>) |
| { |
| if constexpr (same_as<void, __invoke_result_t<_Fun, _As...>>) |
| { |
| __invoke((_Fun&&)__fun, (_As&&)__as...); |
| set_value((_Receiver&&)__rcvr); |
| } |
| else |
| { |
| set_value((_Receiver&&)__rcvr, |
| __invoke((_Fun&&)__fun, (_As&&)__as...)); |
| } |
| } |
| else |
| { |
| try |
| { |
| stdexec::__set_value_invoke<true>((_Receiver&&)__rcvr, |
| (_Fun&&)__fun, (_As&&)__as...); |
| } |
| catch (...) |
| { |
| set_error((_Receiver&&)__rcvr, std::current_exception()); |
| } |
| } |
| } |
| |
| template <class _Fun> |
| struct _WITH_FUNCTION_ |
| {}; |
| |
| template <class... _Args> |
| struct _WITH_ARGUMENTS_ |
| {}; |
| |
| inline constexpr __mstring __not_callable_diag = |
| "The specified function is not callable with the arguments provided."__csz; |
| |
| template <__mstring _Context, __mstring _Diagnostic = __not_callable_diag> |
| struct _NOT_CALLABLE_ |
| {}; |
| |
| template <__mstring _Context> |
| struct __callable_error |
| { |
| template <class _Fun, class... _Args> |
| using __f = // |
| __mexception< // |
| _NOT_CALLABLE_<_Context>, _WITH_FUNCTION_<_Fun>, |
| _WITH_ARGUMENTS_<_Args...>>; |
| }; |
| |
| template <class _Fun, class... _Args> |
| requires __invocable<_Fun, _Args...> |
| using __non_throwing_ = __mbool<__nothrow_invocable<_Fun, _Args...>>; |
| |
| template <class _Tag, class _Fun, class _Sender, class _Env, class _Catch> |
| using __with_error_invoke_t = // |
| __if<__gather_completions_for< |
| _Tag, _Sender, _Env, |
| __mbind_front<__mtry_catch_q<__non_throwing_, _Catch>, _Fun>, |
| __q<__mand>>, |
| completion_signatures<>, __with_exception_ptr>; |
| |
| template <class _Fun, class... _Args> |
| requires __invocable<_Fun, _Args...> |
| using __set_value_invoke_t = // |
| completion_signatures<__minvoke<__remove<void, __qf<set_value_t>>, |
| __invoke_result_t<_Fun, _Args...>>>; |
| |
| struct __default_get_env_fn |
| { |
| // BUGBUG This should hide all but the forwarding queries |
| template <class _Sender> |
| env_of_t<_Sender> operator()(__ignore, __ignore, |
| const _Sender& __child) const noexcept |
| { |
| return get_env(__child); |
| } |
| |
| template <class... _Senders> |
| requires(sizeof...(_Senders) != 1) |
| empty_env operator()(__ignore, __ignore, const _Senders&...) const noexcept |
| { |
| return {}; |
| } |
| }; |
| |
| template <class _Tag> |
| struct __with_default_get_env |
| { |
| template <sender_expr_for<_Tag> _Sender> |
| static auto get_env(const _Sender& __sndr) noexcept |
| -> __call_result_t<__sexpr_apply_t, const _Sender&, |
| __default_get_env_fn> |
| { |
| return __sexpr_apply(__sndr, __default_get_env_fn()); |
| } |
| }; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.then] |
| namespace __then |
| { |
| template <class _ReceiverId, class _Fun> |
| struct __receiver |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __data |
| { |
| _Receiver __rcvr_; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_; |
| }; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver; |
| __data* __op_; |
| |
| // Customize set_value by invoking the invocable and passing the result |
| // to the downstream receiver |
| template <__same_as<set_value_t> _Tag, class... _As> |
| requires __invocable<_Fun, _As...> && |
| __receiver_of_invoke_result<_Receiver, _Fun, _As...> |
| friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept |
| { |
| stdexec::__set_value_invoke((_Receiver&&)__self.__op_->__rcvr_, |
| (_Fun&&)__self.__op_->__fun_, |
| (_As&&)__as...); |
| } |
| |
| template <__one_of<set_error_t, set_stopped_t> _Tag, class... _As> |
| requires __callable<_Tag, _Receiver, _As...> |
| friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept |
| { |
| __tag((_Receiver&&)__self.__op_->__rcvr_, (_As&&)__as...); |
| } |
| |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| -> env_of_t<const _Receiver&> |
| { |
| return get_env(__self.__op_->__rcvr_); |
| } |
| }; |
| }; |
| |
| template <class _CvrefSender, class _ReceiverId, class _Fun> |
| struct __operation |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using __receiver_id = __receiver<_ReceiverId, _Fun>; |
| using __receiver_t = stdexec::__t<__receiver_id>; |
| |
| struct __t : __immovable |
| { |
| using __id = __operation; |
| typename __receiver_id::__data __data_; |
| connect_result_t<_CvrefSender, __receiver_t> __op_; |
| |
| __t(_CvrefSender&& __sndr, _Receiver __rcvr, _Fun __fun) // |
| noexcept(__nothrow_decay_copyable<_Receiver> // |
| && __nothrow_decay_copyable<_Fun> // |
| && __nothrow_connectable<_CvrefSender, __receiver_t>) : |
| __data_{(_Receiver&&)__rcvr, (_Fun&&)__fun}, |
| __op_(connect((_CvrefSender&&)__sndr, __receiver_t{&__data_})) |
| {} |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| start(__self.__op_); |
| } |
| }; |
| }; |
| |
| inline constexpr __mstring __then_context = |
| "In stdexec::then(Sender, Function)..."__csz; |
| using __on_not_callable = __callable_error<__then_context>; |
| |
| template <class _Fun, class _CvrefSender, class _Env> |
| using __completion_signatures_t = // |
| __try_make_completion_signatures< |
| _CvrefSender, _Env, |
| __with_error_invoke_t<set_value_t, _Fun, _CvrefSender, _Env, |
| __on_not_callable>, |
| __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>, |
| _Fun>>; |
| |
| template <class _Receiver> |
| struct __connect_fn |
| { |
| _Receiver& __rcvr_; |
| |
| template <class _Fun, class _Child> |
| using __operation_t = |
| __t<__operation<_Child, __id<_Receiver>, __decay_t<_Fun>>>; |
| |
| template <class _Fun, class _Child> |
| auto operator()(__ignore, _Fun&& __fun, _Child&& __child) const |
| noexcept(__nothrow_constructible_from<__operation_t<_Fun, _Child>, |
| _Child, _Receiver, _Fun>) |
| -> __operation_t<_Fun, _Child> |
| { |
| return __operation_t<_Fun, _Child>{(_Child&&)__child, |
| (_Receiver&&)__rcvr_, (_Fun&&)__fun}; |
| } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////// |
| struct then_t : __with_default_get_env<then_t> |
| { |
| template <sender _Sender, __movable_value _Fun> |
| auto operator()(_Sender&& __sndr, _Fun __fun) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<then_t>((_Fun&&)__fun, (_Sender&&)__sndr)); |
| } |
| |
| template <__movable_value _Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<then_t, _Fun> operator()(_Fun __fun) const |
| { |
| return {{}, {}, {(_Fun&&)__fun}}; |
| } |
| |
| using _Sender = __1; |
| using _Fun = __0; |
| using __legacy_customizations_t = |
| __types<tag_invoke_t(then_t, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(_Sender&)), |
| _Sender, _Fun), |
| tag_invoke_t(then_t, _Sender, _Fun)>; |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <sender_expr_for<then_t> _Sender, class _Env> |
| static auto get_completion_signatures(_Sender&&, _Env&&) |
| -> __completion_signatures_t<__decay_t<__data_of<_Sender>>, |
| __child_of<_Sender>, _Env> |
| { |
| return {}; |
| } |
| |
| template <sender_expr_for<then_t> _Sender, receiver _Receiver> |
| static 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>> |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| __connect_fn<_Receiver>{__rcvr}); |
| } |
| }; |
| } // namespace __then |
| |
| using __then::then_t; |
| inline constexpr then_t then{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.upon_error] |
| namespace __upon_error |
| { |
| template <class _ReceiverId, class _Fun> |
| struct __receiver |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __data |
| { |
| _Receiver __rcvr_; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_; |
| }; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver; |
| __data* __op_; |
| |
| // Customize set_value by invoking the invocable and passing the result |
| // to the downstream receiver |
| template <__same_as<set_error_t> _Tag, class _Error> |
| requires __invocable<_Fun, _Error> && |
| __receiver_of_invoke_result<_Receiver, _Fun, _Error> |
| friend void tag_invoke(_Tag, __t&& __self, _Error&& __error) noexcept |
| { |
| stdexec::__set_value_invoke((_Receiver&&)__self.__op_->__rcvr_, |
| (_Fun&&)__self.__op_->__fun_, |
| (_Error&&)__error); |
| } |
| |
| template <__one_of<set_value_t, set_stopped_t> _Tag, class... _As> |
| requires __callable<_Tag, _Receiver, _As...> |
| friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept |
| { |
| __tag((_Receiver&&)__self.__op_->__rcvr_, (_As&&)__as...); |
| } |
| |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| -> env_of_t<const _Receiver&> |
| { |
| return get_env(__self.__op_->__rcvr_); |
| } |
| }; |
| }; |
| |
| template <class _CvrefSender, class _ReceiverId, class _Fun> |
| struct __operation |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using __receiver_id = __receiver<_ReceiverId, _Fun>; |
| using __receiver_t = stdexec::__t<__receiver_id>; |
| |
| struct __t : __immovable |
| { |
| using __id = __operation; |
| typename __receiver_id::__data __data_; |
| connect_result_t<_CvrefSender, __receiver_t> __op_; |
| |
| __t(_CvrefSender&& __sndr, _Receiver __rcvr, _Fun __fun) // |
| noexcept(__nothrow_decay_copyable<_Receiver> // |
| && __nothrow_decay_copyable<_Fun> // |
| && __nothrow_connectable<_CvrefSender, __receiver_t>) : |
| __data_{(_Receiver&&)__rcvr, (_Fun&&)__fun}, |
| __op_(connect((_CvrefSender&&)__sndr, __receiver_t{&__data_})) |
| {} |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| start(__self.__op_); |
| } |
| }; |
| }; |
| |
| inline constexpr __mstring __upon_error_context = |
| "In stdexec::upon_error(Sender, Function)..."__csz; |
| using __on_not_callable = __callable_error<__upon_error_context>; |
| |
| template <class _Fun, class _CvrefSender, class _Env> |
| using __completion_signatures_t = // |
| __try_make_completion_signatures< |
| _CvrefSender, _Env, |
| __with_error_invoke_t<set_error_t, _Fun, _CvrefSender, _Env, |
| __on_not_callable>, |
| __q<__compl_sigs::__default_set_value>, |
| __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>, |
| _Fun>>; |
| |
| template <class _Receiver> |
| struct __connect_fn |
| { |
| _Receiver& __rcvr_; |
| |
| template <class _Fun, class _Child> |
| using __operation_t = |
| __t<__operation<_Child, __id<_Receiver>, __decay_t<_Fun>>>; |
| |
| template <class _Fun, class _Child> |
| auto operator()(__ignore, _Fun&& __fun, _Child&& __child) const |
| noexcept(__nothrow_constructible_from<__operation_t<_Fun, _Child>, |
| _Child, _Receiver, _Fun>) |
| -> __operation_t<_Fun, _Child> |
| { |
| return __operation_t<_Fun, _Child>{(_Child&&)__child, |
| (_Receiver&&)__rcvr_, (_Fun&&)__fun}; |
| } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////// |
| struct upon_error_t : __with_default_get_env<upon_error_t> |
| { |
| template <sender _Sender, __movable_value _Fun> |
| auto operator()(_Sender&& __sndr, _Fun __fun) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<upon_error_t>((_Fun&&)__fun, (_Sender&&)__sndr)); |
| } |
| |
| template <__movable_value _Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<upon_error_t, _Fun> operator()(_Fun __fun) const |
| { |
| return {{}, {}, {(_Fun&&)__fun}}; |
| } |
| |
| using _Sender = __1; |
| using _Fun = __0; |
| using __legacy_customizations_t = |
| __types<tag_invoke_t(upon_error_t, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(_Sender&)), |
| _Sender, _Fun), |
| tag_invoke_t(upon_error_t, _Sender, _Fun)>; |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <sender_expr_for<upon_error_t> _Sender, class _Env> |
| static auto get_completion_signatures(_Sender&&, _Env&&) |
| -> __completion_signatures_t<__decay_t<__data_of<_Sender>>, |
| __child_of<_Sender>, _Env> |
| { |
| return {}; |
| } |
| |
| template <sender_expr_for<upon_error_t> _Sender, receiver _Receiver> |
| static 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>> |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| __connect_fn<_Receiver>{__rcvr}); |
| } |
| }; |
| } // namespace __upon_error |
| |
| using __upon_error::upon_error_t; |
| inline constexpr upon_error_t upon_error{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.upon_stopped] |
| namespace __upon_stopped |
| { |
| template <class _ReceiverId, class _Fun> |
| struct __receiver |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __data |
| { |
| _Receiver __rcvr_; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_; |
| }; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver; |
| __data* __op_; |
| |
| // Customize set_value by invoking the invocable and passing the result |
| // to the downstream receiver |
| template <__same_as<set_stopped_t> _Tag> |
| requires __invocable<_Fun> && |
| __receiver_of_invoke_result<_Receiver, _Fun> |
| friend void tag_invoke(_Tag, __t&& __self) noexcept |
| { |
| stdexec::__set_value_invoke((_Receiver&&)__self.__op_->__rcvr_, |
| (_Fun&&)__self.__op_->__fun_); |
| } |
| |
| template <__one_of<set_value_t, set_error_t> _Tag, class... _As> |
| requires __callable<_Tag, _Receiver, _As...> |
| friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept |
| { |
| __tag((_Receiver&&)__self.__op_->__rcvr_, (_As&&)__as...); |
| } |
| |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| -> env_of_t<const _Receiver&> |
| { |
| return get_env(__self.__op_->__rcvr_); |
| } |
| }; |
| }; |
| |
| template <class _CvrefSender, class _ReceiverId, class _Fun> |
| struct __operation |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using __receiver_id = __receiver<_ReceiverId, _Fun>; |
| using __receiver_t = stdexec::__t<__receiver_id>; |
| |
| struct __t : __immovable |
| { |
| using __id = __operation; |
| typename __receiver_id::__data __data_; |
| connect_result_t<_CvrefSender, __receiver_t> __op_; |
| |
| __t(_CvrefSender&& __sndr, _Receiver __rcvr, _Fun __fun) // |
| noexcept(__nothrow_decay_copyable<_Receiver> // |
| && __nothrow_decay_copyable<_Fun> // |
| && __nothrow_connectable<_CvrefSender, __receiver_t>) : |
| __data_{(_Receiver&&)__rcvr, (_Fun&&)__fun}, |
| __op_(connect((_CvrefSender&&)__sndr, __receiver_t{&__data_})) |
| {} |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| start(__self.__op_); |
| } |
| }; |
| }; |
| |
| inline constexpr __mstring __upon_stopped_context = |
| "In stdexec::upon_stopped(Sender, Function)..."__csz; |
| using __on_not_callable = __callable_error<__upon_stopped_context>; |
| |
| template <class _Fun, class _CvrefSender, class _Env> |
| using __completion_signatures_t = // |
| __try_make_completion_signatures< |
| _CvrefSender, _Env, |
| __with_error_invoke_t<set_stopped_t, _Fun, _CvrefSender, _Env, |
| __on_not_callable>, |
| __q<__compl_sigs::__default_set_value>, |
| __q<__compl_sigs::__default_set_error>, __set_value_invoke_t<_Fun>>; |
| |
| template <class _Receiver> |
| struct __connect_fn |
| { |
| _Receiver& __rcvr_; |
| |
| template <class _Fun, class _Child> |
| using __operation_t = |
| __t<__operation<_Child, __id<_Receiver>, __decay_t<_Fun>>>; |
| |
| template <class _Fun, class _Child> |
| auto operator()(__ignore, _Fun&& __fun, _Child&& __child) const |
| noexcept(__nothrow_constructible_from<__operation_t<_Fun, _Child>, |
| _Child, _Receiver, _Fun>) |
| -> __operation_t<_Fun, _Child> |
| { |
| return __operation_t<_Fun, _Child>{(_Child&&)__child, |
| (_Receiver&&)__rcvr_, (_Fun&&)__fun}; |
| } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////// |
| struct upon_stopped_t : __with_default_get_env<upon_stopped_t> |
| { |
| template <sender _Sender, __movable_value _Fun> |
| requires __callable<_Fun> |
| auto operator()(_Sender&& __sndr, _Fun __fun) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<upon_stopped_t>((_Fun&&)__fun, (_Sender&&)__sndr)); |
| } |
| |
| template <__movable_value _Fun> |
| requires __callable<_Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<upon_stopped_t, _Fun> |
| operator()(_Fun __fun) const |
| { |
| return {{}, {}, {(_Fun&&)__fun}}; |
| } |
| |
| using _Sender = __1; |
| using _Fun = __0; |
| using __legacy_customizations_t = |
| __types<tag_invoke_t(upon_stopped_t, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(_Sender&)), |
| _Sender, _Fun), |
| tag_invoke_t(upon_stopped_t, _Sender, _Fun)>; |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <sender_expr_for<upon_stopped_t> _Sender, class _Env> |
| static auto get_completion_signatures(_Sender&&, _Env&&) |
| -> __completion_signatures_t<__decay_t<__data_of<_Sender>>, |
| __child_of<_Sender>, _Env> |
| { |
| return {}; |
| } |
| |
| template <sender_expr_for<upon_stopped_t> _Sender, receiver _Receiver> |
| static 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>> |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| __connect_fn<_Receiver>{__rcvr}); |
| } |
| }; |
| } // namespace __upon_stopped |
| |
| using __upon_stopped::upon_stopped_t; |
| inline constexpr upon_stopped_t upon_stopped{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.bulk] |
| namespace __bulk |
| { |
| inline constexpr __mstring __bulk_context = |
| "In stdexec::bulk(Sender, Shape, Function)..."__csz; |
| using __on_not_callable = __callable_error<__bulk_context>; |
| |
| template <class _Shape, class _Fun> |
| struct __data |
| { |
| _Shape __shape_; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_; |
| static constexpr auto __mbrs_ = |
| __mliterals<&__data::__shape_, &__data::__fun_>(); |
| }; |
| template <class _Shape, class _Fun> |
| __data(_Shape, _Fun) -> __data<_Shape, _Fun>; |
| |
| template <class _ReceiverId, class _Shape, class _Fun> |
| struct __receiver |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __op_state : __data<_Shape, _Fun> |
| { |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_; |
| }; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver; |
| __op_state* __op_; |
| |
| template <same_as<set_value_t> _Tag, class... _As> |
| requires __callable<_Tag, _Receiver, _As...> |
| friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept |
| requires __nothrow_callable<_Fun, _Shape, _As&...> |
| { |
| for (_Shape __i{}; __i != __self.__op_->__shape_; ++__i) |
| { |
| __self.__op_->__fun_(__i, __as...); |
| } |
| stdexec::set_value((_Receiver&&)__self.__op_->__rcvr_, |
| (_As&&)__as...); |
| } |
| |
| template <same_as<set_value_t> _Tag, class... _As> |
| requires __callable<_Tag, _Receiver, _As...> |
| friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept |
| requires __callable<_Fun, _Shape, _As&...> |
| { |
| try |
| { |
| for (_Shape __i{}; __i != __self.__op_->__shape_; ++__i) |
| { |
| __self.__op_->__fun_(__i, __as...); |
| } |
| stdexec::set_value((_Receiver&&)__self.__op_->__rcvr_, |
| (_As&&)__as...); |
| } |
| catch (...) |
| { |
| stdexec::set_error((_Receiver&&)__self.__op_->__rcvr_, |
| std::current_exception()); |
| } |
| } |
| |
| template <same_as<set_error_t> _Tag, class _Error> |
| requires __callable<_Tag, _Receiver, _Error> |
| friend void tag_invoke(_Tag, __t&& __self, _Error&& __error) noexcept |
| { |
| stdexec::set_error((_Receiver&&)__self.__op_->__rcvr_, |
| (_Error&&)__error); |
| } |
| |
| template <same_as<set_stopped_t> _Tag> |
| requires __callable<_Tag, _Receiver> |
| friend void tag_invoke(_Tag, __t&& __self) noexcept |
| { |
| stdexec::set_stopped((_Receiver&&)__self.__op_->__rcvr_); |
| } |
| |
| friend env_of_t<_Receiver> tag_invoke(get_env_t, |
| const __t& __self) noexcept |
| { |
| return stdexec::get_env(__self.__op_->__rcvr_); |
| } |
| }; |
| }; |
| |
| template <class _CvrefSenderId, class _ReceiverId, class _Shape, class _Fun> |
| struct __operation |
| { |
| using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using __receiver_id = __receiver<_ReceiverId, _Shape, _Fun>; |
| using __receiver_t = stdexec::__t<__receiver_id>; |
| |
| struct __t : __immovable |
| { |
| using __id = __operation; |
| using __data_t = __data<_Shape, _Fun>; |
| |
| typename __receiver_id::__op_state __state_; |
| connect_result_t<_CvrefSender, __receiver_t> __op_; |
| |
| __t(_CvrefSender&& __sndr, _Receiver __rcvr, __data_t __data) // |
| noexcept(__nothrow_decay_copyable<_Receiver> // |
| && __nothrow_decay_copyable<__data_t> // |
| && __nothrow_connectable<_CvrefSender, __receiver_t>) : |
| __state_{(__data_t&&)__data, (_Receiver&&)__rcvr}, |
| __op_(connect((_CvrefSender&&)__sndr, __receiver_t{&__state_})) |
| {} |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| start(__self.__op_); |
| } |
| }; |
| }; |
| |
| template <class _Ty> |
| using __decay_ref = __decay_t<_Ty>&; |
| |
| template <class _CvrefSender, class _Env, class _Shape, class _Fun, |
| class _Catch> |
| using __with_error_invoke_t = // |
| __if<__try_value_types_of_t< |
| _CvrefSender, _Env, |
| __transform<__q<__decay_ref>, |
| __mbind_front<__mtry_catch_q<__non_throwing_, _Catch>, |
| _Fun, _Shape>>, |
| __q<__mand>>, |
| completion_signatures<>, __with_exception_ptr>; |
| |
| template <class _CvrefSender, class _Env, class _Shape, class _Fun> |
| using __completion_signatures = // |
| __try_make_completion_signatures< |
| _CvrefSender, _Env, |
| __with_error_invoke_t<_CvrefSender, _Env, _Shape, _Fun, |
| __on_not_callable>>; |
| |
| template <class _Receiver> |
| struct __connect_fn |
| { |
| _Receiver& __rcvr_; |
| |
| template <class _Child, class _Data> |
| using __operation_t = // |
| __t<__operation<__cvref_id<_Child>, __id<_Receiver>, |
| decltype(_Data::__shape_), decltype(_Data::__fun_)>>; |
| |
| template <class _Data, class _Child> |
| auto operator()(__ignore, _Data __data, _Child&& __child) const |
| noexcept(__nothrow_constructible_from<__operation_t<_Child, _Data>, |
| _Child, _Receiver, _Data>) |
| -> __operation_t<_Child, _Data> |
| { |
| return __operation_t<_Child, _Data>{ |
| (_Child&&)__child, (_Receiver&&)__rcvr_, (_Data&&)__data}; |
| } |
| }; |
| |
| struct bulk_t : __with_default_get_env<bulk_t> |
| { |
| template <sender _Sender, integral _Shape, __movable_value _Fun> |
| STDEXEC_ATTRIBUTE((host, device)) |
| auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<bulk_t>(__data{__shape, (_Fun&&)__fun}, |
| (_Sender&&)__sndr)); |
| } |
| |
| template <integral _Shape, class _Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<bulk_t, _Shape, _Fun> operator()(_Shape __shape, |
| _Fun __fun) const |
| { |
| return {{}, {}, {(_Shape&&)__shape, (_Fun&&)__fun}}; |
| } |
| |
| // This describes how to use the pieces of a bulk sender to find |
| // legacy customizations of the bulk algorithm. |
| using _Sender = __1; |
| using _Shape = __nth_member<0>(__0); |
| using _Fun = __nth_member<1>(__0); |
| using __legacy_customizations_t = |
| __types<tag_invoke_t(bulk_t, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(_Sender&)), |
| _Sender, _Shape, _Fun), |
| tag_invoke_t(bulk_t, _Sender, _Shape, _Fun)>; |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <class _Sender> |
| using __fun_t = decltype(__decay_t<__data_of<_Sender>>::__fun_); |
| |
| template <class _Sender> |
| using __shape_t = decltype(__decay_t<__data_of<_Sender>>::__shape_); |
| |
| template <sender_expr_for<bulk_t> _Sender, class _Env> |
| static auto get_completion_signatures(_Sender&&, _Env&&) |
| -> __completion_signatures<__child_of<_Sender>, _Env, |
| __shape_t<_Sender>, __fun_t<_Sender>> |
| { |
| return {}; |
| } |
| |
| template <sender_expr_for<bulk_t> _Sender, receiver _Receiver> |
| static 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>> |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| __connect_fn<_Receiver>{__rcvr}); |
| } |
| }; |
| } // namespace __bulk |
| |
| using __bulk::bulk_t; |
| inline constexpr bulk_t bulk{}; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.split] |
| namespace __split |
| { |
| template <class _BaseEnv> |
| using __env_t = // |
| __make_env_t<_BaseEnv, // BUGBUG NOT TO SPEC |
| __with<get_stop_token_t, in_place_stop_token>>; |
| |
| template <class _CvrefSenderId, class _EnvId> |
| struct __sh_state; |
| |
| template <class _CvrefSenderId, class _EnvId> |
| struct __receiver |
| { |
| using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>; |
| using _Env = stdexec::__t<_EnvId>; |
| |
| class __t |
| { |
| stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>& __sh_state_; |
| |
| public: |
| using is_receiver = void; |
| using __id = __receiver; |
| |
| template <__completion_tag _Tag, class... _As> |
| friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept |
| { |
| stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>& __state = |
| __self.__sh_state_; |
| |
| try |
| { |
| using __tuple_t = __decayed_tuple<_Tag, _As...>; |
| __state.__data_.template emplace<__tuple_t>(__tag, |
| (_As&&)__as...); |
| } |
| catch (...) |
| { |
| using __tuple_t = |
| __decayed_tuple<set_error_t, std::exception_ptr>; |
| __state.__data_.template emplace<__tuple_t>( |
| set_error, std::current_exception()); |
| } |
| __state.__notify(); |
| } |
| |
| friend const __env_t<_Env>& tag_invoke(get_env_t, |
| const __t& __self) noexcept |
| { |
| return __self.__sh_state_.__env_; |
| } |
| |
| explicit __t(stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>& |
| __sh_state) noexcept : |
| __sh_state_(__sh_state) |
| {} |
| }; |
| }; |
| |
| struct __operation_base |
| { |
| using __notify_fn = void(__operation_base*) noexcept; |
| |
| __operation_base* __next_{}; |
| __notify_fn* __notify_{}; |
| }; |
| |
| template <class _CvrefSenderId, class _EnvId = __id<empty_env>> |
| struct __sh_state |
| { |
| using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>; |
| using _Env = stdexec::__t<_EnvId>; |
| |
| struct __t |
| { |
| using __id = __sh_state; |
| |
| template <class... _Ts> |
| using __bind_tuples = // |
| __mbind_front_q<__variant, |
| std::tuple<set_stopped_t>, // Initial state of the |
| // variant is set_stopped |
| std::tuple<set_error_t, std::exception_ptr>, |
| _Ts...>; |
| |
| using __bound_values_t = // |
| __value_types_of_t<_CvrefSender, __env_t<_Env>, |
| __mbind_front_q<__decayed_tuple, set_value_t>, |
| __q<__bind_tuples>>; |
| |
| using __variant_t = // |
| __error_types_of_t< |
| _CvrefSender, __env_t<_Env>, |
| __transform<__mbind_front_q<__decayed_tuple, set_error_t>, |
| __bound_values_t>>; |
| |
| using __receiver_ = stdexec::__t<__receiver<_CvrefSenderId, _EnvId>>; |
| |
| in_place_stop_source __stop_source_{}; |
| __variant_t __data_; |
| std::atomic<void*> __head_{nullptr}; |
| __env_t<_Env> __env_; |
| connect_result_t<_CvrefSender, __receiver_> __op_state2_; |
| |
| explicit __t(_CvrefSender&& __sndr, _Env __env = {}) : |
| __env_( |
| __make_env((_Env&&)__env, __mkprop(__stop_source_.get_token(), |
| get_stop_token))), |
| __op_state2_(connect((_CvrefSender&&)__sndr, __receiver_{*this})) |
| {} |
| |
| void __notify() noexcept |
| { |
| void* const __completion_state = static_cast<void*>(this); |
| void* __old = __head_.exchange(__completion_state, |
| std::memory_order_acq_rel); |
| __operation_base* __op_state = |
| static_cast<__operation_base*>(__old); |
| |
| while (__op_state != nullptr) |
| { |
| __operation_base* __next = __op_state->__next_; |
| __op_state->__notify_(__op_state); |
| __op_state = __next; |
| } |
| } |
| }; |
| }; |
| |
| template <class _CvrefSenderId, class _EnvId, class _ReceiverId> |
| requires sender_to<__cvref_t<_CvrefSenderId>, |
| __t<__receiver<_CvrefSenderId, _EnvId>>> |
| struct __operation |
| { |
| using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>; |
| using _Env = stdexec::__t<_EnvId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| class __t : public __operation_base |
| { |
| struct __on_stop_requested |
| { |
| in_place_stop_source& __stop_source_; |
| |
| void operator()() noexcept |
| { |
| __stop_source_.request_stop(); |
| } |
| }; |
| |
| using __on_stop = // |
| std::optional<typename stop_token_of_t<env_of_t<_Receiver>&>:: |
| template callback_type<__on_stop_requested>>; |
| |
| _Receiver __rcvr_; |
| __on_stop __on_stop_{}; |
| std::shared_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>> |
| __shared_state_; |
| |
| public: |
| using __id = __operation; |
| |
| __t( // |
| _Receiver&& __rcvr, |
| std::shared_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>> |
| __shared_state) // |
| noexcept(std::is_nothrow_move_constructible_v<_Receiver>) : |
| __operation_base{nullptr, __notify}, |
| __rcvr_((_Receiver&&)__rcvr), |
| __shared_state_(std::move(__shared_state)) |
| {} |
| |
| STDEXEC_IMMOVABLE(__t); |
| |
| static void __notify(__operation_base* __self) noexcept |
| { |
| __t* __op = static_cast<__t*>(__self); |
| __op->__on_stop_.reset(); |
| |
| std::visit( |
| [&](const auto& __tupl) noexcept -> void { |
| __apply( |
| [&](auto __tag, const auto&... __args) noexcept -> void { |
| __tag((_Receiver&&)__op->__rcvr_, __args...); |
| }, |
| __tupl); |
| }, |
| __op->__shared_state_->__data_); |
| } |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>* __shared_state = |
| __self.__shared_state_.get(); |
| std::atomic<void*>& __head = __shared_state->__head_; |
| void* const __completion_state = static_cast<void*>(__shared_state); |
| void* __old = __head.load(std::memory_order_acquire); |
| |
| if (__old != __completion_state) |
| { |
| __self.__on_stop_.emplace( |
| get_stop_token(get_env(__self.__rcvr_)), |
| __on_stop_requested{__shared_state->__stop_source_}); |
| } |
| |
| do |
| { |
| if (__old == __completion_state) |
| { |
| __self.__notify(&__self); |
| return; |
| } |
| __self.__next_ = static_cast<__operation_base*>(__old); |
| } while (!__head.compare_exchange_weak( |
| __old, static_cast<void*>(&__self), std::memory_order_release, |
| std::memory_order_acquire)); |
| |
| if (__old == nullptr) |
| { |
| // the inner sender isn't running |
| if (__shared_state->__stop_source_.stop_requested()) |
| { |
| // 1. resets __head to completion state |
| // 2. notifies waiting threads |
| // 3. propagates "stopped" signal to `out_r'` |
| __shared_state->__notify(); |
| } |
| else |
| { |
| start(__shared_state->__op_state2_); |
| } |
| } |
| } |
| }; |
| }; |
| |
| struct __split_t |
| { |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <class... _Tys> |
| using __set_value_t = |
| completion_signatures<set_value_t(const __decay_t<_Tys>&...)>; |
| |
| template <class _Ty> |
| using __set_error_t = |
| completion_signatures<set_error_t(const __decay_t<_Ty>&)>; |
| |
| template <class _CvrefSenderId, class _EnvId> |
| using __completions_t = // |
| __try_make_completion_signatures< |
| // NOT TO SPEC: |
| // See |
| // https://github.com/brycelelbach/wg21_p2300_execution/issues/26 |
| __cvref_t<_CvrefSenderId>, __env_t<__t<_EnvId>>, |
| completion_signatures<set_error_t(const std::exception_ptr&), |
| set_stopped_t()>, // NOT TO SPEC |
| __q<__set_value_t>, __q<__set_error_t>>; |
| |
| static inline constexpr auto __connect_fn = |
| []<class _Receiver>(_Receiver& __rcvr) noexcept { |
| return |
| [&]<class _ShState>(auto, std::shared_ptr<_ShState> __sh_state) // |
| noexcept(__nothrow_decay_copyable<_Receiver>) |
| -> __t<__mapply<__mbind_back_q<__operation, __id<_Receiver>>, |
| __id<_ShState>>> { |
| return {(_Receiver&&)__rcvr, std::move(__sh_state)}; |
| }; |
| }; |
| |
| static inline constexpr auto __get_completion_signatures_fn = |
| []<class _ShState>(auto, const std::shared_ptr<_ShState>&) // |
| -> __mapply<__q<__completions_t>, __id<_ShState>> { return {}; }; |
| |
| template <sender_expr_for<__split_t> _Self, class _Receiver> |
| static auto connect(_Self&& __self, _Receiver __rcvr) noexcept( |
| __nothrow_callable< |
| __sexpr_apply_t, _Self, |
| __call_result_t<__mtypeof<__connect_fn>, _Receiver&>>) |
| -> __call_result_t<__sexpr_apply_t, _Self, |
| __call_result_t<__mtypeof<__connect_fn>, _Receiver&>> |
| { |
| return __sexpr_apply((_Self&&)__self, __connect_fn(__rcvr)); |
| } |
| |
| template <sender_expr_for<__split_t> _Self, class _OtherEnv> |
| static auto get_completion_signatures(_Self&&, _OtherEnv&&) |
| -> __call_result_t<__sexpr_apply_t, _Self, |
| __mtypeof<__get_completion_signatures_fn>> |
| { |
| return {}; |
| } |
| }; |
| |
| struct split_t : __split_t |
| { |
| template <sender _Sender> |
| requires sender_in<_Sender, empty_env> && |
| __decay_copyable<env_of_t<_Sender>> |
| auto operator()(_Sender&& __sndr) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<split_t>(__(), (_Sender&&)__sndr)); |
| } |
| |
| template <sender _Sender, class _Env> |
| requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>> |
| auto operator()(_Sender&& __sndr, _Env&& __env) const |
| { |
| auto __domain = __get_late_domain(__sndr, __env); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<split_t>(__(), (_Sender&&)__sndr), |
| (_Env&&)__env); |
| } |
| |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<split_t> operator()() const |
| { |
| return {{}, {}, {}}; |
| } |
| |
| using _Sender = __1; |
| using __legacy_customizations_t = // |
| __types<tag_invoke_t(split_t, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(const _Sender&)), |
| _Sender), |
| tag_invoke_t(split_t, _Sender)>; |
| |
| template <class _Sender, class... _Env> |
| static auto transform_sender(_Sender&& __sndr, _Env... __env) |
| { |
| return __sexpr_apply( |
| (_Sender&&)__sndr, |
| [&]<class _Child>(__ignore, __ignore, _Child&& __child) { |
| using __sh_state_t = |
| __t<__sh_state<__cvref_id<_Child>, __id<_Env>...>>; |
| auto __sh_state = std::make_shared<__sh_state_t>( |
| (_Child&&)__child, std::move(__env)...); |
| return __make_sexpr<__split_t>(std::move(__sh_state)); |
| }); |
| } |
| }; |
| } // namespace __split |
| |
| using __split::split_t; |
| inline constexpr split_t split{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.ensure_started] |
| namespace __ensure_started |
| { |
| template <class _BaseEnv> |
| using __env_t = // |
| __make_env_t<_BaseEnv, // NOT TO SPEC |
| __with<get_stop_token_t, in_place_stop_token>>; |
| |
| template <class _CvrefSenderId, class _EnvId> |
| struct __sh_state; |
| |
| template <class _CvrefSenderId, class _EnvId = __id<empty_env>> |
| struct __receiver |
| { |
| using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>; |
| using _Env = stdexec::__t<_EnvId>; |
| |
| class __t |
| { |
| __intrusive_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>> |
| __shared_state_; |
| |
| public: |
| using is_receiver = void; |
| using __id = __receiver; |
| |
| explicit __t(stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>& |
| __shared_state) noexcept : |
| __shared_state_(__shared_state.__intrusive_from_this()) |
| {} |
| |
| template <__completion_tag _Tag, class... _As> |
| friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept |
| { |
| stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>& __state = |
| *__self.__shared_state_; |
| |
| try |
| { |
| using __tuple_t = __decayed_tuple<_Tag, _As...>; |
| __state.__data_.template emplace<__tuple_t>(__tag, |
| (_As&&)__as...); |
| } |
| catch (...) |
| { |
| using __tuple_t = |
| __decayed_tuple<set_error_t, std::exception_ptr>; |
| __state.__data_.template emplace<__tuple_t>( |
| set_error, std::current_exception()); |
| } |
| |
| __state.__notify(); |
| __self.__shared_state_.reset(); |
| } |
| |
| friend const __env_t<_Env>& tag_invoke(get_env_t, |
| const __t& __self) noexcept |
| { |
| return __self.__shared_state_->__env_; |
| } |
| }; |
| }; |
| |
| struct __operation_base |
| { |
| using __notify_fn = void(__operation_base*) noexcept; |
| __notify_fn* __notify_{}; |
| }; |
| |
| template <class _CvrefSenderId, class _EnvId = __id<empty_env>> |
| struct __sh_state |
| { |
| using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>; |
| using _Env = stdexec::__t<_EnvId>; |
| |
| struct __t : __enable_intrusive_from_this<__t> |
| { |
| using __id = __sh_state; |
| |
| template <class... _Ts> |
| using __bind_tuples = // |
| __mbind_front_q<__variant, |
| std::tuple<set_stopped_t>, // Initial state of the |
| // variant is set_stopped |
| std::tuple<set_error_t, std::exception_ptr>, |
| _Ts...>; |
| |
| using __bound_values_t = // |
| __value_types_of_t<_CvrefSender, __env_t<_Env>, |
| __mbind_front_q<__decayed_tuple, set_value_t>, |
| __q<__bind_tuples>>; |
| |
| using __variant_t = // |
| __error_types_of_t< |
| _CvrefSender, __env_t<_Env>, |
| __transform<__mbind_front_q<__decayed_tuple, set_error_t>, |
| __bound_values_t>>; |
| |
| using __receiver_t = stdexec::__t<__receiver<_CvrefSenderId, _EnvId>>; |
| |
| __variant_t __data_; |
| in_place_stop_source __stop_source_{}; |
| |
| std::atomic<void*> __op_state1_{nullptr}; |
| __env_t<_Env> __env_; |
| connect_result_t<_CvrefSender, __receiver_t> __op_state2_; |
| |
| explicit __t(_CvrefSender&& __sndr, _Env __env = {}) : |
| __env_( |
| __make_env((_Env&&)__env, __mkprop(__stop_source_.get_token(), |
| get_stop_token))), |
| __op_state2_(connect((_CvrefSender&&)__sndr, __receiver_t{*this})) |
| { |
| start(__op_state2_); |
| } |
| |
| void __notify() noexcept |
| { |
| void* const __completion_state = static_cast<void*>(this); |
| void* const __old = __op_state1_.exchange( |
| __completion_state, std::memory_order_acq_rel); |
| if (__old != nullptr) |
| { |
| auto* __op = static_cast<__operation_base*>(__old); |
| __op->__notify_(__op); |
| } |
| } |
| |
| void __detach() noexcept |
| { |
| __stop_source_.request_stop(); |
| } |
| }; |
| }; |
| |
| template <class _CvrefSenderId, class _EnvId, class _ReceiverId> |
| struct __operation |
| { |
| using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>; |
| using _Env = stdexec::__t<_EnvId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| class __t : public __operation_base |
| { |
| struct __on_stop_requested |
| { |
| in_place_stop_source& __stop_source_; |
| |
| void operator()() noexcept |
| { |
| __stop_source_.request_stop(); |
| } |
| }; |
| |
| using __on_stop = // |
| std::optional<typename stop_token_of_t<env_of_t<_Receiver>&>:: |
| template callback_type<__on_stop_requested>>; |
| |
| _Receiver __rcvr_; |
| __on_stop __on_stop_{}; |
| __intrusive_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>> |
| __shared_state_; |
| |
| public: |
| using __id = __operation; |
| |
| __t( // |
| _Receiver __rcvr, // |
| __intrusive_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>> |
| __shared_state) // |
| noexcept(std::is_nothrow_move_constructible_v<_Receiver>) : |
| __operation_base{__notify}, |
| __rcvr_((_Receiver&&)__rcvr), |
| __shared_state_(std::move(__shared_state)) |
| {} |
| |
| ~__t() |
| { |
| // Check to see if this operation was ever started. If not, |
| // detach the (potentially still running) operation: |
| if (nullptr == |
| __shared_state_->__op_state1_.load(std::memory_order_acquire)) |
| { |
| __shared_state_->__detach(); |
| } |
| } |
| |
| STDEXEC_IMMOVABLE(__t); |
| |
| static void __notify(__operation_base* __self) noexcept |
| { |
| __t* __op = static_cast<__t*>(__self); |
| __op->__on_stop_.reset(); |
| |
| std::visit( |
| [&](auto& __tupl) noexcept -> void { |
| __apply( |
| [&](auto __tag, auto&... __args) noexcept -> void { |
| __tag((_Receiver&&)__op->__rcvr_, std::move(__args)...); |
| }, |
| __tupl); |
| }, |
| __op->__shared_state_->__data_); |
| } |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>* __shared_state = |
| __self.__shared_state_.get(); |
| std::atomic<void*>& __op_state1 = __shared_state->__op_state1_; |
| void* const __completion_state = static_cast<void*>(__shared_state); |
| void* const __old = __op_state1.load(std::memory_order_acquire); |
| if (__old == __completion_state) |
| { |
| __self.__notify(&__self); |
| } |
| else |
| { |
| // register stop callback: |
| __self.__on_stop_.emplace( |
| get_stop_token(get_env(__self.__rcvr_)), |
| __on_stop_requested{__shared_state->__stop_source_}); |
| // Check if the stop_source has requested cancellation |
| if (__shared_state->__stop_source_.stop_requested()) |
| { |
| // Stop has already been requested. Don't bother starting |
| // the child operations. |
| stdexec::set_stopped((_Receiver&&)__self.__rcvr_); |
| } |
| else |
| { |
| // Otherwise, the inner source hasn't notified completion. |
| // Set this operation as the __op_state1 so it's notified. |
| void* __old = nullptr; |
| if (!__op_state1.compare_exchange_weak( |
| __old, &__self, std::memory_order_release, |
| std::memory_order_acquire)) |
| { |
| // We get here when the task completed during the |
| // execution of this function. Complete the operation |
| // synchronously. |
| STDEXEC_ASSERT(__old == __completion_state); |
| __self.__notify(&__self); |
| } |
| } |
| } |
| } |
| }; |
| }; |
| |
| template <class _ShState> |
| struct __data |
| { |
| __data(__intrusive_ptr<_ShState> __sh_state) noexcept : |
| __sh_state_(std::move(__sh_state)) |
| {} |
| |
| __data(__data&&) = default; |
| __data& operator=(__data&&) = default; |
| |
| ~__data() |
| { |
| if (__sh_state_ != nullptr) |
| { |
| // detach from the still-running operation. |
| // NOT TO SPEC: This also requests cancellation. |
| __sh_state_->__detach(); |
| } |
| } |
| |
| __intrusive_ptr<_ShState> __sh_state_; |
| }; |
| |
| struct __ensure_started_t |
| { |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| friend struct ensure_started_t; |
| #endif |
| |
| template <class... _Tys> |
| using __set_value_t = |
| completion_signatures<set_value_t(__decay_t<_Tys>&&...)>; |
| |
| template <class _Ty> |
| using __set_error_t = completion_signatures<set_error_t(__decay_t<_Ty>&&)>; |
| |
| template <class _CvrefSenderId, class _EnvId> |
| using __completions_t = // |
| __try_make_completion_signatures< |
| // NOT TO SPEC: |
| // See |
| // https://github.com/brycelelbach/wg21_p2300_execution/issues/26 |
| __cvref_t<_CvrefSenderId>, __env_t<__t<_EnvId>>, |
| completion_signatures<set_error_t(std::exception_ptr&&), |
| set_stopped_t()>, // NOT TO SPEC |
| __q<__set_value_t>, __q<__set_error_t>>; |
| |
| static inline constexpr auto __connect_fn = |
| []<class _Receiver>(_Receiver& __rcvr) noexcept { |
| return [&]<class _ShState>(auto, __data<_ShState> __dat) // |
| noexcept(__nothrow_decay_copyable<_Receiver>) |
| -> __t<__mapply<__mbind_back_q<__operation, __id<_Receiver>>, |
| __id<_ShState>>> { |
| return {(_Receiver&&)__rcvr, std::move(__dat.__sh_state_)}; |
| }; |
| }; |
| |
| static inline constexpr auto __get_completion_signatures_fn = |
| []<class _ShState>(auto, const __data<_ShState>&) // |
| -> __mapply<__q<__completions_t>, __id<_ShState>> { return {}; }; |
| |
| template <sender_expr_for<__ensure_started_t> _Self, class _Receiver> |
| static auto connect(_Self&& __self, _Receiver __rcvr) noexcept( |
| __nothrow_callable< |
| __sexpr_apply_t, _Self, |
| __call_result_t<__mtypeof<__connect_fn>, _Receiver&>>) |
| -> __call_result_t<__sexpr_apply_t, _Self, |
| __call_result_t<__mtypeof<__connect_fn>, _Receiver&>> |
| { |
| return __sexpr_apply((_Self&&)__self, __connect_fn(__rcvr)); |
| } |
| |
| template <sender_expr_for<__ensure_started_t> _Self, class _OtherEnv> |
| static auto get_completion_signatures(_Self&&, _OtherEnv&&) |
| -> __call_result_t<__sexpr_apply_t, _Self, |
| __mtypeof<__get_completion_signatures_fn>> |
| { |
| return {}; |
| } |
| }; |
| |
| struct ensure_started_t : __with_default_get_env<ensure_started_t> |
| { |
| template <sender _Sender> |
| requires sender_in<_Sender, empty_env> && |
| __decay_copyable<env_of_t<_Sender>> |
| auto operator()(_Sender&& __sndr) const |
| { |
| if constexpr (sender_expr_for<_Sender, __ensure_started_t>) |
| { |
| return (_Sender&&)__sndr; |
| } |
| else |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<ensure_started_t>(__(), (_Sender&&)__sndr)); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| template <sender _Sender, class _Env> |
| requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>> |
| auto operator()(_Sender&& __sndr, _Env&& __env) const |
| { |
| if constexpr (sender_expr_for<_Sender, __ensure_started_t>) |
| { |
| return (_Sender&&)__sndr; |
| } |
| else |
| { |
| auto __domain = __get_late_domain(__sndr, __env); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<ensure_started_t>(__(), (_Sender&&)__sndr), |
| (_Env&&)__env); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<ensure_started_t> operator()() const |
| { |
| return {{}, {}, {}}; |
| } |
| |
| using _Sender = __1; |
| using __legacy_customizations_t = // |
| __types<tag_invoke_t(ensure_started_t, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(const _Sender&)), |
| _Sender), |
| tag_invoke_t(ensure_started_t, _Sender)>; |
| |
| template <class _CvrefSender, class... _Env> |
| using __receiver_t = stdexec::__t< |
| __meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>...>>; |
| |
| template <class _Sender, class... _Env> |
| requires sender_to<__child_of<_Sender>, |
| __receiver_t<__child_of<_Sender>, _Env...>> |
| static auto transform_sender(_Sender&& __sndr, _Env... __env) |
| { |
| return __sexpr_apply( |
| (_Sender&&)__sndr, |
| [&]<class _Child>(__ignore, __ignore, _Child&& __child) { |
| using __sh_state_t = |
| __t<__sh_state<__cvref_id<_Child>, __id<_Env>...>>; |
| auto __sh_state = __make_intrusive<__sh_state_t>( |
| (_Child&&)__child, std::move(__env)...); |
| return __make_sexpr<__ensure_started_t>( |
| __data{std::move(__sh_state)}); |
| }); |
| } |
| }; |
| } // namespace __ensure_started |
| |
| using __ensure_started::ensure_started_t; |
| inline constexpr ensure_started_t ensure_started{}; |
| |
| STDEXEC_PRAGMA_PUSH() |
| STDEXEC_PRAGMA_IGNORE_EDG(not_used_in_partial_spec_arg_list) |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // a receiver adaptor that augments its environment |
| namespace __detail |
| { |
| template <auto _ReceiverPtr, auto... _EnvFns> |
| struct __receiver_with; |
| |
| template <class _Operation, class _Receiver, |
| _Receiver _Operation::*_ReceiverPtr, auto... _EnvFns> |
| struct __receiver_with<_ReceiverPtr, _EnvFns...> |
| { |
| struct __t : receiver_adaptor<__t> |
| { |
| using __id = __receiver_with; |
| using __env_t = |
| __env::__env_join_t<__result_of<_EnvFns, _Operation*>..., |
| env_of_t<_Receiver>>; |
| |
| _Operation* __op_state_; |
| |
| _Receiver&& base() && noexcept |
| { |
| return static_cast<_Receiver&&>(__op_state_->*_ReceiverPtr); |
| } |
| |
| __env_t get_env() const noexcept |
| { |
| return __join_env(_EnvFns(__op_state_)..., |
| stdexec::get_env(__op_state_->*_ReceiverPtr)); |
| } |
| }; |
| }; |
| } // namespace __detail |
| |
| STDEXEC_PRAGMA_POP() |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.let_value] |
| // [execution.senders.adaptors.let_error] |
| // [execution.senders.adaptors.let_stopped] |
| namespace __let |
| { |
| template <class _Set> |
| struct __on_not_callable_ |
| { |
| using __t = |
| __callable_error<"In stdexec::let_value(Sender, Function)..."__csz>; |
| }; |
| |
| template <> |
| struct __on_not_callable_<set_error_t> |
| { |
| using __t = |
| __callable_error<"In stdexec::let_error(Sender, Function)..."__csz>; |
| }; |
| |
| template <> |
| struct __on_not_callable_<set_stopped_t> |
| { |
| using __t = |
| __callable_error<"In stdexec::let_stopped(Sender, Function)..."__csz>; |
| }; |
| |
| template <class _Set> |
| using __on_not_callable = __t<__on_not_callable_<_Set>>; |
| |
| template <class _Tp> |
| using __decay_ref = __decay_t<_Tp>&; |
| |
| template <class _ReceiverId, class _SchedulerId> |
| struct __operation_base_base_ : __immovable |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using _Scheduler = stdexec::__t<_SchedulerId>; |
| |
| _Receiver __rcvr_; // the input (outer) receiver |
| STDEXEC_ATTRIBUTE((no_unique_address)) |
| _Scheduler __sched_; // the input sender's completion scheduler |
| }; |
| |
| inline constexpr auto __get_scheduler_prop = [](auto* __op) noexcept { |
| return __mkprop(__op->__sched_, get_scheduler); |
| }; |
| |
| inline constexpr auto __get_domain_prop = [](auto*) noexcept { |
| return __mkprop(get_domain); |
| }; |
| |
| // A metafunction that computes the result sender type for a given set of |
| // argument types |
| template <class _Fun, class _Set> |
| using __result_sender_t = // |
| __transform< |
| __q<__decay_ref>, |
| __mbind_front<__mtry_catch_q<__call_result_t, __on_not_callable<_Set>>, |
| _Fun>>; |
| |
| // FUTURE: when we have a scheduler query for "always completes inline", |
| // then we can use that instead of hard-coding `__inln::__scheduler` here. |
| template <class _Scheduler> |
| concept __unknown_context = |
| __one_of<_Scheduler, __none_such, __inln::__scheduler>; |
| |
| // The receiver that gets connected to the result sender is the input receiver, |
| // possibly augmented with the input sender's completion scheduler (which is |
| // where the result sender will be started). |
| template <class _Receiver, class _Scheduler> |
| using __result_receiver_t = __if_c< |
| __unknown_context<_Scheduler>, _Receiver, |
| __t<__detail::__receiver_with< |
| &__operation_base_base_<__id<_Receiver>, __id<_Scheduler>>::__rcvr_, |
| __get_scheduler_prop, __get_domain_prop>>>; |
| |
| // If the input sender knows its completion scheduler, make it the current |
| // scheduler in the environment seen by the result sender. |
| template <class _Env, class _Scheduler> |
| using __result_env_t = |
| __if_c<__unknown_context<_Scheduler>, _Env, |
| __env::__env_join_t<__env::__prop<_Scheduler(get_scheduler_t)>, |
| __env::__prop<void(get_domain_t)>, _Env>>; |
| |
| // A metafunction that computes the type of the resulting operation state for a |
| // given set of argument types. |
| template <class _Receiver, class _Fun, class _Set, class _Sched> |
| using __op_state_for = // |
| __mcompose<__mbind_back_q<connect_result_t, |
| __result_receiver_t<_Receiver, _Sched>>, |
| __result_sender_t<_Fun, _Set>>; |
| |
| template <class _Set, class _Sig> |
| struct __tfx_signal_ |
| { |
| template <class, class, class> |
| using __f = completion_signatures<_Sig>; |
| }; |
| |
| template <class _Set, class... _Args> |
| struct __tfx_signal_<_Set, _Set(_Args...)> |
| { |
| template <class _Env, class _Fun, class _Sched> |
| using __f = // |
| __try_make_completion_signatures< |
| __minvoke<__result_sender_t<_Fun, _Set>, _Args...>, |
| __result_env_t<_Env, _Sched>, |
| // because we don't know if connect-ing the result sender will |
| // throw: |
| completion_signatures<set_error_t(std::exception_ptr)>>; |
| }; |
| |
| // `_Sched` is the input sender's completion scheduler, or __none_such if it |
| // doesn't have one. |
| template <class _Env, class _Fun, class _Set, class _Sched, class _Sig> |
| using __tfx_signal_t = __minvoke<__tfx_signal_<_Set, _Sig>, _Env, _Fun, _Sched>; |
| |
| template <class _ReceiverId, class _Fun, class _Set, class _SchedId, |
| class... _Tuples> |
| struct __operation_base_ |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using _Sched = stdexec::__t<_SchedId>; |
| |
| struct __t : __operation_base_base_<_ReceiverId, _SchedId> |
| { |
| using __id = __operation_base_; |
| using __results_variant_t = std::variant<std::monostate, _Tuples...>; |
| using __op_state_variant_t = // |
| __minvoke< |
| __transform< |
| __uncurry<__op_state_for<_Receiver, _Fun, _Set, _Sched>>, |
| __nullable_variant_t>, |
| _Tuples...>; |
| |
| _Fun __fun_; |
| __results_variant_t __args_; |
| __op_state_variant_t __op_state3_; |
| }; |
| }; |
| |
| template <class _ReceiverId, class _Fun, class _Set, class _SchedId, |
| class... _Tuples> |
| struct __receiver_ |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using _Sched = stdexec::__t<_SchedId>; |
| using _Env = env_of_t<_Receiver>; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver_; |
| |
| decltype(auto) __get_result_receiver() noexcept |
| { |
| if constexpr (same_as<__result_receiver_t<_Receiver, _Sched>, |
| _Receiver>) |
| { |
| return static_cast<_Receiver&&>(__op_state_->__rcvr_); |
| } |
| else |
| { |
| return __result_receiver_t<_Receiver, _Sched>{{}, __op_state_}; |
| } |
| } |
| |
| template <__one_of<_Set> _Tag, class... _As> |
| requires(1 == __v<__minvoke<__mcount<__decayed_tuple<_As...>>, |
| _Tuples...>>) && |
| __minvocable<__result_sender_t<_Fun, _Set>, _As...> && |
| sender_to<__minvoke<__result_sender_t<_Fun, _Set>, _As...>, |
| __result_receiver_t<_Receiver, _Sched>> |
| friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept |
| { |
| try |
| { |
| using __tuple_t = __decayed_tuple<_As...>; |
| using __op_state_t = |
| __minvoke<__op_state_for<_Receiver, _Fun, _Set, _Sched>, |
| _As...>; |
| auto& __args = |
| __self.__op_state_->__args_.template emplace<__tuple_t>( |
| (_As&&)__as...); |
| auto& __op = __self.__op_state_->__op_state3_ |
| .template emplace<__op_state_t>(__conv{[&] { |
| return connect( |
| __apply(std::move(__self.__op_state_->__fun_), __args), |
| __self.__get_result_receiver()); |
| }}); |
| start(__op); |
| } |
| catch (...) |
| { |
| set_error(std::move(__self.__op_state_->__rcvr_), |
| std::current_exception()); |
| } |
| } |
| |
| template <__completion_tag _Tag, class... _As> |
| requires __none_of<_Tag, _Set> && |
| __callable<_Tag, _Receiver, _As...> |
| friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept |
| { |
| __tag(std::move(__self.__op_state_->__rcvr_), (_As&&)__as...); |
| } |
| |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| -> env_of_t<_Receiver> |
| { |
| return get_env(__self.__op_state_->__rcvr_); |
| } |
| |
| using __operation_base_t = stdexec::__t< |
| __operation_base_<_ReceiverId, _Fun, _Set, _SchedId, _Tuples...>>; |
| __operation_base_t* __op_state_; |
| }; |
| }; |
| |
| template <class _Sender, class _Set> |
| using __completion_sched = __query_result_or_t<get_completion_scheduler_t<_Set>, |
| env_of_t<_Sender>, __none_such>; |
| |
| template <class _CvrefSenderId, class _ReceiverId, class _Fun, class _Set> |
| using __receiver = // |
| stdexec::__t<__gather_completions_for< |
| _Set, __cvref_t<_CvrefSenderId>, env_of_t<__t<_ReceiverId>>, |
| __q<__decayed_tuple>, |
| __munique<__mbind_front_q< |
| __receiver_, _ReceiverId, _Fun, _Set, |
| __id<__completion_sched<__cvref_t<_CvrefSenderId>, _Set>>>>>>; |
| |
| template <class _CvrefSenderId, class _ReceiverId, class _Fun, class _Set> |
| using __operation_base = typename __receiver<_CvrefSenderId, _ReceiverId, _Fun, |
| _Set>::__operation_base_t; |
| |
| template <class _CvrefSenderId, class _ReceiverId, class _Fun, class _Set> |
| struct __operation |
| { |
| using _Sender = stdexec::__cvref_t<_CvrefSenderId>; |
| |
| struct __t : __operation_base<_CvrefSenderId, _ReceiverId, _Fun, _Set> |
| { |
| using __id = __operation; |
| using __op_base_t = |
| __operation_base<_CvrefSenderId, _ReceiverId, _Fun, _Set>; |
| using __receiver_t = |
| __receiver<_CvrefSenderId, _ReceiverId, _Fun, _Set>; |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| start(__self.__op_state2_); |
| } |
| |
| template <class _Receiver2> |
| __t(_Sender&& __sndr, _Receiver2&& __rcvr, _Fun __fun) : |
| __op_base_t{{{}, |
| (_Receiver2&&)__rcvr, |
| query_or(get_completion_scheduler<_Set>, |
| get_env(__sndr), __none_such())}, |
| (_Fun&&)__fun, |
| {}, |
| {}}, |
| __op_state2_(connect((_Sender&&)__sndr, __receiver_t{this})) |
| {} |
| |
| connect_result_t<_Sender, __receiver_t> __op_state2_; |
| }; |
| }; |
| |
| template <class _Sender, class _Fun, class _SetId> |
| struct __sender_base |
| { |
| using _Set = stdexec::__t<_SetId>; |
| using is_sender = void; |
| |
| template <class _Self, class _Receiver> |
| using __operation_t = // |
| stdexec::__t<__operation<stdexec::__cvref_id<_Self, _Sender>, |
| stdexec::__id<_Receiver>, _Fun, _Set>>; |
| template <class _Self, class _Receiver> |
| using __receiver_t = __receiver<stdexec::__cvref_id<_Self, _Sender>, |
| stdexec::__id<_Receiver>, _Fun, _Set>; |
| |
| template <class _CvrefSender> |
| using __completion_sched = |
| __query_result_or_t<get_completion_scheduler_t<_Set>, |
| env_of_t<_CvrefSender>, __none_such>; |
| |
| template <class _CvrefSender, class _Env> |
| using __completions = // |
| __mapply<__transform<__mbind_front_q<__tfx_signal_t, _Env, _Fun, _Set, |
| __completion_sched<_Sender>>, |
| __q<__concat_completion_signatures_t>>, |
| __completion_signatures_of_t<_Sender, _Env>>; |
| |
| template <__decays_to_derived_from<__sender_base> _Self, receiver _Receiver> |
| requires sender_to<__copy_cvref_t<_Self, _Sender>, |
| __receiver_t<_Self, _Receiver>> |
| friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) |
| -> __operation_t<_Self, _Receiver> |
| { |
| return __operation_t<_Self, _Receiver>{((_Self&&)__self).__sndr_, |
| (_Receiver&&)__rcvr, |
| ((_Self&&)__self).__fun_}; |
| } |
| |
| template <__decays_to_derived_from<__sender_base> _Self, class _Env> |
| friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&) |
| -> __completions<__copy_cvref_t<_Self, _Sender>, _Env> |
| { |
| return {}; |
| } |
| |
| // BUGBUG better would be to port the `let_[value|error|stopped]` algorithms |
| // to __sexpr |
| template <class _Self, class _ApplyFun> |
| static auto apply(_Self&& __self, _ApplyFun __fun) -> __call_result_t< |
| _ApplyFun, |
| _SetId, // Actually one of let_value_t, let_error_t, let_stopped_t |
| __copy_cvref_t<_Self, _Fun>, __copy_cvref_t<_Self, _Sender>> |
| { |
| return ((_ApplyFun&&)__fun)(_SetId(), ((_Self&&)__self).__fun_, |
| ((_Self&&)__self).__sndr_); |
| } |
| |
| _Sender __sndr_; |
| _Fun __fun_; |
| }; |
| |
| template <class _SenderId, class _Fun, class _SetId, |
| class _Domain = dependent_domain> |
| struct __sender |
| { |
| struct __t : __sender_base<stdexec::__t<_SenderId>, _Fun, _SetId> |
| { |
| using __id = __sender; |
| |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| /*-> env_of_t<const _Sender&>*/ { |
| return __join_env(__mkprop(_Domain(), get_domain), |
| get_env(__self.__sndr_)); |
| } |
| }; |
| }; |
| |
| template <class _LetTag, class _SetTag> |
| struct __let_xxx_t : __with_default_get_env<_LetTag> |
| { |
| using _Sender = __1; |
| using _Function = __0; |
| using __legacy_customizations_t = |
| __types<tag_invoke_t(_LetTag, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(const _Sender&)), |
| _Sender, _Function), |
| tag_invoke_t(_LetTag, _Sender, _Function)>; |
| |
| using __t = _SetTag; |
| template <class _Sender, class _Fun, class _Domain = dependent_domain> |
| using __sender = |
| stdexec::__t<__let::__sender<stdexec::__id<__decay_t<_Sender>>, _Fun, |
| _LetTag, _Domain>>; |
| |
| template <sender _Sender, __movable_value _Fun> |
| auto operator()(_Sender&& __sndr, _Fun __fun) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __sender<_Sender, _Fun>{{(_Sender&&)__sndr, (_Fun&&)__fun}}); |
| } |
| |
| template <class _Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<_LetTag, _Fun> operator()(_Fun __fun) const |
| { |
| return {{}, {}, {(_Fun&&)__fun}}; |
| } |
| |
| // Compute all the domains of all the result senders and make sure they're |
| // all the same |
| template <class _Child, class _Fun, class _Env> |
| using __result_domain_t = __gather_completions_for< |
| _SetTag, _Child, _Env, |
| __mtry_catch<__mbind_front_q<__call_result_t, _Fun>, |
| __on_not_callable<_SetTag>>, |
| __q<__domain::__common_domain_t>>; |
| |
| static auto get_env(__ignore) noexcept |
| { |
| return __mkprop(dependent_domain(), get_domain); |
| } |
| |
| template <sender_expr_for<_LetTag> _Sender, class _Env> |
| static decltype(auto) transform_env(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply( |
| (_Sender&&)__sndr, |
| [&]<class _Fun, sender_in<_Env> _Child>( |
| __ignore, _Fun&&, _Child&& __child) -> decltype(auto) { |
| using _Scheduler = __completion_sched<_Child, _SetTag>; |
| if constexpr (__unknown_context<_Scheduler>) |
| { |
| return (__env); |
| } |
| else |
| { |
| return __join_env(__mkprop(get_completion_scheduler<_SetTag>( |
| stdexec::get_env(__child)), |
| get_scheduler), |
| __mkprop(get_domain), __env); |
| } |
| STDEXEC_UNREACHABLE(); |
| }); |
| } |
| |
| template <sender_expr_for<_LetTag> _Sender, class _Env> |
| requires same_as<__early_domain_of_t<_Sender>, dependent_domain> |
| static decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| [&]<class _Fun, sender_in<_Env> _Child>( |
| __ignore, _Fun&& __fun, _Child&& __child) { |
| // TODO: propagate errors here |
| using _Domain = __result_domain_t<_Child, _Fun, _Env>; |
| static_assert(__none_of<_Domain, __none_such, dependent_domain>); |
| return __sender<_Child, _Fun, _Domain>{ |
| {(_Child&&)__child, (_Fun&&)__fun}}; |
| }); |
| } |
| }; |
| |
| struct let_value_t : __let::__let_xxx_t<let_value_t, set_value_t> |
| {}; |
| |
| struct let_error_t : __let::__let_xxx_t<let_error_t, set_error_t> |
| {}; |
| |
| struct let_stopped_t : __let::__let_xxx_t<let_stopped_t, set_stopped_t> |
| {}; |
| } // namespace __let |
| |
| using __let::let_value_t; |
| inline constexpr let_value_t let_value{}; |
| using __let::let_error_t; |
| inline constexpr let_error_t let_error{}; |
| using __let::let_stopped_t; |
| inline constexpr let_stopped_t let_stopped{}; |
| |
| // BUGBUG this will also be unnecessary when `on` returns a __sexpr |
| namespace __detail |
| { |
| template <class _SenderId, class _Fun, class _SetId> |
| extern __mconst<__let::__sender<__name_of<__t<_SenderId>>, _Fun, _SetId>> |
| __name_of_v<__let::__sender<_SenderId, _Fun, _SetId>>; |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.stopped_as_optional] |
| // [execution.senders.adaptors.stopped_as_error] |
| namespace __stopped_as_xxx |
| { |
| template <class _CvrefSenderId, class _ReceiverId> |
| struct __operation; |
| |
| template <class _CvrefSenderId, class _ReceiverId> |
| struct __receiver |
| { |
| using _Sender = stdexec::__t<_CvrefSenderId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver; |
| |
| template <same_as<set_value_t> _Tag, class _Ty> |
| friend void tag_invoke(_Tag, __t&& __self, _Ty&& __a) noexcept |
| { |
| try |
| { |
| using _Value = __decay_t< |
| __single_sender_value_t<_Sender, env_of_t<_Receiver>>>; |
| static_assert(constructible_from<_Value, _Ty>); |
| stdexec::set_value((_Receiver&&)__self.__op_->__rcvr_, |
| std::optional<_Value>{(_Ty&&)__a}); |
| } |
| catch (...) |
| { |
| stdexec::set_error((_Receiver&&)__self.__op_->__rcvr_, |
| std::current_exception()); |
| } |
| } |
| |
| template <same_as<set_error_t> _Tag, class _Error> |
| friend void tag_invoke(_Tag, __t&& __self, _Error&& __error) noexcept |
| { |
| stdexec::set_error((_Receiver&&)__self.__op_->__rcvr_, |
| (_Error&&)__error); |
| } |
| |
| template <same_as<set_stopped_t> _Tag> |
| friend void tag_invoke(_Tag, __t&& __self) noexcept |
| { |
| using _Value = __decay_t< |
| __single_sender_value_t<_Sender, env_of_t<_Receiver>>>; |
| stdexec::set_value((_Receiver&&)__self.__op_->__rcvr_, |
| std::optional<_Value>{std::nullopt}); |
| } |
| |
| template <same_as<get_env_t> _Tag> |
| friend env_of_t<_Receiver> tag_invoke(_Tag, const __t& __self) noexcept |
| { |
| return stdexec::get_env(__self.__op_->__rcvr_); |
| } |
| |
| stdexec::__t<__operation<_CvrefSenderId, _ReceiverId>>* __op_; |
| }; |
| }; |
| |
| template <class _CvrefSenderId, class _ReceiverId> |
| struct __operation |
| { |
| using _Sender = stdexec::__t<_CvrefSenderId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using __receiver_t = stdexec::__t<__receiver<_CvrefSenderId, _ReceiverId>>; |
| |
| struct __t |
| { |
| using __id = __operation; |
| |
| __t(_Sender&& __sndr, _Receiver&& __rcvr) : |
| __rcvr_((_Receiver&&)__rcvr), |
| __op_state_(connect((_Sender&&)__sndr, __receiver_t{this})) |
| {} |
| |
| STDEXEC_IMMOVABLE(__t); |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| start(__self.__op_state_); |
| } |
| |
| _Receiver __rcvr_; |
| connect_result_t<_Sender, __receiver_t> __op_state_; |
| }; |
| }; |
| |
| struct stopped_as_optional_t : __with_default_get_env<stopped_as_optional_t> |
| { |
| template <sender _Sender> |
| auto operator()(_Sender&& __sndr) const |
| { |
| return __make_sexpr<stopped_as_optional_t>(__(), (_Sender&&)__sndr); |
| } |
| |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<stopped_as_optional_t> operator()() const noexcept |
| { |
| return {}; |
| } |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <class _CvrefSender, class _Receiver> |
| using __operation_t = |
| stdexec::__t<__operation<stdexec::__cvref_id<_CvrefSender>, |
| stdexec::__id<_Receiver>>>; |
| template <class _CvrefSender, class _Receiver> |
| using __receiver_t = |
| stdexec::__t<__receiver<stdexec::__cvref_id<_CvrefSender>, |
| stdexec::__id<_Receiver>>>; |
| |
| template <class _Receiver> |
| struct __connect_fn |
| { |
| _Receiver __rcvr_; |
| |
| template <class _Child> |
| __operation_t<_Child, _Receiver> operator()(stopped_as_optional_t, __, |
| _Child&& __child) |
| { |
| return {(_Child&&)__child, std::move(__rcvr_)}; |
| } |
| }; |
| |
| template <sender_expr_for<stopped_as_optional_t> _Self, receiver _Receiver> |
| requires __single_typed_sender<__child_of<_Self>, |
| env_of_t<_Receiver>> && |
| sender_to<__child_of<_Self>, |
| __receiver_t<__child_of<_Self>, _Receiver>> |
| static __operation_t<__child_of<_Self>, _Receiver> connect(_Self&& __self, |
| _Receiver __rcvr) |
| { |
| return __sexpr_apply((_Self&&)__self, |
| __connect_fn<_Receiver>{(_Receiver&&)__rcvr}); |
| } |
| |
| template <class... _Tys> |
| requires(sizeof...(_Tys) == 1) |
| using __set_value_t = |
| completion_signatures<set_value_t(std::optional<__decay_t<_Tys>>...)>; |
| |
| template <class _Ty> |
| using __set_error_t = completion_signatures<set_error_t(_Ty)>; |
| |
| template <sender_expr_for<stopped_as_optional_t> _Self, class _Env> |
| static auto get_completion_signatures(_Self&&, _Env&&) |
| -> make_completion_signatures< |
| __child_of<_Self>, _Env, |
| completion_signatures<set_error_t(std::exception_ptr)>, |
| __set_value_t, __set_error_t, completion_signatures<>> |
| { |
| return {}; |
| } |
| }; |
| |
| struct stopped_as_error_t |
| { |
| template <sender _Sender, __movable_value _Error> |
| auto operator()(_Sender&& __sndr, _Error __err) const |
| { |
| return (_Sender&&)__sndr | |
| let_stopped( |
| [__err2 = (_Error&&)__err]() mutable // |
| noexcept(std::is_nothrow_move_constructible_v<_Error>) { |
| return just_error((_Error&&)__err2); |
| }); |
| } |
| |
| template <__movable_value _Error> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| auto operator()(_Error __err) const |
| -> __binder_back<stopped_as_error_t, _Error> |
| { |
| return {{}, {}, {(_Error&&)__err}}; |
| } |
| }; |
| } // namespace __stopped_as_xxx |
| |
| using __stopped_as_xxx::stopped_as_optional_t; |
| inline constexpr stopped_as_optional_t stopped_as_optional{}; |
| using __stopped_as_xxx::stopped_as_error_t; |
| inline constexpr stopped_as_error_t stopped_as_error{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // run_loop |
| namespace __loop |
| { |
| class run_loop; |
| |
| struct __task : __immovable |
| { |
| __task* __next_ = this; |
| |
| union |
| { |
| void (*__execute_)(__task*) noexcept; |
| __task* __tail_; |
| }; |
| |
| void __execute() noexcept |
| { |
| (*__execute_)(this); |
| } |
| }; |
| |
| template <class _ReceiverId> |
| struct __operation |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t : __task |
| { |
| using __id = __operation; |
| |
| run_loop* __loop_; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_; |
| |
| static void __execute_impl(__task* __p) noexcept |
| { |
| auto& __rcvr = ((__t*)__p)->__rcvr_; |
| try |
| { |
| if (get_stop_token(get_env(__rcvr)).stop_requested()) |
| { |
| set_stopped((_Receiver&&)__rcvr); |
| } |
| else |
| { |
| set_value((_Receiver&&)__rcvr); |
| } |
| } |
| catch (...) |
| { |
| set_error((_Receiver&&)__rcvr, std::current_exception()); |
| } |
| } |
| |
| explicit __t(__task* __tail) noexcept : __task{.__tail_ = __tail} {} |
| |
| __t(__task* __next, run_loop* __loop, _Receiver __rcvr) : |
| __task{{}, __next, {&__execute_impl}}, __loop_{__loop}, |
| __rcvr_{(_Receiver&&)__rcvr} |
| {} |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| __self.__start_(); |
| } |
| |
| void __start_() noexcept; |
| }; |
| }; |
| |
| class run_loop |
| { |
| template <class... Ts> |
| using __completion_signatures_ = completion_signatures<Ts...>; |
| |
| template <class> |
| friend struct __operation; |
| |
| public: |
| struct __scheduler |
| { |
| using __t = __scheduler; |
| using __id = __scheduler; |
| bool operator==(const __scheduler&) const noexcept = default; |
| |
| private: |
| struct __schedule_task |
| { |
| using __t = __schedule_task; |
| using __id = __schedule_task; |
| using is_sender = void; |
| using completion_signatures = // |
| __completion_signatures_<set_value_t(), |
| set_error_t(std::exception_ptr), |
| set_stopped_t()>; |
| |
| private: |
| friend __scheduler; |
| |
| template <class _Receiver> |
| using __operation = |
| stdexec::__t<__operation<stdexec::__id<_Receiver>>>; |
| |
| template <class _Receiver> |
| friend __operation<_Receiver> |
| tag_invoke(connect_t, const __schedule_task& __self, |
| _Receiver __rcvr) |
| { |
| return __self.__connect_((_Receiver&&)__rcvr); |
| } |
| |
| template <class _Receiver> |
| __operation<_Receiver> __connect_(_Receiver&& __rcvr) const |
| { |
| return {&__loop_->__head_, __loop_, (_Receiver&&)__rcvr}; |
| } |
| |
| struct __env |
| { |
| run_loop* __loop_; |
| |
| template <class _CPO> |
| friend __scheduler tag_invoke(get_completion_scheduler_t<_CPO>, |
| const __env& __self) noexcept |
| { |
| return __self.__loop_->get_scheduler(); |
| } |
| }; |
| |
| friend __env tag_invoke(get_env_t, |
| const __schedule_task& __self) noexcept |
| { |
| return __env{__self.__loop_}; |
| } |
| |
| explicit __schedule_task(run_loop* __loop) noexcept : |
| __loop_(__loop) |
| {} |
| |
| run_loop* const __loop_; |
| }; |
| |
| friend run_loop; |
| |
| explicit __scheduler(run_loop* __loop) noexcept : __loop_(__loop) {} |
| |
| friend __schedule_task tag_invoke(schedule_t, |
| const __scheduler& __self) noexcept |
| { |
| return __self.__schedule(); |
| } |
| |
| friend stdexec::forward_progress_guarantee |
| tag_invoke(get_forward_progress_guarantee_t, |
| const __scheduler&) noexcept |
| { |
| return stdexec::forward_progress_guarantee::parallel; |
| } |
| |
| // BUGBUG NOT TO SPEC |
| friend bool tag_invoke(execute_may_block_caller_t, |
| const __scheduler&) noexcept |
| { |
| return false; |
| } |
| |
| __schedule_task __schedule() const noexcept |
| { |
| return __schedule_task{__loop_}; |
| } |
| |
| run_loop* __loop_; |
| }; |
| |
| __scheduler get_scheduler() noexcept |
| { |
| return __scheduler{this}; |
| } |
| |
| void run(); |
| |
| void finish(); |
| |
| private: |
| void __push_back_(__task* __task); |
| __task* __pop_front_(); |
| |
| std::mutex __mutex_; |
| std::condition_variable __cv_; |
| __task __head_{.__tail_ = &__head_}; |
| bool __stop_ = false; |
| }; |
| |
| template <class _ReceiverId> |
| inline void __operation<_ReceiverId>::__t::__start_() noexcept |
| { |
| try |
| { |
| __loop_->__push_back_(this); |
| } |
| catch (...) |
| { |
| set_error((_Receiver&&)__rcvr_, std::current_exception()); |
| } |
| } |
| |
| inline void run_loop::run() |
| { |
| for (__task* __task; (__task = __pop_front_()) != &__head_;) |
| { |
| __task->__execute(); |
| } |
| } |
| |
| inline void run_loop::finish() |
| { |
| std::unique_lock __lock{__mutex_}; |
| __stop_ = true; |
| __cv_.notify_all(); |
| } |
| |
| inline void run_loop::__push_back_(__task* __task) |
| { |
| std::unique_lock __lock{__mutex_}; |
| __task->__next_ = &__head_; |
| __head_.__tail_ = __head_.__tail_->__next_ = __task; |
| __cv_.notify_one(); |
| } |
| |
| inline __task* run_loop::__pop_front_() |
| { |
| std::unique_lock __lock{__mutex_}; |
| __cv_.wait(__lock, |
| [this] { return __head_.__next_ != &__head_ || __stop_; }); |
| if (__head_.__tail_ == __head_.__next_) |
| __head_.__tail_ = &__head_; |
| return std::exchange(__head_.__next_, __head_.__next_->__next_); |
| } |
| } // namespace __loop |
| |
| // NOT TO SPEC |
| using run_loop = __loop::run_loop; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.schedule_from] |
| namespace __schedule_from |
| { |
| // 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<set_stopped_t>, |
| // tuple<set_value_t, __decay_t<_Values1>...>, |
| // tuple<set_value_t, __decay_t<_Values2>...>, |
| // ... |
| // tuple<set_error_t, __decay_t<_Error1>>, |
| // tuple<set_error_t, __decay_t<_Error2>>, |
| // ... |
| // > |
| template <class _State, class... _Tuples> |
| using __make_bind_ = __mbind_back<_State, _Tuples...>; |
| |
| template <class _State> |
| using __make_bind = __mbind_front_q<__make_bind_, _State>; |
| |
| template <class _Tag> |
| using __tuple_t = __mbind_front_q<__decayed_tuple, _Tag>; |
| |
| template <class _Sender, class _Env, class _State, class _Tag> |
| using __bind_completions_t = |
| __gather_completions_for<_Tag, _Sender, _Env, __tuple_t<_Tag>, |
| __make_bind<_State>>; |
| |
| template <class _Sender, class _Env> |
| using __variant_for_t = // |
| __minvoke<__minvoke< |
| __mfold_right<__nullable_variant_t, |
| __mbind_front_q<__bind_completions_t, _Sender, _Env>>, |
| set_value_t, set_error_t, set_stopped_t>>; |
| |
| template <class _SchedulerId, class _VariantId, class _ReceiverId> |
| struct __operation1_base; |
| |
| // This receiver is to be completed on the execution context |
| // associated with the scheduler. When the source sender |
| // completes, the completion information is saved off in the |
| // operation state so that when this receiver completes, it can |
| // read the completion out of the operation state and forward it |
| // to the output receiver after transitioning to the scheduler's |
| // context. |
| template <class _SchedulerId, class _VariantId, class _ReceiverId> |
| struct __receiver2 |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver2; |
| __operation1_base<_SchedulerId, _VariantId, _ReceiverId>* __op_state_; |
| |
| // If the work is successfully scheduled on the new execution |
| // context and is ready to run, forward the completion signal in |
| // the operation state |
| template <same_as<set_value_t> _Tag> |
| friend void tag_invoke(_Tag, __t&& __self) noexcept |
| { |
| __self.__op_state_->__complete(); |
| } |
| |
| template <__one_of<set_error_t, set_stopped_t> _Tag, class... _As> |
| requires __callable<_Tag, _Receiver, _As...> |
| friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept |
| { |
| _Tag{}((_Receiver&&)__self.__op_state_->__rcvr_, (_As&&)__as...); |
| } |
| |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| -> env_of_t<_Receiver> |
| { |
| return get_env(__self.__op_state_->__rcvr_); |
| } |
| }; |
| }; |
| |
| // This receiver is connected to the input sender. When that |
| // sender completes (on whatever context it completes on), save |
| // the completion information into the operation state. Then, |
| // schedule a second operation to __complete on the execution |
| // context of the scheduler. That second receiver will read the |
| // completion information out of the operation state and propagate |
| // it to the output receiver from within the desired context. |
| template <class _SchedulerId, class _VariantId, class _ReceiverId> |
| struct __receiver1 |
| { |
| using _Scheduler = stdexec::__t<_SchedulerId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using __receiver2_t = |
| stdexec::__t<__receiver2<_SchedulerId, _VariantId, _ReceiverId>>; |
| |
| struct __t |
| { |
| using __id = __receiver1; |
| using is_receiver = void; |
| __operation1_base<_SchedulerId, _VariantId, _ReceiverId>* __op_state_; |
| |
| template <class... _Args> |
| static constexpr bool __nothrow_complete_ = |
| (__nothrow_decay_copyable<_Args> && ...); |
| |
| template <class _Tag, class... _Args> |
| static void __complete_(_Tag, __t&& __self, _Args&&... __args) // |
| noexcept(__nothrow_complete_<_Args...>) |
| { |
| // Write the tag and the args into the operation state so that |
| // we can forward the completion from within the scheduler's |
| // execution context. |
| __self.__op_state_->__data_ |
| .template emplace<__decayed_tuple<_Tag, _Args...>>( |
| _Tag{}, (_Args&&)__args...); |
| // Enqueue the schedule operation so the completion happens |
| // on the scheduler's execution context. |
| start(__self.__op_state_->__state2_); |
| } |
| |
| template <__completion_tag _Tag, class... _Args> |
| requires __callable<_Tag, _Receiver, __decay_t<_Args>...> |
| friend void tag_invoke(_Tag __tag, __t&& __self, |
| _Args&&... __args) noexcept |
| { |
| __try_call((_Receiver&&)__self.__op_state_->__rcvr_, |
| __function_constant_v<__complete_<_Tag, _Args...>>, |
| (_Tag&&)__tag, (__t&&)__self, (_Args&&)__args...); |
| } |
| |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| -> env_of_t<_Receiver> |
| { |
| return get_env(__self.__op_state_->__rcvr_); |
| } |
| }; |
| }; |
| |
| template <class _SchedulerId, class _VariantId, class _ReceiverId> |
| struct __operation1_base : __immovable |
| { |
| using _Scheduler = stdexec::__t<_SchedulerId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using _Variant = stdexec::__t<_VariantId>; |
| using __receiver2_t = |
| stdexec::__t<__receiver2<_SchedulerId, _VariantId, _ReceiverId>>; |
| |
| _Scheduler __sched_; |
| _Receiver __rcvr_; |
| _Variant __data_; |
| connect_result_t<schedule_result_t<_Scheduler>, __receiver2_t> __state2_; |
| |
| __operation1_base(_Scheduler __sched, _Receiver&& __rcvr) : |
| __sched_((_Scheduler&&)__sched), __rcvr_((_Receiver&&)__rcvr), |
| __state2_(connect(schedule(__sched_), __receiver2_t{this})) |
| {} |
| |
| void __complete() noexcept |
| { |
| STDEXEC_ASSERT(!__data_.valueless_by_exception()); |
| std::visit( |
| [this]<class _Tup>(_Tup& __tupl) -> void { |
| if constexpr (same_as<_Tup, std::monostate>) |
| { |
| std::terminate(); // reaching this indicates a bug in |
| // schedule_from |
| } |
| else |
| { |
| __apply( |
| [&]<class... _Args>(auto __tag, _Args&... __args) -> void { |
| __tag((_Receiver&&)__rcvr_, (_Args&&)__args...); |
| }, |
| __tupl); |
| } |
| }, |
| __data_); |
| } |
| }; |
| |
| template <class _SchedulerId, class _CvrefSenderId, class _ReceiverId> |
| struct __operation1 |
| { |
| using _Scheduler = stdexec::__t<_SchedulerId>; |
| using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using __variant_t = __variant_for_t<_CvrefSender, env_of_t<_Receiver>>; |
| using __receiver1_t = stdexec::__t< |
| __receiver1<_SchedulerId, stdexec::__id<__variant_t>, _ReceiverId>>; |
| using __base_t = __operation1_base<_SchedulerId, stdexec::__id<__variant_t>, |
| _ReceiverId>; |
| |
| struct __t : __base_t |
| { |
| using __id = __operation1; |
| connect_result_t<_CvrefSender, __receiver1_t> __state1_; |
| |
| __t(_Scheduler __sched, _CvrefSender&& __sndr, _Receiver&& __rcvr) : |
| __base_t{(_Scheduler&&)__sched, (_Receiver&&)__rcvr}, |
| __state1_(connect((_CvrefSender&&)__sndr, __receiver1_t{this})) |
| {} |
| |
| friend void tag_invoke(start_t, __t& __op_state) noexcept |
| { |
| start(__op_state.__state1_); |
| } |
| }; |
| }; |
| |
| template <class _Tp> |
| using __decay_rvalue_ref = __decay_t<_Tp>&&; |
| |
| template <class _Tag> |
| using __decay_signature = |
| __transform<__q<__decay_rvalue_ref>, |
| __mcompose<__q<completion_signatures>, __qf<_Tag>>>; |
| |
| template <class _SchedulerId> |
| struct __environ |
| { |
| struct __t : |
| __env::__prop<stdexec::__t<_SchedulerId>( |
| get_completion_scheduler_t<set_value_t>, |
| get_completion_scheduler_t<set_stopped_t>)> |
| { |
| using __id = __environ; |
| |
| template <same_as<get_domain_t> _Key> |
| friend auto tag_invoke(_Key, const __t& __self) noexcept |
| { |
| return query_or(get_domain, __self.__value_, default_domain()); |
| } |
| }; |
| }; |
| |
| template <class... _Ts> |
| using __all_nothrow_decay_copyable = |
| __mbool<(__nothrow_decay_copyable<_Ts> && ...)>; |
| |
| template <class _CvrefSender, class _Env> |
| using __all_values_and_errors_nothrow_decay_copyable = // |
| __mand< |
| __try_error_types_of_t<_CvrefSender, _Env, |
| __q<__all_nothrow_decay_copyable>>, |
| __try_value_types_of_t<_CvrefSender, _Env, |
| __q<__all_nothrow_decay_copyable>, __q<__mand>>>; |
| |
| template <class _CvrefSender, class _Env> |
| using __with_error_t = // |
| __if<__all_values_and_errors_nothrow_decay_copyable<_CvrefSender, _Env>, |
| completion_signatures<>, __with_exception_ptr>; |
| |
| template <class _Scheduler, class _CvrefSender, class _Env> |
| using __completions_t = // |
| __try_make_completion_signatures< |
| _CvrefSender, _Env, |
| __try_make_completion_signatures<schedule_result_t<_Scheduler>, _Env, |
| __with_error_t<_CvrefSender, _Env>, |
| __mconst<completion_signatures<>>>, |
| __decay_signature<set_value_t>, __decay_signature<set_error_t>>; |
| |
| inline auto __get_env_fn() noexcept |
| { |
| return [](__ignore, const auto& __data, |
| const auto& __child) noexcept -> decltype(auto) { |
| return __join_env(__data, stdexec::get_env(__child)); |
| }; |
| } |
| |
| struct schedule_from_t |
| { |
| template <scheduler _Scheduler, sender _Sender> |
| auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const |
| { |
| using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>; |
| auto __env = _Env{{(_Scheduler&&)__sched}}; |
| auto __domain = query_or(get_domain, __sched, default_domain()); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<schedule_from_t>(std::move(__env), (_Sender&&)__sndr)); |
| } |
| |
| using _Sender = __1; |
| using _Env = __0; |
| using __legacy_customizations_t = __types<tag_invoke_t( |
| schedule_from_t, get_completion_scheduler_t<set_value_t>(_Env&), |
| _Sender)>; |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <class _Sender> |
| using __env_t = |
| __sexpr_apply_result_t<const _Sender&, __result_of<__get_env_fn>>; |
| |
| template <class _Sender> |
| using __scheduler_t = |
| __decay_t<__call_result_t<get_completion_scheduler_t<set_value_t>, |
| __env_t<_Sender>>>; |
| |
| template <class _Sender, class _Receiver> |
| using __receiver_t = // |
| stdexec::__t<__receiver1<stdexec::__id<__scheduler_t<_Sender>>, |
| stdexec::__id<__variant_for_t< |
| __child_of<_Sender>, env_of_t<_Receiver>>>, |
| stdexec::__id<_Receiver>>>; |
| |
| template <class _Sender, class _Receiver> |
| using __operation_t = // |
| stdexec::__t<__operation1< // |
| stdexec::__id<__scheduler_t<_Sender>>, |
| stdexec::__cvref_id<__child_of<_Sender>>, |
| stdexec::__id<_Receiver>>>; |
| |
| template <sender_expr_for<schedule_from_t> _Sender> |
| static __env_t<_Sender> get_env(const _Sender& __sndr) noexcept |
| { |
| return __sexpr_apply(__sndr, __get_env_fn()); |
| } |
| |
| template <sender_expr_for<schedule_from_t> _Sender, class _Env> |
| static auto get_completion_signatures(_Sender&&, const _Env&) noexcept |
| -> __completions_t<__scheduler_t<_Sender>, __child_of<_Sender>, _Env> |
| { |
| return {}; |
| } |
| |
| template <sender_expr_for<schedule_from_t> _Sender, receiver _Receiver> |
| requires sender_to<__child_of<_Sender>, |
| __receiver_t<_Sender, _Receiver>> |
| static auto connect(_Sender&& __sndr, _Receiver __rcvr) // |
| -> __operation_t<_Sender, _Receiver> |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| [&]<class _Data, class _Child>( |
| __ignore, _Data&& __data, _Child&& __child) |
| -> __operation_t<_Sender, _Receiver> { |
| auto __sched = get_completion_scheduler<set_value_t>(__data); |
| return {__sched, (_Child&&)__child, (_Receiver&&)__rcvr}; |
| }); |
| } |
| }; |
| } // namespace __schedule_from |
| |
| using __schedule_from::schedule_from_t; |
| inline constexpr schedule_from_t schedule_from{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.transfer] |
| namespace __transfer |
| { |
| using __schedule_from::__environ; |
| |
| template <class _Env> |
| using __scheduler_t = __result_of<get_completion_scheduler<set_value_t>, _Env>; |
| |
| template <class _Sender> |
| using __lowered_t = // |
| __result_of<schedule_from, __scheduler_t<__data_of<_Sender>>, |
| __child_of<_Sender>>; |
| |
| inline auto __get_env_fn() noexcept |
| { |
| return [](__ignore, const auto& __data, const auto& __child) noexcept { |
| return __join_env(__data, stdexec::get_env(__child)); |
| }; |
| } |
| |
| struct transfer_t |
| { |
| template <sender _Sender, scheduler _Scheduler> |
| auto operator()(_Sender&& __sndr, _Scheduler&& __sched) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>; |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<transfer_t>(_Env{{(_Scheduler&&)__sched}}, |
| (_Sender&&)__sndr)); |
| } |
| |
| template <scheduler _Scheduler> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<transfer_t, __decay_t<_Scheduler>> |
| operator()(_Scheduler&& __sched) const |
| { |
| return {{}, {}, {(_Scheduler&&)__sched}}; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////// |
| using _Env = __0; |
| using _Sender = __1; |
| using __legacy_customizations_t = // |
| __types<tag_invoke_t(transfer_t, |
| get_completion_scheduler_t<set_value_t>( |
| get_env_t(const _Sender&)), |
| _Sender, |
| get_completion_scheduler_t<set_value_t>(_Env)), |
| tag_invoke_t(transfer_t, _Sender, |
| get_completion_scheduler_t<set_value_t>(_Env))>; |
| |
| template <sender_expr_for<transfer_t> _Sender> |
| static decltype(auto) get_env(const _Sender& __sndr) noexcept |
| { |
| return __sexpr_apply(__sndr, __get_env_fn()); |
| } |
| |
| template <class _Env> |
| static auto __transform_sender_fn(const _Env&) |
| { |
| return [&]<class _Data, class _Child>(__ignore, _Data&& __data, |
| _Child&& __child) { |
| auto __sched = get_completion_scheduler<set_value_t>(__data); |
| return schedule_from(std::move(__sched), (_Child&&)__child); |
| }; |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply((_Sender&&)__sndr, __transform_sender_fn(__env)); |
| } |
| }; |
| } // namespace __transfer |
| |
| using __transfer::transfer_t; |
| inline constexpr transfer_t transfer{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.transfer_just] |
| namespace __transfer_just |
| { |
| // This is a helper for finding legacy cusutomizations of transfer_just. |
| inline auto __transfer_just_tag_invoke() |
| { |
| return []<class... _Ts>( |
| _Ts&&... __ts) -> tag_invoke_result_t<transfer_just_t, _Ts...> { |
| return tag_invoke(transfer_just, (_Ts&&)__ts...); |
| }; |
| } |
| |
| inline auto __make_env_fn() noexcept |
| { |
| return []<class _Scheduler>(const _Scheduler& __sched, |
| const auto&...) noexcept { |
| using _Env = __t<__schedule_from::__environ<__id<_Scheduler>>>; |
| return _Env{{__sched}}; |
| }; |
| } |
| |
| inline auto __get_env_fn() noexcept |
| { |
| return [](__ignore, const auto& __data) noexcept { |
| return __apply(__make_env_fn(), __data); |
| }; |
| } |
| |
| template <class _Env> |
| auto __make_transform_fn(const _Env& __env) |
| { |
| return [&]<class _Scheduler, class... _Values>(_Scheduler&& __sched, |
| _Values&&... __vals) { |
| return transfer(just((_Values&&)__vals...), (_Scheduler&&)__sched); |
| }; |
| } |
| |
| template <class _Env> |
| auto __transform_sender_fn(const _Env& __env) |
| { |
| return [&]<class _Data>(__ignore, _Data&& __data) { |
| return __apply(__make_transform_fn(__env), (_Data&&)__data); |
| }; |
| } |
| |
| struct transfer_just_t |
| { |
| using _Data = __0; |
| using __legacy_customizations_t = // |
| __types<__apply_t(decltype(__transfer_just_tag_invoke()), _Data)>; |
| |
| template <scheduler _Scheduler, __movable_value... _Values> |
| auto operator()(_Scheduler&& __sched, _Values&&... __vals) const |
| { |
| auto __domain = query_or(get_domain, __sched, default_domain()); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<transfer_just_t>(std::tuple{ |
| (_Scheduler&&)__sched, (_Values&&)__vals...})); |
| } |
| |
| template <sender_expr_for<transfer_just_t> _Sender> |
| static auto get_env(const _Sender& __sndr) noexcept |
| { |
| return __sexpr_apply((_Sender&&)__sndr, __get_env_fn()); |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply((_Sender&&)__sndr, __transform_sender_fn(__env)); |
| } |
| }; |
| } // namespace __transfer_just |
| |
| using __transfer_just::transfer_just_t; |
| inline constexpr transfer_just_t transfer_just{}; |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| // __write adaptor |
| namespace __write_ |
| { |
| template <class _ReceiverId, class _Env> |
| struct __operation_base |
| { |
| using _Receiver = __t<_ReceiverId>; |
| _Receiver __rcvr_; |
| const _Env __env_; |
| }; |
| |
| inline constexpr auto __get_env = [](auto* __op) noexcept -> decltype(auto) { |
| return (__op->__env_); |
| }; |
| |
| template <class _ReceiverId, class _Env> |
| using __receiver_t = // |
| __t<__detail::__receiver_with<&__operation_base<_ReceiverId, _Env>::__rcvr_, |
| __get_env>>; |
| |
| template <class _CvrefSenderId, class _ReceiverId, class _Env> |
| struct __operation : __operation_base<_ReceiverId, _Env> |
| { |
| using _CvrefSender = __cvref_t<_CvrefSenderId>; |
| using _Receiver = __t<_ReceiverId>; |
| using __base_t = __operation_base<_ReceiverId, _Env>; |
| using __receiver_t = __write_::__receiver_t<_ReceiverId, _Env>; |
| connect_result_t<_CvrefSender, __receiver_t> __state_; |
| |
| __operation(_CvrefSender&& __sndr, _Receiver&& __rcvr, auto&& __env) : |
| __base_t{(_Receiver&&)__rcvr, (decltype(__env))__env}, |
| __state_{ |
| stdexec::connect((_CvrefSender&&)__sndr, __receiver_t{{}, this})} |
| {} |
| |
| friend void tag_invoke(start_t, __operation& __self) noexcept |
| { |
| start(__self.__state_); |
| } |
| }; |
| |
| struct __write_t : __with_default_get_env<__write_t> |
| { |
| template <sender _Sender, class... _Envs> |
| auto operator()(_Sender&& __sndr, _Envs... __envs) const |
| { |
| return __make_sexpr<__write_t>(__join_env(std::move(__envs)...), |
| (_Sender&&)__sndr); |
| } |
| |
| template <class... _Envs> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| auto operator()(_Envs... __envs) const -> __binder_back<__write_t, _Envs...> |
| { |
| return {{}, {}, {std::move(__envs)...}}; |
| } |
| |
| template <class _Env> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| static auto __transform_env_fn(_Env&& __env) noexcept |
| { |
| return [&](__ignore, const auto& __data, __ignore) noexcept { |
| return __join_env(__data, (_Env&&)__env); |
| }; |
| } |
| |
| template <sender_expr_for<__write_t> _Self, class _Env> |
| static auto transform_env(const _Self& __self, _Env&& __env) noexcept |
| { |
| return __sexpr_apply(__self, __transform_env_fn((_Env&&)__env)); |
| } |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <class _Self, class _Receiver> |
| using __receiver_t = |
| __write_::__receiver_t<__id<_Receiver>, __decay_t<__data_of<_Self>>>; |
| |
| template <class _Self, class _Receiver> |
| using __operation_t = |
| __operation<__cvref_id<__child_of<_Self>>, __id<_Receiver>, |
| __decay_t<__data_of<_Self>>>; |
| |
| template <sender_expr_for<__write_t> _Self, receiver _Receiver> |
| requires sender_to<__child_of<_Self>, __receiver_t<_Self, _Receiver>> |
| static auto connect(_Self&& __self, _Receiver __rcvr) |
| -> __operation_t<_Self, _Receiver> |
| { |
| return __sexpr_apply((_Self&&)__self, |
| [&]<class _Env, class _Child>( |
| __write_t, _Env&& __env, _Child&& __child) // |
| -> __operation_t<_Self, _Receiver> { |
| return {(_Child&&)__child, (_Receiver&&)__rcvr, (_Env&&)__env}; |
| }); |
| } |
| |
| template <sender_expr_for<__write_t> _Self, class _Env> |
| static auto get_completion_signatures(_Self&&, _Env&&) |
| -> stdexec::__completion_signatures_of_t< |
| __child_of<_Self>, |
| __env::__env_join_t<const __decay_t<__data_of<_Self>>&, _Env>> |
| { |
| return {}; |
| } |
| }; |
| } // namespace __write_ |
| |
| using __write_::__write_t; |
| inline constexpr __write_t __write{}; |
| |
| namespace __detail |
| { |
| template <class _Scheduler> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| auto __mkenv_sched(_Scheduler __sched) |
| { |
| auto __env = __join_env(__mkprop(__sched, get_scheduler), |
| __mkprop(get_domain)); |
| |
| struct __env_t : decltype(__env) |
| {}; |
| |
| return __env_t{__env}; |
| } |
| } // namespace __detail |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.on] |
| namespace __on |
| { |
| template <class _SchedulerId, class _SenderId, class _ReceiverId> |
| struct __operation; |
| |
| template <class _SchedulerId, class _SenderId, class _ReceiverId> |
| struct __operation_base : __immovable |
| { |
| using _Scheduler = stdexec::__t<_SchedulerId>; |
| using _Sender = stdexec::__t<_SenderId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| _Scheduler __scheduler_; |
| _Sender __sndr_; |
| _Receiver __rcvr_; |
| }; |
| |
| inline constexpr auto __sched_prop = [](auto* __op) noexcept { |
| return __mkprop(__op->__scheduler_, get_scheduler); |
| }; |
| inline constexpr auto __domain_prop = [](auto*) noexcept { |
| return __mkprop(get_domain); |
| }; |
| |
| template <class _SchedulerId, class _SenderId, class _ReceiverId> |
| using __receiver_ref_t = __t<__detail::__receiver_with< |
| &__operation_base<_SchedulerId, _SenderId, _ReceiverId>::__rcvr_, |
| __sched_prop, __domain_prop>>; |
| |
| template <class _SchedulerId, class _SenderId, class _ReceiverId> |
| struct __receiver |
| { |
| using _Scheduler = stdexec::__t<_SchedulerId>; |
| using _Sender = stdexec::__t<_SenderId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t : receiver_adaptor<__t> |
| { |
| using __id = __receiver; |
| using __receiver_ref_t = |
| __on::__receiver_ref_t<_SchedulerId, _SenderId, _ReceiverId>; |
| stdexec::__t<__operation<_SchedulerId, _SenderId, _ReceiverId>>* |
| __op_state_; |
| |
| _Receiver&& base() && noexcept |
| { |
| return (_Receiver&&)__op_state_->__rcvr_; |
| } |
| |
| const _Receiver& base() const& noexcept |
| { |
| return __op_state_->__rcvr_; |
| } |
| |
| void set_value() && noexcept |
| { |
| // cache this locally since *this is going bye-bye. |
| auto* __op_state = __op_state_; |
| try |
| { |
| // This line will invalidate *this: |
| start(__op_state->__data_.template emplace<1>( |
| __conv{[__op_state] { |
| return connect((_Sender&&)__op_state->__sndr_, |
| __receiver_ref_t{{}, __op_state}); |
| }})); |
| } |
| catch (...) |
| { |
| set_error((_Receiver&&)__op_state->__rcvr_, |
| std::current_exception()); |
| } |
| } |
| }; |
| }; |
| |
| template <class _SchedulerId, class _SenderId, class _ReceiverId> |
| struct __operation |
| { |
| using _Scheduler = stdexec::__t<_SchedulerId>; |
| using _Sender = stdexec::__t<_SenderId>; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t : __operation_base<_SchedulerId, _SenderId, _ReceiverId> |
| { |
| using _Base = __operation_base<_SchedulerId, _SenderId, _ReceiverId>; |
| using __id = __operation; |
| using __receiver_t = |
| stdexec::__t<__receiver<_SchedulerId, _SenderId, _ReceiverId>>; |
| using __receiver_ref_t = |
| __on::__receiver_ref_t<_SchedulerId, _SenderId, _ReceiverId>; |
| using __schedule_sender_t = __result_of<schedule, _Scheduler>; |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| start(std::get<0>(__self.__data_)); |
| } |
| |
| template <class _Sender2, class _Receiver2> |
| __t(_Scheduler __sched, _Sender2&& __sndr, _Receiver2&& __rcvr) : |
| _Base{{}, |
| (_Scheduler&&)__sched, |
| (_Sender2&&)__sndr, |
| (_Receiver2&&)__rcvr}, |
| __data_{std::in_place_index<0>, __conv{[this] { |
| return connect(schedule(this->__scheduler_), |
| __receiver_t{{}, this}); |
| }}} |
| {} |
| |
| std::variant<connect_result_t<__schedule_sender_t, __receiver_t>, |
| connect_result_t<_Sender, __receiver_ref_t>> |
| __data_; |
| }; |
| }; |
| |
| template <class _SchedulerId, class _SenderId> |
| struct __sender; |
| |
| struct on_t; |
| |
| template <class _SchedulerId, class _SenderId> |
| struct __sender |
| { |
| using _Scheduler = stdexec::__t<_SchedulerId>; |
| using _Sender = stdexec::__t<_SenderId>; |
| |
| struct __t |
| { |
| using __id = __sender; |
| using is_sender = void; |
| |
| using __schedule_sender_t = __result_of<schedule, _Scheduler>; |
| template <class _ReceiverId> |
| using __receiver_ref_t = |
| __on::__receiver_ref_t<_SchedulerId, _SenderId, _ReceiverId>; |
| template <class _ReceiverId> |
| using __receiver_t = |
| stdexec::__t<__receiver<_SchedulerId, _SenderId, _ReceiverId>>; |
| template <class _ReceiverId> |
| using __operation_t = |
| stdexec::__t<__operation<_SchedulerId, _SenderId, _ReceiverId>>; |
| |
| _Scheduler __scheduler_; |
| _Sender __sndr_; |
| |
| template <__decays_to<__t> _Self, receiver _Receiver> |
| requires constructible_from<_Sender, |
| __copy_cvref_t<_Self, _Sender>> && |
| sender_to<__schedule_sender_t, |
| __receiver_t<stdexec::__id<_Receiver>>> && |
| sender_to<_Sender, |
| __receiver_ref_t<stdexec::__id<_Receiver>>> |
| friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) |
| -> __operation_t<stdexec::__id<_Receiver>> |
| { |
| return {((_Self&&)__self).__scheduler_, ((_Self&&)__self).__sndr_, |
| (_Receiver&&)__rcvr}; |
| } |
| |
| friend auto tag_invoke(get_env_t, const __t& __self) noexcept |
| -> env_of_t<const _Sender&> |
| { |
| return get_env(__self.__sndr_); |
| } |
| |
| template <__decays_to<__t> _Self, class _Env> |
| friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&) |
| -> __try_make_completion_signatures< |
| __schedule_sender_t, _Env, |
| __try_make_completion_signatures< |
| __copy_cvref_t<_Self, _Sender>, |
| __make_env_t<_Env, __with<get_scheduler_t, _Scheduler>>, |
| completion_signatures<set_error_t(std::exception_ptr)>>, |
| __mconst<completion_signatures<>>> |
| { |
| return {}; |
| } |
| |
| // BUGBUG better would be to port the `on` algorithm to __sexpr |
| template <class _Self, class _Fun, class _OnTag = on_t> |
| static auto apply(_Self&& __self, _Fun __fun) |
| -> __call_result_t<_Fun, _OnTag, __copy_cvref_t<_Self, _Scheduler>, |
| __copy_cvref_t<_Self, _Sender>> |
| { |
| return ((_Fun&&)__fun)(_OnTag(), ((_Self&&)__self).__scheduler_, |
| ((_Self&&)__self).__sndr_); |
| } |
| }; |
| }; |
| |
| struct on_t : __with_default_get_env<on_t> |
| { |
| using _Sender = __1; |
| using _Scheduler = __0; |
| using __legacy_customizations_t = |
| __types<tag_invoke_t(on_t, _Scheduler, _Sender)>; |
| |
| template <scheduler _Scheduler, sender _Sender> |
| auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const |
| { |
| auto __domain = query_or(get_domain, __sched, default_domain()); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<on_t>((_Scheduler&&)__sched, (_Sender&&)__sndr)); |
| } |
| |
| template <class _Scheduler, class _Sender> |
| using __sender_t = __t<__sender<stdexec::__id<__decay_t<_Scheduler>>, |
| stdexec::__id<__decay_t<_Sender>>>>; |
| |
| template <class _Env> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| static auto __transform_env_fn(_Env&& __env) noexcept |
| { |
| return [&](__ignore, auto __sched, __ignore) noexcept { |
| return __join_env(__detail::__mkenv_sched(__sched), (_Env&&)__env); |
| }; |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_env(const _Sender& __sndr, _Env&& __env) noexcept |
| { |
| return __sexpr_apply(__sndr, __transform_env_fn((_Env&&)__env)); |
| } |
| |
| template <class _Sender, class _Env> |
| requires __is_not_instance_of<__id<__decay_t<_Sender>>, __sender> |
| static auto transform_sender(_Sender&& __sndr, const _Env&) |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| []<class _Data, class _Child>( |
| __ignore, _Data&& __data, _Child&& __child) { |
| return __sender_t<_Data, _Child>{(_Data&&)__data, |
| (_Child&&)__child}; |
| }); |
| } |
| }; |
| } // namespace __on |
| |
| using __on::on_t; |
| inline constexpr on_t on{}; |
| |
| // BUGBUG this will also be unnecessary when `on` returns a __sexpr |
| namespace __detail |
| { |
| template <class _SchedulerId, class _SenderId> |
| extern __mconst<__on::__sender<__t<_SchedulerId>, __name_of<__t<_SenderId>>>> |
| __name_of_v<__on::__sender<_SchedulerId, _SenderId>>; |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.into_variant] |
| namespace __into_variant |
| { |
| template <class _Sender, class _Env> |
| requires sender_in<_Sender, _Env> |
| using __into_variant_result_t = value_types_of_t<_Sender, _Env>; |
| |
| template <class _ReceiverId, class _Variant> |
| struct __receiver |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver; |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| _Receiver __rcvr_; |
| |
| // Customize set_value by building a variant and passing the result |
| // to the base class |
| template <same_as<set_value_t> _Tag, class... _As> |
| requires constructible_from<_Variant, std::tuple<_As&&...>> |
| friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept |
| { |
| try |
| { |
| set_value((_Receiver&&)__self.__rcvr_, |
| _Variant{std::tuple<_As&&...>{(_As&&)__as...}}); |
| } |
| catch (...) |
| { |
| set_error((_Receiver&&)__self.__rcvr_, |
| std::current_exception()); |
| } |
| } |
| |
| template <same_as<set_error_t> _Tag, class _Error> |
| friend void tag_invoke(_Tag, __t&& __self, _Error&& __err) noexcept |
| { |
| set_error((_Receiver&&)__self.__rcvr_, (_Error&&)__err); |
| } |
| |
| template <same_as<set_stopped_t> _Tag> |
| friend void tag_invoke(_Tag, __t&& __self) noexcept |
| { |
| set_stopped((_Receiver&&)__self.__rcvr_); |
| } |
| |
| friend env_of_t<_Receiver> tag_invoke(get_env_t, |
| const __t& __self) noexcept |
| { |
| return get_env(__self.__rcvr_); |
| } |
| }; |
| }; |
| |
| template <class _Sender, class _Env> |
| using __variant_t = __try_value_types_of_t<_Sender, _Env>; |
| |
| template <class _Sender, class _Receiver> |
| using __receiver_t = // |
| stdexec::__t< |
| __receiver<__id<_Receiver>, __variant_t<_Sender, env_of_t<_Receiver>>>>; |
| |
| template <class _Variant> |
| using __variant_completions = |
| completion_signatures<set_value_t(_Variant), |
| set_error_t(std::exception_ptr)>; |
| |
| template <class _Sender, class _Env> |
| using __compl_sigs = // |
| __try_make_completion_signatures< |
| _Sender, _Env, |
| __meval<__variant_completions, __variant_t<_Sender, _Env>>, |
| __mconst<completion_signatures<>>>; |
| |
| struct into_variant_t : __with_default_get_env<into_variant_t> |
| { |
| template <sender _Sender> |
| auto operator()(_Sender&& __sndr) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<into_variant_t>(__(), std::move(__sndr))); |
| } |
| |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| auto operator()() const noexcept |
| { |
| return __binder_back<into_variant_t>{}; |
| } |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <sender_expr_for<into_variant_t> _Self, receiver _Receiver> |
| requires sender_to<__child_of<_Self>, |
| __receiver_t<__child_of<_Self>, _Receiver>> |
| static auto connect(_Self&& __self, _Receiver __rcvr) // |
| noexcept(__nothrow_connectable< |
| __child_of<_Self>, __receiver_t<__child_of<_Self>, _Receiver>>) |
| -> connect_result_t<__child_of<_Self>, |
| __receiver_t<__child_of<_Self>, _Receiver>> |
| { |
| return __sexpr_apply( |
| (_Self&&)__self, |
| [&]<class _Child>(__ignore, __ignore, _Child&& __child) { |
| return stdexec::connect( |
| (_Child&&)__child, |
| __receiver_t<_Child, _Receiver>{(_Receiver&&)__rcvr}); |
| }); |
| } |
| |
| template <sender_expr_for<into_variant_t> _Self, class _Env> |
| static auto get_completion_signatures(_Self&&, _Env&&) // |
| -> __compl_sigs<__child_of<_Self>, _Env> |
| { |
| return {}; |
| } |
| }; |
| } // namespace __into_variant |
| |
| using __into_variant::into_variant_t; |
| inline constexpr into_variant_t into_variant{}; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.when_all] |
| // [execution.senders.adaptors.when_all_with_variant] |
| namespace __when_all |
| { |
| enum __state_t |
| { |
| __started, |
| __error, |
| __stopped |
| }; |
| |
| struct __on_stop_requested |
| { |
| in_place_stop_source& __stop_source_; |
| |
| void operator()() noexcept |
| { |
| __stop_source_.request_stop(); |
| } |
| }; |
| |
| template <class _Env> |
| auto __make_env(_Env&& __env, in_place_stop_source& __stop_source) noexcept |
| { |
| return __join_env(__env::__env_fn{[&](get_stop_token_t) noexcept { |
| return __stop_source.get_token(); |
| }}, |
| (_Env&&)__env); |
| } |
| |
| template <class _Env> |
| using __env_t = // |
| decltype(__when_all::__make_env(__declval<_Env>(), |
| __declval<in_place_stop_source&>())); |
| |
| template <class _Tp> |
| using __decay_rvalue_ref = __decay_t<_Tp>&&; |
| |
| template <class _Sender, class _Env> |
| concept __max1_sender = sender_in<_Sender, _Env> && |
| __mvalid<__value_types_of_t, _Sender, _Env, |
| __mconst<int>, __msingle_or<void>>; |
| |
| template < |
| __mstring _Context = "In stdexec::when_all()..."__csz, |
| __mstring _Diagnostic = |
| "The given sender can complete successfully in more that one way. " |
| "Use stdexec::when_all_with_variant() instead."__csz> |
| struct _INVALID_WHEN_ALL_ARGUMENT_; |
| |
| template <class _Sender, class _Env> |
| using __too_many_value_completions_error = |
| __mexception<_INVALID_WHEN_ALL_ARGUMENT_<>, _WITH_SENDER_<_Sender>, |
| _WITH_ENVIRONMENT_<_Env>>; |
| |
| template <class _Sender, class _Env, class _ValueTuple, class... _Rest> |
| using __value_tuple_t = |
| __minvoke<__if_c<(0 == sizeof...(_Rest)), __mconst<_ValueTuple>, |
| __q<__too_many_value_completions_error>>, |
| _Sender, _Env>; |
| |
| template <class _Env, class _Sender> |
| using __single_values_of_t = // |
| __try_value_types_of_t<_Sender, _Env, |
| __transform<__q<__decay_rvalue_ref>, __q<__types>>, |
| __mbind_front_q<__value_tuple_t, _Sender, _Env>>; |
| |
| template <class _Env, class... _Senders> |
| using __set_values_sig_t = // |
| __meval<completion_signatures, |
| __minvoke<__mconcat<__qf<set_value_t>>, |
| __single_values_of_t<_Env, _Senders>...>>; |
| |
| template <class... _Args> |
| using __all_nothrow_decay_copyable = |
| __mbool<(__nothrow_decay_copyable<_Args> && ...)>; |
| |
| template <class _Env, class... _Senders> |
| using __all_value_and_error_args_nothrow_decay_copyable = // |
| __mand<__compl_sigs::__maybe_for_all_sigs< |
| __completion_signatures_of_t<_Senders, _Env>, |
| __q<__all_nothrow_decay_copyable>, __q<__mand>>...>; |
| |
| template <class _Env, class... _Senders> |
| using __completions_t = // |
| __concat_completion_signatures_t< |
| __if<__all_value_and_error_args_nothrow_decay_copyable<_Env, |
| _Senders...>, |
| completion_signatures<set_stopped_t()>, |
| completion_signatures<set_stopped_t(), |
| set_error_t(std::exception_ptr&&)>>, |
| __minvoke<__with_default<__mbind_front_q<__set_values_sig_t, _Env>, |
| completion_signatures<>>, |
| _Senders...>, |
| __try_make_completion_signatures< |
| _Senders, _Env, completion_signatures<>, |
| __mconst<completion_signatures<>>, |
| __mcompose<__q<completion_signatures>, __qf<set_error_t>, |
| __q<__decay_rvalue_ref>>>...>; |
| |
| struct __not_an_error |
| {}; |
| |
| struct __tie_fn |
| { |
| template <class... _Ty> |
| std::tuple<_Ty&...> operator()(_Ty&... __vals) noexcept |
| { |
| return std::tuple<_Ty&...>{__vals...}; |
| } |
| }; |
| |
| template <class _Tag, class _Receiver> |
| struct __complete_fn |
| { |
| _Receiver& __rcvr_; |
| |
| __complete_fn(_Tag, _Receiver& __rcvr) noexcept : __rcvr_(__rcvr) {} |
| |
| template <class _Ty, class... _Ts> |
| void operator()(_Ty& __t, _Ts&... __ts) const noexcept |
| { |
| if constexpr (!same_as<_Ty, __not_an_error>) |
| { |
| _Tag{}((_Receiver&&)__rcvr_, (_Ty&&)__t, (_Ts&&)__ts...); |
| } |
| } |
| |
| void operator()() const noexcept |
| { |
| _Tag{}((_Receiver&&)__rcvr_); |
| } |
| }; |
| |
| template <class _Receiver, class _ValuesTuple> |
| void __set_values(_Receiver& __rcvr, _ValuesTuple& __values) noexcept |
| { |
| __apply( |
| [&](auto&... __opt_vals) noexcept -> void { |
| __apply(__complete_fn{set_value, __rcvr}, |
| std::tuple_cat(__apply(__tie_fn{}, *__opt_vals)...)); |
| }, |
| __values); |
| } |
| |
| template <class _ReceiverId, class _ValuesTuple, class _ErrorsVariant> |
| struct __operation_base : __immovable |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| void __arrive() noexcept |
| { |
| if (0 == --__count_) |
| { |
| __complete(); |
| } |
| } |
| |
| void __complete() noexcept |
| { |
| // Stop callback is no longer needed. Destroy it. |
| __on_stop_.reset(); |
| // All child operations have completed and arrived at the barrier. |
| switch (__state_.load(std::memory_order_relaxed)) |
| { |
| case __started: |
| if constexpr (!same_as<_ValuesTuple, __ignore>) |
| { |
| // All child operations completed successfully: |
| __when_all::__set_values(__rcvr_, __values_); |
| } |
| break; |
| case __error: |
| if constexpr (!same_as<_ErrorsVariant, |
| std::variant<std::monostate>>) |
| { |
| // One or more child operations completed with an error: |
| std::visit(__complete_fn{set_error, __rcvr_}, __errors_); |
| } |
| break; |
| case __stopped: |
| stdexec::set_stopped((_Receiver&&)__rcvr_); |
| break; |
| default:; |
| } |
| } |
| |
| _Receiver __rcvr_; |
| std::atomic<std::size_t> __count_; |
| in_place_stop_source __stop_source_{}; |
| // Could be non-atomic here and atomic_ref everywhere except __completion_fn |
| std::atomic<__state_t> __state_{__started}; |
| _ErrorsVariant __errors_{}; |
| STDEXEC_ATTRIBUTE((no_unique_address)) _ValuesTuple __values_{}; |
| std::optional<typename stop_token_of_t< |
| env_of_t<_Receiver>&>::template callback_type<__on_stop_requested>> |
| __on_stop_{}; |
| }; |
| |
| template <std::size_t _Index, class _ReceiverId, class _ValuesTuple, |
| class _ErrorsVariant> |
| struct __receiver |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| template <class _Tuple> |
| using __tuple_type = |
| typename std::tuple_element_t<_Index, _Tuple>::value_type; |
| using _TupleType = |
| __minvoke<__with_default<__q<__tuple_type>, __ignore>, _ValuesTuple>; |
| |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver; |
| |
| template <class _Error> |
| void __set_error(_Error&& __err) noexcept |
| { |
| // TODO: What memory orderings are actually needed here? |
| if (__error != __op_state_->__state_.exchange(__error)) |
| { |
| __op_state_->__stop_source_.request_stop(); |
| // We won the race, free to write the error into the operation |
| // state without worry. |
| if constexpr (__nothrow_decay_copyable<_Error>) |
| { |
| __op_state_->__errors_.template emplace<__decay_t<_Error>>( |
| (_Error&&)__err); |
| } |
| else |
| { |
| try |
| { |
| __op_state_->__errors_ |
| .template emplace<__decay_t<_Error>>( |
| (_Error&&)__err); |
| } |
| catch (...) |
| { |
| __op_state_->__errors_ |
| .template emplace<std::exception_ptr>( |
| std::current_exception()); |
| } |
| } |
| } |
| } |
| |
| template <same_as<set_value_t> _Tag, class... _Values> |
| requires same_as<_ValuesTuple, __ignore> || |
| constructible_from<_TupleType, _Values...> |
| friend void tag_invoke(_Tag, __t&& __self, _Values&&... __vals) noexcept |
| { |
| if constexpr (!same_as<_ValuesTuple, __ignore>) |
| { |
| static_assert( |
| same_as<_TupleType, std::tuple<__decay_t<_Values>...>>, |
| "One of the senders in this when_all() is fibbing about what types it sends"); |
| // We only need to bother recording the completion values |
| // if we're not already in the "error" or "stopped" state. |
| if (__self.__op_state_->__state_ == __started) |
| { |
| if constexpr ((__nothrow_decay_copyable<_Values> && ...)) |
| { |
| std::get<_Index>(__self.__op_state_->__values_) |
| .emplace((_Values&&)__vals...); |
| } |
| else |
| { |
| try |
| { |
| std::get<_Index>(__self.__op_state_->__values_) |
| .emplace((_Values&&)__vals...); |
| } |
| catch (...) |
| { |
| __self.__set_error(std::current_exception()); |
| } |
| } |
| } |
| } |
| __self.__op_state_->__arrive(); |
| } |
| |
| template <same_as<set_error_t> _Tag, class _Error> |
| requires requires(_ErrorsVariant& __errors, _Error&& __err) { |
| __errors.template emplace<__decay_t<_Error>>( |
| (_Error&&)__err); |
| } |
| friend void tag_invoke(_Tag, __t&& __self, _Error&& __err) noexcept |
| { |
| __self.__set_error((_Error&&)__err); |
| __self.__op_state_->__arrive(); |
| } |
| |
| template <same_as<set_stopped_t> _Tag> |
| requires receiver_of<_Receiver, completion_signatures<_Tag()>> |
| friend void tag_invoke(_Tag, __t&& __self) noexcept |
| { |
| __state_t __expected = __started; |
| // Transition to the "stopped" state if and only if we're in the |
| // "started" state. (If this fails, it's because we're in an |
| // error state, which trumps cancellation.) |
| if (__self.__op_state_->__state_.compare_exchange_strong(__expected, |
| __stopped)) |
| { |
| __self.__op_state_->__stop_source_.request_stop(); |
| } |
| __self.__op_state_->__arrive(); |
| } |
| |
| friend __env_t<env_of_t<_Receiver>> |
| tag_invoke(get_env_t, const __t& __self) noexcept |
| { |
| return __when_all::__make_env(get_env(__self.__op_state_->__rcvr_), |
| __self.__op_state_->__stop_source_); |
| } |
| |
| __operation_base<_ReceiverId, _ValuesTuple, _ErrorsVariant>* |
| __op_state_; |
| }; |
| }; |
| |
| template <class _Env, class _Sender> |
| using __values_opt_tuple_t = // |
| __value_types_of_t<_Sender, __env_t<_Env>, |
| __mcompose<__q<std::optional>, __q<__decayed_tuple>>, |
| __q<__msingle>>; |
| |
| template <class _Env, __max1_sender<__env_t<_Env>>... _Senders> |
| struct __traits_ |
| { |
| // tuple<optional<tuple<Vs1...>>, optional<tuple<Vs2...>>, ...> |
| using __values_tuple = // |
| __minvoke<__with_default< |
| __transform<__mbind_front_q<__values_opt_tuple_t, _Env>, |
| __q<std::tuple>>, |
| __ignore>, |
| _Senders...>; |
| |
| using __nullable_variant_t_ = |
| __munique<__mbind_front_q<std::variant, __not_an_error>>; |
| |
| using __error_types = // |
| __minvoke<__mconcat<__transform<__q<__decay_t>, __nullable_variant_t_>>, |
| error_types_of_t<_Senders, __env_t<_Env>, __types>...>; |
| |
| using __errors_variant = // |
| __if<__all_value_and_error_args_nothrow_decay_copyable<_Env, |
| _Senders...>, |
| __error_types, |
| __minvoke<__push_back_unique<__q<std::variant>>, __error_types, |
| std::exception_ptr>>; |
| }; |
| |
| template <receiver _Receiver, |
| __max1_sender<__env_t<env_of_t<_Receiver>>>... _Senders> |
| struct __traits : __traits_<env_of_t<_Receiver>, _Senders...> |
| { |
| using _Traits = __traits_<env_of_t<_Receiver>, _Senders...>; |
| using typename _Traits::__errors_variant; |
| using typename _Traits::__values_tuple; |
| |
| template <std::size_t _Index> |
| using __receiver = |
| __t<__when_all::__receiver<_Index, __id<_Receiver>, __values_tuple, |
| __errors_variant>>; |
| |
| using __operation_base = |
| __when_all::__operation_base<__id<_Receiver>, __values_tuple, |
| __errors_variant>; |
| |
| template <class _Sender, class _Index> |
| using __op_state = connect_result_t<_Sender, __receiver<__v<_Index>>>; |
| |
| template <class _Tuple = __q<std::tuple>> |
| using __op_states_tuple = // |
| __minvoke<__mzip_with2<__q<__op_state>, _Tuple>, __types<_Senders...>, |
| __mindex_sequence_for<_Senders...>>; |
| }; |
| |
| template <class _Cvref, class _ReceiverId, class... _SenderIds> |
| using __traits_ex = |
| __traits<__t<_ReceiverId>, __minvoke<_Cvref, __t<_SenderIds>>...>; |
| |
| template <class _Cvref, class _ReceiverId, class... _SenderIds> |
| using __op_states_tuple_ex = |
| typename __traits_ex<_Cvref, _ReceiverId, |
| _SenderIds...>::template __op_states_tuple<>; |
| |
| template <class _Cvref, class _ReceiverId, class... _SenderIds> |
| requires __mvalid<__op_states_tuple_ex, _Cvref, _ReceiverId, _SenderIds...> |
| struct __operation |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| using _Traits = __traits_ex<_Cvref, _ReceiverId, _SenderIds...>; |
| |
| using __operation_base_t = typename _Traits::__operation_base; |
| using __op_states_tuple_t = |
| __op_states_tuple_ex<_Cvref, _ReceiverId, _SenderIds...>; |
| |
| template <std::size_t _Index> |
| using __receiver_t = typename _Traits::template __receiver<_Index>; |
| |
| struct __t : __operation_base_t |
| { |
| using __id = __operation; |
| |
| template <std::size_t... _Is, class... _Senders> |
| __t(_Receiver __rcvr, __indices<_Is...>, _Senders&&... __sndrs) : |
| __operation_base_t{{}, (_Receiver&&)__rcvr, {sizeof...(_Is)}}, |
| __op_states_{__conv{[&, this]() { |
| return stdexec::connect((_Senders&&)__sndrs, |
| __receiver_t<_Is>{this}); |
| }}...} |
| {} |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| // register stop callback: |
| __self.__on_stop_.emplace( |
| get_stop_token(get_env(__self.__rcvr_)), |
| __on_stop_requested{__self.__stop_source_}); |
| if (__self.__stop_source_.stop_requested()) |
| { |
| // Stop has already been requested. Don't bother starting |
| // the child operations. |
| stdexec::set_stopped((_Receiver&&)__self.__rcvr_); |
| } |
| else |
| { |
| __apply( |
| [](auto&... __child_ops) noexcept -> void { |
| (stdexec::start(__child_ops), ...); |
| }, |
| __self.__op_states_); |
| if constexpr (sizeof...(_SenderIds) == 0) |
| { |
| __self.__complete(); |
| } |
| } |
| } |
| |
| __op_states_tuple_t __op_states_; |
| }; |
| }; |
| |
| struct _INVALID_ARGUMENTS_TO_WHEN_ALL_ |
| {}; |
| |
| struct when_all_t : __domain::__get_env_common_domain<when_all_t> |
| { |
| // Used by the default_domain to find legacy customizations: |
| using _Sender = __1; |
| using __legacy_customizations_t = // |
| __types<tag_invoke_t(when_all_t, _Sender...)>; |
| |
| // TODO: improve diagnostic when senders have different domains |
| template <sender... _Senders> |
| requires __domain::__has_common_domain<_Senders...> |
| auto operator()(_Senders&&... __sndrs) const |
| { |
| auto __domain = __domain::__common_domain_t<_Senders...>(); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<when_all_t>(__(), (_Senders&&)__sndrs...)); |
| } |
| |
| #if STDEXEC_FRIENDSHIP_IS_LEXICAL() |
| private: |
| template <class...> |
| friend struct stdexec::__sexpr; |
| #endif |
| |
| template <class _Self, class _Env> |
| using __error = __mexception<_INVALID_ARGUMENTS_TO_WHEN_ALL_, |
| __children_of<_Self, __q<_WITH_SENDERS_>>, |
| _WITH_ENVIRONMENT_<_Env>>; |
| |
| template <class _Self, class _Env> |
| using __completions = // |
| __children_of<_Self, __mbind_front_q<__completions_t, __env_t<_Env>>>; |
| |
| template <sender_expr_for<when_all_t> _Self, class _Env> |
| static auto get_completion_signatures(_Self&&, _Env&&) |
| { |
| return __minvoke<__mtry_catch<__q<__completions>, __q<__error>>, _Self, |
| _Env>(); |
| } |
| |
| template <class _Receiver, class _Indices> |
| struct __connect_fn; |
| |
| template <class _Receiver, std::size_t... _Indices> |
| struct __connect_fn<_Receiver, __indices<_Indices...>> |
| { |
| _Receiver* __rcvr_; |
| |
| template <std::size_t _Index, class... _Senders> |
| using __receiver_t = |
| typename __traits<_Receiver, |
| _Senders...>::template __receiver<_Index>; |
| |
| template <class... _Senders> |
| requires(sender_to<_Senders, __receiver_t<_Indices, _Senders...>> && |
| ...) |
| auto operator()(__ignore, __ignore, _Senders&&... __sndrs) const |
| { |
| using _Cvref = __copy_cvref_fn<__mfront<_Senders..., int>>; |
| using __operation_t = |
| __t<__operation<_Cvref, __id<_Receiver>, |
| __id<__decay_t<_Senders>>...>>; |
| return __operation_t{(_Receiver&&)*__rcvr_, |
| __indices<_Indices...>(), |
| (_Senders&&)__sndrs...}; |
| } |
| }; |
| |
| template <class _Self, class _Receiver> |
| using __connect_fn_for = // |
| __connect_fn<_Receiver, __make_indices<__nbr_children_of<_Self>>>; |
| |
| template <sender_expr_for<when_all_t> _Self, class _Receiver> |
| requires __callable<__sexpr_apply_t, _Self, |
| __connect_fn_for<_Self, _Receiver>> |
| static auto connect(_Self&& __self, _Receiver __rcvr) |
| { |
| using __connect_fn = __connect_fn_for<_Self, _Receiver>; |
| return __sexpr_apply((_Self&&)__self, __connect_fn{&__rcvr}); |
| } |
| }; |
| |
| struct when_all_with_variant_t : |
| __domain::__get_env_common_domain<when_all_with_variant_t> |
| { |
| using _Sender = __1; |
| using __legacy_customizations_t = // |
| __types<tag_invoke_t(when_all_with_variant_t, _Sender...)>; |
| |
| template <sender... _Senders> |
| requires __domain::__has_common_domain<_Senders...> |
| auto operator()(_Senders&&... __sndrs) const |
| { |
| auto __domain = __domain::__common_domain_t<_Senders...>(); |
| return stdexec::transform_sender(__domain, |
| __make_sexpr<when_all_with_variant_t>( |
| __(), (_Senders&&)__sndrs...)); |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| // transform the when_all_with_variant into a regular when_all (looking |
| // for early when_all customizations), then transform it again to look |
| // for late customizations. |
| return __sexpr_apply( |
| (_Sender&&)__sndr, |
| [&]<class... _Child>(__ignore, __ignore, _Child&&... __child) { |
| return when_all_t()(into_variant((_Child&&)__child)...); |
| }); |
| } |
| }; |
| |
| struct transfer_when_all_t |
| { |
| using _Env = __0; |
| using _Sender = __1; |
| using __legacy_customizations_t = // |
| __types<tag_invoke_t( |
| transfer_when_all_t, |
| get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>; |
| |
| template <scheduler _Scheduler, sender... _Senders> |
| requires __domain::__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_sexpr<transfer_when_all_t>(_Env{{(_Scheduler&&)__sched}}, |
| (_Senders&&)__sndrs...)); |
| } |
| |
| template <sender_expr_for<transfer_when_all_t> _Sender> |
| static __data_of<const _Sender&> get_env(const _Sender& __self) noexcept |
| { |
| return __sexpr_apply(__self, __detail::__get_data()); |
| } |
| |
| template <class _Sender, class _Env> |
| 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. |
| return __sexpr_apply( |
| (_Sender&&)__sndr, |
| [&]<class _Data, class... _Child>(__ignore, _Data&& __data, |
| _Child&&... __child) { |
| return transfer(when_all_t()((_Child&&)__child...), |
| get_completion_scheduler<set_value_t>(__data)); |
| }); |
| } |
| }; |
| |
| struct transfer_when_all_with_variant_t |
| { |
| using _Env = __0; |
| using _Sender = __1; |
| using __legacy_customizations_t = // |
| __types<tag_invoke_t( |
| transfer_when_all_with_variant_t, |
| get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>; |
| |
| template <scheduler _Scheduler, sender... _Senders> |
| requires __domain::__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_sexpr<transfer_when_all_with_variant_t>( |
| _Env{{(_Scheduler&&)__sched}}, (_Senders&&)__sndrs...)); |
| } |
| |
| template <sender_expr_for<transfer_when_all_with_variant_t> _Sender> |
| static __data_of<const _Sender&> get_env(const _Sender& __self) noexcept |
| { |
| return __sexpr_apply(__self, __detail::__get_data()); |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| // 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 __sexpr_apply( |
| (_Sender&&)__sndr, |
| [&]<class _Data, class... _Child>(__ignore, _Data&& __data, |
| _Child&&... __child) { |
| return transfer_when_all_t()( |
| get_completion_scheduler<set_value_t>((_Data&&)__data), |
| into_variant((_Child&&)__child)...); |
| }); |
| } |
| }; |
| } // namespace __when_all |
| |
| 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{}; |
| |
| namespace __read |
| { |
| template <class _Tag, class _ReceiverId> |
| using __result_t = __call_result_t<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>; |
| |
| template <class _Tag, class _ReceiverId> |
| concept __nothrow_t = |
| __nothrow_callable<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>; |
| |
| // NOT TO SPEC: if the query returns a value, store the value in the operation |
| // state and pass an rvalue reference to it. |
| template <class _Tag, class _ReceiverId> |
| struct __operation |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t : __immovable |
| { |
| using __id = __operation; |
| _Receiver __rcvr_; |
| std::optional<__result_t<_Tag, _ReceiverId>> __result_; |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| constexpr bool _Nothrow = |
| __nothrow_callable<_Tag, env_of_t<_Receiver>>; |
| auto __query = |
| [&]() noexcept(_Nothrow) -> __result_t<_Tag, _ReceiverId>&& { |
| __self.__result_.emplace(__conv{[&]() noexcept(_Nothrow) { |
| return _Tag()(get_env(__self.__rcvr_)); |
| }}); |
| return std::move(*__self.__result_); |
| }; |
| stdexec::__set_value_invoke(std::move(__self.__rcvr_), __query); |
| } |
| }; |
| }; |
| |
| // if the query returns a reference type, pass it straight through to the |
| // receiver. |
| template <class _Tag, class _ReceiverId> |
| requires std::same_as<__result_t<_Tag, _ReceiverId>&&, |
| __result_t<_Tag, _ReceiverId>> |
| struct __operation<_Tag, _ReceiverId> |
| { |
| using _Receiver = stdexec::__t<_ReceiverId>; |
| |
| struct __t : __immovable |
| { |
| using __id = __operation; |
| _Receiver __rcvr_; |
| |
| friend void tag_invoke(start_t, __t& __self) noexcept |
| { |
| stdexec::__set_value_invoke(std::move(__self.__rcvr_), _Tag(), |
| get_env(__self.__rcvr_)); |
| } |
| }; |
| }; |
| |
| inline constexpr __mstring __query_failed_diag = |
| "The current execution environment doesn't have a value for the given query."__csz; |
| |
| template <class _Tag> |
| struct _WITH_QUERY_; |
| |
| template <class _Tag, class _Env> |
| using __query_failed_error = // |
| __mexception< // |
| _NOT_CALLABLE_<"In stdexec::read()..."__csz, __query_failed_diag>, |
| _WITH_QUERY_<_Tag>, _WITH_ENVIRONMENT_<_Env>>; |
| |
| template <class _Tag, class _Env> |
| requires __callable<_Tag, _Env> |
| using __completions_t = // |
| __if_c<__nothrow_callable<_Tag, _Env>, |
| completion_signatures<set_value_t(__call_result_t<_Tag, _Env>)>, |
| completion_signatures<set_value_t(__call_result_t<_Tag, _Env>), |
| set_error_t(std::exception_ptr)>>; |
| |
| template <class _Tag> |
| struct __sender |
| { |
| using __t = __sender; |
| using __id = __sender; |
| using is_sender = void; |
| |
| template <class _Env> |
| using __completions_t = __minvoke< |
| __mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>, |
| _Tag, _Env>; |
| |
| template <class _Receiver> |
| requires receiver_of<_Receiver, __completions_t<env_of_t<_Receiver>>> |
| friend auto tag_invoke(connect_t, __sender, _Receiver __rcvr) // |
| noexcept(std::is_nothrow_move_constructible_v<_Receiver>) |
| -> stdexec::__t<__operation<_Tag, stdexec::__id<_Receiver>>> |
| { |
| return {{}, (_Receiver&&)__rcvr}; |
| } |
| |
| template <class _Env> |
| friend auto tag_invoke(get_completion_signatures_t, __sender, _Env&&) |
| -> __completions_t<_Env> |
| { |
| return {}; |
| } |
| |
| friend empty_env tag_invoke(get_env_t, const __t&) noexcept |
| { |
| return {}; |
| } |
| }; |
| |
| struct __read_t |
| { |
| template <class _Tag> |
| constexpr __sender<_Tag> operator()(_Tag) const noexcept |
| { |
| return {}; |
| } |
| }; |
| } // namespace __read |
| |
| inline constexpr __read::__read_t read{}; |
| |
| namespace __queries |
| { |
| inline auto get_scheduler_t::operator()() const noexcept |
| { |
| return read(get_scheduler); |
| } |
| |
| template <class _Env> |
| requires tag_invocable<get_scheduler_t, const _Env&> |
| inline auto get_scheduler_t::operator()(const _Env& __env) const noexcept |
| -> tag_invoke_result_t<get_scheduler_t, const _Env&> |
| { |
| static_assert(nothrow_tag_invocable<get_scheduler_t, const _Env&>); |
| static_assert(scheduler<tag_invoke_result_t<get_scheduler_t, const _Env&>>); |
| return tag_invoke(get_scheduler_t{}, __env); |
| } |
| |
| inline auto get_delegatee_scheduler_t::operator()() const noexcept |
| { |
| return read(get_delegatee_scheduler); |
| } |
| |
| template <class _Env> |
| requires tag_invocable<get_delegatee_scheduler_t, const _Env&> |
| inline auto |
| get_delegatee_scheduler_t::operator()(const _Env& __t) const noexcept |
| -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&> |
| { |
| static_assert( |
| nothrow_tag_invocable<get_delegatee_scheduler_t, const _Env&>); |
| static_assert( |
| scheduler<tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>>); |
| return tag_invoke(get_delegatee_scheduler_t{}, std::as_const(__t)); |
| } |
| |
| inline auto get_allocator_t::operator()() const noexcept |
| { |
| return read(get_allocator); |
| } |
| |
| inline auto get_stop_token_t::operator()() const noexcept |
| { |
| return read(get_stop_token); |
| } |
| |
| template <__completion_tag _CPO> |
| template <__has_completion_scheduler_for<_CPO> _Queryable> |
| auto get_completion_scheduler_t<_CPO>::operator()( |
| const _Queryable& __queryable) const noexcept |
| -> tag_invoke_result_t<get_completion_scheduler_t<_CPO>, const _Queryable&> |
| { |
| static_assert(nothrow_tag_invocable<get_completion_scheduler_t<_CPO>, |
| const _Queryable&>, |
| "get_completion_scheduler<_CPO> should be noexcept"); |
| static_assert( |
| scheduler<tag_invoke_result_t<get_completion_scheduler_t<_CPO>, |
| const _Queryable&>>); |
| return tag_invoke(*this, __queryable); |
| } |
| } // namespace __queries |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.adaptors.on] |
| namespace __on_v2 |
| { |
| inline constexpr __mstring __on_context = |
| "In stdexec::on(Scheduler, Sender)..."__csz; |
| inline constexpr __mstring __no_scheduler_diag = |
| "stdexec::on() requires a scheduler to transition back to."__csz; |
| inline constexpr __mstring __no_scheduler_details = |
| "The provided environment lacks a value for the get_scheduler() query."__csz; |
| |
| template <__mstring _Context = __on_context, |
| __mstring _Diagnostic = __no_scheduler_diag, |
| __mstring _Details = __no_scheduler_details> |
| struct _CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_ |
| {}; |
| |
| struct on_t; |
| |
| STDEXEC_PRAGMA_PUSH() |
| STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-local-typedefs") |
| |
| struct __no_scheduler_in_environment |
| { |
| // Issue a custom diagnostic if the environment doesn't provide a scheduler. |
| template <class _Sender, class _Env> |
| static auto transform_sender(_Sender&&, const _Env&) |
| { |
| struct __no_scheduler_in_environment |
| { |
| using is_sender = void; |
| using completion_signatures = // |
| __mexception<_CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_<>, |
| _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>; |
| }; |
| |
| return __no_scheduler_in_environment{}; |
| } |
| }; |
| |
| STDEXEC_PRAGMA_POP() |
| |
| template <class _Ty, class = __name_of<__decay_t<_Ty>>> |
| struct __always |
| { |
| _Ty __val_; |
| |
| _Ty operator()() noexcept |
| { |
| return static_cast<_Ty&&>(__val_); |
| } |
| }; |
| |
| template <class _Ty> |
| __always(_Ty) -> __always<_Ty>; |
| |
| struct on_t : __with_default_get_env<on_t>, __no_scheduler_in_environment |
| { |
| template <scheduler _Scheduler, sender _Sender> |
| auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const |
| { |
| // BUGBUG __get_early_domain, or get_domain(__sched), or ...? |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<on_t>((_Scheduler&&)__sched, (_Sender&&)__sndr)); |
| } |
| |
| template <class _Env> |
| STDEXEC_ATTRIBUTE((always_inline)) |
| static auto __transform_env_fn(_Env&& __env) noexcept |
| { |
| return [&](__ignore, auto __sched, __ignore) noexcept { |
| return __join_env(__detail::__mkenv_sched(__sched), (_Env&&)__env); |
| }; |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_env(const _Sender& __sndr, _Env&& __env) noexcept |
| { |
| return __sexpr_apply(__sndr, __transform_env_fn((_Env&&)__env)); |
| } |
| |
| using __no_scheduler_in_environment::transform_sender; |
| |
| template <class _Sender, class _Env> |
| requires __callable<get_scheduler_t, const _Env&> |
| static auto transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply( |
| (_Sender&&)__sndr, |
| [&]<class _Scheduler, class _Child>(__ignore, _Scheduler __sched, |
| _Child&& __child) { |
| auto __old = get_scheduler(__env); |
| return transfer(let_value(transfer_just(std::move(__sched)), |
| __always{(_Child&&)__child}), |
| std::move(__old)); |
| }); |
| } |
| }; |
| |
| template <class _Scheduler, class _Closure> |
| struct __continue_on_data |
| { |
| _Scheduler __sched_; |
| _Closure __clsur_; |
| }; |
| template <class _Scheduler, class _Closure> |
| __continue_on_data(_Scheduler, _Closure) |
| -> __continue_on_data<_Scheduler, _Closure>; |
| |
| struct continue_on_t : |
| __with_default_get_env<continue_on_t>, |
| __no_scheduler_in_environment |
| { |
| template <sender _Sender, scheduler _Scheduler, |
| __sender_adaptor_closure_for<_Sender> _Closure> |
| auto operator()(_Sender&& __sndr, _Scheduler&& __sched, |
| _Closure&& __clsur) const |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<continue_on_t>( |
| __continue_on_data{(_Scheduler&&)__sched, (_Closure&&)__clsur}, |
| (_Sender&&)__sndr)); |
| } |
| |
| template <scheduler _Scheduler, __sender_adaptor_closure _Closure> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| auto operator()(_Scheduler&& __sched, _Closure&& __clsur) const |
| -> __binder_back<continue_on_t, __decay_t<_Scheduler>, |
| __decay_t<_Closure>> |
| { |
| return {{}, {}, {(_Scheduler&&)__sched, (_Closure&&)__clsur}}; |
| } |
| |
| using __no_scheduler_in_environment::transform_sender; |
| |
| template <class _Sender, class _Env> |
| requires __callable<get_scheduler_t, const _Env&> |
| static auto transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| auto __old = get_scheduler(__env); |
| return __sexpr_apply((_Sender&&)__sndr, |
| [&]<class _Data, class _Child>( |
| __ignore, _Data&& __data, _Child&& __child) { |
| auto&& [__sched, __clsur] = (_Data&&)__data; |
| using _Closure = decltype(__clsur); |
| return __write(transfer(((_Closure&&)__clsur)(transfer( |
| __write((_Child&&)__child, |
| __detail::__mkenv_sched(__old)), |
| __sched)), |
| __old), |
| __detail::__mkenv_sched(__sched)); |
| }); |
| } |
| }; |
| } // namespace __on_v2 |
| |
| namespace v2 |
| { |
| using __on_v2::on_t; |
| inline constexpr on_t on{}; |
| |
| using __on_v2::continue_on_t; |
| inline constexpr continue_on_t continue_on{}; |
| } // namespace v2 |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.consumers.sync_wait] |
| // [execution.senders.consumers.sync_wait_with_variant] |
| namespace __sync_wait |
| { |
| inline auto __make_env(run_loop& __loop) noexcept |
| { |
| return __env::__env_fn{ |
| [&](__one_of<get_scheduler_t, |
| get_delegatee_scheduler_t> auto) noexcept { |
| return __loop.get_scheduler(); |
| }}; |
| } |
| |
| struct __env : __result_of<__make_env, run_loop&> |
| { |
| __env(); |
| |
| __env(run_loop& __loop) noexcept : |
| __result_of<__make_env, run_loop&>{__sync_wait::__make_env(__loop)} |
| {} |
| }; |
| |
| // What should sync_wait(just_stopped()) return? |
| template <class _Sender, class _Continuation> |
| using __sync_wait_result_impl = // |
| __try_value_types_of_t<_Sender, __env, |
| __transform<__q<__decay_t>, _Continuation>, |
| __q<__msingle>>; |
| |
| template <class _Sender> |
| using __sync_wait_result_t = |
| __mtry_eval<__sync_wait_result_impl, _Sender, __q<std::tuple>>; |
| |
| template <class... _Values> |
| struct __state |
| { |
| using _Tuple = std::tuple<_Values...>; |
| std::variant<std::monostate, _Tuple, std::exception_ptr, set_stopped_t> |
| __data_{}; |
| }; |
| |
| template <class... _Values> |
| struct __receiver |
| { |
| struct __t |
| { |
| using is_receiver = void; |
| using __id = __receiver; |
| __state<_Values...>* __state_; |
| run_loop* __loop_; |
| |
| template <class _Error> |
| void __set_error(_Error __err) noexcept |
| { |
| if constexpr (__decays_to<_Error, std::exception_ptr>) |
| __state_->__data_.template emplace<2>((_Error&&)__err); |
| else if constexpr (__decays_to<_Error, std::error_code>) |
| __state_->__data_.template emplace<2>( |
| std::make_exception_ptr(std::system_error(__err))); |
| else |
| __state_->__data_.template emplace<2>( |
| std::make_exception_ptr((_Error&&)__err)); |
| __loop_->finish(); |
| } |
| |
| template <same_as<set_value_t> _Tag, class... _As> |
| requires constructible_from<std::tuple<_Values...>, _As...> |
| friend void tag_invoke(_Tag, __t&& __rcvr, _As&&... __as) noexcept |
| { |
| try |
| { |
| __rcvr.__state_->__data_.template emplace<1>((_As&&)__as...); |
| __rcvr.__loop_->finish(); |
| } |
| catch (...) |
| { |
| __rcvr.__set_error(std::current_exception()); |
| } |
| } |
| |
| template <same_as<set_error_t> _Tag, class _Error> |
| friend void tag_invoke(_Tag, __t&& __rcvr, _Error __err) noexcept |
| { |
| __rcvr.__set_error((_Error&&)__err); |
| } |
| |
| friend void tag_invoke(set_stopped_t __d, __t&& __rcvr) noexcept |
| { |
| __rcvr.__state_->__data_.template emplace<3>(__d); |
| __rcvr.__loop_->finish(); |
| } |
| |
| friend __env tag_invoke(get_env_t, const __t& __rcvr) noexcept |
| { |
| return __env(*__rcvr.__loop_); |
| } |
| }; |
| }; |
| |
| template <class _Sender> |
| using __receiver_t = __t<__sync_wait_result_impl<_Sender, __q<__receiver>>>; |
| |
| // These are for hiding the metaprogramming in diagnostics |
| template <class _Sender> |
| struct __sync_receiver_for |
| { |
| using __t = __receiver_t<_Sender>; |
| }; |
| template <class _Sender> |
| using __sync_receiver_for_t = __t<__sync_receiver_for<_Sender>>; |
| |
| template <class _Sender> |
| struct __value_tuple_for |
| { |
| using __t = __sync_wait_result_t<_Sender>; |
| }; |
| template <class _Sender> |
| using __value_tuple_for_t = __t<__value_tuple_for<_Sender>>; |
| |
| template <class _Sender> |
| struct __variant_for |
| { |
| using __t = __sync_wait_result_t<__result_of<into_variant, _Sender>>; |
| }; |
| template <class _Sender> |
| using __variant_for_t = __t<__variant_for<_Sender>>; |
| |
| inline constexpr __mstring __sync_wait_context_diag = // |
| "In stdexec::sync_wait()..."__csz; |
| inline constexpr __mstring __too_many_successful_completions_diag = |
| "The argument to stdexec::sync_wait() is a sender that can complete successfully in more " |
| "than one way. Use stdexec::sync_wait_with_variant() instead."__csz; |
| |
| template <__mstring _Context, __mstring _Diagnostic> |
| struct _INVALID_ARGUMENT_TO_SYNC_WAIT_; |
| |
| template <__mstring _Diagnostic> |
| using __invalid_argument_to_sync_wait = |
| _INVALID_ARGUMENT_TO_SYNC_WAIT_<__sync_wait_context_diag, _Diagnostic>; |
| |
| template <__mstring _Diagnostic, class _Sender, class _Env = __env> |
| using __sync_wait_error = |
| __mexception<__invalid_argument_to_sync_wait<_Diagnostic>, |
| _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>; |
| |
| template <class _Sender, class> |
| using __too_many_successful_completions_error = |
| __sync_wait_error<__too_many_successful_completions_diag, _Sender>; |
| |
| template <class _Sender> |
| concept __valid_sync_wait_argument = |
| __ok<__minvoke<__mtry_catch_q<__single_value_variant_sender_t, |
| __q<__too_many_successful_completions_error>>, |
| _Sender, __env>>; |
| |
| #if STDEXEC_NVHPC() |
| // It requires some hoop-jumping to get the NVHPC compiler to report a |
| // meaningful diagnostic for SFINAE failures. |
| template <class _Sender> |
| auto __diagnose_error() |
| { |
| if constexpr (!sender_in<_Sender, __env>) |
| { |
| using _Completions = __completion_signatures_of_t<_Sender, __env>; |
| if constexpr (!__ok<_Completions>) |
| { |
| return _Completions(); |
| } |
| else |
| { |
| constexpr __mstring __diag = |
| "The stdexec::sender_in<Sender, Environment> concept check has failed."__csz; |
| return __sync_wait_error<__diag, _Sender>(); |
| } |
| } |
| else if constexpr (!__valid_sync_wait_argument<_Sender>) |
| { |
| return __sync_wait_error<__too_many_successful_completions_diag, |
| _Sender>(); |
| } |
| else if constexpr (!sender_to<_Sender, __sync_receiver_for_t<_Sender>>) |
| { |
| constexpr __mstring __diag = |
| "Failed to connect the given sender to sync_wait's internal receiver. " |
| "The stdexec::connect(Sender, Receiver) expression is ill-formed."__csz; |
| return __sync_wait_error<__diag, _Sender>(); |
| } |
| else |
| { |
| constexpr __mstring __diag = "Unknown concept check failure."__csz; |
| return __sync_wait_error<__diag, _Sender>(); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| template <class _Sender> |
| using __error_description_t = |
| decltype(__sync_wait::__diagnose_error<_Sender>()); |
| #endif |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.consumers.sync_wait] |
| struct sync_wait_t |
| { |
| template <sender_in<__env> _Sender> |
| requires __valid_sync_wait_argument<_Sender> && |
| __has_implementation_for<sync_wait_t, |
| __early_domain_of_t<_Sender>, _Sender> |
| auto operator()(_Sender&& __sndr) const |
| -> std::optional<__value_tuple_for_t<_Sender>> |
| { |
| using _SD = __early_domain_of_t<_Sender>; |
| constexpr bool __has_custom_impl = |
| __callable<apply_sender_t, _SD, sync_wait_t, _Sender>; |
| using _Domain = __if_c<__has_custom_impl, _SD, default_domain>; |
| return stdexec::apply_sender(_Domain(), *this, (_Sender&&)__sndr); |
| } |
| |
| #if STDEXEC_NVHPC() |
| // This is needed to get sensible diagnostics from nvc++ |
| template <class _Sender, class _Error = __error_description_t<_Sender>> |
| auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const |
| -> std::optional<std::tuple<int>> = delete; |
| #endif |
| |
| using _Sender = __0; |
| using __legacy_customizations_t = __types< |
| // For legacy reasons: |
| tag_invoke_t( |
| sync_wait_t, |
| get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)), |
| _Sender), |
| tag_invoke_t(sync_wait_t, _Sender)>; |
| |
| // The default implementation goes here: |
| template <class _Sender> |
| requires sender_to<_Sender, __sync_receiver_for_t<_Sender>> |
| auto apply_sender(_Sender&& __sndr) const |
| -> std::optional<__sync_wait_result_t<_Sender>> |
| { |
| using state_t = __sync_wait_result_impl<_Sender, __q<__state>>; |
| state_t __state{}; |
| run_loop __loop; |
| |
| // Launch the sender with a continuation that will fill in a variant |
| // and notify a condition variable. |
| auto __op_state = connect((_Sender&&)__sndr, |
| __receiver_t<_Sender>{&__state, &__loop}); |
| start(__op_state); |
| |
| // Wait for the variant to be filled in. |
| __loop.run(); |
| |
| if (__state.__data_.index() == 2) |
| std::rethrow_exception(std::get<2>(__state.__data_)); |
| |
| if (__state.__data_.index() == 3) |
| return std::nullopt; |
| |
| return std::move(std::get<1>(__state.__data_)); |
| } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // [execution.senders.consumers.sync_wait_with_variant] |
| struct sync_wait_with_variant_t |
| { |
| struct __impl; |
| |
| template <sender_in<__env> _Sender> |
| requires __callable<apply_sender_t, __early_domain_of_t<_Sender>, |
| sync_wait_with_variant_t, _Sender> |
| auto operator()(_Sender&& __sndr) const |
| -> std::optional<__variant_for_t<_Sender>> |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::apply_sender(__domain, *this, (_Sender&&)__sndr); |
| } |
| |
| #if STDEXEC_NVHPC() |
| template <class _Sender, class _Error = __error_description_t< |
| __result_of<into_variant, _Sender>>> |
| auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const |
| -> std::optional<std::tuple<std::variant<std::tuple<>>>> = delete; |
| #endif |
| |
| using _Sender = __0; |
| using __legacy_customizations_t = __types< |
| // For legacy reasons: |
| tag_invoke_t( |
| sync_wait_with_variant_t, |
| get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)), |
| _Sender), |
| tag_invoke_t(sync_wait_with_variant_t, _Sender)>; |
| |
| template <class _Sender> |
| requires __callable<sync_wait_t, __result_of<into_variant, _Sender>> |
| auto apply_sender(_Sender&& __sndr) const |
| -> std::optional<__variant_for_t<_Sender>> |
| { |
| return sync_wait_t()(into_variant((_Sender&&)__sndr)); |
| } |
| }; |
| } // namespace __sync_wait |
| |
| using __sync_wait::sync_wait_t; |
| inline constexpr sync_wait_t sync_wait{}; |
| |
| using __sync_wait::sync_wait_with_variant_t; |
| inline constexpr sync_wait_with_variant_t sync_wait_with_variant{}; |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| struct __ignore_sender |
| { |
| using is_sender = void; |
| |
| template <sender _Sender> |
| constexpr __ignore_sender(_Sender&&) noexcept |
| {} |
| }; |
| |
| template <auto _Reason = "You cannot pipe one sender into another."__csz> |
| struct _CANNOT_PIPE_INTO_A_SENDER_ |
| {}; |
| |
| template <class _Sender> |
| using __bad_pipe_sink_t = |
| __mexception<_CANNOT_PIPE_INTO_A_SENDER_<>, _WITH_SENDER_<_Sender>>; |
| } // namespace stdexec |
| |
| #if STDEXEC_MSVC() |
| namespace stdexec |
| { |
| // MSVCBUG |
| // https://developercommunity.visualstudio.com/t/Incorrect-codegen-in-await_suspend-aroun/10454102 |
| |
| // MSVC incorrectly allocates the return buffer for await_suspend calls within |
| // the suspended coroutine frame. When the suspended coroutine is destroyed |
| // within await_suspend, the continuation coroutine handle is not only used |
| // after free, but also overwritten by the debug malloc implementation when NRVO |
| // is in play. |
| |
| // This workaround delays the destruction of the suspended coroutine by wrapping |
| // the continuation in another coroutine which destroys the former and transfers |
| // execution to the original continuation. |
| |
| // The wrapping coroutine is thread-local and is reused within the thread for |
| // each destroy-and-continue sequence. The wrapping coroutine itself is |
| // destroyed at thread exit. |
| |
| namespace __destroy_and_continue_msvc |
| { |
| struct __task |
| { |
| struct promise_type |
| { |
| __task get_return_object() noexcept |
| { |
| return { |
| __coro::coroutine_handle<promise_type>::from_promise(*this)}; |
| } |
| |
| static std::suspend_never initial_suspend() noexcept |
| { |
| return {}; |
| } |
| |
| static std::suspend_never final_suspend() noexcept |
| { |
| STDEXEC_ASSERT(!"Should never get here"); |
| return {}; |
| } |
| |
| static void return_void() noexcept |
| { |
| STDEXEC_ASSERT(!"Should never get here"); |
| } |
| |
| static void unhandled_exception() noexcept |
| { |
| STDEXEC_ASSERT(!"Should never get here"); |
| } |
| }; |
| |
| __coro::coroutine_handle<> __coro_; |
| }; |
| |
| struct __continue_t |
| { |
| static constexpr bool await_ready() noexcept |
| { |
| return false; |
| } |
| |
| __coro::coroutine_handle<> |
| await_suspend(__coro::coroutine_handle<>) noexcept |
| { |
| return __continue_; |
| } |
| |
| static void await_resume() noexcept {} |
| |
| __coro::coroutine_handle<> __continue_; |
| }; |
| |
| struct __context |
| { |
| __coro::coroutine_handle<> __destroy_; |
| __coro::coroutine_handle<> __continue_; |
| }; |
| |
| inline __task __co_impl(__context& __c) |
| { |
| while (true) |
| { |
| co_await __continue_t{__c.__continue_}; |
| __c.__destroy_.destroy(); |
| } |
| } |
| |
| struct __context_and_coro |
| { |
| __context_and_coro() |
| { |
| __context_.__continue_ = __coro::noop_coroutine(); |
| __coro_ = __co_impl(__context_).__coro_; |
| } |
| |
| ~__context_and_coro() |
| { |
| __coro_.destroy(); |
| } |
| |
| __context __context_; |
| __coro::coroutine_handle<> __coro_; |
| }; |
| |
| inline __coro::coroutine_handle<> __impl(__coro::coroutine_handle<> __destroy, |
| __coro::coroutine_handle<> __continue) |
| { |
| static thread_local __context_and_coro __c; |
| __c.__context_.__destroy_ = __destroy; |
| __c.__context_.__continue_ = __continue; |
| return __c.__coro_; |
| } |
| } // namespace __destroy_and_continue_msvc |
| } // namespace stdexec |
| |
| #define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \ |
| (::stdexec::__destroy_and_continue_msvc::__impl(__destroy, __continue)) |
| #else |
| #define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \ |
| (__destroy.destroy(), __continue) |
| #endif |
| |
| // For issuing a meaningful diagnostic for the erroneous `snd1 | snd2`. |
| template <stdexec::sender _Sender> |
| requires stdexec::__ok<stdexec::__bad_pipe_sink_t<_Sender>> |
| auto operator|(stdexec::__ignore_sender, _Sender&&) noexcept |
| -> stdexec::__ignore_sender; |
| |
| #include "__detail/__p2300.hpp" |
| |
| STDEXEC_PRAGMA_POP() |