template <class T>
template <class F>
typename std::enable_if<
+ !detail::callableWith<F, exception_wrapper>::value &&
!detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) {
template <class T>
template <class F>
typename std::enable_if<
+ !detail::callableWith<F, exception_wrapper>::value &&
detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) {
.onError([funcw](TimedOut const&) { return (*funcw)(); });
}
+template <class T>
+template <class F>
+typename std::enable_if<
+ detail::callableWith<F, exception_wrapper>::value &&
+ 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>");
+
+ 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 {
+ if (t.hasException()) {
+ try {
+ auto f2 = (*funcm)(std::move(t.exception()));
+ f2.setCallback_([pm](Try<T> t2) mutable {
+ pm->fulfilTry(std::move(t2));
+ });
+ } catch (const std::exception& e2) {
+ pm->setException(exception_wrapper(std::current_exception(), e2));
+ } catch (...) {
+ pm->setException(exception_wrapper(std::current_exception()));
+ }
+ } else {
+ pm->fulfilTry(std::move(t));
+ }
+ });
+
+ return f;
+}
+
+// onError(exception_wrapper) that returns T
+template <class T>
+template <class F>
+typename std::enable_if<
+ detail::callableWith<F, exception_wrapper>::value &&
+ !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>");
+
+ 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 {
+ if (t.hasException()) {
+ pm->fulfil([&]{
+ return (*funcm)(std::move(t.exception()));
+ });
+ } else {
+ pm->fulfilTry(std::move(t));
+ }
+ });
+
+ return f;
+}
+
template <class T>
typename std::add_lvalue_reference<T>::type Future<T>::value() {
throwIfInvalid();
/// });
template <class F>
typename std::enable_if<
+ !detail::callableWith<F, exception_wrapper>::value &&
!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::callableWith<F, exception_wrapper>::value &&
detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func);
+ /// Overload of onError that takes exception_wrapper and returns Future<T>
+ template <class F>
+ typename std::enable_if<
+ detail::callableWith<F, exception_wrapper>::value &&
+ detail::Extract<F>::ReturnsFuture::value,
+ Future<T>>::type
+ onError(F&& func);
+
+ /// Overload of onError that takes exception_wrapper and returns T
+ template <class F>
+ typename std::enable_if<
+ detail::callableWith<F, exception_wrapper>::value &&
+ !detail::Extract<F>::ReturnsFuture::value,
+ Future<T>>::type
+ onError(F&& func);
+
/// func is like std::function<void()> and is executed unconditionally, and
/// the value/exception is passed through to the resulting Future.
/// func shouldn't throw, but if it does it will be captured and propagated,
.onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
EXPECT_THROW(f.value(), eggs_t);
}
+
+ // exception_wrapper, return Future<T>
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (exception_wrapper e) { flag(); return makeFuture(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ // exception_wrapper, return Future<T> but throw
+ {
+ auto f = makeFuture()
+ .then([]{ throw eggs; return 0; })
+ .onError([&] (exception_wrapper e) {
+ flag();
+ throw eggs;
+ return makeFuture<int>(-1);
+ });
+ EXPECT_FLAG();
+ EXPECT_THROW(f.value(), eggs_t);
+ }
+
+ // exception_wrapper, return T
+ {
+ auto f = makeFuture()
+ .then([]{ throw eggs; return 0; })
+ .onError([&] (exception_wrapper e) {
+ flag();
+ return -1;
+ });
+ EXPECT_FLAG();
+ EXPECT_EQ(-1, f.value());
+ }
+
+ // exception_wrapper, return T but throw
+ {
+ auto f = makeFuture()
+ .then([]{ throw eggs; return 0; })
+ .onError([&] (exception_wrapper e) {
+ flag();
+ throw eggs;
+ return -1;
+ });
+ EXPECT_FLAG();
+ EXPECT_THROW(f.value(), eggs_t);
+ }
+
+ // const exception_wrapper&
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (const exception_wrapper& e) {
+ flag();
+ return makeFuture();
+ });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
}
TEST(Future, try) {