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 "../stdexec/concepts.hpp" 19 #include "../stdexec/execution.hpp" 20 #include "../stdexec/functional.hpp" 21 22 #include <sdbusplus/async/stdexec/sequence_senders.hpp> 23 24 #include <cstddef> 25 #include <utility> 26 27 namespace exec 28 { 29 namespace __any 30 { 31 using namespace stdexec; 32 33 struct __create_vtable_t 34 { 35 template <class _VTable, class _Tp> 36 requires __tag_invocable_r<const _VTable*, __create_vtable_t, 37 __mtype<_VTable>, __mtype<_Tp>> operator ()exec::__any::__create_vtable_t38 constexpr auto operator()(__mtype<_VTable>, __mtype<_Tp>) const noexcept 39 -> const _VTable* 40 { 41 return stdexec::tag_invoke(__create_vtable_t{}, __mtype<_VTable>{}, 42 __mtype<_Tp>{}); 43 } 44 }; 45 46 inline constexpr __create_vtable_t __create_vtable{}; 47 48 template <class _Sig> 49 struct __query_vfun; 50 51 template <class _Tag, class _Ret, class... _As> 52 struct __query_vfun<_Tag (*const)(_Ret (*)(_As...))> 53 { 54 _Ret (*__fn_)(void*, _As...); 55 operator ()exec::__any::__query_vfun56 auto operator()(_Tag, void* __rcvr, _As&&... __as) const -> _Ret 57 { 58 return __fn_(__rcvr, static_cast<_As&&>(__as)...); 59 } 60 }; 61 62 template <class _Tag, class _Ret, class... _As> 63 struct __query_vfun<_Tag (*)(_Ret (*)(_As...))> 64 { 65 _Ret (*__fn_)(void*, _As...); 66 operator ()exec::__any::__query_vfun67 auto operator()(_Tag, void* __rcvr, _As&&... __as) const -> _Ret 68 { 69 return __fn_(__rcvr, static_cast<_As&&>(__as)...); 70 } 71 }; 72 73 template <class _Tag, class _Ret, class... _As> 74 struct __query_vfun<_Tag (*const)(_Ret (*)(_As...) noexcept)> 75 { 76 _Ret (*__fn_)(void*, _As...) noexcept; 77 operator ()exec::__any::__query_vfun78 auto operator()(_Tag, void* __rcvr, _As&&... __as) const noexcept -> _Ret 79 { 80 return __fn_(__rcvr, static_cast<_As&&>(__as)...); 81 } 82 }; 83 84 template <class _Tag, class _Ret, class... _As> 85 struct __query_vfun<_Tag (*)(_Ret (*)(_As...) noexcept)> 86 { 87 _Ret (*__fn_)(void*, _As...) noexcept; 88 operator ()exec::__any::__query_vfun89 auto operator()(_Tag, void* __rcvr, _As&&... __as) const noexcept -> _Ret 90 { 91 return __fn_(__rcvr, static_cast<_As&&>(__as)...); 92 } 93 }; 94 95 template <class _Queryable, bool _IsEnvProvider = true> 96 struct __query_vfun_fn; 97 98 template <class _EnvProvider> 99 struct __query_vfun_fn<_EnvProvider, true> 100 { 101 template <class _Tag, class _Ret, class... _As> 102 requires __callable<_Tag, env_of_t<const _EnvProvider&>, _As...> 103 constexpr _Ret (*operator()(_Tag (*)(_Ret (*)(_As...))) 104 const noexcept)(void*, _As...) 105 { __anon1bf2f58b0102exec::__any::__query_vfun_fn106 return +[](void* __env_provider, _As... __as) -> _Ret { 107 return _Tag{}(stdexec::get_env(*static_cast<const _EnvProvider*>( 108 __env_provider)), 109 static_cast<_As&&>(__as)...); 110 }; 111 } 112 113 template <class _Tag, class _Ret, class... _As> 114 requires __callable<_Tag, env_of_t<const _EnvProvider&>, _As...> 115 constexpr _Ret (*operator()(_Tag (*)(_Ret (*)(_As...) noexcept)) 116 const noexcept)(void*, _As...) noexcept 117 { __anon1bf2f58b0202exec::__any::__query_vfun_fn118 return +[](void* __env_provider, _As... __as) noexcept -> _Ret { 119 static_assert( 120 __nothrow_callable<_Tag, const env_of_t<_EnvProvider>&, 121 _As...>); 122 return _Tag{}(stdexec::get_env(*static_cast<const _EnvProvider*>( 123 __env_provider)), 124 static_cast<_As&&>(__as)...); 125 }; 126 } 127 }; 128 129 template <class _Queryable> 130 struct __query_vfun_fn<_Queryable, false> 131 { 132 template <class _Tag, class _Ret, class... _As> 133 requires __callable<_Tag, const _Queryable&, _As...> 134 constexpr _Ret (*operator()(_Tag (*)(_Ret (*)(_As...))) 135 const noexcept)(void*, _As...) 136 { __anon1bf2f58b0302exec::__any::__query_vfun_fn137 return +[](void* __queryable, _As... __as) -> _Ret { 138 return _Tag{}(*static_cast<const _Queryable*>(__queryable), 139 static_cast<_As&&>(__as)...); 140 }; 141 } 142 143 template <class _Tag, class _Ret, class... _As> 144 requires __callable<_Tag, const _Queryable&, _As...> 145 constexpr _Ret (*operator()(_Tag (*)(_Ret (*)(_As...) noexcept)) 146 const noexcept)(void*, _As...) noexcept 147 { __anon1bf2f58b0402exec::__any::__query_vfun_fn148 return +[](void* __env_provider, _As... __as) noexcept -> _Ret { 149 static_assert(__nothrow_callable<_Tag, const _Queryable&, _As...>); 150 return _Tag{}(*static_cast<const _Queryable*>(__env_provider), 151 static_cast<_As&&>(__as)...); 152 }; 153 } 154 }; 155 156 template <class _Sig> 157 struct __storage_vfun; 158 159 template <class _Tag, class... _As> 160 struct __storage_vfun<_Tag(void (*)(_As...))> 161 { __anon1bf2f58b0502exec::__any::__storage_vfun162 void (*__fn_)(void*, _As...) = [](void*, _As...) {}; 163 operator ()exec::__any::__storage_vfun164 void operator()(_Tag, void* __storage, _As&&... __as) const 165 { 166 return __fn_(__storage, static_cast<_As&&>(__as)...); 167 } 168 }; 169 170 template <class _Tag, class... _As> 171 struct __storage_vfun<_Tag(void (*)(_As...) noexcept)> 172 { __anon1bf2f58b0602exec::__any::__storage_vfun173 void (*__fn_)(void*, _As...) noexcept = [](void*, _As...) noexcept {}; 174 operator ()exec::__any::__storage_vfun175 void operator()(_Tag, void* __storage, _As&&... __as) const noexcept 176 { 177 return __fn_(__storage, static_cast<_As&&>(__as)...); 178 } 179 }; 180 181 template <class _Storage, class _Tp> 182 struct __storage_vfun_fn 183 { 184 template <class _Tag, class... _As> 185 requires __callable<_Tag, __mtype<_Tp>, _Storage&, _As...> 186 constexpr void (*operator()(_Tag (*)(void (*)(_As...))) 187 const noexcept)(void*, _As...) 188 { __anon1bf2f58b0702exec::__any::__storage_vfun_fn189 return +[](void* __storage, _As... __as) -> void { 190 return _Tag{}(__mtype<_Tp>{}, *static_cast<_Storage*>(__storage), 191 static_cast<_As&&>(__as)...); 192 }; 193 } 194 195 template <class _Tag, class... _As> 196 requires __callable<_Tag, __mtype<_Tp>, _Storage&, _As...> 197 constexpr void (*operator()(_Tag (*)(void (*)(_As...) noexcept)) 198 const noexcept)(void*, _As...) noexcept 199 { __anon1bf2f58b0802exec::__any::__storage_vfun_fn200 return +[](void* __storage, _As... __as) noexcept -> void { 201 static_assert( 202 __nothrow_callable<_Tag, __mtype<_Tp>, _Storage&, _As...>); 203 return _Tag{}(__mtype<_Tp>{}, *static_cast<_Storage*>(__storage), 204 static_cast<_As&&>(__as)...); 205 }; 206 } 207 }; 208 209 struct __delete_t 210 { 211 template <class _Storage, class _Tp> 212 requires tag_invocable<__delete_t, __mtype<_Tp>, _Storage&> operator ()exec::__any::__delete_t213 void operator()(__mtype<_Tp>, _Storage& __storage) noexcept 214 { 215 static_assert( 216 nothrow_tag_invocable<__delete_t, __mtype<_Tp>, _Storage&>); 217 stdexec::tag_invoke(__delete_t{}, __mtype<_Tp>{}, __storage); 218 } 219 }; 220 221 inline constexpr __delete_t __delete{}; 222 223 struct __copy_construct_t 224 { 225 template <class _Storage, class _Tp> 226 requires tag_invocable<__copy_construct_t, __mtype<_Tp>, _Storage&, 227 const _Storage&> operator ()exec::__any::__copy_construct_t228 void operator()(__mtype<_Tp>, _Storage& __self, const _Storage& __from) // 229 noexcept(nothrow_tag_invocable<__copy_construct_t, __mtype<_Tp>, 230 _Storage&, const _Storage&>) 231 { 232 stdexec::tag_invoke(__copy_construct_t{}, __mtype<_Tp>{}, __self, 233 __from); 234 } 235 }; 236 237 inline constexpr __copy_construct_t __copy_construct{}; 238 239 struct __move_construct_t 240 { 241 template <class _Storage, class _Tp> 242 requires tag_invocable<__move_construct_t, __mtype<_Tp>, _Storage&, 243 _Storage&&> operator ()exec::__any::__move_construct_t244 void operator()(__mtype<_Tp>, _Storage& __self, 245 __midentity<_Storage&&> __from) noexcept 246 { 247 static_assert(nothrow_tag_invocable<__move_construct_t, __mtype<_Tp>, 248 _Storage&, _Storage&&>); 249 stdexec::tag_invoke(__move_construct_t{}, __mtype<_Tp>{}, __self, 250 static_cast<_Storage&&>(__from)); 251 } 252 }; 253 254 inline constexpr __move_construct_t __move_construct{}; 255 256 template <class _ParentVTable, class... _StorageCPOs> 257 struct __storage_vtable; 258 259 template <class _ParentVTable, class... _StorageCPOs> 260 requires requires { _ParentVTable::operator(); } 261 struct __storage_vtable<_ParentVTable, _StorageCPOs...> : 262 _ParentVTable, 263 __storage_vfun<_StorageCPOs>... 264 { 265 using _ParentVTable::operator(); 266 using __storage_vfun<_StorageCPOs>::operator()...; 267 }; 268 269 template <class _ParentVTable, class... _StorageCPOs> 270 requires(!requires { _ParentVTable::operator(); }) 271 struct __storage_vtable<_ParentVTable, _StorageCPOs...> : 272 _ParentVTable, 273 __storage_vfun<_StorageCPOs>... 274 { 275 using __storage_vfun<_StorageCPOs>::operator()...; 276 }; 277 278 template <class _ParentVTable, class... _StorageCPOs> 279 inline constexpr __storage_vtable<_ParentVTable, _StorageCPOs...> 280 __null_storage_vtbl{}; 281 282 template <class _ParentVTable, class... _StorageCPOs> __default_storage_vtable(__storage_vtable<_ParentVTable,_StorageCPOs...> *)283 constexpr auto __default_storage_vtable( 284 __storage_vtable<_ParentVTable, _StorageCPOs...>*) noexcept 285 -> const __storage_vtable<_ParentVTable, _StorageCPOs...>* 286 { 287 return &__null_storage_vtbl<_ParentVTable, _StorageCPOs...>; 288 } 289 290 template <class _Storage, class _Tp, class _ParentVTable, class... _StorageCPOs> 291 static const __storage_vtable<_ParentVTable, _StorageCPOs...> __storage_vtbl{ 292 {*__create_vtable(__mtype<_ParentVTable>{}, __mtype<_Tp>{})}, 293 {__storage_vfun_fn<_Storage, _Tp>{}( 294 static_cast<_StorageCPOs*>(nullptr))}...}; 295 296 template <class _Vtable, class _Allocator, bool _Copyable = false, 297 std::size_t _InlineSize = 3 * sizeof(void*), 298 std::size_t _Alignment = alignof(std::max_align_t)> 299 struct __storage 300 { 301 class __t; 302 }; 303 304 template <class _Vtable, class _Allocator, 305 std::size_t _InlineSize = 3 * sizeof(void*), 306 std::size_t _Alignment = alignof(std::max_align_t)> 307 struct __immovable_storage 308 { 309 class __t : __immovable 310 { 311 static constexpr std::size_t __buffer_size = 312 std::max(_InlineSize, sizeof(void*)); 313 static constexpr std::size_t __alignment = 314 std::max(_Alignment, alignof(void*)); 315 using __with_delete = __delete_t(void() noexcept); 316 using __vtable_t = __storage_vtable<_Vtable, __with_delete>; 317 318 template <class _Tp> 319 static constexpr bool __is_small = 320 sizeof(_Tp) <= __buffer_size && alignof(_Tp) <= __alignment; 321 322 template <class _Tp> __get_vtable_of_type()323 static constexpr auto __get_vtable_of_type() noexcept 324 -> const __vtable_t* 325 { 326 return &__storage_vtbl<__t, __decay_t<_Tp>, _Vtable, __with_delete>; 327 } 328 329 public: 330 using __id = __immovable_storage; 331 332 __t() = default; 333 334 template <__not_decays_to<__t> _Tp> 335 requires __callable<__create_vtable_t, __mtype<_Vtable>, 336 __mtype<__decay_t<_Tp>>> __t(_Tp && __object)337 __t(_Tp&& __object) : __vtable_{__get_vtable_of_type<_Tp>()} 338 { 339 using _Dp = __decay_t<_Tp>; 340 if constexpr (__is_small<_Dp>) 341 { 342 __construct_small<_Dp>(static_cast<_Tp&&>(__object)); 343 } 344 else 345 { 346 __construct_large<_Dp>(static_cast<_Tp&&>(__object)); 347 } 348 } 349 350 template <class _Tp, class... _Args> 351 requires __callable<__create_vtable_t, __mtype<_Vtable>, 352 __mtype<_Tp>> __t(std::in_place_type_t<_Tp>,_Args &&...__args)353 __t(std::in_place_type_t<_Tp>, _Args&&... __args) : 354 __vtable_{__get_vtable_of_type<_Tp>()} 355 { 356 if constexpr (__is_small<_Tp>) 357 { 358 __construct_small<_Tp>(static_cast<_Args&&>(__args)...); 359 } 360 else 361 { 362 __construct_large<_Tp>(static_cast<_Args&&>(__args)...); 363 } 364 } 365 ~__t()366 ~__t() 367 { 368 __reset(); 369 } 370 __reset()371 void __reset() noexcept 372 { 373 (*__vtable_)(__delete, this); 374 __object_pointer_ = nullptr; 375 __vtable_ = 376 __default_storage_vtable(static_cast<__vtable_t*>(nullptr)); 377 } 378 __get_vtable() const379 [[nodiscard]] auto __get_vtable() const noexcept -> const _Vtable* 380 { 381 return __vtable_; 382 } 383 __get_object_pointer() const384 [[nodiscard]] auto __get_object_pointer() const noexcept -> void* 385 { 386 return __object_pointer_; 387 } 388 389 private: 390 template <class _Tp, class... _As> __construct_small(_As &&...__args)391 void __construct_small(_As&&... __args) 392 { 393 static_assert(sizeof(_Tp) <= __buffer_size && 394 alignof(_Tp) <= __alignment); 395 _Tp* __pointer = 396 static_cast<_Tp*>(static_cast<void*>(&__buffer_[0])); 397 using _Alloc = typename std::allocator_traits< 398 _Allocator>::template rebind_alloc<_Tp>; 399 _Alloc __alloc{__allocator_}; 400 std::allocator_traits<_Alloc>::construct( 401 __alloc, __pointer, static_cast<_As&&>(__args)...); 402 __object_pointer_ = __pointer; 403 } 404 405 template <class _Tp, class... _As> __construct_large(_As &&...__args)406 void __construct_large(_As&&... __args) 407 { 408 using _Alloc = typename std::allocator_traits< 409 _Allocator>::template rebind_alloc<_Tp>; 410 _Alloc __alloc{__allocator_}; 411 _Tp* __pointer = 412 std::allocator_traits<_Alloc>::allocate(__alloc, 1); 413 try 414 { 415 std::allocator_traits<_Alloc>::construct( 416 __alloc, __pointer, static_cast<_As&&>(__args)...); 417 } 418 catch (...) 419 { 420 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 421 1); 422 throw; 423 } 424 __object_pointer_ = __pointer; 425 } 426 427 template <class _Tp> STDEXEC_MEMFN_DECL(void __delete)428 STDEXEC_MEMFN_DECL(void __delete)(this __mtype<_Tp>, 429 __t& __self) noexcept 430 { 431 if (!__self.__object_pointer_) 432 { 433 return; 434 } 435 using _Alloc = typename std::allocator_traits< 436 _Allocator>::template rebind_alloc<_Tp>; 437 _Alloc __alloc{__self.__allocator_}; 438 _Tp* __pointer = static_cast<_Tp*>( 439 std::exchange(__self.__object_pointer_, nullptr)); 440 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer); 441 if constexpr (!__is_small<_Tp>) 442 { 443 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 444 1); 445 } 446 } 447 448 private: 449 const __vtable_t* __vtable_{ 450 __default_storage_vtable(static_cast<__vtable_t*>(nullptr))}; 451 void* __object_pointer_{nullptr}; 452 alignas(__alignment) std::byte __buffer_[__buffer_size]{}; 453 STDEXEC_ATTRIBUTE((no_unique_address)) 454 _Allocator __allocator_{}; 455 }; 456 }; 457 458 template <class _Vtable, class _Allocator, bool _Copyable, 459 std::size_t _InlineSize, std::size_t _Alignment> 460 class __storage<_Vtable, _Allocator, _Copyable, _InlineSize, _Alignment>::__t : 461 __if_c<_Copyable, __, __move_only> 462 { 463 static_assert(STDEXEC_IS_CONVERTIBLE_TO( 464 typename std::allocator_traits<_Allocator>::void_pointer, void*)); 465 466 static constexpr std::size_t __buffer_size = 467 std::max(_InlineSize, sizeof(void*)); 468 static constexpr std::size_t __alignment = 469 std::max(_Alignment, alignof(void*)); 470 using __with_copy = __copy_construct_t(void(const __t&)); 471 using __with_move = __move_construct_t(void(__t&&) noexcept); 472 using __with_delete = __delete_t(void() noexcept); 473 474 template <class _Tp> 475 static constexpr bool __is_small = 476 sizeof(_Tp) <= __buffer_size && alignof(_Tp) <= __alignment && 477 std::is_nothrow_move_constructible_v<_Tp>; 478 479 using __vtable_t = __if_c< 480 _Copyable, 481 __storage_vtable<_Vtable, __with_delete, __with_move, __with_copy>, 482 __storage_vtable<_Vtable, __with_delete, __with_move>>; 483 484 template <class _Tp> __get_vtable_of_type()485 static constexpr auto __get_vtable_of_type() noexcept -> const __vtable_t* 486 { 487 if constexpr (_Copyable) 488 { 489 return &__storage_vtbl<__t, __decay_t<_Tp>, _Vtable, __with_delete, 490 __with_move, __with_copy>; 491 } 492 else 493 { 494 return &__storage_vtbl<__t, __decay_t<_Tp>, _Vtable, __with_delete, 495 __with_move>; 496 } 497 } 498 499 public: 500 using __id = __storage; 501 502 __t() = default; 503 504 template <__not_decays_to<__t> _Tp> 505 requires __callable<__create_vtable_t, __mtype<_Vtable>, 506 __mtype<__decay_t<_Tp>>> __t(_Tp && __object)507 __t(_Tp&& __object) : __vtable_{__get_vtable_of_type<_Tp>()} 508 { 509 using _Dp = __decay_t<_Tp>; 510 if constexpr (__is_small<_Dp>) 511 { 512 __construct_small<_Dp>(static_cast<_Tp&&>(__object)); 513 } 514 else 515 { 516 __construct_large<_Dp>(static_cast<_Tp&&>(__object)); 517 } 518 } 519 520 template <class _Tp, class... _Args> 521 requires __callable<__create_vtable_t, __mtype<_Vtable>, __mtype<_Tp>> __t(std::in_place_type_t<_Tp>,_Args &&...__args)522 __t(std::in_place_type_t<_Tp>, _Args&&... __args) : 523 __vtable_{__get_vtable_of_type<_Tp>()} 524 { 525 if constexpr (__is_small<_Tp>) 526 { 527 __construct_small<_Tp>(static_cast<_Args&&>(__args)...); 528 } 529 else 530 { 531 __construct_large<_Tp>(static_cast<_Args&&>(__args)...); 532 } 533 } 534 __t(const __t & __other)535 __t(const __t& __other) 536 requires(_Copyable) 537 { 538 (*__other.__vtable_)(__copy_construct, this, __other); 539 } 540 operator =(const __t & __other)541 auto operator=(const __t& __other) 542 -> __t& requires(_Copyable) { 543 if (&__other != this) 544 { 545 __t tmp(__other); 546 *this = std::move(tmp); 547 } 548 return *this; 549 } 550 __t(__t && __other)551 __t(__t&& __other) noexcept 552 { 553 (*__other.__vtable_)(__move_construct, this, 554 static_cast<__t&&>(__other)); 555 } 556 operator =(__t && __other)557 auto operator=(__t&& __other) noexcept -> __t& 558 { 559 __reset(); 560 (*__other.__vtable_)(__move_construct, this, 561 static_cast<__t&&>(__other)); 562 return *this; 563 } 564 ~__t()565 ~__t() 566 { 567 __reset(); 568 } 569 __reset()570 void __reset() noexcept 571 { 572 (*__vtable_)(__delete, this); 573 __object_pointer_ = nullptr; 574 __vtable_ = __default_storage_vtable(static_cast<__vtable_t*>(nullptr)); 575 } 576 __get_vtable() const577 auto __get_vtable() const noexcept -> const _Vtable* 578 { 579 return __vtable_; 580 } 581 __get_object_pointer() const582 [[nodiscard]] auto __get_object_pointer() const noexcept -> void* 583 { 584 return __object_pointer_; 585 } 586 587 private: 588 template <class _Tp, class... _As> __construct_small(_As &&...__args)589 void __construct_small(_As&&... __args) 590 { 591 static_assert(sizeof(_Tp) <= __buffer_size && 592 alignof(_Tp) <= __alignment); 593 _Tp* __pointer = static_cast<_Tp*>(static_cast<void*>(&__buffer_[0])); 594 using _Alloc = typename std::allocator_traits< 595 _Allocator>::template rebind_alloc<_Tp>; 596 _Alloc __alloc{__allocator_}; 597 std::allocator_traits<_Alloc>::construct(__alloc, __pointer, 598 static_cast<_As&&>(__args)...); 599 __object_pointer_ = __pointer; 600 } 601 602 template <class _Tp, class... _As> __construct_large(_As &&...__args)603 void __construct_large(_As&&... __args) 604 { 605 using _Alloc = typename std::allocator_traits< 606 _Allocator>::template rebind_alloc<_Tp>; 607 _Alloc __alloc{__allocator_}; 608 _Tp* __pointer = std::allocator_traits<_Alloc>::allocate(__alloc, 1); 609 try 610 { 611 std::allocator_traits<_Alloc>::construct( 612 __alloc, __pointer, static_cast<_As&&>(__args)...); 613 } 614 catch (...) 615 { 616 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1); 617 throw; 618 } 619 __object_pointer_ = __pointer; 620 } 621 622 template <class _Tp> STDEXEC_MEMFN_DECL(void __delete)623 STDEXEC_MEMFN_DECL(void __delete)(this __mtype<_Tp>, __t& __self) noexcept 624 { 625 if (!__self.__object_pointer_) 626 { 627 return; 628 } 629 using _Alloc = typename std::allocator_traits< 630 _Allocator>::template rebind_alloc<_Tp>; 631 _Alloc __alloc{__self.__allocator_}; 632 _Tp* __pointer = 633 static_cast<_Tp*>(std::exchange(__self.__object_pointer_, nullptr)); 634 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer); 635 if constexpr (!__is_small<_Tp>) 636 { 637 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1); 638 } 639 } 640 641 template <class _Tp> STDEXEC_MEMFN_DECL(void __move_construct)642 STDEXEC_MEMFN_DECL(void __move_construct)(this __mtype<_Tp>, __t& __self, 643 __t&& __other) noexcept 644 { 645 if (!__other.__object_pointer_) 646 { 647 return; 648 } 649 _Tp* __pointer = static_cast<_Tp*>( 650 std::exchange(__other.__object_pointer_, nullptr)); 651 if constexpr (__is_small<_Tp>) 652 { 653 _Tp& __other_object = *__pointer; 654 __self.template __construct_small<_Tp>( 655 static_cast<_Tp&&>(__other_object)); 656 using _Alloc = typename std::allocator_traits< 657 _Allocator>::template rebind_alloc<_Tp>; 658 _Alloc __alloc{__self.__allocator_}; 659 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer); 660 } 661 else 662 { 663 __self.__object_pointer_ = __pointer; 664 } 665 __self.__vtable_ = std::exchange( 666 __other.__vtable_, 667 __default_storage_vtable(static_cast<__vtable_t*>(nullptr))); 668 } 669 670 template <class _Tp> 671 requires _Copyable STDEXEC_MEMFN_DECL(void __copy_construct)672 STDEXEC_MEMFN_DECL(void __copy_construct)(this __mtype<_Tp>, __t& __self, 673 const __t& __other) 674 { 675 if (!__other.__object_pointer_) 676 { 677 return; 678 } 679 const _Tp& __other_object = 680 *static_cast<const _Tp*>(__other.__object_pointer_); 681 if constexpr (__is_small<_Tp>) 682 { 683 __self.template __construct_small<_Tp>(__other_object); 684 } 685 else 686 { 687 __self.template __construct_large<_Tp>(__other_object); 688 } 689 __self.__vtable_ = __other.__vtable_; 690 } 691 692 const __vtable_t* __vtable_{ 693 __default_storage_vtable(static_cast<__vtable_t*>(nullptr))}; 694 void* __object_pointer_{nullptr}; 695 alignas(__alignment) std::byte __buffer_[__buffer_size]{}; 696 STDEXEC_ATTRIBUTE((no_unique_address)) 697 _Allocator __allocator_{}; 698 }; 699 700 struct __empty_vtable 701 { 702 template <class _Sender> STDEXEC_MEMFN_DECLexec::__any::__empty_vtable703 STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__empty_vtable>, 704 __mtype<_Sender>) noexcept 705 -> const __empty_vtable* 706 { 707 static const __empty_vtable __vtable_{}; 708 return &__vtable_; 709 } 710 }; 711 712 template <class _VTable = __empty_vtable, 713 class _Allocator = std::allocator<std::byte>> 714 using __immovable_storage_t = __t<__immovable_storage<_VTable, _Allocator>>; 715 716 template <class _VTable, class _Allocator = std::allocator<std::byte>> 717 using __unique_storage_t = __t<__storage<_VTable, _Allocator>>; 718 719 template <class _VTable, std::size_t _InlineSize = 3 * sizeof(void*), 720 class _Allocator = std::allocator<std::byte>> 721 using __copyable_storage_t = 722 __t<__storage<_VTable, _Allocator, true, _InlineSize>>; 723 724 template <class _Tag, class... _As> 725 auto __tag_type(_Tag (*)(_As...)) -> _Tag; 726 727 template <class _Tag, class... _As> 728 auto __tag_type(_Tag (*)(_As...) noexcept) -> _Tag; 729 730 template <class _Query> 731 concept __is_stop_token_query = requires { 732 { 733 __tag_type(static_cast<_Query>(nullptr)) 734 } -> same_as<get_stop_token_t>; 735 }; 736 737 template <class _Query> 738 concept __is_not_stop_token_query = !__is_stop_token_query<_Query>; 739 740 template <class _Query> 741 using __is_not_stop_token_query_v = __mbool<__is_not_stop_token_query<_Query>>; 742 743 namespace __rec 744 { 745 template <class _Sigs, class... _Queries> 746 struct __vtable 747 { 748 class __t; 749 }; 750 751 template <class _Sigs, class... _Queries> 752 struct __ref; 753 754 template <class... _Sigs, class... _Queries> 755 struct __vtable<completion_signatures<_Sigs...>, _Queries...> 756 { 757 class __t : 758 public __any_::__rcvr_vfun<_Sigs>..., 759 public __query_vfun<_Queries>... 760 { 761 public: 762 using __query_vfun<_Queries>::operator()...; 763 764 private: 765 template <class _Rcvr> 766 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> && 767 (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...) STDEXEC_MEMFN_DECL(auto __create_vtable)768 STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__t>, 769 __mtype<_Rcvr>) noexcept 770 -> const __t* 771 { 772 static const __t __vtable_{ 773 {__any_::__rcvr_vfun_fn(static_cast<_Rcvr*>(nullptr), 774 static_cast<_Sigs*>(nullptr))}..., 775 {__query_vfun_fn<_Rcvr>{}(static_cast<_Queries>(nullptr))}...}; 776 return &__vtable_; 777 } 778 }; 779 }; 780 781 template <class... _Sigs, class... _Queries> 782 requires(__is_not_stop_token_query<_Queries> && ...) 783 struct __ref<completion_signatures<_Sigs...>, _Queries...> 784 { 785 #if !STDEXEC_MSVC() 786 // MSVCBUG 787 // https://developercommunity.visualstudio.com/t/Private-member-inaccessible-when-used-in/10448363 788 789 private: 790 #endif 791 using __vtable_t = 792 stdexec::__t<__vtable<completion_signatures<_Sigs...>, _Queries...>>; 793 794 struct __env_t 795 { 796 const __vtable_t* __vtable_; 797 void* __rcvr_; 798 inplace_stop_token __token_; 799 800 template <class _Tag, class... _As> 801 requires __callable<const __vtable_t&, _Tag, void*, _As...> queryexec::__any::__rec::__ref::__env_t802 auto query(_Tag, _As&&... __as) const // 803 noexcept(__nothrow_callable<const __vtable_t&, _Tag, void*, _As...>) 804 -> __call_result_t<const __vtable_t&, _Tag, void*, _As...> 805 { 806 return (*__vtable_)(_Tag{}, __rcvr_, static_cast<_As&&>(__as)...); 807 } 808 queryexec::__any::__rec::__ref::__env_t809 auto query(get_stop_token_t) const noexcept -> inplace_stop_token 810 { 811 return __token_; 812 } 813 } __env_; 814 815 public: 816 using receiver_concept = stdexec::receiver_t; 817 using __id = __ref; 818 using __t = __ref; 819 820 template <__none_of<__ref, const __ref, __env_t, const __env_t> _Rcvr> 821 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> && 822 (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...) __refexec::__any::__rec::__ref823 __ref(_Rcvr& __rcvr) noexcept : 824 __env_{__create_vtable(__mtype<__vtable_t>{}, __mtype<_Rcvr>{}), 825 &__rcvr, stdexec::get_stop_token(stdexec::get_env(__rcvr))} 826 {} 827 828 template <class... _As> 829 requires __one_of<set_value_t(_As...), _Sigs...> set_valueexec::__any::__rec::__ref830 void set_value(_As&&... __as) noexcept 831 { 832 const __any_::__rcvr_vfun<set_value_t(_As...)>* __vfun = 833 __env_.__vtable_; 834 (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_As&&>(__as)...); 835 } 836 837 template <class _Error> 838 requires __one_of<set_error_t(_Error), _Sigs...> set_errorexec::__any::__rec::__ref839 void set_error(_Error&& __err) noexcept 840 { 841 const __any_::__rcvr_vfun<set_error_t(_Error)>* __vfun = 842 __env_.__vtable_; 843 (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_Error&&>(__err)); 844 } 845 set_stoppedexec::__any::__rec::__ref846 void set_stopped() noexcept 847 requires __one_of<set_stopped_t(), _Sigs...> 848 { 849 const __any_::__rcvr_vfun<set_stopped_t()>* __vfun = __env_.__vtable_; 850 (*__vfun->__complete_)(__env_.__rcvr_); 851 } 852 get_envexec::__any::__rec::__ref853 auto get_env() const noexcept -> const __env_t& 854 { 855 return __env_; 856 } 857 }; 858 859 auto __test_never_stop_token( 860 get_stop_token_t (*)(never_stop_token (*)() noexcept)) -> __mbool<true>; 861 862 template <class _Tag, class _Ret, class... _As> 863 auto __test_never_stop_token(_Tag (*)(_Ret (*)(_As...) noexcept)) 864 -> __mbool<false>; 865 866 template <class _Tag, class _Ret, class... _As> 867 auto __test_never_stop_token(_Tag (*)(_Ret (*)(_As...))) -> __mbool<false>; 868 869 template <class _Query> 870 using __is_never_stop_token_query = 871 decltype(__test_never_stop_token(static_cast<_Query>(nullptr))); 872 873 template <class... _Sigs, class... _Queries> 874 requires(__is_stop_token_query<_Queries> || ...) 875 struct __ref<completion_signatures<_Sigs...>, _Queries...> 876 { 877 #if !STDEXEC_MSVC() 878 // MSVCBUG 879 // https://developercommunity.visualstudio.com/t/Private-member-inaccessible-when-used-in/10448363 880 881 private: 882 #endif 883 using _FilteredQueries = 884 __minvoke<__mremove_if<__q<__is_never_stop_token_query>>, _Queries...>; 885 using __vtable_t = stdexec::__t< 886 __mapply<__mbind_front_q<__vtable, completion_signatures<_Sigs...>>, 887 _FilteredQueries>>; 888 889 struct __env_t 890 { 891 const __vtable_t* __vtable_; 892 void* __rcvr_; 893 894 template <class _Tag, class... _As> 895 requires __callable<const __vtable_t&, _Tag, void*, _As...> queryexec::__any::__rec::__ref::__env_t896 auto query(_Tag, _As&&... __as) const // 897 noexcept(__nothrow_callable<const __vtable_t&, _Tag, void*, _As...>) 898 -> __call_result_t<const __vtable_t&, _Tag, void*, _As...> 899 { 900 return (*__vtable_)(_Tag{}, __rcvr_, static_cast<_As&&>(__as)...); 901 } 902 } __env_; 903 904 public: 905 using receiver_concept = stdexec::receiver_t; 906 using __id = __ref; 907 using __t = __ref; 908 909 template <__none_of<__ref, const __ref, __env_t, const __env_t> _Rcvr> 910 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> && 911 (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...) __refexec::__any::__rec::__ref912 __ref(_Rcvr& __rcvr) noexcept : 913 __env_{__create_vtable(__mtype<__vtable_t>{}, __mtype<_Rcvr>{}), 914 &__rcvr} 915 {} 916 917 template <class... _As> 918 requires __one_of<set_value_t(_As...), _Sigs...> set_valueexec::__any::__rec::__ref919 void set_value(_As&&... __as) noexcept 920 { 921 const __any_::__rcvr_vfun<set_value_t(_As...)>* __vfun = 922 __env_.__vtable_; 923 (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_As&&>(__as)...); 924 } 925 926 template <class _Error> 927 requires __one_of<set_error_t(_Error), _Sigs...> set_errorexec::__any::__rec::__ref928 void set_error(_Error&& __err) noexcept 929 { 930 const __any_::__rcvr_vfun<set_error_t(_Error)>* __vfun = 931 __env_.__vtable_; 932 (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_Error&&>(__err)); 933 } 934 set_stoppedexec::__any::__rec::__ref935 void set_stopped() noexcept 936 requires __one_of<set_stopped_t(), _Sigs...> 937 { 938 const __any_::__rcvr_vfun<set_stopped_t()>* __vfun = __env_.__vtable_; 939 (*__vfun->__complete_)(__env_.__rcvr_); 940 } 941 get_envexec::__any::__rec::__ref942 auto get_env() const noexcept -> const __env_t& 943 { 944 return __env_; 945 } 946 }; 947 } // namespace __rec 948 949 class __operation_vtable 950 { 951 public: 952 void (*__start_)(void*) noexcept; 953 954 private: 955 template <class _Op> STDEXEC_MEMFN_DECL(auto __create_vtable)956 STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__operation_vtable>, 957 __mtype<_Op>) noexcept 958 -> const __operation_vtable* 959 { 960 static __operation_vtable __vtable{ 961 [](void* __object_pointer) noexcept -> void { 962 STDEXEC_ASSERT(__object_pointer); 963 _Op& __op = *static_cast<_Op*>(__object_pointer); 964 static_assert(operation_state<_Op>); 965 stdexec::start(__op); 966 }}; 967 return &__vtable; 968 } 969 }; 970 971 using __immovable_operation_storage = __immovable_storage_t<__operation_vtable>; 972 973 template <class _Sigs, class _Queries> 974 using __receiver_ref = 975 __mapply<__mbind_front<__q<__rec::__ref>, _Sigs>, _Queries>; 976 977 struct __on_stop_t 978 { 979 stdexec::inplace_stop_source& __source_; 980 operator ()exec::__any::__on_stop_t981 void operator()() const noexcept 982 { 983 __source_.request_stop(); 984 } 985 }; 986 987 template <class _Receiver> 988 struct __operation_base 989 { 990 STDEXEC_ATTRIBUTE((no_unique_address)) 991 _Receiver __rcvr_; 992 stdexec::inplace_stop_source __stop_source_{}; 993 using __stop_callback = typename stdexec::stop_token_of_t< 994 stdexec::env_of_t<_Receiver>>::template callback_type<__on_stop_t>; 995 std::optional<__stop_callback> __on_stop_{}; 996 }; 997 998 template <class _Env> 999 using __env_t = 1000 __env::__join_t<prop<get_stop_token_t, inplace_stop_token>, _Env>; 1001 1002 template <class _ReceiverId> 1003 struct __stoppable_receiver 1004 { 1005 using _Receiver = stdexec::__t<_ReceiverId>; 1006 1007 struct __t 1008 { 1009 using receiver_concept = stdexec::receiver_t; 1010 __operation_base<_Receiver>* __op_; 1011 1012 template <same_as<__t> _Self, class _Item> 1013 requires __callable<set_next_t, _Receiver&, _Item> STDEXEC_MEMFN_DECLexec::__any::__stoppable_receiver::__t1014 STDEXEC_MEMFN_DECL(auto set_next)(this _Self& __self, 1015 _Item&& __item) noexcept 1016 -> __call_result_t<set_next_t, _Receiver&, _Item> 1017 { 1018 return exec::set_next(__self.__op_->__rcvr_, 1019 static_cast<_Item&&>(__item)); 1020 } 1021 1022 template <class... _Args> 1023 requires __callable<set_value_t, _Receiver, _Args...> set_valueexec::__any::__stoppable_receiver::__t1024 void set_value(_Args&&... __args) noexcept 1025 { 1026 __op_->__on_stop_.reset(); 1027 stdexec::set_value(static_cast<_Receiver&&>(__op_->__rcvr_), 1028 static_cast<_Args&&>(__args)...); 1029 } 1030 1031 template <class _Error> 1032 requires __callable<set_error_t, _Receiver, _Error> set_errorexec::__any::__stoppable_receiver::__t1033 void set_error(_Error&& __err) noexcept 1034 { 1035 __op_->__on_stop_.reset(); 1036 stdexec::set_error(static_cast<_Receiver&&>(__op_->__rcvr_), 1037 static_cast<_Error&&>(__err)); 1038 } 1039 set_stoppedexec::__any::__stoppable_receiver::__t1040 void set_stopped() noexcept 1041 requires __callable<set_stopped_t, _Receiver> 1042 { 1043 __op_->__on_stop_.reset(); 1044 stdexec::set_stopped(static_cast<_Receiver&&>(__op_->__rcvr_)); 1045 } 1046 get_envexec::__any::__stoppable_receiver::__t1047 auto get_env() const noexcept -> __env_t<env_of_t<_Receiver>> 1048 { 1049 return __env::__join( 1050 prop{get_stop_token, __op_->__stop_source_.get_token()}, 1051 stdexec::get_env(__op_->__rcvr_)); 1052 } 1053 }; 1054 }; 1055 1056 template <class _ReceiverId> 1057 using __stoppable_receiver_t = stdexec::__t<__stoppable_receiver<_ReceiverId>>; 1058 1059 template <class _ReceiverId, bool> 1060 struct __operation 1061 { 1062 using _Receiver = stdexec::__t<_ReceiverId>; 1063 1064 class __t : public __operation_base<_Receiver> 1065 { 1066 public: 1067 using __id = __operation; 1068 1069 template <class _Sender> __t(_Sender && __sender,_Receiver && __receiver)1070 __t(_Sender&& __sender, _Receiver&& __receiver) : 1071 __operation_base<_Receiver>{static_cast<_Receiver&&>(__receiver)}, 1072 __rec_{this}, __storage_{__sender.__connect(__rec_)} 1073 {} 1074 start()1075 void start() & noexcept 1076 { 1077 this->__on_stop_.emplace( 1078 stdexec::get_stop_token(stdexec::get_env(this->__rcvr_)), 1079 __on_stop_t{this->__stop_source_}); 1080 STDEXEC_ASSERT(__storage_.__get_vtable()->__start_); 1081 __storage_.__get_vtable()->__start_( 1082 __storage_.__get_object_pointer()); 1083 } 1084 1085 private: 1086 __stoppable_receiver_t<_ReceiverId> __rec_; 1087 __immovable_operation_storage __storage_{}; 1088 }; 1089 }; 1090 1091 template <class _ReceiverId> 1092 struct __operation<_ReceiverId, false> 1093 { 1094 using _Receiver = stdexec::__t<_ReceiverId>; 1095 1096 class __t 1097 { 1098 public: 1099 using __id = __operation; 1100 1101 template <class _Sender> __t(_Sender && __sender,_Receiver && __receiver)1102 __t(_Sender&& __sender, _Receiver&& __receiver) : 1103 __rec_{static_cast<_Receiver&&>(__receiver)}, 1104 __storage_{__sender.__connect(__rec_)} 1105 {} 1106 start()1107 void start() & noexcept 1108 { 1109 STDEXEC_ASSERT(__storage_.__get_vtable()->__start_); 1110 __storage_.__get_vtable()->__start_( 1111 __storage_.__get_object_pointer()); 1112 } 1113 1114 private: 1115 STDEXEC_ATTRIBUTE((no_unique_address)) 1116 _Receiver __rec_; 1117 __immovable_operation_storage __storage_{}; 1118 }; 1119 }; 1120 1121 template <class _Queries, bool _IsEnvProvider = true> 1122 class __query_vtable; 1123 1124 template <template <class...> class _List, class... _Queries, 1125 bool _IsEnvProvider> 1126 class __query_vtable<_List<_Queries...>, _IsEnvProvider> : 1127 public __query_vfun<_Queries>... 1128 { 1129 public: 1130 using __query_vfun<_Queries>::operator()...; 1131 1132 private: 1133 template <class _Queryable> 1134 requires( 1135 __callable<__query_vfun_fn<_Queryable, _IsEnvProvider>, _Queries> && 1136 ...) STDEXEC_MEMFN_DECL(auto __create_vtable)1137 STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__query_vtable>, 1138 __mtype<_Queryable>) noexcept 1139 -> const __query_vtable* 1140 { 1141 static const __query_vtable __vtable{ 1142 {__query_vfun_fn<_Queryable, _IsEnvProvider>{}( 1143 static_cast<_Queries>(nullptr))}...}; 1144 return &__vtable; 1145 } 1146 }; 1147 1148 template <class _Sigs, class _SenderQueries = __types<>, 1149 class _ReceiverQueries = __types<>> 1150 struct __sender 1151 { 1152 using __receiver_ref_t = __receiver_ref<_Sigs, _ReceiverQueries>; 1153 static constexpr bool __with_inplace_stop_token = 1154 __v<__mapply<__mall_of<__q<__is_not_stop_token_query_v>>, 1155 _ReceiverQueries>>; 1156 1157 class __vtable : public __query_vtable<_SenderQueries> 1158 { 1159 public: 1160 using __id = __vtable; 1161 __queries() const1162 auto __queries() const noexcept -> const __query_vtable<_SenderQueries>& 1163 { 1164 return *this; 1165 } 1166 1167 __immovable_operation_storage (*__connect_)(void*, __receiver_ref_t); 1168 1169 private: 1170 template <sender_to<__receiver_ref_t> _Sender> STDEXEC_MEMFN_DECL(auto __create_vtable)1171 STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__vtable>, 1172 __mtype<_Sender>) noexcept 1173 -> const __vtable* 1174 { 1175 static const __vtable __vtable_{ 1176 {*__create_vtable(__mtype<__query_vtable<_SenderQueries>>{}, 1177 __mtype<_Sender>{})}, 1178 [](void* __object_pointer, __receiver_ref_t __receiver) 1179 -> __immovable_operation_storage { 1180 _Sender& __sender = 1181 *static_cast<_Sender*>(__object_pointer); 1182 using __op_state_t = 1183 connect_result_t<_Sender, __receiver_ref_t>; 1184 return __immovable_operation_storage{ 1185 std::in_place_type<__op_state_t>, __emplace_from{[&] { 1186 return stdexec::connect( 1187 static_cast<_Sender&&>(__sender), 1188 static_cast<__receiver_ref_t&&>(__receiver)); 1189 }}}; 1190 }}; 1191 return &__vtable_; 1192 } 1193 }; 1194 1195 struct __env_t 1196 { 1197 const __vtable* __vtable_; 1198 void* __sender_; 1199 1200 template <class _Tag, class... _As> 1201 requires __callable<const __query_vtable<_SenderQueries>&, _Tag, 1202 void*, _As...> queryexec::__any::__sender::__env_t1203 auto query(_Tag, _As&&... __as) const // 1204 noexcept(__nothrow_callable<const __query_vtable<_SenderQueries>&, 1205 _Tag, void*, _As...>) 1206 -> __call_result_t<const __query_vtable<_SenderQueries>&, _Tag, 1207 void*, _As...> 1208 { 1209 return __vtable_->__queries()(_Tag{}, __sender_, 1210 static_cast<_As&&>(__as)...); 1211 } 1212 }; 1213 1214 struct __t 1215 { 1216 using __id = __sender; 1217 using completion_signatures = _Sigs; 1218 using sender_concept = stdexec::sender_t; 1219 1220 __t(const __t&) = delete; 1221 auto operator=(const __t&) -> __t& = delete; 1222 1223 __t(__t&&) = default; 1224 auto operator=(__t&&) -> __t& = default; 1225 1226 template <__not_decays_to<__t> _Sender> 1227 requires sender_to<_Sender, __receiver_ref<_Sigs, _ReceiverQueries>> __texec::__any::__sender::__t1228 __t(_Sender&& __sndr) : __storage_{static_cast<_Sender&&>(__sndr)} 1229 {} 1230 __connectexec::__any::__sender::__t1231 auto __connect(__receiver_ref_t __receiver) 1232 -> __immovable_operation_storage 1233 { 1234 return __storage_.__get_vtable()->__connect_( 1235 __storage_.__get_object_pointer(), 1236 static_cast<__receiver_ref_t&&>(__receiver)); 1237 } 1238 operator boolexec::__any::__sender::__t1239 explicit operator bool() const noexcept 1240 { 1241 return __get_object_pointer(__storage_) != nullptr; 1242 } 1243 get_envexec::__any::__sender::__t1244 auto get_env() const noexcept -> __env_t 1245 { 1246 return {__storage_.__get_vtable(), 1247 __storage_.__get_object_pointer()}; 1248 } 1249 1250 template <receiver_of<_Sigs> _Rcvr> connectexec::__any::__sender::__t1251 auto connect(_Rcvr __rcvr) && // 1252 -> stdexec::__t< 1253 __operation<stdexec::__id<_Rcvr>, __with_inplace_stop_token>> 1254 { 1255 return {static_cast<__t&&>(*this), static_cast<_Rcvr&&>(__rcvr)}; 1256 } 1257 1258 private: 1259 __unique_storage_t<__vtable> __storage_; 1260 }; 1261 }; 1262 1263 template <class _ScheduleSender, class _SchedulerQueries = __types<>> 1264 class __scheduler 1265 { 1266 static constexpr std::size_t __buffer_size = 4 * sizeof(void*); 1267 template <class _Ty> 1268 static constexpr bool __is_small = 1269 sizeof(_Ty) <= __buffer_size && 1270 alignof(_Ty) <= alignof(std::max_align_t); 1271 1272 public: 1273 template <class _Scheduler> 1274 requires(!__decays_to<_Scheduler, __scheduler>) && scheduler<_Scheduler> __scheduler(_Scheduler && __scheduler)1275 __scheduler(_Scheduler&& __scheduler) : 1276 __storage_{static_cast<_Scheduler&&>(__scheduler)} 1277 { 1278 static_assert( 1279 __is_small<_Scheduler>, 1280 "any_scheduler<> must have a nothrow copy constructor, so the scheduler object must be " 1281 "small enough to be stored in the internal buffer to avoid dynamic allocation."); 1282 } 1283 1284 __scheduler(__scheduler&&) noexcept = default; 1285 __scheduler(const __scheduler&) noexcept = default; 1286 __scheduler& operator=(__scheduler&&) noexcept = default; 1287 __scheduler& operator=(const __scheduler&) noexcept = default; 1288 1289 using __sender_t = _ScheduleSender; 1290 schedule() const1291 auto schedule() const noexcept -> __sender_t 1292 { 1293 STDEXEC_ASSERT(__storage_.__get_vtable()->__schedule_); 1294 return __storage_.__get_vtable()->__schedule_( 1295 __storage_.__get_object_pointer()); 1296 } 1297 1298 template <class _Tag, class... _As> 1299 requires __callable<const __query_vtable<_SchedulerQueries, false>&, 1300 _Tag, void*, _As...> query(_Tag,_As &&...__as) const1301 auto query(_Tag, _As&&... __as) const // 1302 noexcept( 1303 __nothrow_callable<const __query_vtable<_SchedulerQueries, false>&, 1304 _Tag, void*, _As...>) 1305 -> __call_result_t<const __query_vtable<_SchedulerQueries, false>&, 1306 _Tag, void*, _As...> 1307 { 1308 return __storage_.__get_vtable()->__queries()( 1309 _Tag{}, __storage_.__get_object_pointer(), 1310 static_cast<_As&&>(__as)...); 1311 } 1312 1313 private: 1314 class __vtable : public __query_vtable<_SchedulerQueries, false> 1315 { 1316 public: 1317 __sender_t (*__schedule_)(void*) noexcept; 1318 bool (*__equal_to_)(const void*, const void* other) noexcept; 1319 __queries() const1320 auto __queries() const noexcept 1321 -> const __query_vtable<_SchedulerQueries, false>& 1322 { 1323 return *this; 1324 } 1325 1326 private: 1327 template <scheduler _Scheduler> STDEXEC_MEMFN_DECL(auto __create_vtable)1328 STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__vtable>, 1329 __mtype<_Scheduler>) noexcept 1330 -> const __vtable* 1331 { 1332 static const __vtable __vtable_{ 1333 {*__create_vtable( 1334 __mtype<__query_vtable<_SchedulerQueries, false>>{}, 1335 __mtype<_Scheduler>{})}, 1336 [](void* __object_pointer) noexcept -> __sender_t { 1337 const _Scheduler& __scheduler = 1338 *static_cast<const _Scheduler*>(__object_pointer); 1339 return __sender_t{stdexec::schedule(__scheduler)}; 1340 }, 1341 [](const void* __self, const void* __other) noexcept -> bool { 1342 static_assert(noexcept(__declval<const _Scheduler&>() == 1343 __declval<const _Scheduler&>())); 1344 STDEXEC_ASSERT(__self && __other); 1345 const _Scheduler& __self_scheduler = 1346 *static_cast<const _Scheduler*>(__self); 1347 const _Scheduler& __other_scheduler = 1348 *static_cast<const _Scheduler*>(__other); 1349 return __self_scheduler == __other_scheduler; 1350 }}; 1351 return &__vtable_; 1352 } 1353 }; 1354 operator ==(const __scheduler & __self,const __scheduler & __other)1355 friend auto operator==(const __scheduler& __self, 1356 const __scheduler& __other) noexcept -> bool 1357 { 1358 if (__self.__storage_.__get_vtable() != 1359 __other.__storage_.__get_vtable()) 1360 { 1361 return false; 1362 } 1363 1364 void* __p = __self.__storage_.__get_object_pointer(); 1365 void* __o = __other.__storage_.__get_object_pointer(); 1366 // if both object pointers are not null, use the virtual equal_to 1367 // function 1368 return (__p && __o && 1369 __self.__storage_.__get_vtable()->__equal_to_(__p, __o)) 1370 // if both object pointers are nullptrs, they are equal 1371 || (!__p && !__o); 1372 } 1373 operator !=(const __scheduler & __self,const __scheduler & __other)1374 friend auto operator!=(const __scheduler& __self, 1375 const __scheduler& __other) noexcept -> bool 1376 { 1377 return !(__self == __other); 1378 } 1379 1380 __copyable_storage_t<__vtable, __buffer_size> __storage_{}; 1381 }; 1382 } // namespace __any 1383 1384 template <auto... _Sigs> 1385 using queries = stdexec::__types<decltype(_Sigs)...>; 1386 1387 template <class _Completions, auto... _ReceiverQueries> 1388 class any_receiver_ref 1389 { 1390 using __receiver_base = 1391 __any::__rec::__ref<_Completions, decltype(_ReceiverQueries)...>; 1392 using __env_t = stdexec::env_of_t<__receiver_base>; 1393 __receiver_base __receiver_; 1394 1395 public: 1396 using receiver_concept = stdexec::receiver_t; 1397 using __t = any_receiver_ref; 1398 using __id = any_receiver_ref; 1399 1400 template <stdexec::__none_of<any_receiver_ref, const any_receiver_ref, 1401 __env_t, const __env_t> 1402 _Receiver> 1403 requires stdexec::receiver_of<_Receiver, _Completions> any_receiver_ref(_Receiver & __receiver)1404 any_receiver_ref(_Receiver& __receiver) // 1405 noexcept( 1406 stdexec::__nothrow_constructible_from<__receiver_base, _Receiver>) : 1407 __receiver_(__receiver) 1408 {} 1409 1410 template <class... _As> 1411 requires stdexec::tag_invocable<stdexec::set_value_t, __receiver_base, 1412 _As...> set_value(_As &&...__as)1413 void set_value(_As&&... __as) noexcept 1414 { 1415 stdexec::tag_invoke(stdexec::set_value, 1416 static_cast<__receiver_base&&>(__receiver_), 1417 static_cast<_As&&>(__as)...); 1418 } 1419 1420 template <class _Error> 1421 requires stdexec::tag_invocable<stdexec::set_error_t, __receiver_base, 1422 _Error> set_error(_Error && __err)1423 void set_error(_Error&& __err) noexcept 1424 { 1425 stdexec::tag_invoke(stdexec::set_error, 1426 static_cast<__receiver_base&&>(__receiver_), 1427 static_cast<_Error&&>(__err)); 1428 } 1429 set_stopped()1430 void set_stopped() noexcept 1431 requires stdexec::tag_invocable<stdexec::set_stopped_t, __receiver_base> 1432 { 1433 stdexec::tag_invoke(stdexec::set_stopped, 1434 static_cast<__receiver_base&&>(__receiver_)); 1435 } 1436 get_env() const1437 auto get_env() const noexcept -> stdexec::env_of_t<__receiver_base> 1438 { 1439 return stdexec::get_env(__receiver_); 1440 } 1441 1442 template <auto... _SenderQueries> 1443 class any_sender 1444 { 1445 using __sender_base = stdexec::__t< 1446 __any::__sender<_Completions, queries<_SenderQueries...>, 1447 queries<_ReceiverQueries...>>>; 1448 __sender_base __sender_; 1449 1450 template <class _Tag, stdexec::__decays_to<any_sender> Self, 1451 class... _As> 1452 requires stdexec::tag_invocable< 1453 _Tag, stdexec::__copy_cvref_t<Self, __sender_base>, _As...> tag_invoke(_Tag,Self && __self,_As &&...__as)1454 friend auto tag_invoke(_Tag, Self&& __self, _As&&... __as) // 1455 noexcept( 1456 stdexec::nothrow_tag_invocable< 1457 _Tag, stdexec::__copy_cvref_t<Self, __sender_base>, _As...>) 1458 { 1459 return stdexec::tag_invoke(_Tag{}, 1460 static_cast<Self&&>(__self).__sender_, 1461 static_cast<_As&&>(__as)...); 1462 } 1463 1464 public: 1465 using sender_concept = stdexec::sender_t; 1466 using completion_signatures = 1467 typename __sender_base::completion_signatures; 1468 1469 template <stdexec::__not_decays_to<any_sender> _Sender> 1470 requires stdexec::sender_to<_Sender, __receiver_base> any_sender(_Sender && __sender)1471 any_sender(_Sender&& __sender) // 1472 noexcept( 1473 stdexec::__nothrow_constructible_from<__sender_base, _Sender>) : 1474 __sender_(static_cast<_Sender&&>(__sender)) 1475 {} 1476 1477 template <stdexec::receiver_of<_Completions> _Receiver> connect(_Receiver __rcvr)1478 auto connect(_Receiver __rcvr) && -> stdexec::connect_result_t< 1479 __sender_base, _Receiver> 1480 { 1481 return static_cast<__sender_base&&>(__sender_).connect( 1482 static_cast<_Receiver&&>(__rcvr)); 1483 } 1484 1485 template <auto... _SchedulerQueries> 1486 class any_scheduler 1487 { 1488 using __schedule_completions = 1489 stdexec::__concat_completion_signatures< 1490 _Completions, 1491 stdexec::completion_signatures<stdexec::set_value_t()>>; 1492 using __schedule_receiver = 1493 any_receiver_ref<__schedule_completions, _ReceiverQueries...>; 1494 1495 template <typename _Tag, typename _Sig> 1496 static auto __ret_fn(_Tag (*const)(_Sig)) -> _Tag; 1497 1498 template <class _Tag> 1499 struct __ret_equals_to 1500 { 1501 template <class _Sig> 1502 using __f = std::is_same< 1503 _Tag, decltype(__ret_fn(static_cast<_Sig>(nullptr)))>; 1504 }; 1505 1506 using schedule_sender_queries = stdexec::__minvoke< 1507 stdexec::__mremove_if<__ret_equals_to< 1508 stdexec::get_completion_scheduler_t<stdexec::set_value_t>>>, 1509 decltype(_SenderQueries)...>; 1510 1511 #if STDEXEC_MSVC() 1512 // MSVCBUG 1513 // https://developercommunity.visualstudio.com/t/ICE-and-non-ICE-bug-in-NTTP-argument-w/10361081 1514 1515 static constexpr auto __any_scheduler_noexcept_signature = 1516 stdexec::get_completion_scheduler<stdexec::set_value_t>.signature<any_scheduler() noexcept>; 1517 template <class... _Queries> 1518 using __schedule_sender_fn = 1519 typename __schedule_receiver::template any_sender< 1520 __any_scheduler_noexcept_signature>; 1521 #else 1522 template <class... _Queries> 1523 using __schedule_sender_fn = typename __schedule_receiver::template any_sender< 1524 stdexec::get_completion_scheduler<stdexec::set_value_t>.template signature<any_scheduler() noexcept>>; 1525 #endif 1526 using __schedule_sender = 1527 stdexec::__mapply<stdexec::__q<__schedule_sender_fn>, 1528 schedule_sender_queries>; 1529 1530 using __scheduler_base = 1531 __any::__scheduler<__schedule_sender, 1532 queries<_SchedulerQueries...>>; 1533 1534 __scheduler_base __scheduler_; 1535 1536 public: 1537 using __t = any_scheduler; 1538 using __id = any_scheduler; 1539 1540 template <stdexec::__none_of<any_scheduler> _Scheduler> 1541 requires stdexec::scheduler<_Scheduler> any_scheduler(_Scheduler __scheduler)1542 any_scheduler(_Scheduler __scheduler) : 1543 __scheduler_{static_cast<_Scheduler&&>(__scheduler)} 1544 {} 1545 schedule() const1546 auto schedule() const noexcept -> __schedule_sender 1547 { 1548 return __scheduler_.schedule(); 1549 } 1550 1551 template <class _Tag, class... _As> 1552 requires stdexec::tag_invocable<_Tag, const __scheduler_base&, 1553 _As...> query(_Tag,_As &&...__as) const1554 auto query(_Tag, _As&&... __as) const noexcept( 1555 stdexec::nothrow_tag_invocable<_Tag, const __scheduler_base&, 1556 _As...>) 1557 -> stdexec::tag_invoke_result_t<_Tag, const __scheduler_base&, 1558 _As...> 1559 { 1560 return stdexec::tag_invoke(_Tag(), __scheduler_, 1561 static_cast<_As&&>(__as)...); 1562 } 1563 1564 auto operator==(const any_scheduler&) const noexcept 1565 -> bool = default; 1566 }; 1567 }; 1568 }; 1569 } // namespace exec 1570