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 "__concepts.hpp"
19 #include "__cpo.hpp"
20 #include "__execution_fwd.hpp"
21 #include "__receivers.hpp"
22 #include "__tag_invoke.hpp"
23 #include "__type_traits.hpp"
24 #include "__utility.hpp"
25 
26 namespace stdexec
27 {
28 namespace __adaptors
29 {
30 namespace __no
31 {
32 struct __nope
33 {};
34 
35 struct __receiver : __nope
36 {
37     using receiver_concept = receiver_t;
38 
39     void set_error(std::exception_ptr) noexcept;
40     void set_stopped() noexcept;
41     auto get_env() const noexcept -> empty_env;
42 };
43 } // namespace __no
44 
45 using __not_a_receiver = __no::__receiver;
46 
47 template <class _Base>
48 struct __adaptor_base
49 {
50     template <class _T1>
51         requires constructible_from<_Base, _T1>
__adaptor_basestdexec::__adaptors::__adaptor_base52     explicit __adaptor_base(_T1&& __base) : __base_(static_cast<_T1&&>(__base))
53     {}
54 
55   private:
56     STDEXEC_ATTRIBUTE((no_unique_address))
57     _Base __base_;
58 
59   protected:
60     STDEXEC_ATTRIBUTE((host, device, always_inline))
basestdexec::__adaptors::__adaptor_base61     _Base& base() & noexcept
62     {
63         return __base_;
64     }
65 
66     STDEXEC_ATTRIBUTE((host, device, always_inline))
basestdexec::__adaptors::__adaptor_base67     const _Base& base() const& noexcept
68     {
69         return __base_;
70     }
71 
72     STDEXEC_ATTRIBUTE((host, device, always_inline))
basestdexec::__adaptors::__adaptor_base73     _Base&& base() && noexcept
74     {
75         return static_cast<_Base&&>(__base_);
76     }
77 };
78 
79 template <derived_from<__no::__nope> _Base>
80 struct __adaptor_base<_Base>
81 {};
82 
83 // BUGBUG Not to spec: on gcc and nvc++, member functions in derived classes
84 // don't shadow type aliases of the same name in base classes. :-O
85 // On mingw gcc, 'bool(type::existing_member_function)' evaluates to true,
86 // but 'int(type::existing_member_function)' is an error (as desired).
87 #define STDEXEC_DISPATCH_MEMBER(_TAG)                                          \
88     template <class _Self, class... _Ts>                                       \
89     STDEXEC_ATTRIBUTE((host, device, always_inline))                           \
90     static auto __call_##_TAG(_Self&& __self, _Ts&&... __ts) noexcept          \
91         -> decltype((static_cast<_Self&&>(__self))                             \
92                         ._TAG(static_cast<_Ts&&>(__ts)...))                    \
93     {                                                                          \
94         static_assert(noexcept((static_cast<_Self&&>(__self))                  \
95                                    ._TAG(static_cast<_Ts&&>(__ts)...)));       \
96         return static_cast<_Self&&>(__self)._TAG(static_cast<_Ts&&>(__ts)...); \
97     } /**/
98 #define STDEXEC_CALL_MEMBER(_TAG, ...) __call_##_TAG(__VA_ARGS__)
99 
100 #if STDEXEC_CLANG()
101 // Only clang gets this right.
102 #define STDEXEC_MISSING_MEMBER(_Dp, _TAG) requires { typename _Dp::_TAG; }
103 #define STDEXEC_DEFINE_MEMBER(_TAG)                                            \
104     STDEXEC_DISPATCH_MEMBER(_TAG) using _TAG = void
105 #else
106 #define STDEXEC_MISSING_MEMBER(_Dp, _TAG) (__missing_##_TAG<_Dp>())
107 #define STDEXEC_DEFINE_MEMBER(_TAG)                                            \
108     template <class _Dp>                                                       \
109     static constexpr bool __missing_##_TAG() noexcept                          \
110     {                                                                          \
111         return requires { requires bool(int(_Dp::_TAG)); };                    \
112     }                                                                          \
113     STDEXEC_DISPATCH_MEMBER(_TAG)                                              \
114     static constexpr int _TAG = 1 /**/
115 #endif
116 
117 template <__class _Derived, class _Base = __not_a_receiver>
118 struct receiver_adaptor : __adaptor_base<_Base>, receiver_t
119 {
120     static constexpr bool __has_base = !derived_from<_Base, __no::__nope>;
121 
122     template <class _Self>
123     using __base_from_derived_t = decltype(__declval<_Self>().base());
124 
125     using __get_base_fn =
126         __if_c<__has_base, __mbind_back_q<__copy_cvref_t, _Base>,
127                __q<__base_from_derived_t>>;
128 
129     template <class _Self>
130     using __base_t = __minvoke<__get_base_fn, _Self&&>;
131 
132     template <class _Self>
133     STDEXEC_ATTRIBUTE((host, device))
__get_basestdexec::__adaptors::receiver_adaptor134     static auto __get_base(_Self&& __self) noexcept -> __base_t<_Self>
135     {
136         if constexpr (__has_base)
137         {
138             return __c_upcast<receiver_adaptor>(static_cast<_Self&&>(__self))
139                 .base();
140         }
141         else
142         {
143             return static_cast<_Self&&>(__self).base();
144         }
145     }
146 
147   public:
148     using receiver_concept = receiver_t;
149 
150     receiver_adaptor() = default;
151     using __adaptor_base<_Base>::__adaptor_base;
152 
153     template <class... _As, class _Self = _Derived>
154         requires __callable<set_value_t, __base_t<_Self>, _As...>
155     STDEXEC_ATTRIBUTE((host, device))
set_valuestdexec::__adaptors::receiver_adaptor156     void set_value(_As&&... __as) && noexcept
157     {
158         return stdexec::set_value(__get_base(static_cast<_Self&&>(*this)),
159                                   static_cast<_As&&>(__as)...);
160     }
161 
162     template <class _Error, class _Self = _Derived>
163         requires __callable<set_error_t, __base_t<_Self>, _Error>
164     STDEXEC_ATTRIBUTE((host, device))
set_errorstdexec::__adaptors::receiver_adaptor165     void set_error(_Error&& __err) && noexcept
166     {
167         return stdexec::set_error(__get_base(static_cast<_Self&&>(*this)),
168                                   static_cast<_Error&&>(__err));
169     }
170 
171     template <class _Self = _Derived>
172         requires __callable<set_stopped_t, __base_t<_Self>>
173     STDEXEC_ATTRIBUTE((host, device))
set_stoppedstdexec::__adaptors::receiver_adaptor174     void set_stopped() && noexcept
175     {
176         return stdexec::set_stopped(__get_base(static_cast<_Self&&>(*this)));
177     }
178 
179     template <class _Self = _Derived>
180     STDEXEC_ATTRIBUTE((host, device))
get_envstdexec::__adaptors::receiver_adaptor181     auto get_env() const noexcept -> env_of_t<__base_t<const _Self&>>
182     {
183         return stdexec::get_env(__get_base(static_cast<const _Self&>(*this)));
184     }
185 };
186 } // namespace __adaptors
187 
188 template <__class _Derived, receiver _Base = __adaptors::__not_a_receiver>
189 using receiver_adaptor = __adaptors::receiver_adaptor<_Derived, _Base>;
190 } // namespace stdexec
191