FE 0.13.1
Header-only C++ frontend library
Loading...
Searching...
No Matches
span.h
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <iterator>
5#include <ranges>
6#include <span>
7#include <type_traits>
8
9namespace fe {
10
11/// Something which behaves like `std::vector` or `std::array`.
12template<class Vec>
13concept Vectorlike = requires(Vec vec) {
14 typename Vec::value_type;
15 vec.size();
16 vec.data();
17};
18
19/// This is a thin wrapper for [`std::span<T, N>`](https://en.cppreference.com/w/cpp/container/span)
20/// with the following additional features:
21/// * Constructor with `std::initializer_list` (C++26 will get this ...)
22/// * Constructor for any compatible Vectorlike argument
23/// * rsubspan (reverse subspan)
24/// * structured binding, if `N != std::dynamic_extent:`
25/// ```
26/// void f(Span<int, 3> span) {
27/// auto& [a, b, c] = span;
28/// b = 23;
29/// // ...
30/// }
31/// ```
32template<class T, size_t N = std::dynamic_extent>
33class Span : public std::span<T, N> {
34public:
35 using Base = std::span<T, N>;
36 constexpr static auto D = std::dynamic_extent;
37
38 using Base::data;
39 using Base::empty;
40 using Base::size;
41
42 /// @name Constructors
43 ///@{
44 using Base::Base;
45 explicit(N != D) constexpr Span(std::initializer_list<T> init) noexcept
46 : Base(std::begin(init), std::ranges::distance(init)) {}
47 constexpr Span(std::span<T, N> span) noexcept
48 : Base(span) {}
49 template<Vectorlike Vec>
50 requires(std::is_same_v<typename Vec::value_type, T> && !std::is_const_v<Vec>)
51 explicit(N != D) constexpr Span(Vec& vec) noexcept(noexcept(vec.data()) && noexcept(vec.size()))
52 : Base(vec.data(), vec.size()) {}
53 template<Vectorlike Vec>
54 requires(std::is_const_v<T> && std::is_same_v<std::add_const_t<typename Vec::value_type>, T>)
55 explicit(N != D) constexpr Span(const Vec& vec) noexcept(noexcept(vec.data()) && noexcept(vec.size()))
56 : Base(vec.data(), vec.size()) {}
57 constexpr explicit Span(typename Base::pointer p) noexcept
58 : Base(p, N) {
59 static_assert(N != D);
60 }
61 ///@}
62
63 /// @name subspan
64 /// Wrappers for `std::span::subspan` that return a
65 /// `fe::Span`. Example: If `span` points to `0, 1, 2, 3, 4, 5, 6, 7, 8, 9`, then
66 /// * `span.subspan<2, 5>()` and `span.subspan(2, 5)` will point to `2, 3, 4, 5, 6`.
67 /// * `span.subspan<2>()` and `span.subspan(2)` will point to `2, 3, 4, 5, 6, 7, 8, 9`.
68 ///@{
69 [[nodiscard]] constexpr Span<T, D> subspan(size_t i, size_t n = D) const noexcept { return Base::subspan(i, n); }
70
71 template<size_t i, size_t n = D>
72 [[nodiscard]] constexpr Span<T, n != D ? n : (N != D ? N - i : D)> subspan() const noexcept {
73 return Base::template subspan<i, n>();
74 }
75
76 /// Get first `n` elements while keeping track of size statically - useful for structured binding!
77 template<size_t n>
78 [[nodiscard]] constexpr Span<T, n> span() const noexcept {
79 return Base::template subspan<0, n>();
80 }
81 ///@}
82
83 /// @name rsubspan
84 /// Similar to Span::subspan but in *reverse*:
85 ///@{
86 [[nodiscard]] constexpr Span<T, D> rsubspan(size_t i, size_t n = D) const noexcept {
87 return n != D ? subspan(size() - i - n, n) : subspan(0, size() - i);
88 }
89
90 /// `span.rsubspan(3, 5)` removes the last 3 elements and picks 5 elements starting before those.
91 template<size_t i, size_t n = D>
92 [[nodiscard]] constexpr Span<T, n != D ? n : (N != D ? N - i : D)> rsubspan() const noexcept {
93 if constexpr (n != D)
94 return Span<T, n>(data() + size() - i - n);
95 else if constexpr (N != D)
96 return Span<T, N - i>(data());
97 else
98 return Span<T, D>(data(), size() - i);
99 }
100 ///@}
101};
102
103static_assert(std::ranges::contiguous_range<Span<int>>);
104
105template<class T, size_t N = std::dynamic_extent>
107
108/// @name Deduction Guides
109///@{
110// clang-format off
112template<class T, size_t N> Span(T (&)[N]) -> Span< T, N>;
113template<class T, size_t N> Span( std::array<T, N>&) -> Span< T, N>;
114template<class T, size_t N> Span(const std::array<T, N>&) -> Span<const T, N>;
118// clang-format on
119///@}
120
121template<size_t I, class T, size_t N>
122requires(N != std::dynamic_extent) constexpr decltype(auto) get(Span<T, N> span) noexcept {
123 static_assert(I < N, "index I out of bound N");
124 return span[I];
125}
126
127} // namespace fe
128
129namespace std {
130/// @name Structured Binding Support for Span
131///@{
132template<class T, size_t N>
133requires(N != std::dynamic_extent) struct tuple_size<fe::Span<T, N>> : std::integral_constant<size_t, N> {};
134
135template<size_t I, class T, size_t N>
136requires(N != std::dynamic_extent) struct tuple_element<I, fe::Span<T, N>> {
138};
139///@}
140} // namespace std
This is a thin wrapper for std::span<T, N> with the following additional features:
Definition span.h:33
constexpr Span(typename Base::pointer p) noexcept
Definition span.h:57
constexpr Span< T, n > span() const noexcept
Definition span.h:78
constexpr Span< T, D > subspan(size_t i, size_t n=D) const noexcept
Definition span.h:69
std::span< T, N > Base
Definition span.h:35
constexpr Span(std::span< T, N > span) noexcept
Definition span.h:47
constexpr Span< T, n !=D ? n :(N !=D ? N - i :D)> rsubspan() const noexcept
span.rsubspan(3, 5) removes the last 3 elements and picks 5 elements starting before those.
Definition span.h:92
static constexpr auto D
Definition span.h:36
constexpr Span< T, D > rsubspan(size_t i, size_t n=D) const noexcept
Definition span.h:86
constexpr Span< T, n !=D ? n :(N !=D ? N - i :D)> subspan() const noexcept
Definition span.h:72
Something which behaves like std::vector or std::array.
Definition span.h:13
Definition algo.h:17
Span< const T, N > View
Definition span.h:106
constexpr decltype(auto) get(Span< T, N > span) noexcept
Definition span.h:122
Definition span.h:129
typename fe::Span< T, N >::reference type
Definition span.h:137