| 1 | /* |
| 2 | Copyright (c) 2005-2021 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 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 | |
| 17 | #ifndef __TBB_detail__template_helpers_H |
| 18 | #define __TBB_detail__template_helpers_H |
| 19 | |
| 20 | #include "_utils.h" |
| 21 | #include "_config.h" |
| 22 | |
| 23 | #include <cstddef> |
| 24 | #include <cstdint> |
| 25 | #include <utility> |
| 26 | #include <type_traits> |
| 27 | #include <memory> |
| 28 | #include <iterator> |
| 29 | |
| 30 | namespace tbb { |
| 31 | namespace detail { |
| 32 | inline namespace d0 { |
| 33 | |
| 34 | // An internal implementation of void_t, which can be used in SFINAE contexts |
| 35 | template <typename...> |
| 36 | struct void_impl { |
| 37 | using type = void; |
| 38 | }; // struct void_impl |
| 39 | |
| 40 | template <typename... Args> |
| 41 | using void_t = typename void_impl<Args...>::type; |
| 42 | |
| 43 | // Generic SFINAE helper for expression checks, based on the idea demonstrated in ISO C++ paper n4502 |
| 44 | template <typename T, typename, template <typename> class... Checks> |
| 45 | struct supports_impl { |
| 46 | using type = std::false_type; |
| 47 | }; |
| 48 | |
| 49 | template <typename T, template <typename> class... Checks> |
| 50 | struct supports_impl<T, void_t<Checks<T>...>, Checks...> { |
| 51 | using type = std::true_type; |
| 52 | }; |
| 53 | |
| 54 | template <typename T, template <typename> class... Checks> |
| 55 | using supports = typename supports_impl<T, void, Checks...>::type; |
| 56 | |
| 57 | //! A template to select either 32-bit or 64-bit constant as compile time, depending on machine word size. |
| 58 | template <unsigned u, unsigned long long ull > |
| 59 | struct select_size_t_constant { |
| 60 | // Explicit cast is needed to avoid compiler warnings about possible truncation. |
| 61 | // The value of the right size, which is selected by ?:, is anyway not truncated or promoted. |
| 62 | static const std::size_t value = (std::size_t)((sizeof(std::size_t)==sizeof(u)) ? u : ull); |
| 63 | }; |
| 64 | |
| 65 | // TODO: do we really need it? |
| 66 | //! Cast between unrelated pointer types. |
| 67 | /** This method should be used sparingly as a last resort for dealing with |
| 68 | situations that inherently break strict ISO C++ aliasing rules. */ |
| 69 | // T is a pointer type because it will be explicitly provided by the programmer as a template argument; |
| 70 | // U is a referent type to enable the compiler to check that "ptr" is a pointer, deducing U in the process. |
| 71 | template<typename T, typename U> |
| 72 | inline T punned_cast( U* ptr ) { |
| 73 | std::uintptr_t x = reinterpret_cast<std::uintptr_t>(ptr); |
| 74 | return reinterpret_cast<T>(x); |
| 75 | } |
| 76 | |
| 77 | template<class T, size_t S, size_t R> |
| 78 | struct padded_base : T { |
| 79 | char pad[S - R]; |
| 80 | }; |
| 81 | template<class T, size_t S> struct padded_base<T, S, 0> : T {}; |
| 82 | |
| 83 | //! Pads type T to fill out to a multiple of cache line size. |
| 84 | template<class T, size_t S = max_nfs_size> |
| 85 | struct padded : padded_base<T, S, sizeof(T) % S> {}; |
| 86 | |
| 87 | #if __TBB_CPP14_INTEGER_SEQUENCE_PRESENT |
| 88 | |
| 89 | using std::index_sequence; |
| 90 | using std::make_index_sequence; |
| 91 | |
| 92 | #else |
| 93 | |
| 94 | template<std::size_t... S> class index_sequence {}; |
| 95 | |
| 96 | template<std::size_t N, std::size_t... S> |
| 97 | struct make_index_sequence_impl : make_index_sequence_impl < N - 1, N - 1, S... > {}; |
| 98 | |
| 99 | template<std::size_t... S> |
| 100 | struct make_index_sequence_impl <0, S...> { |
| 101 | using type = index_sequence<S...>; |
| 102 | }; |
| 103 | |
| 104 | template<std::size_t N> |
| 105 | using make_index_sequence = typename make_index_sequence_impl<N>::type; |
| 106 | |
| 107 | #endif /* __TBB_CPP14_INTEGER_SEQUENCE_PRESENT */ |
| 108 | |
| 109 | #if __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT |
| 110 | using std::conjunction; |
| 111 | using std::disjunction; |
| 112 | #else // __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT |
| 113 | |
| 114 | template <typename...> |
| 115 | struct conjunction : std::true_type {}; |
| 116 | |
| 117 | template <typename First, typename... Args> |
| 118 | struct conjunction<First, Args...> |
| 119 | : std::conditional<bool(First::value), conjunction<Args...>, First>::type {}; |
| 120 | |
| 121 | template <typename T> |
| 122 | struct conjunction<T> : T {}; |
| 123 | |
| 124 | template <typename...> |
| 125 | struct disjunction : std::false_type {}; |
| 126 | |
| 127 | template <typename First, typename... Args> |
| 128 | struct disjunction<First, Args...> |
| 129 | : std::conditional<bool(First::value), First, disjunction<Args...>>::type {}; |
| 130 | |
| 131 | template <typename T> |
| 132 | struct disjunction<T> : T {}; |
| 133 | |
| 134 | #endif // __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT |
| 135 | |
| 136 | template <typename Iterator> |
| 137 | using iterator_value_t = typename std::iterator_traits<Iterator>::value_type; |
| 138 | |
| 139 | template <typename Iterator> |
| 140 | using iterator_key_t = typename std::remove_const<typename iterator_value_t<Iterator>::first_type>::type; |
| 141 | |
| 142 | template <typename Iterator> |
| 143 | using iterator_mapped_t = typename iterator_value_t<Iterator>::second_type; |
| 144 | |
| 145 | template <typename Iterator> |
| 146 | using iterator_alloc_pair_t = std::pair<typename std::add_const<iterator_key_t<Iterator>>::type, |
| 147 | iterator_mapped_t<Iterator>>; |
| 148 | |
| 149 | template <typename A> using alloc_value_type = typename A::value_type; |
| 150 | template <typename A> using alloc_ptr_t = typename std::allocator_traits<A>::pointer; |
| 151 | template <typename A> using has_allocate = decltype(std::declval<alloc_ptr_t<A>&>() = std::declval<A>().allocate(0)); |
| 152 | template <typename A> using has_deallocate = decltype(std::declval<A>().deallocate(std::declval<alloc_ptr_t<A>>(), 0)); |
| 153 | |
| 154 | // alloc_value_type should be checked first, because it can be used in other checks |
| 155 | template <typename T> |
| 156 | using is_allocator = supports<T, alloc_value_type, has_allocate, has_deallocate>; |
| 157 | |
| 158 | #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT |
| 159 | template <typename T> |
| 160 | inline constexpr bool is_allocator_v = is_allocator<T>::value; |
| 161 | #endif |
| 162 | |
| 163 | // Template class in which the "type" determines the type of the element number N in pack Args |
| 164 | template <std::size_t N, typename... Args> |
| 165 | struct pack_element { |
| 166 | using type = void; |
| 167 | }; |
| 168 | |
| 169 | template <std::size_t N, typename T, typename... Args> |
| 170 | struct pack_element<N, T, Args...> { |
| 171 | using type = typename pack_element<N-1, Args...>::type; |
| 172 | }; |
| 173 | |
| 174 | template <typename T, typename... Args> |
| 175 | struct pack_element<0, T, Args...> { |
| 176 | using type = T; |
| 177 | }; |
| 178 | |
| 179 | template <std::size_t N, typename... Args> |
| 180 | using pack_element_t = typename pack_element<N, Args...>::type; |
| 181 | |
| 182 | template <typename Func> |
| 183 | class raii_guard { |
| 184 | public: |
| 185 | static_assert( |
| 186 | std::is_nothrow_copy_constructible<Func>::value && |
| 187 | std::is_nothrow_move_constructible<Func>::value, |
| 188 | "Throwing an exception during the Func copy or move construction cause an unexpected behavior." |
| 189 | ); |
| 190 | |
| 191 | raii_guard( Func f ) noexcept : my_func(f), is_active(true) {} |
| 192 | |
| 193 | raii_guard( raii_guard&& g ) noexcept : my_func(std::move(g.my_func)), is_active(g.is_active) { |
| 194 | g.is_active = false; |
| 195 | } |
| 196 | |
| 197 | ~raii_guard() { |
| 198 | if (is_active) { |
| 199 | my_func(); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | void dismiss() { |
| 204 | is_active = false; |
| 205 | } |
| 206 | private: |
| 207 | Func my_func; |
| 208 | bool is_active; |
| 209 | }; // class raii_guard |
| 210 | |
| 211 | template <typename Func> |
| 212 | raii_guard<Func> make_raii_guard( Func f ) { |
| 213 | return raii_guard<Func>(f); |
| 214 | } |
| 215 | |
| 216 | template <typename Body> |
| 217 | struct try_call_proxy { |
| 218 | try_call_proxy( Body b ) : body(b) {} |
| 219 | |
| 220 | template <typename OnExceptionBody> |
| 221 | void on_exception( OnExceptionBody on_exception_body ) { |
| 222 | auto guard = make_raii_guard(on_exception_body); |
| 223 | body(); |
| 224 | guard.dismiss(); |
| 225 | } |
| 226 | |
| 227 | template <typename OnCompletionBody> |
| 228 | void on_completion(OnCompletionBody on_completion_body) { |
| 229 | auto guard = make_raii_guard(on_completion_body); |
| 230 | body(); |
| 231 | } |
| 232 | |
| 233 | Body body; |
| 234 | }; // struct try_call_proxy |
| 235 | |
| 236 | // Template helper function for API |
| 237 | // try_call(lambda1).on_exception(lambda2) |
| 238 | // Executes lambda1 and if it throws an exception - executes lambda2 |
| 239 | template <typename Body> |
| 240 | try_call_proxy<Body> try_call( Body b ) { |
| 241 | return try_call_proxy<Body>(b); |
| 242 | } |
| 243 | |
| 244 | #if __TBB_CPP17_IS_SWAPPABLE_PRESENT |
| 245 | using std::is_nothrow_swappable; |
| 246 | using std::is_swappable; |
| 247 | #else // __TBB_CPP17_IS_SWAPPABLE_PRESENT |
| 248 | namespace is_swappable_detail { |
| 249 | using std::swap; |
| 250 | |
| 251 | template <typename T> |
| 252 | using has_swap = decltype(swap(std::declval<T&>(), std::declval<T&>())); |
| 253 | |
| 254 | #if _MSC_VER && _MSC_VER <= 1900 && !__INTEL_COMPILER |
| 255 | // Workaround for VS2015: it fails to instantiate noexcept(...) inside std::integral_constant. |
| 256 | template <typename T> |
| 257 | struct noexcept_wrapper { |
| 258 | static const bool value = noexcept(swap(std::declval<T&>(), std::declval<T&>())); |
| 259 | }; |
| 260 | template <typename T> |
| 261 | struct is_nothrow_swappable_impl : std::integral_constant<bool, noexcept_wrapper<T>::value> {}; |
| 262 | #else |
| 263 | template <typename T> |
| 264 | struct is_nothrow_swappable_impl : std::integral_constant<bool, noexcept(swap(std::declval<T&>(), std::declval<T&>()))> {}; |
| 265 | #endif |
| 266 | } |
| 267 | |
| 268 | template <typename T> |
| 269 | struct is_swappable : supports<T, is_swappable_detail::has_swap> {}; |
| 270 | |
| 271 | template <typename T> |
| 272 | struct is_nothrow_swappable |
| 273 | : conjunction<is_swappable<T>, is_swappable_detail::is_nothrow_swappable_impl<T>> {}; |
| 274 | #endif // __TBB_CPP17_IS_SWAPPABLE_PRESENT |
| 275 | |
| 276 | //! Allows to store a function parameter pack as a variable and later pass it to another function |
| 277 | template< typename... Types > |
| 278 | struct stored_pack; |
| 279 | |
| 280 | template<> |
| 281 | struct stored_pack<> |
| 282 | { |
| 283 | using pack_type = stored_pack<>; |
| 284 | stored_pack() {} |
| 285 | |
| 286 | // Friend front-end functions |
| 287 | template< typename F, typename Pack > friend void call(F&& f, Pack&& p); |
| 288 | template< typename Ret, typename F, typename Pack > friend Ret call_and_return(F&& f, Pack&& p); |
| 289 | |
| 290 | protected: |
| 291 | // Ideally, ref-qualified non-static methods would be used, |
| 292 | // but that would greatly reduce the set of compilers where it works. |
| 293 | template< typename Ret, typename F, typename... Preceding > |
| 294 | static Ret call(F&& f, const pack_type& /*pack*/, Preceding&&... params) { |
| 295 | return std::forward<F>(f)(std::forward<Preceding>(params)...); |
| 296 | } |
| 297 | template< typename Ret, typename F, typename... Preceding > |
| 298 | static Ret call(F&& f, pack_type&& /*pack*/, Preceding&&... params) { |
| 299 | return std::forward<F>(f)(std::forward<Preceding>(params)...); |
| 300 | } |
| 301 | }; |
| 302 | |
| 303 | template< typename T, typename... Types > |
| 304 | struct stored_pack<T, Types...> : stored_pack<Types...> |
| 305 | { |
| 306 | using pack_type = stored_pack<T, Types...>; |
| 307 | using pack_remainder = stored_pack<Types...>; |
| 308 | |
| 309 | // Since lifetime of original values is out of control, copies should be made. |
| 310 | // Thus references should be stripped away from the deduced type. |
| 311 | typename std::decay<T>::type leftmost_value; |
| 312 | |
| 313 | // Here rvalue references act in the same way as forwarding references, |
| 314 | // as long as class template parameters were deduced via forwarding references. |
| 315 | stored_pack(T&& t, Types&&... types) |
| 316 | : pack_remainder(std::forward<Types>(types)...), leftmost_value(std::forward<T>(t)) {} |
| 317 | |
| 318 | // Friend front-end functions |
| 319 | template< typename F, typename Pack > friend void call(F&& f, Pack&& p); |
| 320 | template< typename Ret, typename F, typename Pack > friend Ret call_and_return(F&& f, Pack&& p); |
| 321 | |
| 322 | protected: |
| 323 | template< typename Ret, typename F, typename... Preceding > |
| 324 | static Ret call(F&& f, pack_type& pack, Preceding&&... params) { |
| 325 | return pack_remainder::template call<Ret>( |
| 326 | std::forward<F>(f), static_cast<pack_remainder&>(pack), |
| 327 | std::forward<Preceding>(params)... , pack.leftmost_value |
| 328 | ); |
| 329 | } |
| 330 | |
| 331 | template< typename Ret, typename F, typename... Preceding > |
| 332 | static Ret call(F&& f, pack_type&& pack, Preceding&&... params) { |
| 333 | return pack_remainder::template call<Ret>( |
| 334 | std::forward<F>(f), static_cast<pack_remainder&&>(pack), |
| 335 | std::forward<Preceding>(params)... , std::move(pack.leftmost_value) |
| 336 | ); |
| 337 | } |
| 338 | }; |
| 339 | |
| 340 | //! Calls the given function with arguments taken from a stored_pack |
| 341 | template< typename F, typename Pack > |
| 342 | void call(F&& f, Pack&& p) { |
| 343 | std::decay<Pack>::type::template call<void>(std::forward<F>(f), std::forward<Pack>(p)); |
| 344 | } |
| 345 | |
| 346 | template< typename Ret, typename F, typename Pack > |
| 347 | Ret call_and_return(F&& f, Pack&& p) { |
| 348 | return std::decay<Pack>::type::template call<Ret>(std::forward<F>(f), std::forward<Pack>(p)); |
| 349 | } |
| 350 | |
| 351 | template< typename... Types > |
| 352 | stored_pack<Types...> save_pack(Types&&... types) { |
| 353 | return stored_pack<Types...>(std::forward<Types>(types)...); |
| 354 | } |
| 355 | |
| 356 | // A structure with the value which is equal to Trait::value |
| 357 | // but can be used in the immediate context due to parameter T |
| 358 | template <typename Trait, typename T> |
| 359 | struct dependent_bool : std::integral_constant<bool, bool(Trait::value)> {}; |
| 360 | |
| 361 | template <typename Callable> |
| 362 | struct body_arg_detector; |
| 363 | |
| 364 | template <typename Callable, typename ReturnType, typename Arg> |
| 365 | struct body_arg_detector<ReturnType(Callable::*)(Arg)> { |
| 366 | using arg_type = Arg; |
| 367 | }; |
| 368 | |
| 369 | template <typename Callable, typename ReturnType, typename Arg> |
| 370 | struct body_arg_detector<ReturnType(Callable::*)(Arg) const> { |
| 371 | using arg_type = Arg; |
| 372 | }; |
| 373 | |
| 374 | template <typename Callable> |
| 375 | struct argument_detector; |
| 376 | |
| 377 | template <typename Callable> |
| 378 | struct argument_detector { |
| 379 | using type = typename body_arg_detector<decltype(&Callable::operator())>::arg_type; |
| 380 | }; |
| 381 | |
| 382 | template <typename ReturnType, typename Arg> |
| 383 | struct argument_detector<ReturnType(*)(Arg)> { |
| 384 | using type = Arg; |
| 385 | }; |
| 386 | |
| 387 | // Detects the argument type of callable, works for callable with one argument. |
| 388 | template <typename Callable> |
| 389 | using argument_type_of = typename argument_detector<typename std::decay<Callable>::type>::type; |
| 390 | |
| 391 | template <typename T> |
| 392 | struct type_identity { |
| 393 | using type = T; |
| 394 | }; |
| 395 | |
| 396 | template <typename T> |
| 397 | using type_identity_t = typename type_identity<T>::type; |
| 398 | |
| 399 | } // inline namespace d0 |
| 400 | } // namespace detail |
| 401 | } // namespace tbb |
| 402 | |
| 403 | #endif // __TBB_detail__template_helpers_H |
| 404 | |
| 405 | |