|
3 | 3 | /// @file userver/utils/span.hpp |
4 | 4 | /// @brief @copybrief utils::span |
5 | 5 |
|
| 6 | +#include <concepts> |
6 | 7 | #include <cstddef> |
7 | 8 | #include <iterator> |
| 9 | +#include <span> |
8 | 10 | #include <type_traits> |
9 | 11 |
|
| 12 | +// TODO remove extra include |
10 | 13 | #include <userver/utils/assert.hpp> |
11 | 14 |
|
12 | 15 | USERVER_NAMESPACE_BEGIN |
13 | 16 |
|
14 | 17 | namespace utils { |
15 | 18 |
|
16 | | -namespace impl { |
17 | | - |
| 19 | +/// A polyfill for std::span with some of the newer features enabled. |
18 | 20 | template <typename T> |
19 | | -struct TypeIdentityImpl final { |
20 | | - using type = T; |
21 | | -}; |
| 21 | +class span : public std::span<T> { // NOLINT(readability-identifier-naming) |
| 22 | +public: |
| 23 | + // std::span gains this alias only in C++23. |
| 24 | + using const_iterator = typename std::span<T>::iterator; |
22 | 25 |
|
23 | | -template <typename T> |
24 | | -using TypeIdentity = typename TypeIdentityImpl<T>::type; |
| 26 | + using std::span<T>::span; |
25 | 27 |
|
26 | | -} // namespace impl |
| 28 | + constexpr explicit(false) span(std::span<T> s) noexcept : std::span<T>(s) {} |
27 | 29 |
|
28 | | -/// A polyfill for std::span from C++20 |
29 | | -template <typename T> |
30 | | -class span final { // NOLINT(readability-identifier-naming) |
31 | | -public: |
32 | | - using iterator = T*; |
33 | | - using value_type = std::remove_cv_t<T>; |
34 | | - |
35 | | - constexpr span() noexcept : span(nullptr, nullptr) {} |
36 | | - |
37 | | - constexpr span(T* begin, T* end) noexcept : begin_(begin), end_(end) { |
38 | | - // GCC 11.4.0 has issues with comparing pointers at compile time. |
39 | | - UASSERT( |
40 | | - std::is_constant_evaluated() || (begin != nullptr && end != nullptr && begin <= end) || |
41 | | - (begin == nullptr && end == nullptr) |
42 | | - ); |
43 | | - } |
44 | | - |
45 | | - constexpr span(T* begin, std::size_t size) noexcept : begin_(begin), end_(begin + size) { |
46 | | - // GCC 11.4.0 has issues with comparing pointers at compile time. |
47 | | - UASSERT(std::is_constant_evaluated() || begin != nullptr || size == 0); |
48 | | - } |
49 | | - |
50 | | -#if defined(__GNUC__) && !defined(__clang__) |
51 | | -#if __GNUC__ >= 9 |
52 | | -#pragma GCC diagnostic push |
53 | | -#pragma GCC diagnostic ignored "-Winit-list-lifetime" |
54 | | -#endif |
55 | | -#endif |
56 | | - template <typename Void = void, typename = std::enable_if_t<std::is_const_v<T> && std::is_void_v<Void>>> |
57 | | - constexpr /*implicit*/ span(std::initializer_list<value_type> il) |
58 | | - : begin_(il.begin()), |
59 | | - end_(il.end()) |
60 | | - {} |
61 | | -#if defined(__GNUC__) && !defined(__clang__) |
62 | | -#if __GNUC__ >= 9 |
63 | | -#pragma GCC diagnostic pop |
64 | | -#endif |
65 | | -#endif |
66 | | - |
67 | | - template < |
68 | | - typename Container, |
69 | | - typename = std::enable_if_t< |
70 | | - // Either Container is lvalue, or this span's elements are const |
71 | | - (std::is_lvalue_reference_v<Container> || std::is_const_v<T>)&& |
72 | | - // Copy and move constructor fix |
73 | | - !std::is_same_v<std::remove_cv_t<std::remove_reference_t<Container>>, span> && |
74 | | - // Container is a range of T |
75 | | - std::is_convertible_v<decltype(std::data(std::declval<Container&>())), T*>>> |
76 | | - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) |
77 | | - constexpr /*implicit*/ span(Container&& cont) noexcept : span(std::data(cont), std::size(cont)) {} |
78 | | - |
79 | | - template <std::size_t Size> |
80 | | - constexpr /*implicit*/ span(impl::TypeIdentity<T> (&array)[Size]) noexcept |
81 | | - : span(std::data(array), std::size(array)) {} |
82 | | - |
83 | | - constexpr T* begin() const noexcept { return begin_; } |
84 | | - constexpr T* end() const noexcept { return end_; } |
85 | | - |
86 | | - constexpr T* data() const noexcept { return begin_; } |
87 | | - constexpr std::size_t size() const noexcept { return end_ - begin_; } |
88 | | - constexpr bool empty() const noexcept { return size() == 0; } |
89 | | - |
90 | | - constexpr span<T> first(std::size_t count) const noexcept { |
91 | | - UASSERT(count <= size()); |
92 | | - return span{begin_, begin_ + count}; |
93 | | - } |
94 | | - |
95 | | - constexpr span<T> last(std::size_t count) const noexcept { |
96 | | - UASSERT(count <= size()); |
97 | | - return span{end_ - count, end_}; |
98 | | - } |
99 | | - |
100 | | - constexpr span<T> subspan(std::size_t offset) const noexcept { |
101 | | - UASSERT(offset <= size()); |
102 | | - return span{begin_ + offset, end_}; |
103 | | - } |
104 | | - |
105 | | - constexpr span<T> subspan( |
106 | | - std::size_t offset, // |
107 | | - std::size_t count |
108 | | - ) const noexcept { |
109 | | - UASSERT(offset + count <= size()); |
110 | | - return span{begin_ + offset, begin_ + offset + count}; |
111 | | - } |
112 | | - |
113 | | - constexpr T& operator[](std::size_t index) const noexcept { |
114 | | - UASSERT(index < size()); |
115 | | - return begin_[index]; |
116 | | - } |
117 | | - |
118 | | -private: |
119 | | - T* begin_; |
120 | | - T* end_; |
| 30 | + // Allows converting utils::span<U> to utils::span<T>, following std::span. |
| 31 | + template <typename U> |
| 32 | + requires std::is_convertible_v<U (*)[], T (*)[]> |
| 33 | + constexpr explicit(false) span(span<U> other) noexcept : std::span<T>(other.data(), other.size()) {} |
| 34 | + |
| 35 | + // std::span will only gain this constructor in C++29 or later. |
| 36 | + constexpr explicit(false) span(std::initializer_list<std::remove_cv_t<T>> il) noexcept |
| 37 | + requires std::is_const_v<T> && (!std::same_as<std::decay_t<T>, bool>) |
| 38 | + : std::span<T>(il.begin(), il.end()) {} |
121 | 39 | }; |
122 | 40 |
|
123 | | -template <typename Container> |
124 | | -span(Container&& cont) -> span<std::remove_reference_t<decltype(*std::begin(cont))>>; |
| 41 | +template <typename It, typename EndOrSize> |
| 42 | +span(It, EndOrSize) -> span<std::remove_reference_t<decltype(*std::declval<It&>())>>; |
| 43 | + |
| 44 | +template <typename R> |
| 45 | +requires std::ranges::contiguous_range<R> |
| 46 | +span(R&&) -> span<std::remove_reference_t<decltype(*std::begin(std::declval<R&>()))>>; |
125 | 47 |
|
126 | | -/// A polyfill for std::as_bytes from C++20 |
| 48 | +/// Reinterprets the elements of a span as bytes. |
127 | 49 | template <typename T> |
128 | 50 | span<const std::byte> as_bytes(span<T> s) noexcept { // NOLINT(readability-identifier-naming) |
129 | | - const auto* const data = reinterpret_cast<const std::byte*>(s.data()); |
130 | | - return {data, data + s.size() * sizeof(T)}; |
| 51 | + return span<const std::byte>{std::as_bytes(std::span<T>{s})}; |
131 | 52 | } |
132 | 53 |
|
133 | | -/// A polyfill for std::as_writable_bytes from C++20 |
134 | | -template <typename T, typename = std::enable_if_t<!std::is_const_v<T>>> |
| 54 | +/// Reinterprets the elements of a span as writable bytes. |
| 55 | +template <typename T> |
| 56 | +requires(!std::is_const_v<T>) |
135 | 57 | span<std::byte> as_writable_bytes(span<T> s) noexcept { // NOLINT(readability-identifier-naming) |
136 | | - auto* const data = reinterpret_cast<std::byte*>(s.data()); |
137 | | - return {data, data + s.size() * sizeof(T)}; |
| 58 | + return span<std::byte>{std::as_writable_bytes(std::span<T>{s})}; |
138 | 59 | } |
139 | 60 |
|
140 | 61 | } // namespace utils |
141 | 62 |
|
142 | 63 | USERVER_NAMESPACE_END |
143 | 64 |
|
144 | | -/// @cond |
145 | | - |
146 | | -// Boost requires ranges to have a nested constant_iterator alias, |
147 | | -// but utils::span does not have one. |
148 | | -namespace boost { |
149 | | - |
150 | | -template <typename T, typename Enabler> |
151 | | -struct range_const_iterator; |
152 | | - |
| 65 | +// std::span must implement std::ranges::enable_borrowed_range, so <span> will pull it in. |
153 | 66 | template <typename T> |
154 | | -struct range_const_iterator<USERVER_NAMESPACE::utils::span<T>, void> { |
155 | | - using type = typename USERVER_NAMESPACE::utils::span<T>::iterator; |
156 | | -}; |
157 | | - |
158 | | -} // namespace boost |
| 67 | +inline constexpr bool std::ranges::enable_borrowed_range<USERVER_NAMESPACE::utils::span<T>> = true; |
159 | 68 |
|
160 | | -/// @endcond |
| 69 | +// std::span must implement std::ranges::enable_view, so <span> will pull it in. |
| 70 | +template <typename T> |
| 71 | +inline constexpr bool std::ranges::enable_view<USERVER_NAMESPACE::utils::span<T>> = true; |
0 commit comments