]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/yap/example/pipable_algorithms.cpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / libs / yap / example / pipable_algorithms.cpp
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 //]