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> operator ()stdexec::__queries::forwarding_query_t60 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> operator ()stdexec::__queries::query_or_t90 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> operator ()stdexec::__queries::query_or_t98 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>> operator ()stdexec::__queries::execute_may_block_caller_t112 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 operator ()stdexec::__queries::execute_may_block_caller_t122 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>> operator ()stdexec::__queries::get_forward_progress_guarantee_t133 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 operator ()stdexec::__queries::get_forward_progress_guarantee_t142 constexpr auto operator()(auto&&) const noexcept 143 -> 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>> operator ()stdexec::__queries::__has_algorithm_customizations_t158 constexpr auto operator()(_Tp&&) const noexcept(noexcept(__result_t<_Tp>{})) 159 -> __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 operator ()stdexec::__queries::__has_algorithm_customizations_t169 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 { querystdexec::__queries::get_scheduler_t181 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 { querystdexec::__queries::get_delegation_scheduler_t200 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 { querystdexec::__queries::get_allocator_t216 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&> operator ()stdexec::__queries::get_allocator_t223 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 { querystdexec::__queries::get_stop_token_t238 static constexpr auto query(forwarding_query_t) noexcept -> bool 239 { 240 return true; 241 } 242 243 template <class _Env, class _Token = never_stop_token> operator ()stdexec::__queries::get_stop_token_t244 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&> operator ()stdexec::__queries::get_stop_token_t251 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 { querystdexec::__queries::get_completion_scheduler_t273 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&> operator ()stdexec::__queries::get_domain_t288 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 querystdexec::__queries::get_domain_t299 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> operator ()stdexec::__queries::__is_scheduler_affine_t308 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 querystdexec::__queries::__is_scheduler_affine_t323 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&> operator ()stdexec::__queries::__root_t333 constexpr auto operator()(const _Env& __env) const noexcept -> bool 334 { 335 STDEXEC_ASSERT(tag_invoke(__root_t{}, __env) == true); 336 return true; 337 } 338 querystdexec::__queries::__root_t339 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 STDEXEC_MEMFN_DECLstdexec::__queries::__root_env350 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> await_transformstdexec::__env::__promise424 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&> await_transformstdexec::__env::__promise431 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)) querystdexec::__env::prop467 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)) __get_1ststdexec::__env::env491 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)) querystdexec::__env::env502 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)) __get_1ststdexec::__env::env529 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 __withstdexec::__env::__with569 constexpr explicit __with(_Value __value) noexcept( 570 __nothrow_decay_copyable<_Value>) : 571 __value_(static_cast<_Value&&>(__value)) 572 {} 573 __withstdexec::__env::__with574 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> querystdexec::__env::__with580 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> querystdexec::__env::__fwd::__t611 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> operator ()stdexec::__env::__fwd_fn625 auto operator()(_Env&& __env) const 626 { 627 return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)}; 628 } 629 operator ()stdexec::__env::__fwd_fn630 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)) querystdexec::__env::__without_::__t657 auto query(_Key) const 658 noexcept(nothrow_tag_invocable<_Key, __cvref_env_t>) 659 -> decltype(auto) 660 { 661 return tag_invoke(_Key(), __env_); 662 } 663 664 __t& operator=(const __t&) = delete; 665 }; 666 }; 667 668 struct __without_fn 669 { 670 template <class _Env, class _Tag> operator ()stdexec::__env::__without_fn671 constexpr auto operator()(_Env&& __env, _Tag) const noexcept 672 -> decltype(auto) 673 { 674 if constexpr (tag_invocable<_Tag, _Env>) 675 { 676 using _Without = __t<__without_<__cvref_id<_Env>, _Tag>>; 677 return _Without{static_cast<_Env&&>(__env)}; 678 } 679 else 680 { 681 return static_cast<_Env>(static_cast<_Env&&>(__env)); 682 } 683 } 684 }; 685 686 inline constexpr __without_fn __without{}; 687 688 template <class _Env, class _Tag, class... _Tags> 689 using __without_t = __result_of<__without, _Env, _Tag, _Tags...>; 690 691 template <__nothrow_move_constructible _Fun> 692 struct __from 693 { 694 using __t = __from; 695 using __id = __from; 696 STDEXEC_ATTRIBUTE((no_unique_address)) 697 _Fun __fun_; 698 699 template <class _Tag> 700 requires __callable<const _Fun&, _Tag> querystdexec::__env::__from701 auto query(_Tag) const noexcept(__nothrow_callable<const _Fun&, _Tag>) 702 -> __call_result_t<const _Fun&, _Tag> 703 { 704 return __fun_(_Tag()); 705 } 706 707 __from& operator=(const __from&) = delete; 708 }; 709 710 template <class _Fun> 711 __from(_Fun) -> __from<_Fun>; 712 713 struct __join_fn 714 { operator ()stdexec::__env::__join_fn715 auto operator()(empty_env, empty_env) const noexcept -> empty_env 716 { 717 return {}; 718 } 719 720 template <class _Env> operator ()stdexec::__env::__join_fn721 auto operator()(_Env&& __env, empty_env = {}) const noexcept -> _Env 722 { 723 return static_cast<_Env&&>(__env); 724 } 725 726 template <class _Env> operator ()stdexec::__env::__join_fn727 auto operator()(empty_env, _Env&& __env) const noexcept -> decltype(auto) 728 { 729 return __fwd_fn()(static_cast<_Env&&>(__env)); 730 } 731 732 template <class _First, class _Second> operator ()stdexec::__env::__join_fn733 auto operator()(_First&& __first, _Second&& __second) const noexcept 734 -> env<_First, __call_result_t<__fwd_fn, _Second>> 735 { 736 return {static_cast<_First&&>(__first), 737 __fwd_fn()(static_cast<_Second&&>(__second))}; 738 } 739 }; 740 741 inline constexpr __join_fn __join{}; 742 743 template <class _First, class... _Second> 744 using __join_t = __result_of<__join, _First, _Second...>; 745 746 struct __as_root_env_fn 747 { 748 template <class _Env> operator ()stdexec::__env::__as_root_env_fn749 constexpr auto operator()(_Env __env) const noexcept 750 -> __join_t<__root_env, std::unwrap_reference_t<_Env>> 751 { 752 return __join(__root_env{}, 753 static_cast<std::unwrap_reference_t<_Env>&&>(__env)); 754 } 755 }; 756 757 inline constexpr __as_root_env_fn __as_root_env{}; 758 759 template <class _Env> 760 using __as_root_env_t = __result_of<__as_root_env, _Env>; 761 } // namespace __env 762 763 using __env::env; 764 using __env::prop; 765 using empty_env = env<>; 766 767 ///////////////////////////////////////////////////////////////////////////// 768 namespace __get_env 769 { 770 // For getting an execution environment from a receiver or the attributes from a 771 // sender. 772 struct get_env_t 773 { 774 template <__same_as<get_env_t> _Self, class _EnvProvider> 775 STDEXEC_ATTRIBUTE((always_inline)) tag_invoke(_Self,const _EnvProvider & __env_provider)776 friend auto tag_invoke(_Self, const _EnvProvider& __env_provider) noexcept 777 -> decltype(__env_provider.get_env()) 778 { 779 static_assert(noexcept(__env_provider.get_env()), 780 "get_env() members must be noexcept"); 781 return __env_provider.get_env(); 782 } 783 784 template <class _EnvProvider> 785 requires tag_invocable<get_env_t, const _EnvProvider&> 786 STDEXEC_ATTRIBUTE((always_inline)) operator ()stdexec::__get_env::get_env_t787 constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept 788 -> tag_invoke_result_t<get_env_t, const _EnvProvider&> 789 { 790 static_assert( 791 queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>); 792 static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>); 793 return tag_invoke(*this, __env_provider); 794 } 795 796 template <class _EnvProvider> operator ()stdexec::__get_env::get_env_t797 constexpr auto operator()(const _EnvProvider&) const noexcept -> empty_env 798 { 799 return {}; 800 } 801 }; 802 } // namespace __get_env 803 804 using __get_env::get_env_t; 805 inline constexpr get_env_t get_env{}; 806 807 template <class _EnvProvider> 808 concept environment_provider = // 809 requires(_EnvProvider& __ep) { 810 { get_env(std::as_const(__ep)) } -> queryable; 811 }; 812 813 using __env::__as_root_env; 814 using __env::__as_root_env_t; 815 816 template <class _Env> 817 concept __is_root_env = requires(_Env&& __env) { 818 { __root_t{}(__env) } -> same_as<bool>; 819 }; 820 821 template <class _Sender> 822 concept __is_scheduler_affine = // 823 requires { 824 requires __v< 825 __call_result_t<__is_scheduler_affine_t, env_of_t<_Sender>>>; 826 }; 827 } // namespace stdexec 828 829 STDEXEC_PRAGMA_POP() 830