#pragma once #include #include #include #include namespace cruft::ranges { // Largely based on the exposition elements of the specification at // https://en.cppreference.com/w/cpp/ranges/chunk_view // // A lot of the implementation is simplified by assuming we're only working with forward ranges. // But we should remove this code when clang-19 gets released. // // clang#18: remove me when clang gets std::views::chunk template requires std::ranges::forward_range class chunk_view : public std::ranges::view_interface> { private: V base_; std::ranges::range_difference_t n_; public: template class iterator { friend chunk_view; private: using Parent = std::conditional_t; using Base = std::conditional_t; std::ranges::iterator_t current_; std::ranges::iterator_t end_; std::ranges::range_difference_t n_; std::ranges::range_difference_t missing_; public: using iterator_category = std::input_iterator_tag; // HACK: assuming we're modelling only forward iterator using iterator_concept = std::forward_iterator_tag; using value_type = decltype( std::views::take( std::ranges::subrange(current_, end_), n_ ) ); using difference_type = std::ranges::range_difference_t; iterator() = default; constexpr iterator (iterator i) requires Const and std::convertible_to, std::ranges::iterator_t> and std::convertible_to, std::ranges::sentinel_t> : current_ (std::move (i.current_)) , end_ (std::move (i.end_)) , n_ (i.n_) , missing_ (i.missing_) { ; } private: constexpr iterator ( Parent* parent, std::ranges::iterator_t current, std::ranges::range_difference_t missing = 0 ) : current_ (current) , end_ (std::ranges::end (parent->base_)) , n_ (parent->n_) , missing_ (missing) {} public: constexpr value_type operator*() const { CHECK_NEQ (current_, end_); return std::views::take ( std::ranges::subrange (current_, end_), n_ ); } constexpr iterator& operator++() { missing_ = std::ranges::advance(current_, n_, end_); return *this; } constexpr iterator operator++(int) { auto tmp = *this; ++*this; return tmp; } constexpr iterator& operator--() requires std::ranges::bidirectional_range { std::ranges::advance(current_, missing_ - n_); missing_ = 0; return *this; } constexpr iterator operator+= (difference_type x) requires std::ranges::random_access_range { if (x > 0) { CHECK (std::ranges::distance(current_, end_) > n_ * (x - 1)); std::ranges::advance(current_, n_ * (x - 1)); missing_ = std::ranges::advance(current_, n_, end_); } else if (x < 0) { std::ranges::advance(current_, n_ * x + missing_); missing_ = 0; } return *this; } constexpr iterator& operator-= (difference_type x) requires std::ranges::random_access_range { return *this += -x; } friend constexpr bool operator== (iterator const& x, iterator const &y) { return x.current_ == y.current_; } friend constexpr bool operator== (iterator const& x, std::default_sentinel_t) { return x.current_ == x.end_; } friend constexpr auto operator<=> (iterator const& x, iterator const& y) requires std::ranges::random_access_range && std::three_way_comparable> { return x.current_ <=> y.current_; } }; public: constexpr explicit chunk_view (V base, std::ranges::range_difference_t n) : base_ (std::move (base)) , n_ (n) { ; } constexpr V base () const& requires std::copy_constructible { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin () { return iterator(this, std::ranges::begin(base_)); } constexpr auto begin () const requires std::ranges::forward_range { return iterator( this, std::ranges::begin(base_) ); } constexpr auto end () { if constexpr (std::ranges::common_range and std::ranges::sized_range) { auto missing = (n_ - std::ranges::distance (base_) % n_) % n_; return iterator(this, std::ranges::end (base_), missing); } else if constexpr (std::ranges::common_range and !std::ranges::bidirectional_range) return iterator(this, std::ranges::end(base_)); else return std::default_sentinel; } constexpr auto end () const requires std::ranges::forward_range { if constexpr (std::ranges::common_range and std::ranges::sized_range) { auto missing = (n_ - std::ranges::distance (base_) % n_) % n_; return iterator(this, std::ranges::end (base_), missing); } else if constexpr (std::ranges::common_range and !std::ranges::bidirectional_range) return iterator(this, std::ranges::end(base_)); else return std::default_sentinel; } }; template chunk_view (R&&, std::ranges::range_difference_t) -> chunk_view>; namespace detail { struct chunk_closure_t : std::ranges::range_adaptor_closure { int n; template constexpr auto operator() (R &&t) const { return chunk_view (std::forward (t), n); } }; struct chunk_base { template constexpr auto operator() ( R&& r, std::ranges::range_difference_t n ) const { return chunk_view (std::views::all (std::forward (r)), n); } constexpr auto operator() (int n) const { return chunk_closure_t {.n = n}; } }; } constexpr detail::chunk_base chunk; } template< class V > constexpr bool std::ranges::enable_borrowed_range> = std::ranges::forward_range && std::ranges::enable_borrowed_range;