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