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))
62     __call_result_t<_T1, __call_result_t<_T0, _Sender>>
operator ()stdexec::__closure::__compose63         operator()(_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))
73     __call_result_t<_T1, __call_result_t<_T0, _Sender>>
operator ()stdexec::__closure::__compose74         operator()(_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))
86 __call_result_t<_Closure, _Sender>
operator |(_Sender && __sndr,_Closure && __clsur)87     operator|(_Sender&& __sndr, _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     template <sender _Sender>
108         requires __callable<_Fun, _Sender, _As...>
109     STDEXEC_ATTRIBUTE((host, device, always_inline))
operator ()stdexec::__closure::__binder_back110     __call_result_t<_Fun, _Sender, _As...> operator()(
111         _Sender&& __sndr) && noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
112     {
113         return this->apply(
114             [&__sndr, this](_As&... __as) noexcept(
115                 __nothrow_callable<_Fun, _Sender, _As...>)
116                 -> __call_result_t<_Fun, _Sender, _As...> {
117                 return static_cast<_Fun&&>(
118                     __fun_)(static_cast<_Sender&&>(__sndr),
119                             static_cast<_As&&>(__as)...);
120             },
121             *this);
122     }
123 
124     template <sender _Sender>
125         requires __callable<const _Fun&, _Sender, const _As&...>
126     STDEXEC_ATTRIBUTE((host, device, always_inline))
operator ()stdexec::__closure::__binder_back127     auto operator()(_Sender&& __sndr) const& //
128         noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
129             -> __call_result_t<const _Fun&, _Sender, const _As&...>
130     {
131         return this->apply(
132             [&__sndr, this](const _As&... __as) noexcept(
133                 __nothrow_callable<_Fun, _Sender, const _As&...>)
134                 -> __call_result_t<const _Fun&, _Sender, const _As&...> {
135                 return __fun_(static_cast<_Sender&&>(__sndr), __as...);
136             },
137             *this);
138     }
139 };
140 } // namespace __closure
141 
142 using __closure::__binder_back;
143 } // namespace stdexec
144