| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| namespace fbl { |
| |
| template <typename T, T v> |
| struct integral_constant { |
| static constexpr T value = v; |
| |
| using value_type = T; |
| using type = integral_constant<T, v>; |
| }; |
| |
| using true_type = integral_constant<bool, true>; |
| using false_type = integral_constant<bool, false>; |
| |
| // is_void: |
| template <typename T> |
| struct is_void : false_type {}; |
| |
| template <> |
| struct is_void<void> : true_type {}; |
| |
| template <> |
| struct is_void<const void> : true_type {}; |
| |
| template <> |
| struct is_void<volatile void> : true_type {}; |
| |
| template <> |
| struct is_void<const volatile void> : true_type {}; |
| |
| // is_null_pointer: |
| template <typename T> |
| struct is_null_pointer : false_type {}; |
| |
| template <> |
| struct is_null_pointer<decltype(nullptr)> : true_type {}; |
| |
| template <> |
| struct is_null_pointer<const decltype(nullptr)> : true_type {}; |
| |
| template <> |
| struct is_null_pointer<volatile decltype(nullptr)> : true_type {}; |
| |
| template <> |
| struct is_null_pointer<const volatile decltype(nullptr)> : true_type {}; |
| |
| // is_const: |
| |
| template <typename T> |
| struct is_const : false_type {}; |
| |
| template <typename T> |
| struct is_const<const T> : true_type {}; |
| |
| // is_lvalue_reference: |
| |
| template <typename T> |
| struct is_lvalue_reference : false_type {}; |
| |
| template <typename T> |
| struct is_lvalue_reference<T&> : true_type {}; |
| |
| // is_rvalue_reference: |
| |
| template <typename T> |
| struct is_rvalue_reference : false_type {}; |
| |
| template <typename T> |
| struct is_rvalue_reference<T&&> : true_type {}; |
| |
| // is_reference: |
| template <typename T> |
| struct is_reference : false_type {}; |
| |
| template <typename T> |
| struct is_reference<T&> : true_type {}; |
| |
| template <typename T> |
| struct is_reference<T&&> : true_type {}; |
| |
| // remove_reference: |
| |
| template <typename T> |
| struct remove_reference { |
| using type = T; |
| }; |
| |
| template <typename T> |
| struct remove_reference<T&> { |
| using type = T; |
| }; |
| |
| template <typename T> |
| struct remove_reference<T&&> { |
| using type = T; |
| }; |
| |
| // remove_pointer: |
| |
| template <typename T> |
| struct remove_pointer { |
| using type = T; |
| }; |
| |
| template <typename T> |
| struct remove_pointer<T*> { |
| using type = T; |
| }; |
| |
| // remove_const: |
| |
| template <typename T> |
| struct remove_const { |
| typedef T type; |
| }; |
| |
| template <typename T> |
| struct remove_const<const T> { |
| typedef T type; |
| }; |
| |
| // remove_volatile: |
| |
| template <typename T> |
| struct remove_volatile { |
| typedef T type; |
| }; |
| |
| template <typename T> |
| struct remove_volatile<volatile T> { |
| typedef T type; |
| }; |
| |
| // remove_cv: |
| |
| template <typename T> |
| struct remove_cv { |
| typedef typename remove_volatile<typename remove_const<T>::type>::type type; |
| }; |
| |
| // remove_extent: |
| |
| template <typename T> |
| struct remove_extent { |
| using type = T; |
| }; |
| |
| template <typename T> |
| struct remove_extent<T[]> { |
| using type = T; |
| }; |
| |
| // Avoid having to pull in a header to name size_t; just use sizeof to |
| // get at it. |
| template <typename T, decltype(sizeof(nullptr)) N> |
| struct remove_extent<T[N]> { |
| using type = T; |
| }; |
| |
| // forward: |
| |
| template <typename T> |
| constexpr T&& forward(typename remove_reference<T>::type& t) { |
| return static_cast<T&&>(t); |
| } |
| |
| template <typename T> |
| constexpr T&& forward(typename remove_reference<T>::type&& t) { |
| static_assert(!is_lvalue_reference<T>::value, "bad fbl::forward call"); |
| return static_cast<T&&>(t); |
| } |
| |
| // is_same: |
| |
| template<class T, class U> struct is_same : false_type {}; |
| template<class T> struct is_same<T, T> : true_type {}; |
| |
| // enable_if: |
| |
| template<bool B, class T = void> struct enable_if { }; |
| template<class T> struct enable_if<true, T> { |
| typedef T type; |
| }; |
| |
| // conditional: |
| |
| template<bool B, class T, class F> |
| struct conditional { typedef T type; }; |
| |
| template<class T, class F> |
| struct conditional<false, T, F> { typedef F type; }; |
| |
| // is_integral. By default, T is not integral (aka, not an integer) |
| template <typename T> |
| struct is_integral : false_type {}; |
| |
| // Specializations. Every basic integral type needs to be called out. |
| template <> struct is_integral<bool> : true_type {}; |
| template <> struct is_integral<char> : true_type {}; |
| template <> struct is_integral<char16_t> : true_type {}; |
| template <> struct is_integral<char32_t> : true_type {}; |
| template <> struct is_integral<wchar_t> : true_type {}; |
| template <> struct is_integral<signed char> : true_type {}; |
| template <> struct is_integral<unsigned char> : true_type {}; |
| template <> struct is_integral<short int> : true_type {}; |
| template <> struct is_integral<unsigned short int> : true_type {}; |
| template <> struct is_integral<int> : true_type {}; |
| template <> struct is_integral<unsigned int> : true_type {}; |
| template <> struct is_integral<long int> : true_type {}; |
| template <> struct is_integral<unsigned long int> : true_type {}; |
| template <> struct is_integral<long long int> : true_type {}; |
| template <> struct is_integral<unsigned long long int> : true_type {}; |
| |
| // is_floating_point. By default, T is not a floating point type. |
| template <typename T> |
| struct is_floating_point : false_type {}; |
| |
| // Specializations. Every basic floating point type needs to be called out. |
| template <> struct is_floating_point<float> : true_type {}; |
| template <> struct is_floating_point<double> : true_type {}; |
| template <> struct is_floating_point<long double> : true_type {}; |
| |
| // Arithmetic data types are either floats or integers |
| template <typename T> |
| struct is_arithmetic : |
| integral_constant<bool, is_integral<T>::value || is_floating_point<T>::value> { }; |
| |
| namespace internal { |
| |
| template<typename T, bool = is_arithmetic<T>::value> |
| struct is_signed : integral_constant<bool, T(-1) < T(0)> {}; |
| template<typename T> |
| struct is_signed<T, false> : fbl::false_type {}; |
| |
| template<typename T, bool = is_arithmetic<T>::value> |
| struct is_unsigned : integral_constant<bool, T(0) < T(-1)> {}; |
| template<typename T> |
| struct is_unsigned<T, false> : fbl::false_type {}; |
| |
| template<typename T, bool = is_integral<T>::value> |
| struct is_signed_integer : integral_constant<bool, T(-1) < T(0)> {}; |
| template<typename T> |
| struct is_signed_integer<T, false> : fbl::false_type {}; |
| |
| template<typename T, bool = is_integral<T>::value> |
| struct is_unsigned_integer : integral_constant<bool, T(0) < T(-1)> {}; |
| template<typename T> |
| struct is_unsigned_integer<T, false> : fbl::false_type {}; |
| |
| } // namespace internal |
| |
| template <typename T> |
| struct is_signed : internal::is_signed<T>::type {}; |
| |
| template <typename T> |
| struct is_unsigned : internal::is_unsigned<T>::type {}; |
| |
| template <typename T> |
| struct is_signed_integer : internal::is_signed_integer<T>::type {}; |
| |
| template <typename T> |
| struct is_unsigned_integer : internal::is_unsigned_integer<T>::type {}; |
| |
| // is_enum is a builtin |
| template<typename T> |
| struct is_enum : integral_constant<bool, __is_enum(T)> { }; |
| |
| // is_pod is a builtin |
| template<typename T> |
| struct is_pod : integral_constant<bool, __is_pod(T)> { }; |
| |
| // is_standard_layout is a builtin |
| template<typename T> |
| struct is_standard_layout : integral_constant<bool, __is_standard_layout(T)> { }; |
| |
| // underlying_type is a builtin |
| template<typename T> |
| struct underlying_type { |
| using type = __underlying_type(T); |
| }; |
| |
| // match_cv: match_cv<SrcType, DestType>::type is DestType cv-qualified in the same way as SrcType. |
| |
| // Primary template: |
| template <typename SrcType, typename DestType> |
| struct match_cv { |
| using type = typename remove_cv<DestType>::type; |
| }; |
| |
| // Specializations for const/volatile/const volatile: |
| template <typename SrcType, typename DestType> |
| struct match_cv<const SrcType, DestType> { |
| using type = typename remove_cv<DestType>::type const; |
| }; |
| template <typename SrcType, typename DestType> |
| struct match_cv<volatile SrcType, DestType> { |
| using type = typename remove_cv<DestType>::type volatile; |
| }; |
| template <typename SrcType, typename DestType> |
| struct match_cv<const volatile SrcType, DestType> { |
| using type = typename remove_cv<DestType>::type const volatile; |
| }; |
| |
| // is_class builtin |
| template <typename T> |
| struct is_class : public integral_constant<bool, __is_class(T)> { }; |
| |
| // is_union builtin |
| template <typename T> |
| struct is_union : public integral_constant<bool, __is_union(T)> { }; |
| |
| // is_base_of builtin |
| template <typename Base, typename Derived> |
| struct is_base_of : public integral_constant<bool, __is_base_of(Base, Derived)> { }; |
| |
| // has_virtual_destructor |
| template <typename T> |
| struct has_virtual_destructor : public integral_constant<bool, __has_virtual_destructor(T)> { }; |
| |
| // has_trivial_destructor |
| template <typename T> |
| struct has_trivial_destructor : public integral_constant<bool, __has_trivial_destructor(T)> { }; |
| |
| // is_pointer |
| namespace internal { |
| template <typename T> |
| struct is_pointer : public false_type { }; |
| |
| template <typename T> |
| struct is_pointer<T*> : public true_type { }; |
| } // namespace internal |
| |
| template <typename T> |
| struct is_pointer : |
| public integral_constant<bool, |
| internal::is_pointer<typename remove_cv<T>::type>::value> { }; |
| |
| // is_convertible_pointer |
| // |
| // Note: this is a simplified version of std::is_convertible. Whereas |
| // std::is_convertible will check to see if any two types are implicitly |
| // convertible, is_convertible_pointer will only check to see if two pointer |
| // types are implicitly convertible. Additionally, no explicit support or tests |
| // have been added for function pointers conversion. |
| template <typename From, typename To> |
| struct is_convertible_pointer { |
| private: |
| static true_type test(To); |
| static false_type test(...); |
| static From make_from_type(); |
| |
| public: |
| static constexpr bool value = |
| is_pointer<From>::value && |
| is_pointer<To>::value && |
| decltype(test(make_from_type()))::value; |
| }; |
| |
| namespace internal { |
| |
| template <typename...> |
| struct make_void { |
| using type = void; |
| }; |
| |
| } // namespace internal |
| |
| // Utility type for SFINAE expression evaluation, equivalent to C++17 std::void_t. |
| template <typename... Ts> |
| using void_t = typename internal::make_void<Ts...>::type; |
| |
| // is_function: |
| |
| // Morally, is_function could be implemented in the same style as |
| // e.g. is_reference: a base case of false, and specializations of |
| // true for every kind of function. However, listing all of those |
| // cases is brittle and language version dependent. For instance, |
| // C++17 makes noexcept specifiers as part of the type. |
| |
| // Instead, this implementation uses a pair of tricky overloaded |
| // functions to detect every type that _isn't_ a function. This |
| // includes: |
| // - objects (classes, unions, and primitives) (including incomplete types) |
| // - references (including references to functions) |
| // - nullptr_t |
| // - void |
| // - pointers (including pointers to functions) |
| // - pointers to members |
| // - arrays of both complete and incomplete type |
| |
| namespace internal { |
| |
| // This leaves pointers, pointer-to-members, arrays, and functions. |
| template <typename T> |
| struct is_object_void_reference_or_null_pointer { |
| static constexpr bool value = |
| is_class<T>::value || |
| is_union<T>::value || |
| is_void<T>::value || |
| is_reference<T>::value || |
| is_null_pointer<T>::value; |
| }; |
| |
| struct dummy {}; |
| |
| struct func_tag {}; |
| |
| struct nonfunc_tag {}; |
| |
| template <typename T> |
| func_tag choose(T*); |
| |
| template <typename T> |
| func_tag choose(dummy); |
| |
| template <typename T> |
| nonfunc_tag choose(...); |
| |
| template <typename T> |
| T& make(int); |
| |
| template <typename T> |
| dummy make(...); |
| |
| } // namespace internal |
| |
| template <typename T, |
| bool = internal::is_object_void_reference_or_null_pointer<T>::value> |
| struct is_function : public integral_constant<bool, |
| is_same<internal::func_tag, |
| decltype(internal::choose<T>(internal::make<T>(0))) |
| >::value> { |
| }; |
| |
| template <typename T> |
| struct is_function<T, true> : public false_type { |
| }; |
| |
| } // namespace fbl |