forked from NVIDIA/stdexec
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathjust_from.hpp
More file actions
290 lines (257 loc) · 9.5 KB
/
just_from.hpp
File metadata and controls
290 lines (257 loc) · 9.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/*
* Copyright (c) 2024 NVIDIA Corporation
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "../stdexec/__detail/__meta.hpp"
#include "../stdexec/execution.hpp"
#include "completion_behavior.hpp"
namespace experimental::execution
{
struct AN_ERROR_COMPLETION_MUST_HAVE_EXACTLY_ONE_ERROR_ARGUMENT;
struct A_STOPPED_COMPLETION_MUST_HAVE_NO_ARGUMENTS;
struct just_from_t;
struct just_error_from_t;
struct just_stopped_from_t;
namespace detail
{
auto _just_from(just_from_t*) -> STDEXEC::set_value_t;
auto _just_from(just_error_from_t*) -> STDEXEC::set_error_t;
auto _just_from(just_stopped_from_t*) -> STDEXEC::set_stopped_t;
} // namespace detail
template <class JustTag>
struct _just_from
{ // NOLINT(bugprone-crtp-constructor-accessibility)
private:
friend JustTag;
using _set_tag_t = decltype(detail::_just_from(static_cast<JustTag*>(nullptr)));
using _diag_t = STDEXEC::__if_c<STDEXEC_IS_SAME(_set_tag_t, STDEXEC::set_error_t),
AN_ERROR_COMPLETION_MUST_HAVE_EXACTLY_ONE_ERROR_ARGUMENT,
A_STOPPED_COMPLETION_MUST_HAVE_NO_ARGUMENTS>;
template <class... Ts>
using _error_t = STDEXEC::_ERROR_<STDEXEC::_WHAT_(_diag_t),
STDEXEC::_WHERE_(STDEXEC::_IN_ALGORITHM_, JustTag),
STDEXEC::_WITH_COMPLETION_SIGNATURE_<_set_tag_t(Ts...)>>;
struct _probe_fn
{
template <class... Ts>
auto operator()(Ts&&... ts) const noexcept -> _error_t<Ts...>;
template <class... Ts>
requires STDEXEC::__cmplsigs::__is_compl_sig<_set_tag_t(Ts...)>
auto operator()(Ts&&...) const noexcept -> STDEXEC::completion_signatures<_set_tag_t(Ts...)>
{
return {};
}
};
template <class Rcvr>
struct _complete_fn
{
Rcvr& _rcvr;
template <class... Ts>
STDEXEC_ATTRIBUTE(always_inline, host, device)
void operator()(Ts&&... ts) const noexcept
{
_set_tag_t()(static_cast<Rcvr&&>(_rcvr), static_cast<Ts&&>(ts)...);
}
};
template <class Rcvr, class Fn>
struct _opstate
{
using operation_state_concept = STDEXEC::operation_state_tag;
Rcvr _rcvr;
Fn _fn;
STDEXEC_ATTRIBUTE(host, device) void start() & noexcept
{
if constexpr (STDEXEC::__nothrow_callable<Fn, _complete_fn<Rcvr>>)
{
static_cast<Fn&&>(_fn)(_complete_fn<Rcvr>{_rcvr});
}
else
{
STDEXEC_TRY
{
static_cast<Fn&&>(_fn)(_complete_fn<Rcvr>{_rcvr});
}
STDEXEC_CATCH_ALL
{
STDEXEC::set_error(static_cast<Rcvr&&>(_rcvr), std::current_exception());
}
}
}
};
struct _nothrow_completions
{
template <class Fn>
using __f = STDEXEC::__call_result_t<Fn, _probe_fn>;
};
struct _throw_completions
{
template <class Fn>
using __f = STDEXEC::__concat_completion_signatures_t<STDEXEC::__call_result_t<Fn, _probe_fn>,
STDEXEC::__eptr_completion_t>;
};
template <class Fn>
using _completions = STDEXEC::__minvoke_if_c<STDEXEC::__nothrow_callable<Fn, _probe_fn>,
_nothrow_completions,
_throw_completions,
Fn>;
template <class... Tags>
struct _attrs
{
template <STDEXEC::__one_of<Tags...> Tag>
[[nodiscard]]
constexpr auto query(get_completion_behavior_t<Tag>) const noexcept
{
return completion_behavior::inline_completion;
}
};
template <class Fn>
struct _sndr_base
{
using sender_concept = STDEXEC::sender_tag;
using completion_signatures = _completions<Fn>;
STDEXEC_ATTRIBUTE(no_unique_address) JustTag _tag;
Fn _fn;
template <class Rcvr>
STDEXEC_ATTRIBUTE(host, device)
auto connect(Rcvr rcvr) && noexcept(STDEXEC::__nothrow_decay_copyable<Rcvr, Fn>)
-> _opstate<Rcvr, Fn>
{
return _opstate<Rcvr, Fn>{static_cast<Rcvr&&>(rcvr), static_cast<Fn&&>(_fn)};
}
template <class Rcvr>
STDEXEC_ATTRIBUTE(host, device)
auto connect(Rcvr rcvr) const & noexcept(STDEXEC::__nothrow_decay_copyable<Rcvr, Fn const &>)
-> _opstate<Rcvr, Fn>
{
return _opstate<Rcvr, Fn>{static_cast<Rcvr&&>(rcvr), _fn};
}
[[nodiscard]]
constexpr auto get_env() const noexcept
{
// Extract the tags from the completion signatures and use them to construct the attributes.
return STDEXEC::__mapply<
STDEXEC::__mtransform<STDEXEC::__q1<STDEXEC::__detail::__tag_of_sig_t>,
STDEXEC::__munique<STDEXEC::__qq<_just_from::_attrs>>>,
completion_signatures>();
}
template <class Rcvr>
STDEXEC_ATTRIBUTE(host, device)
auto submit(Rcvr rcvr) && noexcept -> void
{
auto op = static_cast<_sndr_base&&>(*this).connect(static_cast<Rcvr&&>(rcvr));
STDEXEC::start(op);
}
template <class Rcvr>
STDEXEC_ATTRIBUTE(host, device)
auto submit(Rcvr rcvr) const & noexcept -> void
{
auto op = this->connect(static_cast<Rcvr&&>(rcvr));
STDEXEC::start(op);
}
};
template <class Fn, class Tag = JustTag>
using _sndr = Tag::template _sndr<Fn>;
public:
template <class Fn>
STDEXEC_ATTRIBUTE(always_inline, host, device)
auto operator()(Fn fn) const noexcept
{
if constexpr (STDEXEC::__callable<Fn, _probe_fn>)
{
using _completions = STDEXEC::__call_result_t<Fn, _probe_fn>;
static_assert(STDEXEC::__is_instance_of<_completions, STDEXEC::completion_signatures>,
"The function passed to just_from, just_error_from, and just_stopped_from "
"must return an "
"instance of a specialization of STDEXEC::completion_signatures<>.");
return _sndr<Fn>{
{{}, static_cast<Fn&&>(fn)}
};
}
else
{
static_assert(STDEXEC::__callable<Fn, _probe_fn>,
"The function passed to just_from, just_error_from, and just_stopped_from "
"must be "
"callable with a sink function.");
}
}
};
//! @brief `just_from(fn)` creates a sender that completes inline by passing a "sink" function to
//! `fn`. Calling the sink function with arguments sends the arguments as values to the receiver.
//!
//! @post The sink function passed to `fn` must be called exactly once.
//!
//! @param fn The callable to be invoked when the sender is started.
//!
//! @par
//! The function passed to `just_from` must return an instance of a specialization of
//! `STDEXEC::completion_signatures<>` that describes the ways the sink function might be
//! invoked. The sink function returns such a specialization of `STDEXEC::completion_signatures<>`
//! corresponding to the arguments passed to it, but if your function uses the sink function
//! in several different ways, you must specify the return type explicitly.
//!
//! @par Example:
//! @code
//! // The following sender is equivalent to just(42, 3.14):
//! auto sndr = exec::just_from([](auto sink) { return sink(42, 3.14); });
//! @endcode
//!
//! @par Example:
//! @code
//! // A just_from sender can have multiple completion signatures:
//! auto sndr = exec::just_from(
//! [](auto sink) {
//! if (some-condition) {
//! sink(42);
//! } else {
//! sink(3.14);
//! }
//! return STDEXEC::completion_signatures<STDEXEC::set_value_t(int),
//! STDEXEC::set_value_t(double)>{};
//! });
//! @endcode
struct just_from_t : _just_from<just_from_t>
{
template <class _Fn>
struct _sndr : _just_from::_sndr_base<_Fn>
{};
};
//! @brief `just_error_from(fn)` creates a sender that completes inline by passing a "sink"
//! function to `fn`. Calling the sink function with an argument sends that argument as an error
//! to the receiver.
//!
//! @sa just_from
struct just_error_from_t : _just_from<just_error_from_t>
{
template <class _Fn>
struct _sndr : _just_from::_sndr_base<_Fn>
{};
};
//! @brief `just_stopped_from(fn)` creates a sender that completes inline by passing a "sink"
//! function to `fn`. Calling the sink function with no arguments sends a stopped signal to the
//! receiver.
//!
//! @sa just_from
struct just_stopped_from_t : _just_from<just_stopped_from_t>
{
template <class _Fn>
struct _sndr : _just_from::_sndr_base<_Fn>
{};
};
inline constexpr just_from_t just_from{};
inline constexpr just_error_from_t just_error_from{};
inline constexpr just_stopped_from_t just_stopped_from{};
} // namespace experimental::execution
namespace exec = experimental::execution;