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 | |