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