Commit f44d1a08 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

SemiFuture::deferError and Future::thenError taking tag

Summary: [Folly] SemiFuture::deferError and Future::thenError overloads taking exception-type-carrying tag to distinguish the exception type. Solves the case where `.deferError` or `.thenError` is called on an object with a dependent type, where passing the exception type otherwise requires the `template` keyword.

Reviewed By: LeeHowes

Differential Revision: D13855497

fbshipit-source-id: 74200853043e3fdbc08419b33b959f3519d704ef
parent 515bf2bb
...@@ -932,7 +932,7 @@ SemiFuture<T>::deferValue(F&& func) && { ...@@ -932,7 +932,7 @@ SemiFuture<T>::deferValue(F&& func) && {
template <class T> template <class T>
template <class ExceptionType, class F> template <class ExceptionType, class F>
SemiFuture<T> SemiFuture<T>::deferError(F&& func) && { SemiFuture<T> SemiFuture<T>::deferError(tag_t<ExceptionType>, 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 (auto e = t.template tryGetExceptionObject<ExceptionType>()) { if (auto e = t.template tryGetExceptionObject<ExceptionType>()) {
...@@ -1114,7 +1114,7 @@ template <class ExceptionType, class F> ...@@ -1114,7 +1114,7 @@ template <class ExceptionType, class F>
typename std::enable_if< typename std::enable_if<
isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value, isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type Future<T>>::type
Future<T>::thenError(F&& func) && { Future<T>::thenError(tag_t<ExceptionType>, F&& func) && {
Promise<T> p; Promise<T> p;
auto sf = p.getSemiFuture(); auto sf = p.getSemiFuture();
auto* ePtr = this->getExecutor(); auto* ePtr = this->getExecutor();
...@@ -1146,7 +1146,7 @@ template <class ExceptionType, class F> ...@@ -1146,7 +1146,7 @@ template <class ExceptionType, class F>
typename std::enable_if< typename std::enable_if<
!isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value, !isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type Future<T>>::type
Future<T>::thenError(F&& func) && { Future<T>::thenError(tag_t<ExceptionType>, F&& func) && {
Promise<T> p; Promise<T> p;
p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler()); p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler());
auto sf = p.getSemiFuture(); auto sf = p.getSemiFuture();
...@@ -1243,7 +1243,8 @@ Future<T>::onError(F&& func) && { ...@@ -1243,7 +1243,8 @@ Future<T>::onError(F&& func) && {
// NOTE: Removes the executor to maintain historical behaviour. // NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this) return std::move(*this)
.template thenError<Exn>( .thenError(
tag_t<Exn>{},
[func = std::forward<F>(func)](auto&& ex) mutable { [func = std::forward<F>(func)](auto&& ex) mutable {
return std::forward<F>(func)(ex); return std::forward<F>(func)(ex);
}) })
...@@ -1266,7 +1267,8 @@ Future<T>::onError(F&& func) && { ...@@ -1266,7 +1267,8 @@ Future<T>::onError(F&& func) && {
// NOTE: Removes the executor to maintain historical behaviour. // NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this) return std::move(*this)
.template thenError<Exn>( .thenError(
tag_t<Exn>{},
[func = std::forward<F>(func)](auto&& ex) mutable { [func = std::forward<F>(func)](auto&& ex) mutable {
return std::forward<F>(func)(ex); return std::forward<F>(func)(ex);
}) })
...@@ -1286,7 +1288,8 @@ Future<T> Future<T>::ensure(F&& func) && { ...@@ -1286,7 +1288,8 @@ Future<T> Future<T>::ensure(F&& func) && {
template <class T> template <class T>
template <class F> template <class F>
Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) && { Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) && {
return std::move(*this).within(dur, tk).template thenError<FutureTimeout>( return std::move(*this).within(dur, tk).thenError(
tag_t<FutureTimeout>{},
[funcw = std::forward<F>(func)](auto const&) mutable { [funcw = std::forward<F>(func)](auto const&) mutable {
return std::forward<F>(funcw)(); return std::forward<F>(funcw)();
}); });
...@@ -1306,7 +1309,7 @@ Future<T>::onError(F&& func) && { ...@@ -1306,7 +1309,7 @@ Future<T>::onError(F&& func) && {
// NOTE: Removes the executor to maintain historical behaviour. // NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this) return std::move(*this)
.template thenError(std::forward<F>(func)) .thenError(std::forward<F>(func))
.via(&InlineExecutor::instance()); .via(&InlineExecutor::instance());
} }
...@@ -1325,7 +1328,7 @@ Future<T>::onError(F&& func) && { ...@@ -1325,7 +1328,7 @@ Future<T>::onError(F&& func) && {
// NOTE: Removes the executor to maintain historical behaviour. // NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this) return std::move(*this)
.template thenError(std::forward<F>(func)) .thenError(std::forward<F>(func))
.via(&InlineExecutor::instance()); .via(&InlineExecutor::instance());
} }
......
...@@ -738,7 +738,7 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -738,7 +738,7 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/// throw std::runtime_error("oh no!"); /// throw std::runtime_error("oh no!");
/// return 42; /// return 42;
/// }) /// })
/// .deferError<std::runtime_error>([] (auto const& e) { /// .deferError(folly::tag_t<std::runtime_error>{}, [] (auto const& e) {
/// LOG(INFO) << "std::runtime_error: " << e.what(); /// LOG(INFO) << "std::runtime_error: " << e.what();
/// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1) /// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)
/// }); /// });
...@@ -753,11 +753,17 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -753,11 +753,17 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/// - `valid() == false` /// - `valid() == false`
/// - `RESULT.valid() == true` /// - `RESULT.valid() == true`
template <class ExceptionType, class F> template <class ExceptionType, class F>
SemiFuture<T> deferError(F&& func) &&; SemiFuture<T> deferError(tag_t<ExceptionType>, F&& func) &&;
template <class ExceptionType, class R, class... Args> template <class ExceptionType, class R, class... Args>
SemiFuture<T> deferError(R (&func)(Args...)) && { SemiFuture<T> deferError(tag_t<ExceptionType> tag, R (&func)(Args...)) && {
return std::move(*this).template deferError<ExceptionType>(&func); return std::move(*this).deferError(tag, &func);
}
template <class ExceptionType, class F>
SemiFuture<T> deferError(F&& func) && {
return std::move(*this).deferError(
tag_t<ExceptionType>{}, std::forward<F>(func));
} }
/// Set an error continuation for this SemiFuture where the continuation can /// Set an error continuation for this SemiFuture where the continuation can
...@@ -1309,7 +1315,7 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1309,7 +1315,7 @@ class Future : private futures::detail::FutureBase<T> {
/// throw std::runtime_error("oh no!"); /// throw std::runtime_error("oh no!");
/// return 42; /// return 42;
/// }) /// })
/// .thenError<std::runtime_error>([] (auto const& e) { /// .thenError(folly::tag_t<std::runtime_error>{}, [] (auto const& e) {
/// LOG(INFO) << "std::runtime_error: " << e.what(); /// LOG(INFO) << "std::runtime_error: " << e.what();
/// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1) /// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)
/// }); /// });
...@@ -1326,17 +1332,23 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1326,17 +1332,23 @@ class Future : private futures::detail::FutureBase<T> {
typename std::enable_if< typename std::enable_if<
isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value, isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type Future<T>>::type
thenError(F&& func) &&; thenError(tag_t<ExceptionType>, F&& func) &&;
template <class ExceptionType, class F> template <class ExceptionType, class F>
typename std::enable_if< typename std::enable_if<
!isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value, !isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type Future<T>>::type
thenError(F&& func) &&; thenError(tag_t<ExceptionType>, F&& func) &&;
template <class ExceptionType, class R, class... Args> template <class ExceptionType, class R, class... Args>
Future<T> thenError(R (&func)(Args...)) && { Future<T> thenError(tag_t<ExceptionType> tag, R (&func)(Args...)) && {
return std::move(*this).template thenError<ExceptionType>(&func); return std::move(*this).thenError(tag, &func);
}
template <class ExceptionType, class F>
Future<T> thenError(F&& func) && {
return std::move(*this).thenError(
tag_t<ExceptionType>{}, std::forward<F>(func));
} }
/// Set an error continuation for this Future where the continuation can /// Set an error continuation for this Future where the continuation can
......
...@@ -480,7 +480,7 @@ TEST(Future, onError) { ...@@ -480,7 +480,7 @@ TEST(Future, onError) {
{ {
auto f = makeFuture() auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; }) .thenValue([](auto &&) -> int { throw eggs; })
.thenError<eggs_t>(onErrorHelperEggs) .thenError(tag_t<eggs_t>{}, onErrorHelperEggs)
.thenError<std::exception>(onErrorHelperGeneric); .thenError<std::exception>(onErrorHelperGeneric);
EXPECT_EQ(10, f.value()); EXPECT_EQ(10, f.value());
} }
...@@ -488,7 +488,7 @@ TEST(Future, onError) { ...@@ -488,7 +488,7 @@ TEST(Future, onError) {
auto f = auto f =
makeFuture() makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); }) .thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.thenError<eggs_t>(onErrorHelperEggs) .thenError(tag_t<eggs_t>{}, onErrorHelperEggs)
.thenError(onErrorHelperWrapper); .thenError(onErrorHelperWrapper);
EXPECT_EQ(30, f.value()); EXPECT_EQ(30, f.value());
} }
...@@ -496,7 +496,7 @@ TEST(Future, onError) { ...@@ -496,7 +496,7 @@ TEST(Future, onError) {
auto f = auto f =
makeFuture() makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); }) .thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.thenError<eggs_t>(onErrorHelperEggs); .thenError(tag_t<eggs_t>{}, onErrorHelperEggs);
EXPECT_THROW(f.value(), std::runtime_error); EXPECT_THROW(f.value(), std::runtime_error);
} }
...@@ -654,18 +654,20 @@ TEST(Future, thenError) { ...@@ -654,18 +654,20 @@ TEST(Future, thenError) {
// By reference // By reference
{ {
auto f = makeFuture() auto f =
.thenValue([](auto&&) { throw eggs; }) makeFuture()
.thenError<eggs_t>([&](const eggs_t& /* e */) { flag(); }); .thenValue([](auto&&) { throw eggs; })
.thenError(tag_t<eggs_t>{}, [&](const eggs_t& /* e */) { flag(); });
EXPECT_FLAG(); EXPECT_FLAG();
EXPECT_NO_THROW(f.value()); EXPECT_NO_THROW(f.value());
} }
// By auto reference // By auto reference
{ {
auto f = makeFuture() auto f =
.thenValue([](auto&&) { throw eggs; }) makeFuture()
.thenError<eggs_t>([&](auto const& /* e */) { flag(); }); .thenValue([](auto&&) { throw eggs; })
.thenError(tag_t<eggs_t>{}, [&](auto const& /* e */) { flag(); });
EXPECT_FLAG(); EXPECT_FLAG();
EXPECT_NO_THROW(f.value()); EXPECT_NO_THROW(f.value());
} }
......
...@@ -746,7 +746,8 @@ TEST(SemiFuture, onError) { ...@@ -746,7 +746,8 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto&&) { throw eggs; }) .defer([](auto&&) { throw eggs; })
.deferError<eggs_t>([&](eggs_t const& /* e */) { flag(); }); .deferError(
tag_t<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();
} }
...@@ -754,7 +755,8 @@ TEST(SemiFuture, onError) { ...@@ -754,7 +755,8 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer(deferHelper) .defer(deferHelper)
.deferError<eggs_t>([&](eggs_t const& /* e */) { flag(); }); .deferError(
tag_t<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();
} }
...@@ -763,7 +765,7 @@ TEST(SemiFuture, onError) { ...@@ -763,7 +765,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto&&) { throw eggs; }) .defer([](auto&&) { throw eggs; })
.deferError<eggs_t>([&](auto& /* e */) { flag(); }); .deferError(tag_t<eggs_t>{}, [&](auto& /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
...@@ -772,7 +774,7 @@ TEST(SemiFuture, onError) { ...@@ -772,7 +774,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto&&) { throw eggs; }) .defer([](auto&&) { throw eggs; })
.deferError<eggs_t>([&](eggs_t /* e */) { flag(); }); .deferError(tag_t<eggs_t>{}, [&](eggs_t /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
...@@ -781,17 +783,18 @@ TEST(SemiFuture, onError) { ...@@ -781,17 +783,18 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto&&) { throw eggs; }) .defer([](auto&&) { throw eggs; })
.deferError<eggs_t>([&](auto /* e */) { flag(); }); .deferError(tag_t<eggs_t>{}, [&](auto /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
// Polymorphic // Polymorphic
{ {
auto f = auto f = makeSemiFuture()
makeSemiFuture() .defer([](auto&&) { throw eggs; })
.defer([](auto&&) { throw eggs; }) .deferError(tag_t<std::exception>{}, [&](auto const& /* e */) {
.deferError<std::exception>([&](auto const& /* e */) { flag(); }); flag();
});
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
...@@ -800,17 +803,18 @@ TEST(SemiFuture, onError) { ...@@ -800,17 +803,18 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto&&) { throw - 1; }) .defer([](auto&&) { throw - 1; })
.deferError<int>([&](auto /* e */) { flag(); }); .deferError(tag_t<int>{}, [&](auto /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
// Mutable lambda // Mutable lambda
{ {
auto f = auto f = makeSemiFuture()
makeSemiFuture() .defer([](auto&&) { throw eggs; })
.defer([](auto&&) { throw eggs; }) .deferError(tag_t<eggs_t>{}, [&](auto const& /* e */) mutable {
.deferError<eggs_t>([&](auto const& /* e */) mutable { flag(); }); flag();
});
EXPECT_NO_THROW(std::move(f).get()); EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG(); EXPECT_FLAG();
} }
...@@ -819,7 +823,7 @@ TEST(SemiFuture, onError) { ...@@ -819,7 +823,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto &&) -> int { throw eggs; }) .defer([](auto &&) -> int { throw eggs; })
.deferError<eggs_t>(onErrorHelperEggs) .deferError(tag_t<eggs_t>{}, onErrorHelperEggs)
.deferError(onErrorHelperGeneric); .deferError(onErrorHelperGeneric);
EXPECT_EQ(10, std::move(f).get()); EXPECT_EQ(10, std::move(f).get());
} }
...@@ -827,7 +831,7 @@ TEST(SemiFuture, onError) { ...@@ -827,7 +831,7 @@ TEST(SemiFuture, onError) {
auto f = auto f =
makeSemiFuture() makeSemiFuture()
.defer([](auto &&) -> int { throw std::runtime_error("test"); }) .defer([](auto &&) -> int { throw std::runtime_error("test"); })
.deferError<eggs_t>(onErrorHelperEggs) .deferError(tag_t<eggs_t>{}, onErrorHelperEggs)
.deferError(onErrorHelperGeneric); .deferError(onErrorHelperGeneric);
EXPECT_EQ(20, std::move(f).get()); EXPECT_EQ(20, std::move(f).get());
} }
...@@ -835,7 +839,7 @@ TEST(SemiFuture, onError) { ...@@ -835,7 +839,7 @@ TEST(SemiFuture, onError) {
auto f = auto f =
makeSemiFuture() makeSemiFuture()
.defer([](auto &&) -> int { throw std::runtime_error("test"); }) .defer([](auto &&) -> int { throw std::runtime_error("test"); })
.deferError<eggs_t>(onErrorHelperEggs); .deferError(tag_t<eggs_t>{}, onErrorHelperEggs);
EXPECT_THROW(std::move(f).get(), std::runtime_error); EXPECT_THROW(std::move(f).get(), std::runtime_error);
} }
...@@ -843,7 +847,7 @@ TEST(SemiFuture, onError) { ...@@ -843,7 +847,7 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto&&) { return 42; }) .defer([](auto&&) { return 42; })
.deferError<eggs_t>([&](auto& /* e */) { .deferError(tag_t<eggs_t>{}, [&](auto& /* e */) {
flag(); flag();
return -1; return -1;
}); });
...@@ -856,7 +860,8 @@ TEST(SemiFuture, onError) { ...@@ -856,7 +860,8 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto&&) { throw eggs; }) .defer([](auto&&) { throw eggs; })
.deferError<std::runtime_error>( .deferError(
tag_t<std::runtime_error>{},
[&](auto const& /* e */) { flag(); }); [&](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();
...@@ -866,7 +871,8 @@ TEST(SemiFuture, onError) { ...@@ -866,7 +871,8 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto &&) -> int { throw eggs; }) .defer([](auto &&) -> int { throw eggs; })
.deferError<eggs_t>([&](auto const& /* e */) { return 42; }); .deferError(
tag_t<eggs_t>{}, [&](auto const& /* e */) { return 42; });
EXPECT_EQ(42, std::move(f).get()); EXPECT_EQ(42, std::move(f).get());
} }
...@@ -874,7 +880,8 @@ TEST(SemiFuture, onError) { ...@@ -874,7 +880,8 @@ TEST(SemiFuture, onError) {
{ {
auto f = makeSemiFuture() auto f = makeSemiFuture()
.defer([](auto &&) -> int { throw eggs; }) .defer([](auto &&) -> int { throw eggs; })
.deferError<eggs_t>([&](auto const& e) -> int { throw e; }); .deferError(
tag_t<eggs_t>{}, [&](auto const& e) -> 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