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
--- 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;
}