Bug 953296 - Implement mozilla::IsClass and mozilla::IsEmpty. r=jcranmer
authorJeff Walden <jwalden@mit.edu>
Mon, 30 Dec 2013 01:07:39 -0600
changeset 162171 25252ec3c38e5d33eb2a3d068784ee684884b453
parent 162170 e40099b1ffa219bb620dd565763b7f906d7144cf
child 162172 96440758eb96ae5b10775b2a8b60d5e60b568163
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjcranmer
bugs953296
milestone29.0a1
Bug 953296 - Implement mozilla::IsClass and mozilla::IsEmpty. r=jcranmer
mfbt/TypeTraits.h
mfbt/tests/TestTypeTraits.cpp
--- a/mfbt/TypeTraits.h
+++ b/mfbt/TypeTraits.h
@@ -164,16 +164,43 @@ struct IsEnumHelper
  * mozilla::IsEnum<enum S*>::value is false;
  * mozilla::IsEnum<int>::value is false;
  */
 template<typename T>
 struct IsEnum
   : detail::IsEnumHelper<typename RemoveCV<T>::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<typename T>
+struct IsClassHelper
+  : IntegralConstant<bool, __is_class(T)>
+{};
+
+} // namespace detail
+
+/**
+ * IsClass determines whether a type is a class type (but not a union).
+ *
+ * struct S {};
+ * union U {};
+ * mozilla::IsClass<int>::value is false;
+ * mozilla::IsClass<const S>::value is true;
+ * mozilla::IsClass<U>::value is false;
+ */
+template<typename T>
+struct IsClass
+  : detail::IsClassHelper<typename RemoveCV<T>::Type>
+{};
+
 /* 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;
@@ -240,16 +267,74 @@ template<> struct IsPod<double>         
 template<> struct IsPod<wchar_t>            : TrueType {};
 #ifdef MOZ_CHAR16_IS_NOT_WCHAR
 template<> struct IsPod<char16_t>           : TrueType {};
 #endif
 template<typename T> struct IsPod<T*>       : 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<typename T>
+struct IsEmptyHelper
+  : IntegralConstant<bool, IsClass<T>::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<int>::value, "not a class => not empty");
+ *   union U1 { int x; };
+ *   static_assert(!mozilla::IsEmpty<U1>::value, "not a class => not empty");
+ *   struct E1 {};
+ *   struct E2 { int : 0 };
+ *   struct E3 : E1 {};
+ *   struct E4 : E2 {};
+ *   static_assert(mozilla::IsEmpty<E1>::value &&
+ *                 mozilla::IsEmpty<E2>::value &&
+ *                 mozilla::IsEmpty<E3>::value &&
+ *                 mozilla::IsEmpty<E4>::value,
+ *                 "all empty");
+ *   union U2 { E1 e1; };
+ *   static_assert(!mozilla::IsEmpty<U2>::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<NE1>::value &&
+ *                 !mozilla::IsEmpty<NE2>::value &&
+ *                 !mozilla::IsEmpty<NE3>::value &&
+ *                 !mozilla::IsEmpty<NE4>::value,
+ *                 "all empty");
+ */
+template<typename T>
+struct IsEmpty : detail::IsEmptyHelper<typename RemoveCV<T>::Type>
+{};
+
+
+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>
--- a/mfbt/tests/TestTypeTraits.cpp
+++ b/mfbt/tests/TestTypeTraits.cpp
@@ -2,23 +2,64 @@
 /* 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/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/TypeTraits.h"
 
 using mozilla::IsBaseOf;
+using mozilla::IsClass;
 using mozilla::IsConvertible;
+using mozilla::IsEmpty;
 using mozilla::IsSame;
 using mozilla::IsSigned;
 using mozilla::IsUnsigned;
 using mozilla::MakeSigned;
 using mozilla::MakeUnsigned;
 
+struct S1 {};
+union U1 { int x; };
+
+static_assert(!IsClass<int>::value, "int isn't a class");
+static_assert(IsClass<const S1>::value, "S is a class");
+static_assert(!IsClass<U1>::value, "U isn't a class");
+
+static_assert(!mozilla::IsEmpty<int>::value, "not a class => not empty");
+static_assert(!mozilla::IsEmpty<bool[5]>::value, "not a class => not empty");
+
+static_assert(!mozilla::IsEmpty<U1>::value, "not a class => not empty");
+
+struct E1 {};
+struct E2 { int : 0; };
+struct E3 : E1 {};
+struct E4 : E2 {};
+
+static_assert(IsEmpty<const volatile S1>::value, "S should be empty");
+
+static_assert(mozilla::IsEmpty<E1>::value &&
+              mozilla::IsEmpty<E2>::value &&
+              mozilla::IsEmpty<E3>::value &&
+              mozilla::IsEmpty<E4>::value,
+              "all empty");
+
+union U2 { E1 e1; };
+static_assert(!mozilla::IsEmpty<U2>::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<NE1>::value &&
+              !mozilla::IsEmpty<NE2>::value &&
+              !mozilla::IsEmpty<NE3>::value &&
+              !mozilla::IsEmpty<NE4>::value,
+              "all empty");
+
 static_assert(!IsSigned<bool>::value, "bool shouldn't be signed");
 static_assert(IsUnsigned<bool>::value, "bool should be unsigned");
 
 static_assert(!IsSigned<const bool>::value, "const bool shouldn't be signed");
 static_assert(IsUnsigned<const bool>::value, "const bool should be unsigned");
 
 static_assert(!IsSigned<volatile bool>::value, "volatile bool shouldn't be signed");
 static_assert(IsUnsigned<volatile bool>::value, "volatile bool should be unsigned");