xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.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 "__senders_core.hpp"
22 #include "__tuple.hpp"
23 #include "__type_traits.hpp"
24 
25 namespace stdexec {
26   // NOT TO SPEC:
27   namespace __closure {
28     template <__class _Dp>
29     struct sender_adaptor_closure;
30   } // namespace __closure
31 
32   using __closure::sender_adaptor_closure;
33 
34   template <class _Tp>
35   concept __sender_adaptor_closure =
36     derived_from<__decay_t<_Tp>, sender_adaptor_closure<__decay_t<_Tp>>>
37     && move_constructible<__decay_t<_Tp>> && constructible_from<__decay_t<_Tp>, _Tp>;
38 
39   template <class _Tp, class _Sender>
40   concept __sender_adaptor_closure_for = __sender_adaptor_closure<_Tp> && sender<__decay_t<_Sender>>
41                                       && __callable<_Tp, __decay_t<_Sender>>
42                                       && sender<__call_result_t<_Tp, __decay_t<_Sender>>>;
43 
44   namespace __closure {
45     template <class _T0, class _T1>
46     struct __compose : sender_adaptor_closure<__compose<_T0, _T1>> {
47       STDEXEC_ATTRIBUTE(no_unique_address) _T0 __t0_;
48       STDEXEC_ATTRIBUTE(no_unique_address) _T1 __t1_;
49 
50       template <sender _Sender>
51         requires __callable<_T0, _Sender> && __callable<_T1, __call_result_t<_T0, _Sender>>
STDEXEC_ATTRIBUTEstdexec::__closure::__compose52       STDEXEC_ATTRIBUTE(always_inline)
53       auto operator()(_Sender&& __sndr) && -> __call_result_t<_T1, __call_result_t<_T0, _Sender>> {
54         return static_cast<_T1&&>(__t1_)(static_cast<_T0&&>(__t0_)(static_cast<_Sender&&>(__sndr)));
55       }
56 
57       template <sender _Sender>
58         requires __callable<const _T0&, _Sender>
59               && __callable<const _T1&, __call_result_t<const _T0&, _Sender>>
STDEXEC_ATTRIBUTEstdexec::__closure::__compose60       STDEXEC_ATTRIBUTE(always_inline)
61       auto operator()(_Sender&& __sndr)
62         const & -> __call_result_t<const _T1&, __call_result_t<const _T0&, _Sender>> {
63         return __t1_(__t0_(static_cast<_Sender&&>(__sndr)));
64       }
65     };
66 
67     template <__class _Dp>
68     struct sender_adaptor_closure { };
69 
70     template <sender _Sender, __sender_adaptor_closure_for<_Sender> _Closure>
71     STDEXEC_ATTRIBUTE(always_inline)
72     auto operator|(_Sender&& __sndr, _Closure&& __clsur) -> __call_result_t<_Closure, _Sender> {
73       return static_cast<_Closure&&>(__clsur)(static_cast<_Sender&&>(__sndr));
74     }
75 
76     template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1>
77     STDEXEC_ATTRIBUTE(always_inline)
78     auto operator|(_T0&& __t0, _T1&& __t1) -> __compose<__decay_t<_T0>, __decay_t<_T1>> {
79       return {{}, static_cast<_T0&&>(__t0), static_cast<_T1&&>(__t1)};
80     }
81 
82     template <class _Fun, class... _As>
83     struct __binder_back
84       : __tuple_for<_As...>
85       , sender_adaptor_closure<__binder_back<_Fun, _As...>> {
STDEXEC_ATTRIBUTEstdexec::__closure::__binder_back86       STDEXEC_ATTRIBUTE(no_unique_address) _Fun __fun_ { };
87 
88 #if STDEXEC_INTELLISENSE()
89       // MSVCBUG https://developercommunity.visualstudio.com/t/rejects-valid-EDG-invocation-of-lambda/10786020
90 
91       template <class _Sender>
92       struct __lambda_rvalue {
93         __binder_back& __self_;
94         _Sender& __sndr_;
95 
STDEXEC_ATTRIBUTEstdexec::__closure::__binder_back::__lambda_rvalue96         STDEXEC_ATTRIBUTE(host, device, always_inline)
97         auto operator()(_As&... __as) const noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
98           -> __call_result_t<_Fun, _Sender, _As...> {
99           return static_cast<_Fun&&>(
100             __self_.__fun_)(static_cast<_Sender&&>(__sndr_), static_cast<_As&&>(__as)...);
101         }
102       };
103 
104       template <class _Sender>
105       struct __lambda_lvalue {
106         __binder_back const & __self_;
107         _Sender& __sndr_;
108 
STDEXEC_ATTRIBUTEstdexec::__closure::__binder_back::__lambda_lvalue109         STDEXEC_ATTRIBUTE(host, device, always_inline)
110         auto operator()(const _As&... __as) const
111           noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
112             -> __call_result_t<const _Fun&, _Sender, const _As&...> {
113           return __self_.__fun_(static_cast<_Sender&&>(__sndr_), __as...);
114         }
115       };
116 #endif
117 
118       template <sender _Sender>
119         requires __callable<_Fun, _Sender, _As...>
STDEXEC_ATTRIBUTEstdexec::__closure::__binder_back120       STDEXEC_ATTRIBUTE(host, device, always_inline)
121       auto operator()(_Sender&& __sndr) && noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
122         -> __call_result_t<_Fun, _Sender, _As...> {
123 #if STDEXEC_INTELLISENSE()
124         return this->apply(__lambda_rvalue<_Sender>{*this, __sndr}, *this);
125 #else
126         return this->apply(
127           [&__sndr, this](_As&... __as) noexcept(
128             __nothrow_callable<_Fun, _Sender, _As...>) -> __call_result_t<_Fun, _Sender, _As...> {
129             return static_cast<_Fun&&>(
130               __fun_)(static_cast<_Sender&&>(__sndr), static_cast<_As&&>(__as)...);
131           },
132           *this);
133 #endif
134       }
135 
136       template <sender _Sender>
137         requires __callable<const _Fun&, _Sender, const _As&...>
STDEXEC_ATTRIBUTEstdexec::__closure::__binder_back138       STDEXEC_ATTRIBUTE(host, device, always_inline)
139       auto operator()(_Sender&& __sndr) const & noexcept(
140         __nothrow_callable<const _Fun&, _Sender, const _As&...>)
141         -> __call_result_t<const _Fun&, _Sender, const _As&...> {
142 #if STDEXEC_INTELLISENSE()
143         return this->apply(__lambda_lvalue<_Sender>{*this, __sndr}, *this);
144 #else
145         return this->apply(
146           [&__sndr, this](const _As&... __as) noexcept(
147             __nothrow_callable<const _Fun&, _Sender, const _As&...>)
148             -> __call_result_t<const _Fun&, _Sender, const _As&...> {
149             return __fun_(static_cast<_Sender&&>(__sndr), __as...);
150           },
151           *this);
152 #endif
153       }
154     };
155   } // namespace __closure
156 
157   using __closure::__binder_back;
158 } // namespace stdexec
159