1 /*
2 * Copyright (c) 2021-2022 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 "__detail/__config.hpp"
19 #include "__detail/__meta.hpp"
20 #include "concepts.hpp"
21
22 #include <cstddef>
23 #include <functional>
24 #include <tuple>
25 #include <type_traits>
26
27 namespace stdexec::__std_concepts
28 {
29 #if STDEXEC_HAS_STD_CONCEPTS_HEADER()
30 using std::invocable;
31 #else
32 template <class _Fun, class... _As>
33 concept invocable = //
34 requires(_Fun&& __f, _As&&... __as) {
35 std::invoke(static_cast<_Fun&&>(__f), static_cast<_As&&>(__as)...);
36 };
37 #endif
38 } // namespace stdexec::__std_concepts
39
40 namespace std
41 {
42 using namespace stdexec::__std_concepts;
43 } // namespace std
44
45 namespace stdexec
46 {
47 template <auto _Fun>
48 struct __function_constant
49 {
50 using _FunT = decltype(_Fun);
51
52 template <class... _Args>
53 requires __callable<_FunT, _Args...>
operator ()stdexec::__function_constant54 STDEXEC_ATTRIBUTE((always_inline)) auto operator()(_Args&&... __args) const
55 noexcept(noexcept(_Fun(static_cast<_Args&&>(__args)...)))
56 -> decltype(_Fun(static_cast<_Args&&>(__args)...))
57 {
58 return _Fun(static_cast<_Args&&>(__args)...);
59 }
60 };
61
62 template <class _Ty, class _Cl, _Ty _Cl::*_MemPtr>
63 struct __function_constant<_MemPtr>
64 {
65 using _FunT = _Ty _Cl::*;
66
67 template <class _Arg>
68 requires requires(_Arg&& __arg) { static_cast<_Arg&&>(__arg).*_MemPtr; }
69 STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
operator ()stdexec::__function_constant70 operator()(_Arg&& __arg) const noexcept
71 -> decltype(((static_cast<_Arg&&>(__arg)).*_MemPtr))
72 {
73 return static_cast<_Arg&&>(__arg).*_MemPtr;
74 }
75 };
76
77 template <auto _Fun>
78 inline constexpr __function_constant<_Fun> __function_constant_v{};
79
80 template <class _Fun0, class _Fun1>
81 struct __composed
82 {
83 STDEXEC_ATTRIBUTE((no_unique_address))
84 _Fun0 __t0_;
85 STDEXEC_ATTRIBUTE((no_unique_address))
86 _Fun1 __t1_;
87
88 template <class... _Ts>
89 requires __callable<_Fun1, _Ts...> &&
90 __callable<_Fun0, __call_result_t<_Fun1, _Ts...>>
91 STDEXEC_ATTRIBUTE((always_inline))
92 __call_result_t<_Fun0, __call_result_t<_Fun1, _Ts...>>
operator ()stdexec::__composed93 operator()(_Ts&&... __ts) &&
94 {
95 return static_cast<_Fun0&&>(__t0_)(
96 static_cast<_Fun1&&>(__t1_)(static_cast<_Ts&&>(__ts)...));
97 }
98
99 template <class... _Ts>
100 requires __callable<const _Fun1&, _Ts...> &&
101 __callable<const _Fun0&, __call_result_t<const _Fun1&, _Ts...>>
102 STDEXEC_ATTRIBUTE((always_inline))
103 __call_result_t<_Fun0, __call_result_t<_Fun1, _Ts...>>
operator ()stdexec::__composed104 operator()(_Ts&&... __ts) const&
105 {
106 return __t0_(__t1_(static_cast<_Ts&&>(__ts)...));
107 }
108 };
109
110 inline constexpr struct __compose_t
111 {
112 template <class _Fun0, class _Fun1>
113 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__compose_t114 __composed<_Fun0, _Fun1> operator()(_Fun0 __fun0, _Fun1 __fun1) const
115 {
116 return {static_cast<_Fun0&&>(__fun0), static_cast<_Fun1&&>(__fun1)};
117 }
118 } __compose{};
119
120 namespace __invoke_
121 {
122 template <class>
123 inline constexpr bool __is_refwrap = false;
124 template <class _Up>
125 inline constexpr bool __is_refwrap<std::reference_wrapper<_Up>> = true;
126
127 struct __funobj
128 {
129 template <class _Fun, class... _Args>
130 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__funobj131 constexpr auto operator()(_Fun&& __fun, _Args&&... __args) const noexcept(
132 noexcept((static_cast<_Fun&&>(__fun))(static_cast<_Args&&>(__args)...)))
133 -> decltype((static_cast<_Fun&&>(__fun))(
134 static_cast<_Args&&>(__args)...))
135 {
136 return static_cast<_Fun&&>(__fun)(static_cast<_Args&&>(__args)...);
137 }
138 };
139
140 struct __memfn
141 {
142 template <class _Memptr, class _Ty, class... _Args>
143 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__memfn144 constexpr auto operator()(_Memptr __mem_ptr, _Ty&& __ty,
145 _Args&&... __args) const
146 noexcept(noexcept(((static_cast<_Ty&&>(__ty)).*
147 __mem_ptr)(static_cast<_Args&&>(__args)...)))
148 -> decltype(((static_cast<_Ty&&>(__ty)).*
149 __mem_ptr)(static_cast<_Args&&>(__args)...))
150 {
151 return ((static_cast<_Ty&&>(__ty)).*
152 __mem_ptr)(static_cast<_Args&&>(__args)...);
153 }
154 };
155
156 struct __memfn_refwrap
157 {
158 template <class _Memptr, class _Ty, class... _Args>
159 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__memfn_refwrap160 constexpr auto operator()(_Memptr __mem_ptr, _Ty __ty,
161 _Args&&... __args) const
162 noexcept(noexcept((__ty.get().*
163 __mem_ptr)(static_cast<_Args&&>(__args)...)))
164 -> decltype((__ty.get().*
165 __mem_ptr)(static_cast<_Args&&>(__args)...))
166 {
167 return (__ty.get().*__mem_ptr)(static_cast<_Args&&>(__args)...);
168 }
169 };
170
171 struct __memfn_smartptr
172 {
173 template <class _Memptr, class _Ty, class... _Args>
174 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__memfn_smartptr175 constexpr auto operator()(_Memptr __mem_ptr, _Ty&& __ty,
176 _Args&&... __args) const
177 noexcept(noexcept(((*static_cast<_Ty&&>(__ty)).*
178 __mem_ptr)(static_cast<_Args&&>(__args)...)))
179 -> decltype(((*static_cast<_Ty&&>(__ty)).*
180 __mem_ptr)(static_cast<_Args&&>(__args)...))
181 {
182 return ((*static_cast<_Ty&&>(__ty)).*
183 __mem_ptr)(static_cast<_Args&&>(__args)...);
184 }
185 };
186
187 struct __memobj
188 {
189 template <class _Mbr, class _Class, class _Ty>
190 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__memobj191 constexpr auto operator()(_Mbr _Class::*__mem_ptr,
192 _Ty&& __ty) const noexcept
193 -> decltype(((static_cast<_Ty&&>(__ty)).*__mem_ptr))
194 {
195 return ((static_cast<_Ty&&>(__ty)).*__mem_ptr);
196 }
197 };
198
199 struct __memobj_refwrap
200 {
201 template <class _Mbr, class _Class, class _Ty>
202 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__memobj_refwrap203 constexpr auto operator()(_Mbr _Class::*__mem_ptr, _Ty __ty) const noexcept
204 -> decltype((__ty.get().*__mem_ptr))
205 {
206 return (__ty.get().*__mem_ptr);
207 }
208 };
209
210 struct __memobj_smartptr
211 {
212 template <class _Mbr, class _Class, class _Ty>
213 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__memobj_smartptr214 constexpr auto operator()(_Mbr _Class::*__mem_ptr,
215 _Ty&& __ty) const noexcept
216 -> decltype(((*static_cast<_Ty&&>(__ty)).*__mem_ptr))
217 {
218 return ((*static_cast<_Ty&&>(__ty)).*__mem_ptr);
219 }
220 };
221
222 auto __invoke_selector(__ignore, __ignore) noexcept -> __funobj;
223
224 template <class _Mbr, class _Class, class _Ty>
__invoke_selector(_Mbr _Class::*,const _Ty &)225 auto __invoke_selector(_Mbr _Class::*, const _Ty&) noexcept
226 {
227 if constexpr (STDEXEC_IS_CONST(_Mbr) || STDEXEC_IS_CONST(const _Mbr))
228 {
229 // member function ptr case
230 if constexpr (STDEXEC_IS_BASE_OF(_Class, _Ty))
231 {
232 return __memobj{};
233 }
234 else if constexpr (__is_refwrap<_Ty>)
235 {
236 return __memobj_refwrap{};
237 }
238 else
239 {
240 return __memobj_smartptr{};
241 }
242 }
243 else
244 {
245 // member object ptr case
246 if constexpr (STDEXEC_IS_BASE_OF(_Class, _Ty))
247 {
248 return __memfn{};
249 }
250 else if constexpr (__is_refwrap<_Ty>)
251 {
252 return __memfn_refwrap{};
253 }
254 else
255 {
256 return __memfn_smartptr{};
257 }
258 }
259 }
260
261 struct __invoke_t
262 {
263 template <class _Fun>
264 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__invoke_t265 constexpr auto operator()(_Fun&& __fun) const
266 noexcept(noexcept((static_cast<_Fun&&>(__fun))()))
267 -> decltype((static_cast<_Fun&&>(__fun))())
268 {
269 return static_cast<_Fun&&>(__fun)();
270 }
271
272 template <class _Fun, class _Ty, class... _Args>
273 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__invoke_::__invoke_t274 constexpr auto operator()(_Fun&& __fun, _Ty&& __ty, _Args&&... __args) const
275 noexcept(noexcept(__invoke_selector(__fun, __ty)(
276 static_cast<_Fun&&>(__fun), static_cast<_Ty&&>(__ty),
277 static_cast<_Args&&>(__args)...)))
278 -> decltype(__invoke_selector(__fun, __ty)(
279 static_cast<_Fun&&>(__fun), static_cast<_Ty&&>(__ty),
280 static_cast<_Args&&>(__args)...))
281 {
282 return decltype(__invoke_selector(__fun, __ty))()(
283 static_cast<_Fun&&>(__fun), static_cast<_Ty&&>(__ty),
284 static_cast<_Args&&>(__args)...);
285 }
286 };
287 } // namespace __invoke_
288
289 inline constexpr __invoke_::__invoke_t __invoke{};
290
291 template <class _Fun, class... _As>
292 concept __invocable = //
293 requires(_Fun&& __f, _As&&... __as) {
294 __invoke(static_cast<_Fun&&>(__f), static_cast<_As&&>(__as)...);
295 };
296
297 template <class _Fun, class... _As>
298 concept __nothrow_invocable = //
299 __invocable<_Fun, _As...> && //
300 requires(_Fun&& __f, _As&&... __as) {
301 {
302 __invoke(static_cast<_Fun&&>(__f), static_cast<_As&&>(__as)...)
303 } noexcept;
304 };
305
306 template <class _Fun, class... _As>
307 using __invoke_result_t = //
308 decltype(__invoke(__declval<_Fun>(), __declval<_As>()...));
309
310 namespace __apply_
311 {
312 using std::get;
313
314 template <std::size_t... _Is, class _Fn, class _Tup>
315 STDEXEC_ATTRIBUTE((always_inline))
__impl(__indices<_Is...>,_Fn && __fn,_Tup && __tup)316 constexpr auto __impl(__indices<_Is...>, _Fn&& __fn, _Tup&& __tup) noexcept(
317 noexcept(__invoke(static_cast<_Fn&&>(__fn),
318 get<_Is>(static_cast<_Tup&&>(__tup))...)))
319 -> decltype(__invoke(static_cast<_Fn&&>(__fn),
320 get<_Is>(static_cast<_Tup&&>(__tup))...))
321 {
322 return __invoke(static_cast<_Fn&&>(__fn),
323 get<_Is>(static_cast<_Tup&&>(__tup))...);
324 }
325
326 template <class _Tup>
327 using __tuple_indices =
328 __make_indices<std::tuple_size<std::remove_cvref_t<_Tup>>::value>;
329
330 template <class _Fn, class _Tup>
331 using __result_t = decltype(__apply_::__impl(
332 __tuple_indices<_Tup>(), __declval<_Fn>(), __declval<_Tup>()));
333 } // namespace __apply_
334
335 template <class _Fn, class _Tup>
336 concept __applicable = __mvalid<__apply_::__result_t, _Fn, _Tup>;
337
338 template <class _Fn, class _Tup>
339 concept __nothrow_applicable = __applicable<_Fn, _Tup> //
340 && noexcept(__apply_::__impl(__apply_::__tuple_indices<_Tup>(),
341 __declval<_Fn>(), __declval<_Tup>()));
342
343 template <class _Fn, class _Tup>
344 requires __applicable<_Fn, _Tup>
345 using __apply_result_t = __apply_::__result_t<_Fn, _Tup>;
346
347 struct __apply_t
348 {
349 template <class _Fn, class _Tup>
350 requires __applicable<_Fn, _Tup>
351 STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
operator ()stdexec::__apply_t352 operator()(_Fn&& __fn, _Tup&& __tup) const
353 noexcept(__nothrow_applicable<_Fn, _Tup>) -> __apply_result_t<_Fn, _Tup>
354 {
355 return __apply_::__impl(__apply_::__tuple_indices<_Tup>(),
356 static_cast<_Fn&&>(__fn),
357 static_cast<_Tup&&>(__tup));
358 }
359 };
360
361 inline constexpr __apply_t __apply{};
362
363 template <class _Tag, class _Ty>
364 struct __field
365 {
366 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__field367 _Ty operator()(_Tag) const noexcept(__nothrow_decay_copyable<const _Ty&>)
368 {
369 return __t_;
370 }
371
372 _Ty __t_;
373 };
374
375 template <class _Tag>
376 struct __mkfield_
377 {
378 template <class _Ty>
379 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__mkfield_380 __field<_Tag, __decay_t<_Ty>> operator()(_Ty&& __ty) const
381 noexcept(__nothrow_decay_copyable<_Ty>)
382 {
383 return {static_cast<_Ty&&>(__ty)};
384 }
385 };
386
387 template <class _Tag>
388 inline constexpr __mkfield_<_Tag> __mkfield{};
389
390 // [func.tag_invoke], tag_invoke
391 namespace __tag_invoke
392 {
393 void tag_invoke();
394
395 // NOT TO SPEC: Don't require tag_invocable to subsume invocable.
396 // std::invoke is more expensive at compile time than necessary,
397 // and results in diagnostics that are more verbose than necessary.
398 template <class _Tag, class... _Args>
399 concept tag_invocable = //
400 requires(_Tag __tag, _Args&&... __args) {
401 tag_invoke(static_cast<_Tag&&>(__tag), static_cast<_Args&&>(__args)...);
402 };
403
404 template <class _Ret, class _Tag, class... _Args>
405 concept __tag_invocable_r = //
406 requires(_Tag __tag, _Args&&... __args) {
407 {
408 static_cast<_Ret>(tag_invoke(static_cast<_Tag&&>(__tag),
409 static_cast<_Args&&>(__args)...))
410 };
411 };
412
413 // NOT TO SPEC: nothrow_tag_invocable subsumes tag_invocable
414 template <class _Tag, class... _Args>
415 concept nothrow_tag_invocable =
416 tag_invocable<_Tag, _Args...> && //
417 requires(_Tag __tag, _Args&&... __args) {
418 {
419 tag_invoke(static_cast<_Tag&&>(__tag),
420 static_cast<_Args&&>(__args)...)
421 } noexcept;
422 };
423
424 template <class _Tag, class... _Args>
425 using tag_invoke_result_t =
426 decltype(tag_invoke(__declval<_Tag>(), __declval<_Args>()...));
427
428 template <class _Tag, class... _Args>
429 struct tag_invoke_result
430 {};
431
432 template <class _Tag, class... _Args>
433 requires tag_invocable<_Tag, _Args...>
434 struct tag_invoke_result<_Tag, _Args...>
435 {
436 using type = tag_invoke_result_t<_Tag, _Args...>;
437 };
438
439 struct tag_invoke_t
440 {
441 template <class _Tag, class... _Args>
442 requires tag_invocable<_Tag, _Args...>
443 STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
operator ()stdexec::__tag_invoke::tag_invoke_t444 operator()(_Tag __tag, _Args&&... __args) const
445 noexcept(nothrow_tag_invocable<_Tag, _Args...>)
446 -> tag_invoke_result_t<_Tag, _Args...>
447 {
448 return tag_invoke(static_cast<_Tag&&>(__tag),
449 static_cast<_Args&&>(__args)...);
450 }
451 };
452
453 } // namespace __tag_invoke
454
455 using __tag_invoke::tag_invoke_t;
456
457 namespace __ti
458 {
459 inline constexpr tag_invoke_t tag_invoke{};
460 } // namespace __ti
461
462 using namespace __ti;
463
464 template <auto& _Tag>
465 using tag_t = __decay_t<decltype(_Tag)>;
466
467 using __tag_invoke::__tag_invocable_r;
468 using __tag_invoke::nothrow_tag_invocable;
469 using __tag_invoke::tag_invocable;
470 using __tag_invoke::tag_invoke_result;
471 using __tag_invoke::tag_invoke_result_t;
472 } // namespace stdexec
473