Commit 8bedb7e1 authored by Phil Willoughby's avatar Phil Willoughby Committed by Facebook Github Bot

Content-conversion constructors for Future

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
parent dedaae98
...@@ -136,6 +136,40 @@ Future<T>& Future<T>::operator=(Future<T>&& other) noexcept { ...@@ -136,6 +136,40 @@ Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
return *this; 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 T>
template <class T2, typename> template <class T2, typename>
Future<T>::Future(T2&& val) Future<T>::Future(T2&& val)
......
...@@ -53,6 +53,31 @@ class Future { ...@@ -53,6 +53,31 @@ class Future {
Future(Future&&) noexcept; Future(Future&&) noexcept;
Future& operator=(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) /// Construct a Future from a value (perfect forwarding)
template <class T2 = T, typename = template <class T2 = T, typename =
typename std::enable_if< typename std::enable_if<
......
/*
* 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);
}
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
using namespace folly; using namespace folly;
namespace {
struct Widget { struct Widget {
int v_, copied_, moved_; int v_, copied_, moved_;
/* implicit */ Widget(int v) : v_(v), copied_(0), moved_(0) {} /* implicit */ Widget(int v) : v_(v), copied_(0), moved_(0) {}
...@@ -35,6 +36,7 @@ struct Widget { ...@@ -35,6 +36,7 @@ struct Widget {
throw std::logic_error("unexpected move assignment"); throw std::logic_error("unexpected move assignment");
} }
}; };
}
TEST(Then, tryConstructor) { TEST(Then, tryConstructor) {
auto t = Try<Widget>(23); auto t = Try<Widget>(23);
......
...@@ -270,6 +270,7 @@ futures_test_SOURCES = \ ...@@ -270,6 +270,7 @@ futures_test_SOURCES = \
../futures/test/CallbackLifetimeTest.cpp \ ../futures/test/CallbackLifetimeTest.cpp \
../futures/test/CollectTest.cpp \ ../futures/test/CollectTest.cpp \
../futures/test/ContextTest.cpp \ ../futures/test/ContextTest.cpp \
../futures/test/ConversionOperatorTest.cpp \
../futures/test/CoreTest.cpp \ ../futures/test/CoreTest.cpp \
../futures/test/EnsureTest.cpp \ ../futures/test/EnsureTest.cpp \
../futures/test/ExecutorTest.cpp \ ../futures/test/ExecutorTest.cpp \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment