return then([] (Try<T>&& t) {});
}
+// onError where the callback returns T
+template <class T>
+template <class F>
+typename std::enable_if<
+ !detail::Extract<F>::ReturnsFuture::value,
+ Future<T>>::type
+Future<T>::onError(F&& func) {
+ typedef typename detail::Extract<F>::FirstArg Exn;
+ static_assert(
+ std::is_same<typename detail::Extract<F>::RawReturn, T>::value,
+ "Return type of onError callback must be T or Future<T>");
+
+ Promise<T> p;
+ auto f = p.getFuture();
+ auto pm = folly::makeMoveWrapper(std::move(p));
+ auto funcm = folly::makeMoveWrapper(std::move(func));
+ setCallback_([pm, funcm](Try<T>&& t) mutable {
+ try {
+ t.throwIfFailed();
+ } catch (Exn& e) {
+ pm->fulfil([&]{
+ return (*funcm)(e);
+ });
+ return;
+ } catch (...) {
+ // fall through
+ }
+ pm->fulfilTry(std::move(t));
+ });
+
+ return f;
+}
+
+// onError where the callback returns Future<T>
+template <class T>
+template <class F>
+typename std::enable_if<
+ detail::Extract<F>::ReturnsFuture::value,
+ Future<T>>::type
+Future<T>::onError(F&& func) {
+ static_assert(
+ std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
+ "Return type of onError callback must be T or Future<T>");
+ typedef typename detail::Extract<F>::FirstArg Exn;
+
+ Promise<T> p;
+ auto f = p.getFuture();
+ auto pm = folly::makeMoveWrapper(std::move(p));
+ auto funcm = folly::makeMoveWrapper(std::move(func));
+ setCallback_([pm, funcm](Try<T>&& t) mutable {
+ try {
+ t.throwIfFailed();
+ } catch (Exn& e) {
+ try {
+ auto f2 = (*funcm)(e);
+ f2.setCallback_([pm](Try<T>&& t2) mutable {
+ pm->fulfilTry(std::move(t2));
+ });
+ } catch (...) {
+ pm->setException(std::current_exception());
+ }
+ return;
+ } catch (...) {
+ // fall through
+ }
+ pm->fulfilTry(std::move(t));
+ });
+
+ return f;
+}
+
template <class T>
typename std::add_lvalue_reference<T>::type Future<T>::value() {
throwIfInvalid();
namespace folly { namespace wangle {
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 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 <typename T>
+struct IsFuture : std::integral_constant<bool, false> {
+ typedef T Inner;
+};
+
+template <template <typename T> class Future, typename T>
+struct IsFuture<Future<T>> : std::integral_constant<bool, true> {
+ typedef T Inner;
+};
+
+template <typename...>
+struct ArgType;
+
+template <typename Arg, typename... Args>
+struct ArgType<Arg, Args...> {
+ typedef Arg FirstArg;
+};
+
+template <>
+struct ArgType<> {
+ typedef void FirstArg;
+};
+
+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 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 Future<typename ReturnsFuture::Inner> Return;
+ typedef typename ReturnsFuture::Inner RawReturn;
+ typedef typename ArgType<Args...>::FirstArg FirstArg;
+};
+
+} // detail
template <class> struct Promise;
/// Exceptions still propagate.
Future<void> then();
+ /// Set an error callback for this Future. The callback should take a single
+ /// argument of the type that you want to catch, and should return a value of
+ /// the same type as this Future, or a Future of that type (see overload
+ /// below). For instance,
+ ///
+ /// makeFuture()
+ /// .then([] {
+ /// throw std::runtime_error("oh no!");
+ /// return 42;
+ /// })
+ /// .onError([] (std::runtime_error& e) {
+ /// LOG(INFO) << "std::runtime_error: " << e.what();
+ /// return -1; // or makeFuture<int>(-1)
+ /// });
+ template <class F>
+ typename std::enable_if<
+ !detail::Extract<F>::ReturnsFuture::value,
+ Future<T>>::type
+ onError(F&& func);
+
+ /// Overload of onError where the error callback returns a Future<T>
+ template <class F>
+ typename std::enable_if<
+ detail::Extract<F>::ReturnsFuture::value,
+ Future<T>>::type
+ onError(F&& func);
+
/// This is not the method you're looking for.
///
/// This needs to be public because it's used by make* and when*, and it's
}
public:
- ThreadExecutor(size_t n = 1024)
+ explicit ThreadExecutor(size_t n = 1024)
: funcs(n), worker(std::bind(&ThreadExecutor::work, this)) {}
~ThreadExecutor() {
// Future
+TEST(Future, onError) {
+ bool theFlag = false;
+ auto flag = [&]{ theFlag = true; };
+#define EXPECT_FLAG() \
+ do { \
+ EXPECT_TRUE(theFlag); \
+ theFlag = false; \
+ } while(0);
+
+#define EXPECT_NO_FLAG() \
+ do { \
+ EXPECT_FALSE(theFlag); \
+ theFlag = false; \
+ } while(0);
+
+ // By reference
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (eggs_t& e) { flag(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (eggs_t& e) { flag(); return makeFuture(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ // By value
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (eggs_t e) { flag(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (eggs_t e) { flag(); return makeFuture(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ // Polymorphic
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (std::exception& e) { flag(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (std::exception& e) { flag(); return makeFuture(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ // Non-exceptions
+ {
+ auto f = makeFuture()
+ .then([] { throw -1; })
+ .onError([&] (int e) { flag(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ {
+ auto f = makeFuture()
+ .then([] { throw -1; })
+ .onError([&] (int e) { flag(); return makeFuture(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ // Mutable lambda
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (eggs_t& e) mutable { flag(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (eggs_t& e) mutable { flag(); return makeFuture(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ // No throw
+ {
+ auto f = makeFuture()
+ .then([] { return 42; })
+ .onError([&] (eggs_t& e) { flag(); return -1; });
+ EXPECT_NO_FLAG();
+ EXPECT_EQ(42, f.value());
+ }
+
+ {
+ auto f = makeFuture()
+ .then([] { return 42; })
+ .onError([&] (eggs_t& e) { flag(); return makeFuture<int>(-1); });
+ EXPECT_NO_FLAG();
+ EXPECT_EQ(42, f.value());
+ }
+
+ // Catch different exception
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (std::runtime_error& e) { flag(); });
+ EXPECT_NO_FLAG();
+ EXPECT_THROW(f.value(), eggs_t);
+ }
+
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (std::runtime_error& e) { flag(); return makeFuture(); });
+ EXPECT_NO_FLAG();
+ EXPECT_THROW(f.value(), eggs_t);
+ }
+
+ // Returned value propagates
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; return 0; })
+ .onError([&] (eggs_t& e) { return 42; });
+ EXPECT_EQ(42, f.value());
+ }
+
+ // Returned future propagates
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; return 0; })
+ .onError([&] (eggs_t& e) { return makeFuture<int>(42); });
+ EXPECT_EQ(42, f.value());
+ }
+
+ // Throw in callback
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; return 0; })
+ .onError([&] (eggs_t& e) { throw e; return -1; });
+ EXPECT_THROW(f.value(), eggs_t);
+ }
+
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; return 0; })
+ .onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
+ EXPECT_THROW(f.value(), eggs_t);
+ }
+}
+
TEST(Future, try) {
class A {
public: