diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 5eb2cd68f..5652c22d0 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -518,7 +518,7 @@ TIMESTAMP = NO # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. @@ -542,7 +542,7 @@ EXTRACT_PACKAGE = NO # included in the documentation. # The default value is: NO. -EXTRACT_STATIC = NO +EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, @@ -1063,7 +1063,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = *__* +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -1071,7 +1071,8 @@ EXCLUDE_PATTERNS = *__* # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test -EXCLUDE_SYMBOLS = *__* +EXCLUDE_SYMBOLS = *__* \ + _* # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include diff --git a/docs/source/conf.py b/docs/source/conf.py index 33f9deb5a..bbd879067 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,7 +23,7 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'stdexec' -copyright = '2023, NVIDIA Corporation' +copyright = '2025, NVIDIA Corporation' author = 'NVIDIA Corporation' release = 'dev' diff --git a/docs/source/developer/index.rst b/docs/source/developer/index.rst new file mode 100644 index 000000000..180cf016d --- /dev/null +++ b/docs/source/developer/index.rst @@ -0,0 +1,32 @@ +.. ============================================================================= +.. Copyright 2025 NVIDIA Corporation +.. +.. Licensed under the Apache License, Version 2.0 (the "License"); +.. you may not use this file except in compliance with the License. +.. You may obtain a copy of the License at +.. +.. http://www.apache.org/licenses/LICENSE-2.0 +.. +.. Unless required by applicable law or agreed to in writing, software +.. distributed under the License is distributed on an "AS IS" BASIS, +.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +.. See the License for the specific language governing permissions and +.. limitations under the License. +.. ============================================================================= + +Developer's Guide +================= + +This section will eventually contain a guide for those interested in developing their own +asynchronous, sender-based algorithms and execution contexts. + +Essential concepts +------------------ + +* Receivers +* Custom Algorithms +* Custom Schedulers +* Customizing ``stdexec``'s algorithms + * Domains + * Early algorithm customization + * Late algorithm customization diff --git a/docs/source/index.rst b/docs/source/index.rst index c7aa15d3a..0dedc00ef 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -17,9 +17,12 @@ Welcome to ``stdexec`` ====================== -``stdexec`` is the reference implementation for P2300, the ``std::execution`` proposal to the C++ standard library. +``stdexec`` is the reference implementation for `P2300 `_, the +``std::execution`` proposal to the C++ standard library which will bring to C++26 a standard +async programming model. It provides: + * TODO .. toctree:: @@ -28,9 +31,9 @@ It provides: reference/index -.. user/index -.. developer/index + user/index + developer/index .. .. toctree: : .. :maxdepth: 1 diff --git a/docs/source/reference/index.rst b/docs/source/reference/index.rst index c3deb0ac2..2420e4166 100644 --- a/docs/source/reference/index.rst +++ b/docs/source/reference/index.rst @@ -21,7 +21,7 @@ Concepts Sender Factories ---------------- -.. doxygenvariable:: stdexec::__write +.. doxygenvariable:: stdexec::read_env Sender Adaptors --------------- @@ -37,7 +37,3 @@ Sender Consumers Utilities --------- -.. doxygenfunction:: stdexec::__write - :project: stdexec - :outline: - diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst new file mode 100644 index 000000000..f99b5cc35 --- /dev/null +++ b/docs/source/user/index.rst @@ -0,0 +1,35 @@ +.. ============================================================================= +.. Copyright 2025 NVIDIA Corporation +.. +.. Licensed under the Apache License, Version 2.0 (the "License"); +.. you may not use this file except in compliance with the License. +.. You may obtain a copy of the License at +.. +.. http://www.apache.org/licenses/LICENSE-2.0 +.. +.. Unless required by applicable law or agreed to in writing, software +.. distributed under the License is distributed on an "AS IS" BASIS, +.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +.. See the License for the specific language governing permissions and +.. limitations under the License. +.. ============================================================================= + +User's Guide +============ + +This section will eventually contain a user's guide for ``stdexec`` that describes what the library +is, the high-level concepts on which it is based, and how to use it. + +TODO + + +Essential concepts +------------------ + +* Execution Contexts +* Senders +* Schedulers +* Sender Algorithms + * Sender Factories + * Sender Adaptors + * Sender Consumers diff --git a/include/exec/finally.hpp b/include/exec/finally.hpp index 1354591ec..5e06ca734 100644 --- a/include/exec/finally.hpp +++ b/include/exec/finally.hpp @@ -285,9 +285,9 @@ namespace exec { struct finally_t { template - requires __domain::__has_common_domain<_Initial, _Final> + requires __has_common_domain<_Initial, _Final> auto operator()(_Initial&& __initial, _Final&& __final) const { - using _Domain = __domain::__common_domain_t<_Initial, _Final>; + using _Domain = __common_domain_t<_Initial, _Final>; return stdexec::transform_sender( _Domain(), __make_sexpr( diff --git a/include/exec/sequence.hpp b/include/exec/sequence.hpp index 183c61d96..c4503d895 100644 --- a/include/exec/sequence.hpp +++ b/include/exec/sequence.hpp @@ -32,7 +32,7 @@ namespace exec { STDEXEC_ATTRIBUTE((nodiscard, host, device)) auto operator()(Sndr sndr) const -> Sndr; template - requires(sizeof...(Sndrs) > 1) && stdexec::__domain::__has_common_domain + requires(sizeof...(Sndrs) > 1) && stdexec::__has_common_domain STDEXEC_ATTRIBUTE((nodiscard, host, device)) auto operator()(Sndrs... sndrs) const -> _sndr; }; @@ -198,7 +198,7 @@ namespace exec { } template - requires(sizeof...(Sndrs) > 1) && stdexec::__domain::__has_common_domain + requires(sizeof...(Sndrs) > 1) && stdexec::__has_common_domain STDEXEC_ATTRIBUTE((host, device)) auto sequence_t::operator()(Sndrs... sndrs) const -> _sndr { return _sndr{{}, {}, {static_cast(sndrs)...}}; } diff --git a/include/stdexec/__detail/__bulk.hpp b/include/stdexec/__detail/__bulk.hpp index 16da53322..96a04ab15 100644 --- a/include/stdexec/__detail/__bulk.hpp +++ b/include/stdexec/__detail/__bulk.hpp @@ -88,20 +88,6 @@ namespace stdexec { {} }; } - - // This describes how to use the pieces of a bulk sender to find - // legacy customizations of the bulk algorithm. - using _Sender = __1; - using _Shape = __nth_member<0>(__0); - using _Fun = __nth_member<1>(__0); - using __legacy_customizations_t = __types< - tag_invoke_t( - bulk_t, - get_completion_scheduler_t(get_env_t(_Sender&)), - _Sender, - _Shape, - _Fun), - tag_invoke_t(bulk_t, _Sender, _Shape, _Fun)>; }; struct __bulk_impl : __sexpr_defaults { diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index 765d8475e..403674e4c 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -27,7 +27,6 @@ #include "__sender_introspection.hpp" #include "__sender_adaptor_closure.hpp" #include "__senders_core.hpp" -#include "__tag_invoke.hpp" #include "__transform_sender.hpp" namespace stdexec { @@ -57,18 +56,6 @@ namespace stdexec { return {{static_cast<_Scheduler&&>(__sched)}, {}, {}}; } - ////////////////////////////////////////////////////////////////////////////////////////////// - using _Sched = __0; - using _Sender = __1; - using __legacy_customizations_t = // - __types< - tag_invoke_t( - continues_on_t, - get_completion_scheduler_t(get_env_t(const _Sender&)), - _Sender, - _Sched), - tag_invoke_t(continues_on_t, _Sender, _Sched)>; - static auto __transform_sender_fn() { return [&](__ignore, _Data&& __data, _Child&& __child) { return schedule_from(static_cast<_Data&&>(__data), static_cast<_Child&&>(__child)); diff --git a/include/stdexec/__detail/__domain.hpp b/include/stdexec/__detail/__domain.hpp index a5caf45c4..a10fc74e3 100644 --- a/include/stdexec/__detail/__domain.hpp +++ b/include/stdexec/__detail/__domain.hpp @@ -31,41 +31,31 @@ namespace stdexec { struct default_domain; struct dependent_domain; - namespace __domain { - template - using __legacy_c11n_for = typename _Tag::__legacy_customizations_t; - - template - using __legacy_c11n_fn = // - __make_dispatcher<__legacy_c11n_for<_Tag>, __none_such, _Args...>; - - template - concept __has_legacy_c11n = // - __callable<__legacy_c11n_fn<_Tag, _Args...>, _Args...>; - - struct __legacy_customization { - template - requires __has_legacy_c11n<_Tag, _Data, _Children...> - auto operator()(_Tag, _Data&& __data, _Children&&... __children) const -> decltype(auto) { - return __legacy_c11n_fn<_Tag, _Data, _Children...>()( - static_cast<_Data&&>(__data), static_cast<_Children&&>(__children)...); - } - }; - + namespace __detail { template concept __has_transform_sender = requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) { __tag.transform_sender(static_cast<_Sender &&>(__sender), __env...); }; + template + concept __has_nothrow_transform_sender = + requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) { + { __tag.transform_sender(static_cast<_Sender &&>(__sender), __env...) } noexcept; + }; + template concept __has_default_transform_sender = // sender_expr<_Sender> // && __has_transform_sender, _Sender, _Env...>; - template - concept __has_transform_env = requires(_Type __obj, _Sender&& __sender, _Env&& __env) { - __obj.transform_env(static_cast<_Sender &&>(__sender), static_cast<_Env &&>(__env)); + template + using __transform_sender_result_t = + decltype(_DomainOrTag{}.transform_sender(__declval<_Sender>(), __declval()...)); + + template + concept __has_transform_env = requires(_DomainOrTag __tag, _Sender&& __sender, _Env&& __env) { + __tag.transform_env(static_cast<_Sender &&>(__sender), static_cast<_Env &&>(__env)); }; template @@ -73,36 +63,19 @@ namespace stdexec { sender_expr<_Sender> // && __has_transform_env, _Sender, _Env>; + template + using __transform_env_result_t = + decltype(_DomainOrTag{}.transform_env(__declval<_Sender>(), __declval<_Env>())); + template concept __has_apply_sender = requires(_DomainOrTag __tag, _Args&&... __args) { __tag.apply_sender(static_cast<_Args &&>(__args)...); }; - template - constexpr auto __is_nothrow_transform_sender() noexcept -> bool { - if constexpr (__callable<__sexpr_apply_t, _Sender, __domain::__legacy_customization>) { - return __nothrow_callable<__sexpr_apply_t, _Sender, __domain::__legacy_customization>; - } else if constexpr (__domain::__has_default_transform_sender<_Sender>) { - return noexcept(tag_of_t<_Sender>().transform_sender(__declval<_Sender>())); - } else { - return __nothrow_constructible_from<_Sender, _Sender>; - } - } - - template - constexpr auto __is_nothrow_transform_sender() noexcept -> bool { - if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>) { - return // - noexcept( - tag_of_t<_Sender>().transform_sender(__declval<_Sender>(), __declval())); - } else { - return __nothrow_constructible_from<_Sender, _Sender>; - } - } - } // namespace __domain + template + using __apply_sender_result_t = decltype(_Tag{}.apply_sender(__declval<_Args>()...)); - namespace __detail { - /////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////// template using __completion_scheduler_for = __meval_or<__call_result_t, __none_such, get_completion_scheduler_t<_Tag>, _Env>; @@ -136,113 +109,121 @@ namespace stdexec { template <__has_completion_domain _Sender> using __completion_domain_of = __completion_domain_or_none<_Sender>; - } // namespace __detail - struct default_domain { - default_domain() = default; + //////////////////////////////////////////////////////////////////////////////////////////////// + //! Function object implementing `get-domain-early(snd)` + //! from [exec.snd.general] item 3.9. It is the first well-formed expression of + //! a) `get_domain(get_env(sndr))` + //! b) `completion-domain(sndr)` + //! c) `default_domain()` + struct __get_early_domain_t { + template + auto operator()(const _Sender&, _Default __def = {}) const noexcept { + if constexpr (__callable>) { + return __domain_of_t>(); + } else if constexpr (__has_completion_domain<_Sender>) { + return __completion_domain_of<_Sender>(); + } else { + return __def; + } + } + }; - // Called without the environment during eager customization - template - STDEXEC_ATTRIBUTE((always_inline)) auto transform_sender(_Sender&& __sndr) const - noexcept(__domain::__is_nothrow_transform_sender<_Sender>()) -> decltype(auto) { - // Look for a legacy customization for the given tag, and if found, apply it. - if constexpr (__callable<__sexpr_apply_t, _Sender, __domain::__legacy_customization>) { - return stdexec::__sexpr_apply( - static_cast<_Sender&&>(__sndr), __domain::__legacy_customization()); - } else if constexpr (__domain::__has_default_transform_sender<_Sender>) { - return tag_of_t<_Sender>().transform_sender(static_cast<_Sender&&>(__sndr)); - } else { - return static_cast<_Sender>(static_cast<_Sender&&>(__sndr)); + template + using __early_domain_of_t = __call_result_t<__get_early_domain_t, _Sender, _Default>; + + //////////////////////////////////////////////////////////////////////////////////////////////// + //! Function object implementing `get-domain-late(snd)` + struct __get_late_domain_t { + // When connect is looking for a customization, it first checks the sender's domain. If the + // sender knows the domain in which it completes, then that is where the subsequent task will + // execute. Otherwise, look to the receiver for late-bound information about the current + // execution context. + template + auto operator()(const _Sender& __sndr, const _Env& __env) const noexcept { + // The schedule_from algorithm is the exception to the rule. It ignores the domain of the + // predecessor, and dispatches based on the domain of the scheduler to which execution is + // being transferred. + if constexpr (sender_expr_for<_Sender, schedule_from_t>) { + return query_or(get_domain, __sexpr_apply(__sndr, __detail::__get_data()), _Default()); + } else if constexpr ( + !same_as>) { + return __get_early_domain_t{}(__sndr); + } else if constexpr (__callable) { + return get_domain(__env); + } else if constexpr (__callable<__composed, const _Env&>) { + return get_domain(get_scheduler(__env)); + } else { + return _Default(); + } } - } + }; - // Called with an environment during lazy customization template - STDEXEC_ATTRIBUTE((always_inline)) auto transform_sender(_Sender&& __sndr, const _Env& __env) const - noexcept(__domain::__is_nothrow_transform_sender<_Sender, _Env>()) -> decltype(auto) { - if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>) { - return tag_of_t<_Sender>().transform_sender(static_cast<_Sender&&>(__sndr), __env); - } else { - return static_cast<_Sender>(static_cast<_Sender&&>(__sndr)); + using __late_domain_of_t = __call_result_t<__get_late_domain_t, _Sender, _Env>; + + struct __common_domain_fn { + template + static auto __common_domain(_Domains...) noexcept { + if constexpr (sizeof...(_Domains) == 0) { + return _Default(); + } else if constexpr (__one_of<_Dependent, _Domains...>) { + return _Dependent(); + } else if constexpr (stdexec::__mvalid) { + return std::common_type_t<_Domains...>(); + } else { + return __none_such(); + } } - } - template - requires __domain::__has_legacy_c11n<_Tag, _Sender, _Args...> - || __domain::__has_apply_sender<_Tag, _Sender, _Args...> - STDEXEC_ATTRIBUTE((always_inline)) auto apply_sender(_Tag, _Sender&& __sndr, _Args&&... __args) const -> decltype(auto) { - // Look for a legacy customization for the given tag, and if found, apply it. - if constexpr (__domain::__has_legacy_c11n<_Tag, _Sender, _Args...>) { - return __domain::__legacy_c11n_fn<_Tag, _Sender, _Args...>()( - static_cast<_Sender&&>(__sndr), static_cast<_Args&&>(__args)...); - } else { - return _Tag().apply_sender(static_cast<_Sender&&>(__sndr), static_cast<_Args&&>(__args)...); + auto operator()(__ignore, __ignore, const auto&... __sndrs) const noexcept { + return __common_domain(__get_early_domain_t{}(__sndrs)...); } + }; + } // namespace __detail + + struct default_domain { + template + requires __detail::__has_default_transform_sender<_Sender, _Env...> + STDEXEC_ATTRIBUTE((always_inline)) auto transform_sender(_Sender&& __sndr, _Env&&... __env) const + noexcept(__detail::__has_nothrow_transform_sender, _Sender, _Env...>) + -> __detail::__transform_sender_result_t, _Sender, _Env...> { + return tag_of_t<_Sender>().transform_sender(static_cast<_Sender&&>(__sndr), __env...); } - template - auto transform_env(_Sender&& __sndr, _Env&& __env) const noexcept -> decltype(auto) { - if constexpr (__domain::__has_default_transform_env<_Sender, _Env>) { - return tag_of_t<_Sender>().transform_env( - static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); - } else { - return static_cast<_Env>(static_cast<_Env&&>(__env)); - } + template + STDEXEC_ATTRIBUTE((always_inline)) auto transform_sender(_Sender&& __sndr, _Env&&...) const + noexcept(__nothrow_constructible_from<_Sender, _Sender>) -> _Sender { + return static_cast<_Sender>(static_cast<_Sender&&>(__sndr)); } - }; - ///////////////////////////////////////////////////////////////////////////// - //! Function object implementing `get-domain-early(snd)` - //! from [exec.snd.general] item 3.9. It is the first well-formed expression of - //! a) `get_domain(get_env(sndr))` - //! b) `completion-domain(sndr)` - //! c) `default_domain()` - inline constexpr struct __get_early_domain_t { - template - auto operator()(const _Sender&, _Default __def = {}) const noexcept { - if constexpr (__callable>) { - return __domain_of_t>(); - } else if constexpr (__detail::__has_completion_domain<_Sender>) { - return __detail::__completion_domain_of<_Sender>(); - } else { - return __def; - } + template + requires __detail::__has_default_transform_env<_Sender, _Env> + auto transform_env(_Sender&& __sndr, _Env&& __env) const noexcept + -> __detail::__transform_env_result_t, _Sender, _Env> { + return tag_of_t<_Sender>().transform_env( + static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); } - } __get_early_domain{}; - template - using __early_domain_of_t = __call_result_t<__get_early_domain_t, _Sender, _Default>; + template + auto transform_env(__ignore, _Env&& __env) const noexcept -> _Env { + return static_cast<_Env>(static_cast<_Env&&>(__env)); + } - ///////////////////////////////////////////////////////////////////////////// - inline constexpr struct __get_late_domain_t { - // When connect is looking for a customization, it first checks the sender's domain. If the - // sender knows the domain in which it completes, then that is where the subsequent task will - // execute. Otherwise, look to the receiver for late-bound information about the current - // execution context. - template - auto operator()(const _Sender& __sndr, const _Env& __env) const noexcept { - // The schedule_from algorithm is the exception to the rule. It ignores the domain of the - // predecessor, and dispatches based on the domain of the scheduler to which execution is - // being transferred. - if constexpr (sender_expr_for<_Sender, schedule_from_t>) { - return query_or(get_domain, __sexpr_apply(__sndr, __detail::__get_data()), default_domain()); - } else if constexpr ( - !same_as>) { - return __get_early_domain(__sndr); - } else if constexpr (__callable) { - return get_domain(__env); - } else if constexpr (__callable<__composed, const _Env&>) { - return get_domain(get_scheduler(__env)); - } else { - return default_domain(); - } + template + requires __detail::__has_apply_sender<_Tag, _Args...> + STDEXEC_ATTRIBUTE((always_inline)) auto apply_sender(_Tag, _Args&&... __args) const // + -> __detail::__apply_sender_result_t<_Tag, _Args...> { + return _Tag().apply_sender(static_cast<_Args&&>(__args)...); } - } __get_late_domain{}; + }; - template - using __late_domain_of_t = __call_result_t<__get_late_domain_t, _Sender, _Env>; + inline constexpr __detail::__get_early_domain_t __get_early_domain{}; + inline constexpr __detail::__get_late_domain_t __get_late_domain{}; + using __detail::__early_domain_of_t; + using __detail::__late_domain_of_t; - ///////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// // dependent_domain struct dependent_domain { // defined in __transform_sender.hpp @@ -256,32 +237,9 @@ namespace stdexec { noexcept(__is_nothrow_transform_sender<_Sender, _Env>()) -> decltype(auto); }; - namespace __domain { - struct __common_domain_fn { - template - static auto __common_domain(_Domains...) noexcept { - if constexpr (sizeof...(_Domains) == 0) { - return default_domain(); - } else if constexpr (__one_of) { - return dependent_domain(); - } else if constexpr (stdexec::__mvalid) { - return std::common_type_t<_Domains...>(); - } else { - return __none_such(); - } - } - - auto operator()(__ignore, __ignore, const auto&... __sndrs) const noexcept { - return __common_domain(__get_early_domain(__sndrs)...); - } - }; - - template - using __common_domain_t = // - __call_result_t<__common_domain_fn, int, int, _Senders...>; + template + using __common_domain_t = __call_result_t<__detail::__common_domain_fn, int, int, _Senders...>; - template - concept __has_common_domain = // - __none_of<__none_such, __common_domain_t<_Senders...>>; - } // namespace __domain + template + concept __has_common_domain = __none_of<__none_such, __common_domain_t<_Senders...>>; } // namespace stdexec diff --git a/include/stdexec/__detail/__ensure_started.hpp b/include/stdexec/__detail/__ensure_started.hpp index 7d560fc35..a3ccda1fc 100644 --- a/include/stdexec/__detail/__ensure_started.hpp +++ b/include/stdexec/__detail/__ensure_started.hpp @@ -55,15 +55,6 @@ namespace stdexec { return {{}, {}, {}}; } - using _Sender = __1; - using __legacy_customizations_t = // - __types< - tag_invoke_t( - ensure_started_t, - get_completion_scheduler_t(get_env_t(const _Sender&)), - _Sender), - tag_invoke_t(ensure_started_t, _Sender)>; - template using __receiver_t = __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>; diff --git a/include/stdexec/__detail/__execute.hpp b/include/stdexec/__detail/__execute.hpp index 8d85fb85c..29a24a296 100644 --- a/include/stdexec/__detail/__execute.hpp +++ b/include/stdexec/__detail/__execute.hpp @@ -22,7 +22,6 @@ #include "__senders.hpp" #include "__schedulers.hpp" #include "__submit.hpp" -#include "__tag_invoke.hpp" #include "__transform_sender.hpp" #include @@ -54,17 +53,9 @@ namespace stdexec { template requires __callable<_Fun&> && move_constructible<_Fun> void operator()(_Scheduler&& __sched, _Fun __fun) const noexcept(false) { - // Look for a legacy customization - if constexpr (tag_invocable) { - tag_invoke(execute_t{}, static_cast<_Scheduler&&>(__sched), static_cast<_Fun&&>(__fun)); - } else { - auto __domain = query_or(get_domain, __sched, default_domain()); - stdexec::apply_sender( - __domain, - *this, - schedule(static_cast<_Scheduler&&>(__sched)), - static_cast<_Fun&&>(__fun)); - } + auto __domain = query_or(get_domain, __sched, default_domain()); + stdexec::apply_sender( + __domain, *this, schedule(static_cast<_Scheduler&&>(__sched)), static_cast<_Fun&&>(__fun)); } template _Sender, class _Fun> diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index 9e8062970..c475ae816 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -27,7 +27,6 @@ #include "__schedulers.hpp" #include "__sender_adaptor_closure.hpp" #include "__senders.hpp" -#include "__tag_invoke.hpp" #include "__transform_sender.hpp" #include "__transform_completion_signatures.hpp" #include "__variant.hpp" @@ -283,12 +282,12 @@ namespace stdexec { // context on which it was started (e.g., just(42)). In this case, the domain of the // scheduler is the domain of the sender. template - using __common_domain_t = // - __domain::__common_domain_t< + using __common_domain = // + __common_domain_t< __if_c<__is_scheduler_affine<_Senders>, schedule_result_t<_Sched>, _Senders>...>; template - using __f = __mcall<__mtry_catch_q<__common_domain_t, __error_fn>, _Senders...>; + using __f = __mcall<__mtry_catch_q<__common_domain, __error_fn>, _Senders...>; }; // Compute all the domains of all the result senders and make sure they're all the same @@ -427,16 +426,6 @@ namespace stdexec { return {{static_cast<_Fun&&>(__fun)}, {}, {}}; } - using _Sender = __1; - using _Function = __0; - using __legacy_customizations_t = __types< - tag_invoke_t( - __let_t, - get_completion_scheduler_t(get_env_t(const _Sender&)), - _Sender, - _Function), - tag_invoke_t(__let_t, _Sender, _Function)>; - template > _Sender, class _Env> static auto transform_env(_Sender&& __sndr, const _Env& __env) -> decltype(auto) { return __sexpr_apply( diff --git a/include/stdexec/__detail/__meta.hpp b/include/stdexec/__detail/__meta.hpp index 52350bf89..21804a635 100644 --- a/include/stdexec/__detail/__meta.hpp +++ b/include/stdexec/__detail/__meta.hpp @@ -1179,148 +1179,6 @@ namespace stdexec { } }; - template - struct __mdispatch_ { - template - auto operator()(_Ts &&...) const noexcept(noexcept(_Ty{})) -> _Ty { - return _Ty{}; - } - }; - - template - struct __mdispatch_<__placeholder<_Np>, _Offset> { - template - auto operator()(_Ts &&...__ts) const noexcept -> decltype(auto) { - return stdexec::__nth_pack_element<_Np + _Offset>(static_cast<_Ts &&>(__ts)...); - } - }; - - template - struct __mdispatch_<__placeholder<_Np> &, _Offset> { - template - auto operator()(_Ts &&...__ts) const noexcept -> decltype(auto) { - return stdexec::__nth_pack_element<_Np + _Offset>(__ts...); - } - }; - - template - struct __mdispatch_<__placeholder<_Np> &&, _Offset> { - template - auto operator()(_Ts &&...__ts) const noexcept -> decltype(auto) { - return std::move(stdexec::__nth_pack_element<_Np + _Offset>(__ts...)); - } - }; - - template - struct __mdispatch_ &, _Offset> { - template - auto operator()(_Ts &&...__ts) const noexcept -> decltype(auto) { - return std::as_const(stdexec::__nth_pack_element<_Np + _Offset>(__ts...)); - } - }; - - template - struct __mdispatch_<_Ret (*)(_Args...), _Offset> { - template - requires(__callable<__mdispatch_<_Args, _Offset>, _Ts...> && ...) - && __callable<_Ret, __call_result_t<__mdispatch_<_Args, _Offset>, _Ts...>...> - auto operator()(_Ts &&...__ts) const - noexcept(__nothrow_callable<_Ret, __call_result_t<__mdispatch_<_Args, _Offset>, _Ts...>...>) - -> __call_result_t<_Ret, __call_result_t<__mdispatch_<_Args, _Offset>, _Ts...>...> { - return _Ret{}(__mdispatch_<_Args, _Offset>{}(static_cast<_Ts &&>(__ts)...)...); - } - }; - - template - struct __mdispatch_<_Ret (*)(_Args..., ...), _Offset> { - static_assert(_Offset == 0, "nested pack expressions are not supported"); - using _Pattern = __mback<_Args...>; - static constexpr std::size_t __offset = - __get_placeholder_offset(static_cast<__mtype<_Pattern> *>(nullptr)); - - struct __impl { - template - requires(__callable<__mdispatch_<_Args>, _Ts...> && ...) - && (__callable<__mdispatch_<_Pattern, _Idx + 1>, _Ts...> && ...) - && __callable< // - _Ret, - __call_result_t<__mdispatch_<_Args>, _Ts...>..., - __call_result_t<__mdispatch_<_Pattern, _Idx + 1>, _Ts...>...> - auto operator()(__indices<_Idx...>, _Ts &&...__ts) const - noexcept(__nothrow_callable< // - _Ret, // - __call_result_t<__mdispatch_<_Args>, _Ts...>..., // - __call_result_t<__mdispatch_<_Pattern, _Idx + 1>, _Ts...>...>) - -> __call_result_t< // - _Ret, - __call_result_t<__mdispatch_<_Args>, _Ts...>..., - __call_result_t<__mdispatch_<_Pattern, _Idx + 1>, _Ts...>...> { - return _Ret()( // - __mdispatch_<_Args>()(static_cast<_Ts &&>(__ts)...)..., // - __mdispatch_<_Pattern, _Idx + 1>()(static_cast<_Ts &&>(__ts)...)...); - } - }; - - template - requires(__offset < sizeof...(_Ts)) - && __callable<__impl, __make_indices, _Ts...> - auto operator()(_Ts &&...__ts) const - noexcept(__nothrow_callable<__impl, __make_indices, _Ts...>) - -> __msecond< - __if_c<(__offset < sizeof...(_Ts))>, - __call_result_t<__impl, __make_indices, _Ts...>> { - return __impl()(__make_indices(), static_cast<_Ts &&>(__ts)...); - } - - template - requires(sizeof...(_Ts) == __offset) - && __callable<__mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...> *>, _Ts...> - auto operator()(_Ts &&...__ts) const // - noexcept( - __nothrow_callable<__mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...> *>, _Ts...>) - -> __msecond< - __if_c<(sizeof...(_Ts) == __offset)>, - __call_result_t<__mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...> *>, _Ts...>> { - return __mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...> *>()( - static_cast<_Ts &&>(__ts)...); - } - }; - - template - struct __mdispatch { }; - - template - struct __mdispatch<_Ret(_Args...)> : __mdispatch_<_Ret (*)(_Args...)> { }; - - template - struct __mdispatch<_Ret(_Args..., ...)> : __mdispatch_<_Ret (*)(_Args..., ...)> { }; - - template - concept __dispatchable = __callable<__mdispatch<_Ty>, _Ts...>; - - template - concept __nothrow_dispatchable = __nothrow_callable<__mdispatch<_Ty>, _Ts...>; - - template - using __dispatch_result_t = __call_result_t<__mdispatch<_Ty>, _Ts...>; - - template - using __try_dispatch_ = __mbool<__dispatchable<_Signature, _Args...>>; - - template > - struct __which { }; - - template