178 lines
4.7 KiB
C++
178 lines
4.7 KiB
C++
|
#pragma once
|
||
|
|
||
|
#include <array>
|
||
|
#include <ranges>
|
||
|
|
||
|
|
||
|
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 <std::ranges::forward_range V, std::size_t N>
|
||
|
requires std::ranges::view<V> and (N > 0)
|
||
|
class adjacent_view
|
||
|
: public std::ranges::view_interface<adjacent_view<V, N>>
|
||
|
{
|
||
|
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<std::ranges::range_value_t<V>, N>;
|
||
|
using difference_type = std::ranges::range_difference_t<V>;
|
||
|
|
||
|
private:
|
||
|
// Uses an index sequence in the hope to avoid default initialisation and copying for
|
||
|
// problematic value_type.
|
||
|
template <std::size_t ...I>
|
||
|
value_type
|
||
|
dereference (std::index_sequence<I...>) const
|
||
|
{
|
||
|
return { *m_cursors[I]..., };
|
||
|
}
|
||
|
|
||
|
std::array<std::ranges::iterator_t<V>, N> m_cursors;
|
||
|
|
||
|
public:
|
||
|
iterator ();
|
||
|
|
||
|
iterator (
|
||
|
std::ranges::iterator_t<V> first,
|
||
|
std::ranges::sentinel_t<V> 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<N> ());
|
||
|
}
|
||
|
|
||
|
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<V> m_end;
|
||
|
|
||
|
public:
|
||
|
sentinel (std::ranges::sentinel_t<V> 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 <std::size_t N>
|
||
|
struct adjacent_adapter
|
||
|
: std::ranges::range_adaptor_closure<adjacent_adapter<N>>
|
||
|
{
|
||
|
template <std::ranges::forward_range V>
|
||
|
decltype (auto)
|
||
|
operator () (V &&v) const
|
||
|
{
|
||
|
return adjacent_view<V, N> (
|
||
|
std::forward<V> (v)
|
||
|
);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
template <std::size_t N>
|
||
|
constexpr detail::adjacent_adapter<N> adjacent;
|
||
|
|
||
|
constexpr detail::adjacent_adapter<2> pairwise;
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class V, std::size_t N>
|
||
|
constexpr bool std::ranges::enable_borrowed_range<cruft::ranges::adjacent_view<V, N>> =
|
||
|
std::ranges::forward_range<V> && std::ranges::enable_borrowed_range<V>;
|