kokkos-execution 0.0.1
Loading...
Searching...
No Matches
invoke.hpp
Go to the documentation of this file.
1// NOLINTBEGIN
2/*
3 * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4 * Copyright (c) 2025 Robert Leahy. All rights reserved.
5 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 *
7 * Licensed under the Apache License, Version 2.0 with LLVM Exceptions (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * https://llvm.org/LICENSE.txt
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20#pragma once
21
22#include <exception>
23#include <functional>
24#include <tuple>
25#include <type_traits>
26
27#include "elide.hpp"
28#include "stdexec/execution.hpp"
29
31
32namespace detail::invoke {
33
34struct tag { };
35
36struct t {
37 template <::STDEXEC::sender Sender, typename F>
38 constexpr ::STDEXEC::sender auto operator()(Sender&& sender, F&& f) const noexcept(
39 std::is_nothrow_constructible_v<std::remove_cvref_t<Sender>, Sender>
40 && std::is_nothrow_constructible_v<std::remove_cvref_t<F>, F>) {
41 return ::STDEXEC::__sexpr{t(), (F&&) f, (Sender&&) sender};
42 }
43
44 template <typename F>
45 constexpr auto operator()(F&& f) const noexcept(std::is_nothrow_constructible_v<std::remove_cvref_t<F>, F>) {
46 return ::STDEXEC::__closure(*this, (F&&) f);
47 }
48
49 template <typename Sender, typename Env>
50 static constexpr auto transform_sender(::STDEXEC::set_value_t, Sender&& sender, const Env&)
51 noexcept(std::is_nothrow_constructible_v<std::remove_cvref_t<Sender>, Sender>) {
52 auto&& [_, f, predecessor] = (Sender&&) sender;
53 static_assert(::STDEXEC::sender<decltype(predecessor)>);
54 return ::STDEXEC::__sexpr{tag(), std::tuple((decltype(f)&&) f, (decltype(predecessor)&&) predecessor)};
55 }
56};
57
58template <typename Sender, typename... Env>
60
61template <typename Sender>
62struct sender_check<Sender> {
63 static constexpr bool value = ::STDEXEC::sender<Sender>;
64};
65
66template <typename Sender, typename Env>
67struct sender_check<Sender, Env> {
68 static constexpr bool value = ::STDEXEC::sender_in<Sender, Env>;
69};
70
71template <typename F, typename... Env>
73 template <typename... Args>
74 class impl_ {
75 static_assert(std::is_invocable_v<F, Args...>);
76 using sender_ = std::invoke_result_t<F, Args...>;
77 static_assert(sender_check<sender_, Env...>::value);
78 static constexpr bool nothrow_invoke_ = std::is_nothrow_invocable_v<F, Args...>;
79 static constexpr bool nothrow_connect_ =
80 ::STDEXEC::__nothrow_connectable<sender_, ::STDEXEC::__receiver_archetype<Env...>>;
81 public:
82 using type = ::STDEXEC::transform_completion_signatures<
83 ::STDEXEC::completion_signatures_of_t<sender_, Env...>,
84 std::conditional_t<
86 ::STDEXEC::completion_signatures<>,
87 ::STDEXEC::completion_signatures<::STDEXEC::set_error_t(std::exception_ptr)>
88 >
89 >;
90 };
91 public:
92 template <typename... Args>
93 using fn = impl_<Args...>::type;
94};
95
96template <typename Sender, typename F, typename... Env>
97using completions = ::STDEXEC::transform_completion_signatures<
98 ::STDEXEC::completion_signatures_of_t<Sender, Env...>,
99 ::STDEXEC::completion_signatures<>,
100 transform_set_value<F, Env...>::template fn
101>;
102
103// TODO: We should be able to get a better "message" than this
105
106template <::STDEXEC::receiver Receiver>
108 using receiver_concept = ::STDEXEC::receiver_t;
109 Receiver& r_;
110
111 constexpr ::STDEXEC::env_of_t<Receiver> get_env() const noexcept {
112 return ::STDEXEC::get_env(r_);
113 }
114
115 template <typename... Args>
116 requires ::STDEXEC::receiver_of<Receiver, ::STDEXEC::completion_signatures<::STDEXEC::set_value_t(Args...)>>
117 constexpr void set_value(Args&&... args) && noexcept {
118 ::STDEXEC::set_value((Receiver&&) r_, (Args&&) args...);
119 }
120
121 template <typename T>
122 requires ::STDEXEC::receiver_of<Receiver, ::STDEXEC::completion_signatures<::STDEXEC::set_error_t(T)>>
123 constexpr void set_error(T&& t) && noexcept {
124 ::STDEXEC::set_error((Receiver&&) r_, (T&&) t);
125 }
126
127 constexpr void set_stopped() && noexcept
128 requires ::STDEXEC::receiver_of<Receiver, ::STDEXEC::completion_signatures<::STDEXEC::set_stopped_t()>>
129 {
130 ::STDEXEC::set_stopped((Receiver&&) r_);
131 }
132};
133
134template <typename F, typename Receiver, typename Additional, typename Signatures, typename InProgress = std::tuple<>>
136
137template <
138 typename F,
139 typename Receiver,
140 typename Additional,
141 typename... Args,
142 typename... Signatures,
143 typename... States
144>
146 F,
147 Receiver,
148 Additional,
149 ::STDEXEC::completion_signatures<::STDEXEC::set_value_t(Args...), Signatures...>,
150 std::tuple<States...>
151> {
153 F,
154 Receiver,
155 Additional,
156 ::STDEXEC::completion_signatures<Signatures...>,
157 std::tuple<::STDEXEC::connect_result_t<std::invoke_result_t<F, Args...>, receiver_ref<Receiver>>, States...>
158 >::type;
159};
160
161template <typename F, typename Receiver, typename Additional, typename Signature, typename... Signatures, typename States>
163 F,
164 Receiver,
165 Additional,
166 ::STDEXEC::completion_signatures<Signature, Signatures...>,
167 States
168> {
170 F,
171 Receiver,
172 Additional,
173 ::STDEXEC::completion_signatures<Signatures...>,
174 States
175 >::type;
176};
177
178template <typename F, typename Receiver, typename... Additional, typename... States>
180 F,
181 Receiver,
182 std::tuple<Additional...>,
183 ::STDEXEC::completion_signatures<>,
184 std::tuple<States...>
185> {
186 using type = ::STDEXEC::__munique<::STDEXEC::__qq<std::variant>>::__f<Additional..., States...>;
187};
188
189template <typename Sender, typename F, typename Receiver>
190class state {
191 using env_type_ = ::STDEXEC::env_of_t<Receiver>;
192
194 using receiver_concept = ::STDEXEC::receiver_t;
196 constexpr env_type_ get_env() const noexcept;
197 template <typename... Args>
198 constexpr void set_value(Args&&...) && noexcept;
199 template <typename... Args>
200 constexpr void set_error(Args&&...) && noexcept;
201 template <typename... Args>
202 constexpr void set_stopped(Args&&...) && noexcept;
203 };
204
205 using operation_state_type_ = ::STDEXEC::connect_result_t<Sender, receiver_type_>;
207 F,
208 Receiver,
209 std::tuple<operation_state_type_>,
210 ::STDEXEC::completion_signatures_of_t<Sender, receiver_type_>
211 >::type;
212 Receiver r_;
213 F f_;
215 static constexpr bool nothrow_connect_ = ::STDEXEC::__nothrow_connectable<Sender, receiver_type_>;
216 public:
217 template <typename T>
218 explicit constexpr state(Sender&& s, T&& t, Receiver r)
219 noexcept(std::is_nothrow_constructible_v<F, T> && nothrow_connect_)
220 : r_((Receiver&&) r)
221 , f_((T&&) t)
222 , ops_(std::in_place_type<operation_state_type_>, ::exec::elide([&]() noexcept(nothrow_connect_) {
223 return ::STDEXEC::connect((Sender&&) s, receiver_type_{*this});
224 })) {
225 }
226
227 constexpr void start() & noexcept {
228 const auto ptr = std::get_if<operation_state_type_>(&ops_);
229 STDEXEC_ASSERT(ptr);
230 ::STDEXEC::start(*ptr);
231 }
232};
233
234template <typename Sender, typename F, typename Receiver>
236 return ::STDEXEC::get_env(self_.r_);
237}
238
239template <typename Sender, typename F, typename Receiver>
240template <typename... Args>
241constexpr void state<Sender, F, Receiver>::receiver_type_::set_value(Args&&... args) && noexcept {
242 constexpr auto nothrow_connect =
243 ::STDEXEC::__nothrow_connectable<std::invoke_result_t<F, Args...>, receiver_ref<Receiver>>;
244 // We store this locally because we're going to destroy the operation state
245 // and therefore, transitively, *this
246 auto&& self = self_;
247 try {
248 // It's important we use auto&& here not auto because the invocable might
249 // return a reference to a sender
250 auto&& sender = std::invoke((F&&) self.f_, (Args&&) args...);
251 // receiver_ref is important here, imagine we didn't use receiver_ref and
252 // instead directly moved the final receiver into connect, then if connect
253 // threw we'd already potentially have "consumed" the receiver and wouldn't
254 // be able to send the error completion anywhere
255 using op_state_type = ::STDEXEC::connect_result_t<decltype(sender), receiver_ref<Receiver>>;
256 auto&& op = self.ops_.template emplace<op_state_type>(::exec::elide([&]() noexcept(nothrow_connect) {
257 return ::STDEXEC::connect((decltype(sender)&&) sender, receiver_ref<Receiver>{self.r_});
258 }));
259 ::STDEXEC::start(op);
260 } catch (...) {
261 if constexpr (std::is_nothrow_invocable_v<F, Args...> && nothrow_connect) {
262 STDEXEC_UNREACHABLE();
263 } else {
264 ::STDEXEC::set_error((Receiver&&) self.r_, std::current_exception());
265 }
266 }
267}
268
269template <typename Sender, typename F, typename Receiver>
270template <typename... Args>
271constexpr void state<Sender, F, Receiver>::receiver_type_::set_error(Args&&... args) && noexcept {
272 ::STDEXEC::set_error((Receiver&&) self_.r_, (Args&&) args...);
273}
274
275template <typename Sender, typename F, typename Receiver>
276template <typename... Args>
277constexpr void state<Sender, F, Receiver>::receiver_type_::set_stopped(Args&&... args) && noexcept {
278 ::STDEXEC::set_stopped((Receiver&&) self_.r_, (Args&&) args...);
279}
280
281template <typename Sender, typename Receiver>
283 using tuple_type_ = std::remove_cvref_t<::STDEXEC::__data_of<Sender>>;
284 using sender_type_ = ::STDEXEC::__copy_cvref_t<Sender, std::tuple_element_t<1, tuple_type_>>;
285 using invocable_type_ = std::tuple_element_t<0, tuple_type_>;
286 public:
287 static constexpr bool value = std::is_nothrow_constructible_v<
290 ::STDEXEC::__copy_cvref_t<Sender, invocable_type_>,
291 Receiver
292 >;
293};
294
295template <typename Tuple, typename, typename = std::remove_cvref_t<Tuple>>
297
298template <typename Tuple, typename Receiver, typename F, typename Sender>
299struct get_state_result<Tuple, Receiver, std::tuple<F, Sender>> {
301};
302
303struct impl : public ::STDEXEC::__sexpr_defaults {
304 template <typename Self, typename... Env>
305 static consteval auto __get_completion_signatures() {
306 using tuple = std::remove_cvref_t<::STDEXEC::__data_of<Self>>;
307 using f = std::tuple_element_t<0, tuple>;
308 using sender = ::STDEXEC::__copy_cvref_t<Self, std::tuple_element_t<1, tuple>>;
309 static_assert(::STDEXEC::sender<sender>);
310 if constexpr (sizeof...(Env)) {
311 static_assert(::STDEXEC::sender_in<sender, Env...>);
312 }
313 if constexpr (::STDEXEC::__minvocable_q<completions, sender, f, Env...>) {
314 return completions<sender, f, Env...>{};
315 } else {
316 return ::STDEXEC::__throw_compile_time_error<
318 ::STDEXEC::_WITH_SENDER_<Self>
319 >();
320 }
321 }
322
323 static constexpr auto __get_state = []<typename Sender, typename Receiver>(Sender&& sender, Receiver r) noexcept(
326 auto&& [_, tuple] = (Sender&&) sender;
327 auto&& [f, inner] = (decltype(tuple)&&) tuple;
329 (decltype(inner)&&) inner, (decltype(f)&&) f, (Receiver&&) r);
330 };
331 static constexpr auto __start = [](auto& state) noexcept {
332 state.start();
333 };
334};
335
336} // namespace detail::invoke
337
339inline constexpr invoke_t invoke;
340
341} // namespace experimental::execution
342
343namespace STDEXEC {
344
345template <>
346struct __sexpr_impl<::exec::detail::invoke::tag> : ::exec::detail::invoke::impl { };
347
348template <>
349struct __sexpr_impl<::exec::invoke_t> : ::STDEXEC::__sexpr_defaults {
350 template <class Sender, class... Env>
351 static consteval auto get_completion_signatures() {
352 using type = decltype(::STDEXEC::transform_sender(std::declval<Sender>(), std::declval<const Env&>()...));
353 static_assert(!std::is_same_v<std::remove_cvref_t<type>, std::remove_cvref_t<Sender>>);
354 return ::STDEXEC::get_completion_signatures<type, Env...>();
355 }
356};
357
358} // namespace STDEXEC
359
360// NOLINTEND
::stdexec::__copy_cvref_t< Sender, std::tuple_element_t< 1, tuple_type_ > > sender_type_
Definition invoke.hpp:284
std::tuple_element_t< 0, tuple_type_ > invocable_type_
Definition invoke.hpp:285
std::remove_cvref_t<::stdexec::__data_of< Sender > > tuple_type_
Definition invoke.hpp:283
variant_for_operation_states< F, Receiver, std::tuple< operation_state_type_ >, ::stdexec::completion_signatures_of_t< Sender, receiver_type_ > >::type operation_states_type_
Definition invoke.hpp:206
constexpr state(Sender &&s, T &&t, Receiver r) noexcept(std::is_nothrow_constructible_v< F, T > &&nothrow_connect_)
Definition invoke.hpp:218
::stdexec::env_of_t< Receiver > env_type_
Definition invoke.hpp:191
::stdexec::connect_result_t< Sender, receiver_type_ > operation_state_type_
Definition invoke.hpp:205
::stdexec::transform_completion_signatures< ::stdexec::completion_signatures_of_t< sender_, Env... >, std::conditional_t< nothrow_invoke_ &&nothrow_connect_, ::stdexec::completion_signatures<>, ::stdexec::completion_signatures<::stdexec::set_error_t(std::exception_ptr)> > > type
Definition invoke.hpp:82
::stdexec::transform_completion_signatures< ::stdexec::completion_signatures_of_t< Sender, Env... >, ::stdexec::completion_signatures<>, transform_set_value< F, Env... >::template fn > completions
Definition invoke.hpp:97
detail::invoke::t invoke_t
Definition invoke.hpp:338
constexpr invoke_t invoke
Definition invoke.hpp:339
static consteval auto __get_completion_signatures()
Definition invoke.hpp:305
requires ::stdexec::receiver_of< Receiver, ::stdexec::completion_signatures<::stdexec::set_error_t(T)> > constexpr void set_error(T &&t) &&noexcept
Definition invoke.hpp:123
requires ::stdexec::receiver_of< Receiver, ::stdexec::completion_signatures<::stdexec::set_value_t(Args...)> > constexpr void set_value(Args &&... args) &&noexcept
Definition invoke.hpp:117
constexpr ::stdexec::env_of_t< Receiver > get_env() const noexcept
Definition invoke.hpp:111
static constexpr auto transform_sender(::stdexec::set_value_t, Sender &&sender, const Env &) noexcept(std::is_nothrow_constructible_v< std::remove_cvref_t< Sender >, Sender >)
Definition invoke.hpp:50
constexpr ::stdexec::sender auto operator()(Sender &&sender, F &&f) const noexcept(std::is_nothrow_constructible_v< std::remove_cvref_t< Sender >, Sender > &&std::is_nothrow_constructible_v< std::remove_cvref_t< F >, F >)
Definition invoke.hpp:38
constexpr auto operator()(F &&f) const noexcept(std::is_nothrow_constructible_v< std::remove_cvref_t< F >, F >)
Definition invoke.hpp:45
variant_for_operation_states< F, Receiver, Additional, ::stdexec::completion_signatures< Signatures... >, States >::type type
Definition invoke.hpp:169
variant_for_operation_states< F, Receiver, Additional, ::stdexec::completion_signatures< Signatures... >, std::tuple<::stdexec::connect_result_t< std::invoke_result_t< F, Args... >, receiver_ref< Receiver > >, States... > >::type type
Definition invoke.hpp:152
static consteval auto get_completion_signatures()
Definition invoke.hpp:351