diff --git a/include/preview/__span/span.h b/include/preview/__span/span.h index a4eecea..b1b013b 100644 --- a/include/preview/__span/span.h +++ b/include/preview/__span/span.h @@ -34,23 +34,69 @@ namespace preview { namespace detail { +struct static_constexpr_value_tester { + template + static constexpr auto test(int) + -> decltype(std::integral_constant{}, std::true_type{}); + template + static constexpr auto test(...) -> std::false_type; +}; + +struct static_constexpr_operator_tester { + template + static constexpr auto test(int) + -> decltype(std::integral_constant{}, std::true_type{}); + template + static constexpr auto test(...) -> std::false_type; +}; + +template +struct has_static_constexpr_value + : conjunction< + decltype(static_constexpr_value_tester::test>(0)), + decltype(static_constexpr_operator_tester::test>(0)) + > {}; + +template::value> +struct integral_constant_like : std::false_type {}; +template +struct integral_constant_like + : conjunction< + std::is_integral, + negation>>, + convertible_to, + equality_comparable_with, + bool_constant<(T() == T::value)>, + bool_constant<(static_cast(T()) == T::value)> + > {}; + +template::value> +struct maybe_static_ext : std::integral_constant {}; + +template +struct maybe_static_ext : std::integral_constant {}; + template struct static_span_storage { constexpr static_span_storage() noexcept : ptr_(nullptr) {} constexpr static_span_storage(T* ptr, std::size_t) noexcept : ptr_(ptr) {} T* data() const noexcept { return ptr_; } + + [[nodiscard]] std::size_t size() const noexcept { return Extent; } T* ptr_; }; -template +template struct dynamic_span_storage { constexpr dynamic_span_storage() noexcept : ptr_(nullptr), size_(0) {} constexpr dynamic_span_storage(T* ptr, std::size_t size) noexcept : ptr_(ptr), size_(size) {} T* data() const noexcept { return ptr_; } + + [[nodiscard]] std::size_t size() const noexcept { return size_; } T* ptr_; @@ -59,7 +105,7 @@ struct dynamic_span_storage { template using span_storage_t = std::conditional_t< - Extent == dynamic_extent, dynamic_span_storage, + Extent == dynamic_extent, dynamic_span_storage, static_span_storage>; template @@ -93,7 +139,7 @@ template> >, negation< std::is_array> > >::value /* true */> -struct span_ctor_range : std::is_convertible, T> {}; +struct span_ctor_range : std::is_convertible>(*)[], T(*)[]> {}; template struct span_ctor_range : std::false_type {}; @@ -101,29 +147,24 @@ struct span_ctor_range : std::false_type {}; template class span : private detail::span_storage_t { + using base = detail::span_storage_t; + public: using element_type = T; using value_type = std::remove_cv_t; using size_type = std::size_t; using difference_type = std::ptrdiff_t; - using pointer = T*; - using const_pointer = const T*; - using reference = T&; - using const_reference = const T&; + using pointer = element_type*; + using const_pointer = const element_type*; + using reference = element_type&; + using const_reference = const element_type&; + // TODO: Custom iterator? using iterator = pointer; using const_iterator = preview::const_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = preview::const_iterator; + static constexpr size_type extent = Extent; - private: - using base = detail::span_storage_t; - - template - using subspan_type = span; - public: // (1) // constexpr span() noexcept; constexpr span() noexcept : base() {} @@ -132,19 +173,17 @@ class span : private detail::span_storage_t { // template< class It > // explicit(extent != std::dynamic_extent) // constexpr span( It first, size_type count ); - template, detail::span_ctor_first_count - >::value, int> = 0> + >::value, int> = 0> constexpr span(It first, size_type size) : base(preview::to_address(first), size) {} - template, detail::span_ctor_first_count - >::value, int> = 0> + >::value, int> = 0> constexpr explicit span(It first, size_type size) : base(preview::to_address(first), size) {} @@ -152,52 +191,44 @@ class span : private detail::span_storage_t { // template< class It, class End > // explicit(extent != std::dynamic_extent) // constexpr span( It first, End last ); - template, detail::span_ctor_first_last - >::value , int> = 0> + >::value , int> = 0> constexpr span(It first, End last) : base(preview::to_address(first), last - first) {} - template, detail::span_ctor_first_last - >::value , int> = 0> + >::value , int> = 0> constexpr explicit span(It first, End last) : base(preview::to_address(first), last - first) {} // (4) // template< std::size_t N > // constexpr span( std::type_identity_t (&arr)[N] ) noexcept; - template, - std::is_convertible, element_type> - >::value, int> = 0> + template = 0> constexpr span(type_identity_t (&arr)[N]) noexcept : base(arr, N) {} // (5) // template< class U, std::size_t N > // constexpr span( std::array& arr ) noexcept; - template, - std::is_convertible - >::value, int> = 0> + std::is_convertible + >::value, int> = 0> constexpr span(std::array& arr) noexcept : base(arr.data(), N) {} // (6) // template< class U, std::size_t N > // constexpr span( const std::array& arr ) noexcept; - template, - std::is_convertible - >::value, int> = 0> + std::is_convertible + >::value, int> = 0> constexpr span(const std::array& arr) noexcept : base(arr.data(), N) {} @@ -205,37 +236,33 @@ class span : private detail::span_storage_t { // template< class R > // explicit(extent != std::dynamic_extent) // constexpr span( R&& range ); - template, detail::span_ctor_range >::value, int> = 0> constexpr span(R&& range) - : base(ranges::data(range), - ranges::size(range)) {} + : base(ranges::data(range), ranges::size(range)) {} - template, detail::span_ctor_range >::value, int> = 0> constexpr explicit span(R&& range) - : base(ranges::data(range), - ranges::size(range)) {} + : base(ranges::data(range), ranges::size(range)) {} // (8) (C++ 26) // explicit(extent != std::dynamic_extent) // constexpr span( std::initializer_list il ) noexcept; - template, bool_constant, - std::is_const + std::is_const >::value, int> = 0> constexpr span(std::initializer_list il) noexcept : base(il.begin(), il.size()) {} - template, bool_constant, - std::is_const + std::is_const >::value, int> = 0> constexpr explicit span(std::initializer_list il) noexcept : base(il.begin(), il.size()) {} @@ -244,46 +271,46 @@ class span : private detail::span_storage_t { // template< class U, std::size_t N > // explicit(extent != std::dynamic_extent && N == std::dynamic_extent) // constexpr span( const std::span& source ) noexcept; - template, - bool_constant<( - Extent == dynamic_extent || - N == dynamic_extent || - N == Extent)>, - std::is_convertible - >::value, int> = 0> - constexpr span(const span& source) noexcept : base(source.data(), source.size()) {} - - template, - bool_constant<( + template, // explicit(false) + bool_constant<( + Extent == dynamic_extent || + OtherExtent == dynamic_extent || + Extent == OtherExtent)>, + std::is_convertible + >::value, int> = 0> + constexpr span(const span& source) noexcept + : base(source.data(), source.size()) {} + + template, // explicit(true) + bool_constant<( Extent == dynamic_extent || - N == dynamic_extent || - N == Extent)>, - std::is_convertible - >::value, int> = 0> - constexpr explicit span(const span& source) noexcept : base(source.data(), source.size()) {} + OtherExtent == dynamic_extent || + Extent == OtherExtent)>, + std::is_convertible + >::value, int> = 0> + constexpr explicit span(const span& source) noexcept + : base(source.data(), source.size()) {} // (10) // constexpr span( const span& other ) noexcept = default; constexpr span(const span& other) noexcept = default; - constexpr iterator begin() const noexcept { return iterator(data()); } - constexpr iterator end() const noexcept { return iterator(data() + size()); } + constexpr iterator begin() const noexcept { return iterator{data()}; } + constexpr iterator end() const noexcept { return iterator{data() + size()}; } - constexpr const_iterator cbegin() const noexcept { return const_iterator{begin()}; } - constexpr const_iterator cend() const noexcept { return const_iterator{end()}; } + 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(data() + size()); } - constexpr reverse_iterator rend() const noexcept { return reverse_iterator(data()); } + 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{rbegin()}; } - constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{rend()}; } + constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } + constexpr const_reverse_iterator crend() const noexcept { return rend(); } - constexpr reference front() const { return *begin(); } - constexpr reference back() const { return *(end() - 1); } + constexpr reference front() const { return *data(); } + constexpr reference back() const { return *(data() + (size() - 1)); } constexpr reference at(size_type pos) const { if (pos >= size()) { @@ -292,7 +319,7 @@ class span : private detail::span_storage_t { return *(data() + pos); } - constexpr reference operator[](size_type idx) const { return data()[idx]; } + constexpr reference operator[](size_type idx) const { return *(data() +idx); } constexpr pointer data() const noexcept { return base::data(); } @@ -305,29 +332,30 @@ class span : private detail::span_storage_t { constexpr span first() const { return span(data(), Count); } - constexpr span first(size_type Count) const { - return span(data(), Count); + constexpr span first(size_type count) const { + return span(data(), count); } template = 0> constexpr span last() const { - return span(data() + size() - Count, Count); + return span(data() + (size() - Count), Count); } - constexpr span last(size_type Count) const { - return span(data() + size() - Count, Count); + constexpr span last(size_type count) const { + return span(data() + (size() - count), count); } template = 0> - constexpr subspan_type subspan() const { - return subspan_type( - data() + Offset, - (Count == dynamic_extent ? size() - Offset : Count)); - }; - constexpr span subspan(size_type Offset, size_type Count) const { - return span( - data() + Offset, - (Count == dynamic_extent ? size() - Offset : Count)); + (Offset <= Extent) && + (Count == dynamic_extent || Count <= (Extent - Offset)) + , int> = 0> + constexpr auto subspan() const { + using subspan_type = span; + return subspan_type(data() + Offset, Count != dynamic_extent ? Count : size() - Offset); + } + constexpr span subspan(size_type offset, size_type count) const { + return span(data() + offset, (count == dynamic_extent ? size() - offset : count)); } }; @@ -337,7 +365,7 @@ struct ranges::enable_view> : std::true_type {}; #if __cplusplus >= 201703L template::value, int> = 0> -span(It, EndOrSize) -> span>>; +span(It, EndOrSize) -> span>, detail::maybe_static_ext::value>; template span(T (&)[N]) -> span; @@ -348,7 +376,7 @@ span(std::array&) -> span; template span(const std::array&) -> span; -template +template::value, int> = 0> span(R&&) -> span>>; #endif diff --git a/test/span.cc b/test/span.cc new file mode 100644 index 0000000..b299784 --- /dev/null +++ b/test/span.cc @@ -0,0 +1,116 @@ +#include "preview/span.h" +#include "gtest.h" + +#include +#include +#include + +#include "preview/algorithm.h" +#include "preview/ranges.h" + +namespace ranges = preview::ranges; +namespace views = preview::views; + +TEST(VERSIONED(Span), ctor) { + int c[]{1, 2, 3}; + EXPECT_TRUE(ranges::equal(preview::span(c), c)); // constructs from array + + const std::array a{4, 5, 6}; + EXPECT_TRUE(ranges::equal(preview::span(a), a)); // constructs from std::array + + std::vector v{7, 8, 9}; + EXPECT_TRUE(ranges::equal(preview::span(v), v)); // constructs from std::vector + + EXPECT_TRUE(ranges::equal(preview::span{0, 1, 2}, {0, 1, 2})); // constructs from initializer_list +} + +TEST(VERSIONED(Span), operator_assign) { + std::array a1; + std::array a2; + a1.fill(3); + a2.fill(4); + +#if PREVIEW_CXX_VERSION < 17 + auto s1 = preview::span(a1); + auto s2 = preview::span(a2); +#else + auto s1 = preview::span(a1); + auto s2 = preview::span(a2); +#endif + EXPECT_TRUE(ranges::equal(s1, views::repeat(3, 6))); + EXPECT_TRUE(ranges::equal(s2, views::repeat(4, 6))); + + // Check that assignment performs a shallow copy. + s1 = s2; + EXPECT_EQ(s1.data(), s2.data()); + EXPECT_EQ(s1.size(), s2.size()); + + ranges::fill(s1, 5); + // s2 is also 'updated' since s1 and s2 point to the same data + EXPECT_TRUE(ranges::equal(s1, s2)); +// print("s1", s1); +// print("s2", s2); +// print(); + + int a3[]{1, 2, 3, 4}; + int a4[]{2, 3, 4, 5}; + int a5[]{3, 4, 5}; + + preview::span dynamic_1{a3}; + preview::span dynamic_2{a4, 3}; + preview::span static_1{a3}; + preview::span static_2{a4}; + preview::span static_3{a5}; + + + dynamic_1 = dynamic_2; // OK + dynamic_1 = static_1; // OK + EXPECT_FALSE_TYPE(std::is_assignable); // ERROR: no match for ‘operator=’ + static_1 = static_2; // OK: same extents = 4 + EXPECT_FALSE_TYPE(std::is_assignable); // ERROR: different extents: 4 and 3 +} + +#if PREVIEW_CXX_VERSION >= 17 + +TEST(VERSIONED(Span), deduction_guides) { + int a[]{1, 2, 3, 4, 5}; + + preview::span s1{std::begin(a), std::end(a)}; // guide (1) + EXPECT_EQ(sizeof s1, sizeof(std::size_t) * 2); + EXPECT_EQ(s1.extent, preview::dynamic_extent); + + preview::span s2{std::begin(a), 3}; // guide (1) + EXPECT_EQ(sizeof s2, sizeof(std::size_t) * 2); + EXPECT_EQ(s2.extent, preview::dynamic_extent); + + preview::span s3{std::begin(a), std::integral_constant{}}; // guide (1) + EXPECT_EQ(sizeof s3, sizeof(std::size_t) * 1); + EXPECT_EQ(s3.extent, 2); + + preview::span s4{a}; // guide (2) + EXPECT_EQ(sizeof s4, sizeof(std::size_t) * 1); + EXPECT_EQ(s4.extent, 5); + + preview::span s5{a}; // does not use a guide, makes a dynamic span + EXPECT_EQ(sizeof s5, sizeof(std::size_t) * 2); + EXPECT_EQ(s5.extent, preview::dynamic_extent); + + std::array arr{6, 7, 8}; + preview::span s6{arr}; // guide (3) + EXPECT_EQ(sizeof s6, sizeof(std::size_t) * 1); + EXPECT_EQ(s6.extent, 3); + s6[0] = 42; // OK, element_type is 'int' + + const std::array arr2{9, 10, 11}; + preview::span s7{arr2}; // guide (4) + EXPECT_EQ(sizeof s7, sizeof(std::size_t) * 1); + EXPECT_EQ(s7.extent, 3); + // s7[0] = 42; // Error: element_type is 'const int' + + std::vector v{66, 69, 99}; + preview::span s8{v}; // guide (5) + EXPECT_EQ(sizeof s8, sizeof(std::size_t) * 2); + EXPECT_EQ(s8.extent, preview::dynamic_extent); +} + +#endif // C++17