| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* Template-based metaprogramming and type-testing facilities. */ |
| |
| #ifndef mozilla_TypeTraits_h_ |
| #define mozilla_TypeTraits_h_ |
| |
| /* |
| * These traits are approximate copies of the traits and semantics from C++11's |
| * <type_traits> header. Don't add traits not in that header! When all |
| * platforms provide that header, we can convert all users and remove this one. |
| */ |
| |
| #include <wchar.h> |
| |
| namespace mozilla { |
| |
| /* Forward declarations. */ |
| |
| template<typename> struct RemoveCV; |
| |
| /* 20.9.3 Helper classes [meta.help] */ |
| |
| /** |
| * Helper class used as a base for various type traits, exposed publicly |
| * because <type_traits> exposes it as well. |
| */ |
| template<typename T, T Value> |
| struct IntegralConstant |
| { |
| static const T value = Value; |
| typedef T ValueType; |
| typedef IntegralConstant<T, Value> Type; |
| }; |
| |
| /** Convenient aliases. */ |
| typedef IntegralConstant<bool, true> TrueType; |
| typedef IntegralConstant<bool, false> FalseType; |
| |
| /* 20.9.4 Unary type traits [meta.unary] */ |
| |
| /* 20.9.4.1 Primary type categories [meta.unary.cat] */ |
| |
| namespace detail { |
| |
| template <typename T> |
| struct IsIntegralHelper : FalseType {}; |
| |
| template<> struct IsIntegralHelper<char> : TrueType {}; |
| template<> struct IsIntegralHelper<signed char> : TrueType {}; |
| template<> struct IsIntegralHelper<unsigned char> : TrueType {}; |
| template<> struct IsIntegralHelper<short> : TrueType {}; |
| template<> struct IsIntegralHelper<unsigned short> : TrueType {}; |
| template<> struct IsIntegralHelper<int> : TrueType {}; |
| template<> struct IsIntegralHelper<unsigned int> : TrueType {}; |
| template<> struct IsIntegralHelper<long> : TrueType {}; |
| template<> struct IsIntegralHelper<unsigned long> : TrueType {}; |
| template<> struct IsIntegralHelper<long long> : TrueType {}; |
| template<> struct IsIntegralHelper<unsigned long long> : TrueType {}; |
| template<> struct IsIntegralHelper<bool> : TrueType {}; |
| template<> struct IsIntegralHelper<wchar_t> : TrueType {}; |
| |
| } /* namespace detail */ |
| |
| /** |
| * IsIntegral determines whether a type is an integral type. |
| * |
| * mozilla::IsIntegral<int>::value is true; |
| * mozilla::IsIntegral<unsigned short>::value is true; |
| * mozilla::IsIntegral<const long>::value is true; |
| * mozilla::IsIntegral<int*>::value is false; |
| * mozilla::IsIntegral<double>::value is false; |
| * |
| * Note that the behavior of IsIntegral on char16_t and char32_t is |
| * unspecified. |
| */ |
| template<typename T> |
| struct IsIntegral : detail::IsIntegralHelper<typename RemoveCV<T>::Type> |
| {}; |
| |
| template<typename T, typename U> |
| struct IsSame; |
| |
| namespace detail { |
| |
| template<typename T> |
| struct IsFloatingPointHelper |
| : IntegralConstant<bool, |
| IsSame<T, float>::value || |
| IsSame<T, double>::value || |
| IsSame<T, long double>::value> |
| {}; |
| |
| } // namespace detail |
| |
| /** |
| * IsFloatingPoint determines whether a type is a floating point type (float, |
| * double, long double). |
| * |
| * mozilla::IsFloatingPoint<int>::value is false; |
| * mozilla::IsFloatingPoint<const float>::value is true; |
| * mozilla::IsFloatingPoint<long double>::value is true; |
| * mozilla::IsFloatingPoint<double*>::value is false. |
| */ |
| template<typename T> |
| struct IsFloatingPoint |
| : detail::IsFloatingPointHelper<typename RemoveCV<T>::Type> |
| {}; |
| |
| /** |
| * IsPointer determines whether a type is a pointer type (but not a pointer-to- |
| * member type). |
| * |
| * mozilla::IsPointer<struct S*>::value is true; |
| * mozilla::IsPointer<int**>::value is true; |
| * mozilla::IsPointer<void (*)(void)>::value is true; |
| * mozilla::IsPointer<int>::value is false; |
| * mozilla::IsPointer<struct S>::value is false. |
| */ |
| template<typename T> |
| struct IsPointer : FalseType {}; |
| |
| template<typename T> |
| struct IsPointer<T*> : TrueType {}; |
| |
| /* 20.9.4.2 Composite type traits [meta.unary.comp] */ |
| |
| /** |
| * IsArithmetic determines whether a type is arithmetic. A type is arithmetic |
| * iff it is an integral type or a floating point type. |
| * |
| * mozilla::IsArithmetic<int>::value is true; |
| * mozilla::IsArithmetic<double>::value is true; |
| * mozilla::IsArithmetic<long double*>::value is false. |
| */ |
| template<typename T> |
| struct IsArithmetic |
| : IntegralConstant<bool, IsIntegral<T>::value || IsFloatingPoint<T>::value> |
| {}; |
| |
| /* 20.9.4.3 Type properties [meta.unary.prop] */ |
| |
| /** |
| * IsConst determines whether a type is const or not. |
| * |
| * mozilla::IsConst<int>::value is false; |
| * mozilla::IsConst<void* const>::value is true; |
| * mozilla::IsConst<const char*>::value is false. |
| */ |
| template<typename T> |
| struct IsConst : FalseType {}; |
| |
| template<typename T> |
| struct IsConst<const T> : TrueType {}; |
| |
| /** |
| * IsVolatile determines whether a type is volatile or not. |
| * |
| * mozilla::IsVolatile<int>::value is false; |
| * mozilla::IsVolatile<void* volatile>::value is true; |
| * mozilla::IsVolatile<volatile char*>::value is false. |
| */ |
| template<typename T> |
| struct IsVolatile : FalseType {}; |
| |
| template<typename T> |
| struct IsVolatile<volatile T> : TrueType {}; |
| |
| /** |
| * Traits class for identifying POD types. Until C++11 there's no automatic |
| * way to detect PODs, so for the moment this is done manually. Users may |
| * define specializations of this class that inherit from mozilla::TrueType and |
| * mozilla::FalseType (or equivalently mozilla::IntegralConstant<bool, true or |
| * false>, or conveniently from mozilla::IsPod for composite types) as needed to |
| * ensure correct IsPod behavior. |
| */ |
| template<typename T> |
| struct IsPod : public FalseType {}; |
| |
| template<> struct IsPod<char> : TrueType {}; |
| template<> struct IsPod<signed char> : TrueType {}; |
| template<> struct IsPod<unsigned char> : TrueType {}; |
| template<> struct IsPod<short> : TrueType {}; |
| template<> struct IsPod<unsigned short> : TrueType {}; |
| template<> struct IsPod<int> : TrueType {}; |
| template<> struct IsPod<unsigned int> : TrueType {}; |
| template<> struct IsPod<long> : TrueType {}; |
| template<> struct IsPod<unsigned long> : TrueType {}; |
| template<> struct IsPod<long long> : TrueType {}; |
| template<> struct IsPod<unsigned long long> : TrueType {}; |
| template<> struct IsPod<bool> : TrueType {}; |
| template<> struct IsPod<float> : TrueType {}; |
| template<> struct IsPod<double> : TrueType {}; |
| template<> struct IsPod<wchar_t> : TrueType {}; |
| template<typename T> struct IsPod<T*> : TrueType {}; |
| |
| namespace detail { |
| |
| template<typename T, bool = IsFloatingPoint<T>::value> |
| struct IsSignedHelper; |
| |
| template<typename T> |
| struct IsSignedHelper<T, true> : TrueType {}; |
| |
| template<typename T> |
| struct IsSignedHelper<T, false> |
| : IntegralConstant<bool, IsArithmetic<T>::value && T(-1) < T(1)> |
| {}; |
| |
| } // namespace detail |
| |
| /** |
| * IsSigned determines whether a type is a signed arithmetic type. |char| is |
| * considered a signed type if it has the same representation as |signed char|. |
| * |
| * Don't use this if the type might be user-defined! You might or might not get |
| * a compile error, depending. |
| * |
| * mozilla::IsSigned<int>::value is true; |
| * mozilla::IsSigned<const unsigned int>::value is false; |
| * mozilla::IsSigned<unsigned char>::value is false; |
| * mozilla::IsSigned<float>::value is true. |
| */ |
| template<typename T> |
| struct IsSigned : detail::IsSignedHelper<T> {}; |
| |
| namespace detail { |
| |
| template<typename T, bool = IsFloatingPoint<T>::value> |
| struct IsUnsignedHelper; |
| |
| template<typename T> |
| struct IsUnsignedHelper<T, true> : FalseType {}; |
| |
| template<typename T> |
| struct IsUnsignedHelper<T, false> |
| : IntegralConstant<bool, |
| IsArithmetic<T>::value && |
| (IsSame<typename RemoveCV<T>::Type, bool>::value || |
| T(1) < T(-1))> |
| {}; |
| |
| } // namespace detail |
| |
| /** |
| * IsUnsigned determines whether a type is an unsigned arithmetic type. |
| * |
| * Don't use this if the type might be user-defined! You might or might not get |
| * a compile error, depending. |
| * |
| * mozilla::IsUnsigned<int>::value is false; |
| * mozilla::IsUnsigned<const unsigned int>::value is true; |
| * mozilla::IsUnsigned<unsigned char>::value is true; |
| * mozilla::IsUnsigned<float>::value is false. |
| */ |
| template<typename T> |
| struct IsUnsigned : detail::IsUnsignedHelper<T> {}; |
| |
| /* 20.9.5 Type property queries [meta.unary.prop.query] */ |
| |
| /* 20.9.6 Relationships between types [meta.rel] */ |
| |
| /** |
| * IsSame tests whether two types are the same type. |
| * |
| * mozilla::IsSame<int, int>::value is true; |
| * mozilla::IsSame<int*, int*>::value is true; |
| * mozilla::IsSame<int, unsigned int>::value is false; |
| * mozilla::IsSame<void, void>::value is true; |
| * mozilla::IsSame<const int, int>::value is false; |
| * mozilla::IsSame<struct S, struct S>::value is true. |
| */ |
| template<typename T, typename U> |
| struct IsSame : FalseType {}; |
| |
| template<typename T> |
| struct IsSame<T, T> : TrueType {}; |
| |
| namespace detail { |
| |
| // The trickery used to implement IsBaseOf here makes it possible to use it for |
| // the cases of private and multiple inheritance. This code was inspired by the |
| // sample code here: |
| // |
| // http://stackoverflow.com/questions/2910979/how-is-base-of-works |
| template<class Base, class Derived> |
| struct BaseOfHelper |
| { |
| public: |
| operator Base*() const; |
| operator Derived*(); |
| }; |
| |
| template<class Base, class Derived> |
| struct BaseOfTester |
| { |
| private: |
| template<class T> |
| static char test(Derived*, T); |
| static int test(Base*, int); |
| |
| public: |
| static const bool value = |
| sizeof(test(BaseOfHelper<Base, Derived>(), int())) == sizeof(char); |
| }; |
| |
| template<class Base, class Derived> |
| struct BaseOfTester<Base, const Derived> |
| { |
| private: |
| template<class T> |
| static char test(Derived*, T); |
| static int test(Base*, int); |
| |
| public: |
| static const bool value = |
| sizeof(test(BaseOfHelper<Base, Derived>(), int())) == sizeof(char); |
| }; |
| |
| template<class Base, class Derived> |
| struct BaseOfTester<Base&, Derived&> : FalseType {}; |
| |
| template<class Type> |
| struct BaseOfTester<Type, Type> : TrueType {}; |
| |
| template<class Type> |
| struct BaseOfTester<Type, const Type> : TrueType {}; |
| |
| } /* namespace detail */ |
| |
| /* |
| * IsBaseOf allows to know whether a given class is derived from another. |
| * |
| * Consider the following class definitions: |
| * |
| * class A {}; |
| * class B : public A {}; |
| * class C {}; |
| * |
| * mozilla::IsBaseOf<A, B>::value is true; |
| * mozilla::IsBaseOf<A, C>::value is false; |
| */ |
| template<class Base, class Derived> |
| struct IsBaseOf |
| : IntegralConstant<bool, detail::BaseOfTester<Base, Derived>::value> |
| {}; |
| |
| namespace detail { |
| |
| template<typename From, typename To> |
| struct ConvertibleTester |
| { |
| private: |
| static From create(); |
| |
| template<typename From1, typename To1> |
| static char test(To to); |
| |
| template<typename From1, typename To1> |
| static int test(...); |
| |
| public: |
| static const bool value = |
| sizeof(test<From, To>(create())) == sizeof(char); |
| }; |
| |
| } // namespace detail |
| |
| /** |
| * IsConvertible determines whether a value of type From will implicitly convert |
| * to a value of type To. For example: |
| * |
| * struct A {}; |
| * struct B : public A {}; |
| * struct C {}; |
| * |
| * mozilla::IsConvertible<A, A>::value is true; |
| * mozilla::IsConvertible<A*, A*>::value is true; |
| * mozilla::IsConvertible<B, A>::value is true; |
| * mozilla::IsConvertible<B*, A*>::value is true; |
| * mozilla::IsConvertible<C, A>::value is false; |
| * mozilla::IsConvertible<A, C>::value is false; |
| * mozilla::IsConvertible<A*, C*>::value is false; |
| * mozilla::IsConvertible<C*, A*>::value is false. |
| * |
| * For obscure reasons, you can't use IsConvertible when the types being tested |
| * are related through private inheritance, and you'll get a compile error if |
| * you try. Just don't do it! |
| */ |
| template<typename From, typename To> |
| struct IsConvertible |
| : IntegralConstant<bool, detail::ConvertibleTester<From, To>::value> |
| {}; |
| |
| /* 20.9.7 Transformations between types [meta.trans] */ |
| |
| /* 20.9.7.1 Const-volatile modifications [meta.trans.cv] */ |
| |
| /** |
| * RemoveConst removes top-level const qualifications on a type. |
| * |
| * mozilla::RemoveConst<int>::Type is int; |
| * mozilla::RemoveConst<const int>::Type is int; |
| * mozilla::RemoveConst<const int*>::Type is const int*; |
| * mozilla::RemoveConst<int* const>::Type is int*. |
| */ |
| template<typename T> |
| struct RemoveConst |
| { |
| typedef T Type; |
| }; |
| |
| template<typename T> |
| struct RemoveConst<const T> |
| { |
| typedef T Type; |
| }; |
| |
| /** |
| * RemoveVolatile removes top-level volatile qualifications on a type. |
| * |
| * mozilla::RemoveVolatile<int>::Type is int; |
| * mozilla::RemoveVolatile<volatile int>::Type is int; |
| * mozilla::RemoveVolatile<volatile int*>::Type is volatile int*; |
| * mozilla::RemoveVolatile<int* volatile>::Type is int*. |
| */ |
| template<typename T> |
| struct RemoveVolatile |
| { |
| typedef T Type; |
| }; |
| |
| template<typename T> |
| struct RemoveVolatile<volatile T> |
| { |
| typedef T Type; |
| }; |
| |
| /** |
| * RemoveCV removes top-level const and volatile qualifications on a type. |
| * |
| * mozilla::RemoveCV<int>::Type is int; |
| * mozilla::RemoveCV<const int>::Type is int; |
| * mozilla::RemoveCV<volatile int>::Type is int; |
| * mozilla::RemoveCV<int* const volatile>::Type is int*. |
| */ |
| template<typename T> |
| struct RemoveCV |
| { |
| typedef typename RemoveConst<typename RemoveVolatile<T>::Type>::Type Type; |
| }; |
| |
| /* 20.9.7.2 Reference modifications [meta.trans.ref] */ |
| |
| /* 20.9.7.3 Sign modifications [meta.trans.sign] */ |
| |
| template<bool B, typename T = void> |
| struct EnableIf; |
| |
| template<bool Condition, typename A, typename B> |
| struct Conditional; |
| |
| namespace detail { |
| |
| template<bool MakeConst, typename T> |
| struct WithC : Conditional<MakeConst, const T, T> |
| {}; |
| |
| template<bool MakeVolatile, typename T> |
| struct WithV : Conditional<MakeVolatile, volatile T, T> |
| {}; |
| |
| |
| template<bool MakeConst, bool MakeVolatile, typename T> |
| struct WithCV : WithC<MakeConst, typename WithV<MakeVolatile, T>::Type> |
| {}; |
| |
| template<typename T> |
| struct CorrespondingSigned; |
| |
| template<> |
| struct CorrespondingSigned<char> { typedef signed char Type; }; |
| template<> |
| struct CorrespondingSigned<unsigned char> { typedef signed char Type; }; |
| template<> |
| struct CorrespondingSigned<unsigned short> { typedef short Type; }; |
| template<> |
| struct CorrespondingSigned<unsigned int> { typedef int Type; }; |
| template<> |
| struct CorrespondingSigned<unsigned long> { typedef long Type; }; |
| template<> |
| struct CorrespondingSigned<unsigned long long> { typedef long long Type; }; |
| |
| template<typename T, |
| typename CVRemoved = typename RemoveCV<T>::Type, |
| bool IsSignedIntegerType = IsSigned<CVRemoved>::value && |
| !IsSame<char, CVRemoved>::value> |
| struct MakeSigned; |
| |
| template<typename T, typename CVRemoved> |
| struct MakeSigned<T, CVRemoved, true> |
| { |
| typedef T Type; |
| }; |
| |
| template<typename T, typename CVRemoved> |
| struct MakeSigned<T, CVRemoved, false> |
| : WithCV<IsConst<T>::value, IsVolatile<T>::value, |
| typename CorrespondingSigned<CVRemoved>::Type> |
| {}; |
| |
| } // namespace detail |
| |
| /** |
| * MakeSigned produces the corresponding signed integer type for a given |
| * integral type T, with the const/volatile qualifiers of T. T must be a |
| * possibly-const/volatile-qualified integral type that isn't bool. |
| * |
| * If T is already a signed integer type (not including char!), then T is |
| * produced. |
| * |
| * Otherwise, if T is an unsigned integer type, the signed variety of T, with |
| * T's const/volatile qualifiers, is produced. |
| * |
| * Otherwise, the integral type of the same size as T, with the lowest rank, |
| * with T's const/volatile qualifiers, is produced. (This basically only acts |
| * to produce signed char when T = char.) |
| * |
| * mozilla::MakeSigned<unsigned long>::Type is signed long; |
| * mozilla::MakeSigned<volatile int>::Type is volatile int; |
| * mozilla::MakeSigned<const unsigned short>::Type is const signed short; |
| * mozilla::MakeSigned<const char>::Type is const signed char; |
| * mozilla::MakeSigned<bool> is an error; |
| * mozilla::MakeSigned<void*> is an error. |
| */ |
| template<typename T> |
| struct MakeSigned |
| : EnableIf<IsIntegral<T>::value && !IsSame<bool, typename RemoveCV<T>::Type>::value, |
| typename detail::MakeSigned<T> |
| >::Type |
| {}; |
| |
| namespace detail { |
| |
| template<typename T> |
| struct CorrespondingUnsigned; |
| |
| template<> |
| struct CorrespondingUnsigned<char> { typedef unsigned char Type; }; |
| template<> |
| struct CorrespondingUnsigned<signed char> { typedef unsigned char Type; }; |
| template<> |
| struct CorrespondingUnsigned<short> { typedef unsigned short Type; }; |
| template<> |
| struct CorrespondingUnsigned<int> { typedef unsigned int Type; }; |
| template<> |
| struct CorrespondingUnsigned<long> { typedef unsigned long Type; }; |
| template<> |
| struct CorrespondingUnsigned<long long> { typedef unsigned long long Type; }; |
| |
| |
| template<typename T, |
| typename CVRemoved = typename RemoveCV<T>::Type, |
| bool IsUnsignedIntegerType = IsUnsigned<CVRemoved>::value && |
| !IsSame<char, CVRemoved>::value> |
| struct MakeUnsigned; |
| |
| template<typename T, typename CVRemoved> |
| struct MakeUnsigned<T, CVRemoved, true> |
| { |
| typedef T Type; |
| }; |
| |
| template<typename T, typename CVRemoved> |
| struct MakeUnsigned<T, CVRemoved, false> |
| : WithCV<IsConst<T>::value, IsVolatile<T>::value, |
| typename CorrespondingUnsigned<CVRemoved>::Type> |
| {}; |
| |
| } // namespace detail |
| |
| /** |
| * MakeUnsigned produces the corresponding unsigned integer type for a given |
| * integral type T, with the const/volatile qualifiers of T. T must be a |
| * possibly-const/volatile-qualified integral type that isn't bool. |
| * |
| * If T is already an unsigned integer type (not including char!), then T is |
| * produced. |
| * |
| * Otherwise, if T is an signed integer type, the unsigned variety of T, with |
| * T's const/volatile qualifiers, is produced. |
| * |
| * Otherwise, the unsigned integral type of the same size as T, with the lowest |
| * rank, with T's const/volatile qualifiers, is produced. (This basically only |
| * acts to produce unsigned char when T = char.) |
| * |
| * mozilla::MakeUnsigned<signed long>::Type is unsigned long; |
| * mozilla::MakeUnsigned<volatile unsigned int>::Type is volatile unsigned int; |
| * mozilla::MakeUnsigned<const signed short>::Type is const unsigned short; |
| * mozilla::MakeUnsigned<const char>::Type is const unsigned char; |
| * mozilla::MakeUnsigned<bool> is an error; |
| * mozilla::MakeUnsigned<void*> is an error. |
| */ |
| template<typename T> |
| struct MakeUnsigned |
| : EnableIf<IsIntegral<T>::value && !IsSame<bool, typename RemoveCV<T>::Type>::value, |
| typename detail::MakeUnsigned<T> |
| >::Type |
| {}; |
| |
| /* 20.9.7.4 Array modifications [meta.trans.arr] */ |
| |
| /* 20.9.7.5 Pointer modifications [meta.trans.ptr] */ |
| |
| /* 20.9.7.6 Other transformations [meta.trans.other] */ |
| |
| /** |
| * EnableIf is a struct containing a typedef of T if and only if B is true. |
| * |
| * mozilla::EnableIf<true, int>::Type is int; |
| * mozilla::EnableIf<false, int>::Type is a compile-time error. |
| * |
| * Use this template to implement SFINAE-style (Substitution Failure Is not An |
| * Error) requirements. For example, you might use it to impose a restriction |
| * on a template parameter: |
| * |
| * template<typename T> |
| * class PodVector // vector optimized to store POD (memcpy-able) types |
| * { |
| * EnableIf<IsPod<T>::value, T>::Type* vector; |
| * size_t length; |
| * ... |
| * }; |
| */ |
| template<bool B, typename T> |
| struct EnableIf |
| {}; |
| |
| template<typename T> |
| struct EnableIf<true, T> |
| { |
| typedef T Type; |
| }; |
| |
| /** |
| * Conditional selects a class between two, depending on a given boolean value. |
| * |
| * mozilla::Conditional<true, A, B>::Type is A; |
| * mozilla::Conditional<false, A, B>::Type is B; |
| */ |
| template<bool Condition, typename A, typename B> |
| struct Conditional |
| { |
| typedef A Type; |
| }; |
| |
| template<class A, class B> |
| struct Conditional<false, A, B> |
| { |
| typedef B Type; |
| }; |
| |
| } /* namespace mozilla */ |
| |
| #endif /* mozilla_TypeTraits_h_ */ |