• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2016-2018 T. Zachary Laine
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //[ pipable_algorithms
7 #include <boost/yap/algorithm.hpp>
8 
9 #include <algorithm>
10 #include <vector>
11 
12 
13 //[ pipable_algorithms_and_simple_range
14 // An enum to represent all the standard algorithms we want to adapt.  In this
15 // example, we only care about std::sort() and std::unique().
16 enum class algorithm_t { sort, unique };
17 
18 // Just about the simplest range template you could construct.  Nothing fancy.
19 template<typename Iter>
20 struct simple_range
21 {
beginsimple_range22     Iter begin() const { return first_; }
endsimple_range23     Iter end() const { return last_; }
24 
25     Iter first_;
26     Iter last_;
27 };
28 
29 // This simply calls the standard algorithm that corresponds to "a".  This
30 // certainly won't work for all the algorithms, but it works for many of them
31 // that just take a begin/end pair.
32 template<typename Range>
call_algorithm(algorithm_t a,Range & r)33 auto call_algorithm(algorithm_t a, Range & r)
34 {
35     simple_range<decltype(r.begin())> retval{r.begin(), r.end()};
36     if (a == algorithm_t::sort) {
37         std::sort(r.begin(), r.end());
38     } else if (a == algorithm_t::unique) {
39         retval.last_ = std::unique(r.begin(), r.end());
40     }
41     return retval;
42 }
43 //]
44 
45 // This is the transform that evaluates our piped expressions.  It returns a
46 // simple_range<>, not a Yap expression.
47 struct algorithm_eval
48 {
49     // A pipe should always have a Yap expression on the left and an
50     // algorithm_t terminal on the right.
51     template<typename LExpr>
operator ()algorithm_eval52     auto operator()(
53         boost::yap::expr_tag<boost::yap::expr_kind::bitwise_or>,
54         LExpr && left,
55         algorithm_t right)
56     {
57         // Recursively evaluate the left ...
58         auto const left_result =
59             boost::yap::transform(std::forward<LExpr>(left), *this);
60         // ... and use the result to call the function on the right.
61         return call_algorithm(right, left_result);
62     }
63 
64     // A call operation is evaluated directly.  Note that the range parameter
65     // is taken as an lvalue reference, to prevent binding to a temporary and
66     // taking dangling references to its begin and end.  We let the compiler
67     // deduce whether the lvalue reference is const.
68 //[ tag_xform
69     template<typename Range>
operator ()algorithm_eval70     auto operator()(
71         boost::yap::expr_tag<boost::yap::expr_kind::call>,
72         algorithm_t a,
73         Range & r)
74     {
75         return call_algorithm(a, r);
76     }
77 //]
78 };
79 
80 // This is the expression template we use for the general case of a pipable
81 // algorithm expression.  Terminals are handled separately.
82 template<boost::yap::expr_kind Kind, typename Tuple>
83 struct algorithm_expr
84 {
85     static boost::yap::expr_kind const kind = Kind;
86 
87     Tuple elements;
88 
89     // This is how we get the nice initializer semantics we see in the example
90     // usage below.  This is a bit limited though, because we always create a
91     // temporary.  It might therefore be better just to create algorithm_expr
92     // expressions, call yap::evaluate(), and then use the sequence containers
93     // assign() member function to do the actual assignment.
94     template<typename Assignee>
operator Assigneealgorithm_expr95     operator Assignee() const
96     {
97         // Exercise left for the reader: static_assert() that Assignee is some
98         // sort of container type.
99         auto const range = boost::yap::transform(*this, algorithm_eval{});
100         return Assignee(range.begin(), range.end());
101     }
102 };
103 
104 
105 // This is a bit loose, because it allows us to write "sort(v) | unique(u)" or
106 // similar.  It works fine for this example, but in production code you may
107 // want to write out the functions generated by this macro, and add SFINAE or
108 // concepts constraints on the right template parameter.
109 BOOST_YAP_USER_BINARY_OPERATOR(bitwise_or, algorithm_expr, algorithm_expr)
110 
111 // It's useful to specially handle terminals, because we want a different set
112 // of operations to apply to them.  We don't want "sort(x)(y)" to be
113 // well-formed, for instance, or "sort | unique" either.
114 struct algorithm
115 {
116     static boost::yap::expr_kind const kind = boost::yap::expr_kind::terminal;
117 
118     boost::hana::tuple<algorithm_t> elements;
119 
120     BOOST_YAP_USER_CALL_OPERATOR_N(::algorithm_expr, 1)
121 };
122 
123 // Here are ready-made Yap terminals, one for each algorithm enumerated in
124 // algorithm_t.
125 constexpr algorithm sort{{algorithm_t::sort}};
126 constexpr algorithm unique{{algorithm_t::unique}};
127 
main()128 int main()
129 {
130     {
131 //[ typical_sort_unique_usage
132         std::vector<int> v1 = {0, 2, 2, 7, 1, 3, 8};
133         std::sort(v1.begin(), v1.end());
134         auto it = std::unique(v1.begin(), v1.end());
135         std::vector<int> const v2(v1.begin(), it);
136         assert(v2 == std::vector<int>({0, 1, 2, 3, 7, 8}));
137 //]
138     }
139     {
140 //[ pipable_sort_unique_usage
141         std::vector<int> v1 = {0, 2, 2, 7, 1, 3, 8};
142         std::vector<int> const v2 = sort(v1) | unique;
143         assert(v2 == std::vector<int>({0, 1, 2, 3, 7, 8}));
144 //]
145     }
146 }
147 //]
148