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 <cstddef>
7#include <iterator>
8#include <type_traits>
9
10#include <userver/utils/assert.hpp>
11
12USERVER_NAMESPACE_BEGIN
13
14namespace utils {
15
16namespace impl {
17
18template <typename T>
19struct TypeIdentityImpl final {
20 using type = T;
21};
22
23template <typename T>
24using TypeIdentity = typename TypeIdentityImpl<T>::type;
25
26} // namespace impl
27
28/// A polyfill for std::span from C++20
29template <typename T>
30class span final { // NOLINT(readability-identifier-naming)
31public:
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
118private:
119 T* begin_;
120 T* end_;
121};
122
123template <typename Container>
124span(Container&& cont) -> span<std::remove_reference_t<decltype(*std::begin(cont))>>;
125
126/// A polyfill for std::as_bytes from C++20
127template <typename T>
128span<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)};
131}
132
133/// A polyfill for std::as_writable_bytes from C++20
134template <typename T, typename = std::enable_if_t<!std::is_const_v<T>>>
135span<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)};
138}
139
140} // namespace utils
141
142USERVER_NAMESPACE_END
143
144/// @cond
145
146// Boost requires ranges to have a nested constant_iterator alias,
147// but utils::span does not have one.
148namespace boost {
149
150template <typename T, typename Enabler>
151struct range_const_iterator;
152
153template <typename T>
154struct range_const_iterator<USERVER_NAMESPACE::utils::span<T>, void> {
155 using type = typename USERVER_NAMESPACE::utils::span<T>::iterator;
156};
157
158} // namespace boost
159
160/// @endcond