diff --git a/examples/nvexec/launch.cu b/examples/nvexec/launch.cu index 90b16a3ff..fd31ac133 100644 --- a/examples/nvexec/launch.cu +++ b/examples/nvexec/launch.cu @@ -23,8 +23,8 @@ #include -constexpr std::size_t N = 2 * 1024; -constexpr std::size_t THREAD_BLOCK_SIZE = 128u; +constexpr std::size_t N = 2ul * 1024ul; +constexpr std::size_t THREAD_BLOCK_SIZE = 128ul; constexpr std::size_t NUM_BLOCKS = (N + THREAD_BLOCK_SIZE - 1) / THREAD_BLOCK_SIZE; enum { @@ -46,12 +46,13 @@ auto main() -> int { nvexec::stream_context stream{}; - auto snd = stdexec::transfer_just(stream.get_scheduler(), first, last) + auto snd = stdexec::just(first, last) // + | stdexec::continues_on(stream.get_scheduler()) | nvexec::launch( {.grid_size = NUM_BLOCKS, .block_size = THREAD_BLOCK_SIZE}, [](cudaStream_t, int* first, int* last) { assert(nvexec::is_on_gpu()); - int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; + ptrdiff_t idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx < (last - first)) { first[idx] *= scaling; } diff --git a/examples/nvexec/reduce.cu b/examples/nvexec/reduce.cu index dec18ecf1..757d42ce4 100644 --- a/examples/nvexec/reduce.cu +++ b/examples/nvexec/reduce.cu @@ -19,7 +19,7 @@ #include -#include +#include #include namespace ex = stdexec; @@ -32,7 +32,8 @@ auto main() -> int { nvexec::stream_context stream_ctx{}; - auto snd = ex::transfer_just(stream_ctx.get_scheduler(), std::span{first, last}) + auto snd = ex::just(std::span{first, last}) // + | ex::continues_on(stream_ctx.get_scheduler()) // | nvexec::reduce(42.0f); auto [result] = stdexec::sync_wait(std::move(snd)).value(); diff --git a/examples/server_theme/let_value.cpp b/examples/server_theme/let_value.cpp index b9e4fee6c..a2613fd0d 100644 --- a/examples/server_theme/let_value.cpp +++ b/examples/server_theme/let_value.cpp @@ -24,21 +24,20 @@ * - optional GPU context that may be used on some types of servers * * Specific problem description: - * - we are looking at the flow of processing an HTTP request and sending back - * the response - * - show how one can break the (slightly complex) flow into steps with let_* - * functions - * - different phases of processing HTTP requests are broken down into separate - * concerns - * - each part of the processing might use different execution contexts (details - * not shown in this example) - * - error handling is generic, regardless which component fails; we always send - * the right response to the clients + * + * - we are looking at the flow of processing an HTTP request and sending back the + * response + * - show how one can break the (slightly complex) flow into steps with let_* functions + * - different phases of processing HTTP requests are broken down into separate concerns + * - each part of the processing might use different execution contexts (details not shown + * in this example) + * - error handling is generic, regardless which component fails; we always send the right + * response to the clients * * Example goals: * - show how one can break more complex flows into steps with let_* functions - * - exemplify the use of let_value, let_error, let_stopped, transfer_just and just - * algorithms + * - exemplify the use of let_value, let_error, let_stopped, continues_on and just + * algorithms */ #include @@ -77,7 +76,7 @@ auto schedule_request_start(S sched, int idx) -> ex::sender auto { std::cout << "HTTP request " << idx << " arrived\n"; // Return a sender for the incoming http_request - return ex::transfer_just(std::forward(sched), std::move(req)); + return ex::just(std::move(req)) | ex::continues_on(std::forward(sched)); } // Sends a response back to the client; yields a void signal on success diff --git a/include/exec/any_sender_of.hpp b/include/exec/any_sender_of.hpp index 645f094a6..09e12efef 100644 --- a/include/exec/any_sender_of.hpp +++ b/include/exec/any_sender_of.hpp @@ -855,7 +855,7 @@ namespace exec { STDEXEC::inplace_stop_source __stop_source_{}; using __stop_callback = typename STDEXEC::stop_token_of_t< STDEXEC::env_of_t<_Receiver> - >::template callback_type<__forward_stop_request>; + >::template callback_type; std::optional<__stop_callback> __on_stop_{}; }; diff --git a/include/exec/env.hpp b/include/exec/env.hpp index f0ef16b78..519d3cb6c 100644 --- a/include/exec/env.hpp +++ b/include/exec/env.hpp @@ -33,10 +33,10 @@ namespace exec { }; template - struct __without : STDEXEC::__env::__env_base_t<_Env> { + struct __without : STDEXEC::__env_base_t<_Env> { static_assert(STDEXEC::__nothrow_move_constructible<_Env>); - using STDEXEC::__env::__env_base_t<_Env>::query; + using STDEXEC::__env_base_t<_Env>::query; STDEXEC_ATTRIBUTE(nodiscard, host, device) auto query(_Query) const noexcept = delete; @@ -168,9 +168,9 @@ namespace exec { inline constexpr __read_with_default::__read_with_default_t read_with_default{}; [[deprecated("exec::write has been renamed to STDEXEC::write_env")]] - inline constexpr STDEXEC::__write::write_env_t write{}; - [[deprecated("write_env has been moved to the STDEXEC:: namespace")]] - inline constexpr STDEXEC::__write::write_env_t write_env{}; + inline constexpr STDEXEC::__write_env_t write{}; + [[deprecated("exec::write_env has been moved to the STDEXEC:: namespace")]] + inline constexpr STDEXEC::__write_env_t write_env{}; namespace __write_attrs { using namespace STDEXEC; diff --git a/include/exec/start_now.hpp b/include/exec/start_now.hpp index 76f4afc2c..6dd850f2f 100644 --- a/include/exec/start_now.hpp +++ b/include/exec/start_now.hpp @@ -188,7 +188,7 @@ namespace exec { struct start_now_t { template < - STDEXEC::queryable _Env, + STDEXEC::__queryable _Env, exec::__scope::__async_scope _AsyncScope, STDEXEC::sender... _Sender > diff --git a/include/exec/task.hpp b/include/exec/task.hpp index baf07645d..bd59e0dce 100644 --- a/include/exec/task.hpp +++ b/include/exec/task.hpp @@ -334,7 +334,7 @@ namespace exec { private: using __scheduler_t = - __query_result_or_t; + __call_result_or_t; struct __final_awaitable { static constexpr auto await_ready() noexcept -> bool { diff --git a/include/stdexec/__detail/__as_awaitable.hpp b/include/stdexec/__detail/__as_awaitable.hpp index 066722ed0..90443253f 100644 --- a/include/stdexec/__detail/__as_awaitable.hpp +++ b/include/stdexec/__detail/__as_awaitable.hpp @@ -47,7 +47,7 @@ namespace STDEXEC { template using __value_t = __decay_t< __value_types_of_t<_Sender, env_of_t<_Promise&>, __q<__single_value>, __msingle_or> - >; + >; } // namespace __detail ///////////////////////////////////////////////////////////////////////////// @@ -185,61 +185,62 @@ namespace STDEXEC { constexpr void return_void() noexcept; constexpr auto unhandled_stopped() noexcept -> __std::coroutine_handle<>; }; + } // namespace __as_awaitable - struct as_awaitable_t { - template - static consteval auto __get_declfn() noexcept { - if constexpr (__connect_await::__has_as_awaitable_member<_Tp, _Promise>) { - using __result_t = decltype(__declval<_Tp>().as_awaitable(__declval<_Promise&>())); - constexpr bool __is_nothrow = noexcept(__declval<_Tp>() - .as_awaitable(__declval<_Promise&>())); - return __declfn<__result_t, __is_nothrow>(); - // NOLINTNEXTLINE(bugprone-branch-clone) - } else if constexpr (__awaitable<_Tp, __unspecified>) { // NOT __awaitable<_Tp, _Promise> !! - return __declfn<_Tp&&>(); - } else if constexpr (__awaitable_sender<_Tp, _Promise>) { - using __result_t = __sender_awaitable<_Promise, _Tp>; - constexpr bool __is_nothrow = - __nothrow_constructible_from<__result_t, _Tp, __std::coroutine_handle<_Promise>>; - return __declfn<__result_t, __is_nothrow>(); - } else { - return __declfn<_Tp&&>(); - } - } - - template ()> - requires __callable<__mtypeof<_DeclFn>> - auto operator()(_Tp&& __t, _Promise& __promise) const noexcept(noexcept(_DeclFn())) - -> decltype(_DeclFn()) { - if constexpr (__connect_await::__has_as_awaitable_member<_Tp, _Promise>) { - using __result_t = decltype(static_cast<_Tp&&>(__t).as_awaitable(__promise)); - static_assert(__awaitable<__result_t, _Promise>); - return static_cast<_Tp&&>(__t).as_awaitable(__promise); - // NOLINTNEXTLINE(bugprone-branch-clone) - } else if constexpr (__awaitable<_Tp, __unspecified>) { // NOT __awaitable<_Tp, _Promise> !! - return static_cast<_Tp&&>(__t); - } else if constexpr (__awaitable_sender<_Tp, _Promise>) { - auto __hcoro = __std::coroutine_handle<_Promise>::from_promise(__promise); - return __sender_awaitable<_Promise, _Tp>{static_cast<_Tp&&>(__t), __hcoro}; - } else { - return static_cast<_Tp&&>(__t); - } + struct as_awaitable_t { + template + static consteval auto __get_declfn() noexcept { + using __as_awaitable::__unspecified; + if constexpr (__connect_await::__has_as_awaitable_member<_Tp, _Promise>) { + using __result_t = decltype(__declval<_Tp>().as_awaitable(__declval<_Promise&>())); + constexpr bool __is_nothrow = noexcept(__declval<_Tp>() + .as_awaitable(__declval<_Promise&>())); + return __declfn<__result_t, __is_nothrow>(); + // NOLINTNEXTLINE(bugprone-branch-clone) + } else if constexpr (__awaitable<_Tp, __unspecified>) { // NOT __awaitable<_Tp, _Promise> !! + return __declfn<_Tp&&>(); + } else if constexpr (__as_awaitable::__awaitable_sender<_Tp, _Promise>) { + using __result_t = __as_awaitable::__sender_awaitable<_Promise, _Tp>; + constexpr bool __is_nothrow = + __nothrow_constructible_from<__result_t, _Tp, __std::coroutine_handle<_Promise>>; + return __declfn<__result_t, __is_nothrow>(); + } else { + return __declfn<_Tp&&>(); } + } - template ()> - requires __callable<__mtypeof<_DeclFn>> || __tag_invocable - [[deprecated("the use of tag_invoke for as_awaitable is deprecated")]] - auto operator()(_Tp&& __t, _Promise& __promise) const - noexcept(__nothrow_tag_invocable) - -> __tag_invoke_result_t { - using __result_t = __tag_invoke_result_t; + template ()> + requires __callable<__mtypeof<_DeclFn>> + auto operator()(_Tp&& __t, _Promise& __promise) const noexcept(noexcept(_DeclFn())) + -> decltype(_DeclFn()) { + using __as_awaitable::__unspecified; + if constexpr (__connect_await::__has_as_awaitable_member<_Tp, _Promise>) { + using __result_t = decltype(static_cast<_Tp&&>(__t).as_awaitable(__promise)); static_assert(__awaitable<__result_t, _Promise>); - return __tag_invoke(*this, static_cast<_Tp&&>(__t), __promise); + return static_cast<_Tp&&>(__t).as_awaitable(__promise); + // NOLINTNEXTLINE(bugprone-branch-clone) + } else if constexpr (__awaitable<_Tp, __unspecified>) { // NOT __awaitable<_Tp, _Promise> !! + return static_cast<_Tp&&>(__t); + } else if constexpr (__as_awaitable::__awaitable_sender<_Tp, _Promise>) { + auto __hcoro = __std::coroutine_handle<_Promise>::from_promise(__promise); + return __as_awaitable::__sender_awaitable<_Promise, _Tp>{static_cast<_Tp&&>(__t), __hcoro}; + } else { + return static_cast<_Tp&&>(__t); } - }; - } // namespace __as_awaitable + } + + template ()> + requires __callable<__mtypeof<_DeclFn>> || __tag_invocable + [[deprecated("the use of tag_invoke for as_awaitable is deprecated")]] + auto operator()(_Tp&& __t, _Promise& __promise) const + noexcept(__nothrow_tag_invocable) + -> __tag_invoke_result_t { + using __result_t = __tag_invoke_result_t; + static_assert(__awaitable<__result_t, _Promise>); + return __tag_invoke(*this, static_cast<_Tp&&>(__t), __promise); + } + }; - using __as_awaitable::as_awaitable_t; inline constexpr as_awaitable_t as_awaitable{}; #endif } // namespace STDEXEC diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 1e32c36df..d71990640 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -46,7 +46,7 @@ namespace STDEXEC { #else // ^^^ EDG ^^^ / vvv !EDG vvv # define STDEXEC_SEXPR_DESCRIPTOR_FN(_Descriptor) ([] { return _Descriptor(); }) # define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \ - STDEXEC::__descriptor_fn_v> + STDEXEC::__descriptor_fn_v> #endif #if defined(STDEXEC_DEMANGLE_SENDER_NAMES) @@ -59,7 +59,7 @@ namespace STDEXEC { template consteval auto __descriptor_fn() noexcept { - return __descriptor_fn_v<__detail::__desc<_Tag, _Data, _Child...>>; + return __descriptor_fn_v<__desc<_Tag, _Data, _Child...>>; } template diff --git a/include/stdexec/__detail/__bulk.hpp b/include/stdexec/__detail/__bulk.hpp index 29e5a62f0..ead520bdb 100644 --- a/include/stdexec/__detail/__bulk.hpp +++ b/include/stdexec/__detail/__bulk.hpp @@ -34,10 +34,6 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.bulk] namespace __bulk { - struct bulk_t; - struct bulk_chunked_t; - struct bulk_unchunked_t; - //! Wrapper for a policy object. //! //! If we wrap a standard execution policy, we don't store anything, as we know the type. @@ -240,29 +236,6 @@ namespace STDEXEC { template STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __as_bulk_chunked_fn(_Fun) -> __as_bulk_chunked_fn<_Fun>; - struct bulk_t : __generic_bulk_t { - struct __transform_sender_fn { - template - constexpr auto operator()(__ignore, _Data&& __data, _Child&& __child) const { - // Lower `bulk` to `bulk_chunked`. If `bulk_chunked` is customized, we will see the customization. - return bulk_chunked( - static_cast<_Child&&>(__child), - __data.__pol_.__get(), - __data.__shape_, - __as_bulk_chunked_fn(std::move(__data.__fun_))); - } - }; - - template - static constexpr auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { - return __apply(__transform_sender_fn(), static_cast<_Sender&&>(__sndr)); - } - }; - - struct bulk_chunked_t : __generic_bulk_t { }; - - struct bulk_unchunked_t : __generic_bulk_t { }; - template struct __impl_base : __sexpr_defaults { template @@ -356,9 +329,23 @@ namespace STDEXEC { }; } // namespace __bulk - using __bulk::bulk_t; - using __bulk::bulk_chunked_t; - using __bulk::bulk_unchunked_t; + struct bulk_t : __bulk::__generic_bulk_t { + template + static constexpr auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { + auto& [__tag, __data, __child] = __sndr; + // Lower `bulk` to `bulk_chunked`. If `bulk_chunked` is customized, we will see the customization. + return bulk_chunked( + STDEXEC::__forward_like<_Sender>(__child), + __data.__pol_.__get(), + __data.__shape_, + __bulk::__as_bulk_chunked_fn(STDEXEC::__forward_like<_Sender>(__data).__fun_)); + } + }; + + struct bulk_chunked_t : __bulk::__generic_bulk_t { }; + + struct bulk_unchunked_t : __bulk::__generic_bulk_t { }; + inline constexpr bulk_t bulk{}; inline constexpr bulk_chunked_t bulk_chunked{}; inline constexpr bulk_unchunked_t bulk_unchunked{}; diff --git a/include/stdexec/__detail/__completion_behavior.hpp b/include/stdexec/__detail/__completion_behavior.hpp index c05d1fd15..378b22f6e 100644 --- a/include/stdexec/__detail/__completion_behavior.hpp +++ b/include/stdexec/__detail/__completion_behavior.hpp @@ -20,7 +20,6 @@ // include these after __execution_fwd.hpp #include "__concepts.hpp" #include "__config.hpp" -#include "__meta.hpp" #include "__query.hpp" #include "__utility.hpp" @@ -95,50 +94,45 @@ namespace STDEXEC { ////////////////////////////////////////////////////////////////////////////////////////// // get_completion_behavior: A sender can define this attribute to describe the sender's // completion behavior - namespace __queries { - template <__completion_tag _Tag> - struct get_completion_behavior_t { - private: - template - STDEXEC_ATTRIBUTE(always_inline, host, device) - static constexpr auto __validate() noexcept { - using __result_t = __member_query_result_t<_Attrs, get_completion_behavior_t, _Env...>; - static_assert( - __nothrow_member_queryable_with<_Attrs, get_completion_behavior_t, _Env...>, - "The get_completion_behavior query must be noexcept."); - static_assert( - __std::convertible_to<__result_t, completion_behavior::behavior>, - "The get_completion_behavior query must return one of the static member variables in " - "execution::completion_behavior."); - return __result_t{}; - } + template <__completion_tag _Tag> + struct get_completion_behavior_t { + private: + template + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr auto __validate() noexcept { + using __result_t = __member_query_result_t<_Attrs, get_completion_behavior_t, _Env...>; + static_assert( + __nothrow_member_queryable_with<_Attrs, get_completion_behavior_t, _Env...>, + "The get_completion_behavior query must be noexcept."); + static_assert( + __std::convertible_to<__result_t, completion_behavior::behavior>, + "The get_completion_behavior query must return one of the static member variables in " + "execution::completion_behavior."); + return __result_t{}; + } - public: - template - static inline constexpr get_completion_behavior_t (*signature)(_Sig) = nullptr; + public: + template + static inline constexpr get_completion_behavior_t (*signature)(_Sig) = nullptr; - template - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()(const _Attrs&, const _Env&...) const noexcept { - if constexpr ( - __member_queryable_with, _Env...>) { - return __validate<_Attrs, _Env...>(); - } else if constexpr (__member_queryable_with< - const _Attrs&, - get_completion_behavior_t<_Tag> - >) { - return __validate<_Attrs>(); - } else { - return completion_behavior::unknown; - } + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(const _Attrs&, const _Env&...) const noexcept { + if constexpr ( + __member_queryable_with, _Env...>) { + return __validate<_Attrs, _Env...>(); + } else if constexpr (__member_queryable_with>) { + return __validate<_Attrs>(); + } else { + return completion_behavior::unknown; } + } - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static constexpr auto query(forwarding_query_t) noexcept -> bool { - return true; - } - }; - } // namespace __queries + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static constexpr auto query(forwarding_query_t) noexcept -> bool { + return true; + } + }; [[deprecated("use STDEXEC::completion_behavior::weakest instead")]] inline constexpr const auto& min = completion_behavior::weakest; diff --git a/include/stdexec/__detail/__config.hpp b/include/stdexec/__detail/__config.hpp index a30faa2e3..1d2e11ed2 100644 --- a/include/stdexec/__detail/__config.hpp +++ b/include/stdexec/__detail/__config.hpp @@ -179,7 +179,9 @@ #endif STDEXEC_NAMESPACE_STD_BEGIN -namespace execution { +namespace execution::system_context_replaceability { +} +namespace this_thread { } STDEXEC_NAMESPACE_STD_END @@ -207,6 +209,18 @@ STDEXEC_NAMESPACE_STD_END # error stdexec cannot be defined directly in namespace std, but a namespace nested inside std is allowed. #endif +// clang-format off +#if STDEXEC_NAMESPACE_IS_WITHIN_STD() +# define STDEXEC_P2300_NAMESPACE_BEGIN(...) STDEXEC_NAMESPACE_STD_BEGIN __VA_OPT__(namespace __VA_ARGS__ {) +# define STDEXEC_P2300_NAMESPACE_END(...) __VA_OPT__(}) STDEXEC_NAMESPACE_STD_END +# define STDEXEC_P2300_DEPRECATED_SYMBOL(...) using __VA_ARGS__; +#else +# define STDEXEC_P2300_NAMESPACE_BEGIN(...) namespace STDEXEC { +# define STDEXEC_P2300_NAMESPACE_END(...) } +# define STDEXEC_P2300_DEPRECATED_SYMBOL(...) +#endif +// clang-format on + //////////////////////////////////////////////////////////////////////////////////////////////////// #if __cpp_impl_coroutine >= 2019'02L && __cpp_lib_coroutine >= 2019'02L # include // IWYU pragma: keep diff --git a/include/stdexec/__detail/__connect.hpp b/include/stdexec/__detail/__connect.hpp index 9ec5c8687..e9d684b55 100644 --- a/include/stdexec/__detail/__connect.hpp +++ b/include/stdexec/__detail/__connect.hpp @@ -50,14 +50,7 @@ namespace STDEXEC { || __with_member<_Sender, _Receiver> || __with_co_await<_Sender, _Receiver> || __with_legacy_tag_invoke<_Sender, _Receiver>; - } // namespace __connect - - template - concept __connectable_to = requires(__declfn_t<_Sender&&> __sndr, __declfn_t<_Receiver&> __rcvr) { - { transform_sender(__sndr(), get_env(__rcvr())) } -> __connect::__with_any_connect<_Receiver>; - }; - namespace __connect { #if !STDEXEC_MSVC() # define STDEXEC_CONNECT_DECLFN_FOR(_EXPR) __declfn_t @@ -197,44 +190,48 @@ namespace STDEXEC { # undef STDEXEC_CONNECT_DECLFN_FOR #endif // STDEXEC_MSVC() + } // namespace __connect - ///////////////////////////////////////////////////////////////////////////// - // connect_t - struct connect_t { - template < - class _Sender, - class _Receiver, - class _DeclFn = __connect_declfn_t<_Sender, _Receiver> - > - requires __connectable_to<_Sender, _Receiver> - STDEXEC_ATTRIBUTE(always_inline) - constexpr auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const - noexcept(__nothrow_callable<_DeclFn>) -> __call_result_t<_DeclFn> { - auto&& __new_sndr = transform_sender(static_cast<_Sender&&>(__sndr), get_env(__rcvr)); - using __new_sndr_t = decltype(__new_sndr); - - if constexpr (__with_static_member<__new_sndr_t, _Receiver>) { - return STDEXEC_REMOVE_REFERENCE(__new_sndr_t)::static_connect( - static_cast<__new_sndr_t&&>(__new_sndr), static_cast<_Receiver&&>(__rcvr)); - } else if constexpr (__with_member<__new_sndr_t, _Receiver>) { - return static_cast<__new_sndr_t&&>(__new_sndr).connect(static_cast<_Receiver&&>(__rcvr)); - } else if constexpr (__with_co_await<__new_sndr_t, _Receiver>) { - return __connect_awaitable( - static_cast<__new_sndr_t&&>(__new_sndr), static_cast<_Receiver&&>(__rcvr)); - } else { - return __tag_invoke( - *this, static_cast<__new_sndr_t&&>(__new_sndr), static_cast<_Receiver&&>(__rcvr)); - } - } + template + concept __connectable_to = requires(__declfn_t<_Sender&&> __sndr, __declfn_t<_Receiver&> __rcvr) { + { transform_sender(__sndr(), get_env(__rcvr())) } -> __connect::__with_any_connect<_Receiver>; + }; - static constexpr auto query(forwarding_query_t) noexcept -> bool { - return false; + ///////////////////////////////////////////////////////////////////////////// + // connect_t + struct connect_t { + template < + class _Sender, + class _Receiver, + class _DeclFn = __connect::__connect_declfn_t<_Sender, _Receiver> + > + requires __connectable_to<_Sender, _Receiver> + STDEXEC_ATTRIBUTE(always_inline) + constexpr auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const + noexcept(__nothrow_callable<_DeclFn>) -> __call_result_t<_DeclFn> { + auto&& __new_sndr = transform_sender(static_cast<_Sender&&>(__sndr), get_env(__rcvr)); + using __new_sndr_t = decltype(__new_sndr); + + if constexpr (__connect::__with_static_member<__new_sndr_t, _Receiver>) { + return STDEXEC_REMOVE_REFERENCE(__new_sndr_t)::static_connect( + static_cast<__new_sndr_t&&>(__new_sndr), static_cast<_Receiver&&>(__rcvr)); + } else if constexpr (__connect::__with_member<__new_sndr_t, _Receiver>) { + return static_cast<__new_sndr_t&&>(__new_sndr).connect(static_cast<_Receiver&&>(__rcvr)); + } else if constexpr (__connect::__with_co_await<__new_sndr_t, _Receiver>) { + return __connect_awaitable( + static_cast<__new_sndr_t&&>(__new_sndr), static_cast<_Receiver&&>(__rcvr)); + } else { + return __tag_invoke( + *this, static_cast<__new_sndr_t&&>(__new_sndr), static_cast<_Receiver&&>(__rcvr)); } - }; - } // namespace __connect + } + + static constexpr auto query(forwarding_query_t) noexcept -> bool { + return false; + } + }; - using __connect::connect_t; - inline constexpr __connect::connect_t connect{}; + inline constexpr connect_t connect{}; template concept __nothrow_connectable = sender_to<_Sender, _Receiver> diff --git a/include/stdexec/__detail/__connect_awaitable.hpp b/include/stdexec/__detail/__connect_awaitable.hpp index b9ca0559a..905dbd121 100644 --- a/include/stdexec/__detail/__connect_awaitable.hpp +++ b/include/stdexec/__detail/__connect_awaitable.hpp @@ -165,79 +165,80 @@ namespace STDEXEC { _Receiver& __rcvr_; }; + } // namespace __connect_await - struct __connect_awaitable_t { - private: - template - static constexpr auto __co_call(_Fun __fun, _Ts&&... __as) noexcept { - auto __fn = [&, __fun]() noexcept { - __fun(static_cast<_Ts&&>(__as)...); - }; + struct __connect_awaitable_t { + private: + template + static constexpr auto __co_call(_Fun __fun, _Ts&&... __as) noexcept { + auto __fn = [&, __fun]() noexcept { + __fun(static_cast<_Ts&&>(__as)...); + }; - struct __awaiter { - decltype(__fn) __fn_; + struct __awaiter { + decltype(__fn) __fn_; - static constexpr auto await_ready() noexcept -> bool { - return false; - } + static constexpr auto await_ready() noexcept -> bool { + return false; + } - constexpr void await_suspend(__std::coroutine_handle<>) noexcept { - __fn_(); - } + constexpr void await_suspend(__std::coroutine_handle<>) noexcept { + __fn_(); + } - [[noreturn]] - void await_resume() noexcept { - std::terminate(); - } - }; + [[noreturn]] + void await_resume() noexcept { + std::terminate(); + } + }; - return __awaiter{__fn}; - } + return __awaiter{__fn}; + } - template + template # if STDEXEC_GCC() && (STDEXEC_GCC_VERSION >= 12'00) - __attribute__((__used__)) + __attribute__((__used__)) # endif - static auto __co_impl(_Awaitable __awaitable, _Receiver __rcvr) -> __operation<_Receiver> { - using __result_t = __await_result_t<_Awaitable, __promise<_Receiver>>; - std::exception_ptr __eptr; - STDEXEC_TRY { - if constexpr (__std::same_as<__result_t, void>) - co_await ( - co_await static_cast<_Awaitable&&>(__awaitable), - __co_call(set_value, static_cast<_Receiver&&>(__rcvr))); - else - co_await __co_call( - set_value, - static_cast<_Receiver&&>(__rcvr), - co_await static_cast<_Awaitable&&>(__awaitable)); - } - STDEXEC_CATCH_ALL { - __eptr = std::current_exception(); - } - co_await __co_call( - set_error, static_cast<_Receiver&&>(__rcvr), static_cast(__eptr)); + static auto __co_impl(_Awaitable __awaitable, _Receiver __rcvr) + -> __connect_await::__operation<_Receiver> { + using __result_t = __await_result_t<_Awaitable, __connect_await::__promise<_Receiver>>; + std::exception_ptr __eptr; + STDEXEC_TRY { + if constexpr (__std::same_as<__result_t, void>) + co_await ( + co_await static_cast<_Awaitable&&>(__awaitable), + __co_call(set_value, static_cast<_Receiver&&>(__rcvr))); + else + co_await __co_call( + set_value, + static_cast<_Receiver&&>(__rcvr), + co_await static_cast<_Awaitable&&>(__awaitable)); + } + STDEXEC_CATCH_ALL { + __eptr = std::current_exception(); } + co_await __co_call( + set_error, static_cast<_Receiver&&>(__rcvr), static_cast(__eptr)); + } - template + template using __completions_t = completion_signatures< __minvoke< // set_value_t() or set_value_t(T) __mremove>, - __await_result_t<_Awaitable, __promise<_Receiver>>>, + __await_result_t<_Awaitable, __connect_await::__promise<_Receiver>>>, set_error_t(std::exception_ptr), set_stopped_t()>; - public: - template > _Awaitable> - requires receiver_of<_Receiver, __completions_t<_Receiver, _Awaitable>> - auto operator()(_Awaitable&& __awaitable, _Receiver __rcvr) const -> __operation<_Receiver> { - return __co_impl(static_cast<_Awaitable&&>(__awaitable), static_cast<_Receiver&&>(__rcvr)); - } - }; - } // namespace __connect_await + public: + template > _Awaitable> + requires receiver_of<_Receiver, __completions_t<_Receiver, _Awaitable>> + auto operator()(_Awaitable&& __awaitable, _Receiver __rcvr) const + -> __connect_await::__operation<_Receiver> { + return __co_impl(static_cast<_Awaitable&&>(__awaitable), static_cast<_Receiver&&>(__rcvr)); + } + }; - using __connect_await::__connect_awaitable_t; #else namespace __connect_await { template diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index 0b9dc57e0..1339f3de9 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -144,21 +144,6 @@ namespace STDEXEC { connect_result_t, __receiver2_t> __state2_; }; - struct continues_on_t { - template - constexpr auto - operator()(_Sender&& __sndr, _Scheduler __sched) const -> __well_formed_sender auto { - return __make_sexpr( - static_cast<_Scheduler&&>(__sched), schedule_from(static_cast<_Sender&&>(__sndr))); - } - - template - STDEXEC_ATTRIBUTE(always_inline) - constexpr auto operator()(_Scheduler __sched) const noexcept { - return __closure(*this, static_cast<_Scheduler&&>(__sched)); - } - }; - //! @brief The @c continues_on sender's attributes. template struct __attrs { @@ -401,7 +386,21 @@ namespace STDEXEC { }; } // namespace __trnsfr - using __trnsfr::continues_on_t; + struct continues_on_t { + template + constexpr auto + operator()(_Sender&& __sndr, _Scheduler __sched) const -> __well_formed_sender auto { + return __make_sexpr( + static_cast<_Scheduler&&>(__sched), schedule_from(static_cast<_Sender&&>(__sndr))); + } + + template + STDEXEC_ATTRIBUTE(always_inline) + constexpr auto operator()(_Scheduler __sched) const noexcept { + return __closure(*this, static_cast<_Scheduler&&>(__sched)); + } + }; + inline constexpr continues_on_t continues_on{}; template <> diff --git a/include/stdexec/__detail/__debug.hpp b/include/stdexec/__detail/__debug.hpp index b9a889f44..2846d69ec 100644 --- a/include/stdexec/__detail/__debug.hpp +++ b/include/stdexec/__detail/__debug.hpp @@ -29,13 +29,11 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // Some utilities for debugging senders - namespace __queries { - struct __debug_env_t : __query<__debug_env_t> { - static constexpr auto query(forwarding_query_t) noexcept -> bool { - return true; - } - }; - } // namespace __queries + struct __debug_env_t : __query<__debug_env_t> { + static constexpr auto query(forwarding_query_t) noexcept -> bool { + return true; + } + }; namespace __debug { struct _COMPLETION_SIGNATURES_MISMATCH_ { }; @@ -59,7 +57,7 @@ namespace STDEXEC { } template - using __env_t = env, _Env>; + using __env_t = env, _Env>; template struct __receiver { diff --git a/include/stdexec/__detail/__domain.hpp b/include/stdexec/__detail/__domain.hpp index 221a2b714..acb785ed3 100644 --- a/include/stdexec/__detail/__domain.hpp +++ b/include/stdexec/__detail/__domain.hpp @@ -204,206 +204,201 @@ namespace STDEXEC { } } // namespace __detail - namespace __queries { - //! @brief A wrapper around an environment that hides a set of queries. - template - struct __hide_query { - constexpr explicit __hide_query(_Env&& __env, _Queries...) noexcept - : __env_{static_cast<_Env&&>(__env)} { - } + //! @brief A wrapper around an environment that hides a set of queries. + template + struct __hide_query { + constexpr explicit __hide_query(_Env&& __env, _Queries...) noexcept + : __env_{static_cast<_Env&&>(__env)} { + } - template <__none_of<_Queries...> _Query, class... _As> - requires __queryable_with<_Env, _Query, _As...> - constexpr auto operator()(_Query, _As&&... __as) const - noexcept(__nothrow_queryable_with<_Env, _Query, _As...>) - -> __query_result_t<_Env, _Query, _As...> { - return __query<_Query>()(__env_, static_cast<_As&&>(__as)...); - } + template <__none_of<_Queries...> _Query, class... _As> + requires __queryable_with<_Env, _Query, _As...> + constexpr auto operator()(_Query, _As&&... __as) const + noexcept(__nothrow_queryable_with<_Env, _Query, _As...>) + -> __query_result_t<_Env, _Query, _As...> { + return __query<_Query>()(__env_, static_cast<_As&&>(__as)...); + } - private: - _Env __env_; - }; + private: + _Env __env_; + }; - template - STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE - __hide_query(_Env&&, _Queries...) -> __hide_query<_Env, _Queries...>; + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE + __hide_query(_Env&&, _Queries...) -> __hide_query<_Env, _Queries...>; - //! @brief A wrapper around an environment that hides the get_scheduler and get_domain - //! queries. - template - struct __hide_scheduler : __hide_query<_Env, get_scheduler_t, get_domain_t> { - constexpr explicit __hide_scheduler(_Env&& __env) noexcept - : __hide_query<_Env, get_scheduler_t, get_domain_t>{static_cast<_Env&&>(__env), {}, {}} { - } - }; - - template - STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __hide_scheduler(_Env&&) -> __hide_scheduler<_Env>; + //! @brief A wrapper around an environment that hides the get_scheduler and get_domain + //! queries. + template + struct __hide_scheduler : __hide_query<_Env, get_scheduler_t, get_domain_t> { + constexpr explicit __hide_scheduler(_Env&& __env) noexcept + : __hide_query<_Env, get_scheduler_t, get_domain_t>{static_cast<_Env&&>(__env), {}, {}} { + } + }; - ////////////////////////////////////////////////////////////////////////////////////////// - //! @brief A query type for asking a sender's attributes for the domain on which that - //! sender will complete. As with @c get_domain, it is used in tag dispatching to find a - //! custom implementation of a sender algorithm. - //! - //! @tparam _Tag one of set_value_t, set_error_t, or set_stopped_t - template - struct get_completion_domain_t { - template - static inline constexpr get_completion_domain_t<_Tag> (*signature)(Sig) = nullptr; - - // This function object reads the completion domain from an attribute object or a - // scheduler, accounting for the fact that the query member function may or may not - // accept an environment. - struct __read_query_t { - template - requires __queryable_with<_Attrs, get_completion_domain_t> - constexpr auto operator()(const _Attrs&, __ignore = {}) const noexcept { - return __decay_t<__query_result_t<_Attrs, get_completion_domain_t>>{}; - } + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __hide_scheduler(_Env&&) -> __hide_scheduler<_Env>; - template - requires __queryable_with<_Attrs, get_completion_domain_t, const _Env&> - constexpr auto operator()(const _Attrs&, const _Env&) const noexcept { - return __decay_t<__query_result_t<_Attrs, get_completion_domain_t, const _Env&>>{}; - } - }; + ////////////////////////////////////////////////////////////////////////////////////////// + //! @brief A query type for asking a sender's attributes for the domain on which that + //! sender will complete. As with @c get_domain, it is used in tag dispatching to find a + //! custom implementation of a sender algorithm. + //! + //! @tparam _Tag one of set_value_t, set_error_t, or set_stopped_t + template + struct get_completion_domain_t { + template + static inline constexpr get_completion_domain_t<_Tag> (*signature)(Sig) = nullptr; + + // This function object reads the completion domain from an attribute object or a + // scheduler, accounting for the fact that the query member function may or may not + // accept an environment. + struct __read_query_t { + template + requires __queryable_with<_Attrs, get_completion_domain_t> + constexpr auto operator()(const _Attrs&, __ignore = {}) const noexcept { + return __decay_t<__query_result_t<_Attrs, get_completion_domain_t>>{}; + } - private: - template - static consteval auto __check_domain_(_Domain) noexcept { - static_assert( - __same_as<_Domain, __detail::__scheduler_domain_t<_Sch, const _Env&...>>, - "the sender claims to complete on a domain that is not the domain of its completion " - "scheduler"); + template + requires __queryable_with<_Attrs, get_completion_domain_t, const _Env&> + constexpr auto operator()(const _Attrs&, const _Env&) const noexcept { + return __decay_t<__query_result_t<_Attrs, get_completion_domain_t, const _Env&>>{}; } + }; + + private: + template + static consteval auto __check_domain_(_Domain) noexcept { + static_assert( + __same_as<_Domain, __detail::__scheduler_domain_t<_Sch, const _Env&...>>, + "the sender claims to complete on a domain that is not the domain of its completion " + "scheduler"); + } - template - static consteval auto __check_domain(_Domain) noexcept -> _Domain { - // Sanity check: if a completion scheduler can be determined from the attributes - // (not the environment), then its domain must match the domain returned by the attributes. - if constexpr (!__same_as<_Tag, void>) { - if constexpr (__callable, const _Attrs&, const _Env&...>) { - using __sch_t = - __call_result_t, const _Attrs&, const _Env&...>; - // Skip check if the "scheduler" is the same as the domain or the attributes - // (this can happen with __prop_like which answers any query with the same type) - if constexpr (!__same_as<__sch_t, _Attrs>) { - __check_domain_<__sch_t, _Env...>(_Domain{}); - } + template + static consteval auto __check_domain(_Domain) noexcept -> _Domain { + // Sanity check: if a completion scheduler can be determined from the attributes + // (not the environment), then its domain must match the domain returned by the attributes. + if constexpr (!__same_as<_Tag, void>) { + if constexpr (__callable, const _Attrs&, const _Env&...>) { + using __sch_t = + __call_result_t, const _Attrs&, const _Env&...>; + // Skip check if the "scheduler" is the same as the domain or the attributes + // (this can happen with __prop_like which answers any query with the same type) + if constexpr (!__same_as<__sch_t, _Attrs>) { + __check_domain_<__sch_t, _Env...>(_Domain{}); } } - return {}; } + return {}; + } - template - static constexpr auto __get_domain() noexcept { - // If __attrs has a completion domain, then return it: - if constexpr (__callable<__read_query_t, const _Attrs&, const _Env&...>) { - using __domain_t = __call_result_t<__read_query_t, const _Attrs&, const _Env&...>; + template + static constexpr auto __get_domain() noexcept { + // If __attrs has a completion domain, then return it: + if constexpr (__callable<__read_query_t, const _Attrs&, const _Env&...>) { + using __domain_t = __call_result_t<__read_query_t, const _Attrs&, const _Env&...>; + return __check_domain<_Attrs, _Env...>(__domain_t{}); + // Otherwise, if _Tag is void, fall back to querying for the set_value_t completion domain: + } else if constexpr (__same_as<_Tag, void>) { + if constexpr ( + __callable, const _Attrs&, const _Env&...>) { + using __domain_t = + __call_result_t, const _Attrs&, const _Env&...>; return __check_domain<_Attrs, _Env...>(__domain_t{}); - // Otherwise, if _Tag is void, fall back to querying for the set_value_t completion domain: - } else if constexpr (__same_as<_Tag, void>) { - if constexpr ( - __callable, const _Attrs&, const _Env&...>) { - using __domain_t = - __call_result_t, const _Attrs&, const _Env&...>; - return __check_domain<_Attrs, _Env...>(__domain_t{}); - } else { - return void(); - } + } else { + return void(); } - // Otherwise, if __attrs has a completion scheduler, we can ask that scheduler for its - // completion domain. - else if constexpr ( - __callable, const _Attrs&, const _Env&...>) { - using __sch_t = - __call_result_t, const _Attrs&, const _Env&...>; - using X [[maybe_unused]] = decltype(__declval<__sch_t>().schedule()); - using __read_query_t = typename get_completion_domain_t::__read_query_t; - - if constexpr (__callable<__read_query_t, __sch_t, const _Env&...>) { - using __domain_t = __call_result_t<__read_query_t, __sch_t, const _Env&...>; - return __domain_t{}; - } - // Otherwise, if the scheduler's sender indicates that it completes inline, we can ask - // the environment for its domain. - else if constexpr ( - __completes_inline<_Tag, env_of_t<__call_result_t>, _Env...> - && __callable) { - return __call_result_t{}; - } - // Otherwise, if we are asking "late" (with an environment), return the default_domain - else if constexpr (sizeof...(_Env) != 0) { - return default_domain{}; - } + } + // Otherwise, if __attrs has a completion scheduler, we can ask that scheduler for its + // completion domain. + else if constexpr ( + __callable, const _Attrs&, const _Env&...>) { + using __sch_t = + __call_result_t, const _Attrs&, const _Env&...>; + using X [[maybe_unused]] = decltype(__declval<__sch_t>().schedule()); + using __read_query_t = typename get_completion_domain_t::__read_query_t; + + if constexpr (__callable<__read_query_t, __sch_t, const _Env&...>) { + using __domain_t = __call_result_t<__read_query_t, __sch_t, const _Env&...>; + return __domain_t{}; } - // Otherwise, if the attributes indicates that the sender completes inline, we can ask + // Otherwise, if the scheduler's sender indicates that it completes inline, we can ask // the environment for its domain. else if constexpr ( - __completes_inline<_Tag, _Attrs, _Env...> && __callable) { + __completes_inline<_Tag, env_of_t<__call_result_t>, _Env...> + && __callable) { return __call_result_t{}; } // Otherwise, if we are asking "late" (with an environment), return the default_domain else if constexpr (sizeof...(_Env) != 0) { return default_domain{}; } - // Otherwise, no completion domain can be determined. Return void. } + // Otherwise, if the attributes indicates that the sender completes inline, we can ask + // the environment for its domain. + else if constexpr ( + __completes_inline<_Tag, _Attrs, _Env...> && __callable) { + return __call_result_t{}; + } + // Otherwise, if we are asking "late" (with an environment), return the default_domain + else if constexpr (sizeof...(_Env) != 0) { + return default_domain{}; + } + // Otherwise, no completion domain can be determined. Return void. + } - template - using __result_t = __unless_one_of_t()), void>; + template + using __result_t = __unless_one_of_t()), void>; - public: - template - constexpr auto - operator()(const _Attrs&, const _Env&...) const noexcept -> __result_t<_Attrs, _Env...> { - return {}; - } + public: + template + constexpr auto + operator()(const _Attrs&, const _Env&...) const noexcept -> __result_t<_Attrs, _Env...> { + return {}; + } - static constexpr auto query(forwarding_query_t) noexcept -> bool { - return true; - } - }; + static constexpr auto query(forwarding_query_t) noexcept -> bool { + return true; + } + }; - struct get_domain_t { - template - static inline constexpr get_domain_t (*signature)(_Sig) = nullptr; - - // Query with a .query member function: - template - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()(const _Env&) const noexcept -> auto { - if constexpr (__member_queryable_with) { - return __decay_t<__member_query_result_t<_Env, get_domain_t>>{}; - } else if constexpr (__callable) { - using __sch_t = __call_result_t; - using __env_t = __hide_scheduler; - using __cmpl_sch_t = - __call_result_t, __sch_t, __env_t>; - return __detail::__scheduler_domain_t<__cmpl_sch_t, __env_t>{}; - } else { - return default_domain{}; - } - } + struct get_domain_t { + template + static inline constexpr get_domain_t (*signature)(_Sig) = nullptr; - // Query with tag_invoke (legacy): - template - requires __tag_invocable - [[deprecated("use a query member function instead of tag_invoke for queries")]] - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) // - constexpr auto operator()(const _Env&) const noexcept { - return __decay_t<__tag_invoke_result_t>{}; + // Query with a .query member function: + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(const _Env&) const noexcept -> auto { + if constexpr (__member_queryable_with) { + return __decay_t<__member_query_result_t<_Env, get_domain_t>>{}; + } else if constexpr (__callable) { + using __sch_t = __call_result_t; + using __env_t = __hide_scheduler; + using __cmpl_sch_t = + __call_result_t, __sch_t, __env_t>; + return __detail::__scheduler_domain_t<__cmpl_sch_t, __env_t>{}; + } else { + return default_domain{}; } + } - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static consteval auto query(forwarding_query_t) noexcept -> bool { - return true; - } - }; - } // namespace __queries + // Query with tag_invoke (legacy): + template + requires __tag_invocable + [[deprecated("use a query member function instead of tag_invoke for queries")]] + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) // + constexpr auto operator()(const _Env&) const noexcept { + return __decay_t<__tag_invoke_result_t>{}; + } - using __queries::get_completion_domain_t; - using __queries::get_domain_t; + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static consteval auto query(forwarding_query_t) noexcept -> bool { + return true; + } + }; #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__) template diff --git a/include/stdexec/__detail/__env.hpp b/include/stdexec/__detail/__env.hpp index 7496ba32d..f994bac4d 100644 --- a/include/stdexec/__detail/__env.hpp +++ b/include/stdexec/__detail/__env.hpp @@ -34,32 +34,8 @@ namespace STDEXEC { ////////////////////////////////////////////////////////////////////////////////////////////////// // [exec.envs] namespace __env { - // A singleton environment from a query/value pair - template - struct prop { - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto query(_Query, auto&&...) const noexcept -> const _Value& { - return __value; - } - - STDEXEC_ATTRIBUTE(no_unique_address) _Query __query; - STDEXEC_ATTRIBUTE(no_unique_address) _Value __value; - - private: - struct __prop_like { - STDEXEC_ATTRIBUTE(noreturn, nodiscard, host, device) - constexpr auto query(_Query) const noexcept -> const _Value& { - STDEXEC_TERMINATE(); - } - }; - - static_assert(__callable<_Query, __prop_like>); - }; - - template - STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE - prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>; - + ////////////////////////////////////////////////////////////////////// + // cprop template struct cprop { STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) @@ -68,60 +44,6 @@ namespace STDEXEC { } }; - ////////////////////////////////////////////////////////////////////// - // env - template - struct env; - - template <> - struct env<> { - STDEXEC_ATTRIBUTE(nodiscard, host, device) - auto query() const = delete; - }; - - template - struct env : Env { }; - - template - struct env { - template - requires __queryable_with - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto query(Query, _Args&&... __args) const - noexcept(__nothrow_queryable_with) - -> __query_result_t { - return __query()(env_, static_cast<_Args&&>(__args)...); - } - - Env& env_; - }; - - template - using __env_base_t = __if_c, env, Env>; - - template - struct env : __env_base_t { - using __env_base_t::query; - - template - requires(!__queryable_with) - && __queryable_with - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto query(Query, _Args&&... __args) const - noexcept(__nothrow_queryable_with) - -> __query_result_t { - return __query()(env2_, static_cast<_Args&&>(__args)...); - } - - STDEXEC_ATTRIBUTE(no_unique_address) Env2 env2_; - }; - - template - struct env : env, Envs...> { }; - - template - STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE env(_Envs...) -> env...>; - template struct __fwd { static_assert(__nothrow_move_constructible<_Env>); @@ -218,57 +140,129 @@ namespace STDEXEC { { __root_t{}(__env) } -> __std::same_as; }; + ////////////////////////////////////////////////////////////////////// + // A singleton environment from a query/value pair + template + struct prop { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(_Query, auto&&...) const noexcept -> const _Value& { + return __value; + } + + STDEXEC_ATTRIBUTE(no_unique_address) _Query __query; + STDEXEC_ATTRIBUTE(no_unique_address) _Value __value; + + private: + struct __prop_like { + STDEXEC_ATTRIBUTE(noreturn, nodiscard, host, device) + constexpr auto query(_Query) const noexcept -> const _Value& { + STDEXEC_TERMINATE(); + } + }; + + static_assert(__callable<_Query, __prop_like>); + }; + + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE + prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>; + + ////////////////////////////////////////////////////////////////////// + // env + template + struct env; + + template <> + struct env<> { + STDEXEC_ATTRIBUTE(nodiscard, host, device) + auto query() const = delete; + }; + + template + struct env : Env { }; + + template + struct env { + template + requires __queryable_with + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(Query, _Args&&... __args) const + noexcept(__nothrow_queryable_with) + -> __query_result_t { + return __query()(env_, static_cast<_Args&&>(__args)...); + } + + Env& env_; + }; + + template + using __env_base_t = __if_c, env, Env>; + + template + struct env : __env_base_t { + using __env_base_t::query; + + template + requires(!__queryable_with) && __queryable_with + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(Query, _Args&&... __args) const + noexcept(__nothrow_queryable_with) + -> __query_result_t { + return __query()(env2_, static_cast<_Args&&>(__args)...); + } + + STDEXEC_ATTRIBUTE(no_unique_address) Env2 env2_; + }; + + template + struct env : env, Envs...> { }; + + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE env(_Envs...) -> env...>; ///////////////////////////////////////////////////////////////////////////// - namespace __get_env { + namespace __detail { template using __get_env_member_result_t = decltype(__declval<_EnvProvider>().get_env()); template - concept __has_get_env = requires { typename __get_env_member_result_t<_EnvProvider>; }; + concept __has_get_env_member = requires { typename __get_env_member_result_t<_EnvProvider>; }; + } // namespace __detail - // For getting an execution environment from a receiver or the attributes from a sender. - struct get_env_t { - template - requires __has_get_env - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept - -> __get_env_member_result_t { - static_assert(noexcept(__env_provider.get_env()), "get_env() members must be noexcept"); - return __env_provider.get_env(); - } - - template - requires __has_get_env - || __tag_invocable - [[deprecated("the use of tag_invoke for get_env is deprecated")]] - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) // - constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept - -> __tag_invoke_result_t { - static_assert( - __nothrow_tag_invocable, - "get_env __tag_invoke overloads must be noexcept"); - return __tag_invoke(*this, __env_provider); - } + // For getting an execution environment from a receiver or the attributes from a sender. + struct get_env_t { + template + requires __detail::__has_get_env_member + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept + -> __detail::__get_env_member_result_t { + static_assert(noexcept(__env_provider.get_env()), "get_env() members must be noexcept"); + return __env_provider.get_env(); + } - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()(__ignore) const noexcept -> env<> { - return {}; - } - }; - } // namespace __get_env + template + requires __detail::__has_get_env_member + || __tag_invocable + [[deprecated("the use of tag_invoke for get_env is deprecated")]] + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) // + constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept + -> __tag_invoke_result_t { + static_assert( + __nothrow_tag_invocable, + "get_env __tag_invoke overloads must be noexcept"); + return __tag_invoke(*this, __env_provider); + } + + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(__ignore) const noexcept -> env<> { + return {}; + } + }; - using __get_env::get_env_t; inline constexpr get_env_t get_env{}; - // template - // concept environment_provider = requires(_EnvProvider& __ep) { - // { get_env(std::as_const(__ep)) } -> queryable; - // }; - template - concept environment_provider = __minvocable_q<__call_result_t, get_env_t, const _EnvProvider&>; - + concept __environment_provider = __minvocable_q<__call_result_t, get_env_t, const _EnvProvider&>; } // namespace STDEXEC STDEXEC_PRAGMA_POP() diff --git a/include/stdexec/__detail/__execute.hpp b/include/stdexec/__detail/__execute.hpp index 7d45eb13f..19c7b38d2 100644 --- a/include/stdexec/__detail/__execute.hpp +++ b/include/stdexec/__detail/__execute.hpp @@ -28,27 +28,23 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // [execution.execute] - namespace __execute_ { - struct execute_t { - template - requires __callable<_Fun&> && __std::move_constructible<_Fun> - void operator()(_Scheduler&& __sched, _Fun __fun) const noexcept(false) { - auto __domain = get_domain(__sched); - STDEXEC::apply_sender( - __domain, - *this, - schedule(static_cast<_Scheduler&&>(__sched)), - static_cast<_Fun&&>(__fun)); - } + struct __execute_t { + template + requires __callable<_Fun&> && __std::move_constructible<_Fun> + void operator()(_Scheduler&& __sched, _Fun __fun) const noexcept(false) { + auto __domain = get_domain(__sched); + STDEXEC::apply_sender( + __domain, *this, schedule(static_cast<_Scheduler&&>(__sched)), static_cast<_Fun&&>(__fun)); + } - template _Sender, class _Fun> - requires __callable<_Fun&> && __std::move_constructible<_Fun> - void apply_sender(_Sender&& __sndr, _Fun __fun) const noexcept(false) { - start_detached(then(static_cast<_Sender&&>(__sndr), static_cast<_Fun&&>(__fun))); - } - }; - } // namespace __execute_ + template _Sender, class _Fun> + requires __callable<_Fun&> && __std::move_constructible<_Fun> + void apply_sender(_Sender&& __sndr, _Fun __fun) const noexcept(false) { + start_detached(then(static_cast<_Sender&&>(__sndr), static_cast<_Fun&&>(__fun))); + } + }; - using __execute_::execute_t; + using execute_t [[deprecated]] = __execute_t; + [[deprecated]] inline constexpr execute_t execute{}; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__execution_fwd.hpp b/include/stdexec/__detail/__execution_fwd.hpp index c6be96ab0..08dd01702 100644 --- a/include/stdexec/__detail/__execution_fwd.hpp +++ b/include/stdexec/__detail/__execution_fwd.hpp @@ -64,15 +64,10 @@ namespace STDEXEC { struct indeterminate_domain; ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __rcvrs { - struct set_value_t; - struct set_error_t; - struct set_stopped_t; - } // namespace __rcvrs - - using __rcvrs::set_value_t; - using __rcvrs::set_error_t; - using __rcvrs::set_stopped_t; + struct set_value_t; + struct set_error_t; + struct set_stopped_t; + extern const set_value_t set_value; extern const set_error_t set_error; extern const set_stopped_t set_stopped; @@ -80,33 +75,24 @@ namespace STDEXEC { template concept __completion_tag = __one_of<_Tag, set_value_t, set_error_t, set_stopped_t>; - struct receiver_t; - template extern const bool enable_receiver; namespace __env { - template - struct prop; - template struct cprop; - - template - struct env; } // namespace __env - using __env::prop; - using __env::cprop; - using __env::env; + template + struct prop; + + template + struct env; + using empty_env [[deprecated("STDEXEC::empty_env is now spelled STDEXEC::env<>")]] = env<>; ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __get_env { - struct get_env_t; - } // namespace __get_env - - using __get_env::get_env_t; + struct get_env_t; extern const get_env_t get_env; template @@ -119,44 +105,24 @@ namespace STDEXEC { weakly_parallel }; - namespace __queries { - struct forwarding_query_t; - struct execute_may_block_caller_t; - struct get_forward_progress_guarantee_t; - struct get_scheduler_t; - struct get_delegation_scheduler_t; - struct get_allocator_t; - struct get_stop_token_t; - template <__completion_tag _CPO> - struct get_completion_scheduler_t; - template - struct get_completion_domain_t; - template <__completion_tag _CPO> - struct get_completion_behavior_t; - struct get_domain_t; - - struct __debug_env_t; - } // namespace __queries - - using __queries::forwarding_query_t; - using __queries::execute_may_block_caller_t; - using __queries::get_forward_progress_guarantee_t; - using __queries::get_allocator_t; - using __queries::get_scheduler_t; - using __queries::get_delegation_scheduler_t; - using __queries::get_stop_token_t; - using __queries::get_completion_scheduler_t; - using __queries::get_completion_domain_t; - using __queries::get_completion_behavior_t; - using __queries::get_domain_t; - - extern const forwarding_query_t forwarding_query; - extern const execute_may_block_caller_t execute_may_block_caller; + struct __execute_may_block_caller_t; + struct get_forward_progress_guarantee_t; + struct get_scheduler_t; + struct get_delegation_scheduler_t; + template <__completion_tag _CPO> + struct get_completion_scheduler_t; + template + struct get_completion_domain_t; + template <__completion_tag _CPO> + struct get_completion_behavior_t; + struct get_domain_t; + + struct __debug_env_t; + + extern const __execute_may_block_caller_t execute_may_block_caller; extern const get_forward_progress_guarantee_t get_forward_progress_guarantee; extern const get_scheduler_t get_scheduler; extern const get_delegation_scheduler_t get_delegation_scheduler; - extern const get_allocator_t get_allocator; - extern const get_stop_token_t get_stop_token; template <__completion_tag _CPO> extern const get_completion_scheduler_t<_CPO> get_completion_scheduler; template @@ -164,25 +130,23 @@ namespace STDEXEC { extern const get_domain_t get_domain; template - concept __is_debug_env = __callable<__queries::__debug_env_t, _Env>; + concept __is_debug_env = __callable<__debug_env_t, _Env>; namespace __debug { struct __completion_signatures { }; } // namespace __debug + ////////////////////////////////////////////////////////////////////////////////////////////////// + // concept tag types: + struct sender_t; + struct operation_state_t; + struct scheduler_t; + struct receiver_t; + template STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto get_completion_behavior() noexcept; - struct never_stop_token; - class inplace_stop_source; - class inplace_stop_token; - template - class inplace_stop_callback; - - template - using stop_token_of_t = __decay_t<__call_result_t>; - template using __domain_of_t = __decay_t<__call_result_t>; @@ -221,47 +185,25 @@ namespace STDEXEC { consteval auto __throw_compile_time_error(__mexception<_What...>); ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __connect { - struct connect_t; - } // namespace __connect - - using __connect::connect_t; + struct connect_t; extern const connect_t connect; template using connect_result_t = __call_result_t; - struct sender_t; - template extern const bool enable_sender; ////////////////////////////////////////////////////////////////////////////////////////////////// - struct operation_state_t; - - namespace __start { - struct start_t; - } // namespace __start - - using __start::start_t; + struct start_t; extern const start_t start; ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __sched { - struct schedule_t; - } // namespace __sched - - using __sched::schedule_t; + struct schedule_t; extern const schedule_t schedule; - struct scheduler_t; - ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __as_awaitable { - struct as_awaitable_t; - } // namespace __as_awaitable - - using __as_awaitable::as_awaitable_t; + struct as_awaitable_t; extern const as_awaitable_t as_awaitable; ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -272,11 +214,7 @@ namespace STDEXEC { using transform_sender_result_t = __call_result_t; ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __starts_on_ns { - struct starts_on_t; - } // namespace __starts_on_ns - - using __starts_on_ns::starts_on_t; + struct starts_on_t; extern const starts_on_t starts_on; using start_on_t [[deprecated("start_on_t has been renamed starts_on_t")]] = starts_on_t; @@ -284,18 +222,10 @@ namespace STDEXEC { extern const starts_on_t start_on; ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __schfr { - struct schedule_from_t; - } // namespace __schfr - - using __schfr::schedule_from_t; + struct schedule_from_t; extern const schedule_from_t schedule_from; - namespace __trnsfr { - struct continues_on_t; - } // namespace __trnsfr - - using __trnsfr::continues_on_t; + struct continues_on_t; extern const continues_on_t continues_on; // Backward compatibility: @@ -303,40 +233,39 @@ namespace STDEXEC { [[deprecated("transfer has been renamed continues_on")]] inline constexpr const continues_on_t& transfer = continues_on; - // Backward compatibility: - namespace v2 { - using continue_on_t - [[deprecated("continue_on_t has been renamed continues_on_t")]] = continues_on_t; - [[deprecated("continue_on has been renamed continues_on")]] - inline constexpr const continues_on_t& continue_on = continues_on; - } // namespace v2 - - // Backward compatibility: - using v2::continue_on_t; - using v2::continue_on; - ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __transfer_just { - struct transfer_just_t; - } // namespace __transfer_just - - using __transfer_just::transfer_just_t; - extern const transfer_just_t transfer_just; + struct __transfer_just_t; + extern const __transfer_just_t transfer_just; ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __bulk { - struct bulk_t; - struct bulk_chunked_t; - struct bulk_unchunked_t; - } // namespace __bulk - - using __bulk::bulk_t; - using __bulk::bulk_chunked_t; - using __bulk::bulk_unchunked_t; + struct bulk_t; + struct bulk_chunked_t; + struct bulk_unchunked_t; + extern const bulk_t bulk; extern const bulk_chunked_t bulk_chunked; extern const bulk_unchunked_t bulk_unchunked; + ////////////////////////////////////////////////////////////////////////////////////////////////// + struct just_t; + extern const just_t just; + + struct just_error_t; + extern const just_error_t just_error; + + struct just_stopped_t; + extern const just_stopped_t just_stopped; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + struct then_t; + extern const then_t then; + + struct upon_error_t; + extern const upon_error_t upon_error; + + struct upon_stopped_t; + extern const upon_stopped_t upon_stopped; + ////////////////////////////////////////////////////////////////////////////////////////////////// struct let_value_t; extern const let_value_t let_value; @@ -347,6 +276,24 @@ namespace STDEXEC { struct let_stopped_t; extern const let_stopped_t let_stopped; + ////////////////////////////////////////////////////////////////////////////////////////////////// + struct when_all_t; + extern const when_all_t when_all; + + struct when_all_with_variant_t; + extern const when_all_with_variant_t when_all_with_variant; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + struct __read_env_t; + extern const __read_env_t read_env; + + struct __write_env_t; + extern const __write_env_t write_env; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + struct into_variant_t; + extern const into_variant_t into_variant; + ////////////////////////////////////////////////////////////////////////////////////////////////// namespace __split { struct split_t; @@ -366,10 +313,68 @@ namespace STDEXEC { extern const ensure_started_t ensure_started; ////////////////////////////////////////////////////////////////////////////////////////////////// - namespace __on { - struct on_t; - } // namespace __on - - using __on::on_t; + struct on_t; extern const on_t on; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + struct stopped_as_error_t; + extern const stopped_as_error_t stopped_as_error; + + struct stopped_as_optional_t; + extern const stopped_as_optional_t stopped_as_optional; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + template <__class _Derived> + struct sender_adaptor_closure; +} // namespace STDEXEC + +STDEXEC_P2300_NAMESPACE_BEGIN() +struct forwarding_query_t; +struct get_allocator_t; +struct get_stop_token_t; + +extern const forwarding_query_t forwarding_query; +extern const get_allocator_t get_allocator; +extern const get_stop_token_t get_stop_token; + +template +using stop_token_of_t = STDEXEC::__decay_t>; + +struct never_stop_token; +class inplace_stop_source; +class inplace_stop_token; +template +class inplace_stop_callback; +STDEXEC_P2300_NAMESPACE_END() + +//////////////////////////////////////////////////////////////////////////////////////////////////// +STDEXEC_P2300_NAMESPACE_BEGIN(this_thread) +struct sync_wait_t; +struct sync_wait_with_variant_t; +extern const sync_wait_t sync_wait; +extern const sync_wait_with_variant_t sync_wait_with_variant; +STDEXEC_P2300_NAMESPACE_END(this_thread) + +// NOT TO SPEC: make sync_wait et. al. available in namespace STDEXEC (possibly +// std::execution) as well: +namespace STDEXEC { + STDEXEC_P2300_DEPRECATED_SYMBOL(std::forwarding_query_t) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::get_allocator_t) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::get_stop_token_t) + + STDEXEC_P2300_DEPRECATED_SYMBOL(std::forwarding_query) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::get_stop_token) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::get_allocator) + + STDEXEC_P2300_DEPRECATED_SYMBOL(std::stop_token_of_t) + + STDEXEC_P2300_DEPRECATED_SYMBOL(std::never_stop_token) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::inplace_stop_source) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::inplace_stop_token) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::inplace_stop_callback) + + STDEXEC_P2300_DEPRECATED_SYMBOL(std::this_thread::sync_wait_t) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::this_thread::sync_wait) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::this_thread::sync_wait_with_variant_t) + STDEXEC_P2300_DEPRECATED_SYMBOL(std::this_thread::sync_wait_with_variant) } // namespace STDEXEC diff --git a/include/stdexec/__detail/__into_variant.hpp b/include/stdexec/__detail/__into_variant.hpp index a6c601b43..630eea826 100644 --- a/include/stdexec/__detail/__into_variant.hpp +++ b/include/stdexec/__detail/__into_variant.hpp @@ -56,22 +56,9 @@ namespace STDEXEC { __mconst>::__f >; - struct into_variant_t { - template - constexpr auto operator()(_Sender&& __sndr) const -> __well_formed_sender auto { - return __make_sexpr(__(), static_cast<_Sender&&>(__sndr)); - } - - STDEXEC_ATTRIBUTE(always_inline) - auto operator()() const noexcept { - return __closure(*this); - } - }; - template struct __state { using __variant_t = _Variant; - STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Receiver __rcvr_; }; @@ -90,7 +77,7 @@ namespace STDEXEC { if constexpr (__same_as<_Tag, set_value_t>) { using __variant_t = _State::__variant_t; STDEXEC_TRY { - set_value( + STDEXEC::set_value( static_cast<_State&&>(__state).__rcvr_, __variant_t{std::tuple<_Args&&...>{static_cast<_Args&&>(__args)...}}); } @@ -110,7 +97,18 @@ namespace STDEXEC { }; } // namespace __into_variant - using __into_variant::into_variant_t; + struct into_variant_t { + template + constexpr auto operator()(_Sender&& __sndr) const -> __well_formed_sender auto { + return __make_sexpr(__(), static_cast<_Sender&&>(__sndr)); + } + + STDEXEC_ATTRIBUTE(always_inline) + constexpr auto operator()() const noexcept { + return __closure(*this); + } + }; + inline constexpr into_variant_t into_variant{}; template <> diff --git a/include/stdexec/__detail/__just.hpp b/include/stdexec/__detail/__just.hpp index c2652436c..cb8a24423 100644 --- a/include/stdexec/__detail/__just.hpp +++ b/include/stdexec/__detail/__just.hpp @@ -71,41 +71,37 @@ namespace STDEXEC { static_cast<_Receiver&&>(__rcvr), STDEXEC::__forward_like<_Sender>(__data)}; }; }; - - struct just_t { - using __tag_t = set_value_t; - - template <__movable_value... _Ts> - STDEXEC_ATTRIBUTE(host, device) - constexpr auto operator()(_Ts&&... __ts) const noexcept(__nothrow_decay_copyable<_Ts...>) { - return __make_sexpr(__tuple{static_cast<_Ts&&>(__ts)...}); - } - }; - - struct just_error_t { - using __tag_t = set_error_t; - - template <__movable_value _Error> - STDEXEC_ATTRIBUTE(host, device) - constexpr auto operator()(_Error&& __err) const noexcept(__nothrow_decay_copyable<_Error>) { - return __make_sexpr(__tuple{static_cast<_Error&&>(__err)}); - } - }; - - struct just_stopped_t { - using __tag_t = set_stopped_t; - - template - STDEXEC_ATTRIBUTE(host, device) - constexpr auto operator()() const noexcept { - return __make_sexpr<_Tag>(__tuple{}); - } - }; } // namespace __just - using __just::just_t; - using __just::just_error_t; - using __just::just_stopped_t; + struct just_t { + using __tag_t = set_value_t; + + template <__movable_value... _Ts> + STDEXEC_ATTRIBUTE(host, device) + constexpr auto operator()(_Ts&&... __ts) const noexcept(__nothrow_decay_copyable<_Ts...>) { + return __make_sexpr(__tuple{static_cast<_Ts&&>(__ts)...}); + } + }; + + struct just_error_t { + using __tag_t = set_error_t; + + template <__movable_value _Error> + STDEXEC_ATTRIBUTE(host, device) + constexpr auto operator()(_Error&& __err) const noexcept(__nothrow_decay_copyable<_Error>) { + return __make_sexpr(__tuple{static_cast<_Error&&>(__err)}); + } + }; + + struct just_stopped_t { + using __tag_t = set_stopped_t; + + template + STDEXEC_ATTRIBUTE(host, device) + constexpr auto operator()() const noexcept { + return __make_sexpr<_Tag>(__tuple{}); + } + }; template <> struct __sexpr_impl : __just::__impl { }; diff --git a/include/stdexec/__detail/__on.hpp b/include/stdexec/__detail/__on.hpp index 8ae2ac3fb..6e1b83d59 100644 --- a/include/stdexec/__detail/__on.hpp +++ b/include/stdexec/__detail/__on.hpp @@ -52,108 +52,99 @@ namespace STDEXEC { >(); } }; + } // namespace __on - //////////////////////////////////////////////////////////////////////////////////////////////// - struct on_t { - template - constexpr auto - operator()(_Scheduler&& __sched, _Sender&& __sndr) const -> __well_formed_sender auto { - return __make_sexpr( - static_cast<_Scheduler&&>(__sched), static_cast<_Sender&&>(__sndr)); - } + //////////////////////////////////////////////////////////////////////////////////////////////// + struct on_t { + template + constexpr auto + operator()(_Scheduler&& __sched, _Sender&& __sndr) const -> __well_formed_sender auto { + return __make_sexpr(static_cast<_Scheduler&&>(__sched), static_cast<_Sender&&>(__sndr)); + } - template _Closure> - constexpr auto operator()(_Sender&& __sndr, _Scheduler&& __sched, _Closure&& __clsur) const - -> __well_formed_sender auto { - return __make_sexpr( - __tuple{static_cast<_Scheduler&&>(__sched), static_cast<_Closure&&>(__clsur)}, - static_cast<_Sender&&>(__sndr)); - } + template _Closure> + constexpr auto operator()(_Sender&& __sndr, _Scheduler&& __sched, _Closure&& __clsur) const + -> __well_formed_sender auto { + return __make_sexpr( + __tuple{static_cast<_Scheduler&&>(__sched), static_cast<_Closure&&>(__clsur)}, + static_cast<_Sender&&>(__sndr)); + } - template - STDEXEC_ATTRIBUTE(always_inline) - constexpr auto operator()(_Scheduler&& __sched, _Closure&& __clsur) const { - return __closure( - *this, static_cast<_Scheduler&&>(__sched), static_cast<_Closure&&>(__clsur)); - } + template + STDEXEC_ATTRIBUTE(always_inline) + constexpr auto operator()(_Scheduler&& __sched, _Closure&& __clsur) const { + return __closure(*this, static_cast<_Scheduler&&>(__sched), static_cast<_Closure&&>(__clsur)); + } - // This transform_sender overload handles the case where `on` was called like - // `on(sch, sndr)`. In this case, we find the old scheduler by looking in the - // receiver's environment. - template <__decay_copyable _Sender, class _Env> - requires scheduler<__data_of<_Sender>> - STDEXEC_ATTRIBUTE(always_inline) - static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env& __env) { - auto& [__tag, __sched, __child] = __sndr; - auto __old = __with_default(get_scheduler, __end_sched_t<_Sender, _Env>())(__env); - - return continues_on( - starts_on( - STDEXEC::__forward_like<_Sender>(__sched), STDEXEC::__forward_like<_Sender>(__child)), - std::move(__old)); - } + // This transform_sender overload handles the case where `on` was called like + // `on(sch, sndr)`. In this case, we find the old scheduler by looking in the + // receiver's environment. + template <__decay_copyable _Sender, class _Env> + requires scheduler<__data_of<_Sender>> + STDEXEC_ATTRIBUTE(always_inline) + static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env& __env) { + auto& [__tag, __sched, __child] = __sndr; + auto __old = __with_default(get_scheduler, __end_sched_t<_Sender, _Env>())(__env); + + return continues_on( + starts_on( + STDEXEC::__forward_like<_Sender>(__sched), STDEXEC::__forward_like<_Sender>(__child)), + std::move(__old)); + } - // This transform_sender overload handles the case where `on` was called like - // `sndr | on(sch, clsur)` or `on(sndr, sch, clsur)`. In this case, __child is a - // predecessor sender, so the scheduler we want to restore is the completion - // scheduler of __child. - template <__decay_copyable _Sender, class _Env> - requires(!scheduler<__data_of<_Sender>>) - STDEXEC_ATTRIBUTE(always_inline) - static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env& __env) { - auto& [__tag, __data, __child] = __sndr; - auto& [__sched, __clsur] = __data; - - auto __old = __with_default( - get_completion_scheduler, - __end_sched_t<_Sender, _Env>())(get_env(__child), __env); - - auto __pred = __reschedule(STDEXEC::__forward_like<_Sender>(__child), __old, __sched); - return __reschedule( - STDEXEC::__forward_like<_Sender>(__clsur)(std::move(__pred)), - std::move(__sched), - std::move(__old)); - } + // This transform_sender overload handles the case where `on` was called like + // `sndr | on(sch, clsur)` or `on(sndr, sch, clsur)`. In this case, __child is a + // predecessor sender, so the scheduler we want to restore is the completion + // scheduler of __child. + template <__decay_copyable _Sender, class _Env> + requires(!scheduler<__data_of<_Sender>>) + STDEXEC_ATTRIBUTE(always_inline) + static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env& __env) { + auto& [__tag, __data, __child] = __sndr; + auto& [__sched, __clsur] = __data; + + auto __old = __with_default( + get_completion_scheduler, + __end_sched_t<_Sender, _Env>())(get_env(__child), __env); + + auto __pred = __reschedule(STDEXEC::__forward_like<_Sender>(__child), __old, __sched); + return __reschedule( + STDEXEC::__forward_like<_Sender>(__clsur)(std::move(__pred)), + std::move(__sched), + std::move(__old)); + } - template - static auto transform_sender(set_value_t, _Sender&&, const _Env&) { - return __not_a_sender<_SENDER_TYPE_IS_NOT_DECAY_COPYABLE_, _WITH_PRETTY_SENDER_<_Sender>>{}; - } + template + static auto transform_sender(set_value_t, _Sender&&, const _Env&) { + return __not_a_sender<_SENDER_TYPE_IS_NOT_DECAY_COPYABLE_, _WITH_PRETTY_SENDER_<_Sender>>{}; + } - private: - // If __is_root_env<_Env> is true, then this sender has no parent, so there is - // no need to restore the execution context. We can use the inline scheduler - // as the scheduler if __env does not have one. - template - using __end_sched_t = __if_c< - __is_root_env<_Env>, - inline_scheduler, - __not_a_scheduler<__no_scheduler_in_environment<_Sender, _Env>> - >; - - template - static constexpr auto __reschedule( - _Sender&& __sndr, - [[maybe_unused]] _OldSched&& __old_sched, - _NewSched&& __new_sched) { - // BUGBUG TODO(ericniebler): FIXME - // return continues_on( - // write_env(static_cast<_Sender&&>(__sndr), __sched_env{__old_sched}), - // static_cast<_NewSched&&>(__new_sched)); - return continues_on(static_cast<_Sender&&>(__sndr), static_cast<_NewSched&&>(__new_sched)); - } - }; - } // namespace __on + private: + // If __is_root_env<_Env> is true, then this sender has no parent, so there is + // no need to restore the execution context. We can use the inline scheduler + // as the scheduler if __env does not have one. + template + using __end_sched_t = __if_c< + __is_root_env<_Env>, + inline_scheduler, + __not_a_scheduler<__on::__no_scheduler_in_environment<_Sender, _Env>> + >; + + template + static constexpr auto __reschedule( + _Sender&& __sndr, + [[maybe_unused]] _OldSched&& __old_sched, + _NewSched&& __new_sched) { + // BUGBUG TODO(ericniebler): FIXME + // return continues_on( + // write_env(static_cast<_Sender&&>(__sndr), __sched_env{__old_sched}), + // static_cast<_NewSched&&>(__new_sched)); + return continues_on(static_cast<_Sender&&>(__sndr), static_cast<_NewSched&&>(__new_sched)); + } + }; - using __on::on_t; inline constexpr on_t on{}; - namespace v2 { - using on_t [[deprecated("use STDEXEC::on_t instead")]] = STDEXEC::on_t; - [[deprecated("use STDEXEC::on instead")]] - inline constexpr STDEXEC::on_t const & on = STDEXEC::on; - } // namespace v2 - template <> struct __sexpr_impl : __sexpr_defaults { template diff --git a/include/stdexec/__detail/__operation_states.hpp b/include/stdexec/__detail/__operation_states.hpp index a241195a2..e8e064df5 100644 --- a/include/stdexec/__detail/__operation_states.hpp +++ b/include/stdexec/__detail/__operation_states.hpp @@ -29,32 +29,29 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // [execution.op_state] - namespace __start { + template + concept __has_start_member = requires(_Op &__op) { __op.start(); }; + + struct start_t { template - concept __has_start = requires(_Op &__op) { __op.start(); }; + requires __has_start_member<_Op> + STDEXEC_ATTRIBUTE(always_inline) + constexpr void operator()(_Op &__op) const noexcept { + static_assert(noexcept(__op.start()), "start() members must be noexcept"); + static_assert(__same_as, "start() members must return void"); + __op.start(); + } - struct start_t { - template - requires __has_start<_Op> - STDEXEC_ATTRIBUTE(always_inline) + template + requires __has_start_member<_Op> || __tag_invocable + [[deprecated("the use of tag_invoke for start is deprecated")]] + STDEXEC_ATTRIBUTE(always_inline) // constexpr void operator()(_Op &__op) const noexcept { - static_assert(noexcept(__op.start()), "start() members must be noexcept"); - static_assert(__same_as, "start() members must return void"); - __op.start(); - } - - template - requires __has_start<_Op> || __tag_invocable - [[deprecated("the use of tag_invoke for start is deprecated")]] - STDEXEC_ATTRIBUTE(always_inline) // - constexpr void operator()(_Op &__op) const noexcept { - static_assert(__nothrow_tag_invocable); - (void) __tag_invoke(start_t{}, __op); - } - }; - } // namespace __start + static_assert(__nothrow_tag_invocable); + (void) __tag_invoke(start_t{}, __op); + } + }; - using __start::start_t; inline constexpr start_t start{}; ///////////////////////////////////////////////////////////////////////////// diff --git a/include/stdexec/__detail/__queries.hpp b/include/stdexec/__detail/__queries.hpp index 04ccea6d7..b898f5d02 100644 --- a/include/stdexec/__detail/__queries.hpp +++ b/include/stdexec/__detail/__queries.hpp @@ -26,87 +26,59 @@ namespace STDEXEC { ////////////////////////////////////////////////////////////////////////////////////////////////// // [exec.queries] - namespace __queries { - ////////////////////////////////////////////////////////////////////////////////// - // [exec.get.allocator] - struct get_allocator_t : __query { - using __query::operator(); - - // defined in __read_env.hpp - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()() const noexcept; - - template - STDEXEC_ATTRIBUTE(always_inline, host, device) - static constexpr void __validate() noexcept { - static_assert(__nothrow_callable); - static_assert(__allocator_<__call_result_t>); - } - - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static consteval auto query(forwarding_query_t) noexcept -> bool { - return true; - } - }; - - // NOT TO SPEC: - struct __is_scheduler_affine_t { - template - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static consteval auto __ensure_bool_constant() noexcept { - if constexpr (__is_bool_constant<_Result>) { - return static_cast(_Result::value); - } else { - static_assert( - __is_bool_constant<_Result>, - "The __is_scheduler_affine query must be one of the following forms:\n" - " static constexpr bool query(__is_scheduler_affine_t) noexcept;\n" - " bool_constant query(__is_scheduler_affine_t) const noexcept;\n" - " bool_constant query(__is_scheduler_affine_t, const Env&) const noexcept;\n"); - } - } - template - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - consteval auto operator()() const noexcept -> bool { - return __completes_where_it_starts; - } - - template <__queryable_with<__is_scheduler_affine_t> _Attrs, class... _Env> - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - consteval auto operator()() const noexcept -> bool { - if constexpr (__statically_queryable_with<_Attrs, __is_scheduler_affine_t>) { - return _Attrs::query(__is_scheduler_affine_t()); - } else { - return __ensure_bool_constant<__query_result_t<_Attrs, __is_scheduler_affine_t>>(); - } + // NOT TO SPEC: + struct __is_scheduler_affine_t { + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static consteval auto __ensure_bool_constant() noexcept { + if constexpr (__is_bool_constant<_Result>) { + return static_cast(_Result::value); + } else { + static_assert( + __is_bool_constant<_Result>, + "The __is_scheduler_affine query must be one of the following forms:\n" + " static constexpr bool query(__is_scheduler_affine_t) noexcept;\n" + " bool_constant query(__is_scheduler_affine_t) const noexcept;\n" + " bool_constant query(__is_scheduler_affine_t, const Env&) const noexcept;\n"); } + } - template - requires __queryable_with<_Attrs, __is_scheduler_affine_t, const _Env&> - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - consteval auto operator()() const noexcept -> bool { - using __result_t = __query_result_t<_Attrs, __is_scheduler_affine_t, const _Env&>; - return __ensure_bool_constant<__result_t>(); - } + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto operator()() const noexcept -> bool { + return __completes_where_it_starts; + } - template - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - consteval auto operator()(const _Attrs&, const _Env&...) const noexcept -> bool { - return operator()<_Attrs, _Env...>(); + template <__queryable_with<__is_scheduler_affine_t> _Attrs, class... _Env> + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto operator()() const noexcept -> bool { + if constexpr (__statically_queryable_with<_Attrs, __is_scheduler_affine_t>) { + return _Attrs::query(__is_scheduler_affine_t()); + } else { + return __ensure_bool_constant<__query_result_t<_Attrs, __is_scheduler_affine_t>>(); } + } - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static consteval auto query(forwarding_query_t) noexcept -> bool { - return false; - } - }; - } // namespace __queries + template + requires __queryable_with<_Attrs, __is_scheduler_affine_t, const _Env&> + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto operator()() const noexcept -> bool { + using __result_t = __query_result_t<_Attrs, __is_scheduler_affine_t, const _Env&>; + return __ensure_bool_constant<__result_t>(); + } - using __queries::get_allocator_t; - using __queries::__is_scheduler_affine_t; + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto operator()(const _Attrs&, const _Env&...) const noexcept -> bool { + return operator()<_Attrs, _Env...>(); + } - inline constexpr get_allocator_t get_allocator{}; + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static consteval auto query(forwarding_query_t) noexcept -> bool { + return false; + } + }; template concept __is_scheduler_affine = requires { @@ -142,3 +114,29 @@ namespace STDEXEC { template STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __sync_attrs(const _Sender&) -> __sync_attrs<_Sender>; } // namespace STDEXEC + +STDEXEC_P2300_NAMESPACE_BEGIN() +////////////////////////////////////////////////////////////////////////////////// +// [exec.get.allocator] +struct get_allocator_t : STDEXEC::__query { + using STDEXEC::__query::operator(); + + // defined in __read_env.hpp + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()() const noexcept; + + template + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept { + static_assert(STDEXEC::__nothrow_callable); + static_assert(STDEXEC::__allocator_>); + } + + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static consteval auto query(forwarding_query_t) noexcept -> bool { + return true; + } +}; + +inline constexpr get_allocator_t get_allocator{}; +STDEXEC_P2300_NAMESPACE_END() diff --git a/include/stdexec/__detail/__query.hpp b/include/stdexec/__detail/__query.hpp index 2802c08c1..670805e98 100644 --- a/include/stdexec/__detail/__query.hpp +++ b/include/stdexec/__detail/__query.hpp @@ -28,11 +28,11 @@ namespace STDEXEC { // [exec.queries.queryable] template - concept queryable = __std::destructible; + concept __queryable = __std::destructible; template concept __member_queryable_with = - queryable<_Env> + __queryable<_Env> && requires(const _Env& __env, const _Query& __query, __declfn_t<_Args&&>... __args) { { __env.query(__query, __args()...) }; }; @@ -120,55 +120,12 @@ namespace STDEXEC { ////////////////////////////////////////////////////////////////////////////////////////////////// // [exec.queries] - namespace __queries { - template - concept __is_bool_constant = requires { typename __mbool<_Tp::value>; }; - - struct forwarding_query_t { - template - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - consteval auto operator()(_Query) const noexcept -> bool { - if constexpr (__queryable_with<_Query, forwarding_query_t>) { - return __query()(_Query()); - } else { - return __std::derived_from<_Query, forwarding_query_t>; - } - } - }; - - } // namespace __queries - - using __queries::forwarding_query_t; - inline constexpr forwarding_query_t forwarding_query{}; + template + concept __is_bool_constant = requires { typename __mbool<_Tp::value>; }; template concept __forwarding_query = forwarding_query(_Tag{}); - struct query_or_t { - template - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()(_Query, _Queryable&&, _Default&& __default, _Args&&...) const - noexcept(__nothrow_move_constructible<_Default>) -> _Default { - return static_cast<_Default&&>(__default); - } - - template - requires __callable<_Query, _Queryable, _Args...> - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto - operator()(_Query __query, _Queryable&& __queryable, _Default&&, _Args&&... __args) const - noexcept(__nothrow_callable<_Query, _Queryable, _Args...>) - -> __call_result_t<_Query, _Queryable, _Args...> { - return static_cast<_Query&&>( - __query)(static_cast<_Queryable&&>(__queryable), static_cast<_Args&&>(__args)...); - } - }; - - inline constexpr query_or_t query_or{}; // NOT TO SPEC - - template - using __query_result_or_t = __call_result_t; - ////////////////////////////////////////////////////////////////////////////////////////// // __is_completion_query template @@ -180,3 +137,19 @@ namespace STDEXEC { template inline constexpr bool __is_completion_query> = true; } // namespace STDEXEC + +STDEXEC_P2300_NAMESPACE_BEGIN() +struct forwarding_query_t { + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto operator()(_Query) const noexcept -> bool { + if constexpr (STDEXEC::__queryable_with<_Query, forwarding_query_t>) { + return STDEXEC::__query()(_Query()); + } else { + return STDEXEC::__std::derived_from<_Query, forwarding_query_t>; + } + } +}; + +inline constexpr forwarding_query_t forwarding_query{}; +STDEXEC_P2300_NAMESPACE_END() diff --git a/include/stdexec/__detail/__read_env.hpp b/include/stdexec/__detail/__read_env.hpp index a43c33e5d..cd19207fa 100644 --- a/include/stdexec/__detail/__read_env.hpp +++ b/include/stdexec/__detail/__read_env.hpp @@ -35,7 +35,7 @@ #include namespace STDEXEC { - namespace __read { + namespace __read_env { struct _THE_CURRENT_EXECUTION_ENVIRONMENT_DOESNT_HAVE_A_VALUE_FOR_THE_GIVEN_QUERY_; template < @@ -86,13 +86,6 @@ namespace STDEXEC { } }; - struct read_env_t { - template - constexpr auto operator()(_Query) const noexcept { - return __make_sexpr(_Query()); - } - }; - struct __read_env_impl : __sexpr_defaults { static constexpr auto get_attrs = [](__ignore, _Query) noexcept { return __attrs<_Query>{}; @@ -111,7 +104,7 @@ namespace STDEXEC { } else { return STDEXEC::__throw_compile_time_error< _THE_CURRENT_EXECUTION_ENVIRONMENT_DOESNT_HAVE_A_VALUE_FOR_THE_GIVEN_QUERY_, - _WHERE_(_IN_ALGORITHM_, read_env_t), + _WHERE_(_IN_ALGORITHM_, __read_env_t), _WITH_QUERY_(__query_t), _WITH_ENVIRONMENT_(_Env) >(); @@ -128,41 +121,48 @@ namespace STDEXEC { [](const _Sender&, _Receiver&& __rcvr) noexcept requires std::is_reference_v<__call_result_t<__data_of<_Sender>, env_of_t<_Receiver>>> { - static_assert(sender_expr_for<_Sender, read_env_t>); + static_assert(sender_expr_for<_Sender, __read_env_t>); using __query_t = __data_of<_Sender>; STDEXEC::__set_value_from( static_cast<_Receiver&&>(__rcvr), __query_t(), STDEXEC::get_env(__rcvr)); }; }; - } // namespace __read + } // namespace __read_env - using __read::read_env_t; - [[deprecated("read has been renamed to read_env")]] - inline constexpr read_env_t read{}; - inline constexpr read_env_t read_env{}; + struct __read_env_t { + template + constexpr auto operator()(_Query) const noexcept { + return __make_sexpr<__read_env_t>(_Query()); + } + }; - template <> - struct __sexpr_impl<__read::read_env_t> : __read::__read_env_impl { }; + inline constexpr __read_env_t read_env{}; - namespace __queries { - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto get_scheduler_t::operator()() const noexcept { - return read_env(get_scheduler); - } + [[deprecated("read has been renamed to read_env")]] + inline constexpr __read_env_t read{}; - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto get_delegation_scheduler_t::operator()() const noexcept { - return read_env(get_delegation_scheduler); - } + template <> + struct __sexpr_impl<__read_env_t> : __read_env::__read_env_impl { }; - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto get_allocator_t::operator()() const noexcept { - return read_env(get_allocator); - } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto get_scheduler_t::operator()() const noexcept { + return read_env(get_scheduler); + } - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto get_stop_token_t::operator()() const noexcept { - return read_env(get_stop_token); - } - } // namespace __queries + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto get_delegation_scheduler_t::operator()() const noexcept { + return read_env(get_delegation_scheduler); + } } // namespace STDEXEC + +STDEXEC_P2300_NAMESPACE_BEGIN() +STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) +constexpr auto get_allocator_t::operator()() const noexcept { + return STDEXEC::read_env(get_allocator); +} + +STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) +constexpr auto get_stop_token_t::operator()() const noexcept { + return STDEXEC::read_env(get_stop_token); +} +STDEXEC_P2300_NAMESPACE_END() diff --git a/include/stdexec/__detail/__receivers.hpp b/include/stdexec/__detail/__receivers.hpp index 2b4e47eb4..fd72b3bcb 100644 --- a/include/stdexec/__detail/__receivers.hpp +++ b/include/stdexec/__detail/__receivers.hpp @@ -47,118 +47,112 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // [execution.receivers] - namespace __rcvrs { - template - concept __set_value_member = requires(_Receiver &&__rcvr, _As &&...__args) { - static_cast<_Receiver &&>(__rcvr).set_value(static_cast<_As &&>(__args)...); - }; + template + concept __set_value_member = requires(_Receiver &&__rcvr, _As &&...__args) { + static_cast<_Receiver &&>(__rcvr).set_value(static_cast<_As &&>(__args)...); + }; - struct set_value_t : __detail::__completion_tag<__disposition::__value> { - template - using __f = __minvoke<_Fn, _As...>; + struct set_value_t : __detail::__completion_tag<__disposition::__value> { + template + using __f = __minvoke<_Fn, _As...>; - template - requires __set_value_member<_Receiver, _As...> - STDEXEC_ATTRIBUTE(host, device, always_inline) + template + requires __set_value_member<_Receiver, _As...> + STDEXEC_ATTRIBUTE(host, device, always_inline) + constexpr void operator()(_Receiver &&__rcvr, _As &&...__as) const noexcept { + static_assert( + noexcept(static_cast<_Receiver &&>(__rcvr).set_value(static_cast<_As &&>(__as)...)), + "set_value member functions must be noexcept"); + static_assert( + __same_as< + decltype(static_cast<_Receiver &&>(__rcvr).set_value(static_cast<_As &&>(__as)...)), + void + >, + "set_value member functions must return void"); + static_cast<_Receiver &&>(__rcvr).set_value(static_cast<_As &&>(__as)...); + } + + template + requires __set_value_member<_Receiver, _As...> + || __tag_invocable + [[deprecated("the use of tag_invoke for set_value is deprecated")]] + STDEXEC_ATTRIBUTE(host, device, always_inline) // constexpr void operator()(_Receiver &&__rcvr, _As &&...__as) const noexcept { - static_assert( - noexcept(static_cast<_Receiver &&>(__rcvr).set_value(static_cast<_As &&>(__as)...)), - "set_value member functions must be noexcept"); - static_assert( - __same_as< - decltype(static_cast<_Receiver &&>(__rcvr).set_value(static_cast<_As &&>(__as)...)), - void - >, - "set_value member functions must return void"); - static_cast<_Receiver &&>(__rcvr).set_value(static_cast<_As &&>(__as)...); - } + static_assert(__nothrow_tag_invocable); + (void) __tag_invoke(*this, static_cast<_Receiver &&>(__rcvr), static_cast<_As &&>(__as)...); + } + }; - template - requires __set_value_member<_Receiver, _As...> - || __tag_invocable - [[deprecated("the use of tag_invoke for set_value is deprecated")]] - STDEXEC_ATTRIBUTE(host, device, always_inline) // - constexpr void operator()(_Receiver &&__rcvr, _As &&...__as) const noexcept { - static_assert(__nothrow_tag_invocable); - (void) __tag_invoke(*this, static_cast<_Receiver &&>(__rcvr), static_cast<_As &&>(__as)...); - } - }; + template + concept __set_error_member = requires(_Receiver &&__rcvr, _Error &&__err) { + static_cast<_Receiver &&>(__rcvr).set_error(static_cast<_Error &&>(__err)); + }; + + struct set_error_t : __detail::__completion_tag<__disposition::__error> { + template + requires(sizeof...(_Args) == 1) + using __f = __minvoke<_Fn, _Args...>; template - concept __set_error_member = requires(_Receiver &&__rcvr, _Error &&__err) { + requires __set_error_member<_Receiver, _Error> + STDEXEC_ATTRIBUTE(host, device, always_inline) + constexpr void operator()(_Receiver &&__rcvr, _Error &&__err) const noexcept { + static_assert( + noexcept(static_cast<_Receiver &&>(__rcvr).set_error(static_cast<_Error &&>(__err))), + "set_error member functions must be noexcept"); + static_assert( + __same_as< + decltype(static_cast<_Receiver &&>(__rcvr).set_error(static_cast<_Error &&>(__err))), + void + >, + "set_error member functions must return void"); static_cast<_Receiver &&>(__rcvr).set_error(static_cast<_Error &&>(__err)); - }; - - struct set_error_t : __detail::__completion_tag<__disposition::__error> { - template - requires(sizeof...(_Args) == 1) - using __f = __minvoke<_Fn, _Args...>; + } - template - requires __set_error_member<_Receiver, _Error> - STDEXEC_ATTRIBUTE(host, device, always_inline) + template + requires __set_error_member<_Receiver, _Error> + || __tag_invocable + [[deprecated("the use of tag_invoke for set_error is deprecated")]] + STDEXEC_ATTRIBUTE(host, device, always_inline) // constexpr void operator()(_Receiver &&__rcvr, _Error &&__err) const noexcept { - static_assert( - noexcept(static_cast<_Receiver &&>(__rcvr).set_error(static_cast<_Error &&>(__err))), - "set_error member functions must be noexcept"); - static_assert( - __same_as< - decltype(static_cast<_Receiver &&>(__rcvr).set_error(static_cast<_Error &&>(__err))), - void - >, - "set_error member functions must return void"); - static_cast<_Receiver &&>(__rcvr).set_error(static_cast<_Error &&>(__err)); - } + static_assert(__nothrow_tag_invocable); + (void) __tag_invoke(*this, static_cast<_Receiver &&>(__rcvr), static_cast<_Error &&>(__err)); + } + }; - template - requires __set_error_member<_Receiver, _Error> - || __tag_invocable - [[deprecated("the use of tag_invoke for set_error is deprecated")]] - STDEXEC_ATTRIBUTE(host, device, always_inline) // - constexpr void operator()(_Receiver &&__rcvr, _Error &&__err) const noexcept { - static_assert(__nothrow_tag_invocable); - (void) - __tag_invoke(*this, static_cast<_Receiver &&>(__rcvr), static_cast<_Error &&>(__err)); - } - }; + template + concept __set_stopped_member = requires(_Receiver &&__rcvr) { + static_cast<_Receiver &&>(__rcvr).set_stopped(); + }; + + struct set_stopped_t : __detail::__completion_tag<__disposition::__stopped> { + template + requires(sizeof...(_Args) == 0) + using __f = __minvoke<_Fn, _Args...>; template - concept __set_stopped_member = requires(_Receiver &&__rcvr) { + requires __set_stopped_member<_Receiver> + STDEXEC_ATTRIBUTE(host, device, always_inline) + constexpr void operator()(_Receiver &&__rcvr) const noexcept { + static_assert( + noexcept(static_cast<_Receiver &&>(__rcvr).set_stopped()), + "set_stopped member functions must be noexcept"); + static_assert( + __same_as(__rcvr).set_stopped()), void>, + "set_stopped member functions must return void"); static_cast<_Receiver &&>(__rcvr).set_stopped(); - }; - - struct set_stopped_t : __detail::__completion_tag<__disposition::__stopped> { - template - requires(sizeof...(_Args) == 0) - using __f = __minvoke<_Fn, _Args...>; + } - template - requires __set_stopped_member<_Receiver> - STDEXEC_ATTRIBUTE(host, device, always_inline) + template + requires __set_stopped_member<_Receiver> || __tag_invocable + [[deprecated("the use of tag_invoke for set_stopped is deprecated")]] + STDEXEC_ATTRIBUTE(host, device, always_inline) // constexpr void operator()(_Receiver &&__rcvr) const noexcept { - static_assert( - noexcept(static_cast<_Receiver &&>(__rcvr).set_stopped()), - "set_stopped member functions must be noexcept"); - static_assert( - __same_as(__rcvr).set_stopped()), void>, - "set_stopped member functions must return void"); - static_cast<_Receiver &&>(__rcvr).set_stopped(); - } - - template - requires __set_stopped_member<_Receiver> || __tag_invocable - [[deprecated("the use of tag_invoke for set_stopped is deprecated")]] - STDEXEC_ATTRIBUTE(host, device, always_inline) // - constexpr void operator()(_Receiver &&__rcvr) const noexcept { - static_assert(__nothrow_tag_invocable); - (void) __tag_invoke(*this, static_cast<_Receiver &&>(__rcvr)); - } - }; - } // namespace __rcvrs + static_assert(__nothrow_tag_invocable); + (void) __tag_invoke(*this, static_cast<_Receiver &&>(__rcvr)); + } + }; - using __rcvrs::set_value_t; - using __rcvrs::set_error_t; - using __rcvrs::set_stopped_t; inline constexpr set_value_t set_value{}; inline constexpr set_error_t set_error{}; inline constexpr set_stopped_t set_stopped{}; @@ -183,7 +177,7 @@ namespace STDEXEC { template concept receiver = enable_receiver<__decay_t<_Receiver>> - && environment_provider<__cref_t<_Receiver>> + && __environment_provider<__cref_t<_Receiver>> && __nothrow_move_constructible<__decay_t<_Receiver>> && __std::constructible_from<__decay_t<_Receiver>, _Receiver>; diff --git a/include/stdexec/__detail/__run_loop.hpp b/include/stdexec/__detail/__run_loop.hpp index bf630e507..85c0c4b8d 100644 --- a/include/stdexec/__detail/__run_loop.hpp +++ b/include/stdexec/__detail/__run_loop.hpp @@ -32,291 +32,303 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // run_loop - class __run_loop_base : __immovable { - public: - __run_loop_base() = default; - - ~__run_loop_base() noexcept { - STDEXEC_ASSERT(__task_count_.load(__std::memory_order_acquire) == 0); - } - - STDEXEC_ATTRIBUTE(host, device) - void run() noexcept { - // execute work items until the __finishing_ flag is set: - while (!__finishing_.load(__std::memory_order_acquire)) { - __queue_.wait_for_item(); - __execute_all(); - } - // drain the queue, taking care to execute any tasks that get added while - // executing the remaining tasks (also wait for other tasks that might still be in flight): - while (__execute_all() || __task_count_.load(__std::memory_order_acquire) > 0) - ; - } + namespace __run_loop { + class __run_loop_base : __immovable { + public: + __run_loop_base() = default; - STDEXEC_ATTRIBUTE(host, device) - void finish() noexcept { - // Increment our task count to avoid lifetime issues. This is preventing - // a use-after-free issue if finish is called from a different thread. - // We increment the task counter by two to prevent the run loop from - // exiting before we schedule the noop task. - __task_count_.fetch_add(2, __std::memory_order_release); - if (!__finishing_.exchange(true, __std::memory_order_acq_rel)) { - // push an empty work item to the queue to wake up the consuming thread - // and let it finish. - // The count will be decremented once the tasks executes. - __queue_.push(&__noop_task); - // If the task got pushed, simply subtract one again, the other decrement - // happens when the noop task got executed. - __task_count_.fetch_sub(1, __std::memory_order_release); - return; + ~__run_loop_base() noexcept { + STDEXEC_ASSERT(__task_count_.load(__std::memory_order_acquire) == 0); } - // We are done finishing. Decrement the count by two, which signals final completion. - __task_count_.fetch_sub(2, __std::memory_order_release); - } - struct __task : __immovable { - using __execute_fn_t = void(__task*) noexcept; - - constexpr __task() = default; STDEXEC_ATTRIBUTE(host, device) - constexpr explicit __task(__execute_fn_t* __execute_fn) noexcept - : __execute_fn_(__execute_fn) { + void run() noexcept { + // execute work items until the __finishing_ flag is set: + while (!__finishing_.load(__std::memory_order_acquire)) { + __queue_.wait_for_item(); + __execute_all(); + } + // drain the queue, taking care to execute any tasks that get added while + // executing the remaining tasks (also wait for other tasks that might still be in flight): + while (__execute_all() || __task_count_.load(__std::memory_order_acquire) > 0) + ; } STDEXEC_ATTRIBUTE(host, device) - constexpr void __execute() noexcept { - (*__execute_fn_)(this); + void finish() noexcept { + // Increment our task count to avoid lifetime issues. This is preventing + // a use-after-free issue if finish is called from a different thread. + // We increment the task counter by two to prevent the run loop from + // exiting before we schedule the noop task. + __task_count_.fetch_add(2, __std::memory_order_release); + if (!__finishing_.exchange(true, __std::memory_order_acq_rel)) { + // push an empty work item to the queue to wake up the consuming thread + // and let it finish. + // The count will be decremented once the tasks executes. + __queue_.push(&__noop_task); + // If the task got pushed, simply subtract one again, the other decrement + // happens when the noop task got executed. + __task_count_.fetch_sub(1, __std::memory_order_release); + return; + } + // We are done finishing. Decrement the count by two, which signals final completion. + __task_count_.fetch_sub(2, __std::memory_order_release); } - __execute_fn_t* __execute_fn_ = nullptr; - __task* __next_ = nullptr; - }; + struct __task : __immovable { + using __execute_fn_t = void(__task*) noexcept; - template - struct __opstate_t : __task { - __std::atomic* __task_count_; - __atomic_intrusive_queue<&__task::__next_>* __queue_; - _Rcvr __rcvr_; + constexpr __task() = default; + STDEXEC_ATTRIBUTE(host, device) + constexpr explicit __task(__execute_fn_t* __execute_fn) noexcept + : __execute_fn_(__execute_fn) { + } - STDEXEC_ATTRIBUTE(host, device) - static constexpr void __execute_impl(__task* __p) noexcept { - static_assert(noexcept(get_stop_token(__declval>()).stop_requested())); - auto& __rcvr = static_cast<__opstate_t*>(__p)->__rcvr_; - - // NOLINTNEXTLINE(bugprone-branch-clone) - if constexpr (unstoppable_token>>) { - set_value(static_cast<_Rcvr&&>(__rcvr)); - } else if (get_stop_token(get_env(__rcvr)).stop_requested()) { - set_stopped(static_cast<_Rcvr&&>(__rcvr)); - } else { - set_value(static_cast<_Rcvr&&>(__rcvr)); + STDEXEC_ATTRIBUTE(host, device) + constexpr void __execute() noexcept { + (*__execute_fn_)(this); } - } - STDEXEC_ATTRIBUTE(host, device) - constexpr explicit __opstate_t( - __std::atomic* __task_count, - __atomic_intrusive_queue<&__task::__next_>* __queue, - _Rcvr __rcvr) - : __task{&__execute_impl} - , __task_count_(__task_count) - , __queue_{__queue} - , __rcvr_{static_cast<_Rcvr&&>(__rcvr)} { - } + __execute_fn_t* __execute_fn_ = nullptr; + __task* __next_ = nullptr; + }; + template + struct __opstate_t : __task { + __std::atomic* __task_count_; + __atomic_intrusive_queue<&__task::__next_>* __queue_; + _Rcvr __rcvr_; + + STDEXEC_ATTRIBUTE(host, device) + static constexpr void __execute_impl(__task* __p) noexcept { + static_assert(noexcept(get_stop_token(__declval>()).stop_requested())); + auto& __rcvr = static_cast<__opstate_t*>(__p)->__rcvr_; + + // NOLINTNEXTLINE(bugprone-branch-clone) + if constexpr (unstoppable_token>>) { + set_value(static_cast<_Rcvr&&>(__rcvr)); + } else if (get_stop_token(get_env(__rcvr)).stop_requested()) { + set_stopped(static_cast<_Rcvr&&>(__rcvr)); + } else { + set_value(static_cast<_Rcvr&&>(__rcvr)); + } + } + + STDEXEC_ATTRIBUTE(host, device) + constexpr explicit __opstate_t( + __std::atomic* __task_count, + __atomic_intrusive_queue<&__task::__next_>* __queue, + _Rcvr __rcvr) + : __task{&__execute_impl} + , __task_count_(__task_count) + , __queue_{__queue} + , __rcvr_{static_cast<_Rcvr&&>(__rcvr)} { + } + + STDEXEC_ATTRIBUTE(host, device) + constexpr void start() noexcept { + __task_count_->fetch_add(1, __std::memory_order_release); + __queue_->push(this); + } + }; + + // Returns true if any tasks were executed. STDEXEC_ATTRIBUTE(host, device) - constexpr void start() noexcept { - __task_count_->fetch_add(1, __std::memory_order_release); - __queue_->push(this); - } - }; + constexpr bool __execute_all() noexcept { + // Dequeue all tasks at once. This returns an __intrusive_queue. + auto __queue = __queue_.pop_all(); + + // Execute all the tasks in the queue. + auto __it = __queue.begin(); + if (__it == __queue.end()) { + return false; // No tasks to execute. + } + + std::size_t __task_count = 0; - // Returns true if any tasks were executed. - STDEXEC_ATTRIBUTE(host, device) - constexpr bool __execute_all() noexcept { - // Dequeue all tasks at once. This returns an __intrusive_queue. - auto __queue = __queue_.pop_all(); + do { + // Take care to increment the iterator before executing the task, + // because __execute() may invalidate the current node. + auto __prev = __it++; + (*__prev)->__execute(); + ++__task_count; + } while (__it != __queue.end()); - // Execute all the tasks in the queue. - auto __it = __queue.begin(); - if (__it == __queue.end()) { - return false; // No tasks to execute. + __queue.clear(); + __task_count_.fetch_sub(__task_count, __std::memory_order_release); + return true; } - std::size_t __task_count = 0; + STDEXEC_ATTRIBUTE(host, device) static constexpr void __noop_(__task*) noexcept { + } - do { - // Take care to increment the iterator before executing the task, - // because __execute() may invalidate the current node. - auto __prev = __it++; - (*__prev)->__execute(); - ++__task_count; - } while (__it != __queue.end()); + __std::atomic __task_count_{0}; + __std::atomic __finishing_{false}; + __atomic_intrusive_queue<&__task::__next_> __queue_{}; + __task __noop_task{&__noop_}; + }; - __queue.clear(); - __task_count_.fetch_sub(__task_count, __std::memory_order_release); - return true; - } + template + struct __basic_run_loop : __run_loop_base { + private: + friend _Derived; - STDEXEC_ATTRIBUTE(host, device) static constexpr void __noop_(__task*) noexcept { - } + struct __attrs_t { + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto query(get_completion_scheduler_t) const noexcept; + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto query(get_completion_scheduler_t) const noexcept; - __std::atomic __task_count_{0}; - __std::atomic __finishing_{false}; - __atomic_intrusive_queue<&__task::__next_> __queue_{}; - __task __noop_task{&__noop_}; - }; + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto query(get_completion_domain_t) const noexcept; + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto query(get_completion_domain_t) const noexcept; - template - struct basic_run_loop : __run_loop_base { - private: - struct __attrs_t { - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto query(get_completion_scheduler_t) const noexcept; - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto query(get_completion_scheduler_t) const noexcept; + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto query(get_completion_behavior_t) const noexcept { + return completion_behavior::asynchronous; + } + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto query(get_completion_behavior_t) const noexcept { + return completion_behavior::asynchronous; + } - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto query(get_completion_domain_t) const noexcept; - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto query(get_completion_domain_t) const noexcept; + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto query(__execute_may_block_caller_t) const noexcept { + return false; + } - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto query(get_completion_behavior_t) const noexcept { - return completion_behavior::asynchronous; - } - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto query(get_completion_behavior_t) const noexcept { - return completion_behavior::asynchronous; - } + __basic_run_loop* __loop_; + }; - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto query(execute_may_block_caller_t) const noexcept { - return false; - } + class __scheduler : __attrs_t { + private: + friend __basic_run_loop; + friend struct _Derived::scheduler; - basic_run_loop* __loop_; - }; + STDEXEC_ATTRIBUTE(host, device) + constexpr explicit __scheduler(__basic_run_loop* __loop) noexcept + : __attrs_t{__loop} { + } - public: - STDEXEC_ATTRIBUTE(host, device) - constexpr explicit basic_run_loop(_Env __env) noexcept - : __env_{static_cast<_Env&&>(__env)} { - } + public: + using scheduler_concept = scheduler_t; - class scheduler : __attrs_t { - private: - friend basic_run_loop; + struct __sndr_t { + using sender_concept = sender_t; - STDEXEC_ATTRIBUTE(host, device) - constexpr explicit scheduler(basic_run_loop* __loop) noexcept - : __attrs_t{__loop} { - } + template + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto connect(_Rcvr __rcvr) const noexcept -> __opstate_t<_Rcvr> { + return __opstate_t<_Rcvr>{ + &__loop_->__task_count_, &__loop_->__queue_, static_cast<_Rcvr&&>(__rcvr)}; + } - public: - using scheduler_concept = scheduler_t; + template + STDEXEC_ATTRIBUTE(nodiscard, host, device) + static consteval auto get_completion_signatures() noexcept { + return completion_signatures{}; + } - struct __sndr_t { - using sender_concept = sender_t; + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto get_env() const noexcept -> __attrs_t { + return __attrs_t{__loop_}; + } + + private: + friend __scheduler; + STDEXEC_ATTRIBUTE(host, device) + constexpr explicit __sndr_t(__basic_run_loop* __loop) noexcept + : __loop_(__loop) { + } + + __basic_run_loop* __loop_; + }; - template STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto connect(_Rcvr __rcvr) const noexcept -> __opstate_t<_Rcvr> { - return __opstate_t<_Rcvr>{ - &__loop_->__task_count_, &__loop_->__queue_, static_cast<_Rcvr&&>(__rcvr)}; + constexpr auto schedule() const noexcept -> __sndr_t { + return __sndr_t{this->__loop_}; } - template + using __attrs_t::query; + STDEXEC_ATTRIBUTE(nodiscard, host, device) - static consteval auto get_completion_signatures() noexcept { - return completion_signatures{}; + constexpr auto + query(get_forward_progress_guarantee_t) const noexcept -> forward_progress_guarantee { + return forward_progress_guarantee::parallel; } STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto get_env() const noexcept -> __attrs_t { - return __attrs_t{__loop_}; + friend constexpr bool operator==(const __scheduler& __a, const __scheduler& __b) noexcept { + return __a.__loop_ == __b.__loop_; } - private: - friend scheduler; - STDEXEC_ATTRIBUTE(host, device) - constexpr explicit __sndr_t(basic_run_loop* __loop) noexcept - : __loop_(__loop) { + STDEXEC_ATTRIBUTE(nodiscard, host, device) + friend constexpr bool operator!=(const __scheduler& __a, const __scheduler& __b) noexcept { + return __a.__loop_ != __b.__loop_; } - - basic_run_loop* __loop_; }; - using __scheduler - [[deprecated("run_loop::__scheduler has been renamed run_loop::scheduler")]] = scheduler; + __basic_run_loop() = default; - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto schedule() const noexcept -> __sndr_t { - return __sndr_t{this->__loop_}; + STDEXEC_ATTRIBUTE(host, device) + constexpr explicit __basic_run_loop(_Env __env) noexcept + : __env_{static_cast<_Env&&>(__env)} { } - using __attrs_t::query; - + public: STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto - query(get_forward_progress_guarantee_t) const noexcept -> forward_progress_guarantee { - return forward_progress_guarantee::parallel; + constexpr auto get_scheduler() noexcept { + return typename _Derived::scheduler{this}; } STDEXEC_ATTRIBUTE(nodiscard, host, device) - friend constexpr bool operator==(const scheduler& __a, const scheduler& __b) noexcept { - return __a.__loop_ == __b.__loop_; + constexpr auto get_env() const noexcept -> const _Env& { + return __env_; } - STDEXEC_ATTRIBUTE(nodiscard, host, device) - friend constexpr bool operator!=(const scheduler& __a, const scheduler& __b) noexcept { - return __a.__loop_ != __b.__loop_; - } + private: + _Env __env_{}; }; - - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto get_scheduler() noexcept -> scheduler { - return scheduler{this}; - } - - STDEXEC_ATTRIBUTE(nodiscard, host, device) - constexpr auto get_env() const noexcept -> const _Env& { - return __env_; - } - - private: - _Env __env_; - }; + } // namespace __run_loop // A run_loop with an empty environment. This is a struct instead of a type alias to give // it a simpler type name that is easier to read in diagnostics. - struct run_loop : basic_run_loop> { - constexpr run_loop() noexcept - : basic_run_loop>{env{}} { - } + struct run_loop : __run_loop::__basic_run_loop> { + run_loop() = default; + + struct scheduler : __scheduler { + private: + friend __basic_run_loop; + + STDEXEC_ATTRIBUTE(host, device) + constexpr explicit scheduler(__basic_run_loop* __loop) noexcept + : __scheduler{__loop} { + } + }; }; - template + template STDEXEC_ATTRIBUTE(host, device) - constexpr auto basic_run_loop<_Env>::__attrs_t::query( + constexpr auto __run_loop::__basic_run_loop<_Derived, _Env>::__attrs_t::query( get_completion_scheduler_t) const noexcept { if constexpr (__callable) { return STDEXEC::get_scheduler(__loop_->__env_); } else { - return scheduler{__loop_}; + return typename _Derived::scheduler{__loop_}; } } - template + template STDEXEC_ATTRIBUTE(host, device) - constexpr auto basic_run_loop<_Env>::__attrs_t::query( + constexpr auto __run_loop::__basic_run_loop<_Derived, _Env>::__attrs_t::query( get_completion_scheduler_t) const noexcept { return query(get_completion_scheduler); } - template + template STDEXEC_ATTRIBUTE(host, device) - constexpr auto basic_run_loop<_Env>::__attrs_t::query( + constexpr auto __run_loop::__basic_run_loop<_Derived, _Env>::__attrs_t::query( get_completion_domain_t) const noexcept { if constexpr (__callable) { return __call_result_t(); @@ -325,9 +337,9 @@ namespace STDEXEC { } } - template + template STDEXEC_ATTRIBUTE(host, device) - constexpr auto basic_run_loop<_Env>::__attrs_t::query( + constexpr auto __run_loop::__basic_run_loop<_Derived, _Env>::__attrs_t::query( get_completion_domain_t) const noexcept { return query(get_completion_domain); } diff --git a/include/stdexec/__detail/__schedule_from.hpp b/include/stdexec/__detail/__schedule_from.hpp index 9dd59523d..a1d08855d 100644 --- a/include/stdexec/__detail/__schedule_from.hpp +++ b/include/stdexec/__detail/__schedule_from.hpp @@ -25,26 +25,21 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.schedule_from] - namespace __schfr { - struct schedule_from_t { - template - constexpr auto operator()(_Sender&& __sndr) const { - return __make_sexpr({}, static_cast<_Sender&&>(__sndr)); - } - }; + struct schedule_from_t { + template + constexpr auto operator()(_Sender&& __sndr) const { + return __make_sexpr({}, static_cast<_Sender&&>(__sndr)); + } + }; - struct __schedule_from_impl : __sexpr_defaults { - template - static consteval auto get_completion_signatures() { - static_assert(sender_expr_for<_Sender, schedule_from_t>); - return STDEXEC::get_completion_signatures<__child_of<_Sender>, _Env...>(); - } - }; - } // namespace __schfr - - using __schfr::schedule_from_t; inline constexpr schedule_from_t schedule_from{}; template <> - struct __sexpr_impl : __schfr::__schedule_from_impl { }; + struct __sexpr_impl : __sexpr_defaults { + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Sender, schedule_from_t>); + return STDEXEC::get_completion_signatures<__child_of<_Sender>, _Env...>(); + } + }; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__schedulers.hpp b/include/stdexec/__detail/__schedulers.hpp index 72d4d52e4..40a63263e 100644 --- a/include/stdexec/__detail/__schedulers.hpp +++ b/include/stdexec/__detail/__schedulers.hpp @@ -30,43 +30,40 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // [exec.schedule] - namespace __sched { + template + concept __has_schedule_member = requires(_Scheduler&& __sched) { + static_cast<_Scheduler&&>(__sched).schedule(); + }; + + struct schedule_t { template - concept __has_schedule_member = requires(_Scheduler&& __sched) { - static_cast<_Scheduler&&>(__sched).schedule(); - }; + requires __has_schedule_member<_Scheduler> + STDEXEC_ATTRIBUTE(host, device, always_inline) + auto operator()(_Scheduler&& __sched) const + noexcept(noexcept(static_cast<_Scheduler&&>(__sched).schedule())) + -> decltype(static_cast<_Scheduler&&>(__sched).schedule()) { + static_assert( + sender(__sched).schedule())>, + "schedule() member functions must return a sender"); + return static_cast<_Scheduler&&>(__sched).schedule(); + } - struct schedule_t { - template - requires __has_schedule_member<_Scheduler> - STDEXEC_ATTRIBUTE(host, device, always_inline) + template + requires __has_schedule_member<_Scheduler> || __tag_invocable + [[deprecated("the use of tag_invoke for schedule is deprecated")]] + STDEXEC_ATTRIBUTE(host, device, always_inline) // auto operator()(_Scheduler&& __sched) const - noexcept(noexcept(static_cast<_Scheduler&&>(__sched).schedule())) - -> decltype(static_cast<_Scheduler&&>(__sched).schedule()) { - static_assert( - sender(__sched).schedule())>, - "schedule() member functions must return a sender"); - return static_cast<_Scheduler&&>(__sched).schedule(); - } - - template - requires __has_schedule_member<_Scheduler> || __tag_invocable - [[deprecated("the use of tag_invoke for schedule is deprecated")]] - STDEXEC_ATTRIBUTE(host, device, always_inline) // - auto operator()(_Scheduler&& __sched) const - noexcept(__nothrow_tag_invocable) - -> __tag_invoke_result_t { - static_assert(sender<__tag_invoke_result_t>); - return __tag_invoke(*this, static_cast<_Scheduler&&>(__sched)); - } + noexcept(__nothrow_tag_invocable) + -> __tag_invoke_result_t { + static_assert(sender<__tag_invoke_result_t>); + return __tag_invoke(*this, static_cast<_Scheduler&&>(__sched)); + } - static constexpr auto query(forwarding_query_t) noexcept -> bool { - return false; - } - }; - } // namespace __sched + static constexpr auto query(forwarding_query_t) noexcept -> bool { + return false; + } + }; - using __sched::schedule_t; inline constexpr schedule_t schedule{}; ///////////////////////////////////////////////////////////////////////////// @@ -85,233 +82,226 @@ namespace STDEXEC { { get_scheduler(__sp) } -> scheduler; }; - namespace __queries { - struct get_scheduler_t : __query { - using __query::operator(); + struct get_scheduler_t : __query { + using __query::operator(); - // defined in __read_env.hpp - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()() const noexcept; + // defined in __read_env.hpp + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()() const noexcept; - template - STDEXEC_ATTRIBUTE(always_inline, host, device) - static constexpr void __validate() noexcept { - static_assert(__nothrow_callable); - static_assert(scheduler<__call_result_t>); - } + template + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept { + static_assert(__nothrow_callable); + static_assert(scheduler<__call_result_t>); + } - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static consteval auto query(forwarding_query_t) noexcept -> bool { - return true; - } - }; + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static consteval auto query(forwarding_query_t) noexcept -> bool { + return true; + } + }; - //! The type for `get_delegation_scheduler` [exec.get.delegation.scheduler] - //! A query object that asks for a scheduler that can be used to delegate - //! work to for the purpose of forward progress delegation ([intro.progress]). - struct get_delegation_scheduler_t : __query { - using __query::operator(); - - // defined in __read_env.hpp - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()() const noexcept; - - template - STDEXEC_ATTRIBUTE(always_inline, host, device) - static constexpr void __validate() noexcept { - static_assert(__nothrow_callable); - static_assert(scheduler<__call_result_t>); + //! The type for `get_delegation_scheduler` [exec.get.delegation.scheduler] + //! A query object that asks for a scheduler that can be used to delegate + //! work to for the purpose of forward progress delegation ([intro.progress]). + struct get_delegation_scheduler_t : __query { + using __query::operator(); + + // defined in __read_env.hpp + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()() const noexcept; + + template + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept { + static_assert(__nothrow_callable); + static_assert(scheduler<__call_result_t>); + } + + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static consteval auto query(forwarding_query_t) noexcept -> bool { + return true; + } + }; + + //! @brief A query type for asking a sender's attributes for the scheduler on which that + //! sender will complete. + //! + //! @tparam _Tag one of set_value_t, set_error_t, or set_stopped_t + template <__completion_tag _Tag> + struct get_completion_scheduler_t { + template + static inline constexpr get_completion_scheduler_t<_Tag> (*signature)(Sig) = nullptr; + + // This function object reads the completion scheduler from an attribute object or a + // scheduler, accounting for the fact that the query member function may or may not + // accept an environment. + struct __read_query_t { + template + requires __queryable_with<_Attrs, _GetComplSch> + constexpr auto operator()(const _Attrs& __attrs, __ignore = {}) const noexcept + -> __query_result_t<_Attrs, _GetComplSch> { + static_assert(noexcept(__attrs.query(_GetComplSch{}))); + static_assert(scheduler<__query_result_t<_Attrs, _GetComplSch>>); + return __attrs.query(_GetComplSch{}); } - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static consteval auto query(forwarding_query_t) noexcept -> bool { - return true; + template + requires __queryable_with<_Attrs, _GetComplSch, const _Env&> + constexpr auto operator()(const _Attrs& __attrs, const _Env& __env) const noexcept + -> __query_result_t<_Attrs, _GetComplSch, const _Env&> { + static_assert(noexcept(__attrs.query(_GetComplSch{}, __env))); + static_assert(scheduler<__query_result_t<_Attrs, _GetComplSch, const _Env&>>); + return __attrs.query(_GetComplSch{}, __env); } }; - //! @brief A query type for asking a sender's attributes for the scheduler on which that - //! sender will complete. - //! - //! @tparam _Tag one of set_value_t, set_error_t, or set_stopped_t - template <__completion_tag _Tag> - struct get_completion_scheduler_t { - template - static inline constexpr get_completion_scheduler_t<_Tag> (*signature)(Sig) = nullptr; - - // This function object reads the completion scheduler from an attribute object or a - // scheduler, accounting for the fact that the query member function may or may not - // accept an environment. - struct __read_query_t { - template - requires __queryable_with<_Attrs, _GetComplSch> - constexpr auto operator()(const _Attrs& __attrs, __ignore = {}) const noexcept - -> __query_result_t<_Attrs, _GetComplSch> { - static_assert(noexcept(__attrs.query(_GetComplSch{}))); - static_assert(scheduler<__query_result_t<_Attrs, _GetComplSch>>); - return __attrs.query(_GetComplSch{}); - } - - template - requires __queryable_with<_Attrs, _GetComplSch, const _Env&> - constexpr auto operator()(const _Attrs& __attrs, const _Env& __env) const noexcept - -> __query_result_t<_Attrs, _GetComplSch, const _Env&> { - static_assert(noexcept(__attrs.query(_GetComplSch{}, __env))); - static_assert(scheduler<__query_result_t<_Attrs, _GetComplSch, const _Env&>>); - return __attrs.query(_GetComplSch{}, __env); - } - }; - - private: - // A scheduler might have a completion scheduler different from itself; for example, an - // inline_scheduler completes wherever the scheduler's sender is started. So we - // recursively ask the scheduler for its completion scheduler until we find one whose - // completion scheduler is equal to itself (or it doesn't have one). - struct __recurse_query_t { - template - constexpr auto - operator()([[maybe_unused]] _Sch __sch, const _Env&... __env) const noexcept { - static_assert(scheduler<_Sch>); - // When determining where the scheduler's operations will complete, we query - // for the completion scheduler of the value channel: - using __read_query_t = typename get_completion_scheduler_t::__read_query_t; - - if constexpr (__callable<__read_query_t, _Sch, const _Env&...>) { - using __sch2_t = __decay_t<__call_result_t<__read_query_t, _Sch, const _Env&...>>; - if constexpr (__same_as<_Sch, __sch2_t>) { - _Sch __prev = __sch; - do { - __prev = std::exchange(__sch, __read_query_t{}(__sch, __env...)); - } while (__prev != __sch); - return __sch; - } else { - // New scheduler has different type. Recurse! - return _Self{}(__read_query_t{}(__sch, __env...), __env...); - } - } else { - if constexpr ( - __callable<__read_query_t, env_of_t>, const _Env&...>) { - STDEXEC_ASSERT_FN(__sch == __read_query_t{}(get_env(__sch.schedule()), __env...)); - } + private: + // A scheduler might have a completion scheduler different from itself; for example, an + // inline_scheduler completes wherever the scheduler's sender is started. So we + // recursively ask the scheduler for its completion scheduler until we find one whose + // completion scheduler is equal to itself (or it doesn't have one). + struct __recurse_query_t { + template + constexpr auto operator()([[maybe_unused]] _Sch __sch, const _Env&... __env) const noexcept { + static_assert(scheduler<_Sch>); + // When determining where the scheduler's operations will complete, we query + // for the completion scheduler of the value channel: + using __read_query_t = typename get_completion_scheduler_t::__read_query_t; + + if constexpr (__callable<__read_query_t, _Sch, const _Env&...>) { + using __sch2_t = __decay_t<__call_result_t<__read_query_t, _Sch, const _Env&...>>; + if constexpr (__same_as<_Sch, __sch2_t>) { + _Sch __prev = __sch; + do { + __prev = std::exchange(__sch, __read_query_t{}(__sch, __env...)); + } while (__prev != __sch); return __sch; + } else { + // New scheduler has different type. Recurse! + return _Self{}(__read_query_t{}(__sch, __env...), __env...); } + } else { + if constexpr ( + __callable<__read_query_t, env_of_t>, const _Env&...>) { + STDEXEC_ASSERT_FN(__sch == __read_query_t{}(get_env(__sch.schedule()), __env...)); + } + return __sch; } - }; - - template - static constexpr auto __check_domain(_Sch __sch) noexcept -> _Sch { - static_assert(scheduler<_Sch>); - if constexpr (__callable, const _Attrs&, const _Env&...>) { - using __domain_t = - __call_result_t, const _Attrs&, const _Env&...>; - static_assert( - __same_as<__domain_t, __detail::__scheduler_domain_t<_Sch, const _Env&...>>, - "the sender claims to complete on a domain that is not the domain of its completion " - "scheduler"); - } - return __sch; } + }; - template - static consteval auto __get_declfn() noexcept { - // If __attrs has a completion scheduler, then return it (after checking the scheduler - // for _its_ completion scheduler): - if constexpr (__callable<__read_query_t, const _Attrs&, const _Env&...>) { - using __result_t = __call_result_t< - __recurse_query_t, - __call_result_t<__read_query_t, const _Attrs&, const _Env&...>, - const _Env&... - >; - return __declfn<__result_t>(); - } - // Otherwise, if __attrs indicates that its sender completes inline, then we can ask - // the environment for the current scheduler and return that (after checking the - // scheduler for _its_ completion scheduler). - else if constexpr ( - __completes_inline<_Tag, _Attrs, _Env...> - && (__callable || ...)) { - using __result_t = __call_result_t< - __recurse_query_t, - __call_result_t..., - const _Env&... - >; - return __declfn<__result_t>(); - } - // Otherwise, if we are asking a scheduler for a completion scheduler, return the - // scheduler itself. - else if constexpr (scheduler<_Attrs> && sizeof...(_Env) != 0) { - return __declfn<__decay_t<_Attrs>>(); - } - // Otherwise, no completion scheduler can be determined. Return void. + template + static constexpr auto __check_domain(_Sch __sch) noexcept -> _Sch { + static_assert(scheduler<_Sch>); + if constexpr (__callable, const _Attrs&, const _Env&...>) { + using __domain_t = + __call_result_t, const _Attrs&, const _Env&...>; + static_assert( + __same_as<__domain_t, __detail::__scheduler_domain_t<_Sch, const _Env&...>>, + "the sender claims to complete on a domain that is not the domain of its completion " + "scheduler"); } + return __sch; + } - public: - template < - class _Attrs, - class... _Env, - auto _DeclFn = __get_declfn() - > - constexpr auto operator()(const _Attrs& __attrs, const _Env&... __env) const noexcept - -> __unless_one_of_t { - // If __attrs has a completion scheduler, then return it (after checking the scheduler - // for _its_ completion scheduler): - if constexpr (__callable<__read_query_t, const _Attrs&, const _Env&...>) { - return __check_domain<_Attrs, _Env...>( - __recurse_query_t{}(__read_query_t{}(__attrs, __env...), __env...)); - } - // Otherwise, if __attrs indicates that its sender completes inline, then we can ask - // the environment for the current scheduler and return that (after checking the - // scheduler for _its_ completion scheduler). - else if constexpr ( - __completes_inline<_Tag, _Attrs, _Env...> - && __callable) { - return __check_domain<_Attrs, _Env...>( - __recurse_query_t{}(get_scheduler(__env...), __hide_scheduler{__env}...)); - } - // Otherwise, if we are asking a scheduler for a completion scheduler, return the - // scheduler itself. - else { - return __attrs; - } + template + static consteval auto __get_declfn() noexcept { + // If __attrs has a completion scheduler, then return it (after checking the scheduler + // for _its_ completion scheduler): + if constexpr (__callable<__read_query_t, const _Attrs&, const _Env&...>) { + using __result_t = __call_result_t< + __recurse_query_t, + __call_result_t<__read_query_t, const _Attrs&, const _Env&...>, + const _Env&... + >; + return __declfn<__result_t>(); } - - static constexpr auto query(forwarding_query_t) noexcept -> bool { - return true; + // Otherwise, if __attrs indicates that its sender completes inline, then we can ask + // the environment for the current scheduler and return that (after checking the + // scheduler for _its_ completion scheduler). + else if constexpr ( + __completes_inline<_Tag, _Attrs, _Env...> + && (__callable || ...)) { + using __result_t = __call_result_t< + __recurse_query_t, + __call_result_t..., + const _Env&... + >; + return __declfn<__result_t>(); } - }; - - struct execute_may_block_caller_t : __query { - template - STDEXEC_ATTRIBUTE(always_inline, host, device) - static constexpr void __validate() noexcept { - static_assert( - __std::same_as>); - static_assert(__nothrow_callable); + // Otherwise, if we are asking a scheduler for a completion scheduler, return the + // scheduler itself. + else if constexpr (scheduler<_Attrs> && sizeof...(_Env) != 0) { + return __declfn<__decay_t<_Attrs>>(); } - }; + // Otherwise, no completion scheduler can be determined. Return void. + } - struct get_forward_progress_guarantee_t - : __query< - get_forward_progress_guarantee_t, - forward_progress_guarantee::weakly_parallel, - __q1<__decay_t> - > { - template - STDEXEC_ATTRIBUTE(always_inline, host, device) - static constexpr void __validate() noexcept { - using __result_t = __call_result_t; - static_assert(__std::same_as); - static_assert(__nothrow_callable); + public: + template < + class _Attrs, + class... _Env, + auto _DeclFn = __get_declfn() + > + constexpr auto operator()(const _Attrs& __attrs, const _Env&... __env) const noexcept + -> __unless_one_of_t { + // If __attrs has a completion scheduler, then return it (after checking the scheduler + // for _its_ completion scheduler): + if constexpr (__callable<__read_query_t, const _Attrs&, const _Env&...>) { + return __check_domain<_Attrs, _Env...>( + __recurse_query_t{}(__read_query_t{}(__attrs, __env...), __env...)); } - }; - } // namespace __queries + // Otherwise, if __attrs indicates that its sender completes inline, then we can ask + // the environment for the current scheduler and return that (after checking the + // scheduler for _its_ completion scheduler). + else if constexpr ( + __completes_inline<_Tag, _Attrs, _Env...> && __callable) { + return __check_domain<_Attrs, _Env...>( + __recurse_query_t{}(get_scheduler(__env...), __hide_scheduler{__env}...)); + } + // Otherwise, if we are asking a scheduler for a completion scheduler, return the + // scheduler itself. + else { + return __attrs; + } + } - using __queries::execute_may_block_caller_t; - using __queries::get_forward_progress_guarantee_t; - using __queries::get_scheduler_t; - using __queries::get_delegation_scheduler_t; - using __queries::get_completion_scheduler_t; + static constexpr auto query(forwarding_query_t) noexcept -> bool { + return true; + } + }; + + struct __execute_may_block_caller_t : __query<__execute_may_block_caller_t, true> { + template + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept { + static_assert( + __std::same_as>); + static_assert(__nothrow_callable<__execute_may_block_caller_t, const _Attrs&>); + } + }; + + using execute_may_block_caller_t [[deprecated]] = __execute_may_block_caller_t; + + struct get_forward_progress_guarantee_t + : __query< + get_forward_progress_guarantee_t, + forward_progress_guarantee::weakly_parallel, + __q1<__decay_t> + > { + template + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept { + using __result_t = __call_result_t; + static_assert(__std::same_as); + static_assert(__nothrow_callable); + } + }; - inline constexpr execute_may_block_caller_t execute_may_block_caller{}; + [[deprecated]] + inline constexpr __execute_may_block_caller_t execute_may_block_caller{}; inline constexpr get_forward_progress_guarantee_t get_forward_progress_guarantee{}; inline constexpr get_scheduler_t get_scheduler{}; inline constexpr get_delegation_scheduler_t get_delegation_scheduler{}; @@ -387,8 +377,8 @@ namespace STDEXEC { __completes_inline>, _Env...> && (__callable || ...)) { return ( - __sched_env{get_completion_scheduler( - get_scheduler(__env), __queries::__hide_scheduler{__env})}, + __sched_env{ + get_completion_scheduler(get_scheduler(__env), __hide_scheduler{__env})}, ...); } else { return __sched_env{__sch}; diff --git a/include/stdexec/__detail/__sender_adaptor_closure.hpp b/include/stdexec/__detail/__sender_adaptor_closure.hpp index 4ee86291c..650753c71 100644 --- a/include/stdexec/__detail/__sender_adaptor_closure.hpp +++ b/include/stdexec/__detail/__sender_adaptor_closure.hpp @@ -23,14 +23,6 @@ #include "__type_traits.hpp" namespace STDEXEC { - // NOT TO SPEC: - namespace __clsur { - template <__class _Dp> - struct sender_adaptor_closure; - } // namespace __clsur - - using __clsur::sender_adaptor_closure; - template concept __sender_adaptor_closure = __std::derived_from<__decay_t<_Tp>, sender_adaptor_closure<__decay_t<_Tp>>> @@ -64,60 +56,60 @@ namespace STDEXEC { STDEXEC_ATTRIBUTE(no_unique_address) _T1 __t1_; }; - template <__class _Dp> - struct sender_adaptor_closure { }; - - template _Closure> - STDEXEC_ATTRIBUTE(always_inline) - constexpr auto - operator|(_Sender&& __sndr, _Closure&& __clsur) - noexcept(__nothrow_callable<_Closure, _Sender>) -> __call_result_t<_Closure, _Sender> { - return static_cast<_Closure&&>(__clsur)(static_cast<_Sender&&>(__sndr)); - } - - template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1> - STDEXEC_ATTRIBUTE(always_inline) - constexpr auto operator|(_T0&& __t0, _T1&& __t1) -> __compose<__decay_t<_T0>, __decay_t<_T1>> { - return {{}, static_cast<_T0&&>(__t0), static_cast<_T1&&>(__t1)}; - } - - template - struct __closure : sender_adaptor_closure<__closure<_Fn, _As...>> { - template - STDEXEC_ATTRIBUTE(host, device, always_inline) - constexpr explicit __closure(_FnFwd&& __fn, _AsFwd&&... __as) - noexcept(__nothrow_move_constructible<_Fn, _As...>) - : __fn_{static_cast<_FnFwd&&>(__fn)} - , __args_{static_cast<_AsFwd&&>(__as)...} { + struct __sender_adaptor_closure_base { + template _Closure> + STDEXEC_ATTRIBUTE(always_inline) + friend constexpr auto operator|(_Sender&& __sndr, _Closure&& __clsur) + noexcept(__nothrow_callable<_Closure, _Sender>) -> __call_result_t<_Closure, _Sender> { + return static_cast<_Closure&&>(__clsur)(static_cast<_Sender&&>(__sndr)); } - template - requires __callable<_Fn, _Sender, _As...> - STDEXEC_ATTRIBUTE(host, device, always_inline) - constexpr auto - operator()(_Sender&& __sndr) && noexcept(__nothrow_callable<_Fn, _Sender, _As...>) { - return STDEXEC::__apply( - static_cast<_Fn&&>(__fn_), - static_cast<__tuple<_As...>&&>(__args_), - static_cast<_Sender&&>(__sndr)); + template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1> + STDEXEC_ATTRIBUTE(always_inline) + friend constexpr auto operator|(_T0&& __t0, _T1&& __t1) + noexcept(__nothrow_decay_copyable<_T0, _T1>) -> __compose<__decay_t<_T0>, __decay_t<_T1>> { + return {{}, static_cast<_T0&&>(__t0), static_cast<_T1&&>(__t1)}; } + }; + } // namespace __clsur - template - requires __callable - STDEXEC_ATTRIBUTE(host, device, always_inline) - constexpr auto operator()(_Sender&& __sndr) const & noexcept( - __nothrow_callable) { - return STDEXEC::__apply(__fn_, __args_, static_cast<_Sender&&>(__sndr)); - } + template <__class _Dp> + struct sender_adaptor_closure : __clsur::__sender_adaptor_closure_base { }; + + template + struct __closure : sender_adaptor_closure<__closure<_Fn, _As...>> { + template <__not_decays_to<__closure> _FnFwd = _Fn, class... _AsFwd> + STDEXEC_ATTRIBUTE(host, device, always_inline) + constexpr explicit __closure(_FnFwd&& __fn, _AsFwd&&... __as) + noexcept(__nothrow_decay_copyable<_FnFwd, _AsFwd...>) + : __fn_{static_cast<_FnFwd&&>(__fn)} + , __args_{static_cast<_AsFwd&&>(__as)...} { + } - private: - STDEXEC_ATTRIBUTE(no_unique_address) _Fn __fn_; - STDEXEC_ATTRIBUTE(no_unique_address) __tuple<_As...> __args_; - }; + template + requires __callable<_Fn, _Sender, _As...> + STDEXEC_ATTRIBUTE(host, device, always_inline) + constexpr auto + operator()(_Sender&& __sndr) && noexcept(__nothrow_callable<_Fn, _Sender, _As...>) { + return STDEXEC::__apply( + static_cast<_Fn&&>(__fn_), + static_cast<__tuple<_As...>&&>(__args_), + static_cast<_Sender&&>(__sndr)); + } - template - STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __closure(_Fn, _As...) -> __closure<_Fn, _As...>; - } // namespace __clsur + template + requires __callable + STDEXEC_ATTRIBUTE(host, device, always_inline) + constexpr auto operator()(_Sender&& __sndr) const & noexcept( + __nothrow_callable) { + return STDEXEC::__apply(__fn_, __args_, static_cast<_Sender&&>(__sndr)); + } + + private: + STDEXEC_ATTRIBUTE(no_unique_address) _Fn __fn_; + STDEXEC_ATTRIBUTE(no_unique_address) __tuple<_As...> __args_; + }; - using __clsur::__closure; + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __closure(_Fn, _As...) -> __closure<_Fn, _As...>; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__sender_concepts.hpp b/include/stdexec/__detail/__sender_concepts.hpp index e085a8ecc..53f69d3f5 100644 --- a/include/stdexec/__detail/__sender_concepts.hpp +++ b/include/stdexec/__detail/__sender_concepts.hpp @@ -47,8 +47,8 @@ namespace STDEXEC { // [exec.snd.concepts] template - concept sender = enable_sender<__decay_t<_Sender>> // - && environment_provider<__cref_t<_Sender>> // + concept sender = enable_sender<__decay_t<_Sender>> // + && __environment_provider<__cref_t<_Sender>> // && __std::move_constructible<__decay_t<_Sender>> && __std::constructible_from<__decay_t<_Sender>, _Sender>; diff --git a/include/stdexec/__detail/__sender_introspection.hpp b/include/stdexec/__detail/__sender_introspection.hpp index 1e0ef85d0..9dd8f75b2 100644 --- a/include/stdexec/__detail/__sender_introspection.hpp +++ b/include/stdexec/__detail/__sender_introspection.hpp @@ -34,23 +34,23 @@ namespace STDEXEC { } // namespace #endif - namespace __detail { - // A type that describes a sender's metadata - template - struct __desc { - using __tag = _Tag; - using __data = _Data; - using __indices = __make_indices; - using __children = __mlist<_Child...>; - - constexpr auto operator()() const noexcept -> __desc { - return __desc{}; - } - - template - using __f = __minvoke<_Fn, _Args..., _Tag, _Data, _Child...>; - }; + // A type that describes a sender's metadata + template + struct __desc { + using __tag = _Tag; + using __data = _Data; + using __indices = __make_indices; + using __children = __mlist<_Child...>; + + constexpr auto operator()() const noexcept -> __desc { + return __desc{}; + } + + template + using __f = __minvoke<_Fn, _Args..., _Tag, _Data, _Child...>; + }; + namespace __detail { template using __desc_of = STDEXEC_REMOVE_REFERENCE(_Sender)::__desc_t; diff --git a/include/stdexec/__detail/__starts_on.hpp b/include/stdexec/__detail/__starts_on.hpp index 9f6d20864..b147e2916 100644 --- a/include/stdexec/__detail/__starts_on.hpp +++ b/include/stdexec/__detail/__starts_on.hpp @@ -47,36 +47,30 @@ namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.starts_on] - namespace __starts_on_ns { - struct starts_on_t { - template - constexpr auto - operator()(_Scheduler&& __sched, _Sender&& __sndr) const -> __well_formed_sender auto { - return __make_sexpr( - static_cast<_Scheduler&&>(__sched), static_cast<_Sender&&>(__sndr)); - } - - template <__decay_copyable _Sender> - static constexpr auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { - auto& [__tag, __sched, __child] = __sndr; - return let_value( - continues_on(just(), __sched), - __detail::__always{STDEXEC::__forward_like<_Sender>(__child)}); - } - - template - static constexpr auto transform_sender(set_value_t, _Sender&&, __ignore) { - return __not_a_sender<_SENDER_TYPE_IS_NOT_DECAY_COPYABLE_, _WITH_PRETTY_SENDER_<_Sender>>{}; - } - }; - } // namespace __starts_on_ns + struct starts_on_t { + template + constexpr auto + operator()(_Scheduler&& __sched, _Sender&& __sndr) const -> __well_formed_sender auto { + return __make_sexpr( + static_cast<_Scheduler&&>(__sched), static_cast<_Sender&&>(__sndr)); + } + + template <__decay_copyable _Sender> + static constexpr auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { + auto& [__tag, __sched, __child] = __sndr; + return let_value( + continues_on(just(), __sched), + __detail::__always{STDEXEC::__forward_like<_Sender>(__child)}); + } + + template + static constexpr auto transform_sender(set_value_t, _Sender&&, __ignore) { + return __not_a_sender<_SENDER_TYPE_IS_NOT_DECAY_COPYABLE_, _WITH_PRETTY_SENDER_<_Sender>>{}; + } + }; - using __starts_on_ns::starts_on_t; inline constexpr starts_on_t starts_on{}; - using start_on_t = starts_on_t; - inline constexpr starts_on_t start_on{}; - template <> struct __sexpr_impl : __sexpr_defaults { template diff --git a/include/stdexec/__detail/__stop_token.hpp b/include/stdexec/__detail/__stop_token.hpp index 2271df24f..4d7a0ada2 100644 --- a/include/stdexec/__detail/__stop_token.hpp +++ b/include/stdexec/__detail/__stop_token.hpp @@ -20,88 +20,86 @@ #include "__concepts.hpp" -STDEXEC_NAMESPACE_STD_BEGIN -class stop_token; +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 2019'11L +# include // IWYU pragma: export +#endif -template -class stop_callback; -STDEXEC_NAMESPACE_STD_END +STDEXEC_P2300_NAMESPACE_BEGIN() -namespace STDEXEC { - namespace __stok { - template