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