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 "__diagnostics.hpp"
20 #include "__env.hpp"
21 #include "__execution_fwd.hpp"
22 #include "__meta.hpp"
23 #include "__sender_introspection.hpp"
24 #include "__senders_core.hpp"
25 #include "__tuple.hpp"
26 #include "__type_traits.hpp"
27
28 #include <cstddef>
29 #include <type_traits>
30 #include <utility> // for tuple_size/tuple_element
31
32 namespace stdexec
33 {
34 /////////////////////////////////////////////////////////////////////////////
35 // Generic __sender type
36 namespace __detail
37 {
38 template <class _Sender>
39 using __impl_of = decltype((__declval<_Sender>().__impl_));
40
41 struct __get_data
42 {
43 template <class _Data>
44 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__detail::__get_data45 _Data&& operator()(__ignore, _Data&& __data, auto&&...) const noexcept
46 {
47 return static_cast<_Data&&>(__data);
48 }
49 };
50 } // namespace __detail
51
52 namespace
53 {
54 template <class _Descriptor, auto _DescriptorFn = [] { return _Descriptor(); }>
55 inline constexpr auto __descriptor_fn_v = _DescriptorFn;
56
57 template <class _Tag, class _Data, class... _Child>
__descriptor_fn()58 inline constexpr auto __descriptor_fn()
59 {
60 return __descriptor_fn_v<__detail::__desc<_Tag, _Data, _Child...>>;
61 }
62 } // namespace
63
64 #if STDEXEC_NVHPC()
65 #define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \
66 stdexec::__descriptor_fn<_Tag, _Data, _Child>()
67 #else
68 #define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \
69 stdexec::__descriptor_fn_v<stdexec::__detail::__desc<_Tag, _Data, _Child>>
70 #endif
71
72 template <class _Tag>
73 struct __sexpr_impl;
74
75 namespace __detail
76 {
77 template <class _Sexpr, class _Receiver>
78 struct __op_state;
79
80 template <class _Sexpr, class _Receiver>
81 struct __connect_fn;
82
83 template <class _Tag, class _Sexpr, class _Receiver>
84 using __state_type_t =
85 __decay_t<__result_of<__sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>;
86
87 template <class _Self, class _Tag, class _Index, class _Sexpr, class _Receiver>
88 using __env_type_t = __result_of<
89 __sexpr_impl<__meval<__msecond, _Self, _Tag>>::get_env, _Index,
90 __state_type_t<__meval<__msecond, _Self, _Tag>, _Sexpr, _Receiver>&,
91 _Receiver&>;
92
93 template <class _Sexpr, class _Receiver>
94 concept __connectable =
95 __callable<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>,
96 __connect_fn<_Sexpr, _Receiver>> &&
97 __mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>;
98
99 // // Note: This is UB. UBSAN allows it for now.
100 // template <class _Parent, class _Child>
101 // _Parent* __parent_from_child(_Child* __child, _Child _Parent::*__mbr_ptr)
102 // noexcept {
103 // alignas(_Parent) char __buf[sizeof(_Parent)];
104 // _Parent* __parent = (_Parent*) &__buf;
105 // const std::ptrdiff_t __offset = (char*) &(__parent->*__mbr_ptr) - __buf;
106 // return (_Parent*) (static_cast<char*>(__child) - __offset);
107 // }
108
109 inline constexpr auto __get_attrs = //
110 [](__ignore, const auto&... __child) noexcept -> decltype(auto) {
111 if constexpr (sizeof...(__child) == 1)
112 {
113 return stdexec::get_env(
114 __child...); // BUGBUG: should be only the forwarding queries
115 }
116 else
117 {
118 return empty_env();
119 }
120 };
121
122 inline constexpr auto __get_env = //
123 []<class _Receiver>(__ignore, __ignore, const _Receiver& __rcvr) noexcept
124 -> env_of_t<const _Receiver&> { return stdexec::get_env(__rcvr); };
125
126 inline constexpr auto __get_state = //
127 []<class _Sender>(_Sender&& __sndr, __ignore) noexcept -> decltype(auto) {
128 return __sndr.apply(static_cast<_Sender&&>(__sndr), __get_data());
129 };
130
131 inline constexpr auto __connect = //
132 []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver __rcvr) //
133 noexcept(__nothrow_constructible_from<__op_state<_Sender, _Receiver>,
134 _Sender, _Receiver>)
135 -> __op_state<_Sender, _Receiver>
136 requires __connectable<_Sender, _Receiver>
137 {
138 return __op_state<_Sender, _Receiver>{static_cast<_Sender&&>(__sndr),
139 static_cast<_Receiver&&>(__rcvr)};
140 };
141
142 inline constexpr auto __start = //
143 []<class _StartTag = start_t, class... _ChildOps>(
144 __ignore, __ignore, _ChildOps&... __ops) noexcept {
145 (_StartTag()(__ops), ...);
146 };
147
148 inline constexpr auto __complete = //
149 []<class _Index, class _Receiver, class _SetTag, class... _Args>(
150 _Index, __ignore, _Receiver& __rcvr, _SetTag,
151 _Args&&... __args) noexcept {
152 static_assert(__v<_Index> == 0,
153 "I don't know how to complete this operation.");
154 _SetTag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
155 };
156
157 inline constexpr auto __sigs = //
158 []<class _Sender>(_Sender&& __sndr, __ignore = {}) noexcept {
159 static_assert(
160 __mnever<tag_of_t<_Sender>>,
161 "No customization of get_completion_signatures for this sender tag type.");
162 };
163
164 template <class _ReceiverId, class _Sexpr, class _Idx>
165 struct __receiver
166 {
167 struct __t
168 {
169 using receiver_concept = receiver_t;
170 using _Receiver = stdexec::__t<_ReceiverId>;
171 using __sexpr = _Sexpr;
172 using __index = _Idx;
173 using __id = __receiver;
174 using __parent_op_t = __op_state<_Sexpr, _Receiver>;
175 using __tag_t = tag_of_t<_Sexpr>;
176
177 // A pointer to the parent operation state, which contains the one
178 // created with this receiver.
179 __parent_op_t* __op_;
180
181 // template <class _ChildSexpr, class _ChildReceiver>
182 // static __t __from_op_state(__op_state<_ChildSexpr, _ChildReceiver>*
183 // __child) noexcept {
184 // using __parent_op_t = __op_state<_Sexpr, _Receiver>;
185 // std::ptrdiff_t __offset = __parent_op_t::template
186 // __get_child_op_offset<__v<_Idx>>();
187 // __parent_op_t* __parent = (__parent_op_t*)
188 // (static_cast<char*>(__child) - __offset); return __t{__parent};
189 // }
190
191 template <class... _Args>
192 STDEXEC_ATTRIBUTE((always_inline))
set_valuestdexec::__detail::__receiver::__t193 void set_value(_Args&&... __args) noexcept
194 {
195 __op_->__complete(_Idx(), stdexec::set_value,
196 static_cast<_Args&&>(__args)...);
197 }
198
199 template <class _Error>
200 STDEXEC_ATTRIBUTE((always_inline))
set_errorstdexec::__detail::__receiver::__t201 void set_error(_Error&& __err) noexcept
202 {
203 __op_->__complete(_Idx(), stdexec::set_error,
204 static_cast<_Error&&>(__err));
205 }
206
207 STDEXEC_ATTRIBUTE((always_inline))
set_stoppedstdexec::__detail::__receiver::__t208 void set_stopped() noexcept
209 {
210 __op_->__complete(_Idx(), stdexec::set_stopped);
211 }
212
213 template <__same_as<__t> _Self = __t>
214 STDEXEC_ATTRIBUTE((always_inline))
get_envstdexec::__detail::__receiver::__t215 auto get_env() const noexcept
216 -> __env_type_t<_Self, __tag_t, _Idx, _Sexpr, _Receiver>
217 {
218 return __op_->__get_env(_Idx());
219 }
220 };
221 };
222
223 // template <class _Receiver>
224 // using __sexpr_connected_with = __mapply<
225 // __mbind_front_q<__m_at, typename _Receiver::__index>,
226 // typename __call_result_t<__impl_of<typename _Receiver::__sexpr>, __cp,
227 // __get_desc>::__children>;
228
229 template <class _Sexpr, class _Receiver>
230 struct __op_base : __immovable
231 {
232 using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
233 using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
234
235 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
236 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
237
__op_basestdexec::__detail::__op_base238 __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) //
239 noexcept(__nothrow_decay_copyable<_Receiver> &&
240 __nothrow_move_constructible<__state_t>) :
241 __rcvr_(static_cast<_Receiver&&>(__rcvr)),
242 __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
243 __rcvr_))
244 {}
245
__rcvrstdexec::__detail::__op_base246 auto __rcvr() & noexcept -> _Receiver&
247 {
248 return __rcvr_;
249 }
250
__rcvrstdexec::__detail::__op_base251 auto __rcvr() const& noexcept -> const _Receiver&
252 {
253 return __rcvr_;
254 }
255 };
256
257 // template <class _Sexpr, class _Receiver>
258 // requires __is_instance_of<__id<_Receiver>, __receiver>
259 // && __decays_to<_Sexpr, __sexpr_connected_with<_Receiver>>
260 // struct __op_base<_Sexpr, _Receiver> : __immovable {
261 // using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
262 // using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
263
264 // STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
265
266 // __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr)
267 // :
268 // __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
269 // __rcvr)) { STDEXEC_ASSERT(this->__rcvr().__op_ == __rcvr.__op_);
270 // }
271
272 // _Receiver __rcvr() const noexcept {
273 // return _Receiver::__from_op_state( //
274 // static_cast<__op_state<_Sexpr, _Receiver>*>( //
275 // const_cast<__op_base*>(this)));
276 // }
277 // };
278
279 STDEXEC_PRAGMA_PUSH()
280 STDEXEC_PRAGMA_IGNORE_GNU("-Winvalid-offsetof")
281 STDEXEC_PRAGMA_IGNORE_EDG(offset_in_non_POD_nonstandard)
282
283 template <class _Sexpr, class _Receiver>
284 struct __enable_receiver_from_this
285 {
286 using __op_base_t = __op_base<_Sexpr, _Receiver>;
287
__receiverstdexec::__detail::__enable_receiver_from_this288 auto __receiver() noexcept -> decltype(auto)
289 {
290 using __derived_t = decltype(__op_base_t::__state_);
291 auto* __derived = static_cast<__derived_t*>(this);
292 constexpr std::size_t __offset = offsetof(__op_base_t, __state_);
293 auto* __base = reinterpret_cast<__op_base_t*>(
294 reinterpret_cast<char*>(__derived) - __offset);
295 return __base->__rcvr();
296 }
297 };
298
299 STDEXEC_PRAGMA_POP()
300
301 STDEXEC_PRAGMA_PUSH()
302 STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
303
304 template <class _Sexpr, class _Receiver>
305 struct __connect_fn
306 {
307 template <std::size_t _Idx>
308 using __receiver_t =
309 __t<__receiver<__id<_Receiver>, _Sexpr, __msize_t<_Idx>>>;
310
311 __op_state<_Sexpr, _Receiver>* __op_;
312
313 struct __impl
314 {
315 __op_state<_Sexpr, _Receiver>* __op_;
316
317 template <std::size_t... _Is, class... _Child>
operator ()stdexec::__detail::__connect_fn::__impl318 auto operator()(__indices<_Is...>, _Child&&... __child) const
319 noexcept((__nothrow_connectable<_Child, __receiver_t<_Is>> && ...))
320 -> __tuple_for<connect_result_t<_Child, __receiver_t<_Is>>...>
321 {
322 return __tuple{connect(static_cast<_Child&&>(__child),
323 __receiver_t<_Is>{__op_})...};
324 }
325 };
326
327 template <class... _Child>
operator ()stdexec::__detail::__connect_fn328 auto operator()(__ignore, __ignore, _Child&&... __child) const noexcept(
329 __nothrow_callable<__impl, __indices_for<_Child...>, _Child...>)
330 -> __call_result_t<__impl, __indices_for<_Child...>, _Child...>
331 {
332 return __impl{__op_}(__indices_for<_Child...>(),
333 static_cast<_Child&&>(__child)...);
334 }
335
operator ()stdexec::__detail::__connect_fn336 auto operator()(__ignore, __ignore) const noexcept -> __tuple_for<>
337 {
338 return {};
339 }
340 };
341 STDEXEC_PRAGMA_POP()
342
343 template <class _Sexpr, class _Receiver>
344 struct __op_state : __op_base<_Sexpr, _Receiver>
345 {
346 using __desc_t = typename __decay_t<_Sexpr>::__desc_t;
347 using __tag_t = typename __desc_t::__tag;
348 using __data_t = typename __desc_t::__data;
349 // using __children_t = typename __desc_t::__children;
350 using __state_t = typename __op_state::__state_t;
351 using __inner_ops_t =
352 __result_of<__sexpr_apply, _Sexpr, __connect_fn<_Sexpr, _Receiver>>;
353
354 __inner_ops_t __inner_ops_;
355
356 // template <std::size_t _Idx>
357 // static std::ptrdiff_t __get_child_op_offset() noexcept {
358 // __op_state* __self = (__op_state*) &__self;
359 // return (std::ptrdiff_t)((char*) &__tup::get<_Idx>(__self->__inner_ops_)
360 // - static_cast<char*>(__self));
361 // }
362
__op_statestdexec::__detail::__op_state363 __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) //
364 noexcept(__nothrow_constructible_from<__op_base<_Sexpr, _Receiver>,
365 _Sexpr&&, _Receiver&&> &&
366 __nothrow_callable<__sexpr_apply_t, _Sexpr&&,
367 __connect_fn<_Sexpr, _Receiver>>) :
368 __op_state::__op_base{static_cast<_Sexpr&&>(__sexpr),
369 static_cast<_Receiver&&>(__rcvr)},
370 __inner_ops_(__sexpr_apply(static_cast<_Sexpr&&>(__sexpr),
371 __connect_fn<_Sexpr, _Receiver>{this}))
372 {}
373
374 STDEXEC_ATTRIBUTE((always_inline))
startstdexec::__detail::__op_state375 void start() & noexcept
376 {
377 using __tag_t = typename __op_state::__tag_t;
378 auto&& __rcvr = this->__rcvr();
379 __inner_ops_.apply(
380 [&](auto&... __ops) noexcept {
381 __sexpr_impl<__tag_t>::start(this->__state_, __rcvr, __ops...);
382 },
383 __inner_ops_);
384 }
385
386 template <class _Index, class _Tag2, class... _Args>
387 STDEXEC_ATTRIBUTE((always_inline))
__completestdexec::__detail::__op_state388 void __complete(_Index, _Tag2, _Args&&... __args) noexcept
389 {
390 using __tag_t = typename __op_state::__tag_t;
391 auto&& __rcvr = this->__rcvr();
392 using _CompleteFn = __mtypeof<__sexpr_impl<__tag_t>::complete>;
393 if constexpr (__callable<_CompleteFn, _Index, __op_state&, _Tag2,
394 _Args...>)
395 {
396 __sexpr_impl<__tag_t>::complete(_Index(), *this, _Tag2(),
397 static_cast<_Args&&>(__args)...);
398 }
399 else
400 {
401 __sexpr_impl<__tag_t>::complete(_Index(), this->__state_, __rcvr,
402 _Tag2(),
403 static_cast<_Args&&>(__args)...);
404 }
405 }
406
407 template <class _Index>
408 STDEXEC_ATTRIBUTE((always_inline))
__get_envstdexec::__detail::__op_state409 auto __get_env(_Index) const noexcept
410 -> __env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver>
411 {
412 const auto& __rcvr = this->__rcvr();
413 return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state_, __rcvr);
414 }
415 };
416
417 inline constexpr auto __drop_front = //
418 []<class _Fn>(_Fn __fn) noexcept {
419 return [__fn = std::move(__fn)]<class... _Rest>(auto&&,
420 _Rest&&... __rest) //
421 noexcept(__nothrow_callable<const _Fn&, _Rest...>)
422 -> __call_result_t<const _Fn&, _Rest...> {
423 return __fn(static_cast<_Rest&&>(__rest)...);
424 };
425 };
426
427 template <class _Tag, class... _Captures>
428 STDEXEC_ATTRIBUTE((host, device, always_inline))
__captures(_Tag,_Captures &&...__captures2)429 constexpr auto __captures(_Tag, _Captures&&... __captures2)
430 {
431 return
432 [... __captures3 = static_cast<_Captures&&>(
433 __captures2)]<class _Cvref, class _Fun>(_Cvref,
434 _Fun&& __fun) mutable //
435 noexcept(
436 __nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>) //
437 -> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
438 requires __callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
439 {
440 // The use of decltype(__captures3) here instead of _Captures is a
441 // workaround for a codegen bug in nvc++.
442 return static_cast<_Fun&&>(__fun)(
443 _Tag(), const_cast<__minvoke<_Cvref, decltype(__captures3)>&&>(
444 __captures3)...);
445 };
446 }
447
448 template <class _Tag, class _Data, class... _Child>
449 using __captures_t = decltype(__detail::__captures(_Tag(), __declval<_Data>(),
450 __declval<_Child>()...));
451
452 template <class, class, class... _Child>
453 using __tuple_size_t = char[sizeof...(_Child) + 2];
454
455 template <std::size_t _Idx, class _Descriptor>
456 concept __in_range =
457 (_Idx < sizeof(__minvoke<_Descriptor, __q<__tuple_size_t>>));
458
459 } // namespace __detail
460
461 struct __sexpr_defaults
462 {
463 static constexpr auto get_attrs = __detail::__get_attrs;
464 static constexpr auto get_env = __detail::__get_env;
465 static constexpr auto get_state = __detail::__get_state;
466 static constexpr auto connect = __detail::__connect;
467 static constexpr auto start = __detail::__start;
468 static constexpr auto complete = __detail::__complete;
469 static constexpr auto get_completion_signatures = __detail::__sigs;
470 };
471
472 template <class _Tag>
473 struct __sexpr_impl : __sexpr_defaults
474 {
475 using not_specialized = void;
476 };
477
478 using __detail::__enable_receiver_from_this;
479
480 template <class _Tag>
481 using __get_attrs_fn = __result_of<__detail::__drop_front,
482 __mtypeof<__sexpr_impl<_Tag>::get_attrs>>;
483
484 //////////////////////////////////////////////////////////////////////////////////////////////////
485 // __basic_sender
486 template <class...>
487 struct __basic_sender
488 {
489 using __id = __basic_sender;
490 using __t = __basic_sender;
491 };
492
493 template <auto _DescriptorFn, class = __anon>
494 struct __sexpr
495 {
496 using sender_concept = sender_t;
497
498 using __id = __sexpr;
499 using __t = __sexpr;
500 using __desc_t = decltype(_DescriptorFn());
501 using __tag_t = typename __desc_t::__tag;
502 using __captures_t = __minvoke<__desc_t, __q<__detail::__captures_t>>;
503
504 mutable __captures_t __impl_;
505
506 template <class _Tag, class _Data, class... _Child>
507 STDEXEC_ATTRIBUTE((host, device, always_inline))
__sexprstdexec::__sexpr508 explicit __sexpr(_Tag, _Data&& __data, _Child&&... __child) :
509 __impl_(__detail::__captures(_Tag(), static_cast<_Data&&>(__data),
510 static_cast<_Child&&>(__child)...))
511 {}
512
513 template <class _Self>
514 using __impl = __sexpr_impl<__meval<__msecond, _Self, __tag_t>>;
515
516 template <class _Self = __sexpr>
517 STDEXEC_ATTRIBUTE((always_inline))
get_envstdexec::__sexpr518 auto get_env() const noexcept
519 -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>>
520 {
521 return __sexpr_apply(*this,
522 __detail::__drop_front(__impl<_Self>::get_attrs));
523 }
524
525 template <__decays_to<__sexpr> _Self, class... _Env>
526 STDEXEC_ATTRIBUTE((always_inline))
get_completion_signaturesstdexec::__sexpr527 static auto get_completion_signatures(_Self&&, _Env&&...) noexcept //
528 -> __msecond<__if_c<__decays_to<_Self, __sexpr>>,
529 __result_of<__impl<_Self>::get_completion_signatures,
530 _Self, _Env...>>
531 {
532 return {};
533 }
534
535 // BUGBUG fix receiver constraint here:
536 template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver>
537 STDEXEC_ATTRIBUTE((always_inline))
connectstdexec::__sexpr538 static auto connect(_Self&& __self, _Receiver&& __rcvr) //
539 noexcept(__noexcept_of<__impl<_Self>::connect, _Self, _Receiver>) //
540 -> __msecond<__if_c<__decays_to<_Self, __sexpr>>,
541 __result_of<__impl<_Self>::connect, _Self, _Receiver>>
542 {
543 return __impl<_Self>::connect(static_cast<_Self&&>(__self),
544 static_cast<_Receiver&&>(__rcvr));
545 }
546
547 template <class _Sender, class _ApplyFn>
548 STDEXEC_ATTRIBUTE((always_inline))
applystdexec::__sexpr549 static auto apply(_Sender&& __sndr, _ApplyFn&& __fun) //
550 noexcept(__nothrow_callable<__detail::__impl_of<_Sender>,
551 __copy_cvref_fn<_Sender>, _ApplyFn>) //
552 -> __call_result_t<__detail::__impl_of<_Sender>,
553 __copy_cvref_fn<_Sender>, _ApplyFn>
554 { //
555 return static_cast<_Sender&&>(__sndr).__impl_(
556 __copy_cvref_fn<_Sender>(), static_cast<_ApplyFn&&>(__fun)); //
557 }
558
559 template <std::size_t _Idx, __decays_to_derived_from<__sexpr> _Self>
560 STDEXEC_ATTRIBUTE((always_inline))
get(_Self && __self)561 friend auto get(_Self&& __self) noexcept -> decltype(auto)
562 requires __detail::__in_range<_Idx, __desc_t>
563 {
564 if constexpr (_Idx == 0)
565 {
566 return __tag_t();
567 }
568 else
569 {
570 return __self.__impl_(__copy_cvref_fn<_Self>(),
571 __nth_pack_element<_Idx>);
572 }
573 }
574 };
575
576 template <class _Tag, class _Data, class... _Child>
577 STDEXEC_ATTRIBUTE((host, device))
578 __sexpr(_Tag, _Data,
579 _Child...) -> __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>;
580
581 template <class _Tag, class _Data, class... _Child>
582 using __sexpr_t = __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>;
583
584 //////////////////////////////////////////////////////////////////////////////////////////////////
585 // __make_sexpr
586 namespace __detail
587 {
588 template <class _Tag>
589 struct __make_sexpr_t
590 {
591 template <class _Data = __, class... _Child>
operator ()stdexec::__detail::__make_sexpr_t592 constexpr auto operator()(_Data __data = {}, _Child... __child) const
593 {
594 return __sexpr_t<_Tag, _Data, _Child...>{
595 _Tag(), static_cast<_Data&&>(__data),
596 static_cast<_Child&&>(__child)...};
597 }
598 };
599 } // namespace __detail
600
601 template <class _Tag>
602 inline constexpr __detail::__make_sexpr_t<_Tag> __make_sexpr{};
603
604 // The __name_of utility defined below is used to pretty-print the type names of
605 // senders in compiler diagnostics.
606 namespace __detail
607 {
608 struct __basic_sender_name
609 {
610 template <class _Tag, class _Data, class... _Child>
611 using __result = __basic_sender<_Tag, _Data, __name_of<_Child>...>;
612
613 template <class _Sender>
614 using __f = __minvoke<typename __decay_t<_Sender>::__desc_t, __q<__result>>;
615 };
616
617 struct __id_name
618 {
619 template <class _Sender>
620 using __f = __name_of<__id<_Sender>>;
621 };
622
623 template <class _Sender>
624 extern __mcompose<__cplr, __name_of_fn<_Sender>> __name_of_v<_Sender&>;
625
626 template <class _Sender>
627 extern __mcompose<__cprr, __name_of_fn<_Sender>> __name_of_v<_Sender&&>;
628
629 template <class _Sender>
630 extern __mcompose<__cpclr, __name_of_fn<_Sender>> __name_of_v<const _Sender&>;
631
632 template <auto _Descriptor>
633 extern __basic_sender_name __name_of_v<__sexpr<_Descriptor>>;
634
635 template <__has_id _Sender>
636 requires(!same_as<__id<_Sender>, _Sender>)
637 extern __id_name __name_of_v<_Sender>;
638 } // namespace __detail
639 } // namespace stdexec
640
641 namespace std
642 {
643 template <auto _Descriptor>
644 struct tuple_size<stdexec::__sexpr<_Descriptor>> :
645 integral_constant<size_t,
646 stdexec::__v<stdexec::__minvoke<
647 stdexec::__result_of<_Descriptor>, stdexec::__msize>>>
648 {};
649
650 template <size_t _Idx, auto _Descriptor>
651 struct tuple_element<_Idx, stdexec::__sexpr<_Descriptor>>
652 {
653 using type = //
654 stdexec::__remove_rvalue_reference_t< //
655 stdexec::__call_result_t< //
656 stdexec::__detail::__impl_of<stdexec::__sexpr<_Descriptor>>,
657 stdexec::__cp, stdexec::__nth_pack_element_t<_Idx>>>;
658 };
659 } // namespace std
660