Commit 0b2ff089 authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Consistency with folly::SemiFuture continuations 5/n: make deferError use the...

Consistency with folly::SemiFuture continuations 5/n: make deferError use the same exception unpacking machinery as thenError

Summary:
Step 5 in adding full set of r-value-qualified unambiguous continuation methods to folly::Future for consistency with folly::SemiFuture.
 * Back point explicit exceptions election to deferError.

Reviewed By: marshallcline

Differential Revision: D8124828

fbshipit-source-id: 123b1cf460d2df4c8f359eff7329d34fbd8e0a9b
parent e82284a8
...@@ -777,39 +777,13 @@ SemiFuture<T>::deferValue(F&& func) && { ...@@ -777,39 +777,13 @@ SemiFuture<T>::deferValue(F&& func) && {
} }
template <class T> template <class T>
template <class F> template <class ExceptionType, class F>
typename std::enable_if< SemiFuture<T> SemiFuture<T>::deferError(F&& func) && {
!futures::detail::callableWith<F, exception_wrapper>::value &&
!futures::detail::callableWith<F, exception_wrapper&>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
SemiFuture<T>>::type
SemiFuture<T>::deferError(F&& func) && {
using Exn =
std::remove_reference_t<typename futures::detail::Extract<F>::FirstArg>;
return std::move(*this).defer(
[func = std::forward<F>(func)](Try<T>&& t) mutable {
if (auto e = t.template tryGetExceptionObject<Exn>()) {
return makeSemiFuture<T>(makeTryWith([&]() { return func(*e); }));
} else {
return makeSemiFuture<T>(std::move(t));
}
});
}
template <class T>
template <class F>
typename std::enable_if<
!futures::detail::callableWith<F, exception_wrapper>::value &&
!futures::detail::callableWith<F, exception_wrapper&>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
SemiFuture<T>>::type
SemiFuture<T>::deferError(F&& func) && {
using Exn =
std::remove_reference_t<typename futures::detail::Extract<F>::FirstArg>;
return std::move(*this).defer( return std::move(*this).defer(
[func = std::forward<F>(func)](Try<T>&& t) mutable { [func = std::forward<F>(func)](Try<T>&& t) mutable {
if (auto e = t.template tryGetExceptionObject<Exn>()) { if (auto e = t.template tryGetExceptionObject<ExceptionType>()) {
return func(*e); return makeSemiFutureWith(
[&]() mutable { return std::forward<F>(func)(*e); });
} else { } else {
return makeSemiFuture<T>(std::move(t)); return makeSemiFuture<T>(std::move(t));
} }
...@@ -818,32 +792,13 @@ SemiFuture<T>::deferError(F&& func) && { ...@@ -818,32 +792,13 @@ SemiFuture<T>::deferError(F&& func) && {
template <class T> template <class T>
template <class F> template <class F>
typename std::enable_if< SemiFuture<T> SemiFuture<T>::deferError(F&& func) && {
futures::detail::callableWith<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
SemiFuture<T>>::type
SemiFuture<T>::deferError(F&& func) && {
return std::move(*this).defer( return std::move(*this).defer(
[func = std::forward<F>(func)](Try<T> t) mutable { [func = std::forward<F>(func)](Try<T> t) mutable {
if (t.hasException()) { if (t.hasException()) {
return makeSemiFuture<T>(func(std::move(t.exception()))); return makeSemiFutureWith([&]() mutable {
} else { return std::forward<F>(func)(std::move(t.exception()));
return makeSemiFuture<T>(std::move(t));
}
}); });
}
template <class T>
template <class F>
typename std::enable_if<
futures::detail::callableWith<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
SemiFuture<T>>::type
SemiFuture<T>::deferError(F&& func) && {
return std::move(*this).defer(
[func = std::forward<F>(func)](Try<T> t) mutable {
if (t.hasException()) {
return func(std::move(t.exception()));
} else { } else {
return makeSemiFuture<T>(std::move(t)); return makeSemiFuture<T>(std::move(t));
} }
......
...@@ -441,50 +441,37 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -441,50 +441,37 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/// Set an error continuation for this SemiFuture. The continuation should /// Set an error continuation for this SemiFuture. The continuation should
/// take a single argument of the type that you want to catch, and should /// take a single argument of the type that you want to catch, and should
/// return a value of the same type as this SemiFuture, or a SemiFuture of /// return a `T`, `Future<T>` or `SemiFuture<`T`> (see overload below).
/// that type (see overload below). For instance, /// For instance,
/// ///
/// makeSemiFuture() /// makeSemiFuture()
/// .defer([] { /// .defer([] {
/// throw std::runtime_error("oh no!"); /// throw std::runtime_error("oh no!");
/// return 42; /// return 42;
/// }) /// })
/// .deferError([] (std::runtime_error& e) { /// .deferError<std::runtime_error>([] (auto const& e) {
/// LOG(INFO) << "std::runtime_error: " << e.what(); /// LOG(INFO) << "std::runtime_error: " << e.what();
/// return -1; // or makeSemiFuture<int>(-1) /// return -1; // or makeSemiFuture<int>(-1)
/// }); /// });
template <class F> /// Overload of deferError where continuation can be called with a known
typename std::enable_if< /// exception type and returns T, Future<T> or SemiFuture<T>
!futures::detail::callableWith<F, exception_wrapper>::value && template <class ExceptionType, class F>
!futures::detail::callableWith<F, exception_wrapper&>::value && SemiFuture<T> deferError(F&& func) &&;
!futures::detail::Extract<F>::ReturnsFuture::value,
SemiFuture<T>>::type
deferError(F&& func) &&;
/// Overload of deferError where the error continuation returns a Future<T> template <class ExceptionType, class R, class... Args>
template <class F> SemiFuture<T> deferError(R (&func)(Args...)) && {
typename std::enable_if< return std::move(*this).template deferError<ExceptionType>(&func);
!futures::detail::callableWith<F, exception_wrapper>::value && }
!futures::detail::callableWith<F, exception_wrapper&>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
SemiFuture<T>>::type
deferError(F&& func) &&;
/// Overload of deferError that takes exception_wrapper and returns T /// Overload of deferError where continuation can be called with
/// exception_wrapper&& and returns T, Future<T> or SemiFuture<T>
template <class F> template <class F>
typename std::enable_if< SemiFuture<T> deferError(F&& func) &&;
futures::detail::callableWith<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
SemiFuture<T>>::type
deferError(F&& func) &&;
/// Overload of deferError that takes exception_wrapper and returns Future<T> template <class R, class... Args>
template <class F> SemiFuture<T> deferError(R (&func)(Args...)) && {
typename std::enable_if< return std::move(*this).deferError(&func);
futures::detail::callableWith<F, exception_wrapper>::value && }
futures::detail::Extract<F>::ReturnsFuture::value,
SemiFuture<T>>::type
deferError(F&& func) &&;
/// Return a future that completes inline, as if the future had no executor. /// Return a future that completes inline, as if the future had no executor.
/// Intended for porting legacy code without behavioural change, and for rare /// Intended for porting legacy code without behavioural change, and for rare
......
...@@ -257,7 +257,7 @@ namespace { ...@@ -257,7 +257,7 @@ namespace {
SemiFuture<int> onErrorHelperEggs(const eggs_t&) { SemiFuture<int> onErrorHelperEggs(const eggs_t&) {
return makeSemiFuture(10); return makeSemiFuture(10);
} }
SemiFuture<int> onErrorHelperGeneric(const std::exception&) { SemiFuture<int> onErrorHelperGeneric(const folly::exception_wrapper&) {
return makeSemiFuture(20); return makeSemiFuture(20);
} }
} // namespace } // namespace
...@@ -782,24 +782,43 @@ TEST(SemiFuture, onError) { ...@@ -782,24 +782,43 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](eggs_t& /* e */) { flag(); }); .deferError<eggs_t>([&](eggs_t const& /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
{ {
auto f = auto f = makeSemiFuture()
makeSemiFuture().defer(deferHelper).deferError([&](eggs_t& /* e */) { .defer(deferHelper)
.deferError<eggs_t>([&](eggs_t const& /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<eggs_t>([&](eggs_t const& /* e */) {
flag(); flag();
return makeSemiFuture();
}); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
// By auto reference
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](eggs_t& /* e */) { .deferError<eggs_t>([&](auto& /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<eggs_t>([&](auto& /* e */) {
flag(); flag();
return makeSemiFuture(); return makeSemiFuture();
}); });
...@@ -811,7 +830,7 @@ TEST(SemiFuture, onError) { ...@@ -811,7 +830,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](eggs_t /* e */) { flag(); }); .deferError<eggs_t>([&](eggs_t /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
...@@ -819,7 +838,7 @@ TEST(SemiFuture, onError) { ...@@ -819,7 +838,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](eggs_t /* e */) { .deferError<eggs_t>([&](eggs_t /* e */) {
flag(); flag();
return makeSemiFuture(); return makeSemiFuture();
}); });
...@@ -827,11 +846,24 @@ TEST(SemiFuture, onError) { ...@@ -827,11 +846,24 @@ TEST(SemiFuture, onError) {
EXPECT_FLAG(); EXPECT_FLAG();
} }
// Polymorphic // auto value
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](std::exception& /* e */) { flag(); }); .deferError<eggs_t>([&](auto /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
// Polymorphic
{
auto f =
makeSemiFuture()
.defer([] { throw eggs; })
.deferError<std::exception>([&](auto const& /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
...@@ -839,7 +871,7 @@ TEST(SemiFuture, onError) { ...@@ -839,7 +871,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](std::exception& /* e */) { .deferError<std::exception>([&](auto const& /* e */) {
flag(); flag();
return makeSemiFuture(); return makeSemiFuture();
}); });
...@@ -849,17 +881,17 @@ TEST(SemiFuture, onError) { ...@@ -849,17 +881,17 @@ TEST(SemiFuture, onError) {
// Non-exceptions // Non-exceptions
{ {
auto f = auto f = makeSemiFuture()
makeSemiFuture().defer([] { throw - 1; }).deferError([&](int /* e */) { .defer([] { throw - 1; })
flag(); .deferError<int>([&](auto /* e */) { flag(); });
});
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
{ {
auto f = auto f = makeSemiFuture()
makeSemiFuture().defer([] { throw - 1; }).deferError([&](int /* e */) { .defer([] { throw - 1; })
.deferError<int>([&](auto /* e */) {
flag(); flag();
return makeSemiFuture(); return makeSemiFuture();
}); });
...@@ -869,9 +901,10 @@ TEST(SemiFuture, onError) { ...@@ -869,9 +901,10 @@ TEST(SemiFuture, onError) {
// Mutable lambda // Mutable lambda
{ {
auto f = makeSemiFuture() auto f =
makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](eggs_t& /* e */) mutable { flag(); }); .deferError<eggs_t>([&](auto const& /* e */) mutable { flag(); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
...@@ -879,7 +912,7 @@ TEST(SemiFuture, onError) { ...@@ -879,7 +912,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](eggs_t& /* e */) mutable { .deferError<eggs_t>([&](auto const& /* e */) mutable {
flag(); flag();
return makeSemiFuture(); return makeSemiFuture();
}); });
...@@ -891,21 +924,21 @@ TEST(SemiFuture, onError) { ...@@ -891,21 +924,21 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([]() -> int { throw eggs; }) .defer([]() -> int { throw eggs; })
.deferError(onErrorHelperEggs) .deferError<eggs_t>(onErrorHelperEggs)
.deferError(onErrorHelperGeneric); .deferError(onErrorHelperGeneric);
EXPECT_EQ(10, std::move(f).get()); EXPECT_EQ(10, std::move(f).get());
} }
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([]() -> int { throw std::runtime_error("test"); }) .defer([]() -> int { throw std::runtime_error("test"); })
.deferError(onErrorHelperEggs) .deferError<eggs_t>(onErrorHelperEggs)
.deferError(onErrorHelperGeneric); .deferError(onErrorHelperGeneric);
EXPECT_EQ(20, std::move(f).get()); EXPECT_EQ(20, std::move(f).get());
} }
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([]() -> int { throw std::runtime_error("test"); }) .defer([]() -> int { throw std::runtime_error("test"); })
.deferError(onErrorHelperEggs); .deferError<eggs_t>(onErrorHelperEggs);
EXPECT_THROW(std::move(f).get(), std::runtime_error); EXPECT_THROW(std::move(f).get(), std::runtime_error);
} }
...@@ -913,7 +946,7 @@ TEST(SemiFuture, onError) { ...@@ -913,7 +946,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { return 42; }) .defer([] { return 42; })
.deferError([&](eggs_t& /* e */) { .deferError<eggs_t>([&](eggs_t& /* e */) {
flag(); flag();
return -1; return -1;
}); });
...@@ -925,7 +958,7 @@ TEST(SemiFuture, onError) { ...@@ -925,7 +958,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { return 42; }) .defer([] { return 42; })
.deferError([&](eggs_t& /* e */) { .deferError<eggs_t>([&](auto const& /* e */) {
flag(); flag();
return makeSemiFuture<int>(-1); return makeSemiFuture<int>(-1);
}); });
...@@ -937,7 +970,8 @@ TEST(SemiFuture, onError) { ...@@ -937,7 +970,8 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](std::runtime_error& /* e */) { flag(); }); .deferError<std::runtime_error>(
[&](auto const& /* e */) { flag(); });
EXPECT_THROW(std::move(f).get(), eggs_t); EXPECT_THROW(std::move(f).get(), eggs_t);
EXPECT_NO_FLAG(); EXPECT_NO_FLAG();
} }
...@@ -945,7 +979,7 @@ TEST(SemiFuture, onError) { ...@@ -945,7 +979,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([] { throw eggs; }) .defer([] { throw eggs; })
.deferError([&](std::runtime_error& /* e */) { .deferError<std::runtime_error>([&](auto const& /* e */) {
flag(); flag();
return makeSemiFuture(); return makeSemiFuture();
}); });
...@@ -957,7 +991,7 @@ TEST(SemiFuture, onError) { ...@@ -957,7 +991,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([]() -> int { throw eggs; }) .defer([]() -> int { throw eggs; })
.deferError([&](eggs_t& /* e */) { return 42; }); .deferError<eggs_t>([&](auto const& /* e */) { return 42; });
EXPECT_EQ(42, std::move(f).get()); EXPECT_EQ(42, std::move(f).get());
} }
...@@ -965,8 +999,9 @@ TEST(SemiFuture, onError) { ...@@ -965,8 +999,9 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([]() -> int { throw eggs; }) .defer([]() -> int { throw eggs; })
.deferError( .deferError<eggs_t>([&](auto const& /* e */) {
[&](eggs_t& /* e */) { return makeSemiFuture<int>(42); }); return makeSemiFuture<int>(42);
});
EXPECT_EQ(42, std::move(f).get()); EXPECT_EQ(42, std::move(f).get());
} }
...@@ -974,14 +1009,15 @@ TEST(SemiFuture, onError) { ...@@ -974,14 +1009,15 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([]() -> int { throw eggs; }) .defer([]() -> int { throw eggs; })
.deferError([&](eggs_t& e) -> int { throw e; }); .deferError<eggs_t>([&](auto const& e) -> int { throw e; });
EXPECT_THROW(std::move(f).get(), eggs_t); EXPECT_THROW(std::move(f).get(), eggs_t);
} }
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([]() -> int { throw eggs; }) .defer([]() -> int { throw eggs; })
.deferError([&](eggs_t& e) -> SemiFuture<int> { throw e; }); .deferError<eggs_t>(
[&](auto const& e) -> SemiFuture<int> { throw e; });
EXPECT_THROW(std::move(f).get(), eggs_t); EXPECT_THROW(std::move(f).get(), eggs_t);
} }
......
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