You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by mp...@apache.org on 2017/12/05 01:40:49 UTC

[2/2] mesos git commit: Added `lambda::partial` to .

Added `lambda::partial` to <stout/lambda.hpp>.

Review: https://reviews.apache.org/r/64274/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/8670d2e9
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/8670d2e9
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/8670d2e9

Branch: refs/heads/master
Commit: 8670d2e9224485b94a40654d4a2102d8340fe4ac
Parents: 3b9db40
Author: Michael Park <mp...@apache.org>
Authored: Mon Dec 4 17:25:36 2017 -0800
Committer: Michael Park <mp...@apache.org>
Committed: Mon Dec 4 17:25:36 2017 -0800

----------------------------------------------------------------------
 3rdparty/stout/include/stout/lambda.hpp | 150 +++++++++++++++++++++++++++
 3rdparty/stout/tests/lambda_tests.cpp   | 138 +++++++++++++++++++++++-
 2 files changed, 286 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/8670d2e9/3rdparty/stout/include/stout/lambda.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/lambda.hpp b/3rdparty/stout/include/stout/lambda.hpp
index a61d97b..c14241a 100644
--- a/3rdparty/stout/include/stout/lambda.hpp
+++ b/3rdparty/stout/include/stout/lambda.hpp
@@ -15,8 +15,12 @@
 
 #include <algorithm>
 #include <functional>
+#include <type_traits>
+#include <utility>
 #include <vector>
 
+#include <stout/cpp14.hpp>
+#include <stout/cpp17.hpp>
 #include <stout/result_of.hpp>
 
 namespace lambda {
@@ -155,6 +159,152 @@ std::vector<V> map(F&& f, std::initializer_list<U> input)
   return output;
 }
 
+
+#define RETURN(...) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+
+namespace internal {
+
+// The `int` specializations here for `is_placeholder<T>::value`.
+// `is_placeholder<T>::value` returns a `0` for non-placeholders,
+// and I > 0 for placeholders where I indicates the placeholder
+// value. e.g., `is_placeholder<decltype(_1)>::value == 1`
+
+template <int I>
+struct Expand
+{
+  // Bound argument is a placeholder.
+  template <typename T, typename Args>
+  auto operator()(T&&, Args&& args) const
+    RETURN(std::get<I - 1>(std::forward<Args>(args)))
+};
+
+
+template <>
+struct Expand<0>
+{
+  // Bound argument is not a placeholder.
+  template <typename T, typename Args>
+  auto operator()(T&& t, Args&&) const
+    RETURN(std::forward<T>(t))
+};
+
+
+template <typename F, typename... BoundArgs>
+class Partial
+{
+  F f;
+  std::tuple<BoundArgs...> bound_args;
+
+  template <typename T, typename Args>
+  static auto expand(T&& t, Args&& args)
+    RETURN(Expand<std::is_placeholder<typename std::decay<T>::type>::value>{}(
+        std::forward<T>(t), std::forward<Args>(args)))
+
+  // Invoke the given function `f` with bound arguments expanded. If a bound
+  // argument is a placeholder, we use the index `I` of the placeholder to
+  // pass the `I`th argument out of `args` along. Otherwise, we pass the bound
+  // argument through preserving its value category. That is, passing the bound
+  // argument as an lvalue-ref or rvalue-ref depending correspondingly on
+  // whether the `Partial` itself is an lvalue or rvalue.
+  template <typename F_, typename BoundArgs_, typename Args, std::size_t... Is>
+  static auto invoke_expand(
+      F_&& f,
+      BoundArgs_&& bound_args,
+      cpp14::index_sequence<Is...>,
+      Args&& args)
+    RETURN(cpp17::invoke(
+        std::forward<F_>(f),
+        expand(
+            std::get<Is>(std::forward<BoundArgs_>(bound_args)),
+            std::forward<Args>(args))...))
+
+public:
+  template <typename... BoundArgs_>
+  explicit Partial(const F& f, BoundArgs_&&... args)
+    : f(f), bound_args(std::forward<BoundArgs_>(args)...) {}
+
+  template <typename... BoundArgs_>
+  explicit Partial(F&& f, BoundArgs_&&... args)
+    : f(std::move(f)), bound_args(std::forward<BoundArgs_>(args)...) {}
+
+  Partial(const Partial&) = default;
+  Partial(Partial&&) = default;
+
+  Partial& operator=(const Partial&) = default;
+  Partial& operator=(Partial&&) = default;
+
+  template <typename... Args>
+  auto operator()(Args&&... args) &
+    RETURN(invoke_expand(
+      f,
+      bound_args,
+      cpp14::make_index_sequence<sizeof...(BoundArgs)>(),
+      std::forward_as_tuple(std::forward<Args>(args)...)))
+
+  template <typename... Args>
+  auto operator()(Args&&... args) const &
+    RETURN(invoke_expand(
+      f,
+      bound_args,
+      cpp14::make_index_sequence<sizeof...(BoundArgs)>(),
+      std::forward_as_tuple(std::forward<Args>(args)...)))
+
+  template <typename... Args>
+  auto operator()(Args&&... args) &&
+    RETURN(invoke_expand(
+      std::move(f),
+      std::move(bound_args),
+      cpp14::make_index_sequence<sizeof...(BoundArgs)>(),
+      std::forward_as_tuple(std::forward<Args>(args)...)))
+
+  template <typename... Args>
+  auto operator()(Args&&... args) const &&
+    RETURN(invoke_expand(
+      std::move(f),
+      std::move(bound_args),
+      cpp14::make_index_sequence<sizeof...(BoundArgs)>(),
+      std::forward_as_tuple(std::forward<Args>(args)...)))
+};
+
+} // namespace internal {
+
+
+// Performs partial function application, similar to `std::bind`. However,
+// it supports moving the bound arguments through, unlike `std::bind`.
+// To do so, the `operator()` must be invoked on a rvalue `lambda::partial`.
+//
+// Unsupported `std::bind` features:
+//   - There is no special treatment for nested bind expressions. When calling
+//     `operator()` on partial, call parameters will not be passed to nested
+//     bind expression. Instead, bind expression will be passed as-is to the
+//     wrapped function object. This behavior is intentional, for simplicity
+//     reasons, and is in sync with C++20's `std::bind_front`.
+//   - Passing `std::reference_wrapper` is not implemented.
+template <typename F, typename... Args>
+internal::Partial<
+    typename std::decay<F>::type,
+    typename std::decay<Args>::type...>
+partial(F&& f, Args&&... args)
+{
+  using R = internal::Partial<
+      typename std::decay<F>::type,
+      typename std::decay<Args>::type...>;
+  return R(std::forward<F>(f), std::forward<Args>(args)...);
+}
+
+
+#undef RETURN
+
 } // namespace lambda {
 
+
+namespace std {
+
+template <typename F, typename... Args>
+struct is_bind_expression<lambda::internal::Partial<F, Args...>>
+  : true_type {};
+
+} // namespace std {
+
 #endif // __STOUT_LAMBDA_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/8670d2e9/3rdparty/stout/tests/lambda_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/lambda_tests.cpp b/3rdparty/stout/tests/lambda_tests.cpp
index ad8c2ef..11b0343 100644
--- a/3rdparty/stout/tests/lambda_tests.cpp
+++ b/3rdparty/stout/tests/lambda_tests.cpp
@@ -18,14 +18,29 @@
 
 struct OnlyMoveable
 {
+  OnlyMoveable() : i(-1) {}
   OnlyMoveable(int i) : i(i) {}
-  OnlyMoveable(OnlyMoveable&&) = default;
+
+  OnlyMoveable(OnlyMoveable&& that)
+  {
+    *this = std::move(that);
+  }
+
   OnlyMoveable(const OnlyMoveable&) = delete;
-  OnlyMoveable& operator=(OnlyMoveable&&) = default;
+
+  OnlyMoveable& operator=(OnlyMoveable&& that)
+  {
+    i = that.i;
+    j = that.j;
+    that.valid = false;
+    return *this;
+  }
+
   OnlyMoveable& operator=(const OnlyMoveable&) = delete;
 
   int i;
   int j = 0;
+  bool valid = true;
 };
 
 
@@ -88,3 +103,122 @@ TEST(LambdaTest, Map)
     EXPECT_EQ(o.i, o.j);
   }
 }
+
+
+namespace {
+
+template <typename F, typename ...Args>
+auto callable(F&& f, Args&&... args)
+  -> decltype(std::forward<F>(f)(std::forward<Args>(args)...),
+              void(),
+              std::true_type());
+
+
+template <typename F>
+std::false_type callable(F&& f, ...);
+
+
+// Compile-time check that f cannot be called with specified arguments.
+// This is implemented by defining two callable function overloads and
+// differentiating on return type. The first overload is selected only
+// when call expression is valid, and it has return type of std::true_type,
+// while second overload is selected for everything else.
+template <typename F, typename ...Args>
+void EXPECT_CALL_INVALID(F&& f, Args&&... args)
+{
+  static_assert(
+      !decltype(
+          callable(std::forward<F>(f), std::forward<Args>(args)...))::value,
+          "call expression is expected to be invalid");
+}
+
+} // namespace {
+
+
+
+namespace {
+
+int returnIntNoParams()
+{
+  return 8;
+}
+
+
+void returnVoidStringParam(std::string s) {}
+
+
+void returnVoidStringCRefParam(const std::string& s) {}
+
+
+int returnIntOnlyMovableParam(OnlyMoveable o)
+{
+  EXPECT_TRUE(o.valid);
+  return 1;
+}
+
+} // namespace {
+
+
+// This is mostly a compile time test of lambda::partial,
+// verifying that it works for different types of expressions.
+TEST(PartialTest, Test)
+{
+  // standalone functions
+  auto p1 = lambda::partial(returnIntNoParams);
+  int p1r1 = p1();
+  int p1r2 = std::move(p1)();
+  EXPECT_EQ(p1r1, p1r2);
+
+  auto p2 = lambda::partial(returnVoidStringParam, "");
+  p2();
+  std::move(p2)();
+
+  auto p3 = lambda::partial(returnVoidStringParam, lambda::_1);
+  p3("");
+  std::move(p3)("");
+
+  auto p4 = lambda::partial(&returnVoidStringCRefParam, lambda::_1);
+  p4("");
+  std::move(p4)("");
+
+  auto p5 = lambda::partial(&returnIntOnlyMovableParam, lambda::_1);
+  p5(OnlyMoveable());
+  p5(10);
+  std::move(p5)(OnlyMoveable());
+
+  auto p6 = lambda::partial(&returnIntOnlyMovableParam, OnlyMoveable());
+  EXPECT_CALL_INVALID(p6);
+  std::move(p6)();
+
+  // lambdas
+  auto l1 = [](const OnlyMoveable& m) { EXPECT_TRUE(m.valid); };
+  auto pl1 = lambda::partial(l1, OnlyMoveable());
+  pl1();
+  pl1();
+  std::move(pl1)();
+
+  auto pl2 = lambda::partial([](OnlyMoveable&& m) { EXPECT_TRUE(m.valid); },
+      lambda::_1);
+  pl2(OnlyMoveable());
+  pl2(OnlyMoveable());
+  std::move(pl2)(OnlyMoveable());
+
+  auto pl3 = lambda::partial([](OnlyMoveable&& m) { EXPECT_TRUE(m.valid); },
+      OnlyMoveable());
+  EXPECT_CALL_INVALID(pl3);
+  std::move(pl3)();
+
+  // member functions
+  struct Object
+  {
+    int method() { return 0; };
+  };
+
+  auto mp1 = lambda::partial(&Object::method, lambda::_1);
+  mp1(Object());
+  std::move(mp1)(Object());
+
+  auto mp2 = lambda::partial(&Object::method, Object());
+  mp2();
+  std::move(mp2)();
+}