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