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
30namespace tbb {
31namespace detail {
32inline namespace d0 {
33
34// An internal implementation of void_t, which can be used in SFINAE contexts
35template <typename...>
36struct void_impl {
37 using type = void;
38}; // struct void_impl
39
40template <typename... Args>
41using 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
44template <typename T, typename, template <typename> class... Checks>
45struct supports_impl {
46 using type = std::false_type;
47};
48
49template <typename T, template <typename> class... Checks>
50struct supports_impl<T, void_t<Checks<T>...>, Checks...> {
51 using type = std::true_type;
52};
53
54template <typename T, template <typename> class... Checks>
55using 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.
58template <unsigned u, unsigned long long ull >
59struct 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.
71template<typename T, typename U>
72inline T punned_cast( U* ptr ) {
73 std::uintptr_t x = reinterpret_cast<std::uintptr_t>(ptr);
74 return reinterpret_cast<T>(x);
75}
76
77template<class T, size_t S, size_t R>
78struct padded_base : T {
79 char pad[S - R];
80};
81template<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.
84template<class T, size_t S = max_nfs_size>
85struct padded : padded_base<T, S, sizeof(T) % S> {};
86
87#if __TBB_CPP14_INTEGER_SEQUENCE_PRESENT
88
89using std::index_sequence;
90using std::make_index_sequence;
91
92#else
93
94template<std::size_t... S> class index_sequence {};
95
96template<std::size_t N, std::size_t... S>
97struct make_index_sequence_impl : make_index_sequence_impl < N - 1, N - 1, S... > {};
98
99template<std::size_t... S>
100struct make_index_sequence_impl <0, S...> {
101 using type = index_sequence<S...>;
102};
103
104template<std::size_t N>
105using 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
110using std::conjunction;
111using std::disjunction;
112#else // __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT
113
114template <typename...>
115struct conjunction : std::true_type {};
116
117template <typename First, typename... Args>
118struct conjunction<First, Args...>
119 : std::conditional<bool(First::value), conjunction<Args...>, First>::type {};
120
121template <typename T>
122struct conjunction<T> : T {};
123
124template <typename...>
125struct disjunction : std::false_type {};
126
127template <typename First, typename... Args>
128struct disjunction<First, Args...>
129 : std::conditional<bool(First::value), First, disjunction<Args...>>::type {};
130
131template <typename T>
132struct disjunction<T> : T {};
133
134#endif // __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT
135
136template <typename Iterator>
137using iterator_value_t = typename std::iterator_traits<Iterator>::value_type;
138
139template <typename Iterator>
140using iterator_key_t = typename std::remove_const<typename iterator_value_t<Iterator>::first_type>::type;
141
142template <typename Iterator>
143using iterator_mapped_t = typename iterator_value_t<Iterator>::second_type;
144
145template <typename Iterator>
146using iterator_alloc_pair_t = std::pair<typename std::add_const<iterator_key_t<Iterator>>::type,
147 iterator_mapped_t<Iterator>>;
148
149template <typename A> using alloc_value_type = typename A::value_type;
150template <typename A> using alloc_ptr_t = typename std::allocator_traits<A>::pointer;
151template <typename A> using has_allocate = decltype(std::declval<alloc_ptr_t<A>&>() = std::declval<A>().allocate(0));
152template <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
155template <typename T>
156using is_allocator = supports<T, alloc_value_type, has_allocate, has_deallocate>;
157
158#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
159template <typename T>
160inline 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
164template <std::size_t N, typename... Args>
165struct pack_element {
166 using type = void;
167};
168
169template <std::size_t N, typename T, typename... Args>
170struct pack_element<N, T, Args...> {
171 using type = typename pack_element<N-1, Args...>::type;
172};
173
174template <typename T, typename... Args>
175struct pack_element<0, T, Args...> {
176 using type = T;
177};
178
179template <std::size_t N, typename... Args>
180using pack_element_t = typename pack_element<N, Args...>::type;
181
182template <typename Func>
183class raii_guard {
184public:
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 }
206private:
207 Func my_func;
208 bool is_active;
209}; // class raii_guard
210
211template <typename Func>
212raii_guard<Func> make_raii_guard( Func f ) {
213 return raii_guard<Func>(f);
214}
215
216template <typename Body>
217struct 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
239template <typename Body>
240try_call_proxy<Body> try_call( Body b ) {
241 return try_call_proxy<Body>(b);
242}
243
244#if __TBB_CPP17_IS_SWAPPABLE_PRESENT
245using std::is_nothrow_swappable;
246using std::is_swappable;
247#else // __TBB_CPP17_IS_SWAPPABLE_PRESENT
248namespace is_swappable_detail {
249using std::swap;
250
251template <typename T>
252using 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.
256template <typename T>
257struct noexcept_wrapper {
258 static const bool value = noexcept(swap(std::declval<T&>(), std::declval<T&>()));
259};
260template <typename T>
261struct is_nothrow_swappable_impl : std::integral_constant<bool, noexcept_wrapper<T>::value> {};
262#else
263template <typename T>
264struct is_nothrow_swappable_impl : std::integral_constant<bool, noexcept(swap(std::declval<T&>(), std::declval<T&>()))> {};
265#endif
266}
267
268template <typename T>
269struct is_swappable : supports<T, is_swappable_detail::has_swap> {};
270
271template <typename T>
272struct 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
277template< typename... Types >
278struct stored_pack;
279
280template<>
281struct 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
290protected:
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
303template< typename T, typename... Types >
304struct 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
322protected:
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
341template< typename F, typename Pack >
342void call(F&& f, Pack&& p) {
343 std::decay<Pack>::type::template call<void>(std::forward<F>(f), std::forward<Pack>(p));
344}
345
346template< typename Ret, typename F, typename Pack >
347Ret 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
351template< typename... Types >
352stored_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
358template <typename Trait, typename T>
359struct dependent_bool : std::integral_constant<bool, bool(Trait::value)> {};
360
361template <typename Callable>
362struct body_arg_detector;
363
364template <typename Callable, typename ReturnType, typename Arg>
365struct body_arg_detector<ReturnType(Callable::*)(Arg)> {
366 using arg_type = Arg;
367};
368
369template <typename Callable, typename ReturnType, typename Arg>
370struct body_arg_detector<ReturnType(Callable::*)(Arg) const> {
371 using arg_type = Arg;
372};
373
374template <typename Callable>
375struct argument_detector;
376
377template <typename Callable>
378struct argument_detector {
379 using type = typename body_arg_detector<decltype(&Callable::operator())>::arg_type;
380};
381
382template <typename ReturnType, typename Arg>
383struct argument_detector<ReturnType(*)(Arg)> {
384 using type = Arg;
385};
386
387// Detects the argument type of callable, works for callable with one argument.
388template <typename Callable>
389using argument_type_of = typename argument_detector<typename std::decay<Callable>::type>::type;
390
391template <typename T>
392struct type_identity {
393 using type = T;
394};
395
396template <typename T>
397using 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

source code of include/oneapi/tbb/detail/_template_helpers.h