Bug 1246841 - Allow construction of Variant values using type inference. r=waldo
authorSeth Fowler <mark.seth.fowler@gmail.com>
Thu, 25 Feb 2016 14:34:12 -0800
changeset 285647 8901dd8c722185016e3ccc2c4056dedfbf6a033a
parent 285646 a48d1a9ea9063396b6600357a2b5014274f2d777
child 285648 3322ea94dacb87e3934c1c0d0fe560295fcdf9c4
push id30033
push userkwierso@gmail.com
push dateFri, 26 Feb 2016 19:21:05 +0000
treeherdermozilla-central@07438c9bfc83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs1246841
milestone47.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 1246841 - Allow construction of Variant values using type inference. r=waldo
mfbt/Variant.h
--- a/mfbt/Variant.h
+++ b/mfbt/Variant.h
@@ -242,16 +242,49 @@ struct VariantImplementation<N, T, Ts...
       // <...>" then that means that the Matcher::match(T&) overloads
       // are returning different types. They must all return the same
       // Matcher::ReturnType type.
       return Next::match(aMatcher, aV);
     }
   }
 };
 
+/**
+ * AsVariantTemporary stores a value of type T to allow construction of a
+ * Variant value via type inference. Because T is copied and there's no
+ * guarantee that the copy can be elided, AsVariantTemporary is best used with
+ * primitive or very small types.
+ */
+template <typename T>
+struct AsVariantTemporary
+{
+  explicit AsVariantTemporary(const T& aValue)
+    : mValue(aValue)
+  {}
+
+  template<typename U>
+  explicit AsVariantTemporary(U&& aValue)
+    : mValue(Forward<U>(aValue))
+  {}
+
+  AsVariantTemporary(const AsVariantTemporary& aOther)
+    : mValue(aOther.mValue)
+  {}
+
+  AsVariantTemporary(AsVariantTemporary&& aOther)
+    : mValue(Move(aOther.mValue))
+  {}
+
+  AsVariantTemporary() = delete;
+  void operator=(const AsVariantTemporary&) = delete;
+  void operator=(AsVariantTemporary&&) = delete;
+
+  typename RemoveConst<typename RemoveReference<T>::Type>::Type mValue;
+};
+
 } // namespace detail
 
 /**
  * # mozilla::Variant
  *
  * A variant / tagged union / heterogenous disjoint union / sum-type template
  * class. Similar in concept to (but not derived from) `boost::variant`.
  *
@@ -265,16 +298,28 @@ struct VariantImplementation<N, T, Ts...
  * A `mozilla::Variant` instance is constructed (via move or copy) from one of
  * its variant types (ignoring const and references). It does *not* support
  * construction from subclasses of variant types or types that coerce to one of
  * the variant types.
  *
  *     Variant<char, uint32_t> v1('a');
  *     Variant<UniquePtr<A>, B, C> v2(MakeUnique<A>());
  *
+ * Because specifying the full type of a Variant value is often verbose,
+ * AsVariant() can be used to construct a Variant value using type inference in
+ * contexts such as expressions or when returning values from functions. Because
+ * AsVariant() must copy or move the value into a temporary and this cannot
+ * necessarily be elided by the compiler, it's mostly appropriate only for use
+ * with primitive or very small types.
+ *
+ *
+ *     Variant<char, uint32_t> Foo() { return AsVariant('x'); }
+ *     // ...
+ *     Variant<char, uint32_t> v1 = Foo();  // v1 holds char('x').
+ *
  * All access to the contained value goes through type-safe accessors.
  *
  *     void
  *     Foo(Variant<A, B, C> v)
  *     {
  *       if (v.is<A>()) {
  *         A& ref = v.as<A>();
  *         ...
@@ -386,16 +431,29 @@ public:
            // tag, etc.
            typename T = typename detail::SelectVariantType<RefT, Ts...>::Type>
   explicit Variant(RefT&& aT)
     : tag(Impl::template tag<T>())
   {
     new (ptr()) T(Forward<T>(aT));
   }
 
+  /**
+   * Constructs this Variant from an AsVariantTemporary<T> such that T can be
+   * stored in one of the types allowable in this Variant. This is used in the
+   * implementation of AsVariant().
+   */
+  template<typename RefT,
+           typename T = typename detail::SelectVariantType<RefT, Ts...>::Type>
+  MOZ_IMPLICIT Variant(detail::AsVariantTemporary<RefT>&& aValue)
+    : tag(Impl::template tag<T>())
+  {
+    new (ptr()) T(Move(aValue.mValue));
+  }
+
   /** Copy construction. */
   Variant(const Variant& aRhs)
     : tag(aRhs.tag)
   {
     Impl::copyConstruct(ptr(), aRhs);
   }
 
   /** Move construction. */
@@ -416,16 +474,25 @@ public:
   /** Move assignment. */
   Variant& operator=(Variant&& aRhs) {
     MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
     this->~Variant();
     new (this) Variant(Move(aRhs));
     return *this;
   }
 
+  /** Move assignment from AsVariant(). */
+  template <typename T>
+  Variant& operator=(detail::AsVariantTemporary<T>&& aValue)
+  {
+    this->~Variant();
+    new (this) Variant(Move(aValue));
+    return *this;
+  }
+
   ~Variant()
   {
     Impl::destroy(*this);
   }
 
   /** Check which variant type is currently contained. */
   template<typename T>
   bool is() const {
@@ -497,11 +564,31 @@ public:
   /**  Match on a mutable non-const reference. */
   template<typename Matcher>
   typename Matcher::ReturnType
   match(Matcher& aMatcher) {
     return Impl::match(aMatcher, *this);
   }
 };
 
+/*
+ * AsVariant() is used to construct a Variant<T,...> value containing the
+ * provided T value using type inference. It can be used to construct Variant
+ * values in expressions or return them from functions without specifying the
+ * entire Variant type.
+ *
+ * Because AsVariant() must copy or move the value into a temporary and this
+ * cannot necessarily be elided by the compiler, it's mostly appropriate only
+ * for use with primitive or very small types.
+ *
+ * AsVariant() returns a AsVariantTemporary value which is implicitly
+ * convertible to any Variant that can hold a value of type T.
+ */
+template<typename T>
+detail::AsVariantTemporary<T>
+AsVariant(T&& aValue)
+{
+  return detail::AsVariantTemporary<T>(Forward<T>(aValue));
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_Variant_h */