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 110898 9806c99965fbf1b5770813b4c6eb4c6dcb3413e4
parent 110897 2030c47a8e69afb3f88c55d19bbf0dee7734c8c1
child 110899 4c979ca3eb7a1ca1ecd7b671ec61b6af9aa4de43
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersWaldo
bugs802806
milestone19.0a1
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();
 }