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