1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 namespace fbl {
8 
9 template <typename T, T v>
10 struct integral_constant {
11     static constexpr T value = v;
12 
13     using value_type = T;
14     using type = integral_constant<T, v>;
15 };
16 
17 using true_type = integral_constant<bool, true>;
18 using false_type = integral_constant<bool, false>;
19 
20 // is_void:
21 template <typename T>
22 struct is_void : false_type {};
23 
24 template <>
25 struct is_void<void> : true_type {};
26 
27 template <>
28 struct is_void<const void> : true_type {};
29 
30 template <>
31 struct is_void<volatile void> : true_type {};
32 
33 template <>
34 struct is_void<const volatile void> : true_type {};
35 
36 // is_null_pointer:
37 template <typename T>
38 struct is_null_pointer : false_type {};
39 
40 template <>
41 struct is_null_pointer<decltype(nullptr)> : true_type {};
42 
43 template <>
44 struct is_null_pointer<const decltype(nullptr)> : true_type {};
45 
46 template <>
47 struct is_null_pointer<volatile decltype(nullptr)> : true_type {};
48 
49 template <>
50 struct is_null_pointer<const volatile decltype(nullptr)> : true_type {};
51 
52 // is_const:
53 
54 template <typename T>
55 struct is_const : false_type {};
56 
57 template <typename T>
58 struct is_const<const T> : true_type {};
59 
60 // is_lvalue_reference:
61 
62 template <typename T>
63 struct is_lvalue_reference : false_type {};
64 
65 template <typename T>
66 struct is_lvalue_reference<T&> : true_type {};
67 
68 // is_rvalue_reference:
69 
70 template <typename T>
71 struct is_rvalue_reference : false_type {};
72 
73 template <typename T>
74 struct is_rvalue_reference<T&&> : true_type {};
75 
76 // is_reference:
77 template <typename T>
78 struct is_reference : false_type {};
79 
80 template <typename T>
81 struct is_reference<T&> : true_type {};
82 
83 template <typename T>
84 struct is_reference<T&&> : true_type {};
85 
86 // remove_reference:
87 
88 template <typename T>
89 struct remove_reference {
90     using type = T;
91 };
92 
93 template <typename T>
94 struct remove_reference<T&> {
95     using type = T;
96 };
97 
98 template <typename T>
99 struct remove_reference<T&&> {
100     using type = T;
101 };
102 
103 // remove_pointer:
104 
105 template <typename T>
106 struct remove_pointer {
107     using type = T;
108 };
109 
110 template <typename T>
111 struct remove_pointer<T*> {
112     using type = T;
113 };
114 
115 // remove_const:
116 
117 template <typename T>
118 struct remove_const {
119     typedef T type;
120 };
121 
122 template <typename T>
123 struct remove_const<const T> {
124     typedef T type;
125 };
126 
127 // remove_volatile:
128 
129 template <typename T>
130 struct remove_volatile {
131     typedef T type;
132 };
133 
134 template <typename T>
135 struct remove_volatile<volatile T> {
136     typedef T type;
137 };
138 
139 // remove_cv:
140 
141 template <typename T>
142 struct remove_cv {
143     typedef typename remove_volatile<typename remove_const<T>::type>::type type;
144 };
145 
146 // remove_extent:
147 
148 template <typename T>
149 struct remove_extent {
150     using type = T;
151 };
152 
153 template <typename T>
154 struct remove_extent<T[]> {
155     using type = T;
156 };
157 
158 // Avoid having to pull in a header to name size_t; just use sizeof to
159 // get at it.
160 template <typename T, decltype(sizeof(nullptr)) N>
161 struct remove_extent<T[N]> {
162     using type = T;
163 };
164 
165 // forward:
166 
167 template <typename T>
168 constexpr T&& forward(typename remove_reference<T>::type& t) {
169     return static_cast<T&&>(t);
170 }
171 
172 template <typename T>
173 constexpr T&& forward(typename remove_reference<T>::type&& t) {
174     static_assert(!is_lvalue_reference<T>::value, "bad fbl::forward call");
175     return static_cast<T&&>(t);
176 }
177 
178 // is_same:
179 
180 template<class T, class U> struct is_same : false_type {};
181 template<class T> struct is_same<T, T> : true_type {};
182 
183 // enable_if:
184 
185 template<bool B, class T = void> struct enable_if { };
186 template<class T> struct enable_if<true, T> {
187     typedef T type;
188 };
189 
190 // conditional:
191 
192 template<bool B, class T, class F>
193 struct conditional { typedef T type; };
194 
195 template<class T, class F>
196 struct conditional<false, T, F> { typedef F type; };
197 
198 // is_integral.  By default, T is not integral (aka, not an integer)
199 template <typename T>
200 struct is_integral : false_type {};
201 
202 // Specializations.  Every basic integral type needs to be called out.
203 template <> struct is_integral<bool>                   : true_type {};
204 template <> struct is_integral<char>                   : true_type {};
205 template <> struct is_integral<char16_t>               : true_type {};
206 template <> struct is_integral<char32_t>               : true_type {};
207 template <> struct is_integral<wchar_t>                : true_type {};
208 template <> struct is_integral<signed char>            : true_type {};
209 template <> struct is_integral<unsigned char>          : true_type {};
210 template <> struct is_integral<short int>              : true_type {};
211 template <> struct is_integral<unsigned short int>     : true_type {};
212 template <> struct is_integral<int>                    : true_type {};
213 template <> struct is_integral<unsigned int>           : true_type {};
214 template <> struct is_integral<long int>               : true_type {};
215 template <> struct is_integral<unsigned long int>      : true_type {};
216 template <> struct is_integral<long long int>          : true_type {};
217 template <> struct is_integral<unsigned long long int> : true_type {};
218 
219 // is_floating_point.  By default, T is not a floating point type.
220 template <typename T>
221 struct is_floating_point : false_type {};
222 
223 // Specializations.  Every basic floating point type needs to be called out.
224 template <> struct is_floating_point<float>       : true_type {};
225 template <> struct is_floating_point<double>      : true_type {};
226 template <> struct is_floating_point<long double> : true_type {};
227 
228 // Arithmetic data types are either floats or integers
229 template <typename T>
230 struct is_arithmetic :
231     integral_constant<bool, is_integral<T>::value || is_floating_point<T>::value> { };
232 
233 namespace internal {
234 
235 template<typename T, bool = is_arithmetic<T>::value>
236 struct is_signed : integral_constant<bool, T(-1) < T(0)> {};
237 template<typename T>
238 struct is_signed<T, false> : fbl::false_type {};
239 
240 template<typename T, bool = is_arithmetic<T>::value>
241 struct is_unsigned : integral_constant<bool, T(0) < T(-1)> {};
242 template<typename T>
243 struct is_unsigned<T, false> : fbl::false_type {};
244 
245 template<typename T, bool = is_integral<T>::value>
246 struct is_signed_integer : integral_constant<bool, T(-1) < T(0)> {};
247 template<typename T>
248 struct is_signed_integer<T, false> : fbl::false_type {};
249 
250 template<typename T, bool = is_integral<T>::value>
251 struct is_unsigned_integer : integral_constant<bool, T(0) < T(-1)> {};
252 template<typename T>
253 struct is_unsigned_integer<T, false> : fbl::false_type {};
254 
255 } // namespace internal
256 
257 template <typename T>
258 struct is_signed : internal::is_signed<T>::type {};
259 
260 template <typename T>
261 struct is_unsigned : internal::is_unsigned<T>::type {};
262 
263 template <typename T>
264 struct is_signed_integer : internal::is_signed_integer<T>::type {};
265 
266 template <typename T>
267 struct is_unsigned_integer : internal::is_unsigned_integer<T>::type {};
268 
269 // is_enum is a builtin
270 template<typename T>
271 struct is_enum : integral_constant<bool, __is_enum(T)> { };
272 
273 // is_pod is a builtin
274 template<typename T>
275 struct is_pod : integral_constant<bool, __is_pod(T)> { };
276 
277 // is_standard_layout is a builtin
278 template<typename T>
279 struct is_standard_layout : integral_constant<bool, __is_standard_layout(T)> { };
280 
281 // underlying_type is a builtin
282 template<typename T>
283 struct underlying_type {
284   using type = __underlying_type(T);
285 };
286 
287 // match_cv: match_cv<SrcType, DestType>::type is DestType cv-qualified in the same way as SrcType.
288 
289 // Primary template:
290 template <typename SrcType, typename DestType>
291 struct match_cv {
292   using type = typename remove_cv<DestType>::type;
293 };
294 
295 // Specializations for const/volatile/const volatile:
296 template <typename SrcType, typename DestType>
297 struct match_cv<const SrcType, DestType> {
298   using type = typename remove_cv<DestType>::type const;
299 };
300 template <typename SrcType, typename DestType>
301 struct match_cv<volatile SrcType, DestType> {
302   using type = typename remove_cv<DestType>::type volatile;
303 };
304 template <typename SrcType, typename DestType>
305 struct match_cv<const volatile SrcType, DestType> {
306   using type = typename remove_cv<DestType>::type const volatile;
307 };
308 
309 // is_class builtin
310 template <typename T>
311 struct is_class : public integral_constant<bool, __is_class(T)> { };
312 
313 // is_union builtin
314 template <typename T>
315 struct is_union : public integral_constant<bool, __is_union(T)> { };
316 
317 // is_base_of builtin
318 template <typename Base, typename Derived>
319 struct is_base_of : public integral_constant<bool, __is_base_of(Base, Derived)> { };
320 
321 // has_virtual_destructor
322 template <typename T>
323 struct has_virtual_destructor : public integral_constant<bool, __has_virtual_destructor(T)> { };
324 
325 // has_trivial_destructor
326 template <typename T>
327 struct has_trivial_destructor : public integral_constant<bool, __has_trivial_destructor(T)> { };
328 
329 // is_pointer
330 namespace internal {
331 template <typename T>
332 struct is_pointer : public false_type { };
333 
334 template <typename T>
335 struct is_pointer<T*> : public true_type { };
336 }  // namespace internal
337 
338 template <typename T>
339 struct is_pointer :
340     public integral_constant<bool,
341                              internal::is_pointer<typename remove_cv<T>::type>::value> { };
342 
343 // is_convertible_pointer
344 //
345 // Note: this is a simplified version of std::is_convertible.  Whereas
346 // std::is_convertible will check to see if any two types are implicitly
347 // convertible, is_convertible_pointer will only check to see if two pointer
348 // types are implicitly convertible.  Additionally, no explicit support or tests
349 // have been added for function pointers conversion.
350 template <typename From, typename To>
351 struct is_convertible_pointer {
352 private:
353     static true_type  test(To);
354     static false_type test(...);
355     static From       make_from_type();
356 
357 public:
358     static constexpr bool value =
359         is_pointer<From>::value &&
360         is_pointer<To>::value &&
361         decltype(test(make_from_type()))::value;
362 };
363 
364 namespace internal {
365 
366 template <typename...>
367 struct make_void {
368     using type = void;
369 };
370 
371 } // namespace internal
372 
373 // Utility type for SFINAE expression evaluation, equivalent to C++17 std::void_t.
374 template <typename... Ts>
375 using void_t = typename internal::make_void<Ts...>::type;
376 
377 // is_function:
378 
379 // Morally, is_function could be implemented in the same style as
380 // e.g. is_reference: a base case of false, and specializations of
381 // true for every kind of function. However, listing all of those
382 // cases is brittle and language version dependent. For instance,
383 // C++17 makes noexcept specifiers as part of the type.
384 
385 // Instead, this implementation uses a pair of tricky overloaded
386 // functions to detect every type that _isn't_ a function. This
387 // includes:
388 // - objects (classes, unions, and primitives) (including incomplete types)
389 // - references (including references to functions)
390 // - nullptr_t
391 // - void
392 // - pointers (including pointers to functions)
393 // - pointers to members
394 // - arrays of both complete and incomplete type
395 
396 namespace internal {
397 
398 // This leaves pointers, pointer-to-members, arrays, and functions.
399 template <typename T>
400 struct is_object_void_reference_or_null_pointer {
401     static constexpr bool value =
402         is_class<T>::value ||
403         is_union<T>::value ||
404         is_void<T>::value ||
405         is_reference<T>::value ||
406         is_null_pointer<T>::value;
407 };
408 
409 struct dummy {};
410 
411 struct func_tag {};
412 
413 struct nonfunc_tag {};
414 
415 template <typename T>
416 func_tag choose(T*);
417 
418 template <typename T>
419 func_tag choose(dummy);
420 
421 template <typename T>
422 nonfunc_tag choose(...);
423 
424 template <typename T>
425 T& make(int);
426 
427 template <typename T>
428 dummy make(...);
429 
430 } // namespace internal
431 
432 template <typename T,
433           bool = internal::is_object_void_reference_or_null_pointer<T>::value>
434 struct is_function : public integral_constant<bool,
435                                               is_same<internal::func_tag,
436                                                       decltype(internal::choose<T>(internal::make<T>(0)))
437                                                       >::value> {
438 };
439 
440 template <typename T>
441 struct is_function<T, true> : public false_type {
442 };
443 
444 }  // namespace fbl
445