add folly::Function::asSharedProxy v2016.10.24.00
authorSven Over <over@fb.com>
Mon, 24 Oct 2016 05:06:45 +0000 (22:06 -0700)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Mon, 24 Oct 2016 05:08:57 +0000 (22:08 -0700)
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

folly/Function.h
folly/test/FunctionTest.cpp

index a201c95c2cd44baddaba604bcce390d1efab60cc..d6fb29f6010b16735b52ebfd8254e365a1e87f3c 100644 (file)
@@ -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
index b23eb3e941e47084070aadaf4c3c72fa82aec6f0..99c5b6c519767a3bf3eb4a1118d127682049b141 100644 (file)
@@ -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;