]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
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 | { | |
22 | Iter begin() const { return first_; } | |
23 | 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> | |
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> | |
52 | 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> | |
70 | 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> | |
95 | 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 | ||
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 | //] |