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>,
39 __mtype<_Tp>) const noexcept -> 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>
323 static constexpr auto
__get_vtable_of_type()324 __get_vtable_of_type() noexcept -> 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)
429 (this __mtype<_Tp>, __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) -> __t&
542 requires(_Copyable)
543 {
544 if (&__other != this)
545 {
546 __t tmp(__other);
547 *this = std::move(tmp);
548 }
549 return *this;
550 }
551
__t(__t && __other)552 __t(__t&& __other) noexcept
553 {
554 (*__other.__vtable_)(__move_construct, this,
555 static_cast<__t&&>(__other));
556 }
557
operator =(__t && __other)558 auto operator=(__t&& __other) noexcept -> __t&
559 {
560 __reset();
561 (*__other.__vtable_)(__move_construct, this,
562 static_cast<__t&&>(__other));
563 return *this;
564 }
565
~__t()566 ~__t()
567 {
568 __reset();
569 }
570
__reset()571 void __reset() noexcept
572 {
573 (*__vtable_)(__delete, this);
574 __object_pointer_ = nullptr;
575 __vtable_ = __default_storage_vtable(static_cast<__vtable_t*>(nullptr));
576 }
577
__get_vtable() const578 auto __get_vtable() const noexcept -> const _Vtable*
579 {
580 return __vtable_;
581 }
582
__get_object_pointer() const583 [[nodiscard]] auto __get_object_pointer() const noexcept -> void*
584 {
585 return __object_pointer_;
586 }
587
588 private:
589 template <class _Tp, class... _As>
__construct_small(_As &&...__args)590 void __construct_small(_As&&... __args)
591 {
592 static_assert(sizeof(_Tp) <= __buffer_size &&
593 alignof(_Tp) <= __alignment);
594 _Tp* __pointer = static_cast<_Tp*>(static_cast<void*>(&__buffer_[0]));
595 using _Alloc = typename std::allocator_traits<
596 _Allocator>::template rebind_alloc<_Tp>;
597 _Alloc __alloc{__allocator_};
598 std::allocator_traits<_Alloc>::construct(__alloc, __pointer,
599 static_cast<_As&&>(__args)...);
600 __object_pointer_ = __pointer;
601 }
602
603 template <class _Tp, class... _As>
__construct_large(_As &&...__args)604 void __construct_large(_As&&... __args)
605 {
606 using _Alloc = typename std::allocator_traits<
607 _Allocator>::template rebind_alloc<_Tp>;
608 _Alloc __alloc{__allocator_};
609 _Tp* __pointer = std::allocator_traits<_Alloc>::allocate(__alloc, 1);
610 try
611 {
612 std::allocator_traits<_Alloc>::construct(
613 __alloc, __pointer, static_cast<_As&&>(__args)...);
614 }
615 catch (...)
616 {
617 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1);
618 throw;
619 }
620 __object_pointer_ = __pointer;
621 }
622
623 template <class _Tp>
STDEXEC_MEMFN_DECL(void __delete)624 STDEXEC_MEMFN_DECL(void __delete)
625 (this __mtype<_Tp>, __t& __self) noexcept
626 {
627 if (!__self.__object_pointer_)
628 {
629 return;
630 }
631 using _Alloc = typename std::allocator_traits<
632 _Allocator>::template rebind_alloc<_Tp>;
633 _Alloc __alloc{__self.__allocator_};
634 _Tp* __pointer =
635 static_cast<_Tp*>(std::exchange(__self.__object_pointer_, nullptr));
636 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer);
637 if constexpr (!__is_small<_Tp>)
638 {
639 std::allocator_traits<_Alloc>::deallocate(__alloc, __pointer, 1);
640 }
641 }
642
643 template <class _Tp>
STDEXEC_MEMFN_DECL(void __move_construct)644 STDEXEC_MEMFN_DECL(void __move_construct)
645 (this __mtype<_Tp>, __t& __self, __t&& __other) noexcept
646 {
647 if (!__other.__object_pointer_)
648 {
649 return;
650 }
651 _Tp* __pointer = static_cast<_Tp*>(
652 std::exchange(__other.__object_pointer_, nullptr));
653 if constexpr (__is_small<_Tp>)
654 {
655 _Tp& __other_object = *__pointer;
656 __self.template __construct_small<_Tp>(
657 static_cast<_Tp&&>(__other_object));
658 using _Alloc = typename std::allocator_traits<
659 _Allocator>::template rebind_alloc<_Tp>;
660 _Alloc __alloc{__self.__allocator_};
661 std::allocator_traits<_Alloc>::destroy(__alloc, __pointer);
662 }
663 else
664 {
665 __self.__object_pointer_ = __pointer;
666 }
667 __self.__vtable_ = std::exchange(
668 __other.__vtable_,
669 __default_storage_vtable(static_cast<__vtable_t*>(nullptr)));
670 }
671
672 template <class _Tp>
673 requires _Copyable
STDEXEC_MEMFN_DECL(void __copy_construct)674 STDEXEC_MEMFN_DECL(void __copy_construct)
675 (this __mtype<_Tp>, __t& __self, const __t& __other)
676 {
677 if (!__other.__object_pointer_)
678 {
679 return;
680 }
681 const _Tp& __other_object =
682 *static_cast<const _Tp*>(__other.__object_pointer_);
683 if constexpr (__is_small<_Tp>)
684 {
685 __self.template __construct_small<_Tp>(__other_object);
686 }
687 else
688 {
689 __self.template __construct_large<_Tp>(__other_object);
690 }
691 __self.__vtable_ = __other.__vtable_;
692 }
693
694 const __vtable_t* __vtable_{
695 __default_storage_vtable(static_cast<__vtable_t*>(nullptr))};
696 void* __object_pointer_{nullptr};
697 alignas(__alignment) std::byte __buffer_[__buffer_size]{};
698 STDEXEC_ATTRIBUTE((no_unique_address))
699 _Allocator __allocator_{};
700 };
701
702 struct __empty_vtable
703 {
704 template <class _Sender>
STDEXEC_MEMFN_DECLexec::__any::__empty_vtable705 STDEXEC_MEMFN_DECL(auto __create_vtable)
706 (this __mtype<__empty_vtable>,
707 __mtype<_Sender>) noexcept -> const __empty_vtable*
708 {
709 static const __empty_vtable __vtable_{};
710 return &__vtable_;
711 }
712 };
713
714 template <class _VTable = __empty_vtable,
715 class _Allocator = std::allocator<std::byte>>
716 using __immovable_storage_t = __t<__immovable_storage<_VTable, _Allocator>>;
717
718 template <class _VTable, class _Allocator = std::allocator<std::byte>>
719 using __unique_storage_t = __t<__storage<_VTable, _Allocator>>;
720
721 template <class _VTable, std::size_t _InlineSize = 3 * sizeof(void*),
722 class _Allocator = std::allocator<std::byte>>
723 using __copyable_storage_t =
724 __t<__storage<_VTable, _Allocator, true, _InlineSize>>;
725
726 template <class _Tag, class... _As>
727 auto __tag_type(_Tag (*)(_As...)) -> _Tag;
728
729 template <class _Tag, class... _As>
730 auto __tag_type(_Tag (*)(_As...) noexcept) -> _Tag;
731
732 template <class _Query>
733 concept __is_stop_token_query = requires {
734 {
735 __tag_type(static_cast<_Query>(nullptr))
736 } -> same_as<get_stop_token_t>;
737 };
738
739 template <class _Query>
740 concept __is_not_stop_token_query = !__is_stop_token_query<_Query>;
741
742 template <class _Query>
743 using __is_not_stop_token_query_v = __mbool<__is_not_stop_token_query<_Query>>;
744
745 namespace __rec
746 {
747 template <class _Sigs, class... _Queries>
748 struct __vtable
749 {
750 class __t;
751 };
752
753 template <class _Sigs, class... _Queries>
754 struct __ref;
755
756 template <class... _Sigs, class... _Queries>
757 struct __vtable<completion_signatures<_Sigs...>, _Queries...>
758 {
759 class __t :
760 public __any_::__rcvr_vfun<_Sigs>...,
761 public __query_vfun<_Queries>...
762 {
763 public:
764 using __query_vfun<_Queries>::operator()...;
765
766 private:
767 template <class _Rcvr>
768 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> &&
769 (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...)
STDEXEC_MEMFN_DECL(auto __create_vtable)770 STDEXEC_MEMFN_DECL(auto __create_vtable)
771 (this __mtype<__t>, __mtype<_Rcvr>) noexcept -> const __t*
772 {
773 static const __t __vtable_{
774 {__any_::__rcvr_vfun_fn(static_cast<_Rcvr*>(nullptr),
775 static_cast<_Sigs*>(nullptr))}...,
776 {__query_vfun_fn<_Rcvr>{}(static_cast<_Queries>(nullptr))}...};
777 return &__vtable_;
778 }
779 };
780 };
781
782 template <class... _Sigs, class... _Queries>
783 requires(__is_not_stop_token_query<_Queries> && ...)
784 struct __ref<completion_signatures<_Sigs...>, _Queries...>
785 {
786 #if !STDEXEC_MSVC()
787 // MSVCBUG
788 // https://developercommunity.visualstudio.com/t/Private-member-inaccessible-when-used-in/10448363
789
790 private:
791 #endif
792 using __vtable_t =
793 stdexec::__t<__vtable<completion_signatures<_Sigs...>, _Queries...>>;
794
795 struct __env_t
796 {
797 const __vtable_t* __vtable_;
798 void* __rcvr_;
799 inplace_stop_token __token_;
800
801 template <class _Tag, class... _As>
802 requires __callable<const __vtable_t&, _Tag, void*, _As...>
queryexec::__any::__rec::__ref::__env_t803 auto query(_Tag, _As&&... __as) const //
804 noexcept(__nothrow_callable<const __vtable_t&, _Tag, void*, _As...>)
805 -> __call_result_t<const __vtable_t&, _Tag, void*, _As...>
806 {
807 return (*__vtable_)(_Tag{}, __rcvr_, static_cast<_As&&>(__as)...);
808 }
809
queryexec::__any::__rec::__ref::__env_t810 auto query(get_stop_token_t) const noexcept -> inplace_stop_token
811 {
812 return __token_;
813 }
814 } __env_;
815
816 public:
817 using receiver_concept = stdexec::receiver_t;
818 using __id = __ref;
819 using __t = __ref;
820
821 template <__none_of<__ref, const __ref, __env_t, const __env_t> _Rcvr>
822 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> &&
823 (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...)
__refexec::__any::__rec::__ref824 __ref(_Rcvr& __rcvr) noexcept :
825 __env_{__create_vtable(__mtype<__vtable_t>{}, __mtype<_Rcvr>{}),
826 &__rcvr, stdexec::get_stop_token(stdexec::get_env(__rcvr))}
827 {}
828
829 template <class... _As>
830 requires __one_of<set_value_t(_As...), _Sigs...>
set_valueexec::__any::__rec::__ref831 void set_value(_As&&... __as) noexcept
832 {
833 const __any_::__rcvr_vfun<set_value_t(_As...)>* __vfun =
834 __env_.__vtable_;
835 (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_As&&>(__as)...);
836 }
837
838 template <class _Error>
839 requires __one_of<set_error_t(_Error), _Sigs...>
set_errorexec::__any::__rec::__ref840 void set_error(_Error&& __err) noexcept
841 {
842 const __any_::__rcvr_vfun<set_error_t(_Error)>* __vfun =
843 __env_.__vtable_;
844 (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_Error&&>(__err));
845 }
846
set_stoppedexec::__any::__rec::__ref847 void set_stopped() noexcept
848 requires __one_of<set_stopped_t(), _Sigs...>
849 {
850 const __any_::__rcvr_vfun<set_stopped_t()>* __vfun = __env_.__vtable_;
851 (*__vfun->__complete_)(__env_.__rcvr_);
852 }
853
get_envexec::__any::__rec::__ref854 auto get_env() const noexcept -> const __env_t&
855 {
856 return __env_;
857 }
858 };
859
860 auto __test_never_stop_token(
861 get_stop_token_t (*)(never_stop_token (*)() noexcept)) -> __mbool<true>;
862
863 template <class _Tag, class _Ret, class... _As>
864 auto __test_never_stop_token(_Tag (*)(_Ret (*)(_As...) noexcept))
865 -> __mbool<false>;
866
867 template <class _Tag, class _Ret, class... _As>
868 auto __test_never_stop_token(_Tag (*)(_Ret (*)(_As...))) -> __mbool<false>;
869
870 template <class _Query>
871 using __is_never_stop_token_query =
872 decltype(__test_never_stop_token(static_cast<_Query>(nullptr)));
873
874 template <class... _Sigs, class... _Queries>
875 requires(__is_stop_token_query<_Queries> || ...)
876 struct __ref<completion_signatures<_Sigs...>, _Queries...>
877 {
878 #if !STDEXEC_MSVC()
879 // MSVCBUG
880 // https://developercommunity.visualstudio.com/t/Private-member-inaccessible-when-used-in/10448363
881
882 private:
883 #endif
884 using _FilteredQueries =
885 __minvoke<__mremove_if<__q<__is_never_stop_token_query>>, _Queries...>;
886 using __vtable_t = stdexec::__t<
887 __mapply<__mbind_front_q<__vtable, completion_signatures<_Sigs...>>,
888 _FilteredQueries>>;
889
890 struct __env_t
891 {
892 const __vtable_t* __vtable_;
893 void* __rcvr_;
894
895 template <class _Tag, class... _As>
896 requires __callable<const __vtable_t&, _Tag, void*, _As...>
queryexec::__any::__rec::__ref::__env_t897 auto query(_Tag, _As&&... __as) const //
898 noexcept(__nothrow_callable<const __vtable_t&, _Tag, void*, _As...>)
899 -> __call_result_t<const __vtable_t&, _Tag, void*, _As...>
900 {
901 return (*__vtable_)(_Tag{}, __rcvr_, static_cast<_As&&>(__as)...);
902 }
903 } __env_;
904
905 public:
906 using receiver_concept = stdexec::receiver_t;
907 using __id = __ref;
908 using __t = __ref;
909
910 template <__none_of<__ref, const __ref, __env_t, const __env_t> _Rcvr>
911 requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> &&
912 (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...)
__refexec::__any::__rec::__ref913 __ref(_Rcvr& __rcvr) noexcept :
914 __env_{__create_vtable(__mtype<__vtable_t>{}, __mtype<_Rcvr>{}),
915 &__rcvr}
916 {}
917
918 template <class... _As>
919 requires __one_of<set_value_t(_As...), _Sigs...>
set_valueexec::__any::__rec::__ref920 void set_value(_As&&... __as) noexcept
921 {
922 const __any_::__rcvr_vfun<set_value_t(_As...)>* __vfun =
923 __env_.__vtable_;
924 (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_As&&>(__as)...);
925 }
926
927 template <class _Error>
928 requires __one_of<set_error_t(_Error), _Sigs...>
set_errorexec::__any::__rec::__ref929 void set_error(_Error&& __err) noexcept
930 {
931 const __any_::__rcvr_vfun<set_error_t(_Error)>* __vfun =
932 __env_.__vtable_;
933 (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_Error&&>(__err));
934 }
935
set_stoppedexec::__any::__rec::__ref936 void set_stopped() noexcept
937 requires __one_of<set_stopped_t(), _Sigs...>
938 {
939 const __any_::__rcvr_vfun<set_stopped_t()>* __vfun = __env_.__vtable_;
940 (*__vfun->__complete_)(__env_.__rcvr_);
941 }
942
get_envexec::__any::__rec::__ref943 auto get_env() const noexcept -> const __env_t&
944 {
945 return __env_;
946 }
947 };
948 } // namespace __rec
949
950 class __operation_vtable
951 {
952 public:
953 void (*__start_)(void*) noexcept;
954
955 private:
956 template <class _Op>
STDEXEC_MEMFN_DECL(auto __create_vtable)957 STDEXEC_MEMFN_DECL(auto __create_vtable)
958 (this __mtype<__operation_vtable>,
959 __mtype<_Op>) noexcept -> const __operation_vtable*
960 {
961 static __operation_vtable __vtable{
962 [](void* __object_pointer) noexcept -> void {
963 STDEXEC_ASSERT(__object_pointer);
964 _Op& __op = *static_cast<_Op*>(__object_pointer);
965 static_assert(operation_state<_Op>);
966 stdexec::start(__op);
967 }};
968 return &__vtable;
969 }
970 };
971
972 using __immovable_operation_storage = __immovable_storage_t<__operation_vtable>;
973
974 template <class _Sigs, class _Queries>
975 using __receiver_ref =
976 __mapply<__mbind_front<__q<__rec::__ref>, _Sigs>, _Queries>;
977
978 struct __on_stop_t
979 {
980 stdexec::inplace_stop_source& __source_;
981
operator ()exec::__any::__on_stop_t982 void operator()() const noexcept
983 {
984 __source_.request_stop();
985 }
986 };
987
988 template <class _Receiver>
989 struct __operation_base
990 {
991 STDEXEC_ATTRIBUTE((no_unique_address))
992 _Receiver __rcvr_;
993 stdexec::inplace_stop_source __stop_source_{};
994 using __stop_callback = typename stdexec::stop_token_of_t<
995 stdexec::env_of_t<_Receiver>>::template callback_type<__on_stop_t>;
996 std::optional<__stop_callback> __on_stop_{};
997 };
998
999 template <class _Env>
1000 using __env_t =
1001 __env::__join_t<prop<get_stop_token_t, inplace_stop_token>, _Env>;
1002
1003 template <class _ReceiverId>
1004 struct __stoppable_receiver
1005 {
1006 using _Receiver = stdexec::__t<_ReceiverId>;
1007
1008 struct __t
1009 {
1010 using receiver_concept = stdexec::receiver_t;
1011 __operation_base<_Receiver>* __op_;
1012
1013 template <same_as<__t> _Self, class _Item>
1014 requires __callable<set_next_t, _Receiver&, _Item>
STDEXEC_MEMFN_DECLexec::__any::__stoppable_receiver::__t1015 STDEXEC_MEMFN_DECL(auto set_next)
1016 (this _Self& __self, _Item&& __item) noexcept
1017 -> __call_result_t<set_next_t, _Receiver&, _Item>
1018 {
1019 return exec::set_next(__self.__op_->__rcvr_,
1020 static_cast<_Item&&>(__item));
1021 }
1022
1023 template <class... _Args>
1024 requires __callable<set_value_t, _Receiver, _Args...>
set_valueexec::__any::__stoppable_receiver::__t1025 void set_value(_Args&&... __args) noexcept
1026 {
1027 __op_->__on_stop_.reset();
1028 stdexec::set_value(static_cast<_Receiver&&>(__op_->__rcvr_),
1029 static_cast<_Args&&>(__args)...);
1030 }
1031
1032 template <class _Error>
1033 requires __callable<set_error_t, _Receiver, _Error>
set_errorexec::__any::__stoppable_receiver::__t1034 void set_error(_Error&& __err) noexcept
1035 {
1036 __op_->__on_stop_.reset();
1037 stdexec::set_error(static_cast<_Receiver&&>(__op_->__rcvr_),
1038 static_cast<_Error&&>(__err));
1039 }
1040
set_stoppedexec::__any::__stoppable_receiver::__t1041 void set_stopped() noexcept
1042 requires __callable<set_stopped_t, _Receiver>
1043 {
1044 __op_->__on_stop_.reset();
1045 stdexec::set_stopped(static_cast<_Receiver&&>(__op_->__rcvr_));
1046 }
1047
get_envexec::__any::__stoppable_receiver::__t1048 auto get_env() const noexcept -> __env_t<env_of_t<_Receiver>>
1049 {
1050 return __env::__join(
1051 prop{get_stop_token, __op_->__stop_source_.get_token()},
1052 stdexec::get_env(__op_->__rcvr_));
1053 }
1054 };
1055 };
1056
1057 template <class _ReceiverId>
1058 using __stoppable_receiver_t = stdexec::__t<__stoppable_receiver<_ReceiverId>>;
1059
1060 template <class _ReceiverId, bool>
1061 struct __operation
1062 {
1063 using _Receiver = stdexec::__t<_ReceiverId>;
1064
1065 class __t : public __operation_base<_Receiver>
1066 {
1067 public:
1068 using __id = __operation;
1069
1070 template <class _Sender>
__t(_Sender && __sender,_Receiver && __receiver)1071 __t(_Sender&& __sender, _Receiver&& __receiver) :
1072 __operation_base<_Receiver>{static_cast<_Receiver&&>(__receiver)},
1073 __rec_{this}, __storage_{__sender.__connect(__rec_)}
1074 {}
1075
start()1076 void start() & noexcept
1077 {
1078 this->__on_stop_.emplace(
1079 stdexec::get_stop_token(stdexec::get_env(this->__rcvr_)),
1080 __on_stop_t{this->__stop_source_});
1081 STDEXEC_ASSERT(__storage_.__get_vtable()->__start_);
1082 __storage_.__get_vtable()->__start_(
1083 __storage_.__get_object_pointer());
1084 }
1085
1086 private:
1087 __stoppable_receiver_t<_ReceiverId> __rec_;
1088 __immovable_operation_storage __storage_{};
1089 };
1090 };
1091
1092 template <class _ReceiverId>
1093 struct __operation<_ReceiverId, false>
1094 {
1095 using _Receiver = stdexec::__t<_ReceiverId>;
1096
1097 class __t
1098 {
1099 public:
1100 using __id = __operation;
1101
1102 template <class _Sender>
__t(_Sender && __sender,_Receiver && __receiver)1103 __t(_Sender&& __sender, _Receiver&& __receiver) :
1104 __rec_{static_cast<_Receiver&&>(__receiver)},
1105 __storage_{__sender.__connect(__rec_)}
1106 {}
1107
start()1108 void start() & noexcept
1109 {
1110 STDEXEC_ASSERT(__storage_.__get_vtable()->__start_);
1111 __storage_.__get_vtable()->__start_(
1112 __storage_.__get_object_pointer());
1113 }
1114
1115 private:
1116 STDEXEC_ATTRIBUTE((no_unique_address))
1117 _Receiver __rec_;
1118 __immovable_operation_storage __storage_{};
1119 };
1120 };
1121
1122 template <class _Queries, bool _IsEnvProvider = true>
1123 class __query_vtable;
1124
1125 template <template <class...> class _List, class... _Queries,
1126 bool _IsEnvProvider>
1127 class __query_vtable<_List<_Queries...>, _IsEnvProvider> :
1128 public __query_vfun<_Queries>...
1129 {
1130 public:
1131 using __query_vfun<_Queries>::operator()...;
1132
1133 private:
1134 template <class _Queryable>
1135 requires(__callable<__query_vfun_fn<_Queryable, _IsEnvProvider>,
1136 _Queries> &&
1137 ...)
STDEXEC_MEMFN_DECL(auto __create_vtable)1138 STDEXEC_MEMFN_DECL(auto __create_vtable)
1139 (this __mtype<__query_vtable>,
1140 __mtype<_Queryable>) noexcept -> const __query_vtable*
1141 {
1142 static const __query_vtable __vtable{
1143 {__query_vfun_fn<_Queryable, _IsEnvProvider>{}(
1144 static_cast<_Queries>(nullptr))}...};
1145 return &__vtable;
1146 }
1147 };
1148
1149 template <class _Sigs, class _SenderQueries = __types<>,
1150 class _ReceiverQueries = __types<>>
1151 struct __sender
1152 {
1153 using __receiver_ref_t = __receiver_ref<_Sigs, _ReceiverQueries>;
1154 static constexpr bool __with_inplace_stop_token =
1155 __v<__mapply<__mall_of<__q<__is_not_stop_token_query_v>>,
1156 _ReceiverQueries>>;
1157
1158 class __vtable : public __query_vtable<_SenderQueries>
1159 {
1160 public:
1161 using __id = __vtable;
1162
__queries() const1163 auto __queries() const noexcept -> const __query_vtable<_SenderQueries>&
1164 {
1165 return *this;
1166 }
1167
1168 __immovable_operation_storage (*__connect_)(void*, __receiver_ref_t);
1169
1170 private:
1171 template <sender_to<__receiver_ref_t> _Sender>
STDEXEC_MEMFN_DECL(auto __create_vtable)1172 STDEXEC_MEMFN_DECL(auto __create_vtable)
1173 (this __mtype<__vtable>, __mtype<_Sender>) noexcept -> 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)
1329 (this __mtype<__vtable>,
1330 __mtype<_Scheduler>) noexcept -> 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