330 lines
9.8 KiB
C++
330 lines
9.8 KiB
C++
#pragma once
|
|
|
|
#include <array> // for std::array, etc.
|
|
#include <cassert> // for assert
|
|
#include <cstddef> // for std::size_t, etc.
|
|
#include <iterator> // for std::reverse_iterator, etc.
|
|
#include <type_traits> // for std::enable_if, etc.
|
|
|
|
namespace rublon {
|
|
|
|
#define CONSTRAINT(...) std::enable_if_t< (__VA_ARGS__), int > = 0
|
|
#define EXPECTS(...) assert((__VA_ARGS__))
|
|
|
|
// constants
|
|
|
|
// equivalent to std::numeric_limits<std::size_t>::max()
|
|
inline constexpr std::size_t dynamic_extent = -1;
|
|
|
|
// class template span
|
|
|
|
template < class T, std::size_t N = dynamic_extent >
|
|
class span;
|
|
|
|
namespace span_detail {
|
|
|
|
// detect specializations of span
|
|
|
|
template < class T >
|
|
struct is_span : std::false_type {};
|
|
|
|
template < class T, std::size_t N >
|
|
struct is_span< span< T, N > > : std::true_type {};
|
|
|
|
template < class T >
|
|
inline constexpr bool is_span_v = is_span< T >::value;
|
|
|
|
// detect specializations of std::array
|
|
|
|
template < class T >
|
|
struct is_array : std::false_type {};
|
|
|
|
template < class T, std::size_t N >
|
|
struct is_array< std::array< T, N > > : std::true_type {};
|
|
|
|
template < class T >
|
|
inline constexpr bool is_array_v = is_array< T >::value;
|
|
|
|
// ADL-aware data() and size()
|
|
|
|
template < class C >
|
|
constexpr decltype(auto) my_data(C & c) {
|
|
return std::data(c);
|
|
}
|
|
|
|
template < class C >
|
|
constexpr decltype(auto) my_size(C & c) {
|
|
return std::size(c);
|
|
}
|
|
|
|
// detect container
|
|
|
|
template < class C, class = void >
|
|
struct is_cont : std::false_type {};
|
|
|
|
template < class C >
|
|
struct is_cont< C,
|
|
std::void_t< std::enable_if_t< !is_span_v< C > >,
|
|
std::enable_if_t< !is_array_v< C > >,
|
|
std::enable_if_t< !std::is_array_v< C > >,
|
|
decltype(std::data(std::declval< C >())),
|
|
decltype(std::size(std::declval< C >())) > > : std::true_type {};
|
|
|
|
template < class C >
|
|
inline constexpr bool is_cont_v = is_cont< C >::value;
|
|
} // namespace span_detail
|
|
|
|
template < class T, std::size_t N >
|
|
class span {
|
|
public:
|
|
// constants and types
|
|
|
|
using element_type = T;
|
|
using value_type = std::remove_cv_t< T >;
|
|
using index_type = std::size_t;
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
using pointer = T *;
|
|
using const_pointer = const T *;
|
|
using reference = T &;
|
|
using const_reference = const T &;
|
|
|
|
using iterator = T *;
|
|
using const_iterator = const T *;
|
|
using reverse_iterator = std::reverse_iterator< iterator >;
|
|
using const_reverse_iterator = std::reverse_iterator< const_iterator >;
|
|
|
|
static constexpr index_type extent = N;
|
|
|
|
// constructors, copy, and assignment
|
|
|
|
// LWG 3198 applied
|
|
constexpr span() noexcept : size_{0}, data_{nullptr} {
|
|
static_assert(N == dynamic_extent || N == 0);
|
|
}
|
|
|
|
constexpr span(T * ptr, index_type n) : size_{n}, data_{ptr} {
|
|
EXPECTS(N == dynamic_extent || N == n);
|
|
}
|
|
|
|
constexpr span(T * first, T * last) : size_{last - first}, data_{first} {
|
|
EXPECTS(N == dynamic_extent || last - first = N);
|
|
}
|
|
|
|
template < std::size_t M,
|
|
CONSTRAINT(N == dynamic_extent ||
|
|
N == M &&
|
|
std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< T (&)[M] >())) > (*)[], T (*)[] >) >
|
|
constexpr span(T (&arr)[M]) noexcept : size_{M}, data_{arr} {}
|
|
|
|
template < std::size_t M,
|
|
CONSTRAINT(N == dynamic_extent ||
|
|
N == M &&
|
|
std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< T (&)[M] >())) > (*)[], T (*)[] >) >
|
|
constexpr span(std::array< value_type, M > & arr) noexcept : size_{M}, data_{arr.data()} {}
|
|
|
|
template < std::size_t M,
|
|
CONSTRAINT(N == dynamic_extent ||
|
|
N == M &&
|
|
std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< T (&)[M] >())) > (*)[], T (*)[] >) >
|
|
constexpr span(const std::array< value_type, M > & arr) noexcept : size_{M}, data_{arr.data()} {}
|
|
|
|
template < class Cont,
|
|
CONSTRAINT(N == dynamic_extent && span_detail::is_cont_v< Cont > &&
|
|
std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< Cont >())) > (*)[], T (*)[] >) >
|
|
constexpr span(Cont & c) : size_{span_detail::my_size(c)}, data_{span_detail::my_data(c)} {}
|
|
|
|
template < class Cont,
|
|
CONSTRAINT(N == dynamic_extent && span_detail::is_cont_v< Cont > &&
|
|
std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< Cont >())) > (*)[], T (*)[] >) >
|
|
constexpr span(const Cont & c) : size_{span_detail::my_size(c)}, data_{span_detail::my_data(c)} {}
|
|
|
|
constexpr span(const span & other) noexcept = default;
|
|
|
|
// template < class U, std::size_t M, CONSTRAINT(N == dynamic_extent || N == M && std::is_convertible_v< U (*)[], T (*)[] >) >
|
|
// constexpr span(const span< U, M > & s) noexcept : size_{s.size()}, data_{s.data()} {}
|
|
|
|
~span() noexcept = default;
|
|
|
|
constexpr span & operator=(const span & other) noexcept = default;
|
|
|
|
// subviews
|
|
|
|
template < std::size_t Cnt >
|
|
constexpr span< T, Cnt > first() const {
|
|
assert(Cnt <= size());
|
|
return {data(), Cnt};
|
|
}
|
|
|
|
template < std::size_t Cnt >
|
|
constexpr span< T, Cnt > last() const {
|
|
assert(Cnt <= size());
|
|
return {data() + (size() - Cnt), Cnt};
|
|
}
|
|
|
|
template < std::size_t Off, std::size_t Cnt = dynamic_extent >
|
|
constexpr auto subspan() const {
|
|
assert(Off <= size() && (Cnt == dynamic_extent || Off + Cnt <= size()));
|
|
if constexpr(Cnt != dynamic_extent)
|
|
return span< T, Cnt >{data() + Off, Cnt};
|
|
else if constexpr(N != dynamic_extent)
|
|
return span< T, N - Off >{data() + Off, size() - Off};
|
|
else
|
|
return span< T, dynamic_extent >{data() + Off, size() - Off};
|
|
}
|
|
|
|
constexpr span< T, dynamic_extent > first(index_type cnt) const {
|
|
assert(cnt <= size());
|
|
return {data(), cnt};
|
|
}
|
|
|
|
constexpr span< T, dynamic_extent > last(index_type cnt) const {
|
|
assert(cnt <= size());
|
|
return {data() + (size() - cnt), cnt};
|
|
}
|
|
|
|
constexpr span< T, dynamic_extent > subspan(index_type off, index_type cnt = dynamic_extent) const {
|
|
assert(off <= size() && (cnt == dynamic_extent || off + cnt <= size()));
|
|
return {data() + off, cnt == dynamic_extent ? size() - off : cnt};
|
|
}
|
|
|
|
// observers
|
|
|
|
constexpr index_type size() const noexcept {
|
|
return size_;
|
|
}
|
|
|
|
constexpr index_type size_bytes() const noexcept {
|
|
return size() * sizeof(T);
|
|
}
|
|
|
|
[[nodiscard]] constexpr bool empty() const noexcept {
|
|
return size() == 0;
|
|
}
|
|
|
|
// element access
|
|
|
|
constexpr reference operator[](index_type idx) const {
|
|
assert(idx < size());
|
|
return *(data() + idx);
|
|
}
|
|
|
|
constexpr reference front() const {
|
|
assert(!empty());
|
|
return *data();
|
|
}
|
|
|
|
constexpr reference back() const {
|
|
assert(!empty());
|
|
return *(data() + (size() - 1));
|
|
}
|
|
|
|
constexpr pointer data() const noexcept {
|
|
return data_;
|
|
}
|
|
|
|
// iterator support
|
|
|
|
constexpr iterator begin() const noexcept {
|
|
return data();
|
|
}
|
|
|
|
constexpr iterator end() const noexcept {
|
|
return data() + size();
|
|
}
|
|
|
|
constexpr const_iterator cbegin() const noexcept {
|
|
return data();
|
|
}
|
|
|
|
constexpr const_iterator cend() const noexcept {
|
|
return data() + size();
|
|
}
|
|
|
|
constexpr reverse_iterator rbegin() const noexcept {
|
|
return reverse_iterator{end()};
|
|
}
|
|
|
|
constexpr reverse_iterator rend() const noexcept {
|
|
return reverse_iterator{begin()};
|
|
}
|
|
|
|
constexpr const_reverse_iterator crbegin() const noexcept {
|
|
return reverse_iterator{cend()};
|
|
}
|
|
|
|
constexpr const_reverse_iterator crend() const noexcept {
|
|
return reverse_iterator{cbegin()};
|
|
}
|
|
|
|
friend constexpr iterator begin(span s) noexcept {
|
|
return s.begin();
|
|
}
|
|
|
|
friend constexpr iterator end(span s) noexcept {
|
|
return s.end();
|
|
}
|
|
|
|
private:
|
|
pointer data_;
|
|
index_type size_;
|
|
};
|
|
|
|
// deduction guide
|
|
|
|
template < class T, std::size_t N >
|
|
span(T (&)[N]) -> span< T, N >;
|
|
|
|
template < class T, std::size_t N >
|
|
span(std::array< T, N > &) -> span< T, N >;
|
|
|
|
template < class T, std::size_t N >
|
|
span(const std::array< T, N > &) -> span< const T, N >;
|
|
|
|
template < class Cont >
|
|
span(Cont &) -> span< typename Cont::value_type >;
|
|
|
|
template < class Cont >
|
|
span(const Cont &) -> span< const typename Cont::value_type >;
|
|
|
|
// views of objects representation
|
|
|
|
template < class T, std::size_t N >
|
|
auto as_bytes(span< T, N > s) noexcept -> span< const std::byte, N == dynamic_extent ? dynamic_extent : sizeof(T) * N > {
|
|
return {reinterpret_cast< const std::byte * >(s.data()), s.size_bytes()};
|
|
}
|
|
|
|
template < class T, std::size_t N, CONSTRAINT(!std::is_const_v< T >) >
|
|
auto as_writable_bytes(span< T, N > s) noexcept -> span< std::byte, N == dynamic_extent ? dynamic_extent : sizeof(T) * N > {
|
|
return {reinterpret_cast< std::byte * >(s.data()), s.size_bytes()};
|
|
}
|
|
} // namespace rublon
|
|
|
|
namespace std {
|
|
|
|
// tuple interface
|
|
// the primary template declarations are included in <array>
|
|
|
|
template < class T, std::size_t N >
|
|
struct tuple_size< rublon::span< T, N > > : std::integral_constant< std::size_t, N > {};
|
|
|
|
// not defined
|
|
template < class T >
|
|
struct tuple_size< rublon::span< T, rublon::dynamic_extent > >;
|
|
|
|
template < std::size_t I, class T, std::size_t N >
|
|
struct tuple_element< I, rublon::span< T, N > > {
|
|
static_assert(N != rublon::dynamic_extent && I < N);
|
|
using type = T;
|
|
};
|
|
|
|
template < std::size_t I, class T, std::size_t N >
|
|
constexpr T & get(rublon::span< T, N > s) noexcept {
|
|
static_assert(N != rublon::dynamic_extent && I < N);
|
|
return s[I];
|
|
}
|
|
} // namespace std
|
|
|
|
#undef CONSTRAINT
|
|
#undef EXPECTS
|