Commit 62001f5e authored by Sven Over's avatar Sven Over Committed by Facebook Github Bot

add folly::Function::asSharedProxy

Summary:
This diff adds a method to folly::Function that moves the
Function object into a copyable, callable proxy object.

folly::Function already has the asStdFunction method, which
does the same, but wraps the proxy object in a std::function.
When a copyable shared-state proxy of a folly::Function is needed,
it is not necessarily to turn it into a std::function.
If instead the shared proxy will be passed to several functions
taking folly::Function, what happens is the folly::Function
is moved on the heap, a shared_ptr is put into a std::function,
and that is wrapped in a new folly::Function, which always
requires a memory allocation, because std::function is not
noexcept-movable. When using asSharedProxy instead, an
unspecified copyable type is returned that can implicitly
converted into a folly::Function, but without an additional
memory allocation at this point.

Reviewed By: yfeldblum

Differential Revision: D4048621

fbshipit-source-id: b642027b0a6957058fe0089cceeb657ec52e8669
parent d458b0a1
......@@ -305,8 +305,13 @@ struct FunctionTraits<ReturnType(Args...)> {
return fn.call_(fn.data_, static_cast<Args&&>(args)...);
}
struct SharedFunctionImpl {
class SharedProxy {
std::shared_ptr<Function<ReturnType(Args...)>> sp_;
public:
explicit SharedProxy(Function<ReturnType(Args...)>&& func)
: sp_(std::make_shared<Function<ReturnType(Args...)>>(
std::move(func))) {}
ReturnType operator()(Args&&... args) const {
return (*sp_)(static_cast<Args&&>(args)...);
}
......@@ -346,8 +351,13 @@ struct FunctionTraits<ReturnType(Args...) const> {
return fn.call_(fn.data_, static_cast<Args&&>(args)...);
}
struct SharedFunctionImpl {
struct SharedProxy {
std::shared_ptr<Function<ReturnType(Args...) const>> sp_;
public:
explicit SharedProxy(Function<ReturnType(Args...) const>&& func)
: sp_(std::make_shared<Function<ReturnType(Args...) const>>(
std::move(func))) {}
ReturnType operator()(Args&&... args) const {
return (*sp_)(static_cast<Args&&>(args)...);
}
......@@ -634,14 +644,23 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
return exec_(Op::HEAP, nullptr, nullptr);
}
using typename Traits::SharedProxy;
/**
* Move this `Function` into a copyable callable object, of which all copies
* share the state.
*/
SharedProxy asSharedProxy() && {
return SharedProxy{std::move(*this)};
}
/**
* Construct a `std::function` by moving in the contents of this `Function`.
* Note that the returned `std::function` will share its state (i.e. captured
* data) across all copies you make of it, so be very careful when copying.
*/
std::function<typename Traits::NonConstSignature> asStdFunction() && {
using Impl = typename Traits::SharedFunctionImpl;
return Impl{std::make_shared<Function>(std::move(*this))};
return std::move(*this).asSharedProxy();
}
};
FOLLY_POP_WARNING
......
......@@ -820,6 +820,80 @@ TEST(Function, asStdFunction_args_const) {
EXPECT_EQ(1, i);
}
// TEST =====================================================================
// asSharedProxy_*
TEST(Function, asSharedProxy_void) {
int i = 0;
folly::Function<void()> f = [&i] { ++i; };
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
sp();
EXPECT_EQ(1, i);
spcopy();
EXPECT_EQ(2, i);
}
TEST(Function, asSharedProxy_void_const) {
int i = 0;
folly::Function<void() const> f = [&i] { ++i; };
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
sp();
EXPECT_EQ(1, i);
spcopy();
EXPECT_EQ(2, i);
}
TEST(Function, asSharedProxy_return) {
folly::Function<int()> f = [i = 0]() mutable {
++i;
return i;
};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_EQ(1, sp());
EXPECT_EQ(2, spcopy());
}
TEST(Function, asSharedProxy_return_const) {
int i = 0;
folly::Function<int() const> f = [&i] {
++i;
return i;
};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_EQ(1, sp());
EXPECT_EQ(2, spcopy());
}
TEST(Function, asSharedProxy_args) {
int i = 0;
folly::Function<int(int, int)> f = [&](int x, int y) mutable {
++i;
return x + y * 2;
};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_EQ(120, sp(100, 10));
EXPECT_EQ(1, i);
EXPECT_EQ(120, spcopy(100, 10));
EXPECT_EQ(2, i);
}
TEST(Function, asSharedProxy_args_const) {
int i = 0;
folly::Function<int(int, int) const> f = [&i](int x, int y) {
++i;
return x * 100 + y * 10 + i;
};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_EQ(561, sp(5, 6));
EXPECT_EQ(562, spcopy(5, 6));
}
TEST(Function, NoAllocatedMemoryAfterMove) {
Functor<int, 100> foo;
......
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