10
10
#include < algorithm>
11
11
#include < assert.h>
12
12
13
+ #ifdef DEBUG
14
+ #define CONSTEXPR_IF_NOT_DEBUG
15
+ #define ASSERT_IF_DEBUG (x ) assert((x))
16
+ #else
17
+ #define CONSTEXPR_IF_NOT_DEBUG constexpr
18
+ #define ASSERT_IF_DEBUG (x )
19
+ #endif
20
+
13
21
/* * A Span is an object that can refer to a contiguous sequence of objects.
14
22
*
15
23
* It implements a subset of C++20's std::span.
@@ -18,12 +26,29 @@ template<typename C>
18
26
class Span
19
27
{
20
28
C* m_data;
21
- std::ptrdiff_t m_size;
29
+ std::size_t m_size;
22
30
23
31
public:
24
32
constexpr Span () noexcept : m_data(nullptr ), m_size(0 ) {}
25
- constexpr Span (C* data, std::ptrdiff_t size) noexcept : m_data(data), m_size(size) {}
26
- constexpr Span (C* data, C* end) noexcept : m_data(data), m_size(end - data) {}
33
+
34
+ /* * Construct a span from a begin pointer and a size.
35
+ *
36
+ * This implements a subset of the iterator-based std::span constructor in C++20,
37
+ * which is hard to implement without std::address_of.
38
+ */
39
+ template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int >::type = 0 >
40
+ constexpr Span (T* begin, std::size_t size) noexcept : m_data(begin), m_size(size) {}
41
+
42
+ /* * Construct a span from a begin and end pointer.
43
+ *
44
+ * This implements a subset of the iterator-based std::span constructor in C++20,
45
+ * which is hard to implement without std::address_of.
46
+ */
47
+ template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int >::type = 0 >
48
+ CONSTEXPR_IF_NOT_DEBUG Span (T* begin, T* end) noexcept : m_data(begin), m_size(end - begin)
49
+ {
50
+ ASSERT_IF_DEBUG (end >= begin);
51
+ }
27
52
28
53
/* * Implicit conversion of spans between compatible types.
29
54
*
@@ -42,18 +67,59 @@ class Span
42
67
/* * Default assignment operator. */
43
68
Span& operator =(const Span& other) noexcept = default ;
44
69
70
+ /* * Construct a Span from an array. This matches the corresponding C++20 std::span constructor. */
71
+ template <int N>
72
+ constexpr Span (C (&a)[N]) noexcept : m_data(a), m_size(N) {}
73
+
74
+ /* * Construct a Span for objects with .data() and .size() (std::string, std::array, std::vector, ...).
75
+ *
76
+ * This implements a subset of the functionality provided by the C++20 std::span range-based constructor.
77
+ *
78
+ * To prevent surprises, only Spans for constant value types are supported when passing in temporaries.
79
+ * Note that this restriction does not exist when converting arrays or other Spans (see above).
80
+ */
81
+ template <typename V, typename std::enable_if<(std::is_const<C>::value || std::is_lvalue_reference<V>::value) && std::is_convertible<typename std::remove_pointer<decltype (std::declval<V&>().data())>::type (*)[], C (*)[]>::value && std::is_convertible<decltype (std::declval<V&>().size()), std::size_t >::value, int >::type = 0 >
82
+ constexpr Span (V&& v) noexcept : m_data(v.data()), m_size(v.size()) {}
83
+
45
84
constexpr C* data () const noexcept { return m_data; }
46
85
constexpr C* begin () const noexcept { return m_data; }
47
86
constexpr C* end () const noexcept { return m_data + m_size; }
48
- constexpr C& front () const noexcept { return m_data[0 ]; }
49
- constexpr C& back () const noexcept { return m_data[m_size - 1 ]; }
50
- constexpr std::ptrdiff_t size () const noexcept { return m_size; }
51
- constexpr C& operator [](std::ptrdiff_t pos) const noexcept { return m_data[pos]; }
52
-
53
- constexpr Span<C> subspan (std::ptrdiff_t offset) const noexcept { return Span<C>(m_data + offset, m_size - offset); }
54
- constexpr Span<C> subspan (std::ptrdiff_t offset, std::ptrdiff_t count) const noexcept { return Span<C>(m_data + offset, count); }
55
- constexpr Span<C> first (std::ptrdiff_t count) const noexcept { return Span<C>(m_data, count); }
56
- constexpr Span<C> last (std::ptrdiff_t count) const noexcept { return Span<C>(m_data + m_size - count, count); }
87
+ CONSTEXPR_IF_NOT_DEBUG C& front () const noexcept
88
+ {
89
+ ASSERT_IF_DEBUG (size () > 0 );
90
+ return m_data[0 ];
91
+ }
92
+ CONSTEXPR_IF_NOT_DEBUG C& back () const noexcept
93
+ {
94
+ ASSERT_IF_DEBUG (size () > 0 );
95
+ return m_data[m_size - 1 ];
96
+ }
97
+ constexpr std::size_t size () const noexcept { return m_size; }
98
+ CONSTEXPR_IF_NOT_DEBUG C& operator [](std::size_t pos) const noexcept
99
+ {
100
+ ASSERT_IF_DEBUG (size () > pos);
101
+ return m_data[pos];
102
+ }
103
+ CONSTEXPR_IF_NOT_DEBUG Span<C> subspan (std::size_t offset) const noexcept
104
+ {
105
+ ASSERT_IF_DEBUG (size () >= offset);
106
+ return Span<C>(m_data + offset, m_size - offset);
107
+ }
108
+ CONSTEXPR_IF_NOT_DEBUG Span<C> subspan (std::size_t offset, std::size_t count) const noexcept
109
+ {
110
+ ASSERT_IF_DEBUG (size () >= offset + count);
111
+ return Span<C>(m_data + offset, count);
112
+ }
113
+ CONSTEXPR_IF_NOT_DEBUG Span<C> first (std::size_t count) const noexcept
114
+ {
115
+ ASSERT_IF_DEBUG (size () >= count);
116
+ return Span<C>(m_data, count);
117
+ }
118
+ CONSTEXPR_IF_NOT_DEBUG Span<C> last (std::size_t count) const noexcept
119
+ {
120
+ ASSERT_IF_DEBUG (size () >= count);
121
+ return Span<C>(m_data + m_size - count, count);
122
+ }
57
123
58
124
friend constexpr bool operator ==(const Span& a, const Span& b) noexcept { return a.size () == b.size () && std::equal (a.begin (), a.end (), b.begin ()); }
59
125
friend constexpr bool operator !=(const Span& a, const Span& b) noexcept { return !(a == b); }
@@ -65,26 +131,20 @@ class Span
65
131
template <typename O> friend class Span ;
66
132
};
67
133
68
- /* * Create a span to a container exposing data() and size().
69
- *
70
- * This correctly deals with constness: the returned Span's element type will be
71
- * whatever data() returns a pointer to. If either the passed container is const,
72
- * or its element type is const, the resulting span will have a const element type.
73
- *
74
- * std::span will have a constructor that implements this functionality directly.
75
- */
76
- template <typename A, int N>
77
- constexpr Span<A> MakeSpan (A (&a)[N]) { return Span<A>(a, N); }
78
-
79
- template <typename V>
80
- constexpr Span<typename std::remove_pointer<decltype (std::declval<V>().data())>::type> MakeSpan (V& v) { return Span<typename std::remove_pointer<decltype (std::declval<V>().data ())>::type>(v.data (), v.size ()); }
134
+ // MakeSpan helps constructing a Span of the right type automatically.
135
+ /* * MakeSpan for arrays: */
136
+ template <typename A, int N> Span<A> constexpr MakeSpan (A (&a)[N]) { return Span<A>(a, N); }
137
+ /* * MakeSpan for temporaries / rvalue references, only supporting const output. */
138
+ template <typename V> constexpr auto MakeSpan (V&& v) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); }
139
+ /* * MakeSpan for (lvalue) references, supporting mutable output. */
140
+ template <typename V> constexpr auto MakeSpan (V& v) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; }
81
141
82
142
/* * Pop the last element off a span, and return a reference to that element. */
83
143
template <typename T>
84
144
T& SpanPopBack (Span<T>& span)
85
145
{
86
146
size_t size = span.size ();
87
- assert (size > 0 );
147
+ ASSERT_IF_DEBUG (size > 0 );
88
148
T& back = span[size - 1 ];
89
149
span = Span<T>(span.data (), size - 1 );
90
150
return back;
0 commit comments