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
|
random.hpp
|
||||||
range.cpp
|
range.cpp
|
||||||
range.hpp
|
range.hpp
|
||||||
|
ranges/adjacent.hpp
|
||||||
ranges/chunk.hpp
|
ranges/chunk.hpp
|
||||||
ranges/enumerate.hpp
|
ranges/enumerate.hpp
|
||||||
ranges/izip.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
|
rand/generator/normal
|
||||||
random
|
random
|
||||||
range
|
range
|
||||||
|
ranges/adjacent
|
||||||
ranges/chunk
|
ranges/chunk
|
||||||
rational
|
rational
|
||||||
region
|
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