Bug 1184385 - Add a Tie() utility function for tuples (the equivalent of std::tie()) to MFBT. r=froydnj
authorBotond Ballo <botond@mozilla.com>
Sat, 18 Jul 2015 03:48:39 -0400
changeset 253730 1804dea1fadd39ab0921d32c65ffc6089f985664
parent 253729 aa14ddf5babcd5cfd02fba1ce24e71bfdc332a2a
child 253731 3c73a85e23044e3cd985c61462b0fa35580792ae
push id62540
push userbballo@mozilla.com
push dateMon, 20 Jul 2015 19:55:07 +0000
treeherdermozilla-inbound@1804dea1fadd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1184385
milestone42.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 1184385 - Add a Tie() utility function for tuples (the equivalent of std::tie()) to MFBT. r=froydnj
mfbt/Tuple.h
mfbt/tests/TestTuple.cpp
--- a/mfbt/Tuple.h
+++ b/mfbt/Tuple.h
@@ -137,17 +137,40 @@ struct TupleImpl<Index, HeadT, TailT...>
   // Copy and move constructors.
   // We'd like to use '= default' to implement these, but MSVC 2013's support
   // for '= default' is incomplete and this doesn't work.
   TupleImpl(const TupleImpl& aOther)
     : Base(Tail(aOther))
     , mHead(Head(aOther)) {}
   TupleImpl(TupleImpl&& aOther)
     : Base(Move(Tail(aOther)))
-    , mHead(Move(Head(aOther))) {}
+    , mHead(Forward<HeadT>(Head(aOther))) {}
+
+  // Assign from a tuple whose elements are convertible to the elements
+  // of this tuple.
+  template <typename... OtherElements,
+            typename = typename EnableIf<
+                sizeof...(OtherElements) == sizeof...(TailT) + 1>::Type>
+  TupleImpl& operator=(const TupleImpl<Index, OtherElements...>& aOther)
+  {
+    typedef TupleImpl<Index, OtherElements...> OtherT;
+    Head(*this) = OtherT::Head(aOther);
+    Tail(*this) = OtherT::Tail(aOther);
+    return *this;
+  }
+  template <typename... OtherElements,
+            typename = typename EnableIf<
+                sizeof...(OtherElements) == sizeof...(TailT) + 1>::Type>
+  TupleImpl& operator=(TupleImpl<Index, OtherElements...>&& aOther)
+  {
+    typedef TupleImpl<Index, OtherElements...> OtherT;
+    Head(*this) = Move(OtherT::Head(aOther));
+    Tail(*this) = Move(OtherT::Tail(aOther));
+    return *this;
+  }
 
   // Copy and move assignment operators.
   TupleImpl& operator=(const TupleImpl& aOther)
   {
     Head(*this) = Head(aOther);
     Tail(*this) = Tail(aOther);
     return *this;
   }
@@ -190,16 +213,32 @@ public:
                 detail::CheckConvertibility<
                     detail::Group<OtherHead, OtherTail...>,
                     detail::Group<Elements...>>::value>::Type>
   explicit Tuple(OtherHead&& aHead, OtherTail&&... aTail)
     : Impl(Forward<OtherHead>(aHead), Forward<OtherTail>(aTail)...) { }
   Tuple(const Tuple& aOther) : Impl(aOther) { }
   Tuple(Tuple&& aOther) : Impl(Move(aOther)) { }
 
+  template <typename... OtherElements,
+            typename = typename EnableIf<
+                sizeof...(OtherElements) == sizeof...(Elements)>::Type>
+  Tuple& operator=(const Tuple<OtherElements...>& aOther)
+  {
+    static_cast<Impl&>(*this) = aOther;
+    return *this;
+  }
+  template <typename... OtherElements,
+            typename = typename EnableIf<
+                sizeof...(OtherElements) == sizeof...(Elements)>::Type>
+  Tuple& operator=(Tuple<OtherElements...>&& aOther)
+  {
+    static_cast<Impl&>(*this) = Move(aOther);
+    return *this;
+  }
   Tuple& operator=(const Tuple& aOther)
   {
     static_cast<Impl&>(*this) = aOther;
     return *this;
   }
   Tuple& operator=(Tuple&& aOther)
   {
     static_cast<Impl&>(*this) = Move(aOther);
@@ -291,11 +330,30 @@ auto Get(Tuple<Elements...>&& aTuple)
  * auto tuple = MakeTuple(42, 0.5f, 'c');  // has type Tuple<int, float, char>
  */
 template<typename... Elements>
 Tuple<Elements...> MakeTuple(Elements&&... aElements)
 {
   return Tuple<Elements...>(Forward<Elements>(aElements)...);
 }
 
+/**
+ * A convenience function for constructing a tuple of references to a
+ * sequence of variables. Since assignments to the elements of the tuple
+ * "go through" to the referenced variables, this can be used to "unpack"
+ * a tuple into individual variables.
+ *
+ * Example:
+ *
+ * int i;
+ * float f;
+ * char c;
+ * Tie(i, f, c) = FunctionThatReturnsATuple();
+ */
+template<typename... Elements>
+Tuple<Elements&...> Tie(Elements&... aVariables)
+{
+  return Tuple<Elements&...>(aVariables...);
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_Tuple_h */
--- a/mfbt/tests/TestTuple.cpp
+++ b/mfbt/tests/TestTuple.cpp
@@ -13,16 +13,17 @@
 
 #include <stddef.h>
 
 using mozilla::Get;
 using mozilla::IsSame;
 using mozilla::MakeTuple;
 using mozilla::MakeUnique;
 using mozilla::Move;
+using mozilla::Tie;
 using mozilla::Tuple;
 using mozilla::UniquePtr;
 using mozilla::unused;
 
 #define CHECK(c) \
   do { \
     bool cond = !!(c); \
     MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \
@@ -31,17 +32,17 @@ using mozilla::unused;
 // The second argument is the expected type. It's variadic to allow the
 // type to contain commas.
 #define CHECK_TYPE(expression, ...)  \
   static_assert(IsSame<decltype(expression), __VA_ARGS__>::value, \
       "Type mismatch!")
 
 struct ConvertibleToInt
 {
-  operator int() { return 42; }
+  operator int() const { return 42; }
 };
 
 static void
 TestConstruction()
 {
   // Default construction
   Tuple<> a;
   unused << a;
@@ -131,17 +132,39 @@ TestMakeTuple()
   auto tuple = MakeTuple(42, 0.5f, 'c');
   CHECK_TYPE(tuple, Tuple<int, float, char>);
   CHECK(Get<0>(tuple) == 42);
   CHECK(Get<1>(tuple) == 1.0f);
   CHECK(Get<2>(tuple) == 'c');
   return true;
 }
 
+static bool
+TestTie()
+{
+  int i;
+  float f;
+  char c;
+  Tuple<int, float, char> rhs1(42, 0.5f, 'c');
+  Tie(i, f, c) = rhs1;
+  CHECK(i == Get<0>(rhs1));
+  CHECK(f == Get<1>(rhs1));
+  CHECK(c == Get<2>(rhs1));
+  // Test conversions
+  Tuple<ConvertibleToInt, double, unsigned char> rhs2(ConvertibleToInt(),
+      0.7f, 'd');
+  Tie(i, f, c) = rhs2;
+  CHECK(i == Get<0>(rhs2));
+  CHECK(f == Get<1>(rhs2));
+  CHECK(c == Get<2>(rhs2));
+  return true;
+}
+
 int
 main()
 {
   TestConstruction();
   TestAssignment();
   TestGet();
   TestMakeTuple();
+  TestTie();
   return 0;
 }