Content-conversion constructors for Future
authorPhil Willoughby <philwill@fb.com>
Mon, 3 Jul 2017 12:55:21 +0000 (05:55 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Mon, 3 Jul 2017 13:05:19 +0000 (06:05 -0700)
Summary:
Allows you to construct a `Future<A>` from a `Future<B>` if A can be constructed from `B&&`.

The constructor is `implicit` if and only if `B&&` is convertible to `A`.

This is the same philosophy as Optional, and has the same use-cases.

Reviewed By: yfeldblum

Differential Revision: D5264185

fbshipit-source-id: e2fae373ab549c186a69dc5f4e32481ef7b11bba

folly/futures/Future-inl.h
folly/futures/Future.h
folly/futures/test/ConversionOperatorTest.cpp [new file with mode: 0644]
folly/futures/test/ThenTest.cpp
folly/test/Makefile.am

index 09483c151e5c5658ae3cb8786956272a4319d02c..e326be0b69dd6821764004847542aef2c0b34458 100644 (file)
@@ -136,6 +136,40 @@ Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
   return *this;
 }
 
+template <class T>
+template <
+    class T2,
+    typename std::enable_if<
+        !std::is_same<T, typename std::decay<T2>::type>::value &&
+            std::is_constructible<T, T2&&>::value &&
+            std::is_convertible<T2&&, T>::value,
+        int>::type>
+Future<T>::Future(Future<T2>&& other)
+    : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {}
+
+template <class T>
+template <
+    class T2,
+    typename std::enable_if<
+        !std::is_same<T, typename std::decay<T2>::type>::value &&
+            std::is_constructible<T, T2&&>::value &&
+            !std::is_convertible<T2&&, T>::value,
+        int>::type>
+Future<T>::Future(Future<T2>&& other)
+    : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {}
+
+template <class T>
+template <
+    class T2,
+    typename std::enable_if<
+        !std::is_same<T, typename std::decay<T2>::type>::value &&
+            std::is_constructible<T, T2&&>::value,
+        int>::type>
+Future<T>& Future<T>::operator=(Future<T2>&& other) {
+  return operator=(
+      std::move(other).then([](T2&& v) { return T(std::move(v)); }));
+}
+
 template <class T>
 template <class T2, typename>
 Future<T>::Future(T2&& val)
index aaa4cb782ec9773a7c93fe1d5458d38d2661f60b..87bb0cfe417cfcc735cc39aca9ae2ced9877527d 100644 (file)
@@ -53,6 +53,31 @@ class Future {
   Future(Future&&) noexcept;
   Future& operator=(Future&&) noexcept;
 
+  // converting move
+  template <
+      class T2,
+      typename std::enable_if<
+          !std::is_same<T, typename std::decay<T2>::type>::value &&
+              std::is_constructible<T, T2&&>::value &&
+              std::is_convertible<T2&&, T>::value,
+          int>::type = 0>
+  /* implicit */ Future(Future<T2>&&);
+  template <
+      class T2,
+      typename std::enable_if<
+          !std::is_same<T, typename std::decay<T2>::type>::value &&
+              std::is_constructible<T, T2&&>::value &&
+              !std::is_convertible<T2&&, T>::value,
+          int>::type = 0>
+  explicit Future(Future<T2>&&);
+  template <
+      class T2,
+      typename std::enable_if<
+          !std::is_same<T, typename std::decay<T2>::type>::value &&
+              std::is_constructible<T, T2&&>::value,
+          int>::type = 0>
+  Future& operator=(Future<T2>&&);
+
   /// Construct a Future from a value (perfect forwarding)
   template <class T2 = T, typename =
             typename std::enable_if<
diff --git a/folly/futures/test/ConversionOperatorTest.cpp b/folly/futures/test/ConversionOperatorTest.cpp
new file mode 100644 (file)
index 0000000..37613cc
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/futures/Future.h>
+#include <folly/portability/GTest.h>
+
+#include <thread>
+
+using namespace folly;
+
+namespace {
+struct Widget {
+  int v_;
+  /* implicit */ Widget(int v) : v_(v) {}
+  Widget(const Widget& other) = default;
+  Widget(Widget&& other) noexcept = default;
+  Widget& operator=(const Widget& /* other */) {
+    throw std::logic_error("unexpected copy assignment");
+  }
+  Widget& operator=(Widget&& /* other */) {
+    throw std::logic_error("unexpected move assignment");
+  }
+  explicit operator int() && {
+    return v_;
+  }
+};
+}
+
+TEST(ConverstionOperator, DirectInitialization) {
+  auto future = makeFuture<Widget>(23);
+  EXPECT_EQ(future.value().v_, 23);
+  Future<int> secondFuture{std::move(future)};
+  EXPECT_EQ(secondFuture.value(), 23);
+}
+
+TEST(ConverstionOperator, StaticCast) {
+  auto future = makeFuture<Widget>(23);
+  EXPECT_EQ(future.value().v_, 23);
+  EXPECT_EQ(static_cast<Future<int>>(std::move(future)).value(), 23);
+}
index ca20c9651c88bc424b21564d2a51d4f61db4063d..ae52706b8598bed69e80470472f4cc50e00649e8 100644 (file)
@@ -21,6 +21,7 @@
 
 using namespace folly;
 
+namespace {
 struct Widget {
   int v_, copied_, moved_;
   /* implicit */ Widget(int v) : v_(v), copied_(0), moved_(0) {}
@@ -35,6 +36,7 @@ struct Widget {
     throw std::logic_error("unexpected move assignment");
   }
 };
+}
 
 TEST(Then, tryConstructor) {
   auto t = Try<Widget>(23);
index 04dfb671cf7151e76f26fcb59226398491727961..2af336e1c9205b7296b0285fcf6bafdd6ea5b71b 100644 (file)
@@ -270,6 +270,7 @@ futures_test_SOURCES = \
     ../futures/test/CallbackLifetimeTest.cpp \
     ../futures/test/CollectTest.cpp \
     ../futures/test/ContextTest.cpp \
+    ../futures/test/ConversionOperatorTest.cpp \
     ../futures/test/CoreTest.cpp \
     ../futures/test/EnsureTest.cpp \
     ../futures/test/ExecutorTest.cpp \