xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__receiver_adaptor.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
1 /*
2  * Copyright (c) 2021-2024 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 "__execution_fwd.hpp"
19 
20 #include "__concepts.hpp"
21 #include "__receivers.hpp"
22 #include "__type_traits.hpp"
23 #include "__utility.hpp"
24 
25 namespace stdexec {
26   namespace __adaptors {
27     namespace __no {
28       struct __nope { };
29 
30       struct __receiver : __nope {
31         using receiver_concept = receiver_t;
32 
33         void set_error(std::exception_ptr) noexcept;
34         void set_stopped() noexcept;
35         [[nodiscard]]
36         auto get_env() const noexcept -> env<>;
37       };
38     } // namespace __no
39 
40     using __not_a_receiver = __no::__receiver;
41 
42     template <class _Base>
43     struct __adaptor_base {
44       template <class _T1>
45         requires constructible_from<_Base, _T1>
__adaptor_basestdexec::__adaptors::__adaptor_base46       explicit __adaptor_base(_T1&& __base)
47         : __base_(static_cast<_T1&&>(__base)) {
48       }
49 
50      private:
51       STDEXEC_ATTRIBUTE(no_unique_address) _Base __base_;
52 
53      protected:
STDEXEC_ATTRIBUTEstdexec::__adaptors::__adaptor_base54       STDEXEC_ATTRIBUTE(host, device, always_inline) auto base() & noexcept -> _Base& {
55         return __base_;
56       }
57 
STDEXEC_ATTRIBUTEstdexec::__adaptors::__adaptor_base58       STDEXEC_ATTRIBUTE(host, device, always_inline)
59       auto base() const & noexcept -> const _Base& {
60         return __base_;
61       }
62 
STDEXEC_ATTRIBUTEstdexec::__adaptors::__adaptor_base63       STDEXEC_ATTRIBUTE(host, device, always_inline) auto base() && noexcept -> _Base&& {
64         return static_cast<_Base&&>(__base_);
65       }
66     };
67 
68     template <derived_from<__no::__nope> _Base>
69     struct __adaptor_base<_Base> { };
70 
71 // BUGBUG Not to spec: on gcc and nvc++, member functions in derived classes
72 // don't shadow type aliases of the same name in base classes. :-O
73 // On mingw gcc, 'bool(type::existing_member_function)' evaluates to true,
74 // but 'int(type::existing_member_function)' is an error (as desired).
75 #define STDEXEC_DISPATCH_MEMBER(_TAG)                                                              \
76   template <class _Self, class... _Ts>                                                             \
77   STDEXEC_ATTRIBUTE(host, device, always_inline)                                                   \
78   static auto __call_##_TAG(_Self&& __self, _Ts&&... __ts) noexcept                                \
79     -> decltype((static_cast<_Self&&>(__self))._TAG(static_cast<_Ts&&>(__ts)...)) {                \
80     static_assert(noexcept((static_cast<_Self&&>(__self))._TAG(static_cast<_Ts&&>(__ts)...)));     \
81     return static_cast<_Self&&>(__self)._TAG(static_cast<_Ts&&>(__ts)...);                         \
82   } /**/
83 #define STDEXEC_CALL_MEMBER(_TAG, ...) __call_##_TAG(__VA_ARGS__)
84 
85 #if STDEXEC_CLANG()
86 // Only clang gets this right.
87 #  define STDEXEC_MISSING_MEMBER(_Dp, _TAG) requires { typename _Dp::_TAG; }
88 #  define STDEXEC_DEFINE_MEMBER(_TAG)       STDEXEC_DISPATCH_MEMBER(_TAG) using _TAG = void
89 #else
90 #  define STDEXEC_MISSING_MEMBER(_Dp, _TAG) (__missing_##_TAG<_Dp>())
91 #  define STDEXEC_DEFINE_MEMBER(_TAG)                                                              \
92     template <class _Dp>                                                                           \
93     static constexpr bool __missing_##_TAG() noexcept {                                            \
94       return requires { requires bool(int(_Dp::_TAG)); };                                          \
95     }                                                                                              \
96     STDEXEC_DISPATCH_MEMBER(_TAG)                                                                  \
97     static constexpr int _TAG = 1 /**/
98 #endif
99 
100     template <__class _Derived, class _Base = __not_a_receiver>
101     struct receiver_adaptor
102       : __adaptor_base<_Base>
103       , receiver_t {
104 
105       static constexpr bool __has_base = !derived_from<_Base, __no::__nope>;
106 
107       template <class _Self>
108       using __base_from_derived_t = decltype(__declval<_Self>().base());
109 
110       using __get_base_fn =
111         __if_c<__has_base, __mbind_back_q<__copy_cvref_t, _Base>, __q<__base_from_derived_t>>;
112 
113       template <class _Self>
114       using __base_t = __minvoke<__get_base_fn, _Self&&>;
115 
116       template <class _Self>
STDEXEC_ATTRIBUTEstdexec::__adaptors::receiver_adaptor117       STDEXEC_ATTRIBUTE(host, device)
118       static auto __get_base(_Self&& __self) noexcept -> __base_t<_Self> {
119         if constexpr (__has_base) {
120           return __c_upcast<receiver_adaptor>(static_cast<_Self&&>(__self)).base();
121         } else {
122           return static_cast<_Self&&>(__self).base();
123         }
124       }
125 
126      public:
127       using receiver_concept = receiver_t;
128 
129       receiver_adaptor() = default;
130       using __adaptor_base<_Base>::__adaptor_base;
131 
132       template <class... _As, class _Self = _Derived>
133         requires __callable<set_value_t, __base_t<_Self>, _As...>
STDEXEC_ATTRIBUTEstdexec::__adaptors::receiver_adaptor134       STDEXEC_ATTRIBUTE(host, device)
135       void set_value(_As&&... __as) && noexcept {
136         return stdexec::set_value(
137           __get_base(static_cast<_Self&&>(*this)), static_cast<_As&&>(__as)...);
138       }
139 
140       template <class _Error, class _Self = _Derived>
141         requires __callable<set_error_t, __base_t<_Self>, _Error>
STDEXEC_ATTRIBUTEstdexec::__adaptors::receiver_adaptor142       STDEXEC_ATTRIBUTE(host, device)
143       void set_error(_Error&& __err) && noexcept {
144         return stdexec::set_error(
145           __get_base(static_cast<_Self&&>(*this)), static_cast<_Error&&>(__err));
146       }
147 
148       template <class _Self = _Derived>
149         requires __callable<set_stopped_t, __base_t<_Self>>
STDEXEC_ATTRIBUTEstdexec::__adaptors::receiver_adaptor150       STDEXEC_ATTRIBUTE(host, device)
151       void set_stopped() && noexcept {
152         return stdexec::set_stopped(__get_base(static_cast<_Self&&>(*this)));
153       }
154 
155       template <class _Self = _Derived>
STDEXEC_ATTRIBUTEstdexec::__adaptors::receiver_adaptor156       STDEXEC_ATTRIBUTE(host, device)
157       auto get_env() const noexcept -> env_of_t<__base_t<const _Self&>> {
158         return stdexec::get_env(__get_base(static_cast<const _Self&>(*this)));
159       }
160     };
161   } // namespace __adaptors
162 
163   template <__class _Derived, receiver _Base = __adaptors::__not_a_receiver>
164   using receiver_adaptor = __adaptors::receiver_adaptor<_Derived, _Base>;
165 } // namespace stdexec
166