1 /// \file
2 // Range v3 library
3 //
4 //  Copyright Eric Niebler 2013-present
5 //
6 //  Use, modification and distribution is subject to the
7 //  Boost Software License, Version 1.0. (See accompanying
8 //  file LICENSE_1_0.txt or copy at
9 //  http://www.boost.org/LICENSE_1_0.txt)
10 //
11 // Project home: https://github.com/ericniebler/range-v3
12 //
13 
14 #ifndef RANGES_V3_VIEW_SPLIT_WHEN_HPP
15 #define RANGES_V3_VIEW_SPLIT_WHEN_HPP
16 
17 #include <type_traits>
18 #include <utility>
19 
20 #include <meta/meta.hpp>
21 
22 #include <range/v3/range_fwd.hpp>
23 
24 #include <range/v3/algorithm/find_if_not.hpp>
25 #include <range/v3/functional/bind_back.hpp>
26 #include <range/v3/functional/invoke.hpp>
27 #include <range/v3/iterator/default_sentinel.hpp>
28 #include <range/v3/iterator/operations.hpp>
29 #include <range/v3/range/access.hpp>
30 #include <range/v3/range/concepts.hpp>
31 #include <range/v3/range/traits.hpp>
32 #include <range/v3/utility/static_const.hpp>
33 #include <range/v3/view/all.hpp>
34 #include <range/v3/view/facade.hpp>
35 #include <range/v3/view/indirect.hpp>
36 #include <range/v3/view/iota.hpp>
37 #include <range/v3/view/take_while.hpp>
38 #include <range/v3/view/view.hpp>
39 
40 #include <range/v3/detail/prologue.hpp>
41 
42 namespace ranges
43 {
44     /// \addtogroup group-views
45     /// @{
46 
47     template<typename Rng, typename Fun>
48     struct split_when_view
49       : view_facade<split_when_view<Rng, Fun>,
50                     is_finite<Rng>::value ? finite : range_cardinality<Rng>::value>
51     {
52     private:
53         friend range_access;
54         Rng rng_;
55         semiregular_box_t<Fun> fun_;
56 
57         template<bool IsConst>
58         struct cursor
59         {
60         private:
61             friend range_access;
62             friend split_when_view;
63             friend struct cursor<!IsConst>;
64             bool zero_;
65             using CRng = meta::const_if_c<IsConst, Rng>;
66             iterator_t<CRng> cur_;
67             sentinel_t<CRng> last_;
68             using fun_ref_t = semiregular_box_ref_or_val_t<Fun, IsConst>;
69             fun_ref_t fun_;
70 
71             struct search_pred
72             {
73                 bool zero_;
74                 iterator_t<CRng> first_;
75                 sentinel_t<CRng> last_;
76                 fun_ref_t fun_;
operator ()ranges::split_when_view::cursor::search_pred77                 bool operator()(iterator_t<CRng> cur) const
78                 {
79                     return (zero_ && cur == first_) ||
80                            (cur != last_ && !invoke(fun_, cur, last_).first);
81                 }
82             };
83             using reference_ =
84                 indirect_view<take_while_view<iota_view<iterator_t<CRng>>, search_pred>>;
readranges::split_when_view::cursor85             reference_ read() const
86             {
87                 return reference_{{views::iota(cur_), {zero_, cur_, last_, fun_}}};
88             }
nextranges::split_when_view::cursor89             void next()
90             {
91                 RANGES_EXPECT(cur_ != last_);
92                 // If the last match consumed zero elements, bump the position.
93                 if(zero_)
94                 {
95                     zero_ = false;
96                     ++cur_;
97                 }
98                 for(; cur_ != last_; ++cur_)
99                 {
100                     auto p = invoke(fun_, cur_, last_);
101                     if(p.first)
102                     {
103                         zero_ = (cur_ == p.second);
104                         cur_ = p.second;
105                         return;
106                     }
107                 }
108             }
equalranges::split_when_view::cursor109             bool equal(default_sentinel_t) const
110             {
111                 return cur_ == last_;
112             }
equalranges::split_when_view::cursor113             bool equal(cursor const & that) const
114             {
115                 return cur_ == that.cur_;
116             }
cursorranges::split_when_view::cursor117             cursor(fun_ref_t fun, iterator_t<CRng> first, sentinel_t<CRng> last)
118               : cur_(first)
119               , last_(last)
120               , fun_(fun)
121             {
122                 // For skipping an initial zero-length match
123                 auto p = invoke(fun, first, last);
124                 zero_ = p.first && first == p.second;
125             }
126 
127         public:
128             cursor() = default;
129             template(bool Other)(
130                 /// \pre
131                 requires IsConst AND CPP_NOT(Other)) //
132             cursor(cursor<Other> that)
133               : cursor{std::move(that.cur_), std::move(that.last_), std::move(that.fun_)}
134             {}
135         };
136         cursor<false> begin_cursor()
137         {
138             return {fun_, ranges::begin(rng_), ranges::end(rng_)};
139         }
140         template(bool Const = true)(
141             /// \pre
142             requires Const AND range<meta::const_if_c<Const, Rng>> AND
143                 invocable<Fun const &, iterator_t<meta::const_if_c<Const, Rng>>,
144                           sentinel_t<meta::const_if_c<Const, Rng>>>)
145         cursor<Const> begin_cursor() const
146         {
147             return {fun_, ranges::begin(rng_), ranges::end(rng_)};
148         }
149 
150     public:
151         split_when_view() = default;
152         split_when_view(Rng rng, Fun fun)
153           : rng_(std::move(rng))
154           , fun_(std::move(fun))
155         {}
156     };
157 
158 #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
159     template(typename Rng, typename Fun)(
160         /// \pre
161         requires copy_constructible<Fun>)
162     split_when_view(Rng &&, Fun)
163         -> split_when_view<views::all_t<Rng>, Fun>;
164 #endif
165 
166     namespace views
167     {
168         struct split_when_base_fn
169         {
170         private:
171             template<typename Pred>
172             struct predicate_pred_
173             {
174                 semiregular_box_t<Pred> pred_;
175 
176                 template(typename I, typename S)(
177                     /// \pre
178                     requires sentinel_for<S, I>)
179                 std::pair<bool, I> operator()(I cur, S last) const
180                 {
181                     auto where = ranges::find_if_not(cur, last, std::ref(pred_));
182                     return {cur != where, where};
183                 }
184             };
185 
186         public:
187             template(typename Rng, typename Fun)(
188                 /// \pre
189                 requires viewable_range<Rng> AND forward_range<Rng> AND
190                     invocable<Fun &, iterator_t<Rng>, sentinel_t<Rng>> AND
191                     invocable<Fun &, iterator_t<Rng>, iterator_t<Rng>> AND
192                     copy_constructible<Fun> AND
193                     convertible_to<
194                         invoke_result_t<Fun &, iterator_t<Rng>, sentinel_t<Rng>>,
195                         std::pair<bool, iterator_t<Rng>>>)
196             split_when_view<all_t<Rng>, Fun> operator()(Rng && rng, Fun fun) const //
197             {
198                 return {all(static_cast<Rng &&>(rng)), std::move(fun)};
199             }
200             template(typename Rng, typename Fun)(
201                 /// \pre
202                 requires viewable_range<Rng> AND forward_range<Rng> AND
203                     predicate<Fun const &, range_reference_t<Rng>> AND
204                     copy_constructible<Fun>)
205             split_when_view<all_t<Rng>, predicate_pred_<Fun>> //
206             operator()(Rng && rng, Fun fun) const
207             {
208                 return {all(static_cast<Rng &&>(rng)),
209                         predicate_pred_<Fun>{std::move(fun)}};
210             }
211         };
212 
213         struct split_when_fn : split_when_base_fn
214         {
215             using split_when_base_fn::operator();
216 
217             template<typename T>
218             constexpr auto operator()(T && t) const
219             {
220                 return make_view_closure(
221                     bind_back(split_when_base_fn{}, static_cast<T &&>(t)));
222             }
223         };
224 
225         /// \relates split_when_fn
226         /// \ingroup group-views
227         RANGES_INLINE_VARIABLE(split_when_fn, split_when)
228     } // namespace views
229     /// @}
230 } // namespace ranges
231 
232 #include <range/v3/detail/epilogue.hpp>
233 #include <range/v3/detail/satisfy_boost_range.hpp>
234 RANGES_SATISFY_BOOST_RANGE(::ranges::split_when_view)
235 
236 #endif
237