xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__tuple.hpp (revision f083bc1a64e1f94c99fc270b7c0856810f4be638)
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