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.

179 lines
5.7KB

  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_GROUP_BY_HPP
  14. #define RANGES_V3_VIEW_GROUP_BY_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/range/access.hpp>
  24. #include <range/v3/range/concepts.hpp>
  25. #include <range/v3/range/traits.hpp>
  26. #include <range/v3/utility/semiregular_box.hpp>
  27. #include <range/v3/utility/static_const.hpp>
  28. #include <range/v3/view/facade.hpp>
  29. #include <range/v3/view/subrange.hpp>
  30. #include <range/v3/view/take_while.hpp>
  31. #include <range/v3/view/view.hpp>
  32. namespace ranges
  33. {
  34. // TODO group_by could support Input ranges by keeping mutable state in
  35. // the range itself. The group_by view would then be mutable-only and
  36. // Input.
  37. /// \addtogroup group-views
  38. /// @{
  39. template<typename Rng, typename Fun>
  40. struct group_by_view
  41. : view_facade<group_by_view<Rng, Fun>,
  42. is_finite<Rng>::value ? finite : range_cardinality<Rng>::value>
  43. {
  44. private:
  45. friend range_access;
  46. Rng rng_;
  47. semiregular_box_t<Fun> fun_;
  48. template<bool IsConst>
  49. struct cursor
  50. {
  51. private:
  52. friend struct cursor<!IsConst>;
  53. friend range_access;
  54. friend group_by_view;
  55. using CRng = meta::const_if_c<IsConst, Rng>;
  56. iterator_t<CRng> cur_;
  57. sentinel_t<CRng> last_;
  58. semiregular_box_ref_or_val_t<Fun, IsConst> fun_;
  59. struct pred
  60. {
  61. iterator_t<CRng> first_;
  62. semiregular_box_ref_or_val_t<Fun, IsConst> fun_;
  63. bool operator()(range_reference_t<CRng> r) const
  64. {
  65. return invoke(fun_, *first_, r);
  66. }
  67. };
  68. #ifdef RANGES_WORKAROUND_MSVC_787074
  69. template<bool Const = IsConst>
  70. auto read() const
  71. -> take_while_view<subrange<iterator_t<meta::const_if_c<Const, Rng>>,
  72. sentinel_t<meta::const_if_c<Const, Rng>>>,
  73. pred>
  74. #else // ^^^ workaround / no workaround vvv
  75. auto read() const
  76. -> take_while_view<subrange<iterator_t<CRng>, sentinel_t<CRng>>, pred>
  77. #endif // RANGES_WORKAROUND_MSVC_787074
  78. {
  79. return {{cur_, last_}, {cur_, fun_}};
  80. }
  81. void next()
  82. {
  83. cur_ = find_if_not(cur_, last_, pred{cur_, fun_});
  84. }
  85. bool equal(default_sentinel_t) const
  86. {
  87. return cur_ == last_;
  88. }
  89. bool equal(cursor const & that) const
  90. {
  91. return cur_ == that.cur_;
  92. }
  93. cursor(semiregular_box_ref_or_val_t<Fun, IsConst> fun, iterator_t<CRng> first,
  94. sentinel_t<CRng> last)
  95. : cur_(first)
  96. , last_(last)
  97. , fun_(fun)
  98. {}
  99. public:
  100. cursor() = default;
  101. CPP_template(bool Other)( //
  102. requires IsConst && (!Other)) cursor(cursor<Other> that)
  103. : cur_(std::move(that.cur_))
  104. , last_(std::move(last_))
  105. , fun_(std::move(that.fun_))
  106. {}
  107. };
  108. cursor<false> begin_cursor()
  109. {
  110. return {fun_, ranges::begin(rng_), ranges::end(rng_)};
  111. }
  112. template<bool Const = true>
  113. auto begin_cursor() const -> CPP_ret(cursor<Const>)( //
  114. requires Const && range<meta::const_if_c<Const, Rng>> && invocable<
  115. Fun const &, range_common_reference_t<meta::const_if_c<Const, Rng>>,
  116. range_common_reference_t<meta::const_if_c<Const, Rng>>>)
  117. {
  118. return {fun_, ranges::begin(rng_), ranges::end(rng_)};
  119. }
  120. public:
  121. group_by_view() = default;
  122. constexpr group_by_view(Rng rng, Fun fun)
  123. : rng_(std::move(rng))
  124. , fun_(std::move(fun))
  125. {}
  126. };
  127. #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
  128. CPP_template(typename Rng, typename Fun)(requires copy_constructible<Fun>)
  129. group_by_view(Rng &&, Fun)
  130. ->group_by_view<views::all_t<Rng>, Fun>;
  131. #endif
  132. namespace views
  133. {
  134. struct group_by_fn
  135. {
  136. private:
  137. friend view_access;
  138. template<typename Fun>
  139. static constexpr auto bind(group_by_fn group_by, Fun fun)
  140. {
  141. return make_pipeable(bind_back(group_by, std::move(fun)));
  142. }
  143. public:
  144. template<typename Rng, typename Fun>
  145. constexpr auto operator()(Rng && rng, Fun fun) const
  146. -> CPP_ret(group_by_view<all_t<Rng>, Fun>)( //
  147. requires viewable_range<Rng> && forward_range<Rng> &&
  148. indirect_relation<Fun, iterator_t<Rng>>)
  149. {
  150. return {all(static_cast<Rng &&>(rng)), std::move(fun)};
  151. }
  152. };
  153. /// \relates group_by_fn
  154. /// \ingroup group-views
  155. RANGES_INLINE_VARIABLE(view<group_by_fn>, group_by)
  156. } // namespace views
  157. /// @}
  158. } // namespace ranges
  159. #include <range/v3/detail/satisfy_boost_range.hpp>
  160. RANGES_SATISFY_BOOST_RANGE(::ranges::group_by_view)
  161. #endif