core_->setCallback(std::move(func));
}
+// Variant: f.then([](Try<T>&& t){ return t.value(); });
template <class T>
template <class F>
typename std::enable_if<
return std::move(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()) {
+ p->setException(t.getException());
+ } else {
+ p->fulfil([&]() {
+ return (*funcm)(std::move(t.value()));
+ });
+ }
+ });
+
+ return std::move(f);
+}
+
+// Variant: f.then([](){ return; });
+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(t.getException());
+ } else {
+ p->fulfil([&]() {
+ return (*funcm)();
+ });
+ }
+ });
+
+ return std::move(f);
+}
+
+// Variant: f.then([](Try<T>&& t){ return makeFuture<T>(t.value()); });
template <class T>
template <class F>
typename std::enable_if<
return std::move(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()) {
+ p->setException(t.getException());
+ } else {
+ try {
+ auto f2 = (*funcm)(std::move(t.value()));
+ f2.setCallback_([p](Try<B>&& b) mutable {
+ p->fulfilTry(std::move(b));
+ });
+ } catch (...) {
+ p->setException(std::current_exception());
+ }
+ }
+ });
+
+ return std::move(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.getException());
+ } else {
+ try {
+ auto f2 = (*funcm)();
+ f2.setCallback_([p](Try<B>&& b) mutable {
+ p->fulfilTry(std::move(b));
+ });
+ } catch (...) {
+ p->setException(std::current_exception());
+ }
+ }
+ });
+
+ return std::move(f);
+}
+
template <class T>
Future<void> Future<T>::then() {
return then([] (Try<T>&& t) {});
namespace detail {
template <class> struct Core;
template <class...> struct VariadicContext;
+
+ template <class T>
+ struct AliasIfVoid {
+ typedef typename std::conditional<
+ std::is_same<T, void>::value,
+ int,
+ T>::type type;
+ };
}
+
template <class> struct Promise;
template <typename T> struct isFuture;
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(
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>&&);
});
}
+ // 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 {
});
}
+ // 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));
+ });
+ }
+
/// Convenience method for ignoring the value and creating a Future<void>.
/// Exceptions still propagate.
Future<void> then();
#include <type_traits>
#include <exception>
#include <algorithm>
+#include <folly/Likely.h>
+#include <folly/wangle/WangleException.h>
namespace folly { namespace wangle {
bool hasValue() const { return contains_ == Contains::VALUE; }
bool hasException() const { return contains_ == Contains::EXCEPTION; }
+ std::exception_ptr getException() const {
+ if (UNLIKELY(!hasException())) {
+ throw WangleException(
+ "getException(): Try does not contain an exception");
+ }
+ return e_;
+ }
+
private:
Contains contains_;
union {
bool hasValue() const { return hasValue_; }
bool hasException() const { return !hasValue_; }
+ std::exception_ptr getException() const {
+ if (UNLIKELY(!hasException())) {
+ throw WangleException(
+ "getException(): Try does not contain an exception");
+ }
+ return e_;
+ }
+
private:
bool hasValue_;
std::exception_ptr e_;
EXPECT_TRUE(std::is_move_assignable<Future<int>>::value);
}
-TEST(Future, then) {
+TEST(Future, thenTry) {
bool flag = false;
makeFuture<int>(42).then([&](Try<int>&& t) {
EXPECT_TRUE(f.isReady());
}
+TEST(Future, thenValue) {
+ bool flag = false;
+ makeFuture<int>(42).then([&](int i){
+ EXPECT_EQ(42, i);
+ flag = true;
+ });
+ EXPECT_TRUE(flag); flag = false;
+
+ makeFuture<int>(42)
+ .then([](int i){ return i; })
+ .then([&](int i) { flag = true; EXPECT_EQ(42, i); });
+ EXPECT_TRUE(flag); flag = false;
+
+ makeFuture().then([&]{
+ flag = true;
+ });
+ EXPECT_TRUE(flag); flag = false;
+
+ auto f = makeFuture<int>(eggs).then([&](int i){});
+ EXPECT_THROW(f.value(), eggs_t);
+
+ f = makeFuture<void>(eggs).then([&]{});
+ EXPECT_THROW(f.value(), eggs_t);
+}
+
+TEST(Future, thenValueFuture) {
+ bool flag = false;
+ makeFuture<int>(42)
+ .then([](int i){ return makeFuture<int>(std::move(i)); })
+ .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
+ EXPECT_TRUE(flag); flag = false;
+
+ makeFuture()
+ .then([]{ return makeFuture(); })
+ .then([&](Try<void>&& t) { flag = true; });
+ EXPECT_TRUE(flag); flag = false;
+}
+
static string doWorkStatic(Try<string>&& t) {
return t.value() + ";static";
}
-// This file is @generated by thens.rb
+// 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/wangle/test/Thens.h>
+#ifndef __clang__
+// TODO: fails to compile with clang:dev. See task #4412111
+
TEST(Future, thenVariants) {
SomeClass anObject;
Executor* anExecutor;
- {Future<B> f = someFuture<A>().then(aFunction<Future<B>, Try<A>&&>);}
+ {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<B, Try<A>&&>);}
+ {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<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();});}
}
#endif
#"Try<A> const&",
#"Try<A>",
#"Try<A>&",
- #"A&&",
+ "A&&",
#"A const&",
#"A",
#"A&",
param_types.map { |param|
both = "#{ret}, #{param}"
[
- ["aFunction<#{both}>"],
+ ["&aFunction<#{both}>"],
["&SomeClass::aStaticMethod<#{both}>"],
# TODO switch these around (std::bind-style)
["&anObject", "&SomeClass::aMethod<#{both}>"],
#include <folly/wangle/test/Thens.h>
+#ifndef __clang__
+// TODO: fails to compile with clang:dev. See task #4412111
+
TEST(Future, thenVariants) {
SomeClass anObject;
Executor* anExecutor;