1 // Copyright (C) 2019 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 #include <boost/stl_interfaces/view_interface.hpp>
7
8 #include <algorithm>
9 #include <vector>
10
11 #include <cassert>
12
13
14 //[ all_view
15 // A subrange is simply an iterator-sentinel pair. This one is a bit simpler
16 // than the one in std::ranges; its missing a bunch of constructors, prev(),
17 // next(), and advance().
18 template<typename Iterator, typename Sentinel>
19 struct subrange
20 : boost::stl_interfaces::view_interface<subrange<Iterator, Sentinel>>
21 {
22 subrange() = default;
subrangesubrange23 constexpr subrange(Iterator it, Sentinel s) : first_(it), last_(s) {}
24
beginsubrange25 constexpr auto begin() const { return first_; }
endsubrange26 constexpr auto end() const { return last_; }
27
28 private:
29 Iterator first_;
30 Sentinel last_;
31 };
32
33 // std::view::all() returns one of several types, depending on what you pass
34 // it. Here, we're keeping it simple; all() always returns a subrange.
35 template<typename Range>
all(Range && range)36 auto all(Range && range)
37 {
38 return subrange<decltype(range.begin()), decltype(range.end())>(
39 range.begin(), range.end());
40 }
41
42 // A template alias that denotes the type of all(r) for some Range r.
43 template<typename Range>
44 using all_view = decltype(all(std::declval<Range>()));
45 //]
46
47 //[ drop_while_view_template
48
49 // Perhaps its clear now why we defined subrange, all(), etc. above.
50 // drop_while_view contains a view data member. If we just took any old range
51 // that was passed to drop_while_view's constructor, we'd copy the range
52 // itself, which may be a std::vector. So, we want to make a view out of
53 // whatever Range we're given so that this copy of an owning range does not
54 // happen.
55 template<typename Range, typename Pred>
56 struct drop_while_view
57 : boost::stl_interfaces::view_interface<drop_while_view<Range, Pred>>
58 {
59 using base_type = all_view<Range>;
60
61 drop_while_view() = default;
62
drop_while_viewdrop_while_view63 constexpr drop_while_view(Range & base, Pred pred) :
64 base_(all(base)),
65 pred_(std::move(pred))
66 {}
67
basedrop_while_view68 constexpr base_type base() const { return base_; }
preddrop_while_view69 constexpr Pred const & pred() const noexcept { return pred_; }
70
71 // A more robust implementation should probably cache the value computed
72 // by this function, so that subsequent calls can just return the cached
73 // iterator.
begindrop_while_view74 constexpr auto begin()
75 {
76 // We're forced to write this out as a raw loop, since no
77 // std::-namespace algorithms accept a sentinel.
78 auto first = base_.begin();
79 auto const last = base_.end();
80 for (; first != last; ++first) {
81 if (!pred_(*first))
82 break;
83 }
84 return first;
85 }
86
enddrop_while_view87 constexpr auto end() { return base_.end(); }
88
89 private:
90 base_type base_;
91 Pred pred_;
92 };
93
94 // Since this is a C++14 and later library, we're not using CTAD; we therefore
95 // need a make-function.
96 template<typename Range, typename Pred>
make_drop_while_view(Range & base,Pred pred)97 auto make_drop_while_view(Range & base, Pred pred)
98 {
99 return drop_while_view<Range, Pred>(base, std::move(pred));
100 }
101 //]
102
103
main()104 int main()
105 {
106 //[ drop_while_view_usage
107 std::vector<int> const ints = {2, 4, 3, 4, 5, 6};
108
109 // all() returns a subrange, which is a view type containing ints.begin()
110 // and ints.end().
111 auto all_ints = all(ints);
112
113 // This works using just the used-defined members of subrange: begin() and
114 // end().
115 assert(
116 std::equal(all_ints.begin(), all_ints.end(), ints.begin(), ints.end()));
117
118 // These are available because subrange is derived from view_interface.
119 assert(all_ints[2] == 3);
120 assert(all_ints.size() == 6u);
121
122 auto even = [](int x) { return x % 2 == 0; };
123 auto ints_after_even_prefix = make_drop_while_view(ints, even);
124
125 // Available via begin()/end()...
126 assert(std::equal(
127 ints_after_even_prefix.begin(),
128 ints_after_even_prefix.end(),
129 ints.begin() + 2,
130 ints.end()));
131
132 // ... and via view_interface.
133 assert(!ints_after_even_prefix.empty());
134 assert(ints_after_even_prefix[2] == 5);
135 assert(ints_after_even_prefix.back() == 6);
136 //]
137 }
138