Bug 766347 - Implement mozilla::IsConvertible to detect when a value of one type will convert to a value of another type. r=luke
authorJeff Walden <jwalden@mit.edu>
Tue, 19 Jun 2012 13:55:23 -0700
changeset 97511 fe817bf85f36386d626ed35383ffb9dae2f4ddc5
parent 97510 54a17b76f48868f24dfa261309289af896eca39d
child 97512 b0fd1627db78134185402fc1ab59112cc5ffb697
push id22979
push userryanvm@gmail.com
push dateSun, 24 Jun 2012 02:51:36 +0000
treeherdermozilla-central@cb2904476d14 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs766347
milestone16.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 766347 - Implement mozilla::IsConvertible to detect when a value of one type will convert to a value of another type. r=luke
content/base/src/nsGenericElement.cpp
mfbt/TypeTraits.h
mfbt/tests/Makefile.in
mfbt/tests/TestTypeTraits.cpp
--- a/mfbt/TypeTraits.h
+++ b/mfbt/TypeTraits.h
@@ -22,18 +22,58 @@ namespace mozilla {
  * mozilla::IsBaseOf<A, C>::value is false;
  */
 template<class Base, class Derived>
 class IsBaseOf
 {
   private:
     static char test(Base* b);
     static int test(...);
+
   public:
-    static const bool value = (sizeof(test(static_cast<Derived*>(0))) == sizeof(char));
+    static const bool value =
+      sizeof(test(static_cast<Derived*>(0))) == sizeof(char);
+};
+
+/*
+ * 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
+{
+  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);
 };
 
 /*
  * 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;
  */
--- a/mfbt/tests/Makefile.in
+++ b/mfbt/tests/Makefile.in
@@ -8,16 +8,17 @@ srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 STL_FLAGS =
 
 CPP_UNIT_TESTS = \
   TestCheckedInt.cpp \
+  TestTypeTraits.cpp \
   $(NULL)
 
 # in order to prevent rules.mk from trying to link to libraries that are
 # not available to MFBT, we have to reset these MOZ_GLUE*_LDFLAGS before including it
 # and LIBS_ after including it. For WRAP_LDFLAGS, it shouldn't matter.
 # See later comments in bug 732875.
 
 MOZ_GLUE_PROGRAM_LDFLAGS=
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestTypeTraits.cpp
@@ -0,0 +1,55 @@
+/* -*- 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::IsConvertible;
+
+class A { };
+class B : public A { };
+class C : private A { };
+class D { };
+
+static void
+TestIsConvertible()
+{
+  // Pointer type convertibility
+  MOZ_ASSERT((IsConvertible<A*, A*>::value),
+             "A* should convert to A*");
+  MOZ_ASSERT((IsConvertible<B*, A*>::value),
+             "B* should convert to A*");
+  MOZ_ASSERT((!IsConvertible<A*, B*>::value),
+             "A* shouldn't convert to B*");
+  MOZ_ASSERT((!IsConvertible<A*, C*>::value),
+             "A* shouldn't convert to C*");
+  MOZ_ASSERT((!IsConvertible<A*, D*>::value),
+             "A* shouldn't convert to unrelated D*");
+  MOZ_ASSERT((!IsConvertible<D*, A*>::value),
+             "D* shouldn't convert to unrelated A*");
+
+  // Instance type convertibility
+  MOZ_ASSERT((IsConvertible<A, A>::value),
+             "A is A");
+  MOZ_ASSERT((IsConvertible<B, A>::value),
+             "B converts to A");
+  MOZ_ASSERT((!IsConvertible<D, A>::value),
+             "D and A are unrelated");
+  MOZ_ASSERT((!IsConvertible<A, D>::value),
+             "A and D are unrelated");
+
+  // These cases seem to require C++11 support to properly implement them, so
+  // for now just disable them.
+  //MOZ_ASSERT((!IsConvertible<C*, A*>::value),
+  //           "C* shouldn't convert to A* (private inheritance)");
+  //MOZ_ASSERT((!IsConvertible<C, A>::value),
+  //           "C doesn't convert to A (private inheritance)");
+}
+
+int
+main()
+{
+  TestIsConvertible();
+}