xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__transform_sender.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 these after __execution_fwd.hpp
21 #include "__basic_sender.hpp"
22 #include "__concepts.hpp"
23 #include "__diagnostics.hpp"
24 #include "__domain.hpp"
25 #include "__env.hpp"
26 #include "__meta.hpp"
27 #include "__sender_introspection.hpp"
28 #include "__type_traits.hpp"
29 
30 STDEXEC_PRAGMA_PUSH()
31 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
32 
33 namespace stdexec {
34   /////////////////////////////////////////////////////////////////////////////
35   // [execution.transform_sender]
36   namespace __detail {
37     struct __transform_env {
38       template <class _Domain, class _Sender, class _Env>
STDEXEC_ATTRIBUTEstdexec::__detail::__transform_env39       STDEXEC_ATTRIBUTE(always_inline)
40       auto
41         operator()(_Domain __dom, _Sender&& __sndr, _Env&& __env) const noexcept -> decltype(auto) {
42         if constexpr (__detail::__has_transform_env<_Domain, _Sender, _Env>) {
43           return __dom.transform_env(static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env));
44         } else {
45           return default_domain()
46             .transform_env(static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env));
47         }
48       }
49     };
50 
51     struct __transform_sender_1 {
52       template <class _Domain, class _Sender, class... _Env>
STDEXEC_ATTRIBUTEstdexec::__detail::__transform_sender_153       STDEXEC_ATTRIBUTE(always_inline)
54       static constexpr auto __is_nothrow() noexcept -> bool {
55         if constexpr (__detail::__has_transform_sender<_Domain, _Sender, _Env...>) {
56           return noexcept(__declval<_Domain&>()
57                             .transform_sender(__declval<_Sender>(), __declval<const _Env&>()...));
58         } else {
59           return noexcept(default_domain()
60                             .transform_sender(__declval<_Sender>(), __declval<const _Env&>()...));
61         }
62       }
63 
64       template <class _Domain, class _Sender, class... _Env>
STDEXEC_ATTRIBUTEstdexec::__detail::__transform_sender_165       STDEXEC_ATTRIBUTE(always_inline)
66       auto operator()(_Domain __dom, _Sender&& __sndr, const _Env&... __env) const
67         noexcept(__is_nothrow<_Domain, _Sender, const _Env&...>()) -> decltype(auto) {
68         if constexpr (__detail::__has_transform_sender<_Domain, _Sender, _Env...>) {
69           return __dom.transform_sender(static_cast<_Sender&&>(__sndr), __env...);
70         } else {
71           return default_domain().transform_sender(static_cast<_Sender&&>(__sndr), __env...);
72         }
73       }
74     };
75 
76     template <class _Ty, class _Uy>
77     concept __decay_same_as = same_as<__decay_t<_Ty>, __decay_t<_Uy>>;
78 
79     struct __transform_sender {
80       template <class _Self = __transform_sender, class _Domain, class _Sender, class... _Env>
STDEXEC_ATTRIBUTEstdexec::__detail::__transform_sender81       STDEXEC_ATTRIBUTE(always_inline)
82       auto operator()(_Domain __dom, _Sender&& __sndr, const _Env&... __env) const
83         noexcept(__nothrow_callable<__transform_sender_1, _Domain, _Sender, const _Env&...>)
84           -> decltype(auto) {
85         using _Sender2 = __call_result_t<__transform_sender_1, _Domain, _Sender, const _Env&...>;
86         // If the transformation doesn't change the sender's type, then do not
87         // apply the transform recursively.
88         if constexpr (__decay_same_as<_Sender, _Sender2>) {
89           return __transform_sender_1()(__dom, static_cast<_Sender&&>(__sndr), __env...);
90         } else {
91           // We transformed the sender and got back a different sender. Transform that one too.
92           return _Self()(
93             __dom,
94             __transform_sender_1()(__dom, static_cast<_Sender&&>(__sndr), __env...),
95             __env...);
96         }
97       }
98     };
99 
100     struct __transform_dependent_sender {
101       // If we are doing a lazy customization of a type whose domain is value-dependent (e.g.,
102       // let_value), first transform the sender to determine the domain. Then continue transforming
103       // the sender with the requested domain.
104       template <class _Domain, sender_expr _Sender, class _Env>
105         requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
operator ()stdexec::__detail::__transform_dependent_sender106       auto operator()(_Domain __dom, _Sender&& __sndr, const _Env& __env) const
107         noexcept(noexcept(__transform_sender()(
108           __dom,
109           dependent_domain().transform_sender(static_cast<_Sender&&>(__sndr), __env),
110           __env))) -> decltype(auto) {
111         static_assert(__none_of<_Domain, dependent_domain>);
112         return __transform_sender()(
113           __dom, dependent_domain().transform_sender(static_cast<_Sender&&>(__sndr), __env), __env);
114       }
115     };
116   } // namespace __detail
117 
118   /////////////////////////////////////////////////////////////////////////////
119   // [execution.transform_sender]
120   inline constexpr struct transform_sender_t
121     : __detail::__transform_sender
122     , __detail::__transform_dependent_sender {
123     using __detail::__transform_sender::operator();
124     using __detail::__transform_dependent_sender::operator();
125   } transform_sender{};
126 
127   inline constexpr __detail::__transform_env transform_env{};
128 
129   struct _CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_ { };
130 
131   template <class _Sender, class _Env>
__is_nothrow_transform_sender()132   constexpr auto dependent_domain::__is_nothrow_transform_sender() noexcept -> bool {
133     using _Env2 = __call_result_t<__detail::__transform_env, dependent_domain&, _Sender, _Env>;
134     return __v<decltype(__sexpr_apply(
135       __declval<_Sender>(),
136       []<class _Tag, class _Data, class... _Childs>(_Tag, _Data&&, _Childs&&...) {
137         constexpr bool __first_transform_is_nothrow = noexcept(__make_sexpr<_Tag>(
138           __declval<_Data>(),
139           __detail::__transform_sender()(
140             __declval<dependent_domain&>(), __declval<_Childs>(), __declval<const _Env2&>())...));
141         using _Sender2 = decltype(__make_sexpr<_Tag>(
142           __declval<_Data>(),
143           __detail::__transform_sender()(
144             __declval<dependent_domain&>(), __declval<_Childs>(), __declval<const _Env2&>())...));
145         using _Domain2 =
146           decltype(__sexpr_apply(__declval<_Sender2&>(), __detail::__common_domain_fn()));
147         constexpr bool __second_transform_is_nothrow = noexcept(__detail::__transform_sender()(
148           __declval<_Domain2&>(), __declval<_Sender2>(), __declval<const _Env&>()));
149         return __mbool<__first_transform_is_nothrow && __second_transform_is_nothrow>();
150       }))>;
151   }
152 
153   template <sender_expr _Sender, class _Env>
154     requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
transform_sender(_Sender && __sndr,const _Env & __env) const155   auto dependent_domain::transform_sender(_Sender&& __sndr, const _Env& __env) const
156     noexcept(__is_nothrow_transform_sender<_Sender, _Env>()) -> decltype(auto) {
157     // apply any algorithm-specific transformation to the environment
158     const auto& __env2 = transform_env(*this, static_cast<_Sender&&>(__sndr), __env);
159 
160     // recursively transform the sender to determine the domain
161     return __sexpr_apply(
162       static_cast<_Sender&&>(__sndr),
163       [&]<class _Tag, class _Data, class... _Childs>(_Tag, _Data&& __data, _Childs&&... __childs) {
164         // TODO: propagate meta-exceptions here:
165         auto __sndr2 = __make_sexpr<_Tag>(
166           static_cast<_Data&&>(__data),
167           __detail::__transform_sender()(*this, static_cast<_Childs&&>(__childs), __env2)...);
168         using _Sender2 = decltype(__sndr2);
169 
170         auto __domain2 = __sexpr_apply(__sndr2, __detail::__common_domain_fn());
171         using _Domain2 = decltype(__domain2);
172 
173         if constexpr (same_as<_Domain2, __none_such>) {
174           return __mexception<_CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_, _WITH_SENDER_<_Sender2>>();
175         } else {
176           return __detail::__transform_sender()(__domain2, std::move(__sndr2), __env);
177         }
178       });
179   }
180 
181   /////////////////////////////////////////////////////////////////////////////
182   template <class _Tag, class _Domain, class _Sender, class... _Args>
183   concept __has_implementation_for =
184     __detail::__has_apply_sender<_Domain, _Tag, _Sender, _Args...>
185     || __detail::__has_apply_sender<default_domain, _Tag, _Sender, _Args...>;
186 
187   /////////////////////////////////////////////////////////////////////////////
188   // [execution.apply_sender]
189   inline constexpr struct apply_sender_t {
190     template <class _Domain, class _Tag, class _Sender, class... _Args>
191       requires __has_implementation_for<_Tag, _Domain, _Sender, _Args...>
STDEXEC_ATTRIBUTEstdexec::apply_sender_t192     STDEXEC_ATTRIBUTE(always_inline)
193     auto
194       operator()(_Domain __dom, _Tag, _Sender&& __sndr, _Args&&... __args) const -> decltype(auto) {
195       if constexpr (__detail::__has_apply_sender<_Domain, _Tag, _Sender, _Args...>) {
196         return __dom
197           .apply_sender(_Tag(), static_cast<_Sender&&>(__sndr), static_cast<_Args&&>(__args)...);
198       } else {
199         return default_domain()
200           .apply_sender(_Tag(), static_cast<_Sender&&>(__sndr), static_cast<_Args&&>(__args)...);
201       }
202     }
203   } apply_sender{};
204 
205   template <class _Domain, class _Tag, class _Sender, class... _Args>
206   using apply_sender_result_t = __call_result_t<apply_sender_t, _Domain, _Tag, _Sender, _Args...>;
207 
208   /////////////////////////////////////////////////////////////////////////////
209   template <class _Sender, class _Scheduler, class _Tag = set_value_t>
210   concept __completes_on =
211     __decays_to<__call_result_t<get_completion_scheduler_t<_Tag>, env_of_t<_Sender>>, _Scheduler>;
212 
213   /////////////////////////////////////////////////////////////////////////////
214   template <class _Sender, class _Scheduler, class _Env>
215   concept __starts_on = __decays_to<__call_result_t<get_scheduler_t, _Env>, _Scheduler>;
216 } // namespace stdexec
217 
218 STDEXEC_PRAGMA_POP()
219