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.

332 lines
11KB

  1. /// \file
  2. // Range v3 library
  3. //
  4. // Copyright Eric Niebler 2013-present
  5. // Copyright Casey Carter 2017
  6. //
  7. // Use, modification and distribution is subject to the
  8. // Boost Software License, Version 1.0. (See accompanying
  9. // file LICENSE_1_0.txt or copy at
  10. // http://www.boost.org/LICENSE_1_0.txt)
  11. //
  12. // Project home: https://github.com/ericniebler/range-v3
  13. //
  14. #ifndef RANGES_V3_VIEW_STRIDE_HPP
  15. #define RANGES_V3_VIEW_STRIDE_HPP
  16. #include <type_traits>
  17. #include <utility>
  18. #include <meta/meta.hpp>
  19. #include <range/v3/range_fwd.hpp>
  20. #include <range/v3/functional/bind_back.hpp>
  21. #include <range/v3/iterator/operations.hpp>
  22. #include <range/v3/range/access.hpp>
  23. #include <range/v3/range/concepts.hpp>
  24. #include <range/v3/range/primitives.hpp>
  25. #include <range/v3/range/traits.hpp>
  26. #include <range/v3/utility/static_const.hpp>
  27. #include <range/v3/view/adaptor.hpp>
  28. #include <range/v3/view/all.hpp>
  29. #include <range/v3/view/view.hpp>
  30. namespace ranges
  31. {
  32. /// \cond
  33. template<typename Rng>
  34. struct stride_view;
  35. namespace detail
  36. {
  37. template<typename Rng>
  38. using stride_view_adaptor =
  39. view_adaptor<stride_view<Rng>, Rng,
  40. is_finite<Rng>::value ? finite : range_cardinality<Rng>::value>;
  41. // Bidirectional stride views need to remember the distance between
  42. // the penultimate iterator and the last iterator - which may be less
  43. // than the stride - so that decrementing an last iterator properly
  44. // produces the penultimate iterator. stride_view_base specializes on
  45. // that distinction so that only Bidirectional stride views have the
  46. // data member "offset_".
  47. template<typename Rng, bool BidiRange>
  48. struct stride_view_base_;
  49. template<typename Rng>
  50. using stride_view_base = stride_view_base_<Rng, (bool)bidirectional_range<Rng>>;
  51. template<typename Rng, bool /*= bidirectional_range<Rng>*/>
  52. struct stride_view_base_ : stride_view_adaptor<Rng>
  53. {
  54. stride_view_base_() = default;
  55. constexpr stride_view_base_(Rng && rng, range_difference_t<Rng> const stride)
  56. : stride_view_adaptor<Rng>{std::move(rng)}
  57. , stride_{(RANGES_EXPECT(0 < stride), stride)}
  58. , offset_{calc_offset(meta::bool_<sized_range<Rng>>{})}
  59. {}
  60. protected:
  61. constexpr void set_offset(range_difference_t<Rng> const delta) noexcept
  62. {
  63. RANGES_EXPECT(0 <= delta && delta < stride_);
  64. if(0 > offset_)
  65. offset_ = delta;
  66. else
  67. RANGES_EXPECT(offset_ == delta);
  68. }
  69. constexpr void set_offset(range_difference_t<Rng> const) const noexcept
  70. {}
  71. constexpr range_difference_t<Rng> get_offset(bool check = true) const noexcept
  72. {
  73. RANGES_EXPECT(!check || 0 <= offset_);
  74. return offset_;
  75. }
  76. range_difference_t<Rng> stride_;
  77. range_difference_t<Rng> offset_ = -1;
  78. private:
  79. constexpr range_difference_t<Rng> calc_offset(std::true_type)
  80. {
  81. if(auto const rem = ranges::distance(this->base()) % stride_)
  82. return stride_ - rem;
  83. else
  84. return 0;
  85. }
  86. constexpr range_difference_t<Rng> calc_offset(std::false_type) const noexcept
  87. {
  88. return -1;
  89. }
  90. };
  91. template<typename Rng>
  92. struct stride_view_base_<Rng, false> : stride_view_adaptor<Rng>
  93. {
  94. stride_view_base_() = default;
  95. constexpr stride_view_base_(Rng && rng, range_difference_t<Rng> const stride)
  96. : stride_view_adaptor<Rng>{std::move(rng)}
  97. , stride_{(RANGES_EXPECT(0 < stride), stride)}
  98. {}
  99. protected:
  100. constexpr void set_offset(range_difference_t<Rng> const) const noexcept
  101. {}
  102. constexpr range_difference_t<Rng> get_offset(bool = true) const noexcept
  103. {
  104. return 0;
  105. }
  106. range_difference_t<Rng> stride_;
  107. };
  108. } // namespace detail
  109. /// \endcond
  110. /// \addtogroup group-views
  111. /// @{
  112. template<typename Rng>
  113. struct stride_view : detail::stride_view_base<Rng>
  114. {
  115. private:
  116. friend range_access;
  117. // stride_view const models Range if Rng const models Range, and
  118. // either (1) Rng is sized, so we can pre-calculate offset_, or (2)
  119. // Rng is !Bidirectional, so it does not need offset_.
  120. static constexpr bool const_iterable() noexcept
  121. {
  122. return range<Rng const> &&
  123. (sized_range<Rng const> || !bidirectional_range<Rng const>);
  124. }
  125. // If the underlying range doesn't model common_range, then we can't
  126. // decrement the last and there's no reason to adapt the sentinel. Strictly
  127. // speaking, we don't have to adapt the last iterator of input and forward
  128. // ranges, but in the interests of making the resulting stride view model
  129. // common_range, adapt it anyway.
  130. template<bool Const>
  131. static constexpr bool can_bound() noexcept
  132. {
  133. using CRng = meta::const_if_c<Const, Rng>;
  134. return common_range<CRng> &&
  135. (sized_range<CRng> || !bidirectional_range<CRng>);
  136. }
  137. template<bool Const>
  138. struct adaptor : adaptor_base
  139. {
  140. private:
  141. friend struct adaptor<!Const>;
  142. using CRng = meta::const_if_c<Const, Rng>;
  143. using stride_view_t = meta::const_if_c<Const, stride_view>;
  144. stride_view_t * rng_;
  145. public:
  146. adaptor() = default;
  147. constexpr adaptor(stride_view_t * rng) noexcept
  148. : rng_(rng)
  149. {}
  150. CPP_template(bool Other)( //
  151. requires Const && (!Other)) adaptor(adaptor<Other> that)
  152. : rng_(that.rng_)
  153. {}
  154. constexpr void next(iterator_t<CRng> & it)
  155. {
  156. auto const last = ranges::end(rng_->base());
  157. RANGES_EXPECT(it != last);
  158. auto const delta = ranges::advance(it, rng_->stride_, last);
  159. if(it == last)
  160. {
  161. rng_->set_offset(delta);
  162. }
  163. }
  164. CPP_member
  165. constexpr auto prev(iterator_t<CRng> & it) -> CPP_ret(void)( //
  166. requires bidirectional_range<CRng>)
  167. {
  168. RANGES_EXPECT(it != ranges::begin(rng_->base()));
  169. auto delta = -rng_->stride_;
  170. if(it == ranges::end(rng_->base()))
  171. {
  172. RANGES_EXPECT(rng_->get_offset() >= 0);
  173. delta += rng_->get_offset();
  174. }
  175. ranges::advance(it, delta);
  176. }
  177. template<typename Other>
  178. constexpr auto distance_to(iterator_t<CRng> const & here,
  179. Other const & there) const
  180. -> CPP_ret(range_difference_t<Rng>)( //
  181. requires sized_sentinel_for<Other, iterator_t<CRng>>)
  182. {
  183. range_difference_t<Rng> delta = there - here;
  184. if(delta < 0)
  185. delta -= rng_->stride_ - 1;
  186. else
  187. delta += rng_->stride_ - 1;
  188. return delta / rng_->stride_;
  189. }
  190. CPP_member
  191. constexpr auto advance(iterator_t<CRng> & it,
  192. range_difference_t<Rng> n) -> CPP_ret(void)( //
  193. requires random_access_range<CRng>)
  194. {
  195. if(0 == n)
  196. return;
  197. n *= rng_->stride_;
  198. auto const last = ranges::end(rng_->base());
  199. if(it == last)
  200. {
  201. RANGES_EXPECT(n < 0);
  202. RANGES_EXPECT(rng_->get_offset() >= 0);
  203. n += rng_->get_offset();
  204. }
  205. if(0 < n)
  206. {
  207. auto delta = ranges::advance(it, n, last);
  208. if(it == last)
  209. {
  210. // advance hit the last of the base range.
  211. rng_->set_offset(delta % rng_->stride_);
  212. }
  213. }
  214. else if(0 > n)
  215. {
  216. #ifdef NDEBUG
  217. ranges::advance(it, n);
  218. #else
  219. auto const first = ranges::begin(rng_->base());
  220. auto const delta = ranges::advance(it, n, first);
  221. RANGES_EXPECT(delta == 0);
  222. #endif
  223. }
  224. }
  225. };
  226. constexpr auto begin_adaptor() noexcept -> adaptor<false>
  227. {
  228. return adaptor<false>{this};
  229. }
  230. CPP_member
  231. constexpr auto begin_adaptor() const noexcept
  232. -> CPP_ret(adaptor<true>)(requires(const_iterable()))
  233. {
  234. return adaptor<true>{this};
  235. }
  236. constexpr auto end_adaptor() noexcept
  237. -> meta::if_c<can_bound<false>(), adaptor<false>, adaptor_base>
  238. {
  239. return {this};
  240. }
  241. CPP_member
  242. constexpr auto end_adaptor() const noexcept
  243. -> CPP_ret(meta::if_c<can_bound<true>(), adaptor<true>, adaptor_base>)( //
  244. requires(const_iterable()))
  245. {
  246. return {this};
  247. }
  248. public:
  249. stride_view() = default;
  250. constexpr stride_view(Rng rng, range_difference_t<Rng> const stride)
  251. : detail::stride_view_base<Rng>{std::move(rng), stride}
  252. {}
  253. CPP_member
  254. constexpr auto CPP_fun(size)()(requires sized_range<Rng>)
  255. {
  256. using size_type = range_size_t<Rng>;
  257. auto const n = ranges::size(this->base());
  258. return (n + static_cast<size_type>(this->stride_) - 1) /
  259. static_cast<size_type>(this->stride_);
  260. }
  261. CPP_member
  262. constexpr auto CPP_fun(size)()(const requires sized_range<Rng const>)
  263. {
  264. using size_type = range_size_t<Rng const>;
  265. auto const n = ranges::size(this->base());
  266. return (n + static_cast<size_type>(this->stride_) - 1) /
  267. static_cast<size_type>(this->stride_);
  268. }
  269. };
  270. #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
  271. template<typename Rng>
  272. stride_view(Rng &&, range_difference_t<Rng>)->stride_view<views::all_t<Rng>>;
  273. #endif
  274. namespace views
  275. {
  276. struct stride_fn
  277. {
  278. private:
  279. friend view_access;
  280. template<typename Difference>
  281. constexpr static auto CPP_fun(bind)(stride_fn stride, Difference step)( //
  282. requires integral<Difference>)
  283. {
  284. return make_pipeable(bind_back(stride, std::move(step)));
  285. }
  286. public:
  287. template<typename Rng>
  288. constexpr auto operator()(Rng && rng, range_difference_t<Rng> step) const
  289. -> CPP_ret(stride_view<all_t<Rng>>)( //
  290. requires viewable_range<Rng> && input_range<Rng>)
  291. {
  292. return stride_view<all_t<Rng>>{all(static_cast<Rng &&>(rng)), step};
  293. }
  294. };
  295. /// \relates stride_fn
  296. /// \ingroup group-views
  297. RANGES_INLINE_VARIABLE(view<stride_fn>, stride)
  298. } // namespace views
  299. /// @}
  300. } // namespace ranges
  301. #include <range/v3/detail/satisfy_boost_range.hpp>
  302. RANGES_SATISFY_BOOST_RANGE(::ranges::stride_view)
  303. #endif