#pragma once #include #include namespace cruft::ranges { /// A very naive implementation of std::views::adjacent provided because clang-19 lacks it /// /// clang#19: check me again in clang-20 /// /// Models the main iterator as an array of iterators, and the sentinel as a thin wrapper // around the underlying range's sentinel. template requires std::ranges::view and (N > 0) class adjacent_view : public std::ranges::view_interface> { private: V base_; public: class sentinel; class iterator { public: using iterator_category = std::forward_iterator_tag; using iterator_concept = std::forward_iterator_tag; using value_type = std::array, N>; using difference_type = std::ranges::range_difference_t; private: // Uses an index sequence in the hope to avoid default initialisation and copying for // problematic value_type. template value_type dereference (std::index_sequence) const { return { *m_cursors[I]..., }; } std::array, N> m_cursors; public: iterator (); iterator ( std::ranges::iterator_t first, std::ranges::sentinel_t last_ ) { for (std::size_t i = 0; i < N; ++i) { m_cursors[i] = first; if (first != last_) ++first; } } iterator (iterator const&) = default; iterator (iterator &&) noexcept = default; iterator& operator= (iterator const&) = default; iterator& operator= (iterator &&) noexcept = default; iterator& operator++ () { for (auto &i: m_cursors) ++i; return *this; } iterator operator++ (int) const; value_type operator* () const { return dereference (std::make_index_sequence ()); } bool operator==(iterator const &rhs) const { return m_cursors.back () == rhs.m_cursors.back (); } bool operator== (sentinel const &rhs) const { return m_cursors.back () == rhs.inner (); } }; class sentinel { private: std::ranges::sentinel_t m_end; public: sentinel (std::ranges::sentinel_t end_) : m_end (std::move (end_)) { ; } sentinel () = default; sentinel (sentinel const&) = default; sentinel (sentinel&&) = default; sentinel& operator= (sentinel const&) = default; sentinel& operator= (sentinel &&) noexcept = default; auto const& inner (void) const& { return m_end; } }; adjacent_view (V base) : base_ (std::move (base)) { ; } iterator begin (void) const { return iterator ( std::ranges::begin (base_), std::ranges::end (base_) ); } iterator begin (void) { return iterator ( std::ranges::begin (base_), std::ranges::end (base_) ); } sentinel end (void) { return sentinel { std::ranges::end (base_) }; } sentinel end (void) const { return sentinel { std::ranges::end (base_) }; } }; namespace detail { // Subclasses `closure` so we get the pipe operator and such. template struct adjacent_adapter : std::ranges::range_adaptor_closure> { template decltype (auto) operator () (V &&v) const { return adjacent_view ( std::forward (v) ); } }; } template constexpr detail::adjacent_adapter adjacent; constexpr detail::adjacent_adapter<2> pairwise; } template constexpr bool std::ranges::enable_borrowed_range> = std::ranges::forward_range && std::ranges::enable_borrowed_range;