userver: userver/utils/span.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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