Timekeeper* getTimekeeperSingleton();
}
-template <typename T>
-struct isFuture {
- static const bool value = false;
-};
-
-template <typename T>
-struct isFuture<Future<T> > {
- static const bool value = true;
-};
-
template <class T>
Future<T>::Future(Future<T>&& other) noexcept : core_(nullptr) {
*this = std::move(other);
core_->setCallback(std::move(func));
}
-// Variant: f.then([](Try<T>&& t){ return t.value(); });
+// Variant: returns a value
+// e.g. f.then([](Try<T>&& t){ return t.value(); });
template <class T>
-template <class F>
-typename std::enable_if<
- !isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
- Future<typename std::result_of<F(Try<T>&&)>::type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F(Try<T>&&)>::type B;
+template <typename F, typename R, bool isTry, typename... Args>
+typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
+Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
+ static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
+ typedef typename R::ReturnsFuture::Inner B;
throwIfInvalid();
*/
setCallback_(
[p, funcm](Try<T>&& t) mutable {
- p->fulfil([&]() {
- return (*funcm)(std::move(t));
- });
- });
-
- return f;
-}
-
-// Variant: f.then([](T&& t){ return t; });
-template <class T>
-template <class F>
-typename std::enable_if<
- !std::is_same<T, void>::value &&
- !isFuture<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
- Future<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F(T&&)>::type B;
-
- throwIfInvalid();
-
- folly::MoveWrapper<Promise<B>> p;
- folly::MoveWrapper<F> funcm(std::forward<F>(func));
- auto f = p->getFuture();
-
- setCallback_(
- [p, funcm](Try<T>&& t) mutable {
- if (t.hasException()) {
+ if (!isTry && t.hasException()) {
p->setException(std::move(t.exception()));
} else {
p->fulfil([&]() {
- return (*funcm)(std::move(t.value()));
+ return (*funcm)(t.template get<isTry, Args>()...);
});
}
});
return f;
}
-// Variant: f.then([](){ return; });
+// Variant: returns a Future
+// e.g. f.then([](T&& t){ return makeFuture<T>(t); });
template <class T>
-template <class F>
-typename std::enable_if<
- std::is_same<T, void>::value &&
- !isFuture<typename std::result_of<F()>::type>::value,
- Future<typename std::result_of<F()>::type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F()>::type B;
-
- throwIfInvalid();
-
- folly::MoveWrapper<Promise<B>> p;
- folly::MoveWrapper<F> funcm(std::forward<F>(func));
- auto f = p->getFuture();
-
- setCallback_(
- [p, funcm](Try<T>&& t) mutable {
- if (t.hasException()) {
- p->setException(std::move(t.exception()));
- } else {
- p->fulfil([&]() {
- return (*funcm)();
- });
- }
- });
-
- return f;
-}
-
-// Variant: f.then([](Try<T>&& t){ return makeFuture<T>(t.value()); });
-template <class T>
-template <class F>
-typename std::enable_if<
- isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
- Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F(Try<T>&&)>::type::value_type B;
+template <typename F, typename R, bool isTry, typename... Args>
+typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
+Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
+ static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
+ typedef typename R::ReturnsFuture::Inner B;
throwIfInvalid();
setCallback_(
[p, funcm](Try<T>&& t) mutable {
- try {
- auto f2 = (*funcm)(std::move(t));
- // that didn't throw, now we can steal p
- f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
- } catch (const std::exception& e) {
- p->setException(exception_wrapper(std::current_exception(), e));
- } catch (...) {
- p->setException(exception_wrapper(std::current_exception()));
- }
- });
-
- return f;
-}
-
-// Variant: f.then([](T&& t){ return makeFuture<T>(t); });
-template <class T>
-template <class F>
-typename std::enable_if<
- !std::is_same<T, void>::value &&
- isFuture<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
- Future<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type::value_type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F(T&&)>::type::value_type B;
-
- throwIfInvalid();
-
- folly::MoveWrapper<Promise<B>> p;
- folly::MoveWrapper<F> funcm(std::forward<F>(func));
- auto f = p->getFuture();
-
- setCallback_(
- [p, funcm](Try<T>&& t) mutable {
- if (t.hasException()) {
+ if (!isTry && t.hasException()) {
p->setException(std::move(t.exception()));
} else {
try {
- auto f2 = (*funcm)(std::move(t.value()));
+ auto f2 = (*funcm)(t.template get<isTry, Args>()...);
+ // that didn't throw, now we can steal p
f2.setCallback_([p](Try<B>&& b) mutable {
p->fulfilTry(std::move(b));
});
} catch (const std::exception& e) {
- p->setException(exception_wrapper(std::current_exception(), e));
- } catch (...) {
p->setException(exception_wrapper(std::current_exception()));
}
}
return f;
}
-// Variant: f.then([](){ return makeFuture(); });
-template <class T>
-template <class F>
-typename std::enable_if<
- std::is_same<T, void>::value &&
- isFuture<typename std::result_of<F()>::type>::value,
- Future<typename std::result_of<F()>::type::value_type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F()>::type::value_type B;
-
- throwIfInvalid();
-
- folly::MoveWrapper<Promise<B>> p;
- folly::MoveWrapper<F> funcm(std::forward<F>(func));
-
- auto f = p->getFuture();
-
- setCallback_(
- [p, funcm](Try<T>&& t) mutable {
- if (t.hasException()) {
- p->setException(t.exception());
- } else {
- try {
- auto f2 = (*funcm)();
- f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
- } catch (const std::exception& e) {
- p->setException(exception_wrapper(std::current_exception(), e));
- } catch (...) {
- p->setException(exception_wrapper(std::current_exception()));
- }
- }
- });
-
- return f;
+template <typename T>
+template <typename Caller, typename R, typename... Args>
+ Future<typename isFuture<R>::Inner>
+Future<T>::then(Caller *instance, R(Caller::*func)(Args...)) {
+ typedef typename std::remove_cv<
+ typename std::remove_reference<
+ typename detail::ArgType<Args...>::FirstArg>::type>::type FirstArg;
+ return then([instance, func](Try<T>&& t){
+ return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...);
+ });
}
template <class T>
template <class> struct Promise;
-namespace detail {
-
-template <class> struct Core;
-template <class...> struct VariadicContext;
+template <typename T>
+struct isFuture : std::false_type {
+ typedef T Inner;
+};
-template <class T>
-struct AliasIfVoid {
- typedef typename std::conditional<
- std::is_same<T, void>::value,
- int,
- T>::type type;
+template <typename T>
+struct isFuture<Future<T>> : std::true_type {
+ typedef T Inner;
};
+template <typename T>
+struct isTry : std::false_type {};
template <typename T>
-struct IsFuture : std::integral_constant<bool, false> {
- typedef T Inner;
-};
+struct isTry<Try<T>> : std::true_type {};
-template <template <typename T> class Future, typename T>
-struct IsFuture<Future<T>> : std::integral_constant<bool, true> {
- typedef T Inner;
-};
+namespace detail {
+
+template <class> struct Core;
+template <class...> struct VariadicContext;
+
+template<typename F, typename... Args>
+using resultOf = decltype(std::declval<F>()(std::declval<Args>()...));
template <typename...>
struct ArgType;
typedef void FirstArg;
};
+template <bool isTry, typename F, typename... Args>
+struct argResult {
+ typedef resultOf<F, Args...> Result;
+};
+
+template<typename F, typename... Args>
+struct callableWith {
+ template<typename T,
+ typename = detail::resultOf<T, Args...>>
+ static constexpr std::true_type
+ check(std::nullptr_t) { return std::true_type{}; };
+
+ template<typename>
+ static constexpr std::false_type
+ check(...) { return std::false_type{}; };
+
+ typedef decltype(check<F>(nullptr)) type;
+ static constexpr bool value = type::value;
+};
+
+template<typename T, typename F>
+struct callableResult {
+ typedef typename std::conditional<
+ callableWith<F>::value,
+ detail::argResult<false, F>,
+ typename std::conditional<
+ callableWith<F, Try<T>&&>::value,
+ detail::argResult<true, F, Try<T>&&>,
+ typename std::conditional<
+ callableWith<F, Try<T>&>::value,
+ detail::argResult<true, F, Try<T>&>,
+ typename std::conditional<
+ callableWith<F, T&&>::value,
+ detail::argResult<false, F, T&&>,
+ detail::argResult<false, F, T&>>::type>::type>::type>::type Arg;
+ typedef isFuture<typename Arg::Result> ReturnsFuture;
+ typedef Future<typename ReturnsFuture::Inner> Return;
+};
+
+template<typename F>
+struct callableResult<void, F> {
+ typedef typename std::conditional<
+ callableWith<F>::value,
+ detail::argResult<false, F>,
+ typename std::conditional<
+ callableWith<F, Try<void>&&>::value,
+ detail::argResult<true, F, Try<void>&&>,
+ detail::argResult<true, F, Try<void>&>>::type>::type Arg;
+ typedef isFuture<typename Arg::Result> ReturnsFuture;
+ typedef Future<typename ReturnsFuture::Inner> Return;
+};
+
template <typename L>
struct Extract : Extract<decltype(&L::operator())> { };
template <typename Class, typename R, typename... Args>
struct Extract<R(Class::*)(Args...) const> {
- typedef IsFuture<R> ReturnsFuture;
+ typedef isFuture<R> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
typedef typename ReturnsFuture::Inner RawReturn;
typedef typename ArgType<Args...>::FirstArg FirstArg;
template <typename Class, typename R, typename... Args>
struct Extract<R(Class::*)(Args...)> {
- typedef IsFuture<R> ReturnsFuture;
+ typedef isFuture<R> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
typedef typename ReturnsFuture::Inner RawReturn;
typedef typename ArgType<Args...>::FirstArg FirstArg;
struct Timekeeper;
-template <typename T> struct isFuture;
-
/// This namespace is for utility functions that would usually be static
/// members of Future, except they don't make sense there because they don't
/// depend on the template type (rather, on the type of their arguments in
T getVia(DrivableExecutor* e);
/** When this Future has completed, execute func which is a function that
- takes a Try<T>&&. A Future for the return type of func is
- returned. e.g.
+ takes one of:
+ (const) Try<T>&&
+ (const) Try<T>&
+ (const) Try<T>
+ (const) T&&
+ (const) T&
+ (const) T
+ (void)
+
+ Func shall return either another Future or a value.
+
+ A Future for the return type of func is returned.
Future<string> f2 = f1.then([](Try<T>&&) { return string("foo"); });
Future), we might want to support a similar API which could be
implemented a little more efficiently than
f.via(executor).then(callback) */
- template <class F>
- typename std::enable_if<
- !isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
- Future<typename std::result_of<F(Try<T>&&)>::type> >::type
- then(F&& func);
-
- /// Variant where func takes a T directly, bypassing a try. Any exceptions
- /// will be implicitly passed on to the resultant Future.
- ///
- /// Future<int> f = makeFuture<int>(42).then([](int i) { return i+1; });
- template <class F>
- typename std::enable_if<
- !std::is_same<T, void>::value &&
- !isFuture<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
- Future<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type> >::type
- then(F&& func);
-
- /// Like the above variant, but for void futures. That is, func takes no
- /// argument.
- ///
- /// Future<int> f = makeFuture().then([] { return 42; });
- template <class F>
- typename std::enable_if<
- std::is_same<T, void>::value &&
- !isFuture<typename std::result_of<F()>::type>::value,
- Future<typename std::result_of<F()>::type> >::type
- then(F&& func);
-
- /// Variant where func returns a Future<T> instead of a T. e.g.
- ///
- /// Future<string> f2 = f1.then(
- /// [](Try<T>&&) { return makeFuture<string>("foo"); });
- template <class F>
- typename std::enable_if<
- isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
- Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
- then(F&& func);
-
- /// Variant where func returns a Future<T2> and takes a T directly, bypassing
- /// a Try. Any exceptions will be implicitly passed on to the resultant
- /// Future. For example,
- ///
- /// Future<int> f = makeFuture<int>(42).then(
- /// [](int i) { return makeFuture<int>(i+1); });
- template <class F>
- typename std::enable_if<
- !std::is_same<T, void>::value &&
- isFuture<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
- Future<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type::value_type> >::type
- then(F&& func);
-
- /// Like the above variant, but for void futures. That is, func takes no
- /// argument and returns a future.
- ///
- /// Future<int> f = makeFuture().then(
- /// [] { return makeFuture<int>(42); });
- template <class F>
- typename std::enable_if<
- std::is_same<T, void>::value &&
- isFuture<typename std::result_of<F()>::type>::value,
- Future<typename std::result_of<F()>::type::value_type> >::type
- then(F&& func);
-
- /// Variant where func is an ordinary function (static method, method)
- ///
- /// R doWork(Try<T>&&);
- ///
- /// Future<R> f2 = f1.then(doWork);
- ///
- /// or
- ///
- /// struct Worker {
- /// static R doWork(Try<T>&&); }
- ///
- /// Future<R> f2 = f1.then(&Worker::doWork);
- template <class = T, class R = std::nullptr_t>
- typename std::enable_if<!isFuture<R>::value, Future<R>>::type
- inline then(R(*func)(Try<T>&&)) {
- return then([func](Try<T>&& t) {
- return (*func)(std::move(t));
- });
- }
-
- /// Variant where func returns a Future<R> instead of a R. e.g.
- ///
- /// struct Worker {
- /// Future<R> doWork(Try<T>&&); }
- ///
- /// Future<R> f2 = f1.then(&Worker::doWork);
- template <class = T, class R = std::nullptr_t>
- typename std::enable_if<isFuture<R>::value, R>::type
- inline then(R(*func)(Try<T>&&)) {
- return then([func](Try<T>&& t) {
- return (*func)(std::move(t));
- });
+ template <typename F, typename R = detail::callableResult<T, F>>
+ typename R::Return then(F func) {
+ typedef typename R::Arg Arguments;
+ return thenImplementation<F, R>(std::move(func), Arguments());
}
/// Variant where func is an member function
///
/// Worker *w;
/// Future<R> f2 = f1.then(w, &Worker::doWork);
- template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
- typename std::enable_if<!isFuture<R>::value, Future<R>>::type
- inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
- return then([instance, func](Try<T>&& t) {
- return (instance->*func)(std::move(t));
- });
- }
-
- // Same as above, but func takes void instead of Try<void>&&
- template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
- typename std::enable_if<
- std::is_same<T, void>::value && !isFuture<R>::value, Future<R>>::type
- inline then(Caller *instance, R(Caller::*func)()) {
- return then([instance, func]() {
- return (instance->*func)();
- });
- }
-
- // Same as above, but func takes T&& instead of Try<T>&&
- template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
- typename std::enable_if<
- !std::is_same<T, void>::value && !isFuture<R>::value, Future<R>>::type
- inline then(
- Caller *instance,
- R(Caller::*func)(typename detail::AliasIfVoid<T>::type&&)) {
- return then([instance, func](T&& t) {
- return (instance->*func)(std::move(t));
- });
- }
-
- /// Variant where func returns a Future<R> instead of a R. e.g.
- ///
- /// struct Worker {
- /// Future<R> doWork(Try<T>&&); }
- ///
- /// Worker *w;
- /// Future<R> f2 = f1.then(w, &Worker::doWork);
- template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
- typename std::enable_if<isFuture<R>::value, R>::type
- inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
- return then([instance, func](Try<T>&& t) {
- return (instance->*func)(std::move(t));
- });
- }
-
- // Same as above, but func takes void instead of Try<void>&&
- template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
- typename std::enable_if<
- std::is_same<T, void>::value && isFuture<R>::value, R>::type
- inline then(Caller *instance, R(Caller::*func)()) {
- return then([instance, func]() {
- return (instance->*func)();
- });
- }
-
- // Same as above, but func takes T&& instead of Try<T>&&
- template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
- typename std::enable_if<
- !std::is_same<T, void>::value && isFuture<R>::value, R>::type
- inline then(
- Caller *instance,
- R(Caller::*func)(typename detail::AliasIfVoid<T>::type&&)) {
- return then([instance, func](T&& t) {
- return (instance->*func)(std::move(t));
- });
- }
+ template <typename Caller, typename R, typename... Args>
+ Future<typename isFuture<R>::Inner>
+ then(Caller *instance, R(Caller::*func)(Args...));
/// Convenience method for ignoring the value and creating a Future<void>.
/// Exceptions still propagate.
void throwIfInvalid() const;
friend class Promise<T>;
+
+ // Variant: returns a value
+ // e.g. f.then([](Try<T> t){ return t.value(); });
+ template <typename F, typename R, bool isTry, typename... Args>
+ typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
+ thenImplementation(F func, detail::argResult<isTry, F, Args...>);
+
+ // Variant: returns a Future
+ // e.g. f.then([](Try<T> t){ return makeFuture<T>(t); });
+ template <typename F, typename R, bool isTry, typename... Args>
+ typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
+ thenImplementation(F func, detail::argResult<isTry, F, Args...>);
};
/**
return e_->with_exception<Ex>(std::move(func));
}
+ template <bool isTry, typename R>
+ typename std::enable_if<isTry, R>::type get() {
+ return std::forward<R>(*this);
+ }
+
+ template <bool isTry, typename R>
+ typename std::enable_if<!isTry, R>::type get() {
+ return std::forward<R>(value());
+ }
+
private:
Contains contains_;
union {
return e_->with_exception<Ex>(std::move(func));
}
+ template <bool, typename R>
+ R get() {
+ return std::forward<R>(*this);
+ }
+
private:
bool hasValue_;
std::unique_ptr<exception_wrapper> e_{nullptr};
EXPECT_TRUE(std::is_move_assignable<Future<int>>::value);
}
+TEST(Future, then) {
+ auto f = makeFuture<string>("0")
+ .then([](){ return makeFuture<string>("1"); })
+ .then([](Try<string>&& t) { return makeFuture(t.value() + ";2"); })
+ .then([](const Try<string>&& t) { return makeFuture(t.value() + ";3"); })
+ .then([](Try<string>& t) { return makeFuture(t.value() + ";4"); })
+ .then([](const Try<string>& t) { return makeFuture(t.value() + ";5"); })
+ .then([](Try<string> t) { return makeFuture(t.value() + ";6"); })
+ .then([](const Try<string> t) { return makeFuture(t.value() + ";7"); })
+ .then([](string&& s) { return makeFuture(s + ";8"); })
+ .then([](const string&& s) { return makeFuture(s + ";9"); })
+ .then([](string& s) { return makeFuture(s + ";10"); })
+ .then([](const string& s) { return makeFuture(s + ";11"); })
+ .then([](string s) { return makeFuture(s + ";12"); })
+ .then([](const string s) { return makeFuture(s + ";13"); })
+ ;
+ EXPECT_EQ(f.value(), "1;2;3;4;5;6;7;8;9;10;11;12;13");
+}
+
TEST(Future, thenTry) {
bool flag = false;
EXPECT_EQ(f.value(), "start;static;class-static;class");
}
+TEST(Future, thenBind) {
+ auto l = []() {
+ return makeFuture("bind");
+ };
+ auto b = std::bind(l);
+ auto f = makeFuture().then(std::move(b));
+ EXPECT_EQ(f.value(), "bind");
+}
+
+TEST(Future, thenBindTry) {
+ auto l = [](Try<string>&& t) {
+ return makeFuture(t.value() + ";bind");
+ };
+ auto b = std::bind(l, std::placeholders::_1);
+ auto f = makeFuture<string>("start").then(std::move(b));
+
+ EXPECT_EQ(f.value(), "start;bind");
+}
+
TEST(Future, value) {
auto f = makeFuture(unique_ptr<int>(new int(42)));
auto up = std::move(f.value());
--- /dev/null
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+#include <thread>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+struct Widget {
+ int v_, copied_, moved_;
+ /* implicit */ Widget(int v) : v_(v), copied_(0), moved_(0) {}
+ Widget(const Widget& other)
+ : v_(other.v_), copied_(other.copied_ + 1), moved_(other.moved_) {}
+ Widget(Widget&& other) noexcept
+ : v_(other.v_), copied_(other.copied_), moved_(other.moved_ + 1) {}
+ Widget& operator=(const Widget& other)
+ { throw std::logic_error("unexpected copy assignment"); }
+ Widget& operator=(Widget&& other)
+ { throw std::logic_error("unexpected move assignment"); }
+};
+
+TEST(Then, tryConstructor) {
+ auto t = Try<Widget>(23);
+ EXPECT_EQ(t.value().v_, 23);
+ EXPECT_EQ(t.value().copied_, 0);
+ EXPECT_EQ(t.value().moved_, 1);
+}
+
+TEST(Then, makeFuture) {
+ auto future = makeFuture<Widget>(23);
+ EXPECT_EQ(future.value().v_, 23);
+ EXPECT_EQ(future.value().copied_, 0);
+ EXPECT_EQ(future.value().moved_, 2);
+}
+
+TEST(Then, TryConstRValueReference) {
+ auto future = makeFuture<Widget>(23).then(
+ [](const Try<Widget>&& t) {
+ EXPECT_EQ(t.value().copied_, 0);
+ EXPECT_EQ(t.value().moved_, 2);
+ return t.value().v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, TryRValueReference) {
+ auto future = makeFuture<Widget>(23).then(
+ [](Try<Widget>&& t) {
+ EXPECT_EQ(t.value().copied_, 0);
+ EXPECT_EQ(t.value().moved_, 2);
+ return t.value().v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, TryLValueReference) {
+ auto future = makeFuture<Widget>(23).then(
+ [](Try<Widget>& t) {
+ EXPECT_EQ(t.value().copied_, 0);
+ EXPECT_EQ(t.value().moved_, 2);
+ return t.value().v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, TryConstLValueReference) {
+ auto future = makeFuture<Widget>(23).then(
+ [](const Try<Widget>& t) {
+ EXPECT_EQ(t.value().copied_, 0);
+ EXPECT_EQ(t.value().moved_, 2);
+ return t.value().v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, TryValue) {
+ auto future = makeFuture<Widget>(23).then(
+ [](Try<Widget> t) {
+ EXPECT_EQ(t.value().copied_, 0);
+ EXPECT_EQ(t.value().moved_, 3);
+ return t.value().v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, TryConstValue) {
+ auto future = makeFuture<Widget>(23).then(
+ [](const Try<Widget> t) {
+ EXPECT_EQ(t.value().copied_, 0);
+ EXPECT_EQ(t.value().moved_, 3);
+ return t.value().v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, ConstRValueReference) {
+ auto future = makeFuture<Widget>(23).then(
+ [](const Widget&& w) {
+ EXPECT_EQ(w.copied_, 0);
+ EXPECT_EQ(w.moved_, 2);
+ return w.v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, RValueReference) {
+ auto future = makeFuture<Widget>(23).then(
+ [](Widget&& w) {
+ EXPECT_EQ(w.copied_, 0);
+ EXPECT_EQ(w.moved_, 2);
+ return w.v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, LValueReference) {
+ auto future = makeFuture<Widget>(23).then(
+ [](Widget& w) {
+ EXPECT_EQ(w.copied_, 0);
+ EXPECT_EQ(w.moved_, 2);
+ return w.v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, ConstLValueReference) {
+ auto future = makeFuture<Widget>(23).then(
+ [](const Widget& w) {
+ EXPECT_EQ(w.copied_, 0);
+ EXPECT_EQ(w.moved_, 2);
+ return w.v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, Value) {
+ auto future = makeFuture<Widget>(23).then(
+ [](Widget w) {
+ EXPECT_EQ(w.copied_, 0);
+ EXPECT_EQ(w.moved_, 3);
+ return w.v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, ConstValue) {
+ auto future = makeFuture<Widget>(23).then(
+ [](const Widget w) {
+ EXPECT_EQ(w.copied_, 0);
+ EXPECT_EQ(w.moved_, 3);
+ return w.v_;
+ });
+ EXPECT_EQ(future.value(), 23);
+}
// This file is @generated by thens.rb. Do not edit directly.
-// TODO: fails to compile with clang:dev. See task #4412111
-#ifndef __clang__
-
#include <folly/futures/test/Thens.h>
TEST(Future, thenVariants) {
{Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, Try<A>&&>);}
{Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>&&>());}
{Future<B> f = someFuture<A>().then([&](Try<A>&&){return someFuture<B>();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A> const&>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A> const&>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, Try<A> const&>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A> const&>());}
+ {Future<B> f = someFuture<A>().then([&](Try<A> const&){return someFuture<B>();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, Try<A>>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>>());}
+ {Future<B> f = someFuture<A>().then([&](Try<A>){return someFuture<B>();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>&>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>&>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, Try<A>&>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>&>());}
+ {Future<B> f = someFuture<A>().then([&](Try<A>&){return someFuture<B>();});}
{Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A&&>);}
{Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A&&>);}
{Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, A&&>);}
{Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A&&>());}
{Future<B> f = someFuture<A>().then([&](A&&){return someFuture<B>();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A const&>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A const&>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, A const&>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A const&>());}
+ {Future<B> f = someFuture<A>().then([&](A const&){return someFuture<B>();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, A>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A>());}
+ {Future<B> f = someFuture<A>().then([&](A){return someFuture<B>();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A&>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A&>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, A&>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A&>());}
+ {Future<B> f = someFuture<A>().then([&](A&){return someFuture<B>();});}
+ {Future<B> f = someFuture<A>().then([&](){return someFuture<B>();});}
{Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>&&>);}
{Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>&&>);}
{Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, Try<A>&&>);}
{Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>&&>());}
{Future<B> f = someFuture<A>().then([&](Try<A>&&){return B();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A> const&>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A> const&>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, Try<A> const&>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A> const&>());}
+ {Future<B> f = someFuture<A>().then([&](Try<A> const&){return B();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, Try<A>>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>>());}
+ {Future<B> f = someFuture<A>().then([&](Try<A>){return B();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>&>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>&>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, Try<A>&>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>&>());}
+ {Future<B> f = someFuture<A>().then([&](Try<A>&){return B();});}
{Future<B> f = someFuture<A>().then(&aFunction<B, A&&>);}
{Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A&&>);}
{Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, A&&>);}
{Future<B> f = someFuture<A>().then(aStdFunction<B, A&&>());}
{Future<B> f = someFuture<A>().then([&](A&&){return B();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<B, A const&>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A const&>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, A const&>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<B, A const&>());}
+ {Future<B> f = someFuture<A>().then([&](A const&){return B();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<B, A>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, A>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<B, A>());}
+ {Future<B> f = someFuture<A>().then([&](A){return B();});}
+ {Future<B> f = someFuture<A>().then(&aFunction<B, A&>);}
+ {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A&>);}
+ {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, A&>);}
+ {Future<B> f = someFuture<A>().then(aStdFunction<B, A&>());}
+ {Future<B> f = someFuture<A>().then([&](A&){return B();});}
+ {Future<B> f = someFuture<A>().then([&](){return B();});}
}
-#endif
return makeFuture(T());
}
-template <class Ret, class... Params, typename = void>
-Ret aFunction(Params...);
-
template <class Ret, class... Params>
typename std::enable_if<isFuture<Ret>::value, Ret>::type
aFunction(Params...) {
}
class SomeClass {
- B b;
public:
- template <class Ret, class... Params>
- static Ret aStaticMethod(Params...);
-
template <class Ret, class... Params>
static
typename std::enable_if<!isFuture<Ret>::value, Ret>::type
return makeFuture(T());
}
- template <class Ret, class... Params>
- Ret aMethod(Params...);
-
template <class Ret, class... Params>
typename std::enable_if<!isFuture<Ret>::value, Ret>::type
aMethod(Params...) {
#!/usr/bin/env ruby
-# ruby thens.rb > Thens.cpp
+# ruby folly/futures/test/thens.rb > folly/futures/test/Thens.cpp
# An exercise in combinatorics.
# (ordinary/static function, member function, std::function, lambda)
]
param_types = [
"Try<A>&&",
- #"Try<A> const&",
- #"Try<A>",
- #"Try<A>&",
+ "Try<A> const&",
+ "Try<A>",
+ "Try<A>&",
"A&&",
- #"A const&",
- #"A",
- #"A&",
- #"",
+ "A const&",
+ "A",
+ "A&",
+ "",
]
tests = (
return_types.map { |ret|
param_types.map { |param|
- both = "#{ret}, #{param}"
- [
- ["&aFunction<#{both}>"],
- ["&SomeClass::aStaticMethod<#{both}>"],
- # TODO switch these around (std::bind-style)
- ["&anObject", "&SomeClass::aMethod<#{both}>"],
- ["aStdFunction<#{both}>()"],
- ["[&](#{param}){return #{retval(ret)};}"],
- ]
+ if param != "" then
+ both = "#{ret}, #{param}"
+ [
+ ["&aFunction<#{both}>"],
+ ["&SomeClass::aStaticMethod<#{both}>"],
+ # TODO switch these around (std::bind-style)
+ ["&anObject", "&SomeClass::aMethod<#{both}>"],
+ ["aStdFunction<#{both}>()"],
+ ["[&](#{param}){return #{retval(ret)};}"],
+ ]
+ else
+ [["[&](){return #{retval(ret)};}"]]
+ end
}
- }.flatten(2) + [
- #[""],
- ]
+ }.flatten(2)
).map {|a| test(a)}.flatten
print <<EOF
// This file is #{"@"}generated by thens.rb. Do not edit directly.
-// TODO: fails to compile with clang:dev. See task #4412111
-#ifndef __clang__
-
#include <folly/futures/test/Thens.h>
TEST(Future, thenVariants) {
#{tests.join("\n ")}
}
-#endif
EOF