ranges: add a naive implemenation of "adjacent"
This works around a diffiency in the clang-19 ranges library.
This commit is contained in:
parent
cb7ef2ec9e
commit
8eeb35cf36
@ -544,6 +544,7 @@ list (
|
||||
random.hpp
|
||||
range.cpp
|
||||
range.hpp
|
||||
ranges/adjacent.hpp
|
||||
ranges/chunk.hpp
|
||||
ranges/enumerate.hpp
|
||||
ranges/izip.hpp
|
||||
|
177
cruft/util/ranges/adjacent.hpp
Normal file
177
cruft/util/ranges/adjacent.hpp
Normal file
@ -0,0 +1,177 @@
|
||||
#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>;
|
@ -94,6 +94,7 @@ if (TESTS)
|
||||
rand/generator/normal
|
||||
random
|
||||
range
|
||||
ranges/adjacent
|
||||
ranges/chunk
|
||||
rational
|
||||
region
|
||||
|
29
test/ranges/adjacent.cpp
Normal file
29
test/ranges/adjacent.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <cruft/util/ranges/adjacent.hpp>
|
||||
|
||||
#include <cruft/util/tap.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
int main()
|
||||
{
|
||||
static int constexpr INPUT[] = { 1, 1, 2, 3, 5, 8 };
|
||||
static int constexpr EXPECTED[][2] = {
|
||||
{ 1, 1 },
|
||||
{ 1, 2 },
|
||||
{ 2, 3 },
|
||||
{ 3, 5 },
|
||||
{ 5, 8 },
|
||||
};
|
||||
|
||||
cruft::TAP::logger tap;
|
||||
|
||||
const bool same = std::ranges::equal (
|
||||
std::views::all (INPUT) | cruft::ranges::pairwise,
|
||||
EXPECTED,
|
||||
[] (auto const &a, auto const &b) { return std::ranges::equal (a, b); }
|
||||
);
|
||||
|
||||
tap.expect (same, "ranges::pairwise fibonacci");
|
||||
|
||||
return tap.status ();
|
||||
}
|
Loading…
Reference in New Issue
Block a user