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

Invoke Futures then-continuations with the same cvref as passed

Summary:
[Folly] Invoke Futures `then`-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: LeeHowes, phoad

Differential Revision: D8109784

fbshipit-source-id: 8dd56c6197211091ed53eba94aeec9d604268d15
parent a07a458a
...@@ -59,18 +59,19 @@ namespace detail { ...@@ -59,18 +59,19 @@ namespace detail {
// may be fulfilled. Assumes the stored functor to be noexcept-destructible. // may be fulfilled. Assumes the stored functor to be noexcept-destructible.
template <typename T, typename F> template <typename T, typename F>
class CoreCallbackState { class CoreCallbackState {
using DF = _t<std::decay<F>>;
public: public:
template <typename FF> CoreCallbackState(Promise<T>&& promise, F&& func) noexcept(
CoreCallbackState(Promise<T>&& promise, FF&& func) noexcept( noexcept(DF(std::declval<F&&>())))
noexcept(F(std::declval<FF>()))) : func_(std::forward<F>(func)), promise_(std::move(promise)) {
: func_(std::forward<FF>(func)), promise_(std::move(promise)) {
assert(before_barrier()); assert(before_barrier());
} }
CoreCallbackState(CoreCallbackState&& that) noexcept( CoreCallbackState(CoreCallbackState&& that) noexcept(
noexcept(F(std::declval<F>()))) { noexcept(DF(std::declval<F&&>()))) {
if (that.before_barrier()) { if (that.before_barrier()) {
new (&func_) F(std::move(that.func_)); new (&func_) DF(std::forward<F>(that.func_));
promise_ = that.stealPromise(); promise_ = that.stealPromise();
} }
} }
...@@ -87,7 +88,7 @@ class CoreCallbackState { ...@@ -87,7 +88,7 @@ class CoreCallbackState {
auto invoke(Args&&... args) noexcept( auto invoke(Args&&... args) noexcept(
noexcept(std::declval<F&&>()(std::declval<Args&&>()...))) { noexcept(std::declval<F&&>()(std::declval<Args&&>()...))) {
assert(before_barrier()); assert(before_barrier());
return std::move(func_)(std::forward<Args>(args)...); return std::forward<F>(func_)(std::forward<Args>(args)...);
} }
template <typename... Args> template <typename... Args>
...@@ -105,7 +106,7 @@ class CoreCallbackState { ...@@ -105,7 +106,7 @@ class CoreCallbackState {
Promise<T> stealPromise() noexcept { Promise<T> stealPromise() noexcept {
assert(before_barrier()); assert(before_barrier());
func_.~F(); func_.~DF();
return std::move(promise_); return std::move(promise_);
} }
...@@ -115,18 +116,22 @@ class CoreCallbackState { ...@@ -115,18 +116,22 @@ class CoreCallbackState {
} }
union { union {
F func_; DF func_;
}; };
Promise<T> promise_{Promise<T>::makeEmpty()}; Promise<T> promise_{Promise<T>::makeEmpty()};
}; };
template <typename T, typename F> template <typename T, typename F>
auto makeCoreCallbackState(Promise<T>&& p, F&& f) noexcept( auto makeCoreCallbackState(Promise<T>&& p, F&& f) noexcept(
noexcept(CoreCallbackState<T, _t<std::decay<F>>>( noexcept(CoreCallbackState<T, F>(
std::declval<Promise<T>&&>(), std::declval<Promise<T>&&>(),
std::declval<F&&>()))) { std::declval<F&&>()))) {
return CoreCallbackState<T, _t<std::decay<F>>>( return CoreCallbackState<T, F>(std::move(p), std::forward<F>(f));
std::move(p), std::forward<F>(f)); }
template <typename T, typename R, typename... Args>
auto makeCoreCallbackState(Promise<T>&& p, R (&f)(Args...)) noexcept {
return CoreCallbackState<T, R (*)(Args...)>(std::move(p), &f);
} }
template <class T> template <class T>
......
...@@ -1116,10 +1116,11 @@ TEST(Future, invokeCallbackReturningValueAsRvalue) { ...@@ -1116,10 +1116,11 @@ TEST(Future, invokeCallbackReturningValueAsRvalue) {
Foo foo; Foo foo;
Foo const cfoo; Foo const cfoo;
// The callback will be copied when given as lvalue or const ref, and moved // The continuation will be forward-constructed - copied if given as & and
// if provided as rvalue. Either way, it should be executed as rvalue. // moved if given as && - everywhere construction is required.
EXPECT_EQ(103, makeFuture<int>(100).then(foo).value()); // The continuation will be invoked with the same cvref as it is passed.
EXPECT_EQ(203, makeFuture<int>(200).then(cfoo).value()); EXPECT_EQ(101, makeFuture<int>(100).then(foo).value());
EXPECT_EQ(202, makeFuture<int>(200).then(cfoo).value());
EXPECT_EQ(303, makeFuture<int>(300).then(Foo()).value()); EXPECT_EQ(303, makeFuture<int>(300).then(Foo()).value());
} }
...@@ -1139,10 +1140,11 @@ TEST(Future, invokeCallbackReturningFutureAsRvalue) { ...@@ -1139,10 +1140,11 @@ TEST(Future, invokeCallbackReturningFutureAsRvalue) {
Foo foo; Foo foo;
Foo const cfoo; Foo const cfoo;
// The callback will be copied when given as lvalue or const ref, and moved // The continuation will be forward-constructed - copied if given as & and
// if provided as rvalue. Either way, it should be executed as rvalue. // moved if given as && - everywhere construction is required.
EXPECT_EQ(103, makeFuture<int>(100).then(foo).value()); // The continuation will be invoked with the same cvref as it is passed.
EXPECT_EQ(203, makeFuture<int>(200).then(cfoo).value()); EXPECT_EQ(101, makeFuture<int>(100).then(foo).value());
EXPECT_EQ(202, makeFuture<int>(200).then(cfoo).value());
EXPECT_EQ(303, makeFuture<int>(300).then(Foo()).value()); EXPECT_EQ(303, makeFuture<int>(300).then(Foo()).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