/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 #include "mozilla/Types.h" /* * These traits are approximate copies of the traits and semantics from C++11's * 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 namespace mozilla { /* Forward declarations. */ template struct RemoveCV; /* 20.9.3 Helper classes [meta.help] */ /** * Helper class used as a base for various type traits, exposed publicly * because exposes it as well. */ template struct IntegralConstant { static const T value = Value; typedef T ValueType; typedef IntegralConstant Type; }; /** Convenient aliases. */ typedef IntegralConstant TrueType; typedef IntegralConstant FalseType; /* 20.9.4 Unary type traits [meta.unary] */ /* 20.9.4.1 Primary type categories [meta.unary.cat] */ namespace detail { template struct IsIntegralHelper : FalseType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; template<> struct IsIntegralHelper : TrueType {}; #ifdef MOZ_CHAR16_IS_NOT_WCHAR template<> struct IsIntegralHelper : TrueType {}; #endif } /* namespace detail */ /** * IsIntegral determines whether a type is an integral type. * * mozilla::IsIntegral::value is true; * mozilla::IsIntegral::value is true; * mozilla::IsIntegral::value is true; * mozilla::IsIntegral::value is false; * mozilla::IsIntegral::value is false; * * Note that the behavior of IsIntegral on char16_t and char32_t is * unspecified. */ template struct IsIntegral : detail::IsIntegralHelper::Type> {}; template struct IsSame; namespace detail { template struct IsFloatingPointHelper : IntegralConstant::value || IsSame::value || IsSame::value> {}; } // namespace detail /** * IsFloatingPoint determines whether a type is a floating point type (float, * double, long double). * * mozilla::IsFloatingPoint::value is false; * mozilla::IsFloatingPoint::value is true; * mozilla::IsFloatingPoint::value is true; * mozilla::IsFloatingPoint::value is false. */ template struct IsFloatingPoint : detail::IsFloatingPointHelper::Type> {}; namespace detail { template struct IsArrayHelper : FalseType {}; template struct IsArrayHelper : TrueType {}; template struct IsArrayHelper : TrueType {}; } // namespace detail /** * IsArray determines whether a type is an array type, of known or unknown * length. * * mozilla::IsArray::value is false; * mozilla::IsArray::value is true; * mozilla::IsArray::value is true. */ template struct IsArray : detail::IsArrayHelper::Type> {}; /** * IsPointer determines whether a type is a pointer type (but not a pointer-to- * member type). * * mozilla::IsPointer::value is true; * mozilla::IsPointer::value is true; * mozilla::IsPointer::value is true; * mozilla::IsPointer::value is false; * mozilla::IsPointer::value is false. */ template struct IsPointer : FalseType {}; template struct IsPointer : TrueType {}; /** * IsLvalueReference determines whether a type is an lvalue reference. * * mozilla::IsLvalueReference::value is false; * mozilla::IsLvalueReference::value is false; * mozilla::IsLvalueReference::value is false; * mozilla::IsLvalueReference::value is false; * mozilla::IsLvalueReference::value is false; * mozilla::IsLvalueReference::value is true; * mozilla::IsLvalueReference::value is false. */ template struct IsLvalueReference : FalseType {}; template struct IsLvalueReference : TrueType {}; /** * IsRvalueReference determines whether a type is an rvalue reference. * * mozilla::IsRvalueReference::value is false; * mozilla::IsRvalueReference::value is false; * mozilla::IsRvalueReference::value is false; * mozilla::IsRvalueReference::value is false; * mozilla::IsRvalueReference::value is false; * mozilla::IsRvalueReference::value is false; * mozilla::IsRvalueReference::value is true. */ template struct IsRvalueReference : FalseType {}; template struct IsRvalueReference : TrueType {}; namespace detail { // __is_enum is a supported extension across all of our supported compilers. template struct IsEnumHelper : IntegralConstant {}; } // namespace detail /** * IsEnum determines whether a type is an enum type. * * mozilla::IsEnum::value is true; * mozilla::IsEnum::value is false; * mozilla::IsEnum::value is false; */ template struct IsEnum : detail::IsEnumHelper::Type> {}; namespace detail { // __is_class is a supported extension across all of our supported compilers: // http://llvm.org/releases/3.0/docs/ClangReleaseNotes.html // http://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/Type-Traits.html#Type-Traits // http://msdn.microsoft.com/en-us/library/ms177194%28v=vs.100%29.aspx template struct IsClassHelper : IntegralConstant {}; } // namespace detail /** * IsClass determines whether a type is a class type (but not a union). * * struct S {}; * union U {}; * mozilla::IsClass::value is false; * mozilla::IsClass::value is true; * mozilla::IsClass::value is false; */ template struct IsClass : detail::IsClassHelper::Type> {}; /* 20.9.4.2 Composite type traits [meta.unary.comp] */ /** * IsReference determines whether a type is an lvalue or rvalue reference. * * mozilla::IsReference::value is false; * mozilla::IsReference::value is false; * mozilla::IsReference::value is true; * mozilla::IsReference::value is false; * mozilla::IsReference::value is true; * mozilla::IsReference::value is false; * mozilla::IsReference::value is false; * mozilla::IsReference::value is true; * mozilla::IsReference::value is true; * mozilla::IsReference::value is true. */ template struct IsReference : IntegralConstant::value || IsRvalueReference::value> {}; /** * IsArithmetic determines whether a type is arithmetic. A type is arithmetic * iff it is an integral type or a floating point type. * * mozilla::IsArithmetic::value is true; * mozilla::IsArithmetic::value is true; * mozilla::IsArithmetic::value is false. */ template struct IsArithmetic : IntegralConstant::value || IsFloatingPoint::value> {}; /* 20.9.4.3 Type properties [meta.unary.prop] */ /** * IsConst determines whether a type is const or not. * * mozilla::IsConst::value is false; * mozilla::IsConst::value is true; * mozilla::IsConst::value is false. */ template struct IsConst : FalseType {}; template struct IsConst : TrueType {}; /** * IsVolatile determines whether a type is volatile or not. * * mozilla::IsVolatile::value is false; * mozilla::IsVolatile::value is true; * mozilla::IsVolatile::value is false. */ template struct IsVolatile : FalseType {}; template struct IsVolatile : 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, or conveniently from mozilla::IsPod for composite types) as needed to * ensure correct IsPod behavior. */ template struct IsPod : public FalseType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; template<> struct IsPod : TrueType {}; #ifdef MOZ_CHAR16_IS_NOT_WCHAR template<> struct IsPod : TrueType {}; #endif template struct IsPod : TrueType {}; namespace detail { // __is_empty is a supported extension across all of our supported compilers: // http://llvm.org/releases/3.0/docs/ClangReleaseNotes.html // http://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/Type-Traits.html#Type-Traits // http://msdn.microsoft.com/en-us/library/ms177194%28v=vs.100%29.aspx template struct IsEmptyHelper : IntegralConstant::value && __is_empty(T)> {}; } // namespace detail /** * IsEmpty determines whether a type is a class (but not a union) that is empty. * * A class is empty iff it and all its base classes have no non-static data * members (except bit-fields of length 0) and no virtual member functions, and * no base class is empty or a virtual base class. * * Intuitively, empty classes don't have any data that has to be stored in * instances of those classes. (The size of the class must still be non-zero, * because distinct array elements of any type must have different addresses. * However, if the Empty Base Optimization is implemented by the compiler [most * compilers implement it, and in certain cases C++11 requires it], the size of * a class inheriting from an empty |Base| class need not be inflated by * |sizeof(Base)|.) And intuitively, non-empty classes have data members and/or * vtable pointers that must be stored in each instance for proper behavior. * * static_assert(!mozilla::IsEmpty::value, "not a class => not empty"); * union U1 { int x; }; * static_assert(!mozilla::IsEmpty::value, "not a class => not empty"); * struct E1 {}; * struct E2 { int : 0 }; * struct E3 : E1 {}; * struct E4 : E2 {}; * static_assert(mozilla::IsEmpty::value && * mozilla::IsEmpty::value && * mozilla::IsEmpty::value && * mozilla::IsEmpty::value, * "all empty"); * union U2 { E1 e1; }; * static_assert(!mozilla::IsEmpty::value, "not a class => not empty"); * struct NE1 { int x; }; * struct NE2 : virtual E1 {}; * struct NE3 : E2 { virtual ~NE3() {} }; * struct NE4 { virtual void f() {} }; * static_assert(!mozilla::IsEmpty::value && * !mozilla::IsEmpty::value && * !mozilla::IsEmpty::value && * !mozilla::IsEmpty::value, * "all empty"); */ template struct IsEmpty : detail::IsEmptyHelper::Type> {}; namespace detail { template::value> struct IsSignedHelper; template struct IsSignedHelper : TrueType {}; template struct IsSignedHelper : IntegralConstant::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::value is true; * mozilla::IsSigned::value is false; * mozilla::IsSigned::value is false; * mozilla::IsSigned::value is true. */ template struct IsSigned : detail::IsSignedHelper {}; namespace detail { template::value> struct IsUnsignedHelper; template struct IsUnsignedHelper : FalseType {}; template struct IsUnsignedHelper : IntegralConstant::value && (IsSame::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::value is false; * mozilla::IsUnsigned::value is true; * mozilla::IsUnsigned::value is true; * mozilla::IsUnsigned::value is false. */ template struct IsUnsigned : detail::IsUnsignedHelper {}; /* 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::value is true; * mozilla::IsSame::value is true; * mozilla::IsSame::value is false; * mozilla::IsSame::value is true; * mozilla::IsSame::value is false; * mozilla::IsSame::value is true. */ template struct IsSame : FalseType {}; template struct IsSame : 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 struct BaseOfHelper { public: operator Base*() const; operator Derived*(); }; template struct BaseOfTester { private: template static char test(Derived*, T); static int test(Base*, int); public: static const bool value = sizeof(test(BaseOfHelper(), int())) == sizeof(char); }; template struct BaseOfTester { private: template static char test(Derived*, T); static int test(Base*, int); public: static const bool value = sizeof(test(BaseOfHelper(), int())) == sizeof(char); }; template struct BaseOfTester : FalseType {}; template struct BaseOfTester : TrueType {}; template struct BaseOfTester : 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::value is true; * mozilla::IsBaseOf::value is false; */ template struct IsBaseOf : IntegralConstant::value> {}; namespace detail { template struct ConvertibleTester { private: static From create(); template static char test(To to); template static int test(...); public: static const bool value = sizeof(test(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::value is true; * mozilla::IsConvertible::value is true; * mozilla::IsConvertible::value is true; * mozilla::IsConvertible::value is true; * mozilla::IsConvertible::value is false; * mozilla::IsConvertible::value is false; * mozilla::IsConvertible::value is false; * mozilla::IsConvertible::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 struct IsConvertible : IntegralConstant::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::Type is int; * mozilla::RemoveConst::Type is int; * mozilla::RemoveConst::Type is const int*; * mozilla::RemoveConst::Type is int*. */ template struct RemoveConst { typedef T Type; }; template struct RemoveConst { typedef T Type; }; /** * RemoveVolatile removes top-level volatile qualifications on a type. * * mozilla::RemoveVolatile::Type is int; * mozilla::RemoveVolatile::Type is int; * mozilla::RemoveVolatile::Type is volatile int*; * mozilla::RemoveVolatile::Type is int*. */ template struct RemoveVolatile { typedef T Type; }; template struct RemoveVolatile { typedef T Type; }; /** * RemoveCV removes top-level const and volatile qualifications on a type. * * mozilla::RemoveCV::Type is int; * mozilla::RemoveCV::Type is int; * mozilla::RemoveCV::Type is int; * mozilla::RemoveCV::Type is int*. */ template struct RemoveCV { typedef typename RemoveConst::Type>::Type Type; }; /* 20.9.7.2 Reference modifications [meta.trans.ref] */ /** * Converts reference types to the underlying types. * * mozilla::RemoveReference::Type is T; * mozilla::RemoveReference::Type is T; * mozilla::RemoveReference::Type is T; */ template struct RemoveReference { typedef T Type; }; template struct RemoveReference { typedef T Type; }; template struct RemoveReference { typedef T Type; }; /* 20.9.7.3 Sign modifications [meta.trans.sign] */ template struct EnableIf; template struct Conditional; namespace detail { template struct WithC : Conditional {}; template struct WithV : Conditional {}; template struct WithCV : WithC::Type> {}; template struct CorrespondingSigned; template<> struct CorrespondingSigned { typedef signed char Type; }; template<> struct CorrespondingSigned { typedef signed char Type; }; template<> struct CorrespondingSigned { typedef short Type; }; template<> struct CorrespondingSigned { typedef int Type; }; template<> struct CorrespondingSigned { typedef long Type; }; template<> struct CorrespondingSigned { typedef long long Type; }; template::Type, bool IsSignedIntegerType = IsSigned::value && !IsSame::value> struct MakeSigned; template struct MakeSigned { typedef T Type; }; template struct MakeSigned : WithCV::value, IsVolatile::value, typename CorrespondingSigned::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::Type is signed long; * mozilla::MakeSigned::Type is volatile int; * mozilla::MakeSigned::Type is const signed short; * mozilla::MakeSigned::Type is const signed char; * mozilla::MakeSigned is an error; * mozilla::MakeSigned is an error. */ template struct MakeSigned : EnableIf::value && !IsSame::Type>::value, typename detail::MakeSigned >::Type {}; namespace detail { template struct CorrespondingUnsigned; template<> struct CorrespondingUnsigned { typedef unsigned char Type; }; template<> struct CorrespondingUnsigned { typedef unsigned char Type; }; template<> struct CorrespondingUnsigned { typedef unsigned short Type; }; template<> struct CorrespondingUnsigned { typedef unsigned int Type; }; template<> struct CorrespondingUnsigned { typedef unsigned long Type; }; template<> struct CorrespondingUnsigned { typedef unsigned long long Type; }; template::Type, bool IsUnsignedIntegerType = IsUnsigned::value && !IsSame::value> struct MakeUnsigned; template struct MakeUnsigned { typedef T Type; }; template struct MakeUnsigned : WithCV::value, IsVolatile::value, typename CorrespondingUnsigned::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::Type is unsigned long; * mozilla::MakeUnsigned::Type is volatile unsigned int; * mozilla::MakeUnsigned::Type is const unsigned short; * mozilla::MakeUnsigned::Type is const unsigned char; * mozilla::MakeUnsigned is an error; * mozilla::MakeUnsigned is an error. */ template struct MakeUnsigned : EnableIf::value && !IsSame::Type>::value, typename detail::MakeUnsigned >::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::Type is int; * mozilla::EnableIf::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 * class PodVector // vector optimized to store POD (memcpy-able) types * { * EnableIf::value, T>::Type* vector; * size_t length; * ... * }; */ template struct EnableIf {}; template struct EnableIf { typedef T Type; }; /** * Conditional selects a class between two, depending on a given boolean value. * * mozilla::Conditional::Type is A; * mozilla::Conditional::Type is B; */ template struct Conditional { typedef A Type; }; template struct Conditional { typedef B Type; }; } /* namespace mozilla */ #endif /* mozilla_TypeTraits_h */