You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
7.3KB

  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. #ifndef RANGES_V3_VIEW_SPLIT_WHEN_HPP
  14. #define RANGES_V3_VIEW_SPLIT_WHEN_HPP
  15. #include <type_traits>
  16. #include <utility>
  17. #include <meta/meta.hpp>
  18. #include <range/v3/range_fwd.hpp>
  19. #include <range/v3/algorithm/find_if_not.hpp>
  20. #include <range/v3/functional/bind_back.hpp>
  21. #include <range/v3/functional/invoke.hpp>
  22. #include <range/v3/iterator/default_sentinel.hpp>
  23. #include <range/v3/iterator/operations.hpp>
  24. #include <range/v3/range/access.hpp>
  25. #include <range/v3/range/concepts.hpp>
  26. #include <range/v3/range/traits.hpp>
  27. #include <range/v3/utility/static_const.hpp>
  28. #include <range/v3/view/all.hpp>
  29. #include <range/v3/view/facade.hpp>
  30. #include <range/v3/view/indirect.hpp>
  31. #include <range/v3/view/iota.hpp>
  32. #include <range/v3/view/take_while.hpp>
  33. #include <range/v3/view/view.hpp>
  34. namespace ranges
  35. {
  36. /// \addtogroup group-views
  37. /// @{
  38. template<typename Rng, typename Fun>
  39. struct split_when_view
  40. : view_facade<split_when_view<Rng, Fun>,
  41. is_finite<Rng>::value ? finite : range_cardinality<Rng>::value>
  42. {
  43. private:
  44. friend range_access;
  45. Rng rng_;
  46. semiregular_box_t<Fun> fun_;
  47. template<bool IsConst>
  48. struct cursor
  49. {
  50. private:
  51. friend range_access;
  52. friend split_when_view;
  53. friend struct cursor<!IsConst>;
  54. bool zero_;
  55. using CRng = meta::const_if_c<IsConst, Rng>;
  56. iterator_t<CRng> cur_;
  57. sentinel_t<CRng> last_;
  58. using fun_ref_t = semiregular_box_ref_or_val_t<Fun, IsConst>;
  59. fun_ref_t fun_;
  60. struct search_pred
  61. {
  62. bool zero_;
  63. iterator_t<CRng> first_;
  64. sentinel_t<CRng> last_;
  65. fun_ref_t fun_;
  66. bool operator()(iterator_t<CRng> cur) const
  67. {
  68. return (zero_ && cur == first_) ||
  69. (cur != last_ && !invoke(fun_, cur, last_).first);
  70. }
  71. };
  72. using reference_ =
  73. indirect_view<take_while_view<iota_view<iterator_t<CRng>>, search_pred>>;
  74. reference_ read() const
  75. {
  76. return reference_{{views::iota(cur_), {zero_, cur_, last_, fun_}}};
  77. }
  78. void next()
  79. {
  80. RANGES_EXPECT(cur_ != last_);
  81. // If the last match consumed zero elements, bump the position.
  82. if(zero_)
  83. {
  84. zero_ = false;
  85. ++cur_;
  86. }
  87. for(; cur_ != last_; ++cur_)
  88. {
  89. auto p = invoke(fun_, cur_, last_);
  90. if(p.first)
  91. {
  92. zero_ = (cur_ == p.second);
  93. cur_ = p.second;
  94. return;
  95. }
  96. }
  97. }
  98. bool equal(default_sentinel_t) const
  99. {
  100. return cur_ == last_;
  101. }
  102. bool equal(cursor const & that) const
  103. {
  104. return cur_ == that.cur_;
  105. }
  106. cursor(fun_ref_t fun, iterator_t<CRng> first, sentinel_t<CRng> last)
  107. : cur_(first)
  108. , last_(last)
  109. , fun_(fun)
  110. {
  111. // For skipping an initial zero-length match
  112. auto p = invoke(fun, first, last);
  113. zero_ = p.first && first == p.second;
  114. }
  115. public:
  116. cursor() = default;
  117. CPP_template(bool Other)( //
  118. requires IsConst && (!Other)) cursor(cursor<Other> that)
  119. : cursor{std::move(that.cur_), std::move(that.last_), std::move(that.fun_)}
  120. {}
  121. };
  122. cursor<false> begin_cursor()
  123. {
  124. return {fun_, ranges::begin(rng_), ranges::end(rng_)};
  125. }
  126. template<bool Const = true>
  127. auto begin_cursor() const -> CPP_ret(cursor<Const>)( //
  128. requires Const && range<meta::const_if_c<Const, Rng>> &&
  129. invocable<Fun const &, iterator_t<meta::const_if_c<Const, Rng>>,
  130. sentinel_t<meta::const_if_c<Const, Rng>>>)
  131. {
  132. return {fun_, ranges::begin(rng_), ranges::end(rng_)};
  133. }
  134. public:
  135. split_when_view() = default;
  136. split_when_view(Rng rng, Fun fun)
  137. : rng_(std::move(rng))
  138. , fun_(std::move(fun))
  139. {}
  140. };
  141. #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
  142. CPP_template(typename Rng, typename Fun)(requires copy_constructible<Fun>)
  143. split_when_view(Rng &&, Fun)
  144. ->split_when_view<views::all_t<Rng>, Fun>;
  145. #endif
  146. namespace views
  147. {
  148. struct split_when_fn
  149. {
  150. private:
  151. friend view_access;
  152. template<typename T>
  153. static auto bind(split_when_fn split_when, T && t)
  154. {
  155. return make_pipeable(bind_back(split_when, static_cast<T &&>(t)));
  156. }
  157. template<typename Pred>
  158. struct predicate_pred
  159. {
  160. semiregular_box_t<Pred> pred_;
  161. template<typename I, typename S>
  162. auto operator()(I cur, S last) const -> CPP_ret(std::pair<bool, I>)( //
  163. requires sentinel_for<S, I>)
  164. {
  165. auto where = ranges::find_if_not(cur, last, std::ref(pred_));
  166. return {cur != where, where};
  167. }
  168. };
  169. public:
  170. template<typename Rng, typename Fun>
  171. auto operator()(Rng && rng, Fun fun) const -> CPP_ret(
  172. split_when_view<all_t<Rng>, Fun>)( //
  173. requires viewable_range<Rng> && forward_range<Rng> &&
  174. invocable<Fun &, iterator_t<Rng>, sentinel_t<Rng>> &&
  175. invocable<Fun &, iterator_t<Rng>, iterator_t<Rng>> &&
  176. copy_constructible<Fun> && convertible_to<
  177. invoke_result_t<Fun &, iterator_t<Rng>, sentinel_t<Rng>>,
  178. std::pair<bool, iterator_t<Rng>>>)
  179. {
  180. return {all(static_cast<Rng &&>(rng)), std::move(fun)};
  181. }
  182. template<typename Rng, typename Fun>
  183. auto operator()(Rng && rng, Fun fun) const
  184. -> CPP_ret(split_when_view<all_t<Rng>, predicate_pred<Fun>>)( //
  185. requires viewable_range<Rng> && forward_range<Rng> && predicate<
  186. Fun const &, range_reference_t<Rng>> && copy_constructible<Fun>)
  187. {
  188. return {all(static_cast<Rng &&>(rng)),
  189. predicate_pred<Fun>{std::move(fun)}};
  190. }
  191. };
  192. /// \relates split_when_fn
  193. /// \ingroup group-views
  194. RANGES_INLINE_VARIABLE(view<split_when_fn>, split_when)
  195. } // namespace views
  196. /// @}
  197. } // namespace ranges
  198. #include <range/v3/detail/satisfy_boost_range.hpp>
  199. RANGES_SATISFY_BOOST_RANGE(::ranges::split_when_view)
  200. #endif