| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. |
| |
| #include <lib/stdcompat/span.h> |
| #include <lib/stdcompat/string_view.h> |
| |
| #include <array> |
| #include <cstddef> |
| #include <span> |
| #include <type_traits> |
| |
| #include <gtest/gtest.h> |
| |
| namespace { |
| |
| TEST(SpanTest, EmptyConstructorWithDynamicExtent) { |
| constexpr cpp20::span<int> empty_span; |
| |
| static_assert(empty_span.extent == cpp20::dynamic_extent, |
| "Empty span by default is dynamic extent."); |
| static_assert(empty_span.data() == nullptr, "Empty span by default is dynamic extent."); |
| static_assert(empty_span.size() == 0, "Empty span by default is dynamic extent."); |
| static_assert(sizeof(empty_span) == sizeof(int*) + sizeof(size_t), |
| "Dynamic extent span should include the size."); |
| } |
| |
| TEST(SpanTest, EmptyConstructorWithZeroStaticExtent) { |
| constexpr cpp20::span<int, 0> empty_span; |
| |
| static_assert(empty_span.extent == 0, "Empty span by default is static extent."); |
| static_assert(empty_span.data() == nullptr, "Empty span by default is has nullptr."); |
| static_assert(empty_span.size() == 0, "Empty span by default is has size 0."); |
| static_assert(sizeof(empty_span) == sizeof(int*), |
| "Static extent span should not store the size."); |
| } |
| |
| TEST(SpanTest, ConstructFromIteratorAndCountWithDynamicExtent) { |
| constexpr cpp17::string_view kLiteral = "Some String"; |
| constexpr cpp20::span<const char> literal_span(kLiteral.begin(), 2); |
| |
| static_assert(literal_span.extent == cpp20::dynamic_extent, |
| "Empty span by default is dynamic extent."); |
| static_assert(literal_span.data() == kLiteral.data(), "Empty span by default is dynamic extent."); |
| static_assert(literal_span.size() == 2, "Empty span by default is dynamic extent."); |
| } |
| |
| TEST(SpanTest, ConstructFromIteratorAndCountWithStaticExtent) { |
| constexpr cpp17::string_view kLiteral = "Some String"; |
| constexpr cpp20::span<const char, 2> literal_span(kLiteral.begin(), 2); |
| |
| static_assert(literal_span.extent == 2, "Empty span by default is dynamic extent."); |
| static_assert(literal_span.data() == kLiteral.data(), "Empty span by default is dynamic extent."); |
| static_assert(literal_span.size() == 2, "Empty span by default is dynamic extent."); |
| } |
| |
| TEST(SpanTest, ConstructFromIteratorAndEndIterWithDynamicExtent) { |
| static const constexpr std::array<int, 10> kContainer = {}; |
| static_assert(!std::is_convertible<decltype(kContainer.end()), size_t>::value, |
| "kContainer::end() must not be convertible to |size_t|."); |
| |
| constexpr cpp20::span<const int> container_span(kContainer.begin(), kContainer.begin() + 2); |
| |
| static_assert(container_span.extent == cpp20::dynamic_extent, |
| "Empty span by default is dynamic extent."); |
| static_assert(container_span.data() == kContainer.data(), |
| "Empty span by default is dynamic extent."); |
| static_assert(container_span.size() == 2, "Empty span by default is dynamic extent."); |
| } |
| |
| TEST(SpanTest, ConstructFromIteratorAndEndIterWithStaticExtent) { |
| static const constexpr std::array<int, 10> kContainer = {}; |
| static_assert(!std::is_convertible<decltype(kContainer.end()), size_t>::value, |
| "kContainer::end() must not be convertible to |size_t|."); |
| |
| constexpr cpp20::span<const int, 2> container_span(kContainer.begin(), kContainer.begin() + 2); |
| |
| static_assert(container_span.extent == 2, "Empty span by default is dynamic extent."); |
| static_assert(container_span.data() == kContainer.data(), |
| "Empty span by default is dynamic extent."); |
| static_assert(container_span.size() == 2, "Empty span by default is dynamic extent."); |
| } |
| |
| template <typename T, typename U> |
| constexpr bool are_pointers_equal(T* a, U* b) { |
| return static_cast<const void*>(a) == static_cast<const void*>(b); |
| } |
| |
| template <typename T, typename U> |
| constexpr bool are_pointers_equal(volatile T* a, volatile U* b) { |
| return static_cast<const volatile void*>(a) == static_cast<const volatile void*>(b); |
| } |
| |
| template <typename ElementType, size_t expected_extent, typename SpanElementType = ElementType> |
| constexpr bool check_construct_from_array() { |
| ElementType container[10] = {}; |
| |
| cpp20::span<SpanElementType, expected_extent> array_span(container); |
| const cpp20::span<SpanElementType, expected_extent> const_array_span(container); |
| |
| auto check = [](auto& view, auto& container) { |
| if (view.extent != expected_extent) { |
| return false; |
| } |
| |
| if (!are_pointers_equal(view.data(), std::begin(container))) { |
| return false; |
| } |
| |
| if (view.size() != 10) { |
| return false; |
| } |
| |
| return true; |
| }; |
| return check(array_span, container) && check(const_array_span, container); |
| } |
| |
| TEST(SpanTest, ConstructArrayRefWithDynamicExtent) { |
| static_assert(check_construct_from_array<int, cpp20::dynamic_extent>(), |
| "Span constructor with dynamic extent failed with array failed."); |
| } |
| |
| TEST(SpanTest, ConstructFromArrayRefWithStaticExtent) { |
| static_assert(check_construct_from_array<int, 10>(), |
| "Span constructor with static extent failed with array failed."); |
| } |
| |
| TEST(SpanTest, ConstructFromConstArrayRefWithDynamicExtent) { |
| static_assert(check_construct_from_array<const int, cpp20::dynamic_extent>(), |
| "Span constructor with dynamic extent failed with array failed."); |
| } |
| |
| TEST(SpanTest, ConstructFromConstArrayRefWithStaticExtent) { |
| static_assert(check_construct_from_array<const int, 10>(), |
| "Span constructor with static extent failed with array failed."); |
| } |
| |
| TEST(SpanTest, ConstructFromArrayRefWithDynamicExtentAndConvertibleType) { |
| static_assert(check_construct_from_array<int, cpp20::dynamic_extent, const int>(), |
| "Span constructor with dynamic extent failed with array failed."); |
| |
| static_assert( |
| check_construct_from_array<volatile int, cpp20::dynamic_extent, const volatile int>(), |
| "Span constructor with dynamic extent failed with array failed."); |
| } |
| |
| TEST(SpanTest, ConstructFromArrayRefWithStaticExtentAndConvertibleType) { |
| static_assert(check_construct_from_array<int, 10, const int>(), |
| "Span constructor with static extent failed with array failed."); |
| |
| static_assert(check_construct_from_array<volatile int, 10, const volatile int>(), |
| "Span constructor with static extent failed with array failed."); |
| } |
| |
| TEST(SpanTest, ConstructFromContainerWithDynamicExtent) { |
| static constexpr std::array<int, 40> kContainer = {}; |
| constexpr cpp20::span<const int> container_span(kContainer); |
| |
| static_assert(std::is_convertible<decltype(kContainer)&, cpp20::span<const int>>::value, |
| "cpp20::span constructor for container R should be implicit for dynamic extent."); |
| static_assert(container_span.data() == kContainer.data(), |
| "cpp20::span constructor from container R should match the container's data."); |
| static_assert(container_span.size() == kContainer.size(), |
| "cpp20::span constructor from container R should match the container's size."); |
| } |
| |
| TEST(SpanTest, ConstructFromContainerWithStaticExtent) { |
| static constexpr std::array<int, 40> kContainer = {}; |
| constexpr cpp20::span<const int, 40> container_span(kContainer); |
| |
| static_assert(!std::is_convertible<decltype(kContainer)&, cpp20::span<const int, 40>>::value, |
| "cpp20::span from a container R should be explicit for static extent."); |
| |
| static_assert(container_span.data() == kContainer.data(), |
| "cpp20::span constructor from container R should match the container's data."); |
| static_assert(container_span.size() == kContainer.size(), |
| "cpp20::span constructor from container R should match the container's size."); |
| ; |
| } |
| |
| template <typename U, typename T> |
| constexpr bool check_span_from_another(T source_span) { |
| U copy_span(source_span); |
| |
| if (!are_pointers_equal(copy_span.data(), source_span.data())) { |
| return false; |
| } |
| if (copy_span.size() != source_span.size()) { |
| return false; |
| } |
| return true; |
| } |
| |
| TEST(SpanTest, ConstructFromAnotherSpan) { |
| static constexpr std::array<int, 40> kContainer = {}; |
| constexpr cpp20::span<const int> dynamic_span(kContainer); |
| constexpr cpp20::span<const int, 40> static_span(kContainer); |
| |
| static_assert(std::is_constructible<decltype(static_span), decltype(static_span)>::value); |
| static_assert(std::is_constructible<decltype(static_span), decltype(dynamic_span)>::value); |
| static_assert(std::is_constructible<decltype(dynamic_span), decltype(static_span)>::value); |
| static_assert(std::is_constructible<decltype(dynamic_span), decltype(dynamic_span)>::value); |
| |
| static_assert(std::is_convertible<decltype(static_span), decltype(dynamic_span)>::value); |
| static_assert(!std::is_convertible<decltype(dynamic_span), decltype(static_span)>::value); |
| |
| static_assert(check_span_from_another<decltype(dynamic_span)>(static_span), |
| "cpp20::span with dynamic extent constructor from another span with static extent " |
| "not initialized correctly."); |
| static_assert(check_span_from_another<decltype(static_span)>(dynamic_span), |
| "cpp20::span with static extent constructor from another span with dynamic extent " |
| "not initialized correctly."); |
| } |
| |
| TEST(SpanTest, CopyConstructor) { |
| static constexpr std::array<int, 40> kContainer = {}; |
| constexpr cpp20::span<const int> dynamic_span(kContainer); |
| constexpr cpp20::span<const int, 40> static_span(kContainer); |
| |
| static_assert(check_span_from_another<decltype(dynamic_span)>(dynamic_span), |
| "cpp20::span with dynamic extent constructor from another span with dynamic extent " |
| "not initialized correctly."); |
| static_assert(check_span_from_another<decltype(static_span)>(static_span), |
| "cpp20::span with static extent constructor from another span with static extent " |
| "not initialized correctly."); |
| } |
| |
| TEST(SpanTest, AssignmentOperator) { |
| static constexpr std::array<int, 40> kContainer = {}; |
| constexpr cpp20::span<const int> dynamic_span(kContainer); |
| constexpr cpp20::span<const int> new_dynamic_span = dynamic_span; |
| |
| static_assert(dynamic_span.data() == new_dynamic_span.data(), |
| "cpp20::span::operator= must set data to other.data()."); |
| |
| static_assert(dynamic_span.size() == new_dynamic_span.size(), |
| "cpp20::span::operator= must set size to rhs.size()."); |
| |
| constexpr cpp20::span<const int, 40> static_span(kContainer); |
| constexpr cpp20::span<const int, 40> new_static_span = static_span; |
| |
| static_assert(static_span.data() == new_static_span.data(), |
| "cpp20::span::operator= must set data to other.data()."); |
| |
| static_assert(static_span.size() == new_static_span.size(), |
| "cpp20::span::operator= must set size to rhs.size()."); |
| } |
| |
| template <typename T, size_t ContainerSize> |
| constexpr bool span_bracket_operator_check() { |
| std::array<int, ContainerSize> container = {}; |
| T view(container); |
| const T const_view(container); |
| |
| constexpr auto check = [](auto& view, auto& container) { |
| if (!are_pointers_equal(&view[0], &view.front())) { |
| return false; |
| } |
| |
| if (!are_pointers_equal(&view[container.size() - 1], &view.back())) { |
| return false; |
| } |
| |
| for (size_t i = 0; i < view.size(); ++i) { |
| if (!are_pointers_equal(&view[i], view.data() + i)) { |
| return false; |
| } |
| } |
| return true; |
| }; |
| |
| return check(view, container) && check(const_view, container); |
| } |
| |
| TEST(SpanTest, BracketOperator) { |
| static_assert(span_bracket_operator_check<cpp20::span<int>, 10>(), |
| "bracket operator for dynamic extent span failed."); |
| static_assert(span_bracket_operator_check<cpp20::span<const int>, 10>(), |
| "bracket operator for dynamic extent span failed."); |
| |
| static_assert(span_bracket_operator_check<cpp20::span<int, 10>, 10>(), |
| "bracket operator for static extent span failed."); |
| static_assert(span_bracket_operator_check<cpp20::span<const int, 10>, 10>(), |
| "bracket operator for static extent span failed."); |
| } |
| |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_iterator_check() { |
| std::array<T, ElementCount> kContainer = {}; |
| cpp20::span<T, Extent> view(kContainer); |
| const cpp20::span<T, Extent> const_view(kContainer); |
| |
| // No assumption on iterator type, which is why they are dereferenced and then taken the address |
| // of, if someone would choose to wrap the iterators with a type. |
| constexpr auto check = [](auto& view) { |
| for (size_t i = 0; i < view.size(); ++i) { |
| if (!are_pointers_equal(&*(view.begin() + i), &view[i])) { |
| return false; |
| } |
| |
| if (!are_pointers_equal(&*(view.end() - i - 1), &view[view.size() - i - 1])) { |
| return false; |
| } |
| |
| if (!are_pointers_equal(&*(view.rbegin() + i), &view[view.size() - i - 1])) { |
| return false; |
| } |
| |
| if (!are_pointers_equal(&*(view.rend() - i - 1), &view[i])) { |
| return false; |
| } |
| } |
| |
| return true; |
| }; |
| |
| return check(view) && check(const_view); |
| } |
| |
| TEST(SpanTest, IteratorCheck) { |
| static_assert(span_iterator_check<int, cpp20::dynamic_extent, 40>(), |
| "Iterators for span with dynamic extent are not set correctly."); |
| static_assert(span_iterator_check<const int, cpp20::dynamic_extent, 40>(), |
| "Iterators for span with dynamic extent are not set correctly."); |
| |
| static_assert(span_iterator_check<int, 40>(), |
| "Iterators for span with dynamic extent are not set correctly."); |
| static_assert(span_iterator_check<const int, 40>(), |
| "Iterators for span with dynamic extent are not set correctly."); |
| } |
| |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_front_check() { |
| std::array<T, ElementCount> kContainer = {}; |
| cpp20::span<T, Extent> view(kContainer); |
| const cpp20::span<T, Extent> const_view(kContainer); |
| |
| return are_pointers_equal(&view.front(), &view[0]) && |
| are_pointers_equal(&const_view.front(), &const_view[0]); |
| } |
| |
| TEST(SpanTest, FrontPointsToFirstElement) { |
| static_assert(span_front_check<int, cpp20::dynamic_extent, 40>(), |
| "cpp20::span::front must return reference to data[0]."); |
| static_assert(span_front_check<const int, cpp20::dynamic_extent, 40>(), |
| "cpp20::span::front must return reference to data[0]."); |
| |
| static_assert(span_front_check<int, 40>(), |
| "cpp20::span::front must return reference to data[0]."); |
| static_assert(span_front_check<const int, 40>(), |
| "cpp20::span::front must return reference to data[0]."); |
| } |
| |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_back_check() { |
| std::array<T, ElementCount> kContainer = {}; |
| cpp20::span<T, Extent> view(kContainer); |
| const cpp20::span<T, Extent> const_view(kContainer); |
| |
| constexpr auto check = [](auto& view) { |
| return are_pointers_equal(&view.back(), &view[view.size() - 1]); |
| }; |
| |
| return check(view) && check(const_view); |
| } |
| |
| TEST(SpanTest, BackPointsToLastElement) { |
| static_assert(span_back_check<int, cpp20::dynamic_extent, 40>(), |
| "cpp20::span::front must return reference to data[0]."); |
| static_assert(span_back_check<const int, cpp20::dynamic_extent, 40>(), |
| "cpp20::span::front must return reference to data[0]."); |
| |
| static_assert(span_back_check<int, 40>(), "cpp20::span::front must return reference to data[0]."); |
| static_assert(span_back_check<const int, 40>(), |
| "cpp20::span::front must return reference to data[0]."); |
| } |
| |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_size_bytes_check() { |
| std::array<T, ElementCount> kContainer = {}; |
| cpp20::span<T, Extent> view(kContainer); |
| |
| return view.size_bytes() == sizeof(T) * ElementCount; |
| } |
| |
| TEST(SpanTest, SizeBytesReflectsSizeOfType) { |
| static_assert(span_size_bytes_check<int, cpp20::dynamic_extent, 15>(), |
| "cpp20::span::size_bytes should reflect the number of bytes in the span."); |
| static_assert(span_size_bytes_check<const int, cpp20::dynamic_extent, 15>(), |
| "cpp20::span::size_bytes should reflect the number of bytes in the span."); |
| |
| static_assert(span_size_bytes_check<int, 15>(), |
| "cpp20::span::size_bytes should reflect the number of bytes in the span."); |
| static_assert(span_size_bytes_check<const int, 15>(), |
| "cpp20::span::size_bytes should reflect the number of bytes in the span."); |
| } |
| |
| TEST(SpanTest, EmptyWhenSizeIsZero) { |
| constexpr cpp20::span<int> kEmpty; |
| |
| static_assert(kEmpty.data() == nullptr, "default constructed span should have nullptr as data."); |
| static_assert(kEmpty.size() == 0, "default constructed span should have 0 as size."); |
| static_assert(kEmpty.empty(), "default constructed span should be considered empty."); |
| |
| static constexpr std::array<int, 40> kContainer = {}; |
| constexpr cpp20::span<const int> view(kContainer.data(), 0); |
| |
| static_assert(view.data() == kContainer.data(), |
| "default constructed span should have nullptr as data."); |
| static_assert(view.empty(), |
| "a span should be considered empty even if data is not nullptr but size is zero."); |
| } |
| |
| // Checks partitioning a subspan that the contents match. |
| // Essentially that each subspan created within this range has the correct properties. |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_subspan_dynamic_extent_check() { |
| std::array<T, ElementCount> kContainer = {}; |
| cpp20::span<T, Extent> view(kContainer); |
| const cpp20::span<const T, Extent> const_view(kContainer); |
| |
| constexpr auto check = [](auto& view) { |
| for (size_t i = 0; i < view.size(); ++i) { |
| { |
| auto subview = view.subspan(i); |
| static_assert(decltype(subview)::extent == cpp20::dynamic_extent, |
| "cpp20::subspan(size_t, size_t) should always return a cpp20::span<T, " |
| "dynamic_extent>."); |
| if (subview.empty()) { |
| if (i != view.size() - 1) { |
| return false; |
| } |
| continue; |
| } |
| |
| // This is ill-formed for an empty span. |
| if (!are_pointers_equal(&subview.front(), &view[i])) { |
| return false; |
| } |
| |
| if (subview.size() != view.size() - i) { |
| return false; |
| } |
| } |
| for (size_t j = 0; j < view.size() - i; ++j) { |
| auto subview = view.subspan(i, j); |
| |
| if (subview.empty()) { |
| if (j != 0 && i + j != view.size()) { |
| return false; |
| } |
| continue; |
| } |
| |
| if (!are_pointers_equal(&subview.front(), &view[i])) { |
| return false; |
| } |
| |
| if (subview.size() != j) { |
| return false; |
| } |
| } |
| } |
| return true; |
| }; |
| |
| return check(view) && check(const_view); |
| } |
| |
| TEST(SpanTest, SubspanWithDynamicExtent) { |
| static_assert(span_subspan_dynamic_extent_check<int, cpp20::dynamic_extent, 10>(), |
| "cpp20::subspan check failed."); |
| static_assert(span_subspan_dynamic_extent_check<const int, cpp20::dynamic_extent, 10>(), |
| "cpp20::subspan check failed."); |
| static_assert(span_subspan_dynamic_extent_check<int, 10>(), "cpp20::subspan check failed."); |
| static_assert(span_subspan_dynamic_extent_check<const int, 10>(), "cpp20::subspan check failed."); |
| } |
| |
| template <size_t Offset, size_t Count = cpp20::dynamic_extent, typename T> |
| constexpr bool span_subspan_with_static_extent_instance_check(T view) { |
| auto subview = view.template subspan<Offset, Count>(); |
| auto actual_count = (Count == cpp20::dynamic_extent) ? view.size() - Offset : Count; |
| |
| if (subview.empty()) { |
| return !static_cast<bool>(actual_count != 0 && Offset + actual_count != view.size()); |
| } |
| |
| if (!are_pointers_equal(&subview.front(), &view[Offset])) { |
| return false; |
| } |
| |
| if (Count != cpp20::dynamic_extent && subview.size() != Count) { |
| return false; |
| } |
| |
| if (Count == cpp20::dynamic_extent && subview.size() != view.size() - Offset) { |
| return false; |
| } |
| |
| // Extent check. |
| if (Count != cpp20::dynamic_extent && subview.extent != Count) { |
| return false; |
| } |
| |
| if (Count == cpp20::dynamic_extent && T::extent != cpp20::dynamic_extent && |
| subview.extent != T::extent - Offset) { |
| return false; |
| } |
| |
| if (Count == cpp20::dynamic_extent && T::extent == cpp20::dynamic_extent && |
| subview.extent != cpp20::dynamic_extent) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| template <size_t Offset, size_t Count, typename T, |
| typename std::enable_if<(Offset + Count > 5), bool>::type = true> |
| constexpr bool span_subspan_static_extent_check_unfold(T view) { |
| return true; |
| } |
| |
| template <size_t Offset, size_t Count, typename T, |
| typename std::enable_if<Offset + Count <= 5, bool>::type = true> |
| constexpr bool span_subspan_static_extent_check_unfold(T view) { |
| if (!span_subspan_with_static_extent_instance_check<Offset, 5 - Offset>(view)) { |
| return false; |
| } |
| |
| return span_subspan_static_extent_check_unfold<Offset, Count + 1>(view); |
| } |
| |
| template <typename T, size_t Extent> |
| constexpr bool span_subspan_static_extent_check() { |
| std::array<T, 5> container = {}; |
| cpp20::span<T, Extent> view(container); |
| const cpp20::span<const T, Extent> const_view(container); |
| |
| constexpr auto check = [](auto& view) { |
| if (!span_subspan_with_static_extent_instance_check<0>(view)) { |
| return span_subspan_static_extent_check_unfold<0, 0>(view); |
| } |
| |
| if (!span_subspan_with_static_extent_instance_check<1>(view)) { |
| return span_subspan_static_extent_check_unfold<1, 0>(view); |
| } |
| |
| if (!span_subspan_with_static_extent_instance_check<2>(view)) { |
| return span_subspan_static_extent_check_unfold<2, 0>(view); |
| } |
| |
| if (!span_subspan_with_static_extent_instance_check<3>(view)) { |
| return span_subspan_static_extent_check_unfold<3, 0>(view); |
| } |
| |
| if (!span_subspan_with_static_extent_instance_check<4>(view)) { |
| return span_subspan_static_extent_check_unfold<4, 0>(view); |
| } |
| |
| if (!span_subspan_with_static_extent_instance_check<5>(view)) { |
| return span_subspan_static_extent_check_unfold<5, 0>(view); |
| } |
| |
| return true; |
| }; |
| |
| return check(view) && check(const_view); |
| } |
| |
| TEST(SpanTest, SubspanWithStaticExtent) { |
| static_assert(span_subspan_static_extent_check<int, cpp20::dynamic_extent>(), ""); |
| static_assert(span_subspan_static_extent_check<const int, cpp20::dynamic_extent>()); |
| static_assert(span_subspan_static_extent_check<int, 5>()); |
| static_assert(span_subspan_static_extent_check<const int, 5>()); |
| } |
| |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_first_check_with_dynamic_extent() { |
| std::array<T, ElementCount> kContainer = {}; |
| cpp20::span<T, Extent> view(kContainer); |
| const cpp20::span<const T, Extent> const_view(kContainer); |
| |
| constexpr auto check = [](auto& view) { |
| for (auto i = 0u; i < view.size(); ++i) { |
| auto first_view = view.first(i); |
| |
| if (!first_view.empty() && !are_pointers_equal(&first_view.front(), &view.front())) { |
| return false; |
| } |
| |
| if (first_view.size() != i) { |
| return false; |
| } |
| } |
| return true; |
| }; |
| |
| return check(view) && check(const_view); |
| } |
| |
| TEST(SpanTest, FirstWithDynamicExtent) { |
| static_assert(span_first_check_with_dynamic_extent<int, cpp20::dynamic_extent, 40>(), ""); |
| static_assert(span_first_check_with_dynamic_extent<const int, cpp20::dynamic_extent, 40>(), ""); |
| static_assert(span_first_check_with_dynamic_extent<int, 20>(), ""); |
| static_assert(span_first_check_with_dynamic_extent<const int, 20>(), ""); |
| } |
| |
| template <size_t Count, typename T, typename std::enable_if<Count == 0, bool>::type = true> |
| constexpr bool span_first_check_with_static_extent_impl(T& view) { |
| auto subview = view.template first<Count>(); |
| return are_pointers_equal(subview.data(), view.data()) && subview.size() == 0 && |
| subview.extent == 0; |
| } |
| |
| template <size_t Count, typename T, typename std::enable_if<Count != 0, bool>::type = true> |
| constexpr bool span_first_check_with_static_extent_impl(T& view) { |
| auto subview = view.template first<Count>(); |
| |
| if (subview.size() != Count || subview.extent != Count) { |
| return false; |
| } |
| |
| if (!are_pointers_equal(subview.data(), view.data())) { |
| return false; |
| } |
| |
| return span_first_check_with_static_extent_impl<Count - 1>(view); |
| } |
| |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_first_check_with_static_extent() { |
| std::array<T, ElementCount> container = {}; |
| cpp20::span<T, Extent> view(container); |
| const cpp20::span<T, Extent> const_view(container); |
| |
| return span_first_check_with_static_extent_impl<ElementCount>(view) && |
| span_first_check_with_static_extent_impl<ElementCount>(const_view); |
| } |
| |
| TEST(SpanTest, FirstWithStaticExtent) { |
| static_assert(span_first_check_with_static_extent<int, cpp20::dynamic_extent, 40>(), ""); |
| static_assert(span_first_check_with_static_extent<const int, cpp20::dynamic_extent, 40>(), ""); |
| static_assert(span_first_check_with_static_extent<int, 20>(), ""); |
| static_assert(span_first_check_with_static_extent<const int, 20>(), ""); |
| } |
| |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_last_check_with_dynamic_extent() { |
| std::array<T, ElementCount> kContainer = {}; |
| cpp20::span<T, Extent> view(kContainer); |
| const cpp20::span<const T, Extent> const_view(kContainer); |
| |
| constexpr auto check = [](auto& view) { |
| for (auto i = 0u; i < view.size(); ++i) { |
| auto last_view = view.last(i); |
| |
| if (!last_view.empty() && (!are_pointers_equal(&last_view.back(), &view.back()) || |
| !are_pointers_equal(&last_view.front(), &*(view.end() - i)))) { |
| return false; |
| } |
| |
| if (last_view.size() != i) { |
| return false; |
| } |
| } |
| return true; |
| }; |
| |
| return check(view) && check(const_view); |
| } |
| |
| TEST(SpanTest, LastWithDynamicExtent) { |
| static_assert(span_last_check_with_dynamic_extent<int, cpp20::dynamic_extent, 40>(), ""); |
| static_assert(span_last_check_with_dynamic_extent<const int, cpp20::dynamic_extent, 40>(), ""); |
| static_assert(span_last_check_with_dynamic_extent<int, 20>(), ""); |
| static_assert(span_last_check_with_dynamic_extent<const int, 20>(), ""); |
| } |
| |
| template <size_t Count, typename T, typename std::enable_if<Count == 0, bool>::type = true> |
| constexpr bool span_last_check_with_static_extent_impl(T& view) { |
| auto subview = view.template last<Count>(); |
| return are_pointers_equal(subview.data(), view.end()) && subview.size() == 0 && |
| subview.extent == 0; |
| } |
| |
| template <size_t Count, typename T, typename std::enable_if<Count != 0, bool>::type = true> |
| constexpr bool span_last_check_with_static_extent_impl(T& view) { |
| auto subview = view.template last<Count>(); |
| |
| if (subview.size() != Count || subview.extent != Count) { |
| return false; |
| } |
| |
| if (!are_pointers_equal(subview.data(), view.end() - Count)) { |
| return false; |
| } |
| |
| return span_last_check_with_static_extent_impl<Count - 1>(view); |
| } |
| |
| template <typename T, size_t Extent, size_t ElementCount = Extent> |
| constexpr bool span_last_check_with_static_extent() { |
| std::array<T, ElementCount> container = {}; |
| cpp20::span<T, Extent> view(container); |
| const cpp20::span<T, Extent> const_view(container); |
| |
| return span_last_check_with_static_extent_impl<ElementCount>(view) && |
| span_last_check_with_static_extent_impl<ElementCount>(const_view); |
| } |
| |
| TEST(SpanTest, LastWithStaticExtent) { |
| static_assert(span_last_check_with_static_extent<int, cpp20::dynamic_extent, 40>(), ""); |
| static_assert(span_last_check_with_static_extent<const int, cpp20::dynamic_extent, 40>(), ""); |
| static_assert(span_last_check_with_static_extent<int, 20>(), ""); |
| static_assert(span_last_check_with_static_extent<const int, 20>(), ""); |
| } |
| |
| #if __cpp_lib_span >= 202002L && !defined(LIB_STDCOMPAT_USE_POLYFILLS) |
| |
| TEST(SpanTest, IsAliasWhenStdIsAvailable) { |
| static_assert(std::is_same_v<cpp20::span<int>, std::span<int>>, |
| "cpp20::span must an alias of std::span when provided."); |
| static_assert(&cpp20::dynamic_extent == &std::dynamic_extent); |
| } |
| |
| #endif |
| |
| } // namespace |