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

Invoke SemiFuture defer-continuations with same cvref as passed

Summary:
[Folly] Invoke SemiFutures defer-continuations with the same cvref as passed.
for example, if passed as const&&, invoke as const&&, and if passed as volatile&, invoke as volatile&.
This brings a little more consistency to the library.

Reviewed By: yfeldblum

Differential Revision: D8125144

fbshipit-source-id: f2cda643aaae40e0293d5d79608541771642ce06
parent 2c23497f
......@@ -769,9 +769,10 @@ SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
SemiFuture<T>::deferValue(F&& func) && {
return std::move(*this).defer([f = std::forward<F>(func)](
folly::Try<T>&& t) mutable {
return f(t.template get<
false,
typename futures::detail::valueCallableResult<T, F>::FirstArg>());
return std::forward<F>(f)(
t.template get<
false,
typename futures::detail::valueCallableResult<T, F>::FirstArg>());
});
}
......
......@@ -422,6 +422,11 @@ class SemiFuture : private futures::detail::FutureBase<T> {
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
defer(F&& func) &&;
template <typename R, typename... Args>
auto defer(R (&func)(Args...)) && {
return std::move(*this).defer(&func);
}
/**
* Defer for functions taking a T rather than a Try<T>.
*/
......@@ -429,6 +434,11 @@ class SemiFuture : private futures::detail::FutureBase<T> {
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
deferValue(F&& func) &&;
template <typename R, typename... Args>
auto deferValue(R (&func)(Args...)) && {
return std::move(*this).deferValue(&func);
}
/// Set an error callback for this SemiFuture. 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 SemiFuture, or a SemiFuture of that type
......
......@@ -688,6 +688,21 @@ TEST(SemiFuture, SimpleDeferWithValue) {
ASSERT_EQ(innerResult, 7);
}
namespace {
int deferValueHelper(int a) {
return a;
}
} // namespace
TEST(SemiFuture, SimpleDeferWithValueFunctionReference) {
Promise<int> p;
auto f = p.getSemiFuture().toUnsafeFuture();
auto sf = std::move(f).semi().deferValue(deferValueHelper);
p.setValue(7);
// Run "F" here inline in the calling thread
ASSERT_EQ(std::move(sf).get(), 7);
}
TEST(SemiFuture, ChainingDefertoThenWithValue) {
std::atomic<int> innerResult{0};
std::atomic<int> result{0};
......@@ -721,6 +736,10 @@ TEST(SemiFuture, MakeSemiFutureFromFutureWithTry) {
ASSERT_EQ(tryResult.value(), "Try");
}
namespace {
[[noreturn]] void deferHelper() { throw eggs; }
} // namespace
TEST(SemiFuture, DeferWithinContinuation) {
std::atomic<int> innerResult{0};
std::atomic<int> result{0};
......@@ -768,6 +787,15 @@ TEST(SemiFuture, onError) {
EXPECT_FLAG();
}
{
auto f =
makeSemiFuture().defer(deferHelper).deferError([&](eggs_t& /* e */) {
flag();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
......@@ -1024,3 +1052,27 @@ TEST(SemiFuture, makePromiseContract) {
c.second = std::move(c.second).deferValue([](int _) { return _ + 1; });
EXPECT_EQ(4, std::move(c.second).get());
}
TEST(SemiFuture, invokeCallbackReturningFutureWithOriginalCVRef) {
struct Foo {
Future<int> operator()(int x) & {
return x + 1;
}
Future<int> operator()(int x) const& {
return x + 2;
}
Future<int> operator()(int x) && {
return x + 3;
}
};
Foo foo;
Foo const cfoo;
// The continuation will be forward-constructed - copied if given as & and
// moved if given as && - everywhere construction is required.
// The continuation will be invoked with the same cvref as it is passed.
EXPECT_EQ(101, makeSemiFuture<int>(100).deferValue(foo).wait().value());
EXPECT_EQ(202, makeSemiFuture<int>(200).deferValue(cfoo).wait().value());
EXPECT_EQ(303, makeSemiFuture<int>(300).deferValue(Foo()).wait().value());
}
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