xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.hpp (revision 6269157344064457b7e1241d4efe59c6c51c7a59)
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 "__execution_fwd.hpp"
20 #include "__senders_core.hpp"
21 #include "__tuple.hpp"
22 #include "__type_traits.hpp"
23 
24 namespace stdexec
25 {
26 // NOT TO SPEC:
27 namespace __closure
28 {
29 template <__class _Dp>
30 struct sender_adaptor_closure;
31 } // namespace __closure
32 
33 using __closure::sender_adaptor_closure;
34 
35 template <class _Tp>
36 concept __sender_adaptor_closure =
37     derived_from<__decay_t<_Tp>, sender_adaptor_closure<__decay_t<_Tp>>> &&
38     move_constructible<__decay_t<_Tp>> //
39     && constructible_from<__decay_t<_Tp>, _Tp>;
40 
41 template <class _Tp, class _Sender>
42 concept __sender_adaptor_closure_for =     //
43     __sender_adaptor_closure<_Tp>          //
44     && sender<__decay_t<_Sender>>          //
45     && __callable<_Tp, __decay_t<_Sender>> //
46     && sender<__call_result_t<_Tp, __decay_t<_Sender>>>;
47 
48 namespace __closure
49 {
50 template <class _T0, class _T1>
51 struct __compose : sender_adaptor_closure<__compose<_T0, _T1>>
52 {
53     STDEXEC_ATTRIBUTE((no_unique_address))
54     _T0 __t0_;
55     STDEXEC_ATTRIBUTE((no_unique_address))
56     _T1 __t1_;
57 
58     template <sender _Sender>
59         requires __callable<_T0, _Sender> &&
60                  __callable<_T1, __call_result_t<_T0, _Sender>>
61     STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__closure::__compose62     __call_result_t<_T1, __call_result_t<_T0, _Sender>> operator()(
63         _Sender&& __sndr) &&
64     {
65         return static_cast<_T1&&>(__t1_)(
66             static_cast<_T0&&>(__t0_)(static_cast<_Sender&&>(__sndr)));
67     }
68 
69     template <sender _Sender>
70         requires __callable<const _T0&, _Sender> &&
71                  __callable<const _T1&, __call_result_t<const _T0&, _Sender>>
72     STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__closure::__compose73     __call_result_t<_T1, __call_result_t<_T0, _Sender>> operator()(
74         _Sender&& __sndr) const&
75     {
76         return __t1_(__t0_(static_cast<_Sender&&>(__sndr)));
77     }
78 };
79 
80 template <__class _Dp>
81 struct sender_adaptor_closure
82 {};
83 
84 template <sender _Sender, __sender_adaptor_closure_for<_Sender> _Closure>
85 STDEXEC_ATTRIBUTE((always_inline))
operator |(_Sender && __sndr,_Closure && __clsur)86 __call_result_t<_Closure, _Sender> operator|(_Sender&& __sndr,
87                                              _Closure&& __clsur)
88 {
89     return static_cast<_Closure&&>(__clsur)(static_cast<_Sender&&>(__sndr));
90 }
91 
92 template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1>
93 STDEXEC_ATTRIBUTE((always_inline))
operator |(_T0 && __t0,_T1 && __t1)94 __compose<__decay_t<_T0>, __decay_t<_T1>> operator|(_T0&& __t0, _T1&& __t1)
95 {
96     return {{}, static_cast<_T0&&>(__t0), static_cast<_T1&&>(__t1)};
97 }
98 
99 template <class _Fun, class... _As>
100 struct __binder_back :
101     __tuple_for<_As...>,
102     sender_adaptor_closure<__binder_back<_Fun, _As...>>
103 {
104     STDEXEC_ATTRIBUTE((no_unique_address))
105     _Fun __fun_{};
106 
107 #if STDEXEC_INTELLISENSE()
108     // MSVCBUG
109     // https://developercommunity.visualstudio.com/t/rejects-valid-EDG-invocation-of-lambda/10786020
110 
111     template <class _Sender>
112     struct __lambda_rvalue
113     {
114         __binder_back& __self_;
115         _Sender& __sndr_;
116 
117         STDEXEC_ATTRIBUTE((host, device, always_inline))
operator ()stdexec::__closure::__binder_back::__lambda_rvalue118         auto operator()(_As&... __as) const //
119             noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
120                 -> __call_result_t<_Fun, _Sender, _As...>
121         {
122             return static_cast<_Fun&&>(__self_.__fun_)(
123                 static_cast<_Sender&&>(__sndr_), static_cast<_As&&>(__as)...);
124         }
125     };
126 
127     template <class _Sender>
128     struct __lambda_lvalue
129     {
130         const __binder_back& __self_;
131         _Sender& __sndr_;
132 
133         STDEXEC_ATTRIBUTE((host, device, always_inline))
operator ()stdexec::__closure::__binder_back::__lambda_lvalue134         auto operator()(const _As&... __as) const //
135             noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
136                 -> __call_result_t<const _Fun&, _Sender, const _As&...>
137         {
138             return __self_.__fun_(static_cast<_Sender&&>(__sndr_), __as...);
139         }
140     };
141 #endif
142 
143     template <sender _Sender>
144         requires __callable<_Fun, _Sender, _As...>
145     STDEXEC_ATTRIBUTE((host, device, always_inline))
operator ()stdexec::__closure::__binder_back146     auto operator()(_Sender&& __sndr) && //
147         noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
148             -> __call_result_t<_Fun, _Sender, _As...>
149     {
150 #if STDEXEC_INTELLISENSE()
151         return this->apply(__lambda_rvalue<_Sender>{*this, __sndr}, *this);
152 #else
153         return this->apply(
154             [&__sndr, this](_As&... __as) //
155             noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
156                 -> __call_result_t<_Fun, _Sender, _As...> {
157                 return static_cast<_Fun&&>(
158                     __fun_)(static_cast<_Sender&&>(__sndr),
159                             static_cast<_As&&>(__as)...);
160             },
161             *this);
162 #endif
163     }
164 
165     template <sender _Sender>
166         requires __callable<const _Fun&, _Sender, const _As&...>
167     STDEXEC_ATTRIBUTE((host, device, always_inline))
operator ()stdexec::__closure::__binder_back168     auto operator()(_Sender&& __sndr) const& //
169         noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
170             -> __call_result_t<const _Fun&, _Sender, const _As&...>
171     {
172 #if STDEXEC_INTELLISENSE()
173         return this->apply(__lambda_lvalue<_Sender>{*this, __sndr}, *this);
174 #else
175         return this->apply(
176             [&__sndr, this](const _As&... __as) //
177             noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
178                 -> __call_result_t<const _Fun&, _Sender, const _As&...> {
179                 return __fun_(static_cast<_Sender&&>(__sndr), __as...);
180             },
181             *this);
182 #endif
183     }
184 };
185 } // namespace __closure
186 
187 using __closure::__binder_back;
188 } // namespace stdexec
189