userver: userver/utils/span.hpp Source File
Loading...
Searching...
No Matches
span.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/span.hpp
4/// @brief @copybrief utils::span
5
6#include <iterator>
7
8#if __has_include(<span>)
9#include <span>
10#endif
11
12#if __cpp_lib_span >= 202002L
13#include <boost/version.hpp>
14#else
15#include <cstddef>
16#include <type_traits>
17
18#include <userver/utils/assert.hpp>
19#endif // __cpp_lib_span >= 202002L
20
21USERVER_NAMESPACE_BEGIN
22
23namespace utils {
24
25#if __cpp_lib_span >= 202002L || defined(DOXYGEN)
26
27/// A polyfill for std::span from C++20
28using std::span;
29
30/// A polyfill for std::as_bytes from C++20
31using std::as_bytes;
32
33/// A polyfill for std::as_writable_bytes from C++20
34using std::as_writable_bytes;
35
36#else
37
38/// A polyfill for std::span from C++20
39template <typename T>
40class span final {
41 public:
42 using iterator = T*;
43
44 constexpr span() noexcept : span(nullptr, nullptr) {}
45
46 constexpr span(T* begin, T* end) noexcept : begin_(begin), end_(end) {
47 UASSERT((begin != nullptr && end != nullptr && begin <= end) ||
48 (begin == nullptr && end == nullptr));
49 }
50
51 template <
52 typename Container,
53 typename = std::enable_if_t<
54 // Container is a range
55 // Either Container is lvalue, or elements are const
56 (std::is_lvalue_reference_v<Container> || std::is_const_v<T>)&&
57 // Copy and move constructor fix
58 !std::is_same_v<std::remove_cv_t<std::remove_reference_t<Container>>,
59 span>>>
60 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
61 constexpr /*implicit*/ span(Container&& cont) noexcept
62 : span(std::data(cont), std::data(cont) + std::size(cont)) {}
63
64 constexpr T* begin() const noexcept { return begin_; }
65 constexpr T* end() const noexcept { return end_; }
66
67 constexpr T* data() const noexcept { return begin_; }
68 constexpr std::size_t size() const noexcept { return end_ - begin_; }
69 constexpr bool empty() const noexcept { return size() == 0; }
70
71 constexpr span<T> first(std::size_t count) const noexcept {
72 UASSERT(count <= size());
73 return span{begin_, begin_ + count};
74 }
75
76 constexpr span<T> last(std::size_t count) const noexcept {
77 UASSERT(count <= size());
78 return span{end_ - count, end_};
79 }
80
81 constexpr span<T> subspan(std::size_t offset) const noexcept {
82 UASSERT(offset <= size());
83 return span{begin_ + offset, end_};
84 }
85
86 constexpr span<T> subspan(std::size_t offset, std::size_t count) const
87 noexcept {
88 UASSERT(offset + count <= size());
89 return span{begin_ + offset, begin_ + offset + count};
90 }
91
92 constexpr T& operator[](std::size_t index) const noexcept {
93 UASSERT(index < size());
94 return begin_[index];
95 }
96
97 private:
98 T* begin_;
99 T* end_;
100};
101
102template <typename Container>
103span(Container&& cont)
104 -> span<std::remove_reference_t<decltype(*std::begin(cont))>>;
105
106/// A polyfill for std::as_bytes from C++20
107template <typename T>
108span<const std::byte> as_bytes(span<T> s) noexcept {
109 const auto* const data = reinterpret_cast<const std::byte*>(s.data());
110 return {data, data + s.size() * sizeof(T)};
111}
112
113/// A polyfill for std::as_writable_bytes from C++20
114template <typename T>
115span<std::byte> as_writable_bytes(span<T> s) noexcept {
116 static_assert(!std::is_const_v<T>);
117 auto* const data = reinterpret_cast<std::byte*>(s.data());
118 return {data, data + s.size() * sizeof(T)};
119}
120
121#endif
122
123} // namespace utils
124
125USERVER_NAMESPACE_END
126
127/// @cond
128
129// Boost requires ranges to have a nested constant_iterator alias,
130// but utils::span does not have one.
131namespace boost {
132
133template <typename T, typename Enabler>
134struct range_const_iterator;
135
136#if __cpp_lib_span >= 202002L
137
138template <typename T, std::size_t Extent>
139struct range_const_iterator<std::span<T, Extent>, void> {
140 using type = typename std::span<T, Extent>::iterator;
141};
142
143#else
144
145template <typename T>
146struct range_const_iterator<USERVER_NAMESPACE::utils::span<T>, void> {
147 using type = typename USERVER_NAMESPACE::utils::span<T>::iterator;
148};
149
150#endif
151
152} // namespace boost
153
154// Boost < 1.77 does not mark its iterators as contiguous,
155// which makes it impossible to convert Boost containers to std::span.
156// Specialize std::iterator_traits as a workaround.
157#if __cpp_lib_span >= 202002L && BOOST_VERSION < 107700
158
159namespace boost::container {
160
161template <class Pointer, bool IsConst>
162class vec_iterator;
163
164} // namespace boost::container
165
166template <class Pointer, bool IsConst>
167// NOLINTNEXTLINE(cert-dcl58-cpp)
168struct std::iterator_traits<boost::container::vec_iterator<Pointer, IsConst>> {
169 using iterator_concept = std::contiguous_iterator_tag;
170 using iterator_category = std::random_access_iterator_tag;
171 using value_type =
172 typename boost::container::vec_iterator<Pointer, IsConst>::value_type;
173 using difference_type =
174 typename boost::container::vec_iterator<Pointer,
175 IsConst>::difference_type;
176 using pointer =
177 typename boost::container::vec_iterator<Pointer, IsConst>::pointer;
178 using reference =
179 typename boost::container::vec_iterator<Pointer, IsConst>::reference;
180};
181
182#endif
183
184/// @endcond