Bug 1322962 - UniquePtrForCpp11Lambda - r?froydnj draft
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 03 Jan 2017 15:05:55 +1100
changeset 456851 dbdd84147be52c203cdb33bcc3adc7007ef724de
parent 456716 a14094edbad78fc1d16e8d4c57902537cf286fd1
child 456852 1f9b1d8d9eeab8f093c5eb7dd3a9c33e43036c16
child 456854 47953a863f83f423bbcca8e39c3cf19da6f776dc
push id40624
push usergsquelart@mozilla.com
push dateFri, 06 Jan 2017 11:03:56 +0000
reviewersfroydnj
bugs1322962
milestone53.0a1
Bug 1322962 - UniquePtrForCpp11Lambda - r?froydnj Lambdas cannot be captured by C++11 lambdas. This wrapper allows such capture, until we can use C++14. It may also be useful if future UniquePtr-in-lambda code needs to be back- ported to C++11-only versions. MozReview-Commit-ID: 7Y7mhbHsevs
mfbt/UniquePtr.h
mfbt/tests/TestUniquePtr.cpp
--- a/mfbt/UniquePtr.h
+++ b/mfbt/UniquePtr.h
@@ -687,11 +687,95 @@ MakeUnique(decltype(sizeof(int)) aN)
   typedef typename RemoveExtent<T>::Type ArrayType;
   return UniquePtr<T>(new ArrayType[aN]());
 }
 
 template<typename T, typename... Args>
 typename detail::UniqueSelector<T>::KnownBound
 MakeUnique(Args&&... aArgs) = delete;
 
+
+/**
+ * Wrapper class to allow C++11 lambdas to capture UniquePtr.
+ * This is a temporary class, while we don't have C++14 yet.
+ * It may also be useful to port future code back to C++11-only releases.
+ */
+template <typename T>
+class UniquePtrForCpp11Lambda
+{
+public:
+  // Steal pointer from a moved-from or temporary UniquePtr.
+  explicit UniquePtrForCpp11Lambda(UniquePtr<T>&& aUPtr) : mUPtr(Move(aUPtr)) {}
+  // Create UniquePtr from a raw pointer.
+  explicit UniquePtrForCpp11Lambda(T* aRawPtr) : mUPtr(aRawPtr) {}
+
+  // Default nullptr constructor, and some move constructor&assignments, good
+  // when value cannot be known at construction time.
+  UniquePtrForCpp11Lambda() : mUPtr() {}
+  UniquePtrForCpp11Lambda(UniquePtrForCpp11Lambda&& aOther)
+    : mUPtr(Move(aOther.mUPtr))
+  {}
+  UniquePtrForCpp11Lambda& operator=(UniquePtrForCpp11Lambda&& aOther)
+  {
+    mUPtr = Move(aOther.mUPtr);
+    return *this;
+  }
+  UniquePtrForCpp11Lambda& operator=(UniquePtr<T>&& aUPtr)
+  {
+    mUPtr = Move(aUPtr);
+    return *this;
+  }
+  UniquePtrForCpp11Lambda& operator=(T* aRawPtr)
+  {
+    mUPtr.reset(aRawPtr);
+    return *this;
+  }
+  UniquePtrForCpp11Lambda& operator=(decltype(nullptr))
+  {
+    mUPtr.reset(nullptr);
+    return *this;
+  }
+
+  // The copy-constructor that shouldn't exist, which makes capturing by a
+  // C++11 lambda possible.
+  UniquePtrForCpp11Lambda(
+#ifdef _MSC_VER
+  // For some reason, MSC doesn't see the non-const-ref copy constructor
+  // when lambda-capturing, so as an ugly hack we implement the usual const-ref
+  // copy constructor, but make mUPtr `mutable` to allow moving from it.
+                          const
+#endif
+                                UniquePtrForCpp11Lambda& aOther)
+    : mUPtr(Move(aOther.mUPtr))
+  {}
+
+  // Disallow copy-assignment.
+  void operator=(const UniquePtrForCpp11Lambda& aOther) = delete;
+
+  // Most common UniquePtr-like methods.
+  T* get() const { return mUPtr.get(); }
+  explicit operator bool() const { return get(); }
+  T& operator*() const
+  {
+    MOZ_ASSERT(get(), "dereferencing a UniquePtrForCpp11Lambda containing nullptr");
+    return *get();
+  }
+  T* operator->() const
+  {
+    MOZ_ASSERT(get(), "dereferencing a UniquePtrForCpp11Lambda containing nullptr");
+    return get();
+  }
+
+  // Reference to the actual UniquePtr.
+  const UniquePtr<T>& UPtr() const { return mUPtr; }
+  UniquePtr<T>& UPtr() { return mUPtr; }
+
+private:
+#ifdef _MSC_VER
+  // To go with the const-ref copy constructor.
+  mutable
+#endif
+  UniquePtr<T> mUPtr;
+};
+
 } // namespace mozilla
 
 #endif /* mozilla_UniquePtr_h */
--- a/mfbt/tests/TestUniquePtr.cpp
+++ b/mfbt/tests/TestUniquePtr.cpp
@@ -13,16 +13,17 @@
 
 #include <stddef.h>
 
 using mozilla::DefaultDelete;
 using mozilla::IsSame;
 using mozilla::MakeUnique;
 using mozilla::Swap;
 using mozilla::UniquePtr;
+using mozilla::UniquePtrForCpp11Lambda;
 using mozilla::Vector;
 
 #define CHECK(c) \
   do { \
     bool cond = !!(c); \
     MOZ_ASSERT(cond, "Failed assertion: " #c); \
     if (!cond) { \
       return false; \
@@ -557,16 +558,95 @@ TestMakeUnique()
   // various type mismatching to test "fuzzy" forwarding
   UniquePtr<Q> q4(MakeUnique<Q>('s', 66LL, 3.141592654, &q3));
 
   UniquePtr<char[]> c1(MakeUnique<char[]>(5));
 
   return true;
 }
 
+static bool
+TestUniquePtrForCpp11Lambda()
+{
+  // First test to check that TestUniquePtrForCpp11Lambda does its main job
+  // of being moved into a lambda capture.
+  {
+    UniquePtrForCpp11Lambda<int> up11i(new int(1));
+    CHECK(up11i.get() != nullptr);
+    CHECK(*up11i == 1);
+
+    // Run a lambda that captures 'up11i' and copies its value into 'extracted'.
+    int extracted = 0;
+    [up11i, &extracted](){
+      if (up11i) {
+        extracted = *up11i;
+      }
+    }();
+    CHECK(extracted == 1);
+
+    // Local up11i should have been effectively moved-from when captured (by
+    // copy!) by the lambda.
+    CHECK(up11i.get() == nullptr);
+  }
+
+  // More tests for the other methods.
+  {
+    UniquePtr<int> upi(new int(2));
+    CHECK(upi.get() != nullptr);
+    // Construction from moved-from UniquePtr.
+    UniquePtrForCpp11Lambda<int> up11i(Move(upi));
+    CHECK(upi.get() == nullptr);
+    CHECK(up11i.get() != nullptr);
+    CHECK(*up11i == 2);
+  }
+  {
+    // Construction from temporary UniquePtr.
+    UniquePtrForCpp11Lambda<int> up11i(UniquePtr<int>(new int(3)));
+    CHECK(up11i.get() != nullptr);
+    CHECK(*up11i == 3);
+  }
+  {
+    // Assignments.
+    UniquePtrForCpp11Lambda<int> up11i;
+    CHECK(up11i.get() == nullptr);
+    up11i = UniquePtr<int>(new int(4));
+    CHECK(up11i.get() != nullptr);
+    CHECK(*up11i == 4);
+    up11i = new int(5);
+    CHECK(up11i.get() != nullptr);
+    CHECK(*up11i == 5);
+    up11i = nullptr;
+    CHECK(up11i.get() == nullptr);
+    UniquePtr<int> upi(new int(2));
+    UniquePtrForCpp11Lambda<int> up11i2(new int(6));
+    up11i = Move(up11i2);
+    CHECK(up11i2.get() == nullptr);
+    CHECK(up11i.get() != nullptr);
+    CHECK(*up11i == 6);
+  }
+  {
+    // ->
+    struct S { int foo() { return 7; } };
+    UniquePtrForCpp11Lambda<S> up11s(new S());
+    CHECK(up11s.get() != nullptr);
+    CHECK(up11s->foo() == 7);
+  }
+  {
+    // UPtr(), used here to steal pointer.
+    UniquePtrForCpp11Lambda<int> up11i(new int(8));
+    CHECK(up11i.get() != nullptr);
+    CHECK(*up11i == 8);
+    UniquePtr<int> upi(Move(up11i.UPtr()));
+    CHECK(up11i.get() == nullptr);
+    CHECK(upi.get() != nullptr);
+    CHECK(*upi == 8);
+  }
+  return true;
+}
+
 int
 main()
 {
   TestDeleterType();
 
   if (!TestDefaultFree()) {
     return 1;
   }
@@ -583,10 +663,13 @@ main()
     return 1;
   }
   if (!TestArray()) {
     return 1;
   }
   if (!TestMakeUnique()) {
     return 1;
   }
+  if (!TestUniquePtrForCpp11Lambda()) {
+    return 1;
+  }
   return 0;
 }