Skip to content

Commit 6fd3330

Browse files
committed
span, including tests
Note that we have an interesting extension in `span` to support volatile types - this breaks `std::const_iterator`. I've made the choice to, when `span`'s element type is `volatile`, set `const_iterator` and `const_reverse_iterator` to `void`, and disable `cbegin` and friends.
1 parent b43e41f commit 6fd3330

File tree

9 files changed

+354
-331
lines changed

9 files changed

+354
-331
lines changed

libcxx/include/span

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ public:
6565
using reference = element_type&;
6666
using const_reference = const element_type&;
6767
using iterator = implementation-defined;
68+
using const_iterator = std::const_iterator<iterator>; // since C++23
6869
using reverse_iterator = std::reverse_iterator<iterator>;
70+
using const_reverse_iterator = std::const_iterator<reverse_iterator>; // since C++23
6971
static constexpr size_type extent = Extent;
7072
7173
// [span.cons], span constructors, copy, assignment, and destructor
@@ -115,8 +117,12 @@ public:
115117
// [span.iterators], span iterator support
116118
constexpr iterator begin() const noexcept;
117119
constexpr iterator end() const noexcept;
120+
constexpr const_iterator cbegin() const noexcept; // since C++23
121+
constexpr const_iterator cend() const noexcept; // since C++23
118122
constexpr reverse_iterator rbegin() const noexcept;
119123
constexpr reverse_iterator rend() const noexcept;
124+
constexpr const_reverse_iterator crbegin() const noexcept; // since C++23
125+
constexpr const_reverse_iterator crend() const noexcept; // since C++23
120126
121127
private:
122128
pointer data_; // exposition only
@@ -152,6 +158,7 @@ template<class R>
152158
#include <__fwd/span.h>
153159
#include <__iterator/bounded_iter.h>
154160
#include <__iterator/concepts.h>
161+
#include <__iterator/const_iterator.h>
155162
#include <__iterator/iterator_traits.h>
156163
#include <__iterator/reverse_iterator.h>
157164
#include <__iterator/wrap_iter.h>
@@ -167,6 +174,7 @@ template<class R>
167174
#include <__type_traits/is_convertible.h>
168175
#include <__type_traits/is_integral.h>
169176
#include <__type_traits/is_same.h>
177+
#include <__type_traits/is_volatile.h>
170178
#include <__type_traits/remove_const.h>
171179
#include <__type_traits/remove_cv.h>
172180
#include <__type_traits/remove_cvref.h>
@@ -224,6 +232,20 @@ concept __span_compatible_iterator =
224232
template <class _Sentinel, class _It>
225233
concept __span_compatible_sentinel_for = sized_sentinel_for<_Sentinel, _It> && !is_convertible_v<_Sentinel, size_t>;
226234

235+
# if _LIBCPP_STD_VER >= 23
236+
// As an extension, libc++'s `span` supports volatile types; however, `std::const_iterator` does not.
237+
// Thus, we must remove the support for `cbegin` and friends if `span`'s element type is `volatile`-qualified.
238+
template <class _Tp, class _Iter>
239+
struct __span_const_iterator {
240+
using type = void;
241+
};
242+
template <class _Tp, class _Iter>
243+
requires(!is_volatile_v<_Tp>)
244+
struct __span_const_iterator<_Tp, _Iter> {
245+
using type = std::const_iterator<_Iter>;
246+
};
247+
# endif
248+
227249
template <typename _Tp, size_t _Extent>
228250
class _LIBCPP_TEMPLATE_VIS span {
229251
public:
@@ -242,6 +264,10 @@ public:
242264
using iterator = __wrap_iter<pointer>;
243265
# endif
244266
using reverse_iterator = std::reverse_iterator<iterator>;
267+
# if _LIBCPP_STD_VER >= 23
268+
using const_iterator = __span_const_iterator<_Tp, iterator>::type;
269+
using const_reverse_iterator = __span_const_iterator<_Tp, reverse_iterator>::type;
270+
# endif // _LIBCPP_STD_VER >= 23
245271

246272
static constexpr size_type extent = _Extent;
247273

@@ -390,8 +416,33 @@ public:
390416
return iterator(data() + size());
391417
# endif
392418
}
419+
# if _LIBCPP_STD_VER >= 23
420+
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator cbegin() const noexcept
421+
requires(!is_volatile_v<_Tp>)
422+
{
423+
return begin();
424+
}
425+
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator cend() const noexcept
426+
requires(!is_volatile_v<_Tp>)
427+
{
428+
return end();
429+
}
430+
# endif // _LIBCPP_STD_VER >= 23
431+
393432
_LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
394433
_LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
434+
# if _LIBCPP_STD_VER >= 23
435+
_LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crbegin() const noexcept
436+
requires(!is_volatile_v<_Tp>)
437+
{
438+
return rbegin();
439+
}
440+
_LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crend() const noexcept
441+
requires(!is_volatile_v<_Tp>)
442+
{
443+
return rend();
444+
}
445+
# endif // _LIBCPP_STD_VER >= 23
395446

396447
_LIBCPP_HIDE_FROM_ABI span<const byte, _Extent * sizeof(element_type)> __as_bytes() const noexcept {
397448
return span<const byte, _Extent * sizeof(element_type)>{reinterpret_cast<const byte*>(data()), size_bytes()};
@@ -423,6 +474,10 @@ public:
423474
using iterator = __wrap_iter<pointer>;
424475
# endif
425476
using reverse_iterator = std::reverse_iterator<iterator>;
477+
# if _LIBCPP_STD_VER >= 23
478+
using const_iterator = __span_const_iterator<_Tp, iterator>::type;
479+
using const_reverse_iterator = __span_const_iterator<_Tp, reverse_iterator>::type;
480+
# endif
426481

427482
static constexpr size_type extent = dynamic_extent;
428483

@@ -552,8 +607,33 @@ public:
552607
return iterator(data() + size());
553608
# endif
554609
}
610+
# if _LIBCPP_STD_VER >= 23
611+
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator cbegin() const noexcept
612+
requires(!is_volatile_v<_Tp>)
613+
{
614+
return begin();
615+
}
616+
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator cend() const noexcept
617+
requires(!is_volatile_v<_Tp>)
618+
{
619+
return end();
620+
}
621+
# endif
622+
555623
_LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
556624
_LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
625+
# if _LIBCPP_STD_VER >= 23
626+
_LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crbegin() const noexcept
627+
requires(!is_volatile_v<_Tp>)
628+
{
629+
return rbegin();
630+
}
631+
_LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crend() const noexcept
632+
requires(!is_volatile_v<_Tp>)
633+
{
634+
return rend();
635+
}
636+
# endif
557637

558638
_LIBCPP_HIDE_FROM_ABI span<const byte, dynamic_extent> __as_bytes() const noexcept {
559639
return {reinterpret_cast<const byte*>(data()), size_bytes()};

libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <concepts>
1616
#include <ranges>
1717

18+
#include "test_macros.h"
19+
1820
using range = std::span<int>;
1921

2022

@@ -35,3 +37,8 @@ static_assert(!std::ranges::view<range const> && !std::ranges::enable_view<range
3537
static_assert(std::ranges::sized_range<range const>);
3638
static_assert(std::ranges::borrowed_range<range const>);
3739
static_assert(std::ranges::viewable_range<range const>);
40+
41+
#if TEST_STD_VER >= 23
42+
static_assert(std::same_as<std::ranges::const_iterator_t<range>, range::const_iterator>);
43+
static_assert(std::same_as<std::ranges::const_iterator_t<range const>, range::const_iterator>);
44+
#endif

libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp

Lines changed: 58 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -10,98 +10,76 @@
1010
// <span>
1111

1212
// constexpr iterator begin() const noexcept;
13-
// constexpr const_iterator cbegin() const noexcept;
13+
// constexpr const_iterator cbegin() const noexcept; // since C++23
1414

1515
#include <span>
1616
#include <cassert>
1717
#include <string>
1818

1919
#include "test_macros.h"
2020

21-
template <class Span>
22-
constexpr bool testConstexprSpan(Span s)
23-
{
24-
bool ret = true;
25-
typename Span::iterator b = s.begin();
26-
27-
if (s.empty())
28-
{
29-
ret = ret && (b == s.end());
30-
}
31-
else
32-
{
33-
ret = ret && ( *b == s[0]);
34-
ret = ret && (&*b == &s[0]);
35-
}
36-
return ret;
21+
template <class Span, class Iter>
22+
constexpr bool testSpanImpl(Span s, Iter first) {
23+
bool ret = true;
24+
if (s.empty()) {
25+
ret = ret && (first == s.end());
26+
} else {
27+
ret = ret && (*first == s[0]);
28+
ret = ret && (&*first == &s[0]);
29+
}
30+
return ret;
3731
}
3832

33+
template <class EType, size_t Extent, class... Args>
34+
constexpr bool testSpan(Args&&... args) {
35+
auto s1 = std::span<EType>(std::forward<Args>(args)...);
36+
bool ret = true;
3937

40-
template <class Span>
41-
void testRuntimeSpan(Span s)
42-
{
43-
typename Span::iterator b = s.begin();
44-
45-
if (s.empty())
46-
{
47-
assert(b == s.end());
48-
}
49-
else
50-
{
51-
assert( *b == s[0]);
52-
assert(&*b == &s[0]);
53-
}
38+
ret = ret && testSpanImpl(s1, s1.begin());
39+
#if TEST_STD_VER >= 23
40+
ret = ret && testSpanImpl(s1, s1.cbegin());
41+
#endif
42+
43+
auto s2 = std::span<EType, Extent>(std::forward<Args>(args)...);
44+
ret = ret && testSpanImpl(s2, s2.begin());
45+
#if TEST_STD_VER >= 23
46+
ret = ret && testSpanImpl(s2, s2.cbegin());
47+
#endif
48+
49+
return ret;
5450
}
5551

56-
struct A{};
57-
bool operator==(A, A) {return true;}
58-
59-
constexpr int iArr1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
60-
int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
61-
62-
63-
int main(int, char**)
64-
{
65-
static_assert(testConstexprSpan(std::span<int>()), "");
66-
static_assert(testConstexprSpan(std::span<long>()), "");
67-
static_assert(testConstexprSpan(std::span<double>()), "");
68-
static_assert(testConstexprSpan(std::span<A>()), "");
69-
static_assert(testConstexprSpan(std::span<std::string>()), "");
70-
71-
static_assert(testConstexprSpan(std::span<int, 0>()), "");
72-
static_assert(testConstexprSpan(std::span<long, 0>()), "");
73-
static_assert(testConstexprSpan(std::span<double, 0>()), "");
74-
static_assert(testConstexprSpan(std::span<A, 0>()), "");
75-
static_assert(testConstexprSpan(std::span<std::string, 0>()), "");
76-
77-
static_assert(testConstexprSpan(std::span<const int>(iArr1, 1)), "");
78-
static_assert(testConstexprSpan(std::span<const int>(iArr1, 2)), "");
79-
static_assert(testConstexprSpan(std::span<const int>(iArr1, 3)), "");
80-
static_assert(testConstexprSpan(std::span<const int>(iArr1, 4)), "");
81-
static_assert(testConstexprSpan(std::span<const int>(iArr1, 5)), "");
82-
83-
84-
testRuntimeSpan(std::span<int> ());
85-
testRuntimeSpan(std::span<long> ());
86-
testRuntimeSpan(std::span<double> ());
87-
testRuntimeSpan(std::span<A> ());
88-
testRuntimeSpan(std::span<std::string>());
89-
90-
testRuntimeSpan(std::span<int, 0> ());
91-
testRuntimeSpan(std::span<long, 0> ());
92-
testRuntimeSpan(std::span<double, 0> ());
93-
testRuntimeSpan(std::span<A, 0> ());
94-
testRuntimeSpan(std::span<std::string, 0>());
95-
96-
testRuntimeSpan(std::span<int>(iArr2, 1));
97-
testRuntimeSpan(std::span<int>(iArr2, 2));
98-
testRuntimeSpan(std::span<int>(iArr2, 3));
99-
testRuntimeSpan(std::span<int>(iArr2, 4));
100-
testRuntimeSpan(std::span<int>(iArr2, 5));
101-
102-
std::string s;
103-
testRuntimeSpan(std::span<std::string>(&s, (std::size_t) 0));
104-
testRuntimeSpan(std::span<std::string>(&s, 1));
52+
struct A {};
53+
bool operator==(A, A) { return true; }
54+
55+
constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
56+
int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
57+
58+
int main(int, char**) {
59+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<int, 0>());
60+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<long, 0>());
61+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<double, 0>());
62+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<A, 0>());
63+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<std::string, 0>());
64+
65+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 1>(iArr1, 1));
66+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 2>(iArr1, 2));
67+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 3>(iArr1, 3));
68+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 4>(iArr1, 4));
69+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 5>(iArr1, 5));
70+
71+
testSpan<int, 1>(iArr2, 1);
72+
testSpan<int, 2>(iArr2, 2);
73+
testSpan<int, 3>(iArr2, 3);
74+
testSpan<int, 4>(iArr2, 4);
75+
testSpan<int, 5>(iArr2, 5);
76+
77+
std::string s1;
78+
constexpr static std::string s2;
79+
testSpan<std::string, 0>(&s1, static_cast<size_t>(0));
80+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 0>(&s2, static_cast<size_t>(0)));
81+
testSpan<std::string, 1>(&s1, 1);
82+
ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 1>(&s2, 1));
10583

10684
return 0;
10785
}

0 commit comments

Comments
 (0)