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