1 /* 2 Copyright (c) 2005-2023 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 = static_cast<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