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