From: Sven Over Date: Mon, 24 Oct 2016 05:06:45 +0000 (-0700) Subject: add folly::Function::asSharedProxy X-Git-Tag: v2016.10.24.00^0 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=62001f5ebab94e79faac4773023ff8381eff0ff1;p=folly.git 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 --- diff --git a/folly/Function.h b/folly/Function.h index a201c95c..d6fb29f6 100644 --- a/folly/Function.h +++ b/folly/Function.h @@ -305,8 +305,13 @@ struct FunctionTraits { return fn.call_(fn.data_, static_cast(args)...); } - struct SharedFunctionImpl { + class SharedProxy { std::shared_ptr> sp_; + + public: + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>( + std::move(func))) {} ReturnType operator()(Args&&... args) const { return (*sp_)(static_cast(args)...); } @@ -346,8 +351,13 @@ struct FunctionTraits { return fn.call_(fn.data_, static_cast(args)...); } - struct SharedFunctionImpl { + struct SharedProxy { std::shared_ptr> sp_; + + public: + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>( + std::move(func))) {} ReturnType operator()(Args&&... args) const { return (*sp_)(static_cast(args)...); } @@ -634,14 +644,23 @@ class Function final : private detail::function::FunctionTraits { 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 asStdFunction() && { - using Impl = typename Traits::SharedFunctionImpl; - return Impl{std::make_shared(std::move(*this))}; + return std::move(*this).asSharedProxy(); } }; FOLLY_POP_WARNING diff --git a/folly/test/FunctionTest.cpp b/folly/test/FunctionTest.cpp index b23eb3e9..99c5b6c5 100644 --- a/folly/test/FunctionTest.cpp +++ b/folly/test/FunctionTest.cpp @@ -820,6 +820,80 @@ TEST(Function, asStdFunction_args_const) { EXPECT_EQ(1, i); } +// TEST ===================================================================== +// asSharedProxy_* + +TEST(Function, asSharedProxy_void) { + int i = 0; + folly::Function 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 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 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 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 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 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 foo;