From bdd7cdede5a1e64e15bf4d3a5238490deccce759 Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Mon, 25 Nov 2019 16:55:25 +0000 Subject: [PATCH] Add std::span implementation std::span is a really weird thing, behaving like a view of a contiguous range (except that it sometimes doesn't actually model the `view` concept), but built using C++17-era idioms rather than the shiny new ranges stuff, and living in a completely different section of the standard. This implementation of span is based on my own from https://github.com/tcbrindle/span, but simplified by assuming C++17 support and removing all the custom bounds-checking macro stuff -- this implementation just use uses plain old assert(). I believe it to be conforming to the current (pre-Belfast) working draft. The tests are taken (as usual) from CMCSTL2, which in turn took them from the Microsoft GSL span implementation. I have modified them to remove/comment out various things which fail -- correctly, I think? -- due to the numerous changes span has undergone during its many visits to L(E)WG. --- CMakeLists.txt | 2 + include/nanorange.hpp | 1 + include/nanorange/span.hpp | 396 ++++++++ test/CMakeLists.txt | 2 +- test/views/span.cpp | 1771 +++++++++++++++++------------------- 5 files changed, 1256 insertions(+), 916 deletions(-) create mode 100644 include/nanorange/span.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 35a4701..801bd12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,8 @@ else () ${CMAKE_CURRENT_SOURCE_DIR}/include/nanorange/memory/uninitialized_move.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/nanorange/memory/uninitialized_value_construct.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/nanorange/span.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/nanorange/views/all.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/nanorange/views/common.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/nanorange/views/counted.hpp diff --git a/include/nanorange.hpp b/include/nanorange.hpp index 3ddf0bd..6a6c369 100644 --- a/include/nanorange.hpp +++ b/include/nanorange.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/include/nanorange/span.hpp b/include/nanorange/span.hpp new file mode 100644 index 0000000..bc0ae45 --- /dev/null +++ b/include/nanorange/span.hpp @@ -0,0 +1,396 @@ + +/* +This is an implementation of C++20's std::span +http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf +*/ + +// Copyright Tristan Brindle 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file ../../LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#ifndef NANORANGE_SPAN_HPP_INCLUDED +#define NANORANGE_SPAN_HPP_INCLUDED + +#include +#include +#include + +#include + +NANO_BEGIN_NAMESPACE + +inline constexpr std::size_t dynamic_extent = -1; + +namespace span_ { + +template +class span; + +} + +using span_::span; + +namespace detail { + +template +struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* ptr, std::size_t /*unused*/) noexcept : ptr(ptr) + {} + + E* ptr = nullptr; + static inline constexpr std::size_t size = S; +}; + +template +struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* ptr, std::size_t size) noexcept + : ptr(ptr), size(size) + {} + + E* ptr = nullptr; + std::size_t size = 0; +}; + +template +struct is_span : std::false_type {}; + +template +struct is_span> : std::true_type {}; + +template +struct is_std_array : std::false_type {}; + +template +struct is_std_array> : std::true_type {}; + +template +struct has_size_and_data : std::false_type {}; + +template +struct has_size_and_data())), + decltype(std::data(std::declval()))>> + : std::true_type {}; + +template > +struct is_container { + static constexpr bool value = + !is_span::value && !is_std_array::value && + !std::is_array::value && has_size_and_data::value; +}; + +template +struct is_container_element_type_compatible : std::false_type {}; + +template +struct is_container_element_type_compatible< + T, E, std::void_t()))>> + : std::is_convertible< + std::remove_pointer_t()))> (*)[], + E (*)[]> {}; + +} // namespace detail + +namespace span_ { + +template +class span { + static_assert(std::is_object::value, + "A span's ElementType must be an object type (not a " + "reference type or void)"); + static_assert(!std::is_abstract::value, + "A span's ElementType cannot be an abstract class type"); + + using storage_type = detail::span_storage; + +public: + // constants and types + using element_type = ElementType; + using value_type = std::remove_cv_t; + using index_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = element_type*; + using const_pointer = const element_type*; + using reference = element_type&; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static constexpr index_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + template < + std::size_t E = Extent, + typename std::enable_if<(E == dynamic_extent || E == 0), int>::type = 0> + constexpr span() noexcept + {} + + constexpr span(pointer ptr, index_type count) : storage_(ptr, count) + { + assert(extent == dynamic_extent || count == extent); + } + + constexpr span(pointer first_elem, pointer last_elem) + : storage_(first_elem, last_elem - first_elem) + { + assert(extent == dynamic_extent || + last_elem - first_elem == static_cast(extent)); + } + + template ::value, + int>::type = 0> + constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N) + {} + + template &, ElementType>::value, + int> = 0> + constexpr span(std::array& arr) noexcept + : storage_(arr.data(), N) + {} + + template &, ElementType>::value, + int> = 0> + constexpr span(const std::array& arr) noexcept + : storage_(arr.data(), N) + {} + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value && + detail::is_container_element_type_compatible< + Container&, ElementType>::value, + int>::type = 0> + constexpr span(Container& cont) : storage_(std::data(cont), std::size(cont)) + {} + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value && + detail::is_container_element_type_compatible< + const Container&, ElementType>::value, + int>::type = 0> + constexpr span(const Container& cont) + : storage_(std::data(cont), std::size(cont)) + {} + + constexpr span(const span& other) noexcept = default; + + template ::value, + int>::type = 0> + constexpr span(const span& other) noexcept + : storage_(other.data(), other.size()) + {} + + ~span() noexcept = default; + + constexpr span& operator=(const span& other) noexcept = default; + + // [span.sub], span subviews + template + constexpr span first() const + { + assert(Count <= size()); + return {data(), Count}; + } + + template + constexpr span last() const + { + assert(Count <= size()); + return {data() + (size() - Count), Count}; + } + + template + using subspan_return_t = + span; + + template + constexpr subspan_return_t subspan() const + { + assert(Offset <= size() && + (Count == dynamic_extent || Offset + Count <= size())); + return {data() + Offset, + Count != dynamic_extent ? Count : size() - Offset}; + } + + constexpr span first(index_type count) const + { + assert(count <= size()); + return {data(), count}; + } + + constexpr span last(index_type count) const + { + assert(count <= size()); + return {data() + (size() - count), count}; + } + + constexpr span + subspan(index_type offset, index_type count = dynamic_extent) const + { + assert(offset <= size() && + (count == dynamic_extent || offset + count <= size())); + return {data() + offset, + count == dynamic_extent ? size() - offset : count}; + } + + // [span.obs], span observers + constexpr index_type size() const noexcept { return storage_.size; } + + constexpr index_type size_bytes() const noexcept + { + return size() * sizeof(element_type); + } + + [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + constexpr reference operator[](index_type idx) const + { + assert(idx < size()); + return *(data() + idx); + } + + constexpr reference front() const + { + assert(!empty()); + return *data(); + } + + constexpr reference back() const + { + assert(!empty()); + return *(data() + (size() - 1)); + } + + constexpr pointer data() const noexcept { return storage_.ptr; } + + // [span.iterators], span iterator support + 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()); + } + + constexpr reverse_iterator rend() const noexcept + { + 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()); + } + + // FIXME: should just be span, not span&& + friend constexpr iterator begin(span&& s) noexcept { return s.begin(); } + + friend constexpr iterator end(span&& s) noexcept { return s.end(); } + +private: + storage_type storage_{}; +}; + +/* Deduction Guides */ +template +span(T (&)[N])->span; + +template +span(std::array&)->span; + +template +span(const std::array&)->span; + +template +span(Container&)->span; + +template +span(const Container&)->span; + +} // namespace span_ + +template +span +as_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template < + class ElementType, size_t Extent, + typename std::enable_if::value, int>::type = 0> +span +as_writable_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template +constexpr T& get(span s) +{ + static_assert(Extent != dynamic_extent && I < Extent); + return s[I]; +} + +NANO_END_NAMESPACE + +namespace std { + +template +class tuple_size<::nano::span> + : public integral_constant {}; + +template +class tuple_size<::nano::span>; // not defined + +template +class tuple_element> { +public: + static_assert(Extent != ::nano::dynamic_extent && + I < Extent); + using type = ElementType; +}; + +} // end namespace std + +#endif // TCB_SPAN_HPP_INCLUDED diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eec5326..4e7b50f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -182,7 +182,7 @@ add_executable(test_nanorange #views/repeat_view.cpp views/reverse_view.cpp views/single_view.cpp - #views/span.cpp + views/span.cpp views/split_view.cpp views/subrange.cpp #views/take_exactly_view.cpp diff --git a/test/views/span.cpp b/test/views/span.cpp index c441a57..b996007 100644 --- a/test/views/span.cpp +++ b/test/views/span.cpp @@ -14,9 +14,6 @@ // /////////////////////////////////////////////////////////////////////////////// -#include -#include - #include #include #include @@ -25,1034 +22,974 @@ #include #include #include -#include "../simple_test.hpp" +#include "../catch.hpp" + +// FIXME: match_results forward decl causes problems with libstdc++ +#ifdef _GLIBCXX_RELEASE +#define NANORANGE_NO_STD_FORWARD_DECLARATIONS +#endif +#include +#include -namespace ranges = std::experimental::ranges; -using ranges::ext::span; -using ranges::ext::__span::narrow_cast; -using ranges::ext::make_span; -using ranges::ext::as_bytes; -using ranges::ext::as_writeable_bytes; +namespace ranges = nano::ranges; +using ranges::span; +using ranges::as_bytes; +using ranges::as_writable_bytes; namespace { - struct BaseClass {}; - struct DerivedClass : BaseClass {}; -} +struct BaseClass {}; +struct DerivedClass : BaseClass {}; -void test_case_default_constructor() +auto make_span = [](auto&&... args) + -> decltype(span(std::forward(args)...)) { - { - span s; - CHECK((s.size() == 0 && s.data() == nullptr)); + return span(std::forward(args)...); +}; - span cs; - CHECK((cs.size() == 0 && cs.data() == nullptr)); - } - - { - span s; - CHECK((s.size() == 0 && s.data() == nullptr)); - - span cs; - CHECK((cs.size() == 0 && cs.data() == nullptr)); - } - - { - span s; - CHECK((s.size() == 1 && s.data() == nullptr)); - } - - { - span s{}; - CHECK((s.size() == 0 && s.data() == nullptr)); - - span cs{}; - CHECK((cs.size() == 0 && cs.data() == nullptr)); - } +void test_case_default_constructor() +{ + { + span s; + CHECK((s.size() == 0 && s.data() == nullptr)); + + span cs; + CHECK((cs.size() == 0 && cs.data() == nullptr)); + } + + { + span s; + CHECK((s.size() == 0 && s.data() == nullptr)); + + span cs; + CHECK((cs.size() == 0 && cs.data() == nullptr)); + } + + { +// span s; +// CHECK((s.size() == 1 && s.data() == nullptr)); + } + + { + span s{}; + CHECK((s.size() == 0 && s.data() == nullptr)); + + span cs{}; + CHECK((cs.size() == 0 && cs.data() == nullptr)); + } } void test_case_size_optimization() { - { - span s; - CHECK(sizeof(s) == sizeof(int*) + sizeof(std::ptrdiff_t)); - } - - { - span s; - CHECK(sizeof(s) == sizeof(int*)); - } + { + span s; + CHECK(sizeof(s) == sizeof(int*) + sizeof(std::ptrdiff_t)); + } + + { + span s; + CHECK(sizeof(s) == sizeof(int*)); + } } void test_case_from_nullptr_constructor() { - // This implementation doesn't support the silly nullptr_t constructor. - static_assert(!std::is_constructible, std::nullptr_t>::value); - static_assert(!std::is_constructible, std::nullptr_t>::value); - - static_assert(!std::is_constructible, std::nullptr_t>::value); - static_assert(!std::is_constructible, std::nullptr_t>::value); - - static_assert(!std::is_constructible, std::nullptr_t>::value); - static_assert(!std::is_constructible, std::nullptr_t>::value); + // This implementation doesn't support the silly nullptr_t constructor. + static_assert(!std::is_constructible, std::nullptr_t>::value); + static_assert( + !std::is_constructible, std::nullptr_t>::value); + + static_assert(!std::is_constructible, std::nullptr_t>::value); + static_assert( + !std::is_constructible, std::nullptr_t>::value); + + static_assert(!std::is_constructible, std::nullptr_t>::value); + static_assert( + !std::is_constructible, std::nullptr_t>::value); } void test_case_from_nullptr_size_constructor() { - { - span s{nullptr, static_cast::index_type>(0)}; - CHECK((s.size() == 0 && s.data() == nullptr)); + { + span s{nullptr, static_cast::index_type>(0)}; + CHECK((s.size() == 0 && s.data() == nullptr)); - span cs{nullptr, static_cast::index_type>(0)}; - CHECK((cs.size() == 0 && cs.data() == nullptr)); - } + span cs{nullptr, static_cast::index_type>(0)}; + CHECK((cs.size() == 0 && cs.data() == nullptr)); + } - { - span s{nullptr, static_cast::index_type>(0)}; - CHECK((s.size() == 0 && s.data() == nullptr)); + { + span s{nullptr, static_cast::index_type>(0)}; + CHECK((s.size() == 0 && s.data() == nullptr)); - span cs{nullptr, static_cast::index_type>(0)}; - CHECK((cs.size() == 0 && cs.data() == nullptr)); - } + span cs{nullptr, static_cast::index_type>(0)}; + CHECK((cs.size() == 0 && cs.data() == nullptr)); + } - { - span s{nullptr, static_cast::index_type>(0)}; - CHECK((s.size() == 0 && s.data() == nullptr)); + { + span s{nullptr, static_cast::index_type>(0)}; + CHECK((s.size() == 0 && s.data() == nullptr)); - span cs{nullptr, static_cast::index_type>(0)}; - CHECK((cs.size() == 0 && cs.data() == nullptr)); - } + span cs{nullptr, static_cast::index_type>(0)}; + CHECK((cs.size() == 0 && cs.data() == nullptr)); + } } void test_case_from_pointer_size_constructor() { - int arr[4] = {1, 2, 3, 4}; - - { - span s{&arr[0], 2}; - CHECK((s.size() == 2 && s.data() == &arr[0])); - CHECK((s[0] == 1 && s[1] == 2)); - } - - { - span s{&arr[0], 2}; - CHECK((s.size() == 2 && s.data() == &arr[0])); - CHECK((s[0] == 1 && s[1] == 2)); - } - - { - int* p = nullptr; - span s{p, static_cast::index_type>(0)}; - CHECK((s.size() == 0 && s.data() == nullptr)); - } - - { - auto s = make_span(&arr[0], 2); - CHECK((s.size() == 2 && s.data() == &arr[0])); - CHECK((s[0] == 1 && s[1] == 2)); - } - - { - int* p = nullptr; - auto s = make_span(p, static_cast::index_type>(0)); - CHECK((s.size() == 0 && s.data() == nullptr)); - } - - { - int i = 42; - span s{&i, 0}; - CHECK((s.size() == 0 && s.data() == &i)); - - span cs{&i, 0}; - CHECK((s.size() == 0 && s.data() == &i)); - } + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], 2}; + CHECK((s.size() == 2 && s.data() == &arr[0])); + CHECK((s[0] == 1 && s[1] == 2)); + } + + { + span s{&arr[0], 2}; + CHECK((s.size() == 2 && s.data() == &arr[0])); + CHECK((s[0] == 1 && s[1] == 2)); + } + + { + int* p = nullptr; + span s{p, static_cast::index_type>(0)}; + CHECK((s.size() == 0 && s.data() == nullptr)); + } + + { + auto s = make_span(&arr[0], 2); + CHECK((s.size() == 2 && s.data() == &arr[0])); + CHECK((s[0] == 1 && s[1] == 2)); + } + + { + int* p = nullptr; + auto s = make_span(p, static_cast::index_type>(0)); + CHECK((s.size() == 0 && s.data() == nullptr)); + } } void test_case_from_pointer_pointer_constructor() { - int arr[4] = {1, 2, 3, 4}; - - { - span s{&arr[0], &arr[2]}; - CHECK((s.size() == 2 && s.data() == &arr[0])); - CHECK((s[0] == 1 && s[1] == 2)); - } - - { - span s{&arr[0], &arr[2]}; - CHECK((s.size() == 2 && s.data() == &arr[0])); - CHECK((s[0] == 1 && s[1] == 2)); - } - - { - span s{&arr[0], &arr[0]}; - CHECK((s.size() == 0 && s.data() == &arr[0])); - } - - { - span s{&arr[0], &arr[0]}; - CHECK((s.size() == 0 && s.data() == &arr[0])); - } - - // this will fail the std::distance() precondition, which asserts on MSVC debug builds - //{ - // auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; - // CHECK_THROWS_AS(workaround_macro(), fail_fast); - //} - - // this will fail the std::distance() precondition, which asserts on MSVC debug builds - //{ - // int* p = nullptr; - // auto workaround_macro = [&]() { span s{&arr[0], p}; }; - // CHECK_THROWS_AS(workaround_macro(), fail_fast); - //} - - { - int* p = nullptr; - span s{p, p}; - CHECK((s.size() == 0 && s.data() == nullptr)); - } - - { - int* p = nullptr; - span s{p, p}; - CHECK((s.size() == 0 && s.data() == nullptr)); - } - - // this will fail the std::distance() precondition, which asserts on MSVC debug builds - //{ - // int* p = nullptr; - // auto workaround_macro = [&]() { span s{&arr[0], p}; }; - // CHECK_THROWS_AS(workaround_macro(), fail_fast); - //} - - { - auto s = make_span(&arr[0], &arr[2]); - CHECK((s.size() == 2 && s.data() == &arr[0])); - CHECK((s[0] == 1 && s[1] == 2)); - } - - { - auto s = make_span(&arr[0], &arr[0]); - CHECK((s.size() == 0 && s.data() == &arr[0])); - } - - { - int* p = nullptr; - auto s = make_span(p, p); - CHECK((s.size() == 0 && s.data() == nullptr)); - } + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], &arr[2]}; + CHECK((s.size() == 2 && s.data() == &arr[0])); + CHECK((s[0] == 1 && s[1] == 2)); + } + + { + span s{&arr[0], &arr[2]}; + CHECK((s.size() == 2 && s.data() == &arr[0])); + CHECK((s[0] == 1 && s[1] == 2)); + } + + { + span s{&arr[0], &arr[0]}; + CHECK((s.size() == 0 && s.data() == &arr[0])); + } + + { + span s{&arr[0], &arr[0]}; + CHECK((s.size() == 0 && s.data() == &arr[0])); + } + + // this will fail the std::distance() precondition, which asserts on MSVC debug builds + //{ + // auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + // CHECK_THROWS_AS(workaround_macro(), fail_fast); + //} + + // this will fail the std::distance() precondition, which asserts on MSVC debug builds + //{ + // int* p = nullptr; + // auto workaround_macro = [&]() { span s{&arr[0], p}; }; + // CHECK_THROWS_AS(workaround_macro(), fail_fast); + //} + + { + int* p = nullptr; + span s{p, p}; + CHECK((s.size() == 0 && s.data() == nullptr)); + } + + { + int* p = nullptr; + span s{p, p}; + CHECK((s.size() == 0 && s.data() == nullptr)); + } + + // this will fail the std::distance() precondition, which asserts on MSVC debug builds + //{ + // int* p = nullptr; + // auto workaround_macro = [&]() { span s{&arr[0], p}; }; + // CHECK_THROWS_AS(workaround_macro(), fail_fast); + //} + + { + auto s = make_span(&arr[0], &arr[2]); + CHECK((s.size() == 2 && s.data() == &arr[0])); + CHECK((s[0] == 1 && s[1] == 2)); + } + + { + auto s = make_span(&arr[0], &arr[0]); + CHECK((s.size() == 0 && s.data() == &arr[0])); + } + + { + int* p = nullptr; + auto s = make_span(p, p); + CHECK((s.size() == 0 && s.data() == nullptr)); + } } void test_case_from_array_constructor() { - int arr[5] = {1, 2, 3, 4, 5}; - - { - span s{arr}; - CHECK((s.size() == 5 && s.data() == &arr[0])); - } - - { - span s{arr}; - CHECK((s.size() == 5 && s.data() == &arr[0])); - } - - int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; - - static_assert(!std::is_constructible, int(&)[5]>::value); - static_assert(!std::is_constructible, int(&)[5]>::value); - static_assert(!std::is_constructible, decltype((arr2d))>::value); - static_assert(!std::is_constructible, decltype((arr2d))>::value); - static_assert(!std::is_constructible, decltype((arr2d))>::value); - - { - span s{&(arr2d[0]), 1}; - CHECK((s.size() == 1 && s.data() == &arr2d[0])); - } - - int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - - static_assert(!std::is_constructible, decltype((arr3d))>::value); - static_assert(!std::is_constructible, decltype((arr3d))>::value); - static_assert(!std::is_constructible, decltype((arr3d))>::value); - static_assert(!std::is_constructible, decltype((arr3d))>::value); - - { - span s{&arr3d[0], 1}; - CHECK((s.size() == 1 && s.data() == &arr3d[0])); - } - - { - auto s = make_span(arr); - CHECK((s.size() == 5 && s.data() == &arr[0])); - } - - { - auto s = make_span(&(arr2d[0]), 1); - CHECK((s.size() == 1 && s.data() == &arr2d[0])); - } - - { - auto s = make_span(&arr3d[0], 1); - CHECK((s.size() == 1 && s.data() == &arr3d[0])); - } + int arr[5] = {1, 2, 3, 4, 5}; + + { + span s{arr}; + CHECK((s.size() == 5 && s.data() == &arr[0])); + } + + { + span s{arr}; + CHECK((s.size() == 5 && s.data() == &arr[0])); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + static_assert(!std::is_constructible, int(&)[5]>::value); + static_assert(!std::is_constructible, int(&)[5]>::value); + static_assert(!std::is_constructible, decltype((arr2d))>::value); + static_assert( + !std::is_constructible, decltype((arr2d))>::value); + static_assert( + !std::is_constructible, decltype((arr2d))>::value); + + { + span s{&(arr2d[0]), 1}; + CHECK((s.size() == 1 && s.data() == &arr2d[0])); + } + + int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + static_assert(!std::is_constructible, decltype((arr3d))>::value); + static_assert( + !std::is_constructible, decltype((arr3d))>::value); + static_assert( + !std::is_constructible, decltype((arr3d))>::value); + static_assert( + !std::is_constructible, decltype((arr3d))>::value); + + { + span s{&arr3d[0], 1}; + CHECK((s.size() == 1 && s.data() == &arr3d[0])); + } + + { + auto s = make_span(arr); + CHECK((s.size() == 5 && s.data() == &arr[0])); + } + + { + auto s = make_span(&(arr2d[0]), 1); + CHECK((s.size() == 1 && s.data() == &arr2d[0])); + } + + { + auto s = make_span(&arr3d[0], 1); + CHECK((s.size() == 1 && s.data() == &arr3d[0])); + } } void test_case_from_std_array_constructor() { - std::array arr = {1, 2, 3, 4}; - - { - span s{arr}; - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - - span cs{arr}; - CHECK((cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data())); - } - - { - span s{arr}; - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - - span cs{arr}; - CHECK((cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data())); - } - - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - - { - auto get_an_array = []() -> std::array { return {1, 2, 3, 4}; }; - auto take_a_span = [](span) {}; - take_a_span(get_an_array()); - } - - { - auto get_an_array = []() -> std::array { return {1, 2, 3, 4}; }; - auto take_a_span = [](span) {}; - take_a_span(get_an_array()); - } + std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + CHECK((s.size() == arr.size() && + s.data() == arr.data())); + + span cs{arr}; + CHECK((cs.size() == arr.size() && + cs.data() == arr.data())); + } + + { + span s{arr}; + CHECK((s.size() == arr.size() && + s.data() == arr.data())); + + span cs{arr}; + CHECK((cs.size() == arr.size() && + cs.data() == arr.data())); + } + + static_assert(!std::is_constructible, decltype((arr))>::value); + static_assert( + !std::is_constructible, decltype((arr))>::value); + static_assert(!std::is_constructible, decltype((arr))>::value); + static_assert( + !std::is_constructible, decltype((arr))>::value); + static_assert(!std::is_constructible, decltype((arr))>::value); + +#if 0 + { + auto get_an_array = []() -> std::array { return {1, 2, 3, 4}; }; + auto take_a_span = [](span) {}; + take_a_span(get_an_array()); + } +#endif - { - auto s = make_span(arr); - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - } + { + auto get_an_array = []() -> std::array { return {1, 2, 3, 4}; }; + auto take_a_span = [](span) {}; + take_a_span(get_an_array()); + } + + { + auto s = make_span(arr); + CHECK((s.size() == arr.size() && + s.data() == arr.data())); + } } void test_case_from_const_std_array_constructor() { - const std::array arr = {1, 2, 3, 4}; - - { - span s{arr}; - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - } - - { - span s{arr}; - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - } - - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - - { - auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; - auto take_a_span = [](span) {}; - take_a_span(get_an_array()); - } - - { - auto s = make_span(arr); - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - } + const std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + CHECK((s.size() == arr.size() && + s.data() == arr.data())); + } + + { + span s{arr}; + CHECK((s.size() == arr.size() && + s.data() == arr.data())); + } + + static_assert( + !std::is_constructible, decltype((arr))>::value); + static_assert( + !std::is_constructible, decltype((arr))>::value); + static_assert( + !std::is_constructible, decltype((arr))>::value); + + { + auto get_an_array = []() -> const std::array { + return {1, 2, 3, 4}; + }; + auto take_a_span = [](span) {}; + take_a_span(get_an_array()); + } + + { + auto s = make_span(arr); + CHECK((s.size() == arr.size() && + s.data() == arr.data())); + } } - +#if 0 void test_case_from_std_array_const_constructor() { - std::array arr = {1, 2, 3, 4}; - - { - span s{arr}; - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - } - - { - span s{arr}; - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - } - - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - static_assert(!std::is_constructible, decltype((arr))>::value); - - { - auto s = make_span(arr); - CHECK((s.size() == narrow_cast(arr.size()) && s.data() == arr.data())); - } + std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + CHECK((s.size() == arr.size() && + s.data() == arr.data())); + } + + { + span s{arr}; + CHECK((s.size() == arr.size() && + s.data() == arr.data())); + } + + static_assert( + !std::is_constructible, decltype((arr))>::value); + static_assert( + !std::is_constructible, decltype((arr))>::value); + static_assert( + !std::is_constructible, decltype((arr))>::value); + static_assert(!std::is_constructible, decltype((arr))>::value); + + { + auto s = make_span(arr); + CHECK((s.size() == narrow_cast(arr.size()) && + s.data() == arr.data())); + } } +#endif void test_case_from_container_constructor() { - std::vector v = {1, 2, 3}; - const std::vector cv = v; - - { - span s{v}; - CHECK((s.size() == narrow_cast(v.size()) && s.data() == v.data())); - - span cs{v}; - CHECK((cs.size() == narrow_cast(v.size()) && cs.data() == v.data())); - } - - std::string str = "hello"; - const std::string cstr = "hello"; - - { - span s{str}; - CHECK((s.size() == narrow_cast(str.size()) && s.data() == str.data())); - } - - { - auto get_temp_string = []() -> std::string { return {}; }; - auto use_span = [](span) {}; - use_span(get_temp_string()); - } - - { - span cs{str}; - CHECK((cs.size() == narrow_cast(str.size()) && cs.data() == str.data())); - } - - { - auto get_temp_string = []() -> std::string { return {}; }; - auto use_span = [](span) {}; - use_span(get_temp_string()); - } - - { - static_assert(!std::is_constructible, decltype((cstr))>::value); - span cs{cstr}; - CHECK((cs.size() == narrow_cast(cstr.size()) && - cs.data() == cstr.data())); - } - - { - auto get_temp_vector = []() -> std::vector { return {}; }; - auto use_span = [](span) {}; - use_span(get_temp_vector()); - } - - { - auto get_temp_vector = []() -> std::vector { return {}; }; - auto use_span = [](span) {}; - use_span(get_temp_vector()); - } - - static_assert(!std::is_convertible, span>::value); - - { - auto get_temp_string = []() -> const std::string { return {}; }; - auto use_span = [](span s) { static_cast(s); }; - use_span(get_temp_string()); - use_span(span(get_temp_string())); - } - - static_assert(!std::is_constructible, std::map&>::value); + std::vector v = {1, 2, 3}; + const std::vector cv = v; + + { + span s{v}; + CHECK((s.size() == v.size() && + s.data() == v.data())); + + span cs{v}; + CHECK((cs.size() == v.size() && + cs.data() == v.data())); + } + + std::string str = "hello"; + const std::string cstr = "hello"; + + { + span s{str}; + CHECK((s.size() == str.size() && + s.data() == str.data())); + } +#if 0 + { + auto get_temp_string = []() -> std::string { return {}; }; + auto use_span = [](span) {}; + use_span(get_temp_string()); + } +#endif - { - auto s = make_span(v); - CHECK((s.size() == narrow_cast(v.size()) && s.data() == v.data())); + { + span cs{str}; + CHECK((cs.size() == str.size() && + cs.data() == str.data())); + } + + { + auto get_temp_string = []() -> std::string { return {}; }; + auto use_span = [](span) {}; + use_span(get_temp_string()); + } + + { + static_assert( + !std::is_constructible, decltype((cstr))>::value); + span cs{cstr}; + CHECK((cs.size() == cstr.size() && + cs.data() == cstr.data())); + } +#if 0 + { + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_span = [](span) {}; + use_span(get_temp_vector()); + } +#endif - auto cs = make_span(cv); - CHECK((cs.size() == narrow_cast(cv.size()) && cs.data() == cv.data())); - } + { + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_span = [](span) {}; + use_span(get_temp_vector()); + } + + static_assert( + !std::is_convertible, span>::value); + + { + auto get_temp_string = []() -> const std::string { return {}; }; + auto use_span = [](span s) { static_cast(s); }; + use_span(get_temp_string()); + use_span(span(get_temp_string())); + } + + static_assert( + !std::is_constructible, std::map&>::value); + + { + auto s = make_span(v); + CHECK((s.size() == v.size() && + s.data() == v.data())); + + auto cs = make_span(cv); + CHECK((cs.size() == cv.size() && + cs.data() == cv.data())); + } } void test_case_from_convertible_span_constructor() { - { - span avd; - span avcd = avd; - static_cast(avcd); - } - - static_assert(!std::is_constructible, span>::value); - static_assert(!std::is_constructible, span>::value); - static_assert(!std::is_constructible, span>::value); - static_assert(!std::is_constructible, span>::value); - static_assert(!std::is_constructible, span>::value); + { + span avd; + span avcd = avd; + static_cast(avcd); + } + + static_assert( + !std::is_constructible, span>::value); + static_assert( + !std::is_constructible, span>::value); + static_assert(!std::is_constructible, span>::value); + static_assert( + !std::is_constructible, span>::value); + static_assert(!std::is_constructible, span>::value); } void test_case_copy_move_and_assignment() { - span s1; - CHECK(s1.empty()); + span s1; + CHECK(s1.empty()); - int arr[] = {3, 4, 5}; + int arr[] = {3, 4, 5}; - span s2 = arr; - CHECK((s2.size() == 3 && s2.data() == &arr[0])); + span s2 = arr; + CHECK((s2.size() == 3 && s2.data() == &arr[0])); - s2 = s1; - CHECK(s2.empty()); + s2 = s1; + CHECK(s2.empty()); - auto get_temp_span = [&]() -> span { return {&arr[1], 2}; }; - auto use_span = [&](span s) { CHECK((s.size() == 2 && s.data() == &arr[1])); }; - use_span(get_temp_span()); + auto get_temp_span = [&]() -> span { return {&arr[1], 2}; }; + auto use_span = [&](span s) { + CHECK((s.size() == 2 && s.data() == &arr[1])); + }; + use_span(get_temp_span()); - s1 = get_temp_span(); - CHECK((s1.size() == 2 && s1.data() == &arr[1])); + s1 = get_temp_span(); + CHECK((s1.size() == 2 && s1.data() == &arr[1])); } void test_case_class_template_argument_deduction() { #ifdef __cpp_deduction_guides - { - int arr[] = {1, 2, 3, 4, 5}; - { - span s{arr}; - static_assert(ranges::Same, decltype(s)>); - } - { - span s{ranges::begin(arr), ranges::size(arr)}; - static_assert(ranges::Same, decltype(s)>); - } - { - span s{ranges::begin(arr), ranges::end(arr)}; - static_assert(ranges::Same, decltype(s)>); - } - } - { - std::array arr = {1, 2, 3, 4, 5}; - { - span s{arr}; - static_assert(ranges::Same, decltype(s)>); - } + { + int arr[] = {1, 2, 3, 4, 5}; + { + span s{arr}; + static_assert(ranges::same_as, decltype(s)>); + } + { + span s{ranges::begin(arr), ranges::size(arr)}; + static_assert(ranges::same_as, decltype(s)>); + } + { + span s{ranges::begin(arr), ranges::end(arr)}; + static_assert(ranges::same_as, decltype(s)>); + } + } + { + std::array arr = {1, 2, 3, 4, 5}; + { + span s{arr}; + static_assert(ranges::same_as, decltype(s)>); + } +#if 0 // TODO: reactivate these cases on the span_updates branch { span s{ranges::begin(arr), ranges::size(arr)}; - static_assert(ranges::Same, decltype(s)>); + static_assert(ranges::same_as, decltype(s)>); } { span s{ranges::begin(arr), ranges::end(arr)}; - static_assert(ranges::Same, decltype(s)>); - } - } - { - std::vector vec = {1, 2, 3, 4, 5}; - { - span s{vec}; - static_assert(ranges::Same, decltype(s)>); + static_assert(ranges::same_as, decltype(s)>); } - } +#endif + } + { + std::vector vec = {1, 2, 3, 4, 5}; + { + span s{vec}; + static_assert(ranges::same_as, decltype(s)>); + } + } #endif } void test_case_first() { - int arr[5] = {1, 2, 3, 4, 5}; - - { - span av = arr; - CHECK(av.first<2>().size() == 2); - CHECK(av.first(2).size() == 2); - } - - { - span av = arr; - CHECK(av.first<0>().size() == 0); - CHECK(av.first(0).size() == 0); - } - - { - span av = arr; - CHECK(av.first<5>().size() == 5); - CHECK(av.first(5).size() == 5); - } - - { - span av; - CHECK(av.first<0>().size() == 0); - CHECK(av.first(0).size() == 0); - } + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK(av.first<2>().size() == 2); + CHECK(av.first(2).size() == 2); + } + + { + span av = arr; + CHECK(av.first<0>().size() == 0); + CHECK(av.first(0).size() == 0); + } + + { + span av = arr; + CHECK(av.first<5>().size() == 5); + CHECK(av.first(5).size() == 5); + } + + { + span av; + CHECK(av.first<0>().size() == 0); + CHECK(av.first(0).size() == 0); + } } void test_case_last() { - int arr[5] = {1, 2, 3, 4, 5}; - - { - span av = arr; - CHECK(av.last<2>().size() == 2); - CHECK(av.last(2).size() == 2); - } - - { - span av = arr; - CHECK(av.last<0>().size() == 0); - CHECK(av.last(0).size() == 0); - } - - { - span av = arr; - CHECK(av.last<5>().size() == 5); - CHECK(av.last(5).size() == 5); - } - - { - span av; - CHECK(av.last<0>().size() == 0); - CHECK(av.last(0).size() == 0); - } + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK(av.last<2>().size() == 2); + CHECK(av.last(2).size() == 2); + } + + { + span av = arr; + CHECK(av.last<0>().size() == 0); + CHECK(av.last(0).size() == 0); + } + + { + span av = arr; + CHECK(av.last<5>().size() == 5); + CHECK(av.last(5).size() == 5); + } + + { + span av; + CHECK(av.last<0>().size() == 0); + CHECK(av.last(0).size() == 0); + } } void test_case_subspan() { - int arr[5] = {1, 2, 3, 4, 5}; - - { - span av = arr; - CHECK((av.subspan<2, 2>().size() == 2)); - CHECK(av.subspan(2, 2).size() == 2); - CHECK(av.subspan(2, 3).size() == 3); - } - - { - span av = arr; - CHECK((av.subspan<0, 0>().size() == 0)); - CHECK(av.subspan(0, 0).size() == 0); - } - - { - span av = arr; - CHECK((av.subspan<0, 5>().size() == 5)); - CHECK(av.subspan(0, 5).size() == 5); - } - - { - span av = arr; - CHECK((av.subspan<4, 0>().size() == 0)); - CHECK(av.subspan(4, 0).size() == 0); - CHECK(av.subspan(5, 0).size() == 0); - } - - { - span av; - CHECK((av.subspan<0, 0>().size() == 0)); - CHECK(av.subspan(0, 0).size() == 0); - } - - { - span av; - CHECK(av.subspan(0).size() == 0); - } - - { - span av = arr; - CHECK(av.subspan(0).size() == 5); - CHECK(av.subspan(1).size() == 4); - CHECK(av.subspan(4).size() == 1); - CHECK(av.subspan(5).size() == 0); - const auto av2 = av.subspan(1); - for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); - } - - { - span av = arr; - CHECK(av.subspan(0).size() == 5); - CHECK(av.subspan(1).size() == 4); - CHECK(av.subspan(4).size() == 1); - CHECK(av.subspan(5).size() == 0); - const auto av2 = av.subspan(1); - for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); - } + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.subspan<2, 2>().size() == 2)); + CHECK(av.subspan(2, 2).size() == 2); + CHECK(av.subspan(2, 3).size() == 3); + } + + { + span av = arr; + CHECK((av.subspan<0, 0>().size() == 0)); + CHECK(av.subspan(0, 0).size() == 0); + } + + { + span av = arr; + CHECK((av.subspan<0, 5>().size() == 5)); + CHECK(av.subspan(0, 5).size() == 5); + } + + { + span av = arr; + CHECK((av.subspan<4, 0>().size() == 0)); + CHECK(av.subspan(4, 0).size() == 0); + CHECK(av.subspan(5, 0).size() == 0); + } + + { + span av; + CHECK((av.subspan<0, 0>().size() == 0)); + CHECK(av.subspan(0, 0).size() == 0); + } + + { + span av; + CHECK(av.subspan(0).size() == 0); + } + + { + span av = arr; + CHECK(av.subspan(0).size() == 5); + CHECK(av.subspan(1).size() == 4); + CHECK(av.subspan(4).size() == 1); + CHECK(av.subspan(5).size() == 0); + const auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) + CHECK(av2[i] == i + 2); + } + + { + span av = arr; + CHECK(av.subspan(0).size() == 5); + CHECK(av.subspan(1).size() == 4); + CHECK(av.subspan(4).size() == 1); + CHECK(av.subspan(5).size() == 0); + const auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) + CHECK(av2[i] == i + 2); + } } void test_case_iterator_value_init() { - span::iterator it1{}; - span::iterator it2{}; - CHECK(it1 == it2); + span::iterator it1{}; + span::iterator it2{}; + CHECK(it1 == it2); } void test_case_iterator_comparisons() { - int a[] = {1, 2, 3, 4}; - { - span s = a; - span::iterator it = s.begin(); - auto it2 = it + 1; - - CHECK(it == it); - CHECK(it == s.begin()); - CHECK(s.begin() == it); - - CHECK(it != it2); - CHECK(it2 != it); - CHECK(it != s.end()); - CHECK(it2 != s.end()); - CHECK(s.end() != it); - - CHECK(it < it2); - CHECK(it <= it2); - CHECK(it2 <= s.end()); - CHECK(it < s.end()); - - CHECK(it2 > it); - CHECK(it2 >= it); - CHECK(s.end() > it2); - CHECK(s.end() >= it2); - } + int a[] = {1, 2, 3, 4}; + { + span s = a; + span::iterator it = s.begin(); + auto it2 = it + 1; + + CHECK(it == it); + CHECK(it == s.begin()); + CHECK(s.begin() == it); + + CHECK(it != it2); + CHECK(it2 != it); + CHECK(it != s.end()); + CHECK(it2 != s.end()); + CHECK(s.end() != it); + + CHECK(it < it2); + CHECK(it <= it2); + CHECK(it2 <= s.end()); + CHECK(it < s.end()); + + CHECK(it2 > it); + CHECK(it2 >= it); + CHECK(s.end() > it2); + CHECK(s.end() >= it2); + } } void test_case_begin_end() { - { - int a[] = {1, 2, 3, 4}; - span s = a; - - span::iterator it = s.begin(); - span::iterator it2 = std::begin(s); - CHECK(it == it2); - - it = s.end(); - it2 = std::end(s); - CHECK(it == it2); - } - - { - int a[] = {1, 2, 3, 4}; - span s = a; - - auto it = s.begin(); - auto first = it; - CHECK(it == first); - CHECK(*it == 1); - - auto beyond = s.end(); - CHECK(it != beyond); - - CHECK((beyond - first) == 4); - CHECK((first - first) == 0); - CHECK((beyond - beyond) == 0); - - ++it; - CHECK((it - first) == 1); - CHECK(*it == 2); - *it = 22; - CHECK(*it == 22); - CHECK((beyond - it) == 3); - - it = first; - CHECK(it == first); - while (it != s.end()) { - *it = 5; - ++it; - } - - CHECK(it == beyond); - CHECK((it - beyond) == 0); - - for (const auto& n : s) { - CHECK(n == 5); - } - } + { + int a[] = {1, 2, 3, 4}; + span s = a; + + span::iterator it = s.begin(); + span::iterator it2 = std::begin(s); + CHECK(it == it2); + + it = s.end(); + it2 = std::end(s); + CHECK(it == it2); + } + + { + int a[] = {1, 2, 3, 4}; + span s = a; + + auto it = s.begin(); + auto first = it; + CHECK(it == first); + CHECK(*it == 1); + + auto beyond = s.end(); + CHECK(it != beyond); + + CHECK((beyond - first) == 4); + CHECK((first - first) == 0); + CHECK((beyond - beyond) == 0); + + ++it; + CHECK((it - first) == 1); + CHECK(*it == 2); + *it = 22; + CHECK(*it == 22); + CHECK((beyond - it) == 3); + + it = first; + CHECK(it == first); + while (it != s.end()) { + *it = 5; + ++it; + } + + CHECK(it == beyond); + CHECK((it - beyond) == 0); + + for (const auto& n : s) { + CHECK(n == 5); + } + } } void test_case_rbegin_rend() { - { - int a[] = {1, 2, 3, 4}; - span s = a; - - auto it = s.rbegin(); - auto first = it; - CHECK(it == first); - CHECK(*it == 4); - - auto beyond = s.rend(); - CHECK(it != beyond); - - CHECK((beyond - first) == 4); - CHECK((first - first) == 0); - CHECK((beyond - beyond) == 0); - - ++it; - CHECK((it - first) == 1); - CHECK(*it == 3); - *it = 22; - CHECK(*it == 22); - CHECK((beyond - it) == 3); - - it = first; - CHECK(it == first); - while (it != s.rend()) { - *it = 5; - ++it; - } - - CHECK(it == beyond); - CHECK((it - beyond) == 0); - - for (const auto& n : s) { - CHECK(n == 5); - } - } -} - -void test_case_comparison_operators() -{ - { - span s1; - span s2; - CHECK(s1 == s2); - CHECK(!(s1 != s2)); - CHECK(!(s1 < s2)); - CHECK(s1 <= s2); - CHECK(!(s1 > s2)); - CHECK(s1 >= s2); - CHECK(s2 == s1); - CHECK(!(s2 != s1)); - CHECK(!(s2 < s1)); - CHECK(s2 <= s1); - CHECK(!(s2 > s1)); - CHECK(s2 >= s1); - } - - { - int arr[] = {2, 1}; - span s1 = arr; - span s2 = arr; - - CHECK(s1 == s2); - CHECK(!(s1 != s2)); - CHECK(!(s1 < s2)); - CHECK(s1 <= s2); - CHECK(!(s1 > s2)); - CHECK(s1 >= s2); - CHECK(s2 == s1); - CHECK(!(s2 != s1)); - CHECK(!(s2 < s1)); - CHECK(s2 <= s1); - CHECK(!(s2 > s1)); - CHECK(s2 >= s1); - } - - { - int arr[] = {2, 1}; // bigger - - span s1; - span s2 = arr; - - CHECK(s1 != s2); - CHECK(s2 != s1); - CHECK(!(s1 == s2)); - CHECK(!(s2 == s1)); - CHECK(s1 < s2); - CHECK(!(s2 < s1)); - CHECK(s1 <= s2); - CHECK(!(s2 <= s1)); - CHECK(s2 > s1); - CHECK(!(s1 > s2)); - CHECK(s2 >= s1); - CHECK(!(s1 >= s2)); - } - - { - int arr1[] = {1, 2}; - int arr2[] = {1, 2}; - span s1 = arr1; - span s2 = arr2; - - CHECK(s1 == s2); - CHECK(!(s1 != s2)); - CHECK(!(s1 < s2)); - CHECK(s1 <= s2); - CHECK(!(s1 > s2)); - CHECK(s1 >= s2); - CHECK(s2 == s1); - CHECK(!(s2 != s1)); - CHECK(!(s2 < s1)); - CHECK(s2 <= s1); - CHECK(!(s2 > s1)); - CHECK(s2 >= s1); - } - - { - int arr[] = {1, 2, 3}; - - span s1 = {&arr[0], 2}; // shorter - span s2 = arr; // longer - - CHECK(s1 != s2); - CHECK(s2 != s1); - CHECK(!(s1 == s2)); - CHECK(!(s2 == s1)); - CHECK(s1 < s2); - CHECK(!(s2 < s1)); - CHECK(s1 <= s2); - CHECK(!(s2 <= s1)); - CHECK(s2 > s1); - CHECK(!(s1 > s2)); - CHECK(s2 >= s1); - CHECK(!(s1 >= s2)); - } - - { - int arr1[] = {1, 2}; // smaller - int arr2[] = {2, 1}; // bigger - - span s1 = arr1; - span s2 = arr2; - - CHECK(s1 != s2); - CHECK(s2 != s1); - CHECK(!(s1 == s2)); - CHECK(!(s2 == s1)); - CHECK(s1 < s2); - CHECK(!(s2 < s1)); - CHECK(s1 <= s2); - CHECK(!(s2 <= s1)); - CHECK(s2 > s1); - CHECK(!(s1 > s2)); - CHECK(s2 >= s1); - CHECK(!(s1 >= s2)); - } + { + int a[] = {1, 2, 3, 4}; + span s = a; + + auto it = s.rbegin(); + auto first = it; + CHECK(it == first); + CHECK(*it == 4); + + auto beyond = s.rend(); + CHECK(it != beyond); + + CHECK((beyond - first) == 4); + CHECK((first - first) == 0); + CHECK((beyond - beyond) == 0); + + ++it; + CHECK((it - first) == 1); + CHECK(*it == 3); + *it = 22; + CHECK(*it == 22); + CHECK((beyond - it) == 3); + + it = first; + CHECK(it == first); + while (it != s.rend()) { + *it = 5; + ++it; + } + + CHECK(it == beyond); + CHECK((it - beyond) == 0); + + for (const auto& n : s) { + CHECK(n == 5); + } + } } void test_case_as_bytes() { - int a[] = {1, 2, 3, 4}; - - { - const span s = a; - CHECK(s.size() == 4); - const auto bs = as_bytes(s); - CHECK(static_cast(bs.data()) == static_cast(s.data())); - CHECK(bs.size() == s.size_bytes()); - } - - { - span s; - const auto bs = as_bytes(s); - CHECK(bs.size() == s.size()); - CHECK(bs.size() == 0); - CHECK(bs.size_bytes() == 0); - CHECK(static_cast(bs.data()) == static_cast(s.data())); - CHECK(bs.data() == nullptr); - } - - { - span s = a; - const auto bs = as_bytes(s); - CHECK(static_cast(bs.data()) == static_cast(s.data())); - CHECK(bs.size() == s.size_bytes()); - } + int a[] = {1, 2, 3, 4}; + + { + const span s = a; + CHECK(s.size() == 4); + const auto bs = as_bytes(s); + CHECK(static_cast(bs.data()) == + static_cast(s.data())); + CHECK(bs.size() == s.size_bytes()); + } + + { + span s; + const auto bs = as_bytes(s); + CHECK(bs.size() == s.size()); + CHECK(bs.size() == 0); + CHECK(bs.size_bytes() == 0); + CHECK(static_cast(bs.data()) == + static_cast(s.data())); + CHECK(bs.data() == nullptr); + } + + { + span s = a; + const auto bs = as_bytes(s); + CHECK(static_cast(bs.data()) == + static_cast(s.data())); + CHECK(bs.size() == s.size_bytes()); + } } void test_case_as_writeable_bytes() { - int a[] = {1, 2, 3, 4}; - - { - span s; - const auto bs = as_writeable_bytes(s); - CHECK(bs.size() == s.size()); - CHECK(bs.size() == 0); - CHECK(bs.size_bytes() == 0); - CHECK(static_cast(bs.data()) == static_cast(s.data())); - CHECK(bs.data() == nullptr); - } - - { - span s = a; - const auto bs = as_writeable_bytes(s); - CHECK(static_cast(bs.data()) == static_cast(s.data())); - CHECK(bs.size() == s.size_bytes()); - } + int a[] = {1, 2, 3, 4}; + + { + span s; + const auto bs = as_writable_bytes(s); + CHECK(bs.size() == s.size()); + CHECK(bs.size() == 0); + CHECK(bs.size_bytes() == 0); + CHECK(static_cast(bs.data()) == static_cast(s.data())); + CHECK(bs.data() == nullptr); + } + + { + span s = a; + const auto bs = as_writable_bytes(s); + CHECK(static_cast(bs.data()) == static_cast(s.data())); + CHECK(bs.size() == s.size_bytes()); + } } void test_case_fixed_size_conversions() { - int arr[] = {1, 2, 3, 4}; - - // converting to an span from an equal size array is ok - span s4 = arr; - CHECK(s4.size() == 4); - - // converting to dynamic_range is always ok - { - span s = s4; - CHECK(s.size() == s4.size()); - static_cast(s); - } - - // initialization or assignment to static span that REDUCES size is NOT ok - static_assert(!std::is_convertible>::value); - static_assert(!std::is_convertible, span>::value); - - // you can convert statically - { - const span s2 = {arr, 2}; - static_cast(s2); - } - { - const span s1 = s4.first<1>(); - static_cast(s1); - } - - // ...or dynamically - { - // NB: implicit conversion to span from span - span s1 = s4.first(1); - static_cast(s1); - } - - // initialization or assignment to static span that requires size INCREASE is not ok. - int arr2[2] = {1, 2}; - (void)arr2; - - static_assert(!std::is_constructible, decltype((arr2))>::value); - static_assert(!std::is_constructible, span>::value); + int arr[] = {1, 2, 3, 4}; + + // converting to an span from an equal size array is ok + span s4 = arr; + CHECK(s4.size() == 4); + + // converting to dynamic_range is always ok + { + span s = s4; + CHECK(s.size() == s4.size()); + static_cast(s); + } + + // initialization or assignment to static span that REDUCES size is NOT ok + static_assert(!std::is_convertible>::value); + static_assert(!std::is_convertible, span>::value); + + // you can convert statically + { + const span s2 = {arr, 2}; + static_cast(s2); + } + { + const span s1 = s4.first<1>(); + static_cast(s1); + } + + // ...or dynamically + { + // NB: implicit conversion to span from span +// span s1 = s4.first(1); +// static_cast(s1); + } + + // initialization or assignment to static span that requires size INCREASE is not ok. + int arr2[2] = {1, 2}; + (void) arr2; + + static_assert( + !std::is_constructible, decltype((arr2))>::value); + static_assert(!std::is_constructible, span>::value); } void test_case_interop_with_std_regex() { - char lat[] = {'1', '2', '3', '4', '5', '6', 'E', 'F', 'G'}; - span s = lat; - const auto f_it = s.begin() + 7; - - std::match_results::iterator> match; - - std::regex_match(s.begin(), s.end(), match, std::regex(".*")); - CHECK(match.ready()); - CHECK(!match.empty()); - CHECK(match[0].matched); - CHECK(match[0].first == s.begin()); - CHECK(match[0].second == s.end()); - - std::regex_search(s.begin(), s.end(), match, std::regex("F")); - CHECK(match.ready()); - CHECK(!match.empty()); - CHECK(match[0].matched); - CHECK(match[0].first == f_it); - CHECK(match[0].second == (f_it + 1)); + char lat[] = {'1', '2', '3', '4', '5', '6', 'E', 'F', 'G'}; + span s = lat; + const auto f_it = s.begin() + 7; + + std::match_results::iterator> match; + + std::regex_match(s.begin(), s.end(), match, std::regex(".*")); + CHECK(match.ready()); + CHECK(!match.empty()); + CHECK(match[0].matched); + CHECK(match[0].first == s.begin()); + CHECK(match[0].second == s.end()); + + std::regex_search(s.begin(), s.end(), match, std::regex("F")); + CHECK(match.ready()); + CHECK(!match.empty()); + CHECK(match[0].matched); + CHECK(match[0].first == f_it); + CHECK(match[0].second == (f_it + 1)); } -void test_case_default_constructible() +void test_case_default_initializable() { - CHECK((std::is_default_constructible>::value)); - CHECK((std::is_default_constructible>::value)); - CHECK((std::is_default_constructible>::value)); + CHECK((std::is_default_constructible>::value)); + CHECK((std::is_default_constructible>::value)); +// CHECK((std::is_default_constructible>::value)); } -int main() { +} // anonymous namespace + +TEST_CASE("views.span") +{ test_case_default_constructor(); test_case_size_optimization(); test_case_from_nullptr_constructor(); @@ -1062,7 +999,7 @@ int main() { test_case_from_array_constructor(); test_case_from_std_array_constructor(); test_case_from_const_std_array_constructor(); - test_case_from_std_array_const_constructor(); + //test_case_from_std_array_const_constructor(); test_case_from_container_constructor(); test_case_from_convertible_span_constructor(); test_case_copy_move_and_assignment(); @@ -1074,26 +1011,30 @@ int main() { test_case_iterator_comparisons(); test_case_begin_end(); test_case_rbegin_rend(); - test_case_comparison_operators(); test_case_as_bytes(); test_case_as_writeable_bytes(); test_case_fixed_size_conversions(); test_case_interop_with_std_regex(); - test_case_default_constructible(); + test_case_default_initializable(); - static_assert(ranges::ContiguousRange> && ranges::View>); - static_assert(ranges::ContiguousRange> && ranges::View>); + static_assert(ranges::contiguous_range> && ranges::view>); + // Fixed-sized span is not default-constructible => !view +// static_assert(ranges::contiguous_range> && ranges::view>); + static_assert(ranges::contiguous_range>); // spans are non-dangling - static_assert(ranges::Same>())), ranges::iterator_t>>); - static_assert(ranges::Same>())), ranges::iterator_t>>); - static_assert(ranges::Same>())), ranges::iterator_t>>); - static_assert(ranges::Same>())), ranges::iterator_t>>); + static_assert(ranges::same_as>())), ranges::iterator_t>>); + static_assert(ranges::same_as>())), ranges::iterator_t>>); + static_assert(ranges::same_as>())), ranges::iterator_t>>); + static_assert(ranges::same_as>())), ranges::iterator_t>>); + { + int some_ints[]{42}; + CHECK(ranges::data(span{some_ints, 42}) == +some_ints); + } { int some_ints[] = {0,1,2,3,4}; auto result = ranges::find(span{some_ints}, 3); - static_assert(ranges::Same); - CHECK(result == some_ints + 3); + CHECK(*result == 3); } }