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 //! The type for `get_delegation_scheduler` [exec.get.delegation.scheduler] 196 //! A query object that asks for a scheduler that can be used to delegate 197 //! work to for the purpose of forward progress delegation ([intro.progress]). 198 struct get_delegation_scheduler_t : __query<get_delegation_scheduler_t> 199 { 200 static constexpr auto query(forwarding_query_t) noexcept -> bool 201 { 202 return true; 203 } 204 205 template <class _Env> 206 requires tag_invocable<get_delegation_scheduler_t, const _Env&> 207 auto operator()(const _Env& __t) const noexcept 208 -> tag_invoke_result_t<get_delegation_scheduler_t, const _Env&>; 209 210 template <class _Tag = get_delegation_scheduler_t> 211 auto operator()() const noexcept; 212 }; 213 214 struct get_allocator_t : __query<get_allocator_t> 215 { 216 static constexpr auto query(forwarding_query_t) noexcept -> bool 217 { 218 return true; 219 } 220 221 template <class _Env> 222 requires tag_invocable<get_allocator_t, const _Env&> 223 auto operator()(const _Env& __env) const noexcept 224 -> tag_invoke_result_t<get_allocator_t, const _Env&> 225 { 226 static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>); 227 static_assert( 228 __allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>); 229 return tag_invoke(get_allocator_t{}, __env); 230 } 231 232 template <class _Tag = get_allocator_t> 233 auto operator()() const noexcept; 234 }; 235 236 struct get_stop_token_t : __query<get_stop_token_t> 237 { 238 static constexpr auto query(forwarding_query_t) noexcept -> bool 239 { 240 return true; 241 } 242 243 template <class _Env, class _Token = never_stop_token> 244 auto operator()(const _Env&) const noexcept -> _Token 245 { 246 return {}; 247 } 248 249 template <class _Env, class = void> 250 requires tag_invocable<get_stop_token_t, const _Env&> 251 auto operator()(const _Env& __env) const noexcept 252 -> tag_invoke_result_t<get_stop_token_t, const _Env&> 253 { 254 static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>); 255 static_assert( 256 stoppable_token< 257 __decay_t<tag_invoke_result_t<get_stop_token_t, const _Env&>>>); 258 return tag_invoke(get_stop_token_t{}, __env); 259 } 260 261 template <class _Tag = get_stop_token_t> 262 auto operator()() const noexcept; 263 }; 264 265 template <class _Queryable, class _Tag> 266 concept __has_completion_scheduler_for = 267 queryable<_Queryable> && // 268 tag_invocable<get_completion_scheduler_t<_Tag>, const _Queryable&>; 269 270 template <__completion_tag _Tag> 271 struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_Tag>> 272 { 273 static constexpr auto query(forwarding_query_t) noexcept -> bool 274 { 275 return true; 276 } 277 278 template <__has_completion_scheduler_for<_Tag> _Queryable> 279 auto operator()(const _Queryable& __queryable) const noexcept 280 -> tag_invoke_result_t<get_completion_scheduler_t<_Tag>, 281 const _Queryable&>; 282 }; 283 284 struct get_domain_t 285 { 286 template <class _Ty> 287 requires tag_invocable<get_domain_t, const _Ty&> 288 constexpr auto operator()(const _Ty& __ty) const noexcept 289 -> __decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>> 290 { 291 static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>, 292 "Customizations of get_domain must be noexcept."); 293 static_assert( 294 __class<__decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>>>, 295 "Customizations of get_domain must return a class type."); 296 return {}; 297 } 298 299 static constexpr auto query(forwarding_query_t) noexcept -> bool 300 { 301 return true; 302 } 303 }; 304 305 struct __is_scheduler_affine_t 306 { 307 template <class _Env> 308 constexpr auto operator()(const _Env&) const noexcept 309 { 310 if constexpr (tag_invocable<__is_scheduler_affine_t, const _Env&>) 311 { 312 using _Result = 313 tag_invoke_result_t<__is_scheduler_affine_t, const _Env&>; 314 static_assert(__same_as<decltype(__v<_Result>), const bool>); 315 return _Result(); 316 } 317 else 318 { 319 return std::false_type(); 320 } 321 } 322 323 static constexpr auto query(forwarding_query_t) noexcept -> bool 324 { 325 return false; 326 } 327 }; 328 329 struct __root_t 330 { 331 template <class _Env> 332 requires tag_invocable<__root_t, const _Env&> 333 constexpr auto operator()(const _Env& __env) const noexcept -> bool 334 { 335 STDEXEC_ASSERT(tag_invoke(__root_t{}, __env) == true); 336 return true; 337 } 338 339 static constexpr auto query(forwarding_query_t) noexcept -> bool 340 { 341 return false; 342 } 343 }; 344 345 struct __root_env 346 { 347 using __t = __root_env; 348 using __id = __root_env; 349 350 constexpr STDEXEC_MEMFN_DECL(auto __root)(this const __root_env&) noexcept 351 -> bool 352 { 353 return true; 354 } 355 }; 356 } // namespace __queries 357 358 using __queries::__has_algorithm_customizations_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_delegation_scheduler_t; 363 using __queries::get_forward_progress_guarantee_t; 364 using __queries::get_scheduler_t; 365 using __queries::query_or_t; 366 using get_delegatee_scheduler_t [[deprecated( 367 "get_delegatee_scheduler_t has been renamed get_delegation_scheduler_t")]] = 368 get_delegation_scheduler_t; 369 using __queries::__is_scheduler_affine_t; 370 using __queries::__root_env; 371 using __queries::__root_t; 372 using __queries::get_completion_scheduler_t; 373 using __queries::get_domain_t; 374 using __queries::get_stop_token_t; 375 376 inline constexpr forwarding_query_t forwarding_query{}; 377 inline constexpr query_or_t query_or{}; // NOT TO SPEC 378 inline constexpr execute_may_block_caller_t execute_may_block_caller{}; 379 inline constexpr __has_algorithm_customizations_t 380 __has_algorithm_customizations{}; 381 inline constexpr get_forward_progress_guarantee_t 382 get_forward_progress_guarantee{}; 383 inline constexpr get_scheduler_t get_scheduler{}; 384 inline constexpr get_delegation_scheduler_t get_delegation_scheduler{}; 385 inline constexpr auto& get_delegatee_scheduler [[deprecated( 386 "get_delegatee_scheduler has been renamed get_delegation_scheduler")]] = 387 get_delegation_scheduler; 388 inline constexpr get_allocator_t get_allocator{}; 389 inline constexpr get_stop_token_t get_stop_token{}; 390 #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__) 391 template <__completion_tag _Tag> 392 inline constexpr get_completion_scheduler_t<_Tag> get_completion_scheduler{}; 393 #else 394 template <> 395 inline constexpr get_completion_scheduler_t<set_value_t> 396 get_completion_scheduler<set_value_t>{}; 397 template <> 398 inline constexpr get_completion_scheduler_t<set_error_t> 399 get_completion_scheduler<set_error_t>{}; 400 template <> 401 inline constexpr get_completion_scheduler_t<set_stopped_t> 402 get_completion_scheduler<set_stopped_t>{}; 403 #endif 404 405 template <class _Tag> 406 concept __forwarding_query = forwarding_query(_Tag{}); 407 408 inline constexpr get_domain_t get_domain{}; 409 410 template <class _Env> 411 using __domain_of_t = __decay_t<__call_result_t<get_domain_t, _Env>>; 412 413 template <class _Tag, class _Queryable, class _Default> 414 using __query_result_or_t = 415 __call_result_t<query_or_t, _Tag, _Queryable, _Default>; 416 417 namespace __env 418 { 419 // To be kept in sync with the promise type used in __connect_awaitable 420 template <class _Env> 421 struct __promise 422 { 423 template <class _Ty> 424 auto await_transform(_Ty&& __value) noexcept -> _Ty&& 425 { 426 return static_cast<_Ty&&>(__value); 427 } 428 429 template <class _Ty> 430 requires tag_invocable<as_awaitable_t, _Ty, __promise&> 431 auto await_transform(_Ty&& __value) // 432 noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __promise&>) 433 -> tag_invoke_result_t<as_awaitable_t, _Ty, __promise&> 434 { 435 return tag_invoke(as_awaitable, static_cast<_Ty&&>(__value), *this); 436 } 437 438 auto get_env() const noexcept -> const _Env&; 439 }; 440 441 template <class _Env, class _Query, class... _Args> 442 concept __queryable = // 443 tag_invocable<_Query, const _Env&, _Args...>; 444 445 template <class _Env, class _Query, class... _Args> 446 concept __nothrow_queryable = // 447 nothrow_tag_invocable<_Query, const _Env&, _Args...>; 448 449 template <class _Env, class _Query, class... _Args> 450 using __query_result_t = // 451 tag_invoke_result_t<_Query, const _Env&, _Args...>; 452 453 // A singleton environment from a query/value pair 454 template <class _Query, class _Value> 455 struct prop 456 { 457 using __t = prop; 458 using __id = prop; 459 460 STDEXEC_ATTRIBUTE((no_unique_address)) 461 _Query __query; 462 463 STDEXEC_ATTRIBUTE((no_unique_address)) 464 _Value __value; 465 466 STDEXEC_ATTRIBUTE((nodiscard)) 467 constexpr const _Value& query(_Query) const noexcept 468 { 469 return __value; 470 } 471 472 prop& operator=(const prop&) = delete; 473 }; 474 475 template <class _Query, class _Value> 476 prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>; 477 478 // utility for joining multiple environments 479 template <class... _Envs> 480 struct env 481 { 482 using __t = env; 483 using __id = env; 484 485 __tuple_for<_Envs...> __tup_; 486 487 // return a reference to the first child env for which 488 // __queryable<_Envs, _Query, _Args...> is true. 489 template <class _Query, class... _Args> 490 STDEXEC_ATTRIBUTE((always_inline)) 491 constexpr decltype(auto) __get_1st() const noexcept 492 { 493 constexpr bool __flags[] = {__queryable<_Envs, _Query, _Args...>...}; 494 constexpr std::size_t __idx = 495 __pos_of(__flags, __flags + sizeof...(_Envs)); 496 return __tup::get<__idx>(__tup_); 497 } 498 499 template <class _Query, class... _Args> 500 requires(__queryable<_Envs, _Query, _Args...> || ...) 501 STDEXEC_ATTRIBUTE((always_inline)) 502 constexpr decltype(auto) query(_Query __q, _Args&&... __args) const 503 noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()), 504 _Query, _Args...>) 505 { 506 return tag_invoke(__q, __get_1st<_Query, _Args...>(), 507 static_cast<_Args&&>(__args)...); 508 } 509 510 env& operator=(const env&) = delete; 511 }; 512 513 // specialization for two envs to avoid warnings about elided braces 514 template <class _Env0, class _Env1> 515 struct env<_Env0, _Env1> 516 { 517 using __t = env; 518 using __id = env; 519 520 STDEXEC_ATTRIBUTE((no_unique_address)) 521 _Env0 __env0_; 522 STDEXEC_ATTRIBUTE((no_unique_address)) 523 _Env1 __env1_; 524 525 // return a reference to the first child env for which 526 // __queryable<_Envs, _Query, _Args...> is true. 527 template <class _Query, class... _Args> 528 STDEXEC_ATTRIBUTE((always_inline)) 529 constexpr decltype(auto) __get_1st() const noexcept 530 { 531 if constexpr (__queryable<_Env0, _Query, _Args...>) 532 { 533 return (__env0_); 534 } 535 else 536 { 537 return (__env1_); 538 } 539 } 540 541 template <class _Query, class... _Args> 542 requires __queryable<_Env0, _Query, _Args...> || 543 __queryable<_Env1, _Query, _Args...> 544 STDEXEC_ATTRIBUTE((always_inline)) 545 constexpr decltype(auto) query(_Query __q, _Args&&... __args) const 546 noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()), 547 _Query, _Args...>) 548 { 549 return tag_invoke(__q, __get_1st<_Query, _Args...>(), 550 static_cast<_Args&&>(__args)...); 551 } 552 553 env& operator=(const env&) = delete; 554 }; 555 556 template <class... _Envs> 557 env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>; 558 559 template <class _Value, class _Tag, class... _Tags> 560 struct __with 561 { 562 using __t = __with; 563 using __id = __with; 564 STDEXEC_ATTRIBUTE((no_unique_address)) 565 _Value __value_; 566 567 __with() = default; 568 569 constexpr explicit __with(_Value __value) noexcept( 570 __nothrow_decay_copyable<_Value>) : 571 __value_(static_cast<_Value&&>(__value)) 572 {} 573 574 constexpr explicit __with(_Value __value, _Tag, _Tags...) noexcept( 575 __nothrow_decay_copyable<_Value>) : 576 __value_(static_cast<_Value&&>(__value)) 577 {} 578 579 template <__one_of<_Tag, _Tags...> _Key> 580 auto query(_Key) const noexcept -> const _Value& 581 { 582 return __value_; 583 } 584 585 __with& operator=(const __with&) = delete; 586 }; 587 588 template <class _Value, class _Tag, class... _Tags> 589 __with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>; 590 591 template <class _EnvId> 592 struct __fwd 593 { 594 using _Env = __cvref_t<_EnvId>; 595 static_assert(__nothrow_move_constructible<_Env>); 596 597 struct __t 598 { 599 using __id = __fwd; 600 STDEXEC_ATTRIBUTE((no_unique_address)) 601 _Env __env_; 602 603 #if STDEXEC_GCC() && __GNUC__ < 12 604 using __cvref_env_t = std::add_const_t<_Env>&; 605 #else 606 using __cvref_env_t = const _Env&; 607 #endif 608 609 template <__forwarding_query _Tag> 610 requires tag_invocable<_Tag, __cvref_env_t> 611 auto query(_Tag) const 612 noexcept(nothrow_tag_invocable<_Tag, __cvref_env_t>) 613 -> tag_invoke_result_t<_Tag, __cvref_env_t> 614 { 615 return tag_invoke(_Tag(), __env_); 616 } 617 618 __t& operator=(const __t&) = delete; 619 }; 620 }; 621 622 struct __fwd_fn 623 { 624 template <class _Env> 625 auto operator()(_Env&& __env) const 626 { 627 return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)}; 628 } 629 630 auto operator()(empty_env) const -> empty_env 631 { 632 return {}; 633 } 634 }; 635 636 template <class _EnvId, class _Tag> 637 struct __without_ 638 { 639 using _Env = __cvref_t<_EnvId>; 640 static_assert(__nothrow_move_constructible<_Env>); 641 642 struct __t 643 { 644 using __id = __without_; 645 _Env __env_; 646 647 #if STDEXEC_GCC() && __GNUC__ < 12 648 using __cvref_env_t = std::add_const_t<_Env>&; 649 #else 650 using __cvref_env_t = const _Env&; 651 #endif 652 653 auto query(_Tag) const noexcept = delete; 654 655 template <tag_invocable<__cvref_env_t> _Key> 656 STDEXEC_ATTRIBUTE((always_inline)) 657 auto query(_Key) const noexcept( 658 nothrow_tag_invocable<_Key, __cvref_env_t>) -> decltype(auto) 659 { 660 return tag_invoke(_Key(), __env_); 661 } 662 663 __t& operator=(const __t&) = delete; 664 }; 665 }; 666 667 struct __without_fn 668 { 669 template <class _Env, class _Tag> 670 constexpr auto operator()(_Env&& __env, 671 _Tag) const noexcept -> decltype(auto) 672 { 673 if constexpr (tag_invocable<_Tag, _Env>) 674 { 675 using _Without = __t<__without_<__cvref_id<_Env>, _Tag>>; 676 return _Without{static_cast<_Env&&>(__env)}; 677 } 678 else 679 { 680 return static_cast<_Env>(static_cast<_Env&&>(__env)); 681 } 682 } 683 }; 684 685 inline constexpr __without_fn __without{}; 686 687 template <class _Env, class _Tag, class... _Tags> 688 using __without_t = __result_of<__without, _Env, _Tag, _Tags...>; 689 690 template <__nothrow_move_constructible _Fun> 691 struct __from 692 { 693 using __t = __from; 694 using __id = __from; 695 STDEXEC_ATTRIBUTE((no_unique_address)) 696 _Fun __fun_; 697 698 template <class _Tag> 699 requires __callable<const _Fun&, _Tag> 700 auto query(_Tag) const noexcept(__nothrow_callable<const _Fun&, _Tag>) 701 -> __call_result_t<const _Fun&, _Tag> 702 { 703 return __fun_(_Tag()); 704 } 705 706 __from& operator=(const __from&) = delete; 707 }; 708 709 template <class _Fun> 710 __from(_Fun) -> __from<_Fun>; 711 712 struct __join_fn 713 { 714 auto operator()(empty_env, empty_env) const noexcept -> empty_env 715 { 716 return {}; 717 } 718 719 template <class _Env> 720 auto operator()(_Env&& __env, empty_env = {}) const noexcept -> _Env 721 { 722 return static_cast<_Env&&>(__env); 723 } 724 725 template <class _Env> 726 auto operator()(empty_env, _Env&& __env) const noexcept -> decltype(auto) 727 { 728 return __fwd_fn()(static_cast<_Env&&>(__env)); 729 } 730 731 template <class _First, class _Second> 732 auto operator()(_First&& __first, _Second&& __second) const noexcept 733 -> env<_First, __call_result_t<__fwd_fn, _Second>> 734 { 735 return {static_cast<_First&&>(__first), 736 __fwd_fn()(static_cast<_Second&&>(__second))}; 737 } 738 }; 739 740 inline constexpr __join_fn __join{}; 741 742 template <class _First, class... _Second> 743 using __join_t = __result_of<__join, _First, _Second...>; 744 745 struct __as_root_env_fn 746 { 747 template <class _Env> 748 constexpr auto operator()(_Env __env) const noexcept 749 -> __join_t<__root_env, std::unwrap_reference_t<_Env>> 750 { 751 return __join(__root_env{}, 752 static_cast<std::unwrap_reference_t<_Env>&&>(__env)); 753 } 754 }; 755 756 inline constexpr __as_root_env_fn __as_root_env{}; 757 758 template <class _Env> 759 using __as_root_env_t = __result_of<__as_root_env, _Env>; 760 } // namespace __env 761 762 using __env::env; 763 using __env::prop; 764 using empty_env = env<>; 765 766 ///////////////////////////////////////////////////////////////////////////// 767 namespace __get_env 768 { 769 // For getting an execution environment from a receiver or the attributes from a 770 // sender. 771 struct get_env_t 772 { 773 template <__same_as<get_env_t> _Self, class _EnvProvider> 774 STDEXEC_ATTRIBUTE((always_inline)) 775 friend auto tag_invoke(_Self, const _EnvProvider& __env_provider) noexcept 776 -> decltype(__env_provider.get_env()) 777 { 778 static_assert(noexcept(__env_provider.get_env()), 779 "get_env() members must be noexcept"); 780 return __env_provider.get_env(); 781 } 782 783 template <class _EnvProvider> 784 requires tag_invocable<get_env_t, const _EnvProvider&> 785 STDEXEC_ATTRIBUTE((always_inline)) 786 constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept 787 -> tag_invoke_result_t<get_env_t, const _EnvProvider&> 788 { 789 static_assert( 790 queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>); 791 static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>); 792 return tag_invoke(*this, __env_provider); 793 } 794 795 template <class _EnvProvider> 796 constexpr auto operator()(const _EnvProvider&) const noexcept -> empty_env 797 { 798 return {}; 799 } 800 }; 801 } // namespace __get_env 802 803 using __get_env::get_env_t; 804 inline constexpr get_env_t get_env{}; 805 806 template <class _EnvProvider> 807 concept environment_provider = // 808 requires(_EnvProvider& __ep) { 809 { get_env(std::as_const(__ep)) } -> queryable; 810 }; 811 812 using __env::__as_root_env; 813 using __env::__as_root_env_t; 814 815 template <class _Env> 816 concept __is_root_env = requires(_Env&& __env) { 817 { __root_t{}(__env) } -> same_as<bool>; 818 }; 819 820 template <class _Sender> 821 concept __is_scheduler_affine = // 822 requires { 823 requires __v< 824 __call_result_t<__is_scheduler_affine_t, env_of_t<_Sender>>>; 825 }; 826 } // namespace stdexec 827 828 STDEXEC_PRAGMA_POP() 829