|
11 | 11 | #include <type_traits> |
12 | 12 | #include <utility> |
13 | 13 |
|
14 | | -#ifdef DEBUG |
15 | | -#define CONSTEXPR_IF_NOT_DEBUG |
16 | | -#define ASSERT_IF_DEBUG(x) assert((x)) |
17 | | -#else |
18 | | -#define CONSTEXPR_IF_NOT_DEBUG constexpr |
19 | | -#define ASSERT_IF_DEBUG(x) |
20 | | -#endif |
21 | | - |
22 | | -#if defined(__clang__) |
23 | | -#if __has_attribute(lifetimebound) |
24 | | -#define SPAN_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] |
25 | | -#else |
26 | | -#define SPAN_ATTR_LIFETIMEBOUND |
27 | | -#endif |
28 | | -#else |
29 | | -#define SPAN_ATTR_LIFETIMEBOUND |
30 | | -#endif |
31 | | - |
32 | 14 | /** A Span is an object that can refer to a contiguous sequence of objects. |
33 | | - * |
34 | | - * This file implements a subset of C++20's std::span. It can be considered |
35 | | - * temporary compatibility code until C++20 and is designed to be a |
36 | | - * self-contained abstraction without depending on other project files. For this |
37 | | - * reason, Clang lifetimebound is defined here instead of including |
38 | | - * <attributes.h>, which also defines it. |
39 | 15 | * |
40 | 16 | * Things to be aware of when writing code that deals with Spans: |
41 | 17 | * |
|
93 | 69 | * result will be present in that variable after the call. Passing a temporary |
94 | 70 | * is useless in that context. |
95 | 71 | */ |
96 | | -template<typename C> |
97 | | -class Span |
98 | | -{ |
99 | | - C* m_data; |
100 | | - std::size_t m_size{0}; |
101 | | - |
102 | | - template <class T> |
103 | | - struct is_Span_int : public std::false_type {}; |
104 | | - template <class T> |
105 | | - struct is_Span_int<Span<T>> : public std::true_type {}; |
106 | | - template <class T> |
107 | | - struct is_Span : public is_Span_int<typename std::remove_cv<T>::type>{}; |
108 | | - |
109 | | - |
110 | | -public: |
111 | | - constexpr Span() noexcept : m_data(nullptr) {} |
112 | | - |
113 | | - /** Construct a span from a begin pointer and a size. |
114 | | - * |
115 | | - * This implements a subset of the iterator-based std::span constructor in C++20, |
116 | | - * which is hard to implement without std::address_of. |
117 | | - */ |
118 | | - template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0> |
119 | | - constexpr Span(T* begin, std::size_t size) noexcept : m_data(begin), m_size(size) {} |
120 | | - |
121 | | - /** Construct a span from a begin and end pointer. |
122 | | - * |
123 | | - * This implements a subset of the iterator-based std::span constructor in C++20, |
124 | | - * which is hard to implement without std::address_of. |
125 | | - */ |
126 | | - template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0> |
127 | | - CONSTEXPR_IF_NOT_DEBUG Span(T* begin, T* end) noexcept : m_data(begin), m_size(end - begin) |
128 | | - { |
129 | | - ASSERT_IF_DEBUG(end >= begin); |
130 | | - } |
131 | | - |
132 | | - /** Implicit conversion of spans between compatible types. |
133 | | - * |
134 | | - * Specifically, if a pointer to an array of type O can be implicitly converted to a pointer to an array of type |
135 | | - * C, then permit implicit conversion of Span<O> to Span<C>. This matches the behavior of the corresponding |
136 | | - * C++20 std::span constructor. |
137 | | - * |
138 | | - * For example this means that a Span<T> can be converted into a Span<const T>. |
139 | | - */ |
140 | | - template <typename O, typename std::enable_if<std::is_convertible<O (*)[], C (*)[]>::value, int>::type = 0> |
141 | | - constexpr Span(const Span<O>& other) noexcept : m_data(other.m_data), m_size(other.m_size) {} |
142 | | - |
143 | | - /** Default copy constructor. */ |
144 | | - constexpr Span(const Span&) noexcept = default; |
145 | | - |
146 | | - /** Default assignment operator. */ |
147 | | - Span& operator=(const Span& other) noexcept = default; |
148 | | - |
149 | | - /** Construct a Span from an array. This matches the corresponding C++20 std::span constructor. */ |
150 | | - template <int N> |
151 | | - constexpr Span(C (&a)[N]) noexcept : m_data(a), m_size(N) {} |
152 | | - |
153 | | - /** Construct a Span for objects with .data() and .size() (std::string, std::array, std::vector, ...). |
154 | | - * |
155 | | - * This implements a subset of the functionality provided by the C++20 std::span range-based constructor. |
156 | | - * |
157 | | - * To prevent surprises, only Spans for constant value types are supported when passing in temporaries. |
158 | | - * Note that this restriction does not exist when converting arrays or other Spans (see above). |
159 | | - */ |
160 | | - template <typename V> |
161 | | - constexpr Span(V& other SPAN_ATTR_LIFETIMEBOUND, |
162 | | - typename std::enable_if<!is_Span<V>::value && |
163 | | - std::is_convertible<typename std::remove_pointer<decltype(std::declval<V&>().data())>::type (*)[], C (*)[]>::value && |
164 | | - std::is_convertible<decltype(std::declval<V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr) |
165 | | - : m_data(other.data()), m_size(other.size()){} |
166 | | - |
167 | | - template <typename V> |
168 | | - constexpr Span(const V& other SPAN_ATTR_LIFETIMEBOUND, |
169 | | - typename std::enable_if<!is_Span<V>::value && |
170 | | - std::is_convertible<typename std::remove_pointer<decltype(std::declval<const V&>().data())>::type (*)[], C (*)[]>::value && |
171 | | - std::is_convertible<decltype(std::declval<const V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr) |
172 | | - : m_data(other.data()), m_size(other.size()){} |
173 | | - |
174 | | - constexpr C* data() const noexcept { return m_data; } |
175 | | - constexpr C* begin() const noexcept { return m_data; } |
176 | | - constexpr C* end() const noexcept { return m_data + m_size; } |
177 | | - CONSTEXPR_IF_NOT_DEBUG C& front() const noexcept |
178 | | - { |
179 | | - ASSERT_IF_DEBUG(size() > 0); |
180 | | - return m_data[0]; |
181 | | - } |
182 | | - CONSTEXPR_IF_NOT_DEBUG C& back() const noexcept |
183 | | - { |
184 | | - ASSERT_IF_DEBUG(size() > 0); |
185 | | - return m_data[m_size - 1]; |
186 | | - } |
187 | | - constexpr std::size_t size() const noexcept { return m_size; } |
188 | | - constexpr std::size_t size_bytes() const noexcept { return sizeof(C) * m_size; } |
189 | | - constexpr bool empty() const noexcept { return size() == 0; } |
190 | | - CONSTEXPR_IF_NOT_DEBUG C& operator[](std::size_t pos) const noexcept |
191 | | - { |
192 | | - ASSERT_IF_DEBUG(size() > pos); |
193 | | - return m_data[pos]; |
194 | | - } |
195 | | - CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset) const noexcept |
196 | | - { |
197 | | - ASSERT_IF_DEBUG(size() >= offset); |
198 | | - return Span<C>(m_data + offset, m_size - offset); |
199 | | - } |
200 | | - CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset, std::size_t count) const noexcept |
201 | | - { |
202 | | - ASSERT_IF_DEBUG(size() >= offset + count); |
203 | | - return Span<C>(m_data + offset, count); |
204 | | - } |
205 | | - CONSTEXPR_IF_NOT_DEBUG Span<C> first(std::size_t count) const noexcept |
206 | | - { |
207 | | - ASSERT_IF_DEBUG(size() >= count); |
208 | | - return Span<C>(m_data, count); |
209 | | - } |
210 | | - CONSTEXPR_IF_NOT_DEBUG Span<C> last(std::size_t count) const noexcept |
211 | | - { |
212 | | - ASSERT_IF_DEBUG(size() >= count); |
213 | | - return Span<C>(m_data + m_size - count, count); |
214 | | - } |
215 | | - |
216 | | - template <typename O> friend class Span; |
217 | | -}; |
218 | | - |
219 | | -// Return result of calling .data() method on type T. This is used to be able to |
220 | | -// write template deduction guides for the single-parameter Span constructor |
221 | | -// below that will work if the value that is passed has a .data() method, and if |
222 | | -// the data method does not return a void pointer. |
223 | | -// |
224 | | -// It is important to check for the void type specifically below, so the |
225 | | -// deduction guides can be used in SFINAE contexts to check whether objects can |
226 | | -// be converted to spans. If the deduction guides did not explicitly check for |
227 | | -// void, and an object was passed that returned void* from data (like |
228 | | -// std::vector<bool>), the template deduction would succeed, but the Span<void> |
229 | | -// object instantiation would fail, resulting in a hard error, rather than a |
230 | | -// SFINAE error. |
231 | | -// https://stackoverflow.com/questions/68759148/sfinae-to-detect-the-explicitness-of-a-ctad-deduction-guide |
232 | | -// https://stackoverflow.com/questions/16568986/what-happens-when-you-call-data-on-a-stdvectorbool |
233 | | -template<typename T> |
234 | | -using DataResult = std::remove_pointer_t<decltype(std::declval<T&>().data())>; |
235 | | - |
236 | | -// Deduction guides for Span |
237 | | -// For the pointer/size based and iterator based constructor: |
238 | | -template <typename T, typename EndOrSize> Span(T*, EndOrSize) -> Span<T>; |
239 | | -// For the array constructor: |
240 | | -template <typename T, std::size_t N> Span(T (&)[N]) -> Span<T>; |
241 | | -// For the temporaries/rvalue references constructor, only supporting const output. |
242 | | -template <typename T> Span(T&&) -> Span<std::enable_if_t<!std::is_lvalue_reference_v<T> && !std::is_void_v<DataResult<T&&>>, const DataResult<T&&>>>; |
243 | | -// For (lvalue) references, supporting mutable output. |
244 | | -template <typename T> Span(T&) -> Span<std::enable_if_t<!std::is_void_v<DataResult<T&>>, DataResult<T&>>>; |
| 72 | +#define Span std::span |
245 | 73 |
|
246 | 74 | /** Pop the last element off a span, and return a reference to that element. */ |
247 | 75 | template <typename T> |
|
0 commit comments