|
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