Bug 802806 - Make mfbt's IsBaseOf work with multiple inheritance; r=Waldo
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 17 Oct 2012 17:37:53 -0400
changeset 118701 9806c99965fbf1b5770813b4c6eb4c6dcb3413e4
parent 118700 2030c47a8e69afb3f88c55d19bbf0dee7734c8c1
child 118702 4c979ca3eb7a1ca1ecd7b671ec61b6af9aa4de43
push id1997
push userakeybl@mozilla.com
push dateMon, 07 Jan 2013 21:25:26 +0000
treeherdermozilla-beta@4baf45cdcf21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs802806
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 802806 - Make mfbt's IsBaseOf work with multiple inheritance; r=Waldo
mfbt/TypeTraits.h
mfbt/tests/TestTypeTraits.cpp
--- a/mfbt/TypeTraits.h
+++ b/mfbt/TypeTraits.h
@@ -4,38 +4,92 @@
 
 /* Template-based metaprogramming and type-testing facilities. */
 
 #ifndef mozilla_TypeTraits_h_
 #define mozilla_TypeTraits_h_
 
 namespace mozilla {
 
+namespace detail {
+
+/**
+ * The trickery used to implement IsBaseOf here makes it possible to use it for
+ * the cases of multiple inheritence.  This code was inspired by the sample code
+ * here:
+ *
+ * http://stackoverflow.com/questions/2910979/how-is-base-of-works
+ */
+template<class Base, class Derived>
+class IsBaseOfHelper
+{
+  public:
+    operator Base*() const;
+    operator Derived*();
+};
+
+} /* 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>
 class IsBaseOf
 {
   private:
-    static char test(Base* b);
-    static int test(...);
+    template<class T>
+    static char test(Derived*, T);
+    static int test(Base*, int);
+
+  public:
+    static const bool value =
+      sizeof(test(detail::IsBaseOfHelper<Base, Derived>(), int())) == sizeof(char);
+};
+
+template<class Base, class Derived>
+class IsBaseOf<Base, const Derived>
+{
+  private:
+    template<class T>
+    static char test(Derived*, T);
+    static int test(Base*, int);
 
   public:
     static const bool value =
-      sizeof(test(static_cast<Derived*>(0))) == sizeof(char);
+      sizeof(test(detail::IsBaseOfHelper<Base, Derived>(), int())) == sizeof(char);
+};
+
+template<class Base, class Derived>
+class IsBaseOf<Base&, Derived&>
+{
+  public:
+    static const bool value = false;
+};
+
+template<class Type>
+class IsBaseOf<Type, Type>
+{
+  public:
+    static const bool value = true;
+};
+
+template<class Type>
+class IsBaseOf<Type, const Type>
+{
+  public:
+    static const bool value = true;
 };
 
 /*
  * 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 {};
--- a/mfbt/tests/TestTypeTraits.cpp
+++ b/mfbt/tests/TestTypeTraits.cpp
@@ -1,22 +1,73 @@
 /* -*- 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/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/TypeTraits.h"
 
+using mozilla::IsBaseOf;
 using mozilla::IsConvertible;
 
+namespace CPlusPlus11IsBaseOf {
+
+// Adapted from C++11 ยง 20.9.6.
+struct B {};
+struct B1 : B {};
+struct B2 : B {};
+struct D : private B1, private B2 {};
+
+static void
+StandardIsBaseOfTests()
+{
+  MOZ_ASSERT((IsBaseOf<B, D>::value) == true);
+  MOZ_ASSERT((IsBaseOf<const B, D>::value) == true);
+  MOZ_ASSERT((IsBaseOf<B, const D>::value) == true);
+  MOZ_ASSERT((IsBaseOf<B, const B>::value) == true);
+  MOZ_ASSERT((IsBaseOf<D, B>::value) == false);
+  MOZ_ASSERT((IsBaseOf<B&, D&>::value) == false);
+  MOZ_ASSERT((IsBaseOf<B[3], D[3]>::value) == false);
+  // We fail at the following test.  To fix it, we need to specialize IsBaseOf
+  // for all built-in types.
+  // MOZ_ASSERT((IsBaseOf<int, int>::value) == false);
+}
+
+} /* namespace CPlusPlus11IsBaseOf */
+
 class A { };
 class B : public A { };
 class C : private A { };
 class D { };
+class E : public A { };
+class F : public B, public E { };
+
+static void
+TestIsBaseOf()
+{
+  MOZ_ASSERT((IsBaseOf<A, B>::value),
+             "A is a base of B");
+  MOZ_ASSERT((!IsBaseOf<B, A>::value),
+             "B is not a base of A");
+  MOZ_ASSERT((IsBaseOf<A, C>::value),
+             "A is a base of C");
+  MOZ_ASSERT((!IsBaseOf<C, A>::value),
+             "C is not a base of A");
+  MOZ_ASSERT((IsBaseOf<A, F>::value),
+             "A is a base of F");
+  MOZ_ASSERT((!IsBaseOf<F, A>::value),
+             "F is not a base of A");
+  MOZ_ASSERT((!IsBaseOf<A, D>::value),
+             "A is not a base of D");
+  MOZ_ASSERT((!IsBaseOf<D, A>::value),
+             "D is not a base of A");
+  MOZ_ASSERT((IsBaseOf<B, B>::value),
+             "B is the same as B (and therefore, a base of B)");
+}
 
 static void
 TestIsConvertible()
 {
   // Pointer type convertibility
   MOZ_ASSERT((IsConvertible<A*, A*>::value),
              "A* should convert to A*");
   MOZ_ASSERT((IsConvertible<B*, A*>::value),
@@ -46,10 +97,12 @@ TestIsConvertible()
   //           "C* shouldn't convert to A* (private inheritance)");
   //MOZ_ASSERT((!IsConvertible<C, A>::value),
   //           "C doesn't convert to A (private inheritance)");
 }
 
 int
 main()
 {
+  CPlusPlus11IsBaseOf::StandardIsBaseOfTests();
+  TestIsBaseOf();
   TestIsConvertible();
 }