1 /* 2 * Copyright (c) 2021-2024 NVIDIA Corporation 3 * 4 * Licensed under the Apache License Version 2.0 with LLVM Exceptions 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * https://llvm.org/LICENSE.txt 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #include "__execution_fwd.hpp" 19 20 #include "__config.hpp" 21 #include "__concepts.hpp" 22 #include "__env.hpp" 23 #include "__sender_introspection.hpp" 24 #include "__meta.hpp" 25 26 #include "../functional.hpp" 27 #include "__utility.hpp" 28 29 namespace stdexec { 30 31 struct default_domain; 32 struct dependent_domain; 33 34 namespace __detail { 35 template <class _DomainOrTag, class _Sender, class... _Env> 36 concept __has_transform_sender = 37 requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) { 38 __tag.transform_sender(static_cast<_Sender &&>(__sender), __env...); 39 }; 40 41 template <class _DomainOrTag, class _Sender, class... _Env> 42 concept __has_nothrow_transform_sender = 43 requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) { 44 { __tag.transform_sender(static_cast<_Sender &&>(__sender), __env...) } noexcept; 45 }; 46 47 template <class _Sender, class... _Env> 48 concept __has_default_transform_sender = 49 sender_expr<_Sender> && __has_transform_sender<tag_of_t<_Sender>, _Sender, _Env...>; 50 51 template <class _DomainOrTag, class _Sender, class... _Env> 52 using __transform_sender_result_t = 53 decltype(_DomainOrTag{}.transform_sender(__declval<_Sender>(), __declval<const _Env&>()...)); 54 55 template <class _DomainOrTag, class _Sender, class _Env> 56 concept __has_transform_env = requires(_DomainOrTag __tag, _Sender&& __sender, _Env&& __env) { 57 __tag.transform_env(static_cast<_Sender &&>(__sender), static_cast<_Env &&>(__env)); 58 }; 59 60 template <class _Sender, class _Env> 61 concept __has_default_transform_env = sender_expr<_Sender> 62 && __has_transform_env<tag_of_t<_Sender>, _Sender, _Env>; 63 64 template <class _DomainOrTag, class _Sender, class _Env> 65 using __transform_env_result_t = 66 decltype(_DomainOrTag{}.transform_env(__declval<_Sender>(), __declval<_Env>())); 67 68 template <class _DomainOrTag, class... _Args> 69 concept __has_apply_sender = requires(_DomainOrTag __tag, _Args&&... __args) { 70 __tag.apply_sender(static_cast<_Args &&>(__args)...); 71 }; 72 73 template <class _Tag, class... _Args> 74 using __apply_sender_result_t = decltype(_Tag{}.apply_sender(__declval<_Args>()...)); 75 76 //////////////////////////////////////////////////////////////////////////////////////////////// 77 template <class _Env, class _Tag> 78 using __completion_scheduler_for = 79 __meval_or<__call_result_t, __none_such, get_completion_scheduler_t<_Tag>, _Env>; 80 81 template <class _Env, class _Tag> 82 using __completion_domain_for = 83 __meval_or<__call_result_t, __none_such, get_domain_t, __completion_scheduler_for<_Env, _Tag>>; 84 85 // Check the value, error, and stopped channels for completion schedulers. 86 // Of the completion schedulers that are known, they must all have compatible 87 // domains. This computes that domain, or else returns __none_such if there 88 // are no completion schedulers or if they don't specify a domain. 89 template <class _Env> 90 struct __completion_domain_or_none_ 91 : __mdefer_< 92 __mtransform< 93 __mbind_front_q<__completion_domain_for, _Env>, 94 __mremove<__none_such, __munique<__msingle_or<__none_such>>> 95 >, 96 set_value_t, 97 set_error_t, 98 set_stopped_t 99 > { }; 100 101 template <class _Sender> 102 using __completion_domain_or_none = __t<__completion_domain_or_none_<env_of_t<_Sender>>>; 103 104 template <class _Sender> 105 concept __consistent_completion_domains = __mvalid<__completion_domain_or_none, _Sender>; 106 107 template <class _Sender> 108 concept __has_completion_domain = (!same_as<__completion_domain_or_none<_Sender>, __none_such>); 109 110 template <__has_completion_domain _Sender> 111 using __completion_domain_of = __completion_domain_or_none<_Sender>; 112 113 //////////////////////////////////////////////////////////////////////////////////////////////// 114 //! Function object implementing `get-domain-early(snd)` 115 //! from [exec.snd.general] item 3.9. It is the first well-formed expression of 116 //! a) `get_domain(get_env(sndr))` 117 //! b) `completion-domain(sndr)` 118 //! c) `default_domain()` 119 struct __get_early_domain_t { 120 template <class _Sender, class _Default = default_domain> operator ()stdexec::__detail::__get_early_domain_t121 auto operator()(const _Sender&, _Default = {}) const noexcept { 122 if constexpr (__callable<get_domain_t, env_of_t<_Sender>>) { 123 return __domain_of_t<env_of_t<_Sender>>(); 124 } else if constexpr (__has_completion_domain<_Sender>) { 125 return __completion_domain_of<_Sender>(); 126 } else { 127 return _Default(); 128 } 129 } 130 }; 131 132 template <class _Sender, class _Default = default_domain> 133 using __early_domain_of_t = __call_result_t<__get_early_domain_t, _Sender, _Default>; 134 135 //////////////////////////////////////////////////////////////////////////////////////////////// 136 //! Function object implementing `get-domain-late(snd)` 137 struct __get_late_domain_t { 138 // When connect is looking for a customization, it first checks if the sender has a 139 // late domain override. If so, that is the domain that is used to transform the 140 // sender. Otherwise, look to the receiver for information about where the resulting 141 // operation state will be started. 142 template <class _Sender, class _Env, class _Default = default_domain> operator ()stdexec::__detail::__get_late_domain_t143 auto operator()(const _Sender& __sndr, const _Env& __env, _Default = {}) const noexcept { 144 // The schedule_from algorithm is the exception to the rule. It ignores the domain 145 // of the predecessor, and dispatches based on the domain of the scheduler to 146 // which execution is being transferred. 147 if constexpr (__callable<get_domain_override_t, env_of_t<_Sender>>) { 148 return get_domain_override(get_env(__sndr)); 149 } else if constexpr (__callable<get_domain_t, const _Env&>) { 150 return get_domain(__env); 151 } else if constexpr (__callable<__composed<get_domain_t, get_scheduler_t>, const _Env&>) { 152 return get_domain(get_scheduler(__env)); 153 } else { 154 return _Default(); 155 } 156 } 157 }; 158 159 template <class _Sender, class _Env, class _Default = default_domain> 160 using __late_domain_of_t = __call_result_t<__get_late_domain_t, _Sender, _Env, _Default>; 161 162 struct __common_domain_fn { 163 template < 164 class _Default = default_domain, 165 class _Dependent = dependent_domain, 166 class... _Domains 167 > __common_domainstdexec::__detail::__common_domain_fn168 static auto __common_domain(_Domains...) noexcept { 169 if constexpr (sizeof...(_Domains) == 0) { 170 return _Default(); 171 } else if constexpr (__one_of<_Dependent, _Domains...>) { 172 return _Dependent(); 173 } else if constexpr (stdexec::__mvalid<std::common_type_t, _Domains...>) { 174 return std::common_type_t<_Domains...>(); 175 } else { 176 return __none_such(); 177 } 178 } 179 operator ()stdexec::__detail::__common_domain_fn180 auto operator()(__ignore, __ignore, const auto&... __sndrs) const noexcept { 181 return __common_domain(__get_early_domain_t{}(__sndrs)...); 182 } 183 }; 184 } // namespace __detail 185 186 struct default_domain { 187 template <class _Sender, class... _Env> 188 requires __detail::__has_default_transform_sender<_Sender, _Env...> STDEXEC_ATTRIBUTEstdexec::default_domain189 STDEXEC_ATTRIBUTE(always_inline) 190 auto transform_sender(_Sender&& __sndr, _Env&&... __env) const 191 noexcept(__detail::__has_nothrow_transform_sender<tag_of_t<_Sender>, _Sender, _Env...>) 192 -> __detail::__transform_sender_result_t<tag_of_t<_Sender>, _Sender, _Env...> { 193 return tag_of_t<_Sender>().transform_sender(static_cast<_Sender&&>(__sndr), __env...); 194 } 195 196 template <class _Sender, class... _Env> STDEXEC_ATTRIBUTEstdexec::default_domain197 STDEXEC_ATTRIBUTE(always_inline) 198 auto transform_sender(_Sender&& __sndr, _Env&&...) const 199 noexcept(__nothrow_constructible_from<_Sender, _Sender>) -> _Sender { 200 return static_cast<_Sender>(static_cast<_Sender&&>(__sndr)); 201 } 202 203 template <class _Sender, class _Env> 204 requires __detail::__has_default_transform_env<_Sender, _Env> transform_envstdexec::default_domain205 auto transform_env(_Sender&& __sndr, _Env&& __env) const noexcept 206 -> __detail::__transform_env_result_t<tag_of_t<_Sender>, _Sender, _Env> { 207 return tag_of_t<_Sender>() 208 .transform_env(static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); 209 } 210 211 template <class _Env> transform_envstdexec::default_domain212 auto transform_env(__ignore, _Env&& __env) const noexcept -> _Env { 213 return static_cast<_Env>(static_cast<_Env&&>(__env)); 214 } 215 216 template <class _Tag, class... _Args> 217 requires __detail::__has_apply_sender<_Tag, _Args...> STDEXEC_ATTRIBUTEstdexec::default_domain218 STDEXEC_ATTRIBUTE(always_inline) 219 auto apply_sender(_Tag, _Args&&... __args) const 220 -> __detail::__apply_sender_result_t<_Tag, _Args...> { 221 return _Tag().apply_sender(static_cast<_Args&&>(__args)...); 222 } 223 }; 224 225 inline constexpr __detail::__get_early_domain_t __get_early_domain{}; 226 inline constexpr __detail::__get_late_domain_t __get_late_domain{}; 227 using __detail::__early_domain_of_t; 228 using __detail::__late_domain_of_t; 229 230 ////////////////////////////////////////////////////////////////////////////////////////////////// 231 // dependent_domain 232 struct dependent_domain { 233 // defined in __transform_sender.hpp 234 template <class _Sender, class _Env> 235 static constexpr auto __is_nothrow_transform_sender() noexcept -> bool; 236 237 // defined in __transform_sender.hpp 238 template <sender_expr _Sender, class _Env> 239 requires same_as<__early_domain_of_t<_Sender>, dependent_domain> 240 STDEXEC_ATTRIBUTE(always_inline) 241 auto transform_sender(_Sender&& __sndr, const _Env& __env) const 242 noexcept(__is_nothrow_transform_sender<_Sender, _Env>()) -> decltype(auto); 243 }; 244 245 template <class... _Senders> 246 using __common_domain_t = __call_result_t<__detail::__common_domain_fn, int, int, _Senders...>; 247 248 template <class... _Senders> 249 concept __has_common_domain = __none_of<__none_such, __common_domain_t<_Senders...>>; 250 } // namespace stdexec 251