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 "__concepts.hpp" 21 #include "__diagnostics.hpp" 22 #include "__env.hpp" 23 #include "__meta.hpp" 24 #include "__senders_core.hpp" 25 #include "__sender_introspection.hpp" 26 #include "__tuple.hpp" 27 #include "__type_traits.hpp" 28 29 #include <utility> // for tuple_size/tuple_element 30 #include <cstddef> 31 #include <new> // IWYU pragma: keep for placement new 32 #include <type_traits> 33 34 namespace stdexec { 35 ///////////////////////////////////////////////////////////////////////////// 36 // Generic __sender type 37 namespace __detail { 38 template <class _Sender> 39 using __impl_of = decltype((__declval<_Sender>().__impl_)); 40 } // namespace __detail 41 42 template < 43 class _Descriptor, 44 auto _DescriptorFn = 45 [] { 46 return _Descriptor(); 47 } 48 > 49 inline constexpr auto __descriptor_fn_v = _DescriptorFn; 50 51 template <class _Tag, class _Data, class... _Child> __descriptor_fn()52 inline constexpr auto __descriptor_fn() { 53 return __descriptor_fn_v<__detail::__desc<_Tag, _Data, _Child...>>; 54 } 55 56 #if STDEXEC_EDG() 57 # define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \ 58 stdexec::__descriptor_fn<_Tag, _Data, _Child>() 59 #else 60 # define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \ 61 stdexec::__descriptor_fn_v<stdexec::__detail::__desc<_Tag, _Data, _Child>> 62 #endif 63 64 template <class _Tag> 65 struct __sexpr_impl; 66 67 template <class _Sexpr, class _Receiver> 68 struct __op_state; 69 70 template <class _ReceiverId, class _Sexpr, std::size_t _Idx> 71 struct __rcvr; 72 73 namespace __detail { 74 template <class _Sexpr, class _Receiver> 75 struct __connect_fn; 76 77 template <class _Tag, class _Sexpr, class _Receiver> 78 using __state_type_t = 79 __decay_t<__result_of<__sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>; 80 81 template <class _Self, class _Tag, class _Index, class _Sexpr, class _Receiver> 82 using __env_type_t = __result_of< 83 __sexpr_impl<__meval<__msecond, _Self, _Tag>>::get_env, 84 _Index, 85 __state_type_t<__meval<__msecond, _Self, _Tag>, _Sexpr, _Receiver>&, 86 _Receiver& 87 >; 88 89 template <class _Sexpr, class _Receiver> 90 concept __connectable = 91 __callable<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>, __connect_fn<_Sexpr, _Receiver>> 92 && __mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>; 93 94 struct __defaults { 95 static constexpr auto get_attrs = 96 [](__ignore, const auto&... __child) noexcept -> decltype(auto) { 97 if constexpr (sizeof...(__child) == 1) { 98 return __env::__fwd_fn()(stdexec::get_env(__child...)); 99 } else { 100 return env<>(); 101 } 102 }; 103 104 static constexpr auto get_env = 105 []<class _Receiver>(__ignore, __ignore, const _Receiver& __rcvr) noexcept 106 -> env_of_t<const _Receiver&> { 107 return stdexec::get_env(__rcvr); 108 }; 109 110 static constexpr auto get_state = 111 []<class _Sender>(_Sender&& __sndr, __ignore) noexcept -> decltype(auto) { 112 return __sndr.apply(static_cast<_Sender&&>(__sndr), __get_data()); 113 }; 114 115 static constexpr auto connect = 116 []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver __rcvr) noexcept( 117 __nothrow_constructible_from<__op_state<_Sender, _Receiver>, _Sender, _Receiver>) 118 -> __op_state<_Sender, _Receiver> 119 requires __connectable<_Sender, _Receiver> 120 { 121 return __op_state<_Sender, _Receiver>{ 122 static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr)}; 123 }; 124 125 static constexpr auto start = []<class _StartTag = start_t, class... _ChildOps>( 126 __ignore, 127 __ignore, 128 _ChildOps&... __ops) noexcept { 129 (_StartTag()(__ops), ...); 130 }; 131 132 static constexpr auto complete = 133 []<class _Index, class _Receiver, class _SetTag, class... _Args>( 134 _Index, 135 __ignore, 136 _Receiver& __rcvr, 137 _SetTag, 138 _Args&&... __args) noexcept { 139 static_assert(__v<_Index> == 0, "I don't know how to complete this operation."); 140 _SetTag()(std::move(__rcvr), static_cast<_Args&&>(__args)...); 141 }; 142 143 static constexpr auto get_completion_signatures = 144 []<class _Sender>(_Sender&&, auto&&...) noexcept { 145 static_assert( 146 __mnever<tag_of_t<_Sender>>, 147 "No customization of get_completion_signatures for this sender tag type."); 148 }; 149 }; 150 151 template <class _Sexpr, class _Receiver> 152 using __state_t = __state_type_t<typename __decay_t<_Sexpr>::__tag_t, _Sexpr, _Receiver>; 153 154 template <class _Sexpr, class _Receiver> 155 struct __op_base; 156 157 template <class _Receiver> 158 struct __receiver_box { 159 _Receiver __rcvr_; 160 STDEXEC_ATTRIBUTEstdexec::__detail::__receiver_box161 STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() & noexcept -> _Receiver& { 162 return this->__rcvr_; 163 } 164 STDEXEC_ATTRIBUTEstdexec::__detail::__receiver_box165 STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() const & noexcept -> const _Receiver& { 166 return this->__rcvr_; 167 } 168 }; 169 170 template <class _Sexpr, class _Receiver> 171 struct __state_box : __immovable { 172 using __tag_t = __decay_t<_Sexpr>::__tag_t; 173 using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>; 174 __state_boxstdexec::__detail::__state_box175 __state_box(_Sexpr&& __sndr, _Receiver& __rcvr) 176 noexcept(__noexcept_of<__sexpr_impl<__tag_t>::get_state, _Sexpr, _Receiver&>) { 177 ::new (static_cast<void*>(__buf_)) auto( 178 __sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr)); 179 } 180 ~__state_boxstdexec::__detail::__state_box181 ~__state_box() { 182 reinterpret_cast<__state_t*>(__buf_)->~__state_t(); 183 } 184 STDEXEC_ATTRIBUTEstdexec::__detail::__state_box185 STDEXEC_ATTRIBUTE(always_inline) auto __state() & noexcept -> __state_t& { 186 return *reinterpret_cast<__state_t*>(__buf_); 187 } 188 STDEXEC_ATTRIBUTEstdexec::__detail::__state_box189 STDEXEC_ATTRIBUTE(always_inline) auto __state() const & noexcept -> const __state_t& { 190 return *reinterpret_cast<const __state_t*>(__buf_); 191 } 192 193 // We use a buffer to store the state object to make __state_box a standard-layout type 194 // regardless of whether __state_t is standard-layout or not. 195 alignas(__state_t) std::byte __buf_[sizeof(__state_t)]; // NOLINT(modernize-avoid-c-arrays) 196 }; 197 198 template <class _Sexpr, class _Receiver, class _State> 199 struct __enable_receiver_from_this { 200 #if STDEXEC_HAS_FEATURE(undefined_behavior_sanitizer) && STDEXEC_CLANG() 201 // See https://github.com/llvm/llvm-project/issues/101276 202 [[clang::noinline]] 203 #endif __receiverstdexec::__detail::__enable_receiver_from_this204 auto __receiver() noexcept -> decltype(auto) { 205 void* __state = static_cast<_State*>(this); 206 // The following cast use the pointer-interconvertibility between the __state_box::__buf_ 207 // member and the containing __state_box object itself. 208 auto* __sbox = static_cast<__state_box<_Sexpr, _Receiver>*>(__state); 209 return (static_cast<__op_base<_Sexpr, _Receiver>*>(__sbox)->__rcvr_); 210 } 211 }; 212 213 template <class _Sexpr, class _Receiver> 214 concept __state_uses_receiver = derived_from< 215 __state_t<_Sexpr, _Receiver>, 216 __enable_receiver_from_this<_Sexpr, _Receiver, __state_t<_Sexpr, _Receiver>> 217 >; 218 219 template <class _Sexpr, class _Receiver> 220 struct __op_base : __immovable { 221 using __tag_t = __decay_t<_Sexpr>::__tag_t; 222 using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>; 223 224 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS 225 _Receiver __rcvr_; 226 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS 227 __state_t __state_; 228 __op_basestdexec::__detail::__op_base229 __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) noexcept( 230 __nothrow_decay_copyable<_Receiver> 231 && noexcept( 232 __state_t(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr_)))) 233 : __rcvr_(static_cast<_Receiver&&>(__rcvr)) 234 , __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr_)) { 235 } 236 STDEXEC_ATTRIBUTEstdexec::__detail::__op_base237 STDEXEC_ATTRIBUTE(always_inline) auto __state() & noexcept -> __state_t& { 238 return __state_; 239 } 240 STDEXEC_ATTRIBUTEstdexec::__detail::__op_base241 STDEXEC_ATTRIBUTE(always_inline) auto __state() const & noexcept -> const __state_t& { 242 return __state_; 243 } 244 STDEXEC_ATTRIBUTEstdexec::__detail::__op_base245 STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() & noexcept -> _Receiver& { 246 return __rcvr_; 247 } 248 STDEXEC_ATTRIBUTEstdexec::__detail::__op_base249 STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() const & noexcept -> const _Receiver& { 250 return __rcvr_; 251 } 252 }; 253 254 template <class _Sexpr, class _Receiver> 255 requires __state_uses_receiver<_Sexpr, _Receiver> 256 struct __op_base<_Sexpr, _Receiver> 257 : __receiver_box<_Receiver> 258 , __state_box<_Sexpr, _Receiver> { 259 using __tag_t = __decay_t<_Sexpr>::__tag_t; 260 using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>; 261 262 STDEXEC_IMMOVABLE(__op_base); 263 __op_basestdexec::__detail::__op_base264 __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) 265 noexcept(__nothrow_decay_copyable<_Receiver> && __nothrow_move_constructible<__state_t>) 266 : __receiver_box<_Receiver>{static_cast<_Receiver&&>(__rcvr)} 267 , __state_box<_Sexpr, _Receiver>{static_cast<_Sexpr&&>(__sndr), this->__rcvr_} { 268 // This is necessary to ensure that the state object is pointer-interconvertible 269 // with the __state_box object for the sake of __enable_receiver_from_this. 270 static_assert(std::is_standard_layout_v<__state_box<_Sexpr, _Receiver>>); 271 } 272 }; 273 274 STDEXEC_PRAGMA_PUSH() 275 STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces") 276 277 template <class _Sexpr, class _Receiver> 278 struct __connect_fn { 279 template <std::size_t _Idx> 280 using __receiver_t = __t<__rcvr<__id<_Receiver>, _Sexpr, _Idx>>; 281 282 __op_state<_Sexpr, _Receiver>* __op_; 283 284 struct __impl { 285 __op_state<_Sexpr, _Receiver>* __op_; 286 287 template <std::size_t... _Is, class... _Child> operator ()stdexec::__detail::__connect_fn::__impl288 auto operator()(__indices<_Is...>, _Child&&... __child) const 289 noexcept((__nothrow_connectable<_Child, __receiver_t<_Is>> && ...)) 290 -> __tuple_for<connect_result_t<_Child, __receiver_t<_Is>>...> { 291 return __tuple{connect(static_cast<_Child&&>(__child), __receiver_t<_Is>{__op_})...}; 292 } 293 }; 294 295 template <class... _Child> operator ()stdexec::__detail::__connect_fn296 auto operator()(__ignore, __ignore, _Child&&... __child) const 297 noexcept(__nothrow_callable<__impl, __indices_for<_Child...>, _Child...>) 298 -> __call_result_t<__impl, __indices_for<_Child...>, _Child...> { 299 return __impl{__op_}(__indices_for<_Child...>(), static_cast<_Child&&>(__child)...); 300 } 301 operator ()stdexec::__detail::__connect_fn302 auto operator()(__ignore, __ignore) const noexcept -> __tuple_for<> { 303 return {}; 304 } 305 }; 306 STDEXEC_PRAGMA_POP()307 STDEXEC_PRAGMA_POP() 308 309 inline constexpr auto __drop_front = []<class _Fn>(_Fn __fn) noexcept { 310 return [__fn = std::move(__fn)]<class... _Rest>(auto&&, _Rest&&... __rest) noexcept( 311 __nothrow_callable<const _Fn&, _Rest...>) -> __call_result_t<const _Fn&, _Rest...> { 312 return __fn(static_cast<_Rest&&>(__rest)...); 313 }; 314 }; 315 316 template <class _Tag, class... _Captures> STDEXEC_ATTRIBUTE(host,device,always_inline)317 STDEXEC_ATTRIBUTE(host, device, always_inline) 318 constexpr auto __captures(_Tag, _Captures&&... __captures2) { 319 return 320 [... __captures3 = static_cast<_Captures&&>(__captures2)]<class _Cvref, class _Fun>( 321 _Cvref, 322 _Fun&& 323 __fun) mutable noexcept(__nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>) 324 -> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...> 325 requires __callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...> 326 { 327 // The use of decltype(__captures3) here instead of _Captures is a workaround for 328 // a codegen bug in nvc++. 329 return static_cast<_Fun&&>( 330 __fun)(_Tag(), const_cast<__minvoke<_Cvref, decltype(__captures3)>&&>(__captures3)...); 331 }; 332 } 333 334 template <class _Tag, class _Data, class... _Child> 335 using __captures_t = 336 decltype(__detail::__captures(_Tag(), __declval<_Data>(), __declval<_Child>()...)); 337 338 template <class, class, class... _Child> 339 using __tuple_size_t = char[sizeof...(_Child) + 2]; // NOLINT(modernize-avoid-c-arrays) 340 341 template <std::size_t _Idx, class _Descriptor> 342 concept __in_range = (_Idx < sizeof(__minvoke<_Descriptor, __q<__tuple_size_t>>)); 343 344 } // namespace __detail 345 346 using __sexpr_defaults = __detail::__defaults; 347 348 template <class _ReceiverId, class _Sexpr, std::size_t _Idx> 349 struct __rcvr { 350 using _Receiver = stdexec::__t<_ReceiverId>; 351 352 struct __t { 353 using receiver_concept = receiver_t; 354 using __id = __rcvr; 355 356 using __index = __msize_t<_Idx>; 357 using __parent_op_t = __op_state<_Sexpr, _Receiver>; 358 using __tag_t = tag_of_t<_Sexpr>; 359 360 // A pointer to the parent operation state, which contains the one created with 361 // this receiver. 362 __parent_op_t* __op_; 363 364 template <class... _Args> STDEXEC_ATTRIBUTEstdexec::__rcvr::__t365 STDEXEC_ATTRIBUTE(always_inline) 366 void set_value(_Args&&... __args) noexcept { 367 __op_->__complete(__index(), stdexec::set_value, static_cast<_Args&&>(__args)...); 368 } 369 370 template <class _Error> STDEXEC_ATTRIBUTEstdexec::__rcvr::__t371 STDEXEC_ATTRIBUTE(always_inline) 372 void set_error(_Error&& __err) noexcept { 373 __op_->__complete(__index(), stdexec::set_error, static_cast<_Error&&>(__err)); 374 } 375 STDEXEC_ATTRIBUTEstdexec::__rcvr::__t376 STDEXEC_ATTRIBUTE(always_inline) void set_stopped() noexcept { 377 __op_->__complete(__index(), stdexec::set_stopped); 378 } 379 380 template <__same_as<__t> _Self = __t> STDEXEC_ATTRIBUTEstdexec::__rcvr::__t381 STDEXEC_ATTRIBUTE(always_inline) 382 auto get_env() const noexcept 383 -> __detail::__env_type_t<_Self, __tag_t, __index, _Sexpr, _Receiver> { 384 return __op_->__get_env(__index()); 385 } 386 }; 387 }; 388 389 template <class _Sexpr, class _Receiver> 390 struct __op_state : __detail::__op_base<_Sexpr, _Receiver> { 391 using __desc_t = __decay_t<_Sexpr>::__desc_t; 392 using __tag_t = __desc_t::__tag; 393 using __data_t = __desc_t::__data; 394 using __state_t = __op_state::__op_base::__state_t; 395 using __inner_ops_t = 396 __result_of<__sexpr_apply, _Sexpr, __detail::__connect_fn<_Sexpr, _Receiver>>; 397 398 __inner_ops_t __inner_ops_; 399 __op_statestdexec::__op_state400 __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) noexcept( 401 __nothrow_constructible_from<__detail::__op_base<_Sexpr, _Receiver>, _Sexpr, _Receiver> 402 && __noexcept_of<__sexpr_apply, _Sexpr, __detail::__connect_fn<_Sexpr, _Receiver>>) 403 : __op_state::__op_base{static_cast<_Sexpr&&>(__sexpr), static_cast<_Receiver&&>(__rcvr)} 404 , __inner_ops_(__sexpr_apply( 405 static_cast<_Sexpr&&>(__sexpr), 406 __detail::__connect_fn<_Sexpr, _Receiver>{this})) { 407 } 408 STDEXEC_ATTRIBUTEstdexec::__op_state409 STDEXEC_ATTRIBUTE(always_inline) void start() & noexcept { 410 using __tag_t = __op_state::__tag_t; 411 auto&& __rcvr = this->__rcvr(); 412 __inner_ops_.apply( 413 [&](auto&... __ops) noexcept { 414 __sexpr_impl<__tag_t>::start(this->__state(), __rcvr, __ops...); 415 }, 416 __inner_ops_); 417 } 418 419 template <class _Index, class _Tag2, class... _Args> STDEXEC_ATTRIBUTEstdexec::__op_state420 STDEXEC_ATTRIBUTE(always_inline) 421 void __complete(_Index, _Tag2, _Args&&... __args) noexcept { 422 using __tag_t = __op_state::__tag_t; 423 auto&& __rcvr = this->__rcvr(); 424 using _CompleteFn = __mtypeof<__sexpr_impl<__tag_t>::complete>; 425 if constexpr (__callable<_CompleteFn, _Index, __op_state&, _Tag2, _Args...>) { 426 __sexpr_impl<__tag_t>::complete(_Index(), *this, _Tag2(), static_cast<_Args&&>(__args)...); 427 } else { 428 __sexpr_impl<__tag_t>::complete( 429 _Index(), this->__state(), __rcvr, _Tag2(), static_cast<_Args&&>(__args)...); 430 } 431 } 432 433 template <class _Index> STDEXEC_ATTRIBUTEstdexec::__op_state434 STDEXEC_ATTRIBUTE(always_inline) 435 auto __get_env(_Index) const noexcept 436 -> __detail::__env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver> { 437 const auto& __rcvr = this->__rcvr(); 438 return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state(), __rcvr); 439 } 440 }; 441 442 template <class _Tag> 443 struct __sexpr_impl : __detail::__defaults { 444 using not_specialized = void; 445 }; 446 447 using __detail::__enable_receiver_from_this; 448 449 template <class _Tag> 450 using __get_attrs_fn = 451 __result_of<__detail::__drop_front, __mtypeof<__sexpr_impl<_Tag>::get_attrs>>; 452 453 //! A dummy type used only for diagnostic purposes. 454 //! See `__sexpr` for the implementation of P2300's _`basic-sender`_. 455 template <class...> 456 struct __basic_sender { 457 // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`. 458 using __id = __basic_sender; 459 using __t = __basic_sender; 460 }; 461 462 namespace { 463 //! A struct template to aid in creating senders. 464 //! This struct closely resembles P2300's [_`basic-sender`_](https://eel.is/c++draft/exec#snd.expos-24), 465 //! but is not an exact implementation. 466 //! Note: The struct named `__basic_sender` is just a dummy type and is also not _`basic-sender`_. 467 template <auto _DescriptorFn> 468 struct __sexpr { 469 using sender_concept = sender_t; 470 471 // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`. 472 using __id = __sexpr; 473 using __t = __sexpr; 474 using __desc_t = decltype(_DescriptorFn()); 475 using __tag_t = __desc_t::__tag; 476 using __captures_t = __minvoke<__desc_t, __q<__detail::__captures_t>>; 477 478 mutable __captures_t __impl_; 479 480 template <class _Tag, class _Data, class... _Child> STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr481 STDEXEC_ATTRIBUTE(host, device, always_inline) 482 explicit __sexpr(_Tag, _Data&& __data, _Child&&... __child) 483 : __impl_( 484 __detail::__captures( 485 _Tag(), 486 static_cast<_Data&&>(__data), 487 static_cast<_Child&&>(__child)...)) { 488 } 489 490 template <class _Self> 491 using __impl = __sexpr_impl<__meval<__msecond, _Self, __tag_t>>; 492 493 template <class _Self = __sexpr> STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr494 STDEXEC_ATTRIBUTE(always_inline) 495 auto get_env() const noexcept 496 -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>> { 497 return __sexpr_apply(*this, __detail::__drop_front(__impl<_Self>::get_attrs)); 498 } 499 500 template <__decays_to<__sexpr> _Self, class... _Env> STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr501 STDEXEC_ATTRIBUTE(always_inline) 502 static auto get_completion_signatures(_Self&&, _Env&&...) noexcept -> __msecond< 503 __if_c<__decays_to<_Self, __sexpr>>, 504 __result_of<__impl<_Self>::get_completion_signatures, _Self, _Env...> 505 > { 506 return {}; 507 } 508 509 // BUGBUG fix receiver constraint here: 510 template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver> STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr511 STDEXEC_ATTRIBUTE(always_inline) 512 static auto connect(_Self&& __self, _Receiver&& __rcvr) 513 noexcept(__noexcept_of<__impl<_Self>::connect, _Self, _Receiver>) -> __msecond< 514 __if_c<__decays_to<_Self, __sexpr>>, 515 __result_of<__impl<_Self>::connect, _Self, _Receiver> 516 > { 517 return __impl<_Self>::connect( 518 static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); 519 } 520 521 template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver> STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr522 STDEXEC_ATTRIBUTE(always_inline) 523 static auto submit(_Self&& __self, _Receiver&& __rcvr) 524 noexcept(__noexcept_of<__impl<_Self>::submit, _Self, _Receiver>) -> __msecond< 525 __if_c<__decays_to<_Self, __sexpr>>, 526 __result_of<__impl<_Self>::submit, _Self, _Receiver> 527 > { 528 return __impl<_Self>::submit( 529 static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); 530 } 531 532 template <class _Sender, class _ApplyFn> STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr533 STDEXEC_ATTRIBUTE(always_inline) 534 static auto apply(_Sender&& __sndr, _ApplyFn&& __fun) noexcept( 535 __nothrow_callable<__detail::__impl_of<_Sender>, __copy_cvref_fn<_Sender>, _ApplyFn>) 536 -> __call_result_t<__detail::__impl_of<_Sender>, __copy_cvref_fn<_Sender>, _ApplyFn> { 537 return static_cast<_Sender&&>(__sndr) 538 .__impl_(__copy_cvref_fn<_Sender>(), static_cast<_ApplyFn&&>(__fun)); 539 } 540 541 template <std::size_t _Idx, __decays_to_derived_from<__sexpr> _Self> STDEXEC_ATTRIBUTE(always_inline)542 STDEXEC_ATTRIBUTE(always_inline) 543 friend auto get(_Self&& __self) noexcept -> decltype(auto) 544 requires __detail::__in_range<_Idx, __desc_t> 545 { 546 if constexpr (_Idx == 0) { 547 return __tag_t(); 548 } else { 549 return __self.__impl_(__copy_cvref_fn<_Self>(), __nth_pack_element<_Idx>); 550 } 551 } 552 }; 553 554 template <class _Tag, class _Data, class... _Child> 555 STDEXEC_ATTRIBUTE(host, device) 556 __sexpr(_Tag, _Data, _Child...) -> __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>; 557 } // namespace 558 559 template <class _Tag, class _Data, class... _Child> 560 using __sexpr_t = __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>; 561 562 ////////////////////////////////////////////////////////////////////////////////////////////////// 563 // __make_sexpr 564 //! A tagged function-object 565 //! Takes data and children and 566 //! returns `__sexpr_t<_Tag, _Data, _Child...>{_Tag(), data, children...}`. 567 namespace __detail { 568 template <class _Tag> 569 struct __make_sexpr_t { 570 template <class _Data = __, class... _Child> operator ()stdexec::__detail::__make_sexpr_t571 constexpr auto operator()(_Data __data = {}, _Child... __child) const { 572 return __sexpr_t<_Tag, _Data, _Child...>{ 573 _Tag(), static_cast<_Data&&>(__data), static_cast<_Child&&>(__child)...}; 574 } 575 }; 576 } // namespace __detail 577 578 template <class _Tag> 579 inline constexpr __detail::__make_sexpr_t<_Tag> __make_sexpr{}; 580 581 // The __name_of utility defined below is used to pretty-print the type names of 582 // senders in compiler diagnostics. 583 namespace __detail { 584 struct __basic_sender_name { 585 template <class _Tag, class _Data, class... _Child> 586 using __result = __basic_sender<_Tag, _Data, __name_of<_Child>...>; 587 588 template <class _Sender> 589 using __f = __minvoke<typename __decay_t<_Sender>::__desc_t, __q<__result>>; 590 }; 591 592 struct __id_name { 593 template <class _Sender> 594 using __f = __name_of<__id<_Sender>>; 595 }; 596 597 template <class _Sender> 598 extern __mcompose<__cplr, __name_of_fn<_Sender>> __name_of_v<_Sender&>; 599 600 template <class _Sender> 601 extern __mcompose<__cprr, __name_of_fn<_Sender>> __name_of_v<_Sender&&>; 602 603 template <class _Sender> 604 extern __mcompose<__cpclr, __name_of_fn<_Sender>> __name_of_v<const _Sender&>; 605 606 template <auto _Descriptor> 607 extern __basic_sender_name __name_of_v<__sexpr<_Descriptor>>; 608 609 template <__has_id _Sender> 610 requires(!same_as<__id<_Sender>, _Sender>) 611 extern __id_name __name_of_v<_Sender>; 612 } // namespace __detail 613 } // namespace stdexec 614 615 namespace std { 616 template <auto _Descriptor> 617 struct tuple_size<stdexec::__sexpr<_Descriptor>> 618 : integral_constant< 619 size_t, 620 stdexec::__v<stdexec::__minvoke<stdexec::__result_of<_Descriptor>, stdexec::__msize>> 621 > { }; 622 623 template <size_t _Idx, auto _Descriptor> 624 struct tuple_element<_Idx, stdexec::__sexpr<_Descriptor>> { 625 using type = stdexec::__remove_rvalue_reference_t<stdexec::__call_result_t< 626 stdexec::__detail::__impl_of<stdexec::__sexpr<_Descriptor>>, 627 stdexec::__cp, 628 stdexec::__nth_pack_element_t<_Idx> 629 >>; 630 }; 631 } // namespace std 632