xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__tuple.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
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 "__config.hpp"
19 #include "__concepts.hpp"
20 #include "__type_traits.hpp"
21 #include "__meta.hpp"
22 
23 #include <cstddef>
24 
25 #if STDEXEC_GCC() || STDEXEC_NVHPC()
26 // GCC (as of v14) does not implement the resolution of CWG1835
27 // https://cplusplus.github.io/CWG/issues/1835.html
28 // See: https://godbolt.org/z/TzxrhK6ea
29 #  define STDEXEC_NO_CWG1835
30 #endif
31 
32 #ifdef STDEXEC_NO_CWG1835
33 #  define STDEXEC_CWG1835_TEMPLATE
34 #else
35 #  define STDEXEC_CWG1835_TEMPLATE template
36 #endif
37 
38 namespace stdexec {
39   namespace __tup {
40     template <class _Ty, std::size_t _Idx>
41     struct __box {
42       STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
43       _Ty __value;
44     };
45 
46     template <class _Ty>
47     concept __empty = STDEXEC_IS_EMPTY(_Ty) && STDEXEC_IS_TRIVIALLY_CONSTRUCTIBLE(_Ty)
48                    && STDEXEC_IS_TRIVIALLY_COPYABLE(_Ty);
49 
50     template <__empty _Ty>
51     inline _Ty __value{};
52 
53     // A specialization for empty types so that they don't take up space.
54     template <__empty _Ty, std::size_t _Idx>
55     struct __box<_Ty, _Idx> {
56       __box() = default;
57 
__boxstdexec::__tup::__box58       constexpr __box(__not_decays_to<__box> auto &&) noexcept {
59       }
60 
61       static constexpr _Ty &__value = __tup::__value<_Ty>;
62     };
63 
64     template <std::size_t _Idx, class _Ty>
STDEXEC_ATTRIBUTE(host,device,always_inline)65     STDEXEC_ATTRIBUTE(host, device, always_inline)
66     constexpr auto __get(__box<_Ty, _Idx> &&__self) noexcept -> _Ty && {
67       return static_cast<_Ty &&>(__self.__value);
68     }
69 
70     template <std::size_t _Idx, class _Ty>
STDEXEC_ATTRIBUTE(host,device,always_inline)71     STDEXEC_ATTRIBUTE(host, device, always_inline)
72     constexpr auto __get(__box<_Ty, _Idx> &__self) noexcept -> _Ty & {
73       return __self.__value;
74     }
75 
76     template <std::size_t _Idx, class _Ty>
STDEXEC_ATTRIBUTE(host,device,always_inline)77     STDEXEC_ATTRIBUTE(host, device, always_inline)
78     constexpr auto __get(const __box<_Ty, _Idx> &__self) noexcept -> const _Ty & {
79       return __self.__value;
80     }
81 
82     template <auto _Idx, class... _Ts>
83     struct __tuple;
84 
85     template <std::size_t... _Is, __indices<_Is...> _Idx, class... _Ts>
86       requires(sizeof...(_Ts) - 1 > 3) // intentional unsigned wrap-around for sizeof...(Ts) is zero
87     struct __tuple<_Idx, _Ts...> : __box<_Ts, _Is>... {
88       template <class... _Us>
__convert_fromstdexec::__tup::__tuple89       static constexpr auto __convert_from(__tuple<_Idx, _Us...> &&__tup) -> __tuple {
90         return __tuple{
91           {static_cast<_Us &&>(__tup.STDEXEC_CWG1835_TEMPLATE __box<_Us, _Is>::__value)}...};
92       }
93 
94       template <class... _Us>
__convert_fromstdexec::__tup::__tuple95       static constexpr auto __convert_from(__tuple<_Idx, _Us...> const &__tup) -> __tuple {
96         return __tuple{{__tup.STDEXEC_CWG1835_TEMPLATE __box<_Us, _Is>::__value}...};
97       }
98 
99       template <std::size_t _Np, class _Self>
STDEXEC_ATTRIBUTEstdexec::__tup::__tuple100       STDEXEC_ATTRIBUTE(host, device, always_inline)
101       static constexpr auto __get(_Self &&__self) noexcept
102         -> decltype(__tup::__get<_Np>(static_cast<_Self &&>(__self))) {
103         return __tup::__get<_Np>(static_cast<_Self &&>(__self));
104       }
105 
106       // clang-format off
107       template <class _Fn, class _Self, class... _Us>
108       STDEXEC_ATTRIBUTE(host, device, always_inline) static constexpr auto apply(_Fn &&__fn, _Self &&__self, _Us &&...__us)
109         STDEXEC_AUTO_RETURN(
110           static_cast<_Fn &&>(__fn)(
111             static_cast<_Us &&>(__us)...,
112             static_cast<_Self &&>(__self).STDEXEC_CWG1835_TEMPLATE __box<_Ts, _Is>::__value...))
113 
114       template <class _Fn, class _Self, class... _Us>
115       STDEXEC_ATTRIBUTE(host, device, always_inline) static constexpr auto for_each(_Fn &&__fn, _Self &&__self)
116         STDEXEC_AUTO_RETURN(
117           (static_cast<void>(
118              __fn(static_cast<_Self &&>(__self).STDEXEC_CWG1835_TEMPLATE __box<_Ts, _Is>::__value)),
119            ...))
120       // clang-format on
121     };
122 
123     // unroll the tuple implementation for up to 4 elements
124 
125 #define STDEXEC_TPARAM_DEFN(_N)             , class _T##_N
126 #define STDEXEC_TPARAM_USE(_N)              , _T##_N
127 #define STDEXEC_TPARAM_OTHER_DEFN(_N)       , class _U##_N
128 #define STDEXEC_TPARAM_OTHER_USE(_N)        , _U##_N
129 #define STDEXEC_TUPLE_ELEM_DEFN(_N)         _T##_N __elem##_N;
130 #define STDEXEC_TUPLE_ELEM_USE(_N)          , static_cast<_Self &&>(__self).__elem##_N
131 #define STDEXEC_TUPLE_OTHER_ELEM_RVALUE(_N) , static_cast<_U##_N &&>(__tup.__elem##_N)
132 #define STDEXEC_TUPLE_OTHER_ELEM_LVALUE(_N) , __tup.__elem##_N
133 #define STDEXEC_TUPLE_FOR_EACH_ELEM(_N)                                                            \
134   , static_cast<void>(static_cast<_Fn &&>(__fn)(__self.__elem##_N))
135 #define STDEXEC_TUPLE_GET_ELEM(_N)                                                                 \
136   if constexpr (_Np == _N)                                                                         \
137     return (static_cast<_Self &&>(__self).__elem##_N);                                             \
138   else
139 
140     // clang-format off
141 #define STDEXEC_TUPLE_DEFN(_N)                                                                     \
142   template <std::size_t... _Is, __indices<_Is...> _Idx STDEXEC_REPEAT(_N, STDEXEC_TPARAM_DEFN)>    \
143   struct __tuple<_Idx STDEXEC_REPEAT(_N, STDEXEC_TPARAM_USE)> {                                    \
144     STDEXEC_REPEAT(_N, STDEXEC_TUPLE_ELEM_DEFN)                                                    \
145                                                                                                    \
146     template <STDEXEC_EVAL(STDEXEC_TAIL, STDEXEC_REPEAT(_N, STDEXEC_TPARAM_OTHER_DEFN))>           \
147     static constexpr auto __convert_from(__tuple<_Idx STDEXEC_REPEAT(_N, STDEXEC_TPARAM_OTHER_USE)> &&__tup) \
148       -> __tuple {                                                                                 \
149       return __tuple{                                                                              \
150         STDEXEC_EVAL(STDEXEC_TAIL, STDEXEC_REPEAT(_N, STDEXEC_TUPLE_OTHER_ELEM_RVALUE))};          \
151     }                                                                                              \
152                                                                                                    \
153     template <STDEXEC_EVAL(STDEXEC_TAIL, STDEXEC_REPEAT(_N, STDEXEC_TPARAM_OTHER_DEFN))>           \
154     static constexpr auto __convert_from(                                                                    \
155       __tuple<_Idx STDEXEC_REPEAT(_N, STDEXEC_TPARAM_OTHER_USE)> const &__tup) -> __tuple {        \
156       return __tuple{                                                                              \
157         STDEXEC_EVAL(STDEXEC_TAIL, STDEXEC_REPEAT(_N, STDEXEC_TUPLE_OTHER_ELEM_LVALUE))};          \
158     }                                                                                              \
159                                                                                                    \
160     template <std::size_t _Np, class _Self>                                                        \
161     STDEXEC_ATTRIBUTE(host, device, always_inline)                                               \
162     static constexpr auto __get(_Self &&__self) noexcept -> decltype(auto) requires(_Np < _N) {              \
163       STDEXEC_REPEAT(_N, STDEXEC_TUPLE_GET_ELEM);                                                  \
164     }                                                                                              \
165                                                                                                    \
166     template <class _Fn, class _Self, class... _Us>                                                \
167     STDEXEC_ATTRIBUTE(host, device, always_inline)                                               \
168     static constexpr auto apply(_Fn &&__fn, _Self &&__self, _Us &&...__us) STDEXEC_AUTO_RETURN(              \
169       static_cast<_Fn &&>(__fn)(static_cast<_Us &&>(__us)...                                       \
170         STDEXEC_REPEAT(_N, STDEXEC_TUPLE_ELEM_USE)))                                               \
171                                                                                                    \
172     template <class _Fn, class _Self>                                                              \
173     STDEXEC_ATTRIBUTE(host, device, always_inline)                                               \
174     static constexpr auto for_each(_Fn &&__fn, _Self &&__self) STDEXEC_AUTO_RETURN(                          \
175       STDEXEC_EVAL(STDEXEC_TAIL, STDEXEC_REPEAT(_N, STDEXEC_TUPLE_FOR_EACH_ELEM)))                 \
176   };
177     // clang-format on
178 
179     STDEXEC_TUPLE_DEFN(1)
180     STDEXEC_TUPLE_DEFN(2)
181     STDEXEC_TUPLE_DEFN(3)
182     STDEXEC_TUPLE_DEFN(4)
183 
184     template <class... _Ts>
STDEXEC_ATTRIBUTE(host,device)185     STDEXEC_ATTRIBUTE(host, device)
186     __tuple(_Ts...) -> __tuple<__indices_for<_Ts...>{}, _Ts...>;
187 
188     template <class _Fn, class _Tuple, class... _Us>
189     using __apply_result_t =
190       decltype(__declval<_Tuple>()
191                  .apply(__declval<_Fn>(), __declval<_Tuple>(), __declval<_Us>()...));
192 
193     template <class _Fn, class _Tuple, class... _Us>
194     concept __applicable = requires { typename __apply_result_t<_Fn, _Tuple, _Us...>; };
195 
196     template <class _Fn, class _Tuple, class... _Us>
197     concept __nothrow_applicable =
198       __applicable<_Fn, _Tuple, _Us...>
199       && noexcept(__declval<_Tuple>()
200                     .apply(__declval<_Fn>(), __declval<_Tuple>(), __declval<_Us>()...));
201 
202 #if STDEXEC_GCC()
203     template <class... _Ts>
204     struct __mk_tuple {
205       using __t = __tuple<__indices_for<_Ts...>{}, _Ts...>;
206     };
207 #endif
208 
209     template <class _Fn, class _Tuple>
210     STDEXEC_ATTRIBUTE(host, device, always_inline)
211     constexpr auto
212       operator<<(_Tuple &&__tup, _Fn __fn) noexcept(__nothrow_move_constructible<_Fn>) {
213       return [&__tup, __fn = static_cast<_Fn &&>(__fn)]<class... _Us>(_Us &&...__us) noexcept(
214                __nothrow_applicable<_Fn, _Tuple, _Us...>) -> __apply_result_t<_Fn, _Tuple, _Us...> {
215         return __tup.apply(__fn, static_cast<_Tuple &&>(__tup), static_cast<_Us &&>(__us)...);
216       };
217     }
218 
219     template <class _Fn, class... _Tuples>
220     constexpr auto __cat_apply(_Fn __fn, _Tuples &&...__tups)
221       STDEXEC_AUTO_RETURN((static_cast<_Tuples &&>(__tups) << ... << __fn)())
222 
223         STDEXEC_PRAGMA_PUSH() STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
224 
225           inline constexpr struct __mktuple_t {
226       template <class... _Ts>
STDEXEC_ATTRIBUTEstdexec::__tup::__mktuple_t227       STDEXEC_ATTRIBUTE(host, device, always_inline)
228       auto operator()(_Ts &&...__ts) const noexcept(noexcept(__tuple{static_cast<_Ts &&>(__ts)...}))
229         -> decltype(__tuple{static_cast<_Ts &&>(__ts)...}) {
230         return __tuple{static_cast<_Ts &&>(__ts)...};
231       }
232     } __mktuple{};
233 
234     STDEXEC_PRAGMA_POP()
235   } // namespace __tup
236 
237   using __tup::__tuple;
238 
239 #if STDEXEC_GCC()
240   template <class... _Ts>
241   using __tuple_for = __t<__tup::__mk_tuple<_Ts...>>;
242 #else
243   template <class... _Ts>
244   using __tuple_for = __tuple<__indices_for<_Ts...>{}, _Ts...>;
245 #endif
246 
247   template <class... _Ts>
248   using __decayed_tuple = __tuple_for<__decay_t<_Ts>...>;
249 
250   // So we can use __tuple as a typelist
251   template <auto _Idx, class... _Ts>
252   struct __muncurry_<__tuple<_Idx, _Ts...>> {
253     template <class _Fn>
254     using __f = __minvoke<_Fn, _Ts...>;
255   };
256 } // namespace stdexec
257