pw_span: Updates for C++20 std::span compatibility
Apply the latest updates from Chromium's span implementation (Chromium
commit d93ae920e4309682deb9352a4637cfc2941c1d1f).
Change-Id: Ifc805b53ff70158fbc99e2a452502f525c050a62
diff --git a/pw_span/public/pw_span/span.h b/pw_span/public/pw_span/span.h
index 2ea01ff..8aa270d 100644
--- a/pw_span/public/pw_span/span.h
+++ b/pw_span/public/pw_span/span.h
@@ -19,7 +19,7 @@
// a template parameter, so this class can be used to without stating its size.
//
// This file a modified version of base::span from Chromium:
-// https://chromium.googlesource.com/chromium/src/+/ef71f9c29f0dc6eddae474879c4ca5232ca93a6c/base/containers/span.h
+// https://chromium.googlesource.com/chromium/src/+/d93ae920e4309682deb9352a4637cfc2941c1d1f/base/containers/span.h
//
// In order to minimize changes from the original, this file does NOT fully
// adhere to Pigweed's style guide.
@@ -246,9 +246,7 @@
using pointer = T*;
using reference = T&;
using iterator = T*;
- using const_iterator = const T*;
using reverse_iterator = std::reverse_iterator<iterator>;
- using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr size_t extent = Extent;
// [span.cons], span constructors, copy, assignment, and destructor
@@ -273,19 +271,18 @@
span_internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {}
- template <
- size_t N,
- typename = span_internal::
- EnableIfSpanCompatibleArray<std::array<value_type, N>&, T, Extent>>
- constexpr span(std::array<value_type, N>& array) noexcept
+ template <typename U,
+ size_t N,
+ typename = span_internal::
+ EnableIfSpanCompatibleArray<std::array<U, N>&, T, Extent>>
+ constexpr span(std::array<U, N>& array) noexcept
: span(std::data(array), N) {}
- template <size_t N,
- typename = span_internal::EnableIfSpanCompatibleArray<
- const std::array<value_type, N>&,
- T,
- Extent>>
- constexpr span(const std::array<value_type, N>& array) noexcept
+ template <typename U,
+ size_t N,
+ typename = span_internal::
+ EnableIfSpanCompatibleArray<const std::array<U, N>&, T, Extent>>
+ constexpr span(const std::array<U, N>& array) noexcept
: span(std::data(array), N) {}
// Conversion from a container that has compatible std::data() and integral
@@ -326,16 +323,14 @@
// [span.sub], span subviews
template <size_t Count>
constexpr span<T, Count> first() const noexcept {
- static_assert(Extent == dynamic_extent || Count <= Extent,
- "Count must not exceed Extent");
+ static_assert(Count <= Extent, "Count must not exceed Extent");
_PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size());
return {data(), Count};
}
template <size_t Count>
constexpr span<T, Count> last() const noexcept {
- static_assert(Extent == dynamic_extent || Count <= Extent,
- "Count must not exceed Extent");
+ static_assert(Count <= Extent, "Count must not exceed Extent");
_PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size());
return {data() + (size() - Count), Count};
}
@@ -347,10 +342,8 @@
: (Extent != dynamic_extent ? Extent - Offset
: dynamic_extent))>
subspan() const noexcept {
- static_assert(Extent == dynamic_extent || Offset <= Extent,
- "Offset must not exceed Extent");
- static_assert(Extent == dynamic_extent || Count == dynamic_extent ||
- Count <= Extent - Offset,
+ static_assert(Offset <= Extent, "Offset must not exceed Extent");
+ static_assert(Count == dynamic_extent || Count <= Extent - Offset,
"Count must not exceed Extent - Offset");
_PW_SPAN_ASSERT(Extent != dynamic_extent || Offset <= size());
_PW_SPAN_ASSERT(Extent != dynamic_extent || Count == dynamic_extent ||
@@ -410,9 +403,6 @@
constexpr iterator begin() const noexcept { return data_; }
constexpr iterator end() const noexcept { return data_ + size(); }
- constexpr const_iterator cbegin() const noexcept { return begin(); }
- constexpr const_iterator cend() const noexcept { return end(); }
-
constexpr reverse_iterator rbegin() const noexcept {
return reverse_iterator(end());
}
@@ -420,13 +410,6 @@
return reverse_iterator(begin());
}
- constexpr const_reverse_iterator crbegin() const noexcept {
- return const_reverse_iterator(cend());
- }
- constexpr const_reverse_iterator crend() const noexcept {
- return const_reverse_iterator(cbegin());
- }
-
private:
T* data_;
};
@@ -486,49 +469,4 @@
} // namespace pw
-// Note: std::tuple_size, std::tuple_element and std::get are specialized for
-// static spans, so that they can be used in C++17's structured bindings.
-namespace std {
-
-// [span.tuple], tuple interface
-#if defined(__clang__)
-// Due to https://llvm.org/PR39871 and https://llvm.org/PR41331 and their
-// respective fixes different versions of libc++ declare std::tuple_size and
-// std::tuple_element either as classes or structs. In order to be able to
-// specialize std::tuple_size and std::tuple_element for custom pw types we
-// thus need to disable -Wmismatched-tags in order to support all build
-// configurations. Note that this is blessed by the standard in
-// https://timsong-cpp.github.io/cppwp/n4140/dcl.type.elab#3.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmismatched-tags"
-#endif
-template <typename T, size_t X>
-struct tuple_size<pw::span<T, X>> : public integral_constant<size_t, X> {};
-
-template <typename T>
-struct tuple_size<pw::span<T, pw::dynamic_extent>>; // not defined
-
-template <size_t I, typename T, size_t X>
-struct tuple_element<I, pw::span<T, X>> {
- static_assert(
- pw::dynamic_extent != X,
- "std::tuple_element<> not supported for pw::span<T, dynamic_extent>");
- static_assert(I < X,
- "Index out of bounds in std::tuple_element<> (pw::span)");
- using type = T;
-};
-#if defined(__clang__)
-#pragma clang diagnostic pop // -Wmismatched-tags
-#endif
-
-template <size_t I, typename T, size_t X>
-constexpr T& get(pw::span<T, X> s) noexcept {
- static_assert(pw::dynamic_extent != X,
- "std::get<> not supported for pw::span<T, dynamic_extent>");
- static_assert(I < X, "Index out of bounds in std::get<> (pw::span)");
- return s[I];
-}
-
-} // namespace std
-
#undef _PW_SPAN_ASSERT
diff --git a/pw_span/span_test.cc b/pw_span/span_test.cc
index 37875f6..37d5ee4 100644
--- a/pw_span/span_test.cc
+++ b/pw_span/span_test.cc
@@ -260,6 +260,99 @@
EXPECT_EQ(vector[i], static_span[i]);
}
+TEST(SpanTest, AllowedConversionsFromStdArray) {
+ // In the following assertions we use std::is_convertible_v<From, To>, which
+ // for non-void types is equivalent to checking whether the following
+ // expression is well-formed:
+ //
+ // T obj = std::declval<From>();
+ //
+ // In particular we are checking whether From is implicitly convertible to To,
+ // which also implies that To is explicitly constructible from From.
+ static_assert(
+ std::is_convertible<std::array<int, 3>&, span<int>>::value,
+ "Error: l-value reference to std::array<int> should be convertible to "
+ "span<int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<std::array<int, 3>&, span<int, 3>>::value,
+ "Error: l-value reference to std::array<int> should be convertible to "
+ "span<int> with the same static extent.");
+ static_assert(
+ std::is_convertible<std::array<int, 3>&, span<const int>>::value,
+ "Error: l-value reference to std::array<int> should be convertible to "
+ "span<const int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<std::array<int, 3>&, span<const int, 3>>::value,
+ "Error: l-value reference to std::array<int> should be convertible to "
+ "span<const int> with the same static extent.");
+ static_assert(
+ std::is_convertible<const std::array<int, 3>&, span<const int>>::value,
+ "Error: const l-value reference to std::array<int> should be "
+ "convertible to span<const int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<const std::array<int, 3>&, span<const int, 3>>::value,
+ "Error: const l-value reference to std::array<int> should be convertible "
+ "to span<const int> with the same static extent.");
+ static_assert(
+ std::is_convertible<std::array<const int, 3>&, span<const int>>::value,
+ "Error: l-value reference to std::array<const int> should be "
+ "convertible to span<const int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<std::array<const int, 3>&, span<const int, 3>>::value,
+ "Error: l-value reference to std::array<const int> should be convertible "
+ "to span<const int> with the same static extent.");
+ static_assert(
+ std::is_convertible<const std::array<const int, 3>&,
+ span<const int>>::value,
+ "Error: const l-value reference to std::array<const int> should be "
+ "convertible to span<const int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<const std::array<const int, 3>&,
+ span<const int, 3>>::value,
+ "Error: const l-value reference to std::array<const int> should be "
+ "convertible to span<const int> with the same static extent.");
+}
+
+TEST(SpanTest, DisallowedConstructionsFromStdArray) {
+ // In the following assertions we use !std::is_constructible_v<T, Args>, which
+ // is equivalent to checking whether the following expression is malformed:
+ //
+ // T obj(std::declval<Args>()...);
+ //
+ // In particular we are checking that T is not explicitly constructible from
+ // Args, which also implies that T is not implicitly constructible from Args
+ // as well.
+ static_assert(
+ !std::is_constructible<span<int>, const std::array<int, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "from const l-value reference to std::array<int>");
+
+ static_assert(
+ !std::is_constructible<span<int>, std::array<const int, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "from l-value reference to std::array<const int>");
+
+ static_assert(
+ !std::is_constructible<span<int>, const std::array<const int, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "const from l-value reference to std::array<const int>");
+
+ static_assert(
+ !std::is_constructible<span<int, 2>, std::array<int, 3>&>::value,
+ "Error: span<int> with static extent should not be constructible "
+ "from l-value reference to std::array<int> with different extent");
+
+ static_assert(
+ !std::is_constructible<span<int, 4>, std::array<int, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "from l-value reference to std::array<int> with different extent");
+
+ static_assert(
+ !std::is_constructible<span<int>, std::array<bool, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "from l-value reference to std::array<bool>");
+}
+
TEST(SpanTest, ConstructFromConstexprArray) {
static constexpr int kArray[] = {5, 4, 3, 2, 1};
@@ -1143,8 +1236,10 @@
EXPECT_TRUE(std::equal(
std::rbegin(kArray), std::rend(kArray), span.rbegin(), span.rend()));
- EXPECT_TRUE(std::equal(
- std::crbegin(kArray), std::crend(kArray), span.crbegin(), span.crend()));
+ EXPECT_TRUE(std::equal(std::crbegin(kArray),
+ std::crend(kArray),
+ std::crbegin(span),
+ std::crend(span)));
}
// Pigweed: These are tests for make_span, which is not included in Pigweed's
@@ -1379,46 +1474,6 @@
}
#endif // 0
-TEST(SpanTest, StdTupleSize) {
- static_assert(std::tuple_size<span<int, 0>>::value == 0, "");
- static_assert(std::tuple_size<span<int, 1>>::value == 1, "");
- static_assert(std::tuple_size<span<int, 2>>::value == 2, "");
-}
-
-TEST(SpanTest, StdTupleElement) {
- static_assert(std::is_same<int, std::tuple_element_t<0, span<int, 1>>>::value,
- "");
- static_assert(
- std::is_same<const int,
- std::tuple_element_t<0, span<const int, 2>>>::value,
- "");
- static_assert(
- std::is_same<const int*,
- std::tuple_element_t<1, span<const int*, 2>>>::value,
- "");
-}
-
-TEST(SpanTest, StdGet) {
- static constexpr int kArray[] = {1, 6, 1, 8, 0};
- constexpr span<const int, 5> span(kArray);
-
- static_assert(
- &kArray[0] == &std::get<0>(span),
- "std::get<0>(span) does not refer to the same element as kArray[0]");
- static_assert(
- &kArray[1] == &std::get<1>(span),
- "std::get<1>(span) does not refer to the same element as kArray[1]");
- static_assert(
- &kArray[2] == &std::get<2>(span),
- "std::get<2>(span) does not refer to the same element as kArray[2]");
- static_assert(
- &kArray[3] == &std::get<3>(span),
- "std::get<3>(span) does not refer to the same element as kArray[3]");
- static_assert(
- &kArray[4] == &std::get<4>(span),
- "std::get<4>(span) does not refer to the same element as kArray[4]");
-}
-
TEST(SpanTest, EnsureConstexprGoodness) {
static constexpr int kArray[] = {5, 4, 3, 2, 1};
constexpr span<const int> constexpr_span(kArray);
@@ -1488,7 +1543,7 @@
span.data() + dest_start_index,
span.data() + dest_start_index + kNumElements)));
EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
- span.cbegin(), span.cend(),
+ std::cbegin(span), std::cend(span),
CheckedContiguousConstIterator<const int>(
span.data() + dest_start_index,
span.data() + dest_start_index + kNumElements)));
@@ -1502,7 +1557,7 @@
span.data() + dest_start_index,
span.data() + dest_start_index + kNumElements)));
EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
- span.cbegin(), span.cend(),
+ std::cbegin(span), std::cend(span),
CheckedContiguousConstIterator<const int>(
span.data() + dest_start_index,
span.data() + dest_start_index + kNumElements)));
@@ -1513,7 +1568,7 @@
span.begin(), span.begin(),
CheckedContiguousIterator<const int>(span.data(), span.data())));
EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
- span.cbegin(), span.cbegin(),
+ std::cbegin(span), std::cbegin(span),
CheckedContiguousConstIterator<const int>(span.data(), span.data())));
// IsRangeMoveSafe is false if end < begin.
@@ -1521,7 +1576,7 @@
span.end(), span.begin(),
CheckedContiguousIterator<const int>(span.data(), span.data())));
EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
- span.cend(), span.cbegin(),
+ std::cend(span), std::cbegin(span),
CheckedContiguousConstIterator<const int>(span.data(), span.data())));
}
@@ -1566,12 +1621,12 @@
TEST(SpanTest, IteratorConversions) {
static_assert(std::is_convertible<span<int>::iterator,
- span<int>::const_iterator>::value,
- "Error: iterator should be convertible to const_iterator");
+ span<const int>::iterator>::value,
+ "Error: iterator should be convertible to const iterator");
- static_assert(!std::is_convertible<span<int>::const_iterator,
+ static_assert(!std::is_convertible<span<const int>::iterator,
span<int>::iterator>::value,
- "Error: const_iterator should not be convertible to iterator");
+ "Error: const iterator should not be convertible to iterator");
}
} // namespace pw