1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 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}));
//]
}
}
//]
|