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 "__concepts.hpp" 19 #include "__cpo.hpp" 20 #include "__execution_fwd.hpp" 21 #include "__meta.hpp" 22 #include "__stop_token.hpp" 23 #include "__tag_invoke.hpp" 24 #include "__tuple.hpp" 25 26 #include <exception> 27 #include <functional> 28 #include <type_traits> 29 30 STDEXEC_PRAGMA_PUSH() 31 STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend) 32 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference) 33 34 namespace stdexec 35 { 36 // [exec.queries.queryable] 37 template <class T> 38 concept queryable = destructible<T>; 39 40 template <class Tag> 41 struct __query 42 { 43 template <class Sig> 44 static inline constexpr Tag (*signature)(Sig) = nullptr; 45 }; 46 47 ////////////////////////////////////////////////////////////////////////////////////////////////// 48 // [exec.queries] 49 namespace __queries 50 { 51 template <class _Tp> 52 concept __is_bool_constant = // 53 requires { // 54 typename __mbool<_Tp::value>; 55 }; 56 57 struct forwarding_query_t 58 { 59 template <class _Query> 60 consteval auto operator()(_Query __query) const noexcept -> bool 61 { 62 if constexpr (tag_invocable<forwarding_query_t, _Query>) 63 { 64 using __result_t = tag_invoke_result_t<forwarding_query_t, _Query>; 65 // If this a integral type wrapper, unpack it and return the value. 66 // Otherwise, return the result of the tag_invoke call expression. 67 if constexpr (__is_bool_constant<__result_t>) 68 { 69 return __result_t::value; 70 } 71 else 72 { 73 return tag_invoke(*this, static_cast<_Query&&>(__query)); 74 } 75 } 76 else if constexpr (derived_from<_Query, forwarding_query_t>) 77 { 78 return true; 79 } 80 else 81 { 82 return false; 83 } 84 } 85 }; 86 87 struct query_or_t 88 { 89 template <class _Query, class _Queryable, class _Default> 90 constexpr auto operator()(_Query, _Queryable&&, _Default&& __default) const 91 noexcept(__nothrow_constructible_from<_Default, _Default&&>) -> _Default 92 { 93 return static_cast<_Default&&>(__default); 94 } 95 96 template <class _Query, class _Queryable, class _Default> 97 requires __callable<_Query, _Queryable> 98 constexpr auto operator()(_Query __query, _Queryable&& __queryable, 99 _Default&&) const 100 noexcept(__nothrow_callable<_Query, _Queryable>) 101 -> __call_result_t<_Query, _Queryable> 102 { 103 return static_cast<_Query&&>(__query)( 104 static_cast<_Queryable&&>(__queryable)); 105 } 106 }; 107 108 struct execute_may_block_caller_t : __query<execute_may_block_caller_t> 109 { 110 template <class _Tp> 111 requires tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>> 112 constexpr auto operator()(_Tp&& __t) const noexcept -> bool 113 { 114 static_assert( 115 same_as<bool, tag_invoke_result_t<execute_may_block_caller_t, 116 __cref_t<_Tp>>>); 117 static_assert( 118 nothrow_tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>); 119 return tag_invoke(execute_may_block_caller_t{}, std::as_const(__t)); 120 } 121 122 constexpr auto operator()(auto&&) const noexcept -> bool 123 { 124 return true; 125 } 126 }; 127 128 struct get_forward_progress_guarantee_t : 129 __query<get_forward_progress_guarantee_t> 130 { 131 template <class _Tp> 132 requires tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>> 133 constexpr auto operator()(_Tp&& __t) const noexcept( 134 nothrow_tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>) 135 -> __decay_t<tag_invoke_result_t<get_forward_progress_guarantee_t, 136 __cref_t<_Tp>>> 137 { 138 return tag_invoke(get_forward_progress_guarantee_t{}, 139 std::as_const(__t)); 140 } 141 142 constexpr auto 143 operator()(auto&&) const noexcept -> stdexec::forward_progress_guarantee 144 { 145 return stdexec::forward_progress_guarantee::weakly_parallel; 146 } 147 }; 148 149 struct __has_algorithm_customizations_t : 150 __query<__has_algorithm_customizations_t> 151 { 152 template <class _Tp> 153 using __result_t = 154 tag_invoke_result_t<__has_algorithm_customizations_t, __cref_t<_Tp>>; 155 156 template <class _Tp> 157 requires tag_invocable<__has_algorithm_customizations_t, __cref_t<_Tp>> 158 constexpr auto operator()(_Tp&&) const 159 noexcept(noexcept(__result_t<_Tp>{})) -> __result_t<_Tp> 160 { 161 using _Boolean = tag_invoke_result_t<__has_algorithm_customizations_t, 162 __cref_t<_Tp>>; 163 static_assert(_Boolean{} 164 ? true 165 : false); // must be contextually convertible to bool 166 return _Boolean{}; 167 } 168 169 constexpr auto operator()(auto&&) const noexcept -> std::false_type 170 { 171 return {}; 172 } 173 }; 174 175 // TODO: implement allocator concept 176 template <class _T0> 177 concept __allocator_c = true; 178 179 struct get_scheduler_t : __query<get_scheduler_t> 180 { 181 static constexpr auto query(forwarding_query_t) noexcept -> bool 182 { 183 return true; 184 } 185 186 template <class _Env> 187 requires tag_invocable<get_scheduler_t, const _Env&> 188 auto operator()(const _Env& __env) const noexcept 189 -> tag_invoke_result_t<get_scheduler_t, const _Env&>; 190 191 template <class _Tag = get_scheduler_t> 192 auto operator()() const noexcept; 193 }; 194 195 struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t> 196 { 197 static constexpr auto query(forwarding_query_t) noexcept -> bool 198 { 199 return true; 200 } 201 202 template <class _Env> 203 requires tag_invocable<get_delegatee_scheduler_t, const _Env&> 204 auto operator()(const _Env& __t) const noexcept 205 -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>; 206 207 template <class _Tag = get_delegatee_scheduler_t> 208 auto operator()() const noexcept; 209 }; 210 211 struct get_allocator_t : __query<get_allocator_t> 212 { 213 static constexpr auto query(forwarding_query_t) noexcept -> bool 214 { 215 return true; 216 } 217 218 template <class _Env> 219 requires tag_invocable<get_allocator_t, const _Env&> 220 auto operator()(const _Env& __env) const noexcept 221 -> tag_invoke_result_t<get_allocator_t, const _Env&> 222 { 223 static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>); 224 static_assert( 225 __allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>); 226 return tag_invoke(get_allocator_t{}, __env); 227 } 228 229 template <class _Tag = get_allocator_t> 230 auto operator()() const noexcept; 231 }; 232 233 struct get_stop_token_t : __query<get_stop_token_t> 234 { 235 static constexpr auto query(forwarding_query_t) noexcept -> bool 236 { 237 return true; 238 } 239 240 template <class _Env, class _Token = never_stop_token> 241 auto operator()(const _Env&) const noexcept -> _Token 242 { 243 return {}; 244 } 245 246 template <class _Env, class = void> 247 requires tag_invocable<get_stop_token_t, const _Env&> 248 auto operator()(const _Env& __env) const noexcept 249 -> tag_invoke_result_t<get_stop_token_t, const _Env&> 250 { 251 static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>); 252 static_assert( 253 stoppable_token< 254 __decay_t<tag_invoke_result_t<get_stop_token_t, const _Env&>>>); 255 return tag_invoke(get_stop_token_t{}, __env); 256 } 257 258 template <class _Tag = get_stop_token_t> 259 auto operator()() const noexcept; 260 }; 261 262 template <class _Queryable, class _Tag> 263 concept __has_completion_scheduler_for = 264 queryable<_Queryable> && // 265 tag_invocable<get_completion_scheduler_t<_Tag>, const _Queryable&>; 266 267 template <__completion_tag _Tag> 268 struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_Tag>> 269 { 270 static constexpr auto query(forwarding_query_t) noexcept -> bool 271 { 272 return true; 273 } 274 275 template <__has_completion_scheduler_for<_Tag> _Queryable> 276 auto operator()(const _Queryable& __queryable) const noexcept 277 -> tag_invoke_result_t<get_completion_scheduler_t<_Tag>, 278 const _Queryable&>; 279 }; 280 281 struct get_domain_t 282 { 283 template <class _Ty> 284 requires tag_invocable<get_domain_t, const _Ty&> 285 constexpr auto operator()(const _Ty& __ty) const noexcept 286 -> __decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>> 287 { 288 static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>, 289 "Customizations of get_domain must be noexcept."); 290 static_assert( 291 __class<__decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>>>, 292 "Customizations of get_domain must return a class type."); 293 return {}; 294 } 295 296 static constexpr auto query(forwarding_query_t) noexcept -> bool 297 { 298 return true; 299 } 300 }; 301 302 struct __is_scheduler_affine_t 303 { 304 template <class _Env> 305 constexpr auto operator()(const _Env&) const noexcept 306 { 307 if constexpr (tag_invocable<__is_scheduler_affine_t, const _Env&>) 308 { 309 using _Result = 310 tag_invoke_result_t<__is_scheduler_affine_t, const _Env&>; 311 static_assert(__same_as<decltype(__v<_Result>), const bool>); 312 return _Result(); 313 } 314 else 315 { 316 return std::false_type(); 317 } 318 } 319 320 static constexpr auto query(forwarding_query_t) noexcept -> bool 321 { 322 return false; 323 } 324 }; 325 326 struct __root_t 327 { 328 template <class _Env> 329 requires tag_invocable<__root_t, const _Env&> 330 constexpr auto operator()(const _Env& __env) const noexcept -> bool 331 { 332 STDEXEC_ASSERT(tag_invoke(__root_t{}, __env) == true); 333 return true; 334 } 335 336 static constexpr auto query(forwarding_query_t) noexcept -> bool 337 { 338 return false; 339 } 340 }; 341 342 struct __root_env 343 { 344 using __t = __root_env; 345 using __id = __root_env; 346 347 constexpr STDEXEC_MEMFN_DECL(auto __root)(this const __root_env&) noexcept 348 -> bool 349 { 350 return true; 351 } 352 }; 353 } // namespace __queries 354 355 using __queries::__has_algorithm_customizations_t; 356 using __queries::__is_scheduler_affine_t; 357 using __queries::__root_env; 358 using __queries::__root_t; 359 using __queries::execute_may_block_caller_t; 360 using __queries::forwarding_query_t; 361 using __queries::get_allocator_t; 362 using __queries::get_completion_scheduler_t; 363 using __queries::get_delegatee_scheduler_t; 364 using __queries::get_domain_t; 365 using __queries::get_forward_progress_guarantee_t; 366 using __queries::get_scheduler_t; 367 using __queries::get_stop_token_t; 368 using __queries::query_or_t; 369 370 inline constexpr forwarding_query_t forwarding_query{}; 371 inline constexpr query_or_t query_or{}; // NOT TO SPEC 372 inline constexpr execute_may_block_caller_t execute_may_block_caller{}; 373 inline constexpr __has_algorithm_customizations_t 374 __has_algorithm_customizations{}; 375 inline constexpr get_forward_progress_guarantee_t 376 get_forward_progress_guarantee{}; 377 inline constexpr get_scheduler_t get_scheduler{}; 378 inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{}; 379 inline constexpr get_allocator_t get_allocator{}; 380 inline constexpr get_stop_token_t get_stop_token{}; 381 #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__) 382 template <__completion_tag _Tag> 383 inline constexpr get_completion_scheduler_t<_Tag> get_completion_scheduler{}; 384 #else 385 template <> 386 inline constexpr get_completion_scheduler_t<set_value_t> 387 get_completion_scheduler<set_value_t>{}; 388 template <> 389 inline constexpr get_completion_scheduler_t<set_error_t> 390 get_completion_scheduler<set_error_t>{}; 391 template <> 392 inline constexpr get_completion_scheduler_t<set_stopped_t> 393 get_completion_scheduler<set_stopped_t>{}; 394 #endif 395 396 template <class _Tag> 397 concept __forwarding_query = forwarding_query(_Tag{}); 398 399 inline constexpr get_domain_t get_domain{}; 400 401 template <class _Env> 402 using __domain_of_t = __decay_t<__call_result_t<get_domain_t, _Env>>; 403 404 template <class _Tag, class _Queryable, class _Default> 405 using __query_result_or_t = 406 __call_result_t<query_or_t, _Tag, _Queryable, _Default>; 407 408 namespace __env 409 { 410 // To be kept in sync with the promise type used in __connect_awaitable 411 template <class _Env> 412 struct __promise 413 { 414 template <class _Ty> 415 auto await_transform(_Ty&& __value) noexcept -> _Ty&& 416 { 417 return static_cast<_Ty&&>(__value); 418 } 419 420 template <class _Ty> 421 requires tag_invocable<as_awaitable_t, _Ty, __promise&> 422 auto await_transform(_Ty&& __value) // 423 noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __promise&>) 424 -> tag_invoke_result_t<as_awaitable_t, _Ty, __promise&> 425 { 426 return tag_invoke(as_awaitable, static_cast<_Ty&&>(__value), *this); 427 } 428 429 auto get_env() const noexcept -> const _Env&; 430 }; 431 432 template <class _Env, class _Query, class... _Args> 433 concept __queryable = // 434 tag_invocable<_Query, const _Env&, _Args...>; 435 436 template <class _Env, class _Query, class... _Args> 437 concept __nothrow_queryable = // 438 nothrow_tag_invocable<_Query, const _Env&, _Args...>; 439 440 template <class _Env, class _Query, class... _Args> 441 using __query_result_t = // 442 tag_invoke_result_t<_Query, const _Env&, _Args...>; 443 444 // A singleton environment from a query/value pair 445 template <class _Query, class _Value> 446 struct prop 447 { 448 using __t = prop; 449 using __id = prop; 450 451 STDEXEC_ATTRIBUTE((no_unique_address)) 452 _Query __query; 453 454 STDEXEC_ATTRIBUTE((no_unique_address)) 455 _Value __value; 456 457 STDEXEC_ATTRIBUTE((nodiscard)) 458 constexpr const _Value& query(_Query) const noexcept 459 { 460 return __value; 461 } 462 463 prop& operator=(const prop&) = delete; 464 }; 465 466 template <class _Query, class _Value> 467 prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>; 468 469 // utility for joining multiple environments 470 template <class... _Envs> 471 struct env 472 { 473 using __t = env; 474 using __id = env; 475 476 __tuple_for<_Envs...> __tup_; 477 478 // return a reference to the first child env for which 479 // __queryable<_Envs, _Query, _Args...> is true. 480 template <class _Query, class... _Args> 481 STDEXEC_ATTRIBUTE((always_inline)) 482 constexpr decltype(auto) __get_1st() const noexcept 483 { 484 constexpr bool __flags[] = {__queryable<_Envs, _Query, _Args...>...}; 485 constexpr std::size_t __idx = 486 __pos_of(__flags, __flags + sizeof...(_Envs)); 487 return __tup::get<__idx>(__tup_); 488 } 489 490 template <class _Query, class... _Args> 491 requires(__queryable<_Envs, _Query, _Args...> || ...) 492 STDEXEC_ATTRIBUTE((always_inline)) 493 constexpr decltype(auto) query(_Query __q, _Args&&... __args) const 494 noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()), 495 _Query, _Args...>) 496 { 497 return tag_invoke(__q, __get_1st<_Query, _Args...>(), 498 static_cast<_Args&&>(__args)...); 499 } 500 501 env& operator=(const env&) = delete; 502 }; 503 504 // specialization for two envs to avoid warnings about elided braces 505 template <class _Env0, class _Env1> 506 struct env<_Env0, _Env1> 507 { 508 using __t = env; 509 using __id = env; 510 511 STDEXEC_ATTRIBUTE((no_unique_address)) 512 _Env0 __env0_; 513 STDEXEC_ATTRIBUTE((no_unique_address)) 514 _Env1 __env1_; 515 516 // return a reference to the first child env for which 517 // __queryable<_Envs, _Query, _Args...> is true. 518 template <class _Query, class... _Args> 519 STDEXEC_ATTRIBUTE((always_inline)) 520 constexpr decltype(auto) __get_1st() const noexcept 521 { 522 if constexpr (__queryable<_Env0, _Query, _Args...>) 523 { 524 return (__env0_); 525 } 526 else 527 { 528 return (__env1_); 529 } 530 } 531 532 template <class _Query, class... _Args> 533 requires __queryable<_Env0, _Query, _Args...> || 534 __queryable<_Env1, _Query, _Args...> 535 STDEXEC_ATTRIBUTE((always_inline)) 536 constexpr decltype(auto) query(_Query __q, _Args&&... __args) const 537 noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()), 538 _Query, _Args...>) 539 { 540 return tag_invoke(__q, __get_1st<_Query, _Args...>(), 541 static_cast<_Args&&>(__args)...); 542 } 543 544 env& operator=(const env&) = delete; 545 }; 546 547 template <class... _Envs> 548 env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>; 549 550 template <class _Value, class _Tag, class... _Tags> 551 struct __with 552 { 553 using __t = __with; 554 using __id = __with; 555 STDEXEC_ATTRIBUTE((no_unique_address)) 556 _Value __value_; 557 558 __with() = default; 559 560 constexpr explicit __with(_Value __value) noexcept( 561 __nothrow_decay_copyable<_Value>) : 562 __value_(static_cast<_Value&&>(__value)) 563 {} 564 565 constexpr explicit __with(_Value __value, _Tag, _Tags...) noexcept( 566 __nothrow_decay_copyable<_Value>) : 567 __value_(static_cast<_Value&&>(__value)) 568 {} 569 570 template <__one_of<_Tag, _Tags...> _Key> 571 auto query(_Key) const noexcept -> const _Value& 572 { 573 return __value_; 574 } 575 576 __with& operator=(const __with&) = delete; 577 }; 578 579 template <class _Value, class _Tag, class... _Tags> 580 __with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>; 581 582 template <class _EnvId> 583 struct __fwd 584 { 585 using _Env = __cvref_t<_EnvId>; 586 static_assert(__nothrow_move_constructible<_Env>); 587 588 struct __t 589 { 590 using __id = __fwd; 591 STDEXEC_ATTRIBUTE((no_unique_address)) 592 _Env __env_; 593 594 #if STDEXEC_GCC() && __GNUC__ < 12 595 using __cvref_env_t = std::add_const_t<_Env>&; 596 #else 597 using __cvref_env_t = const _Env&; 598 #endif 599 600 template <__forwarding_query _Tag> 601 requires tag_invocable<_Tag, __cvref_env_t> 602 auto query(_Tag) const 603 noexcept(nothrow_tag_invocable<_Tag, __cvref_env_t>) 604 -> tag_invoke_result_t<_Tag, __cvref_env_t> 605 { 606 return tag_invoke(_Tag(), __env_); 607 } 608 609 __t& operator=(const __t&) = delete; 610 }; 611 }; 612 613 struct __fwd_fn 614 { 615 template <class _Env> 616 auto operator()(_Env&& __env) const 617 { 618 return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)}; 619 } 620 621 auto operator()(empty_env) const -> empty_env 622 { 623 return {}; 624 } 625 }; 626 627 template <class _EnvId, class _Tag> 628 struct __without_ 629 { 630 using _Env = __cvref_t<_EnvId>; 631 static_assert(__nothrow_move_constructible<_Env>); 632 633 struct __t 634 { 635 using __id = __without_; 636 _Env __env_; 637 638 #if STDEXEC_GCC() && __GNUC__ < 12 639 using __cvref_env_t = std::add_const_t<_Env>&; 640 #else 641 using __cvref_env_t = const _Env&; 642 #endif 643 644 auto query(_Tag) const noexcept = delete; 645 646 template <tag_invocable<__cvref_env_t> _Key> 647 STDEXEC_ATTRIBUTE((always_inline)) 648 auto query(_Key) const noexcept( 649 nothrow_tag_invocable<_Key, __cvref_env_t>) -> decltype(auto) 650 { 651 return tag_invoke(_Key(), __env_); 652 } 653 654 __t& operator=(const __t&) = delete; 655 }; 656 }; 657 658 struct __without_fn 659 { 660 template <class _Env, class _Tag> 661 constexpr auto operator()(_Env&& __env, 662 _Tag) const noexcept -> decltype(auto) 663 { 664 if constexpr (tag_invocable<_Tag, _Env>) 665 { 666 using _Without = __t<__without_<__cvref_id<_Env>, _Tag>>; 667 return _Without{static_cast<_Env&&>(__env)}; 668 } 669 else 670 { 671 return static_cast<_Env>(static_cast<_Env&&>(__env)); 672 } 673 } 674 }; 675 676 inline constexpr __without_fn __without{}; 677 678 template <class _Env, class _Tag, class... _Tags> 679 using __without_t = __result_of<__without, _Env, _Tag, _Tags...>; 680 681 template <__nothrow_move_constructible _Fun> 682 struct __from 683 { 684 using __t = __from; 685 using __id = __from; 686 STDEXEC_ATTRIBUTE((no_unique_address)) 687 _Fun __fun_; 688 689 template <class _Tag> 690 requires __callable<const _Fun&, _Tag> 691 auto query(_Tag) const noexcept(__nothrow_callable<const _Fun&, _Tag>) 692 -> __call_result_t<const _Fun&, _Tag> 693 { 694 return __fun_(_Tag()); 695 } 696 697 __from& operator=(const __from&) = delete; 698 }; 699 700 template <class _Fun> 701 __from(_Fun) -> __from<_Fun>; 702 703 struct __join_fn 704 { 705 auto operator()(empty_env, empty_env) const noexcept -> empty_env 706 { 707 return {}; 708 } 709 710 template <class _Env> 711 auto operator()(_Env&& __env, empty_env = {}) const noexcept -> _Env 712 { 713 return static_cast<_Env&&>(__env); 714 } 715 716 template <class _Env> 717 auto operator()(empty_env, _Env&& __env) const noexcept -> decltype(auto) 718 { 719 return __fwd_fn()(static_cast<_Env&&>(__env)); 720 } 721 722 template <class _First, class _Second> 723 auto operator()(_First&& __first, _Second&& __second) const noexcept 724 -> env<_First, __call_result_t<__fwd_fn, _Second>> 725 { 726 return {static_cast<_First&&>(__first), 727 __fwd_fn()(static_cast<_Second&&>(__second))}; 728 } 729 }; 730 731 inline constexpr __join_fn __join{}; 732 733 template <class _First, class... _Second> 734 using __join_t = __result_of<__join, _First, _Second...>; 735 736 struct __as_root_env_fn 737 { 738 template <class _Env> 739 constexpr auto operator()(_Env __env) const noexcept 740 -> __join_t<__root_env, std::unwrap_reference_t<_Env>> 741 { 742 return __join(__root_env{}, 743 static_cast<std::unwrap_reference_t<_Env>&&>(__env)); 744 } 745 }; 746 747 inline constexpr __as_root_env_fn __as_root_env{}; 748 749 template <class _Env> 750 using __as_root_env_t = __result_of<__as_root_env, _Env>; 751 } // namespace __env 752 753 using __env::env; 754 using __env::prop; 755 using empty_env = env<>; 756 757 ///////////////////////////////////////////////////////////////////////////// 758 namespace __get_env 759 { 760 // For getting an execution environment from a receiver or the attributes from a 761 // sender. 762 struct get_env_t 763 { 764 template <__same_as<get_env_t> _Self, class _EnvProvider> 765 STDEXEC_ATTRIBUTE((always_inline)) 766 friend auto tag_invoke(_Self, const _EnvProvider& __env_provider) noexcept 767 -> decltype(__env_provider.get_env()) 768 { 769 static_assert(noexcept(__env_provider.get_env()), 770 "get_env() members must be noexcept"); 771 return __env_provider.get_env(); 772 } 773 774 template <class _EnvProvider> 775 requires tag_invocable<get_env_t, const _EnvProvider&> 776 STDEXEC_ATTRIBUTE((always_inline)) 777 constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept 778 -> tag_invoke_result_t<get_env_t, const _EnvProvider&> 779 { 780 static_assert( 781 queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>); 782 static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>); 783 return tag_invoke(*this, __env_provider); 784 } 785 786 template <class _EnvProvider> 787 constexpr auto operator()(const _EnvProvider&) const noexcept -> empty_env 788 { 789 return {}; 790 } 791 }; 792 } // namespace __get_env 793 794 using __get_env::get_env_t; 795 inline constexpr get_env_t get_env{}; 796 797 template <class _EnvProvider> 798 concept environment_provider = // 799 requires(_EnvProvider& __ep) { 800 { get_env(std::as_const(__ep)) } -> queryable; 801 }; 802 803 using __env::__as_root_env; 804 using __env::__as_root_env_t; 805 806 template <class _Env> 807 concept __is_root_env = requires(_Env&& __env) { 808 { __root_t{}(__env) } -> same_as<bool>; 809 }; 810 811 template <class _Sender> 812 concept __is_scheduler_affine = // 813 requires { 814 requires __v< 815 __call_result_t<__is_scheduler_affine_t, env_of_t<_Sender>>>; 816 }; 817 } // namespace stdexec 818 819 STDEXEC_PRAGMA_POP() 820