summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/yap/example/pipable_algorithms.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/yap/example/pipable_algorithms.cpp')
-rw-r--r--src/boost/libs/yap/example/pipable_algorithms.cpp147
1 files changed, 147 insertions, 0 deletions
diff --git a/src/boost/libs/yap/example/pipable_algorithms.cpp b/src/boost/libs/yap/example/pipable_algorithms.cpp
new file mode 100644
index 000000000..07c71f548
--- /dev/null
+++ b/src/boost/libs/yap/example/pipable_algorithms.cpp
@@ -0,0 +1,147 @@
+// Copyright (C) 2016-2018 T. Zachary Laine
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//[ pipable_algorithms
+#include <boost/yap/algorithm.hpp>
+
+#include <algorithm>
+#include <vector>
+
+
+//[ pipable_algorithms_and_simple_range
+// An enum to represent all the standard algorithms we want to adapt. In this
+// example, we only care about std::sort() and std::unique().
+enum class algorithm_t { sort, unique };
+
+// Just about the simplest range template you could construct. Nothing fancy.
+template<typename Iter>
+struct simple_range
+{
+ Iter begin() const { return first_; }
+ Iter end() const { return last_; }
+
+ Iter first_;
+ Iter last_;
+};
+
+// This simply calls the standard algorithm that corresponds to "a". This
+// certainly won't work for all the algorithms, but it works for many of them
+// that just take a begin/end pair.
+template<typename Range>
+auto call_algorithm(algorithm_t a, Range & r)
+{
+ simple_range<decltype(r.begin())> retval{r.begin(), r.end()};
+ if (a == algorithm_t::sort) {
+ std::sort(r.begin(), r.end());
+ } else if (a == algorithm_t::unique) {
+ retval.last_ = std::unique(r.begin(), r.end());
+ }
+ return retval;
+}
+//]
+
+// This is the transform that evaluates our piped expressions. It returns a
+// simple_range<>, not a Yap expression.
+struct algorithm_eval
+{
+ // A pipe should always have a Yap expression on the left and an
+ // algorithm_t terminal on the right.
+ template<typename LExpr>
+ auto operator()(
+ boost::yap::expr_tag<boost::yap::expr_kind::bitwise_or>,
+ LExpr && left,
+ algorithm_t right)
+ {
+ // Recursively evaluate the left ...
+ auto const left_result =
+ boost::yap::transform(std::forward<LExpr>(left), *this);
+ // ... and use the result to call the function on the right.
+ return call_algorithm(right, left_result);
+ }
+
+ // A call operation is evaluated directly. Note that the range parameter
+ // is taken as an lvalue reference, to prevent binding to a temporary and
+ // taking dangling references to its begin and end. We let the compiler
+ // deduce whether the lvalue reference is const.
+//[ tag_xform
+ template<typename Range>
+ auto operator()(
+ boost::yap::expr_tag<boost::yap::expr_kind::call>,
+ algorithm_t a,
+ Range & r)
+ {
+ return call_algorithm(a, r);
+ }
+//]
+};
+
+// This is the expression template we use for the general case of a pipable
+// algorithm expression. Terminals are handled separately.
+template<boost::yap::expr_kind Kind, typename Tuple>
+struct algorithm_expr
+{
+ static boost::yap::expr_kind const kind = Kind;
+
+ Tuple elements;
+
+ // This is how we get the nice initializer semantics we see in the example
+ // usage below. This is a bit limited though, because we always create a
+ // temporary. It might therefore be better just to create algorithm_expr
+ // expressions, call yap::evaluate(), and then use the sequence containers
+ // assign() member function to do the actual assignment.
+ template<typename Assignee>
+ operator Assignee() const
+ {
+ // Exercise left for the reader: static_assert() that Assignee is some
+ // sort of container type.
+ auto const range = boost::yap::transform(*this, algorithm_eval{});
+ return Assignee(range.begin(), range.end());
+ }
+};
+
+
+// This is a bit loose, because it allows us to write "sort(v) | unique(u)" or
+// similar. It works fine for this example, but in production code you may
+// want to write out the functions generated by this macro, and add SFINAE or
+// concepts constraints on the right template parameter.
+BOOST_YAP_USER_BINARY_OPERATOR(bitwise_or, algorithm_expr, algorithm_expr)
+
+// It's useful to specially handle terminals, because we want a different set
+// of operations to apply to them. We don't want "sort(x)(y)" to be
+// well-formed, for instance, or "sort | unique" either.
+struct algorithm
+{
+ static boost::yap::expr_kind const kind = boost::yap::expr_kind::terminal;
+
+ boost::hana::tuple<algorithm_t> elements;
+
+ BOOST_YAP_USER_CALL_OPERATOR_N(::algorithm_expr, 1)
+};
+
+// Here are ready-made Yap terminals, one for each algorithm enumerated in
+// algorithm_t.
+constexpr algorithm sort{{algorithm_t::sort}};
+constexpr algorithm unique{{algorithm_t::unique}};
+
+int main()
+{
+ {
+//[ typical_sort_unique_usage
+ std::vector<int> v1 = {0, 2, 2, 7, 1, 3, 8};
+ std::sort(v1.begin(), v1.end());
+ auto it = std::unique(v1.begin(), v1.end());
+ std::vector<int> const v2(v1.begin(), it);
+ assert(v2 == std::vector<int>({0, 1, 2, 3, 7, 8}));
+//]
+ }
+ {
+//[ pipable_sort_unique_usage
+ std::vector<int> v1 = {0, 2, 2, 7, 1, 3, 8};
+ std::vector<int> const v2 = sort(v1) | unique;
+ assert(v2 == std::vector<int>({0, 1, 2, 3, 7, 8}));
+//]
+ }
+}
+//]