1 /*
2  * Copyright (c) 2023 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 "__config.hpp"
20 #include "__meta.hpp"
21 #include "__type_traits.hpp"
22 
23 #include <cstddef>
24 
25 namespace stdexec
26 {
27 namespace __tup
28 {
29 template <class _Ty, std::size_t _Idx>
30 struct __box
31 {
32     // See https://github.com/llvm/llvm-project/issues/93563
33     // STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
34     _Ty __value;
35 };
36 
37 template <class _Ty>
38 concept __empty = //
39     STDEXEC_IS_EMPTY(_Ty) && STDEXEC_IS_TRIVIALLY_CONSTRUCTIBLE(_Ty);
40 
41 template <__empty _Ty>
42 inline _Ty __value{};
43 
44 // A specialization for empty types so that they don't take up space.
45 template <__empty _Ty, std::size_t _Idx>
46 struct __box<_Ty, _Idx>
47 {
48     __box() = default;
49 
__boxstdexec::__tup::__box50     constexpr __box(__not_decays_to<__box> auto&&) noexcept {}
51 
52     static constexpr _Ty& __value = __tup::__value<_Ty>;
53 };
54 
55 template <auto _Idx, class... _Ts>
56 struct __tuple;
57 
58 template <std::size_t... _Is, __indices<_Is...> _Idx, class... _Ts>
59 struct __tuple<_Idx, _Ts...> : __box<_Ts, _Is>...
60 {
61     template <class _Fn, class _Self, class... _Us>
62     STDEXEC_ATTRIBUTE((host, device, always_inline))
applystdexec::__tup::__tuple63     static auto apply(_Fn&& __fn, _Self&& __self, _Us&&... __us) //
64         noexcept(noexcept(static_cast<_Fn&&>(__fn)(
65             static_cast<_Us&&>(__us)...,
66             static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...)))
67             -> decltype(static_cast<_Fn&&>(__fn)(
68                 static_cast<_Us&&>(__us)...,
69                 static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...))
70     {
71         return static_cast<_Fn&&>(
72             __fn)(static_cast<_Us&&>(__us)...,
73                   static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...);
74     }
75 
76     template <class _Fn, class _Self, class... _Us>
77         requires(__callable<_Fn, _Us..., __copy_cvref_t<_Self, _Ts>> && ...)
78     STDEXEC_ATTRIBUTE((host, device, always_inline))
for_eachstdexec::__tup::__tuple79     static auto for_each(_Fn&& __fn, _Self&& __self, _Us&&... __us) //
80         noexcept((__nothrow_callable<_Fn, _Us..., __copy_cvref_t<_Self, _Ts>> &&
81                   ...)) -> void
82     {
83         return (static_cast<_Fn&&>(__fn)(
84                     static_cast<_Us&&>(__us)...,
85                     static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value),
86                 ...);
87     }
88 };
89 
90 template <class... _Ts>
91 STDEXEC_ATTRIBUTE((host, device))
__tuple(_Ts...)92 __tuple(_Ts...) -> __tuple<__indices_for<_Ts...>{}, _Ts...>;
93 
94 template <class _Fn, class _Tuple, class... _Us>
95 using __apply_result_t = //
96     decltype(__declval<_Tuple>().apply(__declval<_Fn>(), __declval<_Tuple>(),
97                                        __declval<_Us>()...));
98 
99 template <class _Fn, class _Tuple, class... _Us>
100 concept __applicable =
101     requires { typename __apply_result_t<_Fn, _Tuple, _Us...>; };
102 
103 template <class _Fn, class _Tuple, class... _Us>
104 concept __nothrow_applicable =
105     __applicable<_Fn, _Tuple, _Us...> &&
106     noexcept(__declval<_Tuple>().apply(__declval<_Fn>(), __declval<_Tuple>(),
107                                        __declval<_Us>()...));
108 
109 #if STDEXEC_GCC()
110 template <class... _Ts>
111 struct __mk_tuple
112 {
113     using __t = __tuple<__indices_for<_Ts...>{}, _Ts...>;
114 };
115 #endif
116 
117 template <std::size_t _Idx, class _Ty>
118 STDEXEC_ATTRIBUTE((host, device, always_inline))
get(__box<_Ty,_Idx> && __self)119 constexpr _Ty&& get(__box<_Ty, _Idx>&& __self) noexcept
120 {
121     return static_cast<_Ty&&>(__self.__value);
122 }
123 
124 template <std::size_t _Idx, class _Ty>
125 STDEXEC_ATTRIBUTE((host, device, always_inline))
get(__box<_Ty,_Idx> & __self)126 constexpr _Ty& get(__box<_Ty, _Idx>& __self) noexcept
127 {
128     return __self.__value;
129 }
130 
131 template <std::size_t _Idx, class _Ty>
132 STDEXEC_ATTRIBUTE((host, device, always_inline))
get(const __box<_Ty,_Idx> & __self)133 constexpr const _Ty& get(const __box<_Ty, _Idx>& __self) noexcept
134 {
135     return __self.__value;
136 }
137 
138 template <class _Fn, class _Tuple>
139 STDEXEC_ATTRIBUTE((host, device, always_inline))
operator <<(_Tuple && __tup,_Fn __fn)140 auto operator<<(_Tuple&& __tup,
141                 _Fn __fn) noexcept(__nothrow_move_constructible<_Fn>)
142 {
143     return [&__tup, __fn]<class... _Us>(_Us&&... __us) //
144            noexcept(__nothrow_applicable<_Fn, _Tuple, _Us...>)
145                -> __apply_result_t<_Fn, _Tuple, _Us...> {
146                return __tup.apply(__fn, static_cast<_Tuple&&>(__tup),
147                                   static_cast<_Us&&>(__us)...);
148            };
149 }
150 
151 template <class _Fn, class... _Tuples>
__cat_apply(_Fn __fn,_Tuples &&...__tups)152 auto __cat_apply(_Fn __fn, _Tuples&&... __tups)                           //
153     noexcept(noexcept((static_cast<_Tuples&&>(__tups) << ... << __fn)())) //
154     -> decltype((static_cast<_Tuples&&>(__tups) << ... << __fn)())
155 {
156     return (static_cast<_Tuples&&>(__tups) << ... << __fn)();
157 }
158 
159 STDEXEC_PRAGMA_PUSH()
160 STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
161 
162 inline constexpr struct __mktuple_t
163 {
164     template <class... _Ts>
165     STDEXEC_ATTRIBUTE((host, device, always_inline))
operator ()stdexec::__tup::__mktuple_t166     auto operator()(_Ts&&... __ts) const
167         noexcept(noexcept(__tuple{static_cast<_Ts&&>(__ts)...}))
168             -> decltype(__tuple{static_cast<_Ts&&>(__ts)...})
169     {
170         return __tuple{static_cast<_Ts&&>(__ts)...};
171     }
172 } __mktuple{};
173 
174 STDEXEC_PRAGMA_POP()
175 
176 } // namespace __tup
177 
178 using __tup::__tuple;
179 
180 #if STDEXEC_GCC()
181 template <class... _Ts>
182 using __tuple_for = __t<__tup::__mk_tuple<_Ts...>>;
183 #else
184 template <class... _Ts>
185 using __tuple_for = __tuple<__indices_for<_Ts...>{}, _Ts...>;
186 #endif
187 
188 template <class... _Ts>
189 using __decayed_tuple = __tuple_for<__decay_t<_Ts>...>;
190 
191 // So we can use __tuple as a typelist
192 template <auto _Idx, class... _Ts>
193 struct __muncurry_<__tuple<_Idx, _Ts...>>
194 {
195     template <class _Fn>
196     using __f = __minvoke<_Fn, _Ts...>;
197 };
198 } // namespace stdexec
199