148 lines
4.9 KiB
C++
148 lines
4.9 KiB
C++
// 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}));
|
|
//]
|
|
}
|
|
}
|
|
//]
|