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)();
+}