From 8bedb7e1549c4a737d3ad4d7003ab9e195cd9fd9 Mon Sep 17 00:00:00 2001 From: Phil Willoughby Date: Mon, 3 Jul 2017 05:55:21 -0700 Subject: [PATCH] Content-conversion constructors for Future Summary: Allows you to construct a `Future` from a `Future` 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 | 34 ++++++++++++ folly/futures/Future.h | 25 +++++++++ folly/futures/test/ConversionOperatorTest.cpp | 53 +++++++++++++++++++ folly/futures/test/ThenTest.cpp | 2 + folly/test/Makefile.am | 1 + 5 files changed, 115 insertions(+) create mode 100644 folly/futures/test/ConversionOperatorTest.cpp diff --git a/folly/futures/Future-inl.h b/folly/futures/Future-inl.h index 09483c15..e326be0b 100644 --- a/folly/futures/Future-inl.h +++ b/folly/futures/Future-inl.h @@ -136,6 +136,40 @@ Future& Future::operator=(Future&& other) noexcept { return *this; } +template +template < + class T2, + typename std::enable_if< + !std::is_same::type>::value && + std::is_constructible::value && + std::is_convertible::value, + int>::type> +Future::Future(Future&& other) + : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {} + +template +template < + class T2, + typename std::enable_if< + !std::is_same::type>::value && + std::is_constructible::value && + !std::is_convertible::value, + int>::type> +Future::Future(Future&& other) + : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {} + +template +template < + class T2, + typename std::enable_if< + !std::is_same::type>::value && + std::is_constructible::value, + int>::type> +Future& Future::operator=(Future&& other) { + return operator=( + std::move(other).then([](T2&& v) { return T(std::move(v)); })); +} + template template Future::Future(T2&& val) diff --git a/folly/futures/Future.h b/folly/futures/Future.h index aaa4cb78..87bb0cfe 100644 --- a/folly/futures/Future.h +++ b/folly/futures/Future.h @@ -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::type>::value && + std::is_constructible::value && + std::is_convertible::value, + int>::type = 0> + /* implicit */ Future(Future&&); + template < + class T2, + typename std::enable_if< + !std::is_same::type>::value && + std::is_constructible::value && + !std::is_convertible::value, + int>::type = 0> + explicit Future(Future&&); + template < + class T2, + typename std::enable_if< + !std::is_same::type>::value && + std::is_constructible::value, + int>::type = 0> + Future& operator=(Future&&); + /// Construct a Future from a value (perfect forwarding) template +#include + +#include + +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(23); + EXPECT_EQ(future.value().v_, 23); + Future secondFuture{std::move(future)}; + EXPECT_EQ(secondFuture.value(), 23); +} + +TEST(ConverstionOperator, StaticCast) { + auto future = makeFuture(23); + EXPECT_EQ(future.value().v_, 23); + EXPECT_EQ(static_cast>(std::move(future)).value(), 23); +} diff --git a/folly/futures/test/ThenTest.cpp b/folly/futures/test/ThenTest.cpp index ca20c965..ae52706b 100644 --- a/folly/futures/test/ThenTest.cpp +++ b/folly/futures/test/ThenTest.cpp @@ -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(23); diff --git a/folly/test/Makefile.am b/folly/test/Makefile.am index 04dfb671..2af336e1 100644 --- a/folly/test/Makefile.am +++ b/folly/test/Makefile.am @@ -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 \ -- 2.34.1