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 these after __execution_fwd.hpp 21 #include "__basic_sender.hpp" 22 #include "__concepts.hpp" 23 #include "__diagnostics.hpp" 24 #include "__domain.hpp" 25 #include "__env.hpp" 26 #include "__meta.hpp" 27 #include "__sender_introspection.hpp" 28 #include "__type_traits.hpp" 29 30 STDEXEC_PRAGMA_PUSH() 31 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference) 32 33 namespace stdexec { 34 ///////////////////////////////////////////////////////////////////////////// 35 // [execution.transform_sender] 36 namespace __detail { 37 struct __transform_env { 38 template <class _Domain, class _Sender, class _Env> STDEXEC_ATTRIBUTEstdexec::__detail::__transform_env39 STDEXEC_ATTRIBUTE(always_inline) 40 auto 41 operator()(_Domain __dom, _Sender&& __sndr, _Env&& __env) const noexcept -> decltype(auto) { 42 if constexpr (__detail::__has_transform_env<_Domain, _Sender, _Env>) { 43 return __dom.transform_env(static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); 44 } else { 45 return default_domain() 46 .transform_env(static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); 47 } 48 } 49 }; 50 51 struct __transform_sender_1 { 52 template <class _Domain, class _Sender, class... _Env> STDEXEC_ATTRIBUTEstdexec::__detail::__transform_sender_153 STDEXEC_ATTRIBUTE(always_inline) 54 static constexpr auto __is_nothrow() noexcept -> bool { 55 if constexpr (__detail::__has_transform_sender<_Domain, _Sender, _Env...>) { 56 return noexcept(__declval<_Domain&>() 57 .transform_sender(__declval<_Sender>(), __declval<const _Env&>()...)); 58 } else { 59 return noexcept(default_domain() 60 .transform_sender(__declval<_Sender>(), __declval<const _Env&>()...)); 61 } 62 } 63 64 template <class _Domain, class _Sender, class... _Env> STDEXEC_ATTRIBUTEstdexec::__detail::__transform_sender_165 STDEXEC_ATTRIBUTE(always_inline) 66 auto operator()(_Domain __dom, _Sender&& __sndr, const _Env&... __env) const 67 noexcept(__is_nothrow<_Domain, _Sender, const _Env&...>()) -> decltype(auto) { 68 if constexpr (__detail::__has_transform_sender<_Domain, _Sender, _Env...>) { 69 return __dom.transform_sender(static_cast<_Sender&&>(__sndr), __env...); 70 } else { 71 return default_domain().transform_sender(static_cast<_Sender&&>(__sndr), __env...); 72 } 73 } 74 }; 75 76 template <class _Ty, class _Uy> 77 concept __decay_same_as = same_as<__decay_t<_Ty>, __decay_t<_Uy>>; 78 79 struct __transform_sender { 80 template <class _Self = __transform_sender, class _Domain, class _Sender, class... _Env> STDEXEC_ATTRIBUTEstdexec::__detail::__transform_sender81 STDEXEC_ATTRIBUTE(always_inline) 82 auto operator()(_Domain __dom, _Sender&& __sndr, const _Env&... __env) const 83 noexcept(__nothrow_callable<__transform_sender_1, _Domain, _Sender, const _Env&...>) 84 -> decltype(auto) { 85 using _Sender2 = __call_result_t<__transform_sender_1, _Domain, _Sender, const _Env&...>; 86 // If the transformation doesn't change the sender's type, then do not 87 // apply the transform recursively. 88 if constexpr (__decay_same_as<_Sender, _Sender2>) { 89 return __transform_sender_1()(__dom, static_cast<_Sender&&>(__sndr), __env...); 90 } else { 91 // We transformed the sender and got back a different sender. Transform that one too. 92 return _Self()( 93 __dom, 94 __transform_sender_1()(__dom, static_cast<_Sender&&>(__sndr), __env...), 95 __env...); 96 } 97 } 98 }; 99 100 struct __transform_dependent_sender { 101 // If we are doing a lazy customization of a type whose domain is value-dependent (e.g., 102 // let_value), first transform the sender to determine the domain. Then continue transforming 103 // the sender with the requested domain. 104 template <class _Domain, sender_expr _Sender, class _Env> 105 requires same_as<__early_domain_of_t<_Sender>, dependent_domain> operator ()stdexec::__detail::__transform_dependent_sender106 auto operator()(_Domain __dom, _Sender&& __sndr, const _Env& __env) const 107 noexcept(noexcept(__transform_sender()( 108 __dom, 109 dependent_domain().transform_sender(static_cast<_Sender&&>(__sndr), __env), 110 __env))) -> decltype(auto) { 111 static_assert(__none_of<_Domain, dependent_domain>); 112 return __transform_sender()( 113 __dom, dependent_domain().transform_sender(static_cast<_Sender&&>(__sndr), __env), __env); 114 } 115 }; 116 } // namespace __detail 117 118 ///////////////////////////////////////////////////////////////////////////// 119 // [execution.transform_sender] 120 inline constexpr struct transform_sender_t 121 : __detail::__transform_sender 122 , __detail::__transform_dependent_sender { 123 using __detail::__transform_sender::operator(); 124 using __detail::__transform_dependent_sender::operator(); 125 } transform_sender{}; 126 127 inline constexpr __detail::__transform_env transform_env{}; 128 129 struct _CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_ { }; 130 131 template <class _Sender, class _Env> __is_nothrow_transform_sender()132 constexpr auto dependent_domain::__is_nothrow_transform_sender() noexcept -> bool { 133 using _Env2 = __call_result_t<__detail::__transform_env, dependent_domain&, _Sender, _Env>; 134 return __v<decltype(__sexpr_apply( 135 __declval<_Sender>(), 136 []<class _Tag, class _Data, class... _Childs>(_Tag, _Data&&, _Childs&&...) { 137 constexpr bool __first_transform_is_nothrow = noexcept(__make_sexpr<_Tag>( 138 __declval<_Data>(), 139 __detail::__transform_sender()( 140 __declval<dependent_domain&>(), __declval<_Childs>(), __declval<const _Env2&>())...)); 141 using _Sender2 = decltype(__make_sexpr<_Tag>( 142 __declval<_Data>(), 143 __detail::__transform_sender()( 144 __declval<dependent_domain&>(), __declval<_Childs>(), __declval<const _Env2&>())...)); 145 using _Domain2 = 146 decltype(__sexpr_apply(__declval<_Sender2&>(), __detail::__common_domain_fn())); 147 constexpr bool __second_transform_is_nothrow = noexcept(__detail::__transform_sender()( 148 __declval<_Domain2&>(), __declval<_Sender2>(), __declval<const _Env&>())); 149 return __mbool<__first_transform_is_nothrow && __second_transform_is_nothrow>(); 150 }))>; 151 } 152 153 template <sender_expr _Sender, class _Env> 154 requires same_as<__early_domain_of_t<_Sender>, dependent_domain> transform_sender(_Sender && __sndr,const _Env & __env) const155 auto dependent_domain::transform_sender(_Sender&& __sndr, const _Env& __env) const 156 noexcept(__is_nothrow_transform_sender<_Sender, _Env>()) -> decltype(auto) { 157 // apply any algorithm-specific transformation to the environment 158 const auto& __env2 = transform_env(*this, static_cast<_Sender&&>(__sndr), __env); 159 160 // recursively transform the sender to determine the domain 161 return __sexpr_apply( 162 static_cast<_Sender&&>(__sndr), 163 [&]<class _Tag, class _Data, class... _Childs>(_Tag, _Data&& __data, _Childs&&... __childs) { 164 // TODO: propagate meta-exceptions here: 165 auto __sndr2 = __make_sexpr<_Tag>( 166 static_cast<_Data&&>(__data), 167 __detail::__transform_sender()(*this, static_cast<_Childs&&>(__childs), __env2)...); 168 using _Sender2 = decltype(__sndr2); 169 170 auto __domain2 = __sexpr_apply(__sndr2, __detail::__common_domain_fn()); 171 using _Domain2 = decltype(__domain2); 172 173 if constexpr (same_as<_Domain2, __none_such>) { 174 return __mexception<_CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_, _WITH_SENDER_<_Sender2>>(); 175 } else { 176 return __detail::__transform_sender()(__domain2, std::move(__sndr2), __env); 177 } 178 }); 179 } 180 181 ///////////////////////////////////////////////////////////////////////////// 182 template <class _Tag, class _Domain, class _Sender, class... _Args> 183 concept __has_implementation_for = 184 __detail::__has_apply_sender<_Domain, _Tag, _Sender, _Args...> 185 || __detail::__has_apply_sender<default_domain, _Tag, _Sender, _Args...>; 186 187 ///////////////////////////////////////////////////////////////////////////// 188 // [execution.apply_sender] 189 inline constexpr struct apply_sender_t { 190 template <class _Domain, class _Tag, class _Sender, class... _Args> 191 requires __has_implementation_for<_Tag, _Domain, _Sender, _Args...> STDEXEC_ATTRIBUTEstdexec::apply_sender_t192 STDEXEC_ATTRIBUTE(always_inline) 193 auto 194 operator()(_Domain __dom, _Tag, _Sender&& __sndr, _Args&&... __args) const -> decltype(auto) { 195 if constexpr (__detail::__has_apply_sender<_Domain, _Tag, _Sender, _Args...>) { 196 return __dom 197 .apply_sender(_Tag(), static_cast<_Sender&&>(__sndr), static_cast<_Args&&>(__args)...); 198 } else { 199 return default_domain() 200 .apply_sender(_Tag(), static_cast<_Sender&&>(__sndr), static_cast<_Args&&>(__args)...); 201 } 202 } 203 } apply_sender{}; 204 205 template <class _Domain, class _Tag, class _Sender, class... _Args> 206 using apply_sender_result_t = __call_result_t<apply_sender_t, _Domain, _Tag, _Sender, _Args...>; 207 208 ///////////////////////////////////////////////////////////////////////////// 209 template <class _Sender, class _Scheduler, class _Tag = set_value_t> 210 concept __completes_on = 211 __decays_to<__call_result_t<get_completion_scheduler_t<_Tag>, env_of_t<_Sender>>, _Scheduler>; 212 213 ///////////////////////////////////////////////////////////////////////////// 214 template <class _Sender, class _Scheduler, class _Env> 215 concept __starts_on = __decays_to<__call_result_t<get_scheduler_t, _Env>, _Scheduler>; 216 } // namespace stdexec 217 218 STDEXEC_PRAGMA_POP() 219