1 /* Copyright (c) 2023 Maikel Nadolski 2 * Copyright (c) 2023 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 <sdbusplus/async/stdexec/execution.hpp> 19 20 #include <cstddef> 21 22 namespace exec 23 { 24 namespace __any 25 { 26 using namespace stdexec; 27 28 struct __create_vtable_t 29 { 30 template <class _VTable, class _Tp> 31 requires __tag_invocable_r<const _VTable*, __create_vtable_t, 32 __mtype<_VTable>, __mtype<_Tp>> 33 constexpr const _VTable* operator()(__mtype<_VTable>, 34 __mtype<_Tp>) const noexcept 35 { 36 return tag_invoke(__create_vtable_t{}, __mtype<_VTable>{}, 37 __mtype<_Tp>{}); 38 } 39 }; 40 41 inline constexpr __create_vtable_t __create_vtable{}; 42 43 template <class _Sig> 44 struct __query_vfun; 45 46 template <class _Tag, class _Ret, class... _As> 47 struct __query_vfun<_Tag (*const)(_Ret (*)(_As...))> 48 { 49 _Ret (*__fn_)(void*, _As...); 50 51 _Ret operator()(_Tag, void* __rcvr, _As&&... __as) const 52 { 53 return __fn_(__rcvr, (_As&&)__as...); 54 } 55 }; 56 57 template <class _Tag, class _Ret, class... _As> 58 struct __query_vfun<_Tag (*)(_Ret (*)(_As...))> 59 { 60 _Ret (*__fn_)(void*, _As...); 61 62 _Ret operator()(_Tag, void* __rcvr, _As&&... __as) const 63 { 64 return __fn_(__rcvr, (_As&&)__as...); 65 } 66 }; 67 68 template <class _Tag, class _Ret, class... _As> 69 struct __query_vfun<_Tag (*const)(_Ret (*)(_As...) noexcept)> 70 { 71 _Ret (*__fn_)(void*, _As...) noexcept; 72 73 _Ret operator()(_Tag, void* __rcvr, _As&&... __as) const noexcept 74 { 75 return __fn_(__rcvr, (_As&&)__as...); 76 } 77 }; 78 79 template <class _Tag, class _Ret, class... _As> 80 struct __query_vfun<_Tag (*)(_Ret (*)(_As...) noexcept)> 81 { 82 _Ret (*__fn_)(void*, _As...) noexcept; 83 84 _Ret operator()(_Tag, void* __rcvr, _As&&... __as) const noexcept 85 { 86 return __fn_(__rcvr, (_As&&)__as...); 87 } 88 }; 89 90 template <class> 91 struct __query_vfun_fn; 92 93 template <class _EnvProvider> 94 requires __callable<get_env_t, const _EnvProvider&> 95 struct __query_vfun_fn<_EnvProvider> 96 { 97 template <class _Tag, class _Ret, class... _As> 98 requires __callable<_Tag, env_of_t<const _EnvProvider&>, _As...> 99 constexpr _Ret (*operator()(_Tag (*)(_Ret (*)(_As...))) 100 const noexcept)(void*, _As...) 101 { 102 return +[](void* __env_provider, _As... __as) -> _Ret { 103 return _Tag{}(get_env(*(const _EnvProvider*)__env_provider), 104 (_As&&)__as...); 105 }; 106 } 107 108 template <class _Tag, class _Ret, class... _As> 109 requires __callable<_Tag, env_of_t<const _EnvProvider&>, _As...> 110 constexpr _Ret (*operator()(_Tag (*)(_Ret (*)(_As...) noexcept)) 111 const noexcept)(void*, _As...) noexcept 112 { 113 return +[](void* __env_provider, _As... __as) noexcept -> _Ret { 114 static_assert( 115 __nothrow_callable<_Tag, const env_of_t<_EnvProvider>&, 116 _As...>); 117 return _Tag{}(get_env(*(const _EnvProvider*)__env_provider), 118 (_As&&)__as...); 119 }; 120 } 121 }; 122 123 template <class _Queryable> 124 requires(!__callable<get_env_t, const _Queryable&>) 125 struct __query_vfun_fn<_Queryable> 126 { 127 template <class _Tag, class _Ret, class... _As> 128 requires __callable<_Tag, const _Queryable&, _As...> 129 constexpr _Ret (*operator()(_Tag (*)(_Ret (*)(_As...))) 130 const noexcept)(void*, _As...) 131 { 132 return +[](void* __queryable, _As... __as) -> _Ret { 133 return _Tag{}(*(const _Queryable*)__queryable, (_As&&)__as...); 134 }; 135 } 136 137 template <class _Tag, class _Ret, class... _As> 138 requires __callable<_Tag, const _Queryable&, _As...> 139 constexpr _Ret (*operator()(_Tag (*)(_Ret (*)(_As...) noexcept)) 140 const noexcept)(void*, _As...) noexcept 141 { 142 return +[](void* __env_provider, _As... __as) noexcept -> _Ret { 143 static_assert(__nothrow_callable<_Tag, const _Queryable&, _As...>); 144 return _Tag{}(*(const _Queryable*)__env_provider, (_As&&)__as...); 145 }; 146 } 147 }; 148 149 template <class _Sig> 150 struct __storage_vfun; 151 152 template <class _Tag, class... _As> 153 struct __storage_vfun<_Tag(void (*)(_As...))> 154 { 155 void (*__fn_)(void*, _As...) = [](void*, _As...) {}; 156 157 void operator()(_Tag, void* __storage, _As&&... __as) const 158 { 159 return __fn_(__storage, (_As&&)__as...); 160 } 161 }; 162 163 template <class _Tag, class... _As> 164 struct __storage_vfun<_Tag(void (*)(_As...) noexcept)> 165 { 166 void (*__fn_)(void*, _As...) noexcept = [](void*, _As...) noexcept {}; 167 168 void operator()(_Tag, void* __storage, _As&&... __as) const noexcept 169 { 170 return __fn_(__storage, (_As&&)__as...); 171 } 172 }; 173 174 template <class _Storage, class _Tp> 175 struct __storage_vfun_fn 176 { 177 template <class _Tag, class... _As> 178 requires __callable<_Tag, __mtype<_Tp>, _Storage&, _As...> 179 constexpr void (*operator()(_Tag (*)(void (*)(_As...))) 180 const noexcept)(void*, _As...) 181 { 182 return +[](void* __storage, _As... __as) -> void { 183 return _Tag{}(__mtype<_Tp>{}, *(_Storage*)__storage, 184 (_As&&)__as...); 185 }; 186 } 187 188 template <class _Tag, class... _As> 189 requires __callable<_Tag, __mtype<_Tp>, _Storage&, _As...> 190 constexpr void (*operator()(_Tag (*)(void (*)(_As...) noexcept)) 191 const noexcept)(void*, _As...) noexcept 192 { 193 return +[](void* __storage, _As... __as) noexcept -> void { 194 static_assert( 195 __nothrow_callable<_Tag, __mtype<_Tp>, _Storage&, _As...>); 196 return _Tag{}(__mtype<_Tp>{}, *(_Storage*)__storage, 197 (_As&&)__as...); 198 }; 199 } 200 }; 201 202 struct __delete_t 203 { 204 template <class _Storage, class _Tp> 205 requires tag_invocable<__delete_t, __mtype<_Tp>, _Storage&> 206 void operator()(__mtype<_Tp>, _Storage& __storage) noexcept 207 { 208 static_assert( 209 nothrow_tag_invocable<__delete_t, __mtype<_Tp>, _Storage&>); 210 tag_invoke(__delete_t{}, __mtype<_Tp>{}, __storage); 211 } 212 }; 213 214 inline constexpr __delete_t __delete{}; 215 216 struct __copy_construct_t 217 { 218 template <class _Storage, class _Tp> 219 requires tag_invocable<__copy_construct_t, __mtype<_Tp>, _Storage&, 220 const _Storage&> 221 void operator()( 222 __mtype<_Tp>, _Storage& __self, 223 const _Storage& 224 __from) noexcept(nothrow_tag_invocable<__copy_construct_t, 225 __mtype<_Tp>, _Storage&, 226 const _Storage&>) 227 { 228 tag_invoke(__copy_construct_t{}, __mtype<_Tp>{}, __self, __from); 229 } 230 }; 231 232 inline constexpr __copy_construct_t __copy_construct{}; 233 234 struct __move_construct_t 235 { 236 template <class _Storage, class _Tp> 237 requires tag_invocable<__move_construct_t, __mtype<_Tp>, _Storage&, 238 _Storage&&> 239 void operator()(__mtype<_Tp>, _Storage& __self, 240 __midentity<_Storage&&> __from) noexcept 241 { 242 static_assert(nothrow_tag_invocable<__move_construct_t, __mtype<_Tp>, 243 _Storage&, _Storage&&>); 244 tag_invoke(__move_construct_t{}, __mtype<_Tp>{}, __self, 245 (_Storage&&)__from); 246 } 247 }; 248 249 inline constexpr __move_construct_t __move_construct{}; 250 251 template <class _ParentVTable, class... _StorageCPOs> 252 struct __storage_vtable; 253 254 template <class _ParentVTable, class... _StorageCPOs> 255 requires requires { _ParentVTable::operator(); } 256 struct __storage_vtable<_ParentVTable, _StorageCPOs...> : 257 _ParentVTable, 258 __storage_vfun<_StorageCPOs>... 259 { 260 using _ParentVTable::operator(); 261 using __storage_vfun<_StorageCPOs>::operator()...; 262 }; 263 264 template <class _ParentVTable, class... _StorageCPOs> 265 requires(!requires { _ParentVTable::operator(); }) 266 struct __storage_vtable<_ParentVTable, _StorageCPOs...> : 267 _ParentVTable, 268 __storage_vfun<_StorageCPOs>... 269 { 270 using __storage_vfun<_StorageCPOs>::operator()...; 271 }; 272 273 template <class _ParentVTable, class... _StorageCPOs> 274 inline constexpr __storage_vtable<_ParentVTable, _StorageCPOs...> 275 __null_storage_vtbl{}; 276 277 template <class _ParentVTable, class... _StorageCPOs> 278 constexpr const __storage_vtable<_ParentVTable, _StorageCPOs...>* 279 __default_storage_vtable( 280 __storage_vtable<_ParentVTable, _StorageCPOs...>*) noexcept 281 { 282 return &__null_storage_vtbl<_ParentVTable, _StorageCPOs...>; 283 } 284 285 template <class _Storage, class _Tp, class _ParentVTable, class... _StorageCPOs> 286 static const __storage_vtable<_ParentVTable, _StorageCPOs...> __storage_vtbl{ 287 {*__create_vtable(__mtype<_ParentVTable>{}, __mtype<_Tp>{})}, 288 {__storage_vfun_fn<_Storage, _Tp>{}((_StorageCPOs*)nullptr)}...}; 289 290 template <class _Vtable, class _Allocator, bool _Copyable = false, 291 std::size_t _Alignment = alignof(std::max_align_t), 292 std::size_t _InlineSize = 3 * sizeof(void*)> 293 struct __storage 294 { 295 class __t; 296 }; 297 298 template <class _Vtable, class _Allocator, bool _Copyable, 299 std::size_t _Alignment, std::size_t _InlineSize> 300 class __storage<_Vtable, _Allocator, _Copyable, _Alignment, _InlineSize>::__t : 301 __if_c<_Copyable, __, __move_only> 302 { 303 static_assert( 304 std::is_convertible_v< 305 typename std::allocator_traits<_Allocator>::void_pointer, void*>); 306 307 static constexpr std::size_t __buffer_size = std::max(_InlineSize, 308 sizeof(void*)); 309 static constexpr std::size_t __alignment = std::max(_Alignment, 310 alignof(void*)); 311 using __with_copy = __copy_construct_t(void(const __t&)); 312 using __with_move = __move_construct_t(void(__t&&) noexcept); 313 using __with_delete = __delete_t(void() noexcept); 314 315 template <class _Tp> 316 static constexpr bool __is_small = 317 sizeof(_Tp) <= __buffer_size && alignof(_Tp) <= __alignment && 318 std::is_nothrow_move_constructible_v<_Tp>; 319 320 using __vtable_t = __if_c< 321 _Copyable, 322 __storage_vtable<_Vtable, __with_delete, __with_move, __with_copy>, 323 __storage_vtable<_Vtable, __with_delete, __with_move>>; 324 325 template <class _Tp> 326 static constexpr const __vtable_t* __get_vtable_of_type() noexcept 327 { 328 if constexpr (_Copyable) 329 { 330 return &__storage_vtbl<__t, __decay_t<_Tp>, _Vtable, __with_delete, 331 __with_move, __with_copy>; 332 } 333 else 334 { 335 return &__storage_vtbl<__t, __decay_t<_Tp>, _Vtable, __with_delete, 336 __with_move>; 337 } 338 } 339 340 public: 341 using __id = __storage; 342 343 __t() = default; 344 345 template <__not_decays_to<__t> _Tp> 346 requires __callable<__create_vtable_t, __mtype<_Vtable>, 347 __mtype<__decay_t<_Tp>>> 348 __t(_Tp&& __object) : __vtable_{__get_vtable_of_type<_Tp>()} 349 { 350 using _Dp = __decay_t<_Tp>; 351 if constexpr (__is_small<_Dp>) 352 { 353 __construct_small<_Dp>((_Tp&&)__object); 354 } 355 else 356 { 357 __construct_large<_Dp>((_Tp&&)__object); 358 } 359 } 360 361 template <class _Tp, class... _Args> 362 requires __callable<__create_vtable_t, __mtype<_Vtable>, __mtype<_Tp>> 363 __t(std::in_place_type_t<_Tp>, _Args&&... __args) : 364 __vtable_{__get_vtable_of_type<_Tp>()} 365 { 366 if constexpr (__is_small<_Tp>) 367 { 368 __construct_small<_Tp>((_Args&&)__args...); 369 } 370 else 371 { 372 __construct_large<_Tp>((_Args&&)__args...); 373 } 374 } 375 376 __t(const __t& __other) 377 requires(_Copyable) 378 { 379 (*__other.__vtable_)(__copy_construct, this, __other); 380 } 381 382 __t& operator=(const __t& __other) 383 requires(_Copyable) 384 { 385 __t tmp(__other); 386 return *this = std::move(tmp); 387 } 388 389 __t(__t&& __other) noexcept 390 { 391 (*__other.__vtable_)(__move_construct, this, (__t&&)__other); 392 } 393 394 __t& operator=(__t&& __other) noexcept 395 { 396 __reset(); 397 (*__other.__vtable_)(__move_construct, this, (__t&&)__other); 398 return *this; 399 } 400 401 ~__t() 402 { 403 __reset(); 404 } 405 406 void __reset() noexcept 407 { 408 (*__vtable_)(__delete, this); 409 __object_pointer_ = nullptr; 410 __vtable_ = __default_storage_vtable((__vtable_t*)nullptr); 411 } 412 413 const _Vtable* __get_vtable() const noexcept 414 { 415 return __vtable_; 416 } 417 418 void* __get_object_pointer() const noexcept 419 { 420 return __object_pointer_; 421 } 422 423 private: 424 template <class _Tp, class... _As> 425 void __construct_small(_As&&... __args) 426 { 427 static_assert(sizeof(_Tp) <= __buffer_size && 428 alignof(_Tp) <= __alignment); 429 _Tp* __pointer = static_cast<_Tp*>(static_cast<void*>(&__buffer_[0])); 430 using _Alloc = typename std::allocator_traits< 431 _Allocator>::template rebind_alloc<_Tp>; 432 _Alloc __alloc{__allocator_}; 433 std::allocator_traits<_Alloc>::construct(__alloc, __pointer, 434 (_As&&)__args...); 435 __object_pointer_ = __pointer; 436 } 437 438 template <class _Tp, class... _As> 439 void __construct_large(_As&&... __args) 440 { 441 using _Alloc = typename std::allocator_traits< 442 _Allocator>::template rebind_alloc<_Tp>; 443 _Alloc __alloc{__allocator_}; 444 _Tp* __pointer = std::allocator_traits<_Alloc>::allocate(__alloc, 1); 445 try 446 { 447 std::allocator_traits<_Alloc>::construct(__alloc, __pointer, 448 (_As&&)__args...); 449 } 450 catch (...) 451 { 452 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1); 453 throw; 454 } 455 __object_pointer_ = __pointer; 456 } 457 458 template <class _Tp> 459 friend void tag_invoke(__delete_t, __mtype<_Tp>, __t& __self) noexcept 460 { 461 if (!__self.__object_pointer_) 462 { 463 return; 464 } 465 using _Alloc = typename std::allocator_traits< 466 _Allocator>::template rebind_alloc<_Tp>; 467 _Alloc __alloc{__self.__allocator_}; 468 _Tp* __pointer = 469 static_cast<_Tp*>(std::exchange(__self.__object_pointer_, nullptr)); 470 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer); 471 if constexpr (!__is_small<_Tp>) 472 { 473 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1); 474 } 475 } 476 477 template <class _Tp> 478 friend void tag_invoke(__move_construct_t, __mtype<_Tp>, __t& __self, 479 __t&& __other) noexcept 480 { 481 if (!__other.__object_pointer_) 482 { 483 return; 484 } 485 _Tp* __pointer = static_cast<_Tp*>( 486 std::exchange(__other.__object_pointer_, nullptr)); 487 if constexpr (__is_small<_Tp>) 488 { 489 _Tp& __other_object = *__pointer; 490 __self.template __construct_small<_Tp>((_Tp&&)__other_object); 491 using _Alloc = typename std::allocator_traits< 492 _Allocator>::template rebind_alloc<_Tp>; 493 _Alloc __alloc{__self.__allocator_}; 494 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer); 495 } 496 else 497 { 498 __self.__object_pointer_ = __pointer; 499 } 500 __self.__vtable_ = std::exchange( 501 __other.__vtable_, __default_storage_vtable((__vtable_t*)nullptr)); 502 } 503 504 template <class _Tp> 505 requires _Copyable 506 friend void tag_invoke(__copy_construct_t, __mtype<_Tp>, __t& __self, 507 const __t& __other) 508 { 509 if (!__other.__object_pointer_) 510 { 511 return; 512 } 513 const _Tp& __other_object = 514 *static_cast<const _Tp*>(__other.__object_pointer_); 515 if constexpr (__is_small<_Tp>) 516 { 517 __self.template __construct_small<_Tp>(__other_object); 518 } 519 else 520 { 521 __self.template __construct_large<_Tp>(__other_object); 522 } 523 __self.__vtable_ = __other.__vtable_; 524 } 525 526 const __vtable_t* __vtable_{__default_storage_vtable((__vtable_t*)nullptr)}; 527 void* __object_pointer_{nullptr}; 528 alignas(__alignment) std::byte __buffer_[__buffer_size]{}; 529 STDEXEC_NO_UNIQUE_ADDRESS _Allocator __allocator_{}; 530 }; 531 532 template <class _VTable, class _Allocator = std::allocator<std::byte>> 533 using __unique_storage_t = __t<__storage<_VTable, _Allocator>>; 534 535 template <class _VTable, class _Allocator = std::allocator<std::byte>> 536 using __copyable_storage_t = __t<__storage<_VTable, _Allocator, true>>; 537 538 namespace __rec 539 { 540 template <class _Sig> 541 struct __rcvr_vfun; 542 543 template <class _Sigs, class... _Queries> 544 struct __vtable 545 { 546 class __t; 547 }; 548 549 template <class _Sigs, class... _Queries> 550 struct __ref; 551 552 template <class _Tag, class... _As> 553 struct __rcvr_vfun<_Tag(_As...)> 554 { 555 void (*__fn_)(void*, _As...) noexcept; 556 }; 557 558 template <class _Rcvr> 559 struct __rcvr_vfun_fn 560 { 561 template <class _Tag, class... _As> 562 constexpr void (*operator()(_Tag (*)(_As...)) 563 const noexcept)(void*, _As...) noexcept 564 { 565 return +[](void* __rcvr, _As... __as) noexcept -> void { 566 _Tag{}((_Rcvr&&)*(_Rcvr*)__rcvr, (_As&&)__as...); 567 }; 568 } 569 }; 570 571 template <class... _Sigs, class... _Queries> 572 struct __vtable<completion_signatures<_Sigs...>, _Queries...> 573 { 574 class __t : public __rcvr_vfun<_Sigs>..., public __query_vfun<_Queries>... 575 { 576 public: 577 using __query_vfun<_Queries>::operator()...; 578 579 private: 580 template <class _Rcvr> 581 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> && 582 (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...) 583 friend const __t* tag_invoke(__create_vtable_t, __mtype<__t>, 584 __mtype<_Rcvr>) noexcept 585 { 586 static const __t __vtable_{ 587 {__rcvr_vfun_fn<_Rcvr>{}((_Sigs*)nullptr)}..., 588 {__query_vfun_fn<_Rcvr>{}((_Queries) nullptr)}...}; 589 return &__vtable_; 590 } 591 }; 592 }; 593 594 template <class... _Sigs, class... _Queries> 595 struct __ref<completion_signatures<_Sigs...>, _Queries...> 596 { 597 private: 598 using __vtable_t = 599 __t<__vtable<completion_signatures<_Sigs...>, _Queries...>>; 600 601 struct __env_t 602 { 603 const __vtable_t* __vtable_; 604 void* __rcvr_; 605 606 template <class _Tag, class... _As> 607 requires __callable<const __vtable_t&, _Tag, void*, _As...> 608 friend auto 609 tag_invoke(_Tag, const __env_t& __self, _As&&... __as) noexcept( 610 __nothrow_callable<const __vtable_t&, _Tag, void*, _As...>) 611 -> __call_result_t<const __vtable_t&, _Tag, void*, _As...> 612 { 613 return (*__self.__vtable_)(_Tag{}, __self.__rcvr_, (_As&&)__as...); 614 } 615 } __env_; 616 617 public: 618 using is_receiver = void; 619 620 template <__none_of<__ref, const __ref, __env_t, const __env_t> _Rcvr> 621 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> && 622 (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...) 623 __ref(_Rcvr& __rcvr) noexcept : 624 __env_{__create_vtable(__mtype<__vtable_t>{}, __mtype<_Rcvr>{}), 625 &__rcvr} 626 {} 627 628 template <__completion_tag _Tag, __decays_to<__ref> _Self, class... _As> 629 requires __one_of<_Tag(_As...), _Sigs...> 630 friend void tag_invoke(_Tag, _Self&& __self, _As&&... __as) noexcept 631 { 632 (*static_cast<const __rcvr_vfun<_Tag(_As...)>*>(__self.__env_.__vtable_) 633 ->__fn_)(((_Self&&)__self).__env_.__rcvr_, (_As&&)__as...); 634 } 635 636 template <std::same_as<__ref> Self> 637 friend const __env_t& tag_invoke(get_env_t, const Self& __self) noexcept 638 { 639 return __self.__env_; 640 } 641 }; 642 } // namespace __rec 643 644 class __operation_vtable 645 { 646 public: 647 void (*__start_)(void*) noexcept; 648 649 private: 650 template <class _Op> 651 friend const __operation_vtable* tag_invoke(__create_vtable_t, 652 __mtype<__operation_vtable>, 653 __mtype<_Op>) noexcept 654 { 655 static __operation_vtable __vtable{ 656 [](void* __object_pointer) noexcept -> void { 657 STDEXEC_ASSERT(__object_pointer); 658 _Op& __op = *static_cast<_Op*>(__object_pointer); 659 static_assert(operation_state<_Op>); 660 start(__op); 661 }}; 662 return &__vtable; 663 } 664 }; 665 666 using __unique_operation_storage = __unique_storage_t<__operation_vtable>; 667 668 template <class _Sigs, class _Queries> 669 using __receiver_ref = 670 __mapply<__mbind_front<__q<__rec::__ref>, _Sigs>, _Queries>; 671 672 template <class _Receiver, class _Sigs, class _Queries> 673 struct __operation_base 674 { 675 STDEXEC_NO_UNIQUE_ADDRESS _Receiver __receiver_; 676 }; 677 678 template <class _Sender, class _Receiver, class _Queries> 679 struct __operation 680 { 681 using _Sigs = completion_signatures_of_t<_Sender>; 682 using __receiver_ref_t = __receiver_ref<_Sigs, _Queries>; 683 684 struct __rec 685 { 686 using is_receiver = void; 687 __operation_base<_Receiver, _Sigs, _Queries>* __op_; 688 689 template <__completion_tag _CPO, __decays_to<__rec> _Self, 690 class... _Args> 691 requires __callable<_CPO, _Receiver&&, _Args...> 692 friend void tag_invoke(_CPO, _Self&& __self, _Args&&... __args) noexcept 693 { 694 _CPO{}((_Receiver&&)__self.__op_->__receiver_, (_Args&&)__args...); 695 } 696 697 friend env_of_t<_Receiver> tag_invoke(get_env_t, 698 const __rec& __self) noexcept 699 { 700 return get_env(__self.__op_->__receiver_); 701 } 702 }; 703 704 class __t : __immovable, __operation_base<_Receiver, _Sigs, _Queries> 705 { 706 public: 707 using __id = __operation; 708 709 __t(_Sender&& __sender, _Receiver&& __receiver) : 710 __operation_base<_Receiver, _Sigs, _Queries>{ 711 (_Receiver&&)__receiver}, 712 __storage_{__sender.__connect(__receiver_ref_t{__rec_})} 713 {} 714 715 private: 716 __rec __rec_{ 717 static_cast<__operation_base<_Receiver, _Sigs, _Queries>*>(this)}; 718 __unique_operation_storage __storage_{}; 719 720 friend void tag_invoke(start_t, __t& __self) noexcept 721 { 722 STDEXEC_ASSERT(__self.__storage_.__get_vtable()->__start_); 723 __self.__storage_.__get_vtable()->__start_( 724 __self.__storage_.__get_object_pointer()); 725 } 726 }; 727 }; 728 729 template <class _Queries> 730 class __query_vtable; 731 732 template <template <class...> class _List, typename... _Queries> 733 class __query_vtable<_List<_Queries...>> : public __query_vfun<_Queries>... 734 { 735 public: 736 using __query_vfun<_Queries>::operator()...; 737 738 private: 739 template <class _EnvProvider> 740 requires(__callable<__query_vfun_fn<_EnvProvider>, _Queries> && ...) 741 friend const __query_vtable* tag_invoke(__create_vtable_t, 742 __mtype<__query_vtable>, 743 __mtype<_EnvProvider>) noexcept 744 { 745 static const __query_vtable __vtable{ 746 {__query_vfun_fn<_EnvProvider>{}((_Queries) nullptr)}...}; 747 return &__vtable; 748 } 749 }; 750 751 template <class _Sigs, class _SenderQueries = __types<>, 752 class _ReceiverQueries = __types<>> 753 struct __sender 754 { 755 using __receiver_ref_t = __receiver_ref<_Sigs, _ReceiverQueries>; 756 757 class __vtable : public __query_vtable<_SenderQueries> 758 { 759 public: 760 using __id = __vtable; 761 762 const __query_vtable<_SenderQueries>& __queries() const noexcept 763 { 764 return *this; 765 } 766 767 __unique_operation_storage (*__connect_)(void*, __receiver_ref_t); 768 769 private: 770 template <sender_to<__receiver_ref_t> _Sender> 771 friend const __vtable* tag_invoke(__create_vtable_t, __mtype<__vtable>, 772 __mtype<_Sender>) noexcept 773 { 774 static const __vtable __vtable_{ 775 {*__create_vtable(__mtype<__query_vtable<_SenderQueries>>{}, 776 __mtype<_Sender>{})}, 777 [](void* __object_pointer, 778 __receiver_ref_t __receiver) -> __unique_operation_storage { 779 _Sender& __sender = 780 *static_cast<_Sender*>(__object_pointer); 781 using __op_state_t = 782 connect_result_t<_Sender, __receiver_ref_t>; 783 return __unique_operation_storage{ 784 std::in_place_type<__op_state_t>, __conv{[&] { 785 return connect((_Sender&&)__sender, 786 (__receiver_ref_t&&)__receiver); 787 }}}; 788 }}; 789 return &__vtable_; 790 } 791 }; 792 793 class __env_t 794 { 795 public: 796 __env_t(const __vtable* __vtable, void* __sender) noexcept : 797 __vtable_{__vtable}, __sender_{__sender} 798 {} 799 800 private: 801 const __vtable* __vtable_; 802 void* __sender_; 803 804 template <class _Tag, class... _As> 805 requires __callable<const __query_vtable<_SenderQueries>&, _Tag, 806 void*, _As...> 807 friend auto 808 tag_invoke(_Tag, const __env_t& __self, _As&&... __as) noexcept( 809 __nothrow_callable<const __query_vtable<_SenderQueries>&, _Tag, 810 void*, _As...>) 811 -> __call_result_t<const __query_vtable<_SenderQueries>&, _Tag, 812 void*, _As...> 813 { 814 return __self.__vtable_->__queries()(_Tag{}, __self.__sender_, 815 (_As&&)__as...); 816 } 817 }; 818 819 class __t 820 { 821 public: 822 using __id = __sender; 823 using completion_signatures = _Sigs; 824 using is_sender = void; 825 826 __t(const __t&) = delete; 827 __t& operator=(const __t&) = delete; 828 829 __t(__t&&) = default; 830 __t& operator=(__t&&) = default; 831 832 template <__not_decays_to<__t> _Sender> 833 requires sender_to<_Sender, __receiver_ref<_Sigs, _ReceiverQueries>> 834 __t(_Sender&& __sndr) : __storage_{(_Sender&&)__sndr} 835 {} 836 837 __unique_operation_storage __connect(__receiver_ref_t __receiver) 838 { 839 return __storage_.__get_vtable()->__connect_( 840 __storage_.__get_object_pointer(), 841 (__receiver_ref_t&&)__receiver); 842 } 843 844 explicit operator bool() const noexcept 845 { 846 return __get_object_pointer(__storage_) != nullptr; 847 } 848 849 private: 850 __unique_storage_t<__vtable> __storage_; 851 852 template <receiver_of<_Sigs> _Rcvr> 853 friend stdexec::__t< 854 __operation<__t, __decay_t<_Rcvr>, _ReceiverQueries>> 855 tag_invoke(connect_t, __t&& __self, _Rcvr&& __rcvr) 856 { 857 return {(__t&&)__self, (_Rcvr&&)__rcvr}; 858 } 859 860 friend __env_t tag_invoke(get_env_t, const __t& __self) noexcept 861 { 862 return {__self.__storage_.__get_vtable(), 863 __self.__storage_.__get_object_pointer()}; 864 } 865 }; 866 }; 867 868 template <class _ScheduleSender, class _SchedulerQueries = __types<>> 869 class __scheduler 870 { 871 public: 872 template <class _Scheduler> 873 requires(!__decays_to<_Scheduler, __scheduler>) && scheduler<_Scheduler> 874 __scheduler(_Scheduler&& __scheduler) : 875 __storage_{(_Scheduler&&)__scheduler} 876 {} 877 878 using __sender_t = _ScheduleSender; 879 880 private: 881 class __vtable : public __query_vtable<_SchedulerQueries> 882 { 883 public: 884 __sender_t (*__schedule_)(void*) noexcept; 885 bool (*__equal_to_)(const void*, const void* other) noexcept; 886 887 const __query_vtable<_SchedulerQueries>& __queries() const noexcept 888 { 889 return *this; 890 } 891 892 private: 893 template <scheduler _Scheduler> 894 friend const __vtable* tag_invoke(__create_vtable_t, __mtype<__vtable>, 895 __mtype<_Scheduler>) noexcept 896 { 897 static const __vtable __vtable_{ 898 {*__create_vtable(__mtype<__query_vtable<_SchedulerQueries>>{}, 899 __mtype<_Scheduler>{})}, 900 [](void* __object_pointer) noexcept -> __sender_t { 901 const _Scheduler& __scheduler = 902 *static_cast<const _Scheduler*>(__object_pointer); 903 return __sender_t{schedule(__scheduler)}; 904 }, 905 [](const void* __self, const void* __other) noexcept -> bool { 906 static_assert(noexcept(std::declval<const _Scheduler&>() == 907 std::declval<const _Scheduler&>())); 908 STDEXEC_ASSERT(__self && __other); 909 const _Scheduler& __self_scheduler = 910 *static_cast<const _Scheduler*>(__self); 911 const _Scheduler& __other_scheduler = 912 *static_cast<const _Scheduler*>(__other); 913 return __self_scheduler == __other_scheduler; 914 }}; 915 return &__vtable_; 916 } 917 }; 918 919 template <same_as<__scheduler> _Self> 920 friend __sender_t tag_invoke(schedule_t, const _Self& __self) noexcept 921 { 922 STDEXEC_ASSERT(__self.__storage_.__get_vtable()->__schedule_); 923 return __self.__storage_.__get_vtable()->__schedule_( 924 __self.__storage_.__get_object_pointer()); 925 } 926 927 template <class _Tag, same_as<__scheduler> _Self, class... _As> 928 requires __callable<const __query_vtable<_SchedulerQueries>&, _Tag, 929 void*, _As...> 930 friend auto tag_invoke(_Tag, const _Self& __self, _As&&... __as) noexcept( 931 __nothrow_callable<const __query_vtable<_SchedulerQueries>&, _Tag, 932 void*, _As...>) 933 -> __call_result_t<const __query_vtable<_SchedulerQueries>&, _Tag, 934 void*, _As...> 935 { 936 return __self.__storage_.__get_vtable()->__queries()( 937 _Tag{}, __self.__storage_.__get_object_pointer(), (_As&&)__as...); 938 } 939 940 friend bool operator==(const __scheduler& __self, 941 const __scheduler& __other) noexcept 942 { 943 if (__self.__storage_.__get_vtable() != 944 __other.__storage_.__get_vtable()) 945 { 946 return false; 947 } 948 void* __p = __self.__storage_.__get_object_pointer(); 949 void* __o = __other.__storage_.__get_object_pointer(); 950 // if both object pointers are not null, use the virtual equal_to 951 // function 952 return (__p && __o && 953 __self.__storage_.__get_vtable()->__equal_to_(__p, __o)) 954 // if both object pointers are nullptrs, they are equal 955 || (!__p && !__o); 956 } 957 958 friend bool operator!=(const __scheduler& __self, 959 const __scheduler& __other) noexcept 960 { 961 return !(__self == __other); 962 } 963 964 __copyable_storage_t<__vtable> __storage_{}; 965 }; 966 } // namespace __any 967 968 template <auto... _Sigs> 969 using queries = stdexec::__types<decltype(_Sigs)...>; 970 971 template <class _Completions, auto... _ReceiverQueries> 972 class any_receiver_ref 973 { 974 using __receiver_base = 975 __any::__rec::__ref<_Completions, decltype(_ReceiverQueries)...>; 976 using __env_t = stdexec::env_of_t<__receiver_base>; 977 __receiver_base __receiver_; 978 979 template <class _Tag, stdexec::__decays_to<any_receiver_ref> Self, 980 class... _As> 981 requires stdexec::tag_invocable< 982 _Tag, stdexec::__copy_cvref_t<Self, __receiver_base>, _As...> 983 friend auto tag_invoke(_Tag, Self&& __self, _As&&... __as) noexcept( 984 std::is_nothrow_invocable_v< 985 _Tag, stdexec::__copy_cvref_t<Self, __receiver_base>, _As...>) 986 { 987 return tag_invoke(_Tag{}, ((Self&&)__self).__receiver_, (_As&&)__as...); 988 } 989 990 public: 991 using is_receiver = void; 992 using __t = any_receiver_ref; 993 using __id = any_receiver_ref; 994 995 template <stdexec::__none_of<any_receiver_ref, const any_receiver_ref, 996 __env_t, const __env_t> 997 _Receiver> 998 requires stdexec::receiver_of<_Receiver, _Completions> 999 any_receiver_ref(_Receiver& __receiver) noexcept( 1000 stdexec::__nothrow_constructible_from<__receiver_base, _Receiver>) : 1001 __receiver_(__receiver) 1002 {} 1003 1004 template <auto... _SenderQueries> 1005 class any_sender 1006 { 1007 using __sender_base = stdexec::__t< 1008 __any::__sender<_Completions, queries<_SenderQueries...>, 1009 queries<_ReceiverQueries...>>>; 1010 __sender_base __sender_; 1011 1012 template <class _Tag, stdexec::__decays_to<any_sender> Self, 1013 class... _As> 1014 requires stdexec::tag_invocable< 1015 _Tag, stdexec::__copy_cvref_t<Self, __sender_base>, _As...> 1016 friend auto tag_invoke(_Tag, Self&& __self, _As&&... __as) noexcept( 1017 std::is_nothrow_invocable_v< 1018 _Tag, stdexec::__copy_cvref_t<Self, __sender_base>, _As...>) 1019 { 1020 return tag_invoke(_Tag{}, ((Self&&)__self).__sender_, 1021 (_As&&)__as...); 1022 } 1023 1024 public: 1025 using is_sender = void; 1026 using completion_signatures = 1027 typename __sender_base::completion_signatures; 1028 1029 template <class _Sender> 1030 requires(!stdexec::__decays_to<_Sender, any_sender>) && 1031 stdexec::sender<_Sender> 1032 any_sender(_Sender&& __sender) noexcept( 1033 stdexec::__nothrow_constructible_from<__sender_base, _Sender>) : 1034 __sender_((_Sender&&)__sender) 1035 {} 1036 1037 template <auto... _SchedulerQueries> 1038 class any_scheduler 1039 { 1040 using __schedule_completions = 1041 stdexec::__concat_completion_signatures_t< 1042 _Completions, 1043 stdexec::completion_signatures<stdexec::set_value_t()>>; 1044 using __schedule_receiver = 1045 any_receiver_ref<__schedule_completions, _ReceiverQueries...>; 1046 1047 template <typename _Tag, typename _Sig> 1048 static _Tag __ret_fn(_Tag (*const)(_Sig)); 1049 1050 template <class _Tag> 1051 struct __ret_equals_to 1052 { 1053 template <class _Sig> 1054 using __f = 1055 std::is_same<_Tag, decltype(__ret_fn((_Sig) nullptr))>; 1056 }; 1057 1058 using schedule_sender_queries = stdexec::__minvoke< 1059 stdexec::__remove_if<__ret_equals_to< 1060 stdexec::get_completion_scheduler_t<stdexec::set_value_t>>>, 1061 decltype(_SenderQueries)...>; 1062 1063 template <class... _Queries> 1064 using __schedule_sender_fn = typename __schedule_receiver::template any_sender< 1065 stdexec::get_completion_scheduler<stdexec::set_value_t>.template signature<any_scheduler() noexcept>>; 1066 using __schedule_sender = 1067 stdexec::__mapply<stdexec::__q<__schedule_sender_fn>, 1068 schedule_sender_queries>; 1069 1070 using __scheduler_base = 1071 __any::__scheduler<__schedule_sender, 1072 queries<_SchedulerQueries...>>; 1073 1074 __scheduler_base __scheduler_; 1075 1076 public: 1077 using __t = any_scheduler; 1078 using __id = any_scheduler; 1079 1080 template <class _Scheduler> 1081 requires(!stdexec::__decays_to<_Scheduler, any_scheduler> && 1082 stdexec::scheduler<_Scheduler>) 1083 any_scheduler(_Scheduler&& __scheduler) : 1084 __scheduler_{(_Scheduler&&)__scheduler} 1085 {} 1086 1087 private: 1088 template <class _Tag, stdexec::__decays_to<any_scheduler> Self, 1089 class... _As> 1090 requires stdexec::tag_invocable< 1091 _Tag, stdexec::__copy_cvref_t<Self, __scheduler_base>, 1092 _As...> 1093 friend auto tag_invoke(_Tag, Self&& __self, _As&&... __as) noexcept( 1094 std::is_nothrow_invocable_v< 1095 _Tag, stdexec::__copy_cvref_t<Self, __scheduler_base>, 1096 _As...>) 1097 { 1098 return tag_invoke(_Tag{}, ((Self&&)__self).__scheduler_, 1099 (_As&&)__as...); 1100 } 1101 1102 friend bool 1103 operator==(const any_scheduler& __self, 1104 const any_scheduler& __other) noexcept = default; 1105 }; 1106 }; 1107 }; 1108 } // namespace exec 1109