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... _Us>
__convert_fromstdexec::__tup::__tuple62 static __tuple __convert_from(__tuple<_Idx, _Us...>&& __tup)
63 {
64 return __tuple{{static_cast<_Us&&>(__tup.__box<_Us, _Is>::__value)}...};
65 }
66
67 template <class... _Us>
__convert_fromstdexec::__tup::__tuple68 static __tuple __convert_from(const __tuple<_Idx, _Us...>& __tup)
69 {
70 return __tuple{{__tup.__box<_Us, _Is>::__value}...};
71 }
72
73 template <class _Fn, class _Self, class... _Us>
74 STDEXEC_ATTRIBUTE((host, device, always_inline))
applystdexec::__tup::__tuple75 static auto apply(_Fn&& __fn, _Self&& __self, _Us&&... __us) //
76 noexcept(noexcept(static_cast<_Fn&&>(__fn)(
77 static_cast<_Us&&>(__us)...,
78 static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...)))
79 -> decltype(static_cast<_Fn&&>(__fn)(
80 static_cast<_Us&&>(__us)...,
81 static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...))
82 {
83 return static_cast<_Fn&&>(
84 __fn)(static_cast<_Us&&>(__us)...,
85 static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...);
86 }
87
88 template <class _Fn, class _Self, class... _Us>
89 requires(__callable<_Fn, _Us..., __copy_cvref_t<_Self, _Ts>> && ...)
90 STDEXEC_ATTRIBUTE((host, device, always_inline))
for_eachstdexec::__tup::__tuple91 static auto for_each(_Fn&& __fn, _Self&& __self, _Us&&... __us) //
92 noexcept((__nothrow_callable<_Fn, _Us..., __copy_cvref_t<_Self, _Ts>> &&
93 ...)) -> void
94 {
95 return (static_cast<_Fn&&>(__fn)(
96 static_cast<_Us&&>(__us)...,
97 static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value),
98 ...);
99 }
100 };
101
102 template <class... _Ts>
103 STDEXEC_ATTRIBUTE((host, device))
__tuple(_Ts...)104 __tuple(_Ts...) -> __tuple<__indices_for<_Ts...>{}, _Ts...>;
105
106 template <class _Fn, class _Tuple, class... _Us>
107 using __apply_result_t = //
108 decltype(__declval<_Tuple>().apply(__declval<_Fn>(), __declval<_Tuple>(),
109 __declval<_Us>()...));
110
111 template <class _Fn, class _Tuple, class... _Us>
112 concept __applicable =
113 requires { typename __apply_result_t<_Fn, _Tuple, _Us...>; };
114
115 template <class _Fn, class _Tuple, class... _Us>
116 concept __nothrow_applicable =
117 __applicable<_Fn, _Tuple, _Us...> &&
118 noexcept(__declval<_Tuple>().apply(__declval<_Fn>(), __declval<_Tuple>(),
119 __declval<_Us>()...));
120
121 #if STDEXEC_GCC()
122 template <class... _Ts>
123 struct __mk_tuple
124 {
125 using __t = __tuple<__indices_for<_Ts...>{}, _Ts...>;
126 };
127 #endif
128
129 template <std::size_t _Idx, class _Ty>
130 STDEXEC_ATTRIBUTE((host, device, always_inline))
get(__box<_Ty,_Idx> && __self)131 constexpr _Ty&& get(__box<_Ty, _Idx>&& __self) noexcept
132 {
133 return static_cast<_Ty&&>(__self.__value);
134 }
135
136 template <std::size_t _Idx, class _Ty>
137 STDEXEC_ATTRIBUTE((host, device, always_inline))
get(__box<_Ty,_Idx> & __self)138 constexpr _Ty& get(__box<_Ty, _Idx>& __self) noexcept
139 {
140 return __self.__value;
141 }
142
143 template <std::size_t _Idx, class _Ty>
144 STDEXEC_ATTRIBUTE((host, device, always_inline))
get(const __box<_Ty,_Idx> & __self)145 constexpr const _Ty& get(const __box<_Ty, _Idx>& __self) noexcept
146 {
147 return __self.__value;
148 }
149
150 template <class _Fn, class _Tuple>
151 STDEXEC_ATTRIBUTE((host, device, always_inline))
operator <<(_Tuple && __tup,_Fn __fn)152 auto operator<<(_Tuple&& __tup,
153 _Fn __fn) noexcept(__nothrow_move_constructible<_Fn>)
154 {
155 return [&__tup, __fn]<class... _Us>(_Us&&... __us) //
156 noexcept(__nothrow_applicable<_Fn, _Tuple, _Us...>)
157 -> __apply_result_t<_Fn, _Tuple, _Us...> {
158 return __tup.apply(__fn, static_cast<_Tuple&&>(__tup),
159 static_cast<_Us&&>(__us)...);
160 };
161 }
162
163 template <class _Fn, class... _Tuples>
__cat_apply(_Fn __fn,_Tuples &&...__tups)164 auto __cat_apply(_Fn __fn, _Tuples&&... __tups) //
165 noexcept(noexcept((static_cast<_Tuples&&>(__tups) << ... << __fn)())) //
166 -> decltype((static_cast<_Tuples&&>(__tups) << ... << __fn)())
167 {
168 return (static_cast<_Tuples&&>(__tups) << ... << __fn)();
169 }
170
171 STDEXEC_PRAGMA_PUSH()
172 STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
173
174 inline constexpr struct __mktuple_t
175 {
176 template <class... _Ts>
177 STDEXEC_ATTRIBUTE((host, device, always_inline))
operator ()stdexec::__tup::__mktuple_t178 auto operator()(_Ts&&... __ts) const
179 noexcept(noexcept(__tuple{static_cast<_Ts&&>(__ts)...}))
180 -> decltype(__tuple{static_cast<_Ts&&>(__ts)...})
181 {
182 return __tuple{static_cast<_Ts&&>(__ts)...};
183 }
184 } __mktuple{};
185
186 STDEXEC_PRAGMA_POP()
187
188 } // namespace __tup
189
190 using __tup::__tuple;
191
192 #if STDEXEC_GCC()
193 template <class... _Ts>
194 using __tuple_for = __t<__tup::__mk_tuple<_Ts...>>;
195 #else
196 template <class... _Ts>
197 using __tuple_for = __tuple<__indices_for<_Ts...>{}, _Ts...>;
198 #endif
199
200 template <class... _Ts>
201 using __decayed_tuple = __tuple_for<__decay_t<_Ts>...>;
202
203 // So we can use __tuple as a typelist
204 template <auto _Idx, class... _Ts>
205 struct __muncurry_<__tuple<_Idx, _Ts...>>
206 {
207 template <class _Fn>
208 using __f = __minvoke<_Fn, _Ts...>;
209 };
210 } // namespace stdexec
211