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/execution.hpp" 19 #include "../stdexec/__detail/__any_receiver_ref.hpp" 20 #include "../stdexec/__detail/__concepts.hpp" 21 #include "../stdexec/__detail/__env.hpp" 22 #include "../stdexec/__detail/__transform_completion_signatures.hpp" 23 24 #include "sequence_senders.hpp" 25 26 #include <cstddef> 27 #include <utility> 28 29 namespace exec { 30 namespace __any { 31 using namespace stdexec; 32 33 struct __create_vtable_t { 34 template <class _VTable, class _Tp> 35 requires __tag_invocable_r<const _VTable*, __create_vtable_t, __mtype<_VTable>, __mtype<_Tp>> operator ()exec::__any::__create_vtable_t36 constexpr auto operator()(__mtype<_VTable>, __mtype<_Tp>) const noexcept -> const _VTable* { 37 return stdexec::tag_invoke(__create_vtable_t{}, __mtype<_VTable>{}, __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 _Ret (*__fn_)(void*, _As...); 49 operator ()exec::__any::__query_vfun50 auto operator()(_Tag, void* __rcvr, _As&&... __as) const -> _Ret { 51 return __fn_(__rcvr, static_cast<_As&&>(__as)...); 52 } 53 }; 54 55 template <class _Tag, class _Ret, class... _As> 56 struct __query_vfun<_Tag (*)(_Ret (*)(_As...))> { 57 _Ret (*__fn_)(void*, _As...); 58 operator ()exec::__any::__query_vfun59 auto operator()(_Tag, void* __rcvr, _As&&... __as) const -> _Ret { 60 return __fn_(__rcvr, static_cast<_As&&>(__as)...); 61 } 62 }; 63 64 template <class _Tag, class _Ret, class... _As> 65 struct __query_vfun<_Tag (*const)(_Ret (*)(_As...) noexcept)> { 66 _Ret (*__fn_)(void*, _As...) noexcept; 67 operator ()exec::__any::__query_vfun68 auto operator()(_Tag, void* __rcvr, _As&&... __as) const noexcept -> _Ret { 69 return __fn_(__rcvr, static_cast<_As&&>(__as)...); 70 } 71 }; 72 73 template <class _Tag, class _Ret, class... _As> 74 struct __query_vfun<_Tag (*)(_Ret (*)(_As...) noexcept)> { 75 _Ret (*__fn_)(void*, _As...) noexcept; 76 operator ()exec::__any::__query_vfun77 auto operator()(_Tag, void* __rcvr, _As&&... __as) const noexcept -> _Ret { 78 return __fn_(__rcvr, static_cast<_As&&>(__as)...); 79 } 80 }; 81 82 template <class _Queryable, bool _IsEnvProvider = true> 83 struct __query_vfun_fn; 84 85 template <class _EnvProvider> 86 struct __query_vfun_fn<_EnvProvider, true> { 87 template <class _Tag, class _Ret, class... _As> 88 requires __callable<_Tag, env_of_t<const _EnvProvider&>, _As...> 89 constexpr auto operator ()exec::__any::__query_vfun_fn90 operator()(_Tag (*)(_Ret (*)(_As...))) const noexcept -> _Ret (*)(void*, _As...) { 91 return +[](void* __env_provider, _As... __as) -> _Ret { 92 return _Tag{}( 93 stdexec::get_env(*static_cast<const _EnvProvider*>(__env_provider)), 94 static_cast<_As&&>(__as)...); 95 }; 96 } 97 98 template <class _Tag, class _Ret, class... _As> 99 requires __callable<_Tag, env_of_t<const _EnvProvider&>, _As...> operator ()exec::__any::__query_vfun_fn100 constexpr auto operator()(_Tag (*)(_Ret (*)(_As...) noexcept)) const noexcept 101 -> _Ret (*)(void*, _As...) noexcept { 102 return +[](void* __env_provider, _As... __as) noexcept -> _Ret { 103 static_assert(__nothrow_callable<_Tag, const env_of_t<_EnvProvider>&, _As...>); 104 return _Tag{}( 105 stdexec::get_env(*static_cast<const _EnvProvider*>(__env_provider)), 106 static_cast<_As&&>(__as)...); 107 }; 108 } 109 }; 110 111 template <class _Queryable> 112 struct __query_vfun_fn<_Queryable, false> { 113 template <class _Tag, class _Ret, class... _As> 114 requires __callable<_Tag, const _Queryable&, _As...> 115 constexpr auto operator ()exec::__any::__query_vfun_fn116 operator()(_Tag (*)(_Ret (*)(_As...))) const noexcept -> _Ret (*)(void*, _As...) { 117 return +[](void* __queryable, _As... __as) -> _Ret { 118 return _Tag{}(*static_cast<const _Queryable*>(__queryable), static_cast<_As&&>(__as)...); 119 }; 120 } 121 122 template <class _Tag, class _Ret, class... _As> 123 requires __callable<_Tag, const _Queryable&, _As...> operator ()exec::__any::__query_vfun_fn124 constexpr auto operator()(_Tag (*)(_Ret (*)(_As...) noexcept)) const noexcept 125 -> _Ret (*)(void*, _As...) noexcept { 126 return +[](void* __env_provider, _As... __as) noexcept -> _Ret { 127 static_assert(__nothrow_callable<_Tag, const _Queryable&, _As...>); 128 return _Tag{}( 129 *static_cast<const _Queryable*>(__env_provider), static_cast<_As&&>(__as)...); 130 }; 131 } 132 }; 133 134 template <class _Sig> 135 struct __storage_vfun; 136 137 template <class _Tag, class... _As> 138 struct __storage_vfun<_Tag(void (*)(_As...))> { __anon1bf2f58b0502exec::__any::__storage_vfun139 void (*__fn_)(void*, _As...) = [](void*, _As...) { 140 }; 141 operator ()exec::__any::__storage_vfun142 void operator()(_Tag, void* __storage, _As&&... __as) const { 143 return __fn_(__storage, static_cast<_As&&>(__as)...); 144 } 145 }; 146 147 template <class _Tag, class... _As> 148 struct __storage_vfun<_Tag(void (*)(_As...) noexcept)> { __anon1bf2f58b0602exec::__any::__storage_vfun149 void (*__fn_)(void*, _As...) noexcept = [](void*, _As...) noexcept { 150 }; 151 operator ()exec::__any::__storage_vfun152 void operator()(_Tag, void* __storage, _As&&... __as) const noexcept { 153 return __fn_(__storage, static_cast<_As&&>(__as)...); 154 } 155 }; 156 157 template <class _Storage, class _Tp> 158 struct __storage_vfun_fn { 159 template <class _Tag, class... _As> 160 requires __callable<_Tag, __mtype<_Tp>, _Storage&, _As...> 161 constexpr auto operator ()exec::__any::__storage_vfun_fn162 operator()(_Tag (*)(void (*)(_As...))) const noexcept -> void (*)(void*, _As...) { 163 return +[](void* __storage, _As... __as) -> void { 164 return _Tag{}( 165 __mtype<_Tp>{}, *static_cast<_Storage*>(__storage), static_cast<_As&&>(__as)...); 166 }; 167 } 168 169 template <class _Tag, class... _As> 170 requires __callable<_Tag, __mtype<_Tp>, _Storage&, _As...> operator ()exec::__any::__storage_vfun_fn171 constexpr auto operator()(_Tag (*)(void (*)(_As...) noexcept)) const noexcept 172 -> void (*)(void*, _As...) noexcept { 173 return +[](void* __storage, _As... __as) noexcept -> void { 174 static_assert(__nothrow_callable<_Tag, __mtype<_Tp>, _Storage&, _As...>); 175 return _Tag{}( 176 __mtype<_Tp>{}, *static_cast<_Storage*>(__storage), static_cast<_As&&>(__as)...); 177 }; 178 } 179 }; 180 181 struct __delete_t { 182 template <class _Storage, class _Tp> 183 requires tag_invocable<__delete_t, __mtype<_Tp>, _Storage&> operator ()exec::__any::__delete_t184 void operator()(__mtype<_Tp>, _Storage& __storage) noexcept { 185 static_assert(nothrow_tag_invocable<__delete_t, __mtype<_Tp>, _Storage&>); 186 stdexec::tag_invoke(__delete_t{}, __mtype<_Tp>{}, __storage); 187 } 188 }; 189 190 inline constexpr __delete_t __delete{}; 191 192 struct __copy_construct_t { 193 template <class _Storage, class _Tp> 194 requires tag_invocable<__copy_construct_t, __mtype<_Tp>, _Storage&, const _Storage&> operator ()exec::__any::__copy_construct_t195 void operator()(__mtype<_Tp>, _Storage& __self, const _Storage& __from) noexcept( 196 nothrow_tag_invocable<__copy_construct_t, __mtype<_Tp>, _Storage&, const _Storage&>) { 197 stdexec::tag_invoke(__copy_construct_t{}, __mtype<_Tp>{}, __self, __from); 198 } 199 }; 200 201 inline constexpr __copy_construct_t __copy_construct{}; 202 203 struct __move_construct_t { 204 template <class _Storage, class _Tp> 205 requires tag_invocable<__move_construct_t, __mtype<_Tp>, _Storage&, _Storage&&> operator ()exec::__any::__move_construct_t206 void operator()(__mtype<_Tp>, _Storage& __self, __midentity<_Storage&&> __from) noexcept { 207 static_assert( 208 nothrow_tag_invocable<__move_construct_t, __mtype<_Tp>, _Storage&, _Storage&&>); 209 stdexec::tag_invoke( 210 __move_construct_t{}, __mtype<_Tp>{}, __self, static_cast<_Storage&&>(__from)); 211 } 212 }; 213 214 inline constexpr __move_construct_t __move_construct{}; 215 216 template <class _ParentVTable, class... _StorageCPOs> 217 struct __storage_vtable; 218 219 template <class _ParentVTable, class... _StorageCPOs> 220 requires requires { _ParentVTable::operator(); } 221 struct __storage_vtable<_ParentVTable, _StorageCPOs...> 222 : _ParentVTable 223 , __storage_vfun<_StorageCPOs>... { 224 using _ParentVTable::operator(); 225 using __storage_vfun<_StorageCPOs>::operator()...; 226 }; 227 228 template <class _ParentVTable, class... _StorageCPOs> 229 requires(!requires { _ParentVTable::operator(); }) 230 struct __storage_vtable<_ParentVTable, _StorageCPOs...> 231 : _ParentVTable 232 , __storage_vfun<_StorageCPOs>... { 233 using __storage_vfun<_StorageCPOs>::operator()...; 234 }; 235 236 template <class _ParentVTable, class... _StorageCPOs> 237 inline constexpr __storage_vtable<_ParentVTable, _StorageCPOs...> __null_storage_vtbl{}; 238 239 template <class _ParentVTable, class... _StorageCPOs> 240 constexpr auto __default_storage_vtable(__storage_vtable<_ParentVTable,_StorageCPOs...> *)241 __default_storage_vtable(__storage_vtable<_ParentVTable, _StorageCPOs...>*) noexcept 242 -> const __storage_vtable<_ParentVTable, _StorageCPOs...>* { 243 return &__null_storage_vtbl<_ParentVTable, _StorageCPOs...>; 244 } 245 246 template <class _Storage, class _Tp, class _ParentVTable, class... _StorageCPOs> 247 static const __storage_vtable<_ParentVTable, _StorageCPOs...> __storage_vtbl{ 248 {*__create_vtable(__mtype<_ParentVTable>{}, __mtype<_Tp>{})}, 249 {__storage_vfun_fn<_Storage, _Tp>{}(static_cast<_StorageCPOs*>(nullptr))}...}; 250 251 template < 252 class _Vtable, 253 class _Allocator, 254 bool _Copyable = false, 255 std::size_t _InlineSize = 3 * sizeof(void*), 256 std::size_t _Alignment = alignof(std::max_align_t) 257 > 258 struct __storage { 259 class __t; 260 }; 261 262 template < 263 class _Vtable, 264 class _Allocator, 265 std::size_t _InlineSize = 3 * sizeof(void*), 266 std::size_t _Alignment = alignof(std::max_align_t) 267 > 268 struct __immovable_storage { 269 class __t : __immovable { 270 static constexpr std::size_t __buffer_size = std::max(_InlineSize, sizeof(void*)); 271 static constexpr std::size_t __alignment = std::max(_Alignment, alignof(void*)); 272 using __with_delete = __delete_t(void() noexcept); 273 using __vtable_t = __storage_vtable<_Vtable, __with_delete>; 274 275 template <class _Tp> 276 static constexpr bool __is_small = sizeof(_Tp) <= __buffer_size 277 && alignof(_Tp) <= __alignment; 278 279 template <class _Tp> __get_vtable_of_type()280 static constexpr auto __get_vtable_of_type() noexcept -> const __vtable_t* { 281 return &__storage_vtbl<__t, __decay_t<_Tp>, _Vtable, __with_delete>; 282 } 283 public: 284 using __id = __immovable_storage; 285 286 __t() = default; 287 288 template <__not_decays_to<__t> _Tp> 289 requires __callable<__create_vtable_t, __mtype<_Vtable>, __mtype<__decay_t<_Tp>>> __t(_Tp && __object)290 __t(_Tp&& __object) 291 : __vtable_{__get_vtable_of_type<_Tp>()} { 292 using _Dp = __decay_t<_Tp>; 293 if constexpr (__is_small<_Dp>) { 294 __construct_small<_Dp>(static_cast<_Tp&&>(__object)); 295 } else { 296 __construct_large<_Dp>(static_cast<_Tp&&>(__object)); 297 } 298 } 299 300 template <class _Tp, class... _Args> 301 requires __callable<__create_vtable_t, __mtype<_Vtable>, __mtype<_Tp>> __t(std::in_place_type_t<_Tp>,_Args &&...__args)302 __t(std::in_place_type_t<_Tp>, _Args&&... __args) 303 : __vtable_{__get_vtable_of_type<_Tp>()} { 304 if constexpr (__is_small<_Tp>) { 305 __construct_small<_Tp>(static_cast<_Args&&>(__args)...); 306 } else { 307 __construct_large<_Tp>(static_cast<_Args&&>(__args)...); 308 } 309 } 310 ~__t()311 ~__t() { 312 __reset(); 313 } 314 __reset()315 void __reset() noexcept { 316 (*__vtable_)(__delete, this); 317 __object_pointer_ = nullptr; 318 __vtable_ = __default_storage_vtable(static_cast<__vtable_t*>(nullptr)); 319 } 320 321 [[nodiscard]] __get_vtable() const322 auto __get_vtable() const noexcept -> const _Vtable* { 323 return __vtable_; 324 } 325 326 [[nodiscard]] __get_object_pointer() const327 auto __get_object_pointer() const noexcept -> void* { 328 return __object_pointer_; 329 } 330 331 private: 332 template <class _Tp, class... _As> __construct_small(_As &&...__args)333 void __construct_small(_As&&... __args) { 334 static_assert(sizeof(_Tp) <= __buffer_size && alignof(_Tp) <= __alignment); 335 _Tp* __pointer = reinterpret_cast<_Tp*>(&__buffer_[0]); 336 using _Alloc = std::allocator_traits<_Allocator>::template rebind_alloc<_Tp>; 337 _Alloc __alloc{__allocator_}; 338 std::allocator_traits<_Alloc>::construct( 339 __alloc, __pointer, static_cast<_As&&>(__args)...); 340 __object_pointer_ = __pointer; 341 } 342 343 template <class _Tp, class... _As> __construct_large(_As &&...__args)344 void __construct_large(_As&&... __args) { 345 using _Alloc = std::allocator_traits<_Allocator>::template rebind_alloc<_Tp>; 346 _Alloc __alloc{__allocator_}; 347 _Tp* __pointer = std::allocator_traits<_Alloc>::allocate(__alloc, 1); 348 STDEXEC_TRY { 349 std::allocator_traits<_Alloc>::construct( 350 __alloc, __pointer, static_cast<_As&&>(__args)...); 351 } 352 STDEXEC_CATCH_ALL { 353 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1); 354 STDEXEC_THROW(); 355 } 356 __object_pointer_ = __pointer; 357 } 358 359 template <class _Tp> STDEXEC_MEMFN_DECL(void __delete)360 STDEXEC_MEMFN_DECL(void __delete)(this __mtype<_Tp>, __t& __self) noexcept { 361 if (!__self.__object_pointer_) { 362 return; 363 } 364 using _Alloc = std::allocator_traits<_Allocator>::template rebind_alloc<_Tp>; 365 _Alloc __alloc{__self.__allocator_}; 366 _Tp* __pointer = static_cast<_Tp*>(std::exchange(__self.__object_pointer_, nullptr)); 367 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer); 368 if constexpr (!__is_small<_Tp>) { 369 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1); 370 } 371 } 372 private: 373 const __vtable_t* __vtable_{__default_storage_vtable(static_cast<__vtable_t*>(nullptr))}; 374 void* __object_pointer_{nullptr}; 375 alignas(__alignment) std::byte __buffer_[__buffer_size]{}; STDEXEC_ATTRIBUTE(no_unique_address)376 STDEXEC_ATTRIBUTE(no_unique_address) _Allocator __allocator_ { }; 377 }; 378 }; 379 380 template < 381 class _Vtable, 382 class _Allocator, 383 bool _Copyable, 384 std::size_t _InlineSize, 385 std::size_t _Alignment 386 > 387 class __storage<_Vtable, _Allocator, _Copyable, _InlineSize, _Alignment>::__t 388 : __if_c<_Copyable, __, __move_only> { 389 static_assert( 390 STDEXEC_IS_CONVERTIBLE_TO(typename std::allocator_traits<_Allocator>::void_pointer, void*)); 391 392 static constexpr std::size_t __buffer_size = std::max(_InlineSize, sizeof(void*)); 393 static constexpr std::size_t __alignment = std::max(_Alignment, alignof(void*)); 394 using __with_copy = __copy_construct_t(void(const __t&)); 395 using __with_move = __move_construct_t(void(__t&&) noexcept); 396 using __with_delete = __delete_t(void() noexcept); 397 398 template <class _Tp> 399 static constexpr bool __is_small = sizeof(_Tp) <= __buffer_size && alignof(_Tp) <= __alignment 400 && std::is_nothrow_move_constructible_v<_Tp>; 401 402 using __vtable_t = __if_c< 403 _Copyable, 404 __storage_vtable<_Vtable, __with_delete, __with_move, __with_copy>, 405 __storage_vtable<_Vtable, __with_delete, __with_move> 406 >; 407 408 template <class _Tp> __get_vtable_of_type()409 static constexpr auto __get_vtable_of_type() noexcept -> const __vtable_t* { 410 if constexpr (_Copyable) { 411 return &__storage_vtbl< 412 __t, 413 __decay_t<_Tp>, 414 _Vtable, 415 __with_delete, 416 __with_move, 417 __with_copy 418 >; 419 } else { 420 return &__storage_vtbl<__t, __decay_t<_Tp>, _Vtable, __with_delete, __with_move>; 421 } 422 } 423 424 public: 425 using __id = __storage; 426 427 __t() = default; 428 429 template <__not_decays_to<__t> _Tp> 430 requires __callable<__create_vtable_t, __mtype<_Vtable>, __mtype<__decay_t<_Tp>>> __t(_Tp && __object)431 __t(_Tp&& __object) 432 : __vtable_{__get_vtable_of_type<_Tp>()} { 433 using _Dp = __decay_t<_Tp>; 434 if constexpr (__is_small<_Dp>) { 435 __construct_small<_Dp>(static_cast<_Tp&&>(__object)); 436 } else { 437 __construct_large<_Dp>(static_cast<_Tp&&>(__object)); 438 } 439 } 440 441 template <class _Tp, class... _Args> 442 requires __callable<__create_vtable_t, __mtype<_Vtable>, __mtype<_Tp>> __t(std::in_place_type_t<_Tp>,_Args &&...__args)443 __t(std::in_place_type_t<_Tp>, _Args&&... __args) 444 : __vtable_{__get_vtable_of_type<_Tp>()} { 445 if constexpr (__is_small<_Tp>) { 446 __construct_small<_Tp>(static_cast<_Args&&>(__args)...); 447 } else { 448 __construct_large<_Tp>(static_cast<_Args&&>(__args)...); 449 } 450 } 451 __t(const __t & __other)452 __t(const __t& __other) 453 requires(_Copyable) 454 : __vtable_(__other.__vtable_) { 455 (*__other.__vtable_)(__copy_construct, this, __other); 456 } 457 operator =(const __t & __other)458 auto operator=(const __t& __other) -> __t& 459 requires(_Copyable) 460 { 461 if (&__other != this) { 462 __t tmp(__other); 463 *this = std::move(tmp); 464 } 465 return *this; 466 } 467 __t(__t && __other)468 __t(__t&& __other) noexcept 469 : __vtable_(__other.__vtable_) { 470 (*__other.__vtable_)(__move_construct, this, static_cast<__t&&>(__other)); 471 } 472 operator =(__t && __other)473 auto operator=(__t&& __other) noexcept -> __t& { 474 __reset(); 475 (*__other.__vtable_)(__move_construct, this, static_cast<__t&&>(__other)); 476 return *this; 477 } 478 ~__t()479 ~__t() { 480 __reset(); 481 } 482 __reset()483 void __reset() noexcept { 484 (*__vtable_)(__delete, this); 485 __object_pointer_ = nullptr; 486 __vtable_ = __default_storage_vtable(static_cast<__vtable_t*>(nullptr)); 487 } 488 __get_vtable() const489 auto __get_vtable() const noexcept -> const _Vtable* { 490 return __vtable_; 491 } 492 493 [[nodiscard]] __get_object_pointer() const494 auto __get_object_pointer() const noexcept -> void* { 495 return __object_pointer_; 496 } 497 498 private: 499 template <class _Tp, class... _As> __construct_small(_As &&...__args)500 void __construct_small(_As&&... __args) { 501 static_assert(sizeof(_Tp) <= __buffer_size && alignof(_Tp) <= __alignment); 502 _Tp* __pointer = reinterpret_cast<_Tp*>(&__buffer_[0]); 503 using _Alloc = std::allocator_traits<_Allocator>::template rebind_alloc<_Tp>; 504 _Alloc __alloc{__allocator_}; 505 std::allocator_traits<_Alloc>::construct(__alloc, __pointer, static_cast<_As&&>(__args)...); 506 __object_pointer_ = __pointer; 507 } 508 509 template <class _Tp, class... _As> __construct_large(_As &&...__args)510 void __construct_large(_As&&... __args) { 511 using _Alloc = std::allocator_traits<_Allocator>::template rebind_alloc<_Tp>; 512 _Alloc __alloc{__allocator_}; 513 _Tp* __pointer = std::allocator_traits<_Alloc>::allocate(__alloc, 1); 514 STDEXEC_TRY { 515 std::allocator_traits<_Alloc>::construct( 516 __alloc, __pointer, static_cast<_As&&>(__args)...); 517 } 518 STDEXEC_CATCH_ALL { 519 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1); 520 STDEXEC_THROW(); 521 } 522 __object_pointer_ = __pointer; 523 } 524 525 template <class _Tp> STDEXEC_MEMFN_DECL(void __delete)526 STDEXEC_MEMFN_DECL(void __delete)(this __mtype<_Tp>, __t& __self) noexcept { 527 if (!__self.__object_pointer_) { 528 return; 529 } 530 using _Alloc = std::allocator_traits<_Allocator>::template rebind_alloc<_Tp>; 531 _Alloc __alloc{__self.__allocator_}; 532 _Tp* __pointer = static_cast<_Tp*>(std::exchange(__self.__object_pointer_, nullptr)); 533 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer); 534 if constexpr (!__is_small<_Tp>) { 535 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1); 536 } 537 } 538 539 template <class _Tp> STDEXEC_MEMFN_DECL(void __move_construct)540 STDEXEC_MEMFN_DECL( 541 void __move_construct)(this __mtype<_Tp>, __t& __self, __t&& __other) noexcept { 542 if (!__other.__object_pointer_) { 543 return; 544 } 545 _Tp* __pointer = static_cast<_Tp*>(std::exchange(__other.__object_pointer_, nullptr)); 546 if constexpr (__is_small<_Tp>) { 547 _Tp& __other_object = *__pointer; 548 __self.template __construct_small<_Tp>(static_cast<_Tp&&>(__other_object)); 549 using _Alloc = std::allocator_traits<_Allocator>::template rebind_alloc<_Tp>; 550 _Alloc __alloc{__self.__allocator_}; 551 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer); 552 } else { 553 __self.__object_pointer_ = __pointer; 554 } 555 __self.__vtable_ = std::exchange( 556 __other.__vtable_, __default_storage_vtable(static_cast<__vtable_t*>(nullptr))); 557 } 558 559 template <class _Tp> 560 requires _Copyable STDEXEC_MEMFN_DECL(void __copy_construct)561 STDEXEC_MEMFN_DECL( 562 void __copy_construct)(this __mtype<_Tp>, __t& __self, const __t& __other) { 563 if (!__other.__object_pointer_) { 564 return; 565 } 566 const _Tp& __other_object = *static_cast<const _Tp*>(__other.__object_pointer_); 567 if constexpr (__is_small<_Tp>) { 568 __self.template __construct_small<_Tp>(__other_object); 569 } else { 570 __self.template __construct_large<_Tp>(__other_object); 571 } 572 __self.__vtable_ = __other.__vtable_; 573 } 574 575 const __vtable_t* __vtable_{__default_storage_vtable(static_cast<__vtable_t*>(nullptr))}; 576 void* __object_pointer_{nullptr}; 577 alignas(__alignment) std::byte __buffer_[__buffer_size]{}; STDEXEC_ATTRIBUTE(no_unique_address)578 STDEXEC_ATTRIBUTE(no_unique_address) _Allocator __allocator_ { }; 579 }; 580 581 struct __empty_vtable { 582 template <class _Sender> STDEXEC_MEMFN_DECLexec::__any::__empty_vtable583 STDEXEC_MEMFN_DECL( 584 auto __create_vtable)(this __mtype<__empty_vtable>, __mtype<_Sender>) noexcept 585 -> const __empty_vtable* { 586 static const __empty_vtable __vtable_{}; 587 return &__vtable_; 588 } 589 }; 590 591 template < 592 class _VTable = __empty_vtable, 593 class _Allocator = std::allocator<std::byte>, 594 std::size_t _InlineSize = 3 * sizeof(void*), 595 std::size_t _Alignment = alignof(std::max_align_t) 596 > 597 using __immovable_storage_t = 598 __t<__immovable_storage<_VTable, _Allocator, _InlineSize, _Alignment>>; 599 600 template <class _VTable, class _Allocator = std::allocator<std::byte>> 601 using __unique_storage_t = __t<__storage<_VTable, _Allocator>>; 602 603 template < 604 class _VTable, 605 std::size_t _InlineSize = 3 * sizeof(void*), 606 class _Allocator = std::allocator<std::byte> 607 > 608 using __copyable_storage_t = __t<__storage<_VTable, _Allocator, true, _InlineSize>>; 609 610 template <class _Tag, class... _As> 611 auto __tag_type(_Tag (*)(_As...)) -> _Tag; 612 613 template <class _Tag, class... _As> 614 auto __tag_type(_Tag (*)(_As...) noexcept) -> _Tag; 615 616 template <class _Query> 617 using __tag_type_t = decltype(__tag_type(static_cast<_Query>(nullptr))); 618 619 template <class _Query> 620 concept __is_stop_token_query = __same_as<__tag_type_t<_Query>, get_stop_token_t>; 621 622 template <class _Query> 623 concept __is_not_stop_token_query = !__is_stop_token_query<_Query>; 624 625 template <class _Query> 626 using __is_not_stop_token_query_t = __mbool<__is_not_stop_token_query<_Query>>; 627 628 auto __test_never_stop_token(get_stop_token_t (*)(never_stop_token (*)() noexcept)) 629 -> __mbool<true>; 630 631 template <class _Tag, class _Ret, class... _As> 632 auto __test_never_stop_token(_Tag (*)(_Ret (*)(_As...) noexcept)) -> __mbool<false>; 633 634 template <class _Tag, class _Ret, class... _As> 635 auto __test_never_stop_token(_Tag (*)(_Ret (*)(_As...))) -> __mbool<false>; 636 637 template <class _Query> 638 using __is_never_stop_token_query_t = decltype(__test_never_stop_token( 639 static_cast<_Query>(nullptr))); 640 641 template <class _Query> 642 concept __is_never_stop_token_query = __is_never_stop_token_query_t<_Query>::value; 643 644 template <class _Query, class _Env> 645 concept __satisfies_receiver_stop_token_query = 646 __same_as<__decay_t<__query_result_t<_Env, __tag_type_t<_Query>>>, stop_token_of_t<_Env>>; 647 648 template <class _Query, class... _Env> 649 concept __satisfies_receiver_query = !__is_stop_token_query<_Query> 650 || __is_never_stop_token_query<_Query> 651 || (__satisfies_receiver_stop_token_query<_Query, _Env> || ...); 652 653 namespace __rec { 654 template <class _Sigs, class... _Queries> 655 struct __vtable { 656 class __t; 657 }; 658 659 template <class _Sigs, class... _Queries> 660 struct __ref; 661 662 template <class... _Sigs, class... _Queries> 663 struct __vtable<completion_signatures<_Sigs...>, _Queries...> { 664 struct __t 665 : __overload<__any_::__rcvr_vfun<_Sigs>...> 666 , __query_vfun<_Queries>... { 667 using __query_vfun<_Queries>::operator()...; 668 669 template <class _Tag, class... _As> 670 requires __one_of<_Tag(_As...), _Sigs...> 671 || __callable<__overload<__any_::__rcvr_vfun<_Sigs>...>, void*, _Tag, _As...> 672 void operator()(void* __rcvr, _Tag, _As&&... __as) const noexcept { 673 if constexpr (__one_of<_Tag(_As...), _Sigs...>) { 674 const __any_::__rcvr_vfun<_Tag(_As...)>& __vfun = *this; 675 __vfun(__rcvr, _Tag(), static_cast<_As&&>(__as)...); 676 } else { 677 const __overload<__any_::__rcvr_vfun<_Sigs>...>& __vfun = *this; 678 __vfun(__rcvr, _Tag(), static_cast<_As&&>(__as)...); 679 } 680 } 681 682 private: 683 template <class _Rcvr> 684 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> 685 && (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...) STDEXEC_MEMFN_DECLexec::__any::__rec::__vtable::__t686 STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__t>, __mtype<_Rcvr>) noexcept 687 -> const __t* { 688 static const __t __vtable_{ 689 {{__any_::__rcvr_vfun_fn( 690 static_cast<_Rcvr*>(nullptr), static_cast<_Sigs*>(nullptr))}...}, 691 {__query_vfun_fn<_Rcvr>{}(static_cast<_Queries>(nullptr))}...}; 692 return &__vtable_; 693 } 694 }; 695 }; 696 697 template <class... _Sigs, class... _Queries> 698 requires(__is_not_stop_token_query<_Queries> && ...) 699 struct __ref<completion_signatures<_Sigs...>, _Queries...> { 700 #if !STDEXEC_MSVC() 701 // MSVCBUG https://developercommunity.visualstudio.com/t/Private-member-inaccessible-when-used-in/10448363 702 private: 703 #endif 704 using __vtable_t = stdexec::__t<__vtable<completion_signatures<_Sigs...>, _Queries...>>; 705 706 struct __env_t { 707 const __vtable_t* __vtable_; 708 void* __rcvr_; 709 inplace_stop_token __token_; 710 711 template <class _Tag, class... _As> 712 requires __callable<const __vtable_t&, _Tag, void*, _As...> queryexec::__any::__rec::__ref::__env_t713 auto query(_Tag, _As&&... __as) const 714 noexcept(__nothrow_callable<const __vtable_t&, _Tag, void*, _As...>) 715 -> __call_result_t<const __vtable_t&, _Tag, void*, _As...> { 716 return (*__vtable_)(_Tag{}, __rcvr_, static_cast<_As&&>(__as)...); 717 } 718 719 [[nodiscard]] queryexec::__any::__rec::__ref::__env_t720 auto query(get_stop_token_t) const noexcept -> inplace_stop_token { 721 return __token_; 722 } 723 } __env_; 724 public: 725 using receiver_concept = stdexec::receiver_t; 726 using __id = __ref; 727 using __t = __ref; 728 729 template <__none_of<__ref, const __ref, __env_t, const __env_t> _Rcvr> 730 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> 731 && (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...) __refexec::__any::__rec::__ref732 __ref(_Rcvr& __rcvr) noexcept 733 : __env_{ 734 __create_vtable(__mtype<__vtable_t>{}, __mtype<_Rcvr>{}), 735 &__rcvr, 736 stdexec::get_stop_token(stdexec::get_env(__rcvr))} { 737 } 738 739 template <class... _As> 740 requires __callable<__vtable_t, void*, set_value_t, _As...> set_valueexec::__any::__rec::__ref741 void set_value(_As&&... __as) noexcept { 742 (*__env_.__vtable_)(__env_.__rcvr_, set_value_t(), static_cast<_As&&>(__as)...); 743 } 744 745 template <class _Error> 746 requires __callable<__vtable_t, void*, set_error_t, _Error> set_errorexec::__any::__rec::__ref747 void set_error(_Error&& __err) noexcept { 748 (*__env_.__vtable_)(__env_.__rcvr_, set_error_t(), static_cast<_Error&&>(__err)); 749 } 750 set_stoppedexec::__any::__rec::__ref751 void set_stopped() noexcept 752 requires __callable<__vtable_t, void*, set_stopped_t> 753 { 754 (*__env_.__vtable_)(__env_.__rcvr_, set_stopped_t()); 755 } 756 get_envexec::__any::__rec::__ref757 auto get_env() const noexcept -> const __env_t& { 758 return __env_; 759 } 760 }; 761 762 template <class... _Sigs, class... _Queries> 763 requires(__is_stop_token_query<_Queries> || ...) 764 struct __ref<completion_signatures<_Sigs...>, _Queries...> { 765 #if !STDEXEC_MSVC() 766 // MSVCBUG https://developercommunity.visualstudio.com/t/Private-member-inaccessible-when-used-in/10448363 767 768 private: 769 #endif 770 using _FilteredQueries = 771 __minvoke<__mremove_if<__q<__is_never_stop_token_query_t>>, _Queries...>; 772 using __vtable_t = stdexec::__t< 773 __mapply<__mbind_front_q<__vtable, completion_signatures<_Sigs...>>, _FilteredQueries> 774 >; 775 776 struct __env_t { 777 const __vtable_t* __vtable_; 778 void* __rcvr_; 779 780 template <class _Tag, class... _As> 781 requires __callable<const __vtable_t&, _Tag, void*, _As...> queryexec::__any::__rec::__ref::__env_t782 auto query(_Tag, _As&&... __as) const 783 noexcept(__nothrow_callable<const __vtable_t&, _Tag, void*, _As...>) 784 -> __call_result_t<const __vtable_t&, _Tag, void*, _As...> { 785 return (*__vtable_)(_Tag{}, __rcvr_, static_cast<_As&&>(__as)...); 786 } 787 } __env_; 788 public: 789 using receiver_concept = stdexec::receiver_t; 790 using __id = __ref; 791 using __t = __ref; 792 793 template <__none_of<__ref, const __ref, __env_t, const __env_t> _Rcvr> 794 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> 795 && (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...) __refexec::__any::__rec::__ref796 __ref(_Rcvr& __rcvr) noexcept 797 : __env_{__create_vtable(__mtype<__vtable_t>{}, __mtype<_Rcvr>{}), &__rcvr} { 798 } 799 800 template <class... _As> 801 requires __one_of<set_value_t(_As...), _Sigs...> 802 || __callable<__overload<__any_::__rcvr_vfun<_Sigs>...>, void*, set_value_t, _As...> 803 void set_value(_As&&... __as) noexcept { 804 if constexpr (__one_of<set_value_t(_As...), _Sigs...>) { 805 const __any_::__rcvr_vfun<set_value_t(_As...)>& __vfun = *__env_.__vtable_; 806 __vfun(__env_.__rcvr_, set_value_t(), static_cast<_As&&>(__as)...); 807 } else { 808 const __overload<__any_::__rcvr_vfun<_Sigs>...>& __vfun = *__env_.__vtable_; 809 __vfun(__env_.__rcvr_, set_value_t(), static_cast<_As&&>(__as)...); 810 } 811 } 812 813 template <class _Error> 814 requires __one_of<set_error_t(_Error), _Sigs...> 815 || __callable<__overload<__any_::__rcvr_vfun<_Sigs>...>, void*, set_error_t, _Error> 816 void set_error(_Error&& __err) noexcept { 817 if constexpr (__one_of<set_error_t(_Error), _Sigs...>) { 818 const __any_::__rcvr_vfun<set_error_t(_Error)>& __vfun = *__env_.__vtable_; 819 __vfun(__env_.__rcvr_, set_error_t(), static_cast<_Error&&>(__err)); 820 } else { 821 const __overload<__any_::__rcvr_vfun<_Sigs>...>& __vfun = *__env_.__vtable_; 822 __vfun(__env_.__rcvr_, set_error_t(), static_cast<_Error&&>(__err)); 823 } 824 } 825 set_stoppedexec::__any::__rec::__ref826 void set_stopped() noexcept 827 requires __one_of<set_stopped_t(), _Sigs...> 828 { 829 const __any_::__rcvr_vfun<set_stopped_t()>& __vfun = *__env_.__vtable_; 830 __vfun(__env_.__rcvr_, set_stopped_t()); 831 } 832 get_envexec::__any::__rec::__ref833 auto get_env() const noexcept -> const __env_t& { 834 return __env_; 835 } 836 }; 837 } // namespace __rec 838 839 class __operation_vtable { 840 public: 841 void (*__start_)(void*) noexcept; 842 843 private: 844 template <class _Op> STDEXEC_MEMFN_DECL(auto __create_vtable)845 STDEXEC_MEMFN_DECL( 846 auto __create_vtable)(this __mtype<__operation_vtable>, __mtype<_Op>) noexcept 847 -> const __operation_vtable* { 848 static __operation_vtable __vtable{[](void* __object_pointer) noexcept -> void { 849 STDEXEC_ASSERT(__object_pointer); 850 _Op& __op = *static_cast<_Op*>(__object_pointer); 851 static_assert(operation_state<_Op>); 852 stdexec::start(__op); 853 }}; 854 return &__vtable; 855 } 856 }; 857 858 using __immovable_operation_storage = 859 __immovable_storage_t<__operation_vtable, std::allocator<std::byte>, 6 * sizeof(void*)>; 860 861 template <class _Sigs, class _Queries> 862 using __receiver_ref = __mapply<__mbind_front_q<__rec::__ref, _Sigs>, _Queries>; 863 864 struct __on_stop_t { 865 stdexec::inplace_stop_source& __source_; 866 operator ()exec::__any::__on_stop_t867 void operator()() const noexcept { 868 __source_.request_stop(); 869 } 870 }; 871 872 template <class _Receiver> 873 struct __operation_base { 874 STDEXEC_ATTRIBUTE(no_unique_address) _Receiver __rcvr_; 875 stdexec::inplace_stop_source __stop_source_{}; 876 using __stop_callback = 877 stdexec::stop_token_of_t<stdexec::env_of_t<_Receiver>>::template callback_type<__on_stop_t>; 878 std::optional<__stop_callback> __on_stop_{}; 879 }; 880 881 template <class _Env> 882 using __env_t = __join_env_t<prop<get_stop_token_t, inplace_stop_token>, _Env>; 883 884 template <class _ReceiverId> 885 struct __stoppable_receiver { 886 using _Receiver = stdexec::__t<_ReceiverId>; 887 888 struct __t { 889 using receiver_concept = stdexec::receiver_t; 890 __operation_base<_Receiver>* __op_; 891 892 template <class _Item> 893 requires __callable<set_next_t, _Receiver&, _Item> 894 [[nodiscard]] set_nextexec::__any::__stoppable_receiver::__t895 auto set_next(_Item&& __item) & noexcept(__nothrow_callable<set_next_t, _Receiver&, _Item>) 896 -> __call_result_t<set_next_t, _Receiver&, _Item> { 897 return exec::set_next(__op_->__rcvr_, static_cast<_Item&&>(__item)); 898 } 899 900 template <class... _Args> 901 requires __callable<set_value_t, _Receiver, _Args...> set_valueexec::__any::__stoppable_receiver::__t902 void set_value(_Args&&... __args) noexcept { 903 __op_->__on_stop_.reset(); 904 stdexec::set_value( 905 static_cast<_Receiver&&>(__op_->__rcvr_), static_cast<_Args&&>(__args)...); 906 } 907 908 template <class _Error> 909 requires __callable<set_error_t, _Receiver, _Error> set_errorexec::__any::__stoppable_receiver::__t910 void set_error(_Error&& __err) noexcept { 911 __op_->__on_stop_.reset(); 912 stdexec::set_error( 913 static_cast<_Receiver&&>(__op_->__rcvr_), static_cast<_Error&&>(__err)); 914 } 915 set_stoppedexec::__any::__stoppable_receiver::__t916 void set_stopped() noexcept 917 requires __callable<set_stopped_t, _Receiver> 918 { 919 __op_->__on_stop_.reset(); 920 stdexec::set_stopped(static_cast<_Receiver&&>(__op_->__rcvr_)); 921 } 922 get_envexec::__any::__stoppable_receiver::__t923 auto get_env() const noexcept -> __env_t<env_of_t<_Receiver>> { 924 return __env::__join( 925 prop{get_stop_token, __op_->__stop_source_.get_token()}, 926 stdexec::get_env(__op_->__rcvr_)); 927 } 928 }; 929 }; 930 931 template <class _ReceiverId> 932 using __stoppable_receiver_t = stdexec::__t<__stoppable_receiver<_ReceiverId>>; 933 934 template <class _ReceiverId, bool> 935 struct __operation { 936 using _Receiver = stdexec::__t<_ReceiverId>; 937 938 class __t : public __operation_base<_Receiver> { 939 public: 940 using __id = __operation; 941 942 template <class _Sender> __t(_Sender && __sender,_Receiver && __receiver)943 __t(_Sender&& __sender, _Receiver&& __receiver) 944 : __operation_base<_Receiver>{static_cast<_Receiver&&>(__receiver)} 945 , __rec_{this} 946 , __storage_{__sender.__connect(__rec_)} { 947 } 948 start()949 void start() & noexcept { 950 this->__on_stop_.emplace( 951 stdexec::get_stop_token(stdexec::get_env(this->__rcvr_)), 952 __on_stop_t{this->__stop_source_}); 953 STDEXEC_ASSERT(__storage_.__get_vtable()->__start_); 954 __storage_.__get_vtable()->__start_(__storage_.__get_object_pointer()); 955 } 956 957 private: 958 __stoppable_receiver_t<_ReceiverId> __rec_; 959 __immovable_operation_storage __storage_{}; 960 }; 961 }; 962 963 template <class _ReceiverId> 964 struct __operation<_ReceiverId, false> { 965 using _Receiver = stdexec::__t<_ReceiverId>; 966 967 class __t { 968 public: 969 using __id = __operation; 970 971 template <class _Sender> __t(_Sender && __sender,_Receiver && __receiver)972 __t(_Sender&& __sender, _Receiver&& __receiver) 973 : __rec_{static_cast<_Receiver&&>(__receiver)} 974 , __storage_{__sender.__connect(__rec_)} { 975 } 976 start()977 void start() & noexcept { 978 STDEXEC_ASSERT(__storage_.__get_vtable()->__start_); 979 __storage_.__get_vtable()->__start_(__storage_.__get_object_pointer()); 980 } 981 982 private: 983 STDEXEC_ATTRIBUTE(no_unique_address) _Receiver __rec_; 984 __immovable_operation_storage __storage_{}; 985 }; 986 }; 987 988 template <class _Queries, bool _IsEnvProvider = true> 989 class __query_vtable; 990 991 template <template <class...> class _List, class... _Queries, bool _IsEnvProvider> 992 class __query_vtable<_List<_Queries...>, _IsEnvProvider> : public __query_vfun<_Queries>... { 993 public: 994 using __query_vfun<_Queries>::operator()...; 995 private: 996 template <class _Queryable> 997 requires(__callable<__query_vfun_fn<_Queryable, _IsEnvProvider>, _Queries> && ...) STDEXEC_MEMFN_DECL(auto __create_vtable)998 STDEXEC_MEMFN_DECL( 999 auto __create_vtable)(this __mtype<__query_vtable>, __mtype<_Queryable>) noexcept 1000 -> const __query_vtable* { 1001 static const __query_vtable __vtable{ 1002 {__query_vfun_fn<_Queryable, _IsEnvProvider>{}(static_cast<_Queries>(nullptr))}...}; 1003 return &__vtable; 1004 } 1005 }; 1006 1007 template <class _Sigs, class _SenderQueries = __types<>, class _ReceiverQueries = __types<>> 1008 struct __sender { 1009 using __receiver_ref_t = __receiver_ref<_Sigs, _ReceiverQueries>; 1010 static constexpr bool __with_inplace_stop_token = 1011 __v<__mapply<__mall_of<__q<__is_not_stop_token_query_t>>, _ReceiverQueries>>; 1012 1013 class __vtable : public __query_vtable<_SenderQueries> { 1014 public: 1015 using __id = __vtable; 1016 __queries() const1017 auto __queries() const noexcept -> const __query_vtable<_SenderQueries>& { 1018 return *this; 1019 } 1020 1021 __immovable_operation_storage (*__connect_)(void*, __receiver_ref_t); 1022 private: 1023 template <sender_to<__receiver_ref_t> _Sender> STDEXEC_MEMFN_DECL(auto __create_vtable)1024 STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__vtable>, __mtype<_Sender>) noexcept 1025 -> const __vtable* { 1026 static const __vtable __vtable_{ 1027 {*__create_vtable(__mtype<__query_vtable<_SenderQueries>>{}, __mtype<_Sender>{})}, 1028 [](void* __object_pointer, __receiver_ref_t __receiver) 1029 -> __immovable_operation_storage { 1030 _Sender& __sender = *static_cast<_Sender*>(__object_pointer); 1031 using __op_state_t = connect_result_t<_Sender, __receiver_ref_t>; 1032 return __immovable_operation_storage{ 1033 std::in_place_type<__op_state_t>, __emplace_from{[&] { 1034 return stdexec::connect( 1035 static_cast<_Sender&&>(__sender), static_cast<__receiver_ref_t&&>(__receiver)); 1036 }}}; 1037 }}; 1038 return &__vtable_; 1039 } 1040 }; 1041 1042 struct __env_t { 1043 const __vtable* __vtable_; 1044 void* __sender_; 1045 1046 template <class _Tag, class... _As> 1047 requires __callable<const __query_vtable<_SenderQueries>&, _Tag, void*, _As...> queryexec::__any::__sender::__env_t1048 auto query(_Tag, _As&&... __as) const 1049 noexcept(__nothrow_callable<const __query_vtable<_SenderQueries>&, _Tag, void*, _As...>) 1050 -> __call_result_t<const __query_vtable<_SenderQueries>&, _Tag, void*, _As...> { 1051 return __vtable_->__queries()(_Tag{}, __sender_, static_cast<_As&&>(__as)...); 1052 } 1053 }; 1054 1055 struct __t { 1056 using __id = __sender; 1057 using completion_signatures = _Sigs; 1058 using sender_concept = stdexec::sender_t; 1059 1060 __t(const __t&) = delete; 1061 auto operator=(const __t&) -> __t& = delete; 1062 1063 __t(__t&&) = default; 1064 auto operator=(__t&&) -> __t& = default; 1065 1066 template <__not_decays_to<__t> _Sender> 1067 requires sender_to<_Sender, __receiver_ref<_Sigs, _ReceiverQueries>> __texec::__any::__sender::__t1068 __t(_Sender&& __sndr) 1069 : __storage_{static_cast<_Sender&&>(__sndr)} { 1070 } 1071 __connectexec::__any::__sender::__t1072 auto __connect(__receiver_ref_t __receiver) -> __immovable_operation_storage { 1073 return __storage_.__get_vtable()->__connect_( 1074 __storage_.__get_object_pointer(), static_cast<__receiver_ref_t&&>(__receiver)); 1075 } 1076 get_envexec::__any::__sender::__t1077 auto get_env() const noexcept -> __env_t { 1078 return {__storage_.__get_vtable(), __storage_.__get_object_pointer()}; 1079 } 1080 1081 template <receiver_of<_Sigs> _Rcvr> connectexec::__any::__sender::__t1082 auto connect(_Rcvr __rcvr) && -> stdexec::__t< 1083 __operation<stdexec::__id<_Rcvr>, __with_inplace_stop_token> 1084 > { 1085 return {static_cast<__t&&>(*this), static_cast<_Rcvr&&>(__rcvr)}; 1086 } 1087 1088 private: 1089 __unique_storage_t<__vtable> __storage_; 1090 }; 1091 }; 1092 1093 template <class _ScheduleSender, class _SchedulerQueries = __types<>> 1094 class __scheduler { 1095 static constexpr std::size_t __buffer_size = 4 * sizeof(void*); 1096 template <class _Ty> 1097 static constexpr bool __is_small = sizeof(_Ty) <= __buffer_size 1098 && alignof(_Ty) <= alignof(std::max_align_t); 1099 1100 public: 1101 template <class _Scheduler> 1102 requires(!__decays_to<_Scheduler, __scheduler>) && scheduler<_Scheduler> __scheduler(_Scheduler && __scheduler)1103 __scheduler(_Scheduler&& __scheduler) 1104 : __storage_{static_cast<_Scheduler&&>(__scheduler)} { 1105 static_assert( 1106 __is_small<_Scheduler>, 1107 "any_scheduler<> must have a nothrow copy constructor, so the scheduler object must be " 1108 "small enough to be stored in the internal buffer to avoid dynamic allocation."); 1109 } 1110 1111 __scheduler(__scheduler&&) noexcept = default; 1112 __scheduler(const __scheduler&) noexcept = default; 1113 auto operator=(__scheduler&&) noexcept -> __scheduler& = default; 1114 auto operator=(const __scheduler&) noexcept -> __scheduler& = default; 1115 1116 using __sender_t = _ScheduleSender; 1117 schedule() const1118 auto schedule() const noexcept -> __sender_t { 1119 STDEXEC_ASSERT(__storage_.__get_vtable()->__schedule_); 1120 return __storage_.__get_vtable()->__schedule_(__storage_.__get_object_pointer()); 1121 } 1122 1123 template <class _Tag, class... _As> 1124 requires __callable<const __query_vtable<_SchedulerQueries, false>&, _Tag, void*, _As...> query(_Tag,_As &&...__as) const1125 auto query(_Tag, _As&&... __as) const noexcept( 1126 __nothrow_callable<const __query_vtable<_SchedulerQueries, false>&, _Tag, void*, _As...>) 1127 -> __call_result_t<const __query_vtable<_SchedulerQueries, false>&, _Tag, void*, _As...> { 1128 return __storage_.__get_vtable() 1129 ->__queries()(_Tag{}, __storage_.__get_object_pointer(), static_cast<_As&&>(__as)...); 1130 } 1131 1132 private: 1133 class __vtable : public __query_vtable<_SchedulerQueries, false> { 1134 public: 1135 __sender_t (*__schedule_)(void*) noexcept; 1136 bool (*__equal_to_)(const void*, const void* other) noexcept; 1137 __queries() const1138 auto __queries() const noexcept -> const __query_vtable<_SchedulerQueries, false>& { 1139 return *this; 1140 } 1141 private: 1142 template <scheduler _Scheduler> STDEXEC_MEMFN_DECL(auto __create_vtable)1143 STDEXEC_MEMFN_DECL( 1144 auto __create_vtable)(this __mtype<__vtable>, __mtype<_Scheduler>) noexcept 1145 -> const __vtable* { 1146 static const __vtable __vtable_{ 1147 {*__create_vtable( 1148 __mtype<__query_vtable<_SchedulerQueries, false>>{}, __mtype<_Scheduler>{})}, 1149 [](void* __object_pointer) noexcept -> __sender_t { 1150 const _Scheduler& __scheduler = *static_cast<const _Scheduler*>(__object_pointer); 1151 return __sender_t{stdexec::schedule(__scheduler)}; 1152 }, 1153 [](const void* __self, const void* __other) noexcept -> bool { 1154 static_assert( 1155 noexcept(__declval<const _Scheduler&>() == __declval<const _Scheduler&>())); 1156 STDEXEC_ASSERT(__self && __other); 1157 const _Scheduler& __self_scheduler = *static_cast<const _Scheduler*>(__self); 1158 const _Scheduler& __other_scheduler = *static_cast<const _Scheduler*>(__other); 1159 return __self_scheduler == __other_scheduler; 1160 }}; 1161 return &__vtable_; 1162 } 1163 }; 1164 1165 friend auto operator ==(const __scheduler & __self,const __scheduler & __other)1166 operator==(const __scheduler& __self, const __scheduler& __other) noexcept -> bool { 1167 if (__self.__storage_.__get_vtable() != __other.__storage_.__get_vtable()) { 1168 return false; 1169 } 1170 1171 void* __p = __self.__storage_.__get_object_pointer(); 1172 void* __o = __other.__storage_.__get_object_pointer(); 1173 // if both object pointers are not null, use the virtual equal_to function 1174 return (__p && __o && __self.__storage_.__get_vtable()->__equal_to_(__p, __o)) 1175 // if both object pointers are nullptrs, they are equal 1176 || (!__p && !__o); 1177 } 1178 1179 friend auto operator !=(const __scheduler & __self,const __scheduler & __other)1180 operator!=(const __scheduler& __self, const __scheduler& __other) noexcept -> bool { 1181 return !(__self == __other); 1182 } 1183 1184 __copyable_storage_t<__vtable, __buffer_size> __storage_{}; 1185 }; 1186 } // namespace __any 1187 1188 template <auto... _Sigs> 1189 using queries = stdexec::__types<decltype(_Sigs)...>; 1190 1191 template <class _Completions, auto... _ReceiverQueries> 1192 class any_receiver_ref { 1193 using __receiver_base = __any::__rec::__ref<_Completions, decltype(_ReceiverQueries)...>; 1194 using __env_t = stdexec::env_of_t<__receiver_base>; 1195 __receiver_base __receiver_; 1196 1197 public: 1198 using receiver_concept = stdexec::receiver_t; 1199 using __t = any_receiver_ref; 1200 using __id = any_receiver_ref; 1201 1202 template <stdexec::__none_of<any_receiver_ref, const any_receiver_ref, __env_t, const __env_t> 1203 _Receiver> 1204 requires stdexec::receiver_of<_Receiver, _Completions> any_receiver_ref(_Receiver & __receiver)1205 any_receiver_ref(_Receiver& __receiver) 1206 noexcept(stdexec::__nothrow_constructible_from<__receiver_base, _Receiver>) 1207 : __receiver_(__receiver) { 1208 } 1209 1210 template <class... _As> 1211 requires stdexec::__callable<stdexec::set_value_t, __receiver_base, _As...> set_value(_As &&...__as)1212 void set_value(_As&&... __as) noexcept { 1213 stdexec::set_value(static_cast<__receiver_base&&>(__receiver_), static_cast<_As&&>(__as)...); 1214 } 1215 1216 template <class _Error> 1217 requires stdexec::__callable<stdexec::set_error_t, __receiver_base, _Error> set_error(_Error && __err)1218 void set_error(_Error&& __err) noexcept { 1219 stdexec::set_error(static_cast<__receiver_base&&>(__receiver_), static_cast<_Error&&>(__err)); 1220 } 1221 set_stopped()1222 void set_stopped() noexcept 1223 requires stdexec::__callable<stdexec::set_stopped_t, __receiver_base> 1224 { 1225 stdexec::set_stopped(static_cast<__receiver_base&&>(__receiver_)); 1226 } 1227 get_env() const1228 auto get_env() const noexcept -> stdexec::env_of_t<__receiver_base> { 1229 return stdexec::get_env(__receiver_); 1230 } 1231 1232 template <auto... _SenderQueries> 1233 class any_sender { 1234 using __sender_base = stdexec::__t< 1235 __any::__sender<_Completions, queries<_SenderQueries...>, queries<_ReceiverQueries...>> 1236 >; 1237 __sender_base __sender_; 1238 1239 public: 1240 using sender_concept = stdexec::sender_t; 1241 using __t = any_sender; 1242 using __id = any_sender; 1243 1244 template <stdexec::__not_decays_to<any_sender> _Sender> 1245 requires stdexec::sender_to<_Sender, __receiver_base> any_sender(_Sender && __sender)1246 any_sender(_Sender&& __sender) 1247 noexcept(stdexec::__nothrow_constructible_from<__sender_base, _Sender>) 1248 : __sender_(static_cast<_Sender&&>(__sender)) { 1249 } 1250 1251 template <stdexec::__decays_to<any_sender> _Self, class... _Env> 1252 requires(__any::__satisfies_receiver_query<decltype(_ReceiverQueries), _Env...> && ...) get_completion_signatures(_Self &&,_Env &&...)1253 static auto get_completion_signatures(_Self&&, _Env&&...) noexcept 1254 -> __sender_base::completion_signatures { 1255 return {}; 1256 } 1257 1258 template <stdexec::receiver_of<_Completions> _Receiver> connect(_Receiver __rcvr)1259 auto connect(_Receiver __rcvr) && -> stdexec::connect_result_t<__sender_base, _Receiver> { 1260 return static_cast<__sender_base&&>(__sender_).connect(static_cast<_Receiver&&>(__rcvr)); 1261 } 1262 get_env() const1263 auto get_env() const noexcept -> stdexec::env_of_t<__sender_base> { 1264 return static_cast<const __sender_base&>(__sender_).get_env(); 1265 } 1266 1267 template <auto... _SchedulerQueries> 1268 class any_scheduler { 1269 // Add the required set_value_t() completions to the schedule-sender. 1270 using __schedule_completions = stdexec::__concat_completion_signatures< 1271 _Completions, 1272 stdexec::completion_signatures<stdexec::set_value_t()> 1273 >; 1274 using __schedule_receiver = any_receiver_ref<__schedule_completions, _ReceiverQueries...>; 1275 1276 template <typename _Tag, typename _Sig> 1277 static auto __ret_fn(_Tag (*const)(_Sig)) -> _Tag; 1278 1279 template <class _Tag> 1280 struct __ret_equals_to { 1281 template <class _Sig> 1282 using __f = 1283 stdexec::__mbool<STDEXEC_IS_SAME(_Tag, decltype(__ret_fn(static_cast<_Sig>(nullptr))))>; 1284 }; 1285 1286 using __schedule_sender_queries = stdexec::__minvoke< 1287 stdexec::__mremove_if< 1288 __ret_equals_to<stdexec::get_completion_scheduler_t<stdexec::set_value_t>> 1289 >, 1290 decltype(_SenderQueries)... 1291 >; 1292 1293 #if STDEXEC_MSVC() 1294 // MSVCBUG https://developercommunity.visualstudio.com/t/ICE-and-non-ICE-bug-in-NTTP-argument-w/10361081 1295 1296 static constexpr auto __any_scheduler_noexcept_signature = 1297 stdexec::get_completion_scheduler<stdexec::set_value_t>.signature<any_scheduler() noexcept>; 1298 template <class... _Queries> 1299 using __schedule_sender_fn = 1300 __schedule_receiver::template any_sender<__any_scheduler_noexcept_signature>; 1301 #else 1302 template <class... _Queries> 1303 using __schedule_sender_fn = __schedule_receiver::template any_sender< 1304 stdexec::get_completion_scheduler<stdexec::set_value_t>.template signature<any_scheduler() noexcept> 1305 >; 1306 #endif 1307 using __schedule_sender = 1308 stdexec::__mapply<stdexec::__q<__schedule_sender_fn>, __schedule_sender_queries>; 1309 1310 using __scheduler_base = 1311 __any::__scheduler<__schedule_sender, queries<_SchedulerQueries...>>; 1312 1313 __scheduler_base __scheduler_; 1314 1315 public: 1316 using scheduler_concept = stdexec::scheduler_t; 1317 using __t = any_scheduler; 1318 using __id = any_scheduler; 1319 1320 template <stdexec::__none_of<any_scheduler> _Scheduler> 1321 requires stdexec::scheduler<_Scheduler> any_scheduler(_Scheduler __scheduler)1322 any_scheduler(_Scheduler __scheduler) 1323 : __scheduler_{static_cast<_Scheduler&&>(__scheduler)} { 1324 } 1325 schedule() const1326 auto schedule() const noexcept -> __schedule_sender { 1327 return __scheduler_.schedule(); 1328 } 1329 1330 template <class _Tag, class... _As> 1331 requires stdexec::__queryable_with<const __scheduler_base&, _Tag, _As...> query(_Tag,_As &&...__as) const1332 auto query(_Tag, _As&&... __as) const 1333 noexcept(stdexec::__nothrow_queryable_with<const __scheduler_base&, _Tag, _As...>) 1334 -> stdexec::__query_result_t<const __scheduler_base&, _Tag, _As...> { 1335 return __scheduler_.query(_Tag{}, static_cast<_As&&>(__as)...); 1336 } 1337 1338 auto operator==(const any_scheduler&) const noexcept -> bool = default; 1339 }; 1340 }; 1341 }; 1342 } // namespace exec 1343