diff --git a/include/beman/span/span.hpp b/include/beman/span/span.hpp index 7ddf4f2..823c398 100644 --- a/include/beman/span/span.hpp +++ b/include/beman/span/span.hpp @@ -56,11 +56,32 @@ template inline constexpr bool is_compatible_element_type_v = std::is_convertible_v> (*)[], ElementType (*)[]>; +template +struct compressed_size { + constexpr compressed_size() = default; + constexpr explicit compressed_size(std::size_t) {} + + constexpr std::size_t size() const { return Extent; } + constexpr void reduce_size() {} +}; + +template <> +struct compressed_size { + constexpr explicit compressed_size(std::size_t sz) : size_(sz) {} + constexpr std::size_t size() const { return size_; } + constexpr void reduce_size(std::size_t N) { size_ -= N; } + + private: + std::size_t size_; +}; + } // namespace detail // 26.7.3 Class template span [views.span] template -class span { +class span : public detail::compressed_size { + using size_holder = detail::compressed_size; + public: // Member types using element_type = ElementType; @@ -83,10 +104,10 @@ class span { // Default constructor: only valid when Extent == 0 or Extent == dynamic_extent template = 0> - constexpr span() noexcept : data_(nullptr), size_(0) {} + constexpr span() noexcept : data_(nullptr), size_holder(0) {} // Pointer + count constructor. - constexpr explicit(Extent != dynamic_extent) span(pointer ptr, size_type count) : data_(ptr), size_(count) { + constexpr explicit(Extent != dynamic_extent) span(pointer ptr, size_type count) : data_(ptr), size_holder(count) { if constexpr (Extent != dynamic_extent) { assert(count == Extent); } @@ -94,7 +115,7 @@ class span { // Pointer pair constructor. constexpr explicit(Extent != dynamic_extent) span(pointer first, pointer last) - : data_(first), size_(static_cast(last - first)) { + : data_(first), size_holder(static_cast(last - first)) { if constexpr (Extent != dynamic_extent) { assert(static_cast(last - first) == Extent); } @@ -109,21 +130,21 @@ class span { ElementType (*)[]>, int> = 0> // NOLINTNEXTLINE(modernize-avoid-c-arrays) - constexpr span(ElementType (&arr)[N]) noexcept : data_(arr), size_(N) {} + constexpr span(ElementType (&arr)[N]) noexcept : data_(arr), size_holder(N) {} // std::array constructor (fixed-size) template = 0, std::enable_if_t, int> = 0> - constexpr span(std::array& arr) noexcept : data_(arr.data()), size_(N) {} + constexpr span(std::array& arr) noexcept : data_(arr.data()), size_holder(N) {} // const std::array constructor template = 0, std::enable_if_t, int> = 0> - constexpr span(const std::array& arr) noexcept : data_(arr.data()), size_(N) {} + constexpr span(const std::array& arr) noexcept : data_(arr.data()), size_holder(N) {} // Range constructor (generic contiguous range) template || std::is_const_v, int> = 0, std::enable_if_t, int> = 0> constexpr explicit(Extent != dynamic_extent) span(Range&& r) - : data_(std::ranges::data(r)), size_(std::ranges::size(r)) { + : data_(std::ranges::data(r)), size_holder(std::ranges::size(r)) { if constexpr (Extent != dynamic_extent) { if constexpr (requires { std::integral_constant{}; }) { static_assert(std::ranges::size(r) == Extent, @@ -156,7 +177,7 @@ class span { std::enable_if_t, int> = 0, std::enable_if_t>, int> = 0> constexpr explicit(Extent != dynamic_extent) span(std::initializer_list il) - : data_(il.begin()), size_(il.size()) { + : data_(il.begin()), size_holder(il.size()) { if constexpr (Extent != dynamic_extent) { assert(il.size() == Extent); } @@ -170,7 +191,7 @@ class span { int> = 0> constexpr explicit(Extent != dynamic_extent && OtherExtent == dynamic_extent) span(const span& s) noexcept - : data_(s.data()), size_(s.size()) { + : data_(s.data()), size_holder(s.size()) { if constexpr (Extent != dynamic_extent) { assert(s.size() == Extent); } @@ -237,19 +258,19 @@ class span { { assert(n <= size()); data_ += n; - size_ -= n; + size_holder::reduce_size(n); } constexpr void remove_suffix(size_type n) noexcept requires(Extent == dynamic_extent) { assert(n <= size()); - size_ -= n; + size_holder::reduce_size(n); } // 26.7.3.4 Observers [span.obs] - [[nodiscard]] constexpr size_type size() const noexcept { return size_; } + [[nodiscard]] constexpr size_type size() const noexcept { return size_holder::size(); } [[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); } @@ -288,20 +309,19 @@ class span { // 26.7.3.6 Iterator support [span.iterators] constexpr iterator begin() const noexcept { return data_; } - constexpr iterator end() const noexcept { return data_ + size_; } + constexpr iterator end() const noexcept { return data_ + size(); } constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } // cbegin/cend yield iterators to const elements even when ElementType is non-const constexpr const_iterator cbegin() const noexcept { return data_; } - constexpr const_iterator cend() const noexcept { return data_ + size_; } + constexpr const_iterator cend() const noexcept { return data_ + size(); } constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } private: - pointer data_; - size_type size_; + pointer data_; }; // Deduction guides (C++17) diff --git a/tests/beman/span/span.test.cpp b/tests/beman/span/span.test.cpp index dfc56e4..3e54341 100644 --- a/tests/beman/span/span.test.cpp +++ b/tests/beman/span/span.test.cpp @@ -33,6 +33,7 @@ TEST(SpanTest, static_extent_constant) { TEST(SpanConstruction, default_dynamic) { bsp::span s; + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 0u); EXPECT_EQ(s.data(), nullptr); EXPECT_TRUE(s.empty()); @@ -40,6 +41,7 @@ TEST(SpanConstruction, default_dynamic) { TEST(SpanConstruction, default_static_zero) { bsp::span s; + static_assert(sizeof(s) == sizeof(int*)); EXPECT_EQ(s.size(), 0u); EXPECT_TRUE(s.empty()); } @@ -51,6 +53,7 @@ TEST(SpanConstruction, default_static_zero) { TEST(SpanConstruction, pointer_and_count_dynamic) { int arr[] = {1, 2, 3, 4, 5}; bsp::span s(arr, 5); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 5u); EXPECT_EQ(s.data(), arr); EXPECT_FALSE(s.empty()); @@ -59,6 +62,7 @@ TEST(SpanConstruction, pointer_and_count_dynamic) { TEST(SpanConstruction, pointer_and_count_static) { int arr[] = {10, 20, 30}; bsp::span s(arr, 3); + static_assert(sizeof(s) == sizeof(int*)); EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s.data(), arr); } @@ -70,6 +74,7 @@ TEST(SpanConstruction, pointer_and_count_static) { TEST(SpanConstruction, pointer_pair) { int arr[] = {5, 6, 7, 8}; bsp::span s(arr, arr + 4); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 4u); EXPECT_EQ(s[0], 5); EXPECT_EQ(s[3], 8); @@ -82,6 +87,7 @@ TEST(SpanConstruction, pointer_pair) { TEST(SpanConstruction, c_array_dynamic) { int arr[] = {1, 2, 3}; bsp::span s(arr); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s.data(), arr); } @@ -89,12 +95,14 @@ TEST(SpanConstruction, c_array_dynamic) { TEST(SpanConstruction, c_array_static) { int arr[] = {10, 20}; bsp::span s(arr); + static_assert(sizeof(s) == sizeof(int*)); EXPECT_EQ(s.size(), 2u); } TEST(SpanConstruction, c_array_deduction) { int arr[] = {1, 2, 3, 4}; bsp::span s(arr); + static_assert(sizeof(s) == sizeof(int*)); static_assert(std::is_same_v>); EXPECT_EQ(s.size(), 4u); } @@ -106,6 +114,7 @@ TEST(SpanConstruction, c_array_deduction) { TEST(SpanConstruction, std_array_mutable) { std::array arr = {1, 2, 3, 4}; bsp::span s(arr); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 4u); EXPECT_EQ(s.data(), arr.data()); } @@ -113,6 +122,7 @@ TEST(SpanConstruction, std_array_mutable) { TEST(SpanConstruction, std_array_const) { const std::array arr = {7, 8, 9}; bsp::span s(arr); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s[0], 7); } @@ -121,12 +131,14 @@ TEST(SpanConstruction, std_array_deduction_mutable) { std::array arr = {1.0, 2.0}; bsp::span s(arr); static_assert(std::is_same_v>); + static_assert(sizeof(s) == sizeof(int*)); } TEST(SpanConstruction, std_array_deduction_const) { const std::array arr = {3.0, 4.0}; bsp::span s(arr); static_assert(std::is_same_v>); + static_assert(sizeof(s) == sizeof(int*)); } // --------------------------------------------------------------------------- @@ -136,6 +148,7 @@ TEST(SpanConstruction, std_array_deduction_const) { TEST(SpanConstruction, from_vector) { std::vector v = {1, 2, 3, 4, 5}; bsp::span s(v); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 5u); EXPECT_EQ(s.data(), v.data()); } @@ -143,12 +156,14 @@ TEST(SpanConstruction, from_vector) { TEST(SpanConstruction, from_const_vector) { const std::vector v = {10, 20}; bsp::span s(v); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 2u); EXPECT_EQ(s[1], 20); } TEST(SpanConstruction, lwg4397_constant_size_matches_extent) { bsp::span s(std::views::empty); + static_assert(sizeof(s) == sizeof(int*)); EXPECT_EQ(s.size(), 0u); EXPECT_TRUE(s.empty()); } @@ -156,6 +171,7 @@ TEST(SpanConstruction, lwg4397_constant_size_matches_extent) { TEST(SpanConstruction, lwg4397_runtime_sized_range_not_rejected) { std::vector v(3); bsp::span s(v); + static_assert(sizeof(s) == sizeof(int*)); EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s.data(), v.data()); } @@ -168,6 +184,8 @@ TEST(SpanConstruction, copy_dynamic_from_dynamic) { int arr[] = {1, 2, 3}; bsp::span a(arr); bsp::span b(a); + static_assert(sizeof(a) == sizeof(int*) + sizeof(std::size_t)); + static_assert(sizeof(b) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(b.size(), 3u); EXPECT_EQ(b.data(), arr); } @@ -176,6 +194,8 @@ TEST(SpanConstruction, const_from_mutable) { int arr[] = {4, 5, 6}; bsp::span mutable_s(arr); bsp::span const_s(mutable_s); + static_assert(sizeof(mutable_s) == sizeof(int*) + sizeof(std::size_t)); + static_assert(sizeof(const_s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(const_s.size(), 3u); EXPECT_EQ(const_s[0], 4); } @@ -184,11 +204,14 @@ TEST(SpanConstruction, dynamic_from_static) { int arr[] = {1, 2, 3, 4}; bsp::span fixed(arr); bsp::span dynamic(fixed); + static_assert(sizeof(fixed) == sizeof(int*)); + static_assert(sizeof(dynamic) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(dynamic.size(), 4u); } TEST(SpanInitList, dynamic_from_braced_list) { auto verify = [](bsp::span s) { + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s[0], 1); EXPECT_EQ(s[1], 2); @@ -199,6 +222,7 @@ TEST(SpanInitList, dynamic_from_braced_list) { TEST(SpanInitList, fixed_extent_from_braced_list) { auto verify = [](bsp::span s) { + static_assert(sizeof(s) == sizeof(int*)); EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s[0], 1); EXPECT_EQ(s[2], 3); @@ -208,6 +232,7 @@ TEST(SpanInitList, fixed_extent_from_braced_list) { TEST(SpanInitList, const_bool_from_braced_list) { auto verify = [](bsp::span s) { + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 3u); EXPECT_TRUE(s[0]); EXPECT_FALSE(s[1]); @@ -219,6 +244,7 @@ TEST(SpanInitList, const_bool_from_braced_list) { TEST(SpanInitList, named_initializer_list_keeps_array_alive) { std::initializer_list il = {10, 20, 30}; bsp::span s(il); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s[0], 10); EXPECT_EQ(s[2], 30); @@ -229,6 +255,7 @@ TEST(SpanInitList, pointer_and_size_resolves_to_pointer_count_ctor) { bool* ptr = data; std::size_t n = 4; bsp::span s(ptr, n); + static_assert(sizeof(s) == sizeof(int*) + sizeof(std::size_t)); EXPECT_EQ(s.size(), 4u); EXPECT_EQ(s.data(), data); } @@ -606,7 +633,8 @@ TEST(SpanObjectRepresentation, as_writable_bytes) { TEST(SpanObjectRepresentation, as_bytes_fixed_extent) { int arr[3] = {1, 2, 3}; bsp::span s(arr); - auto bytes = bsp::as_bytes(s); + static_assert(sizeof(s) == sizeof(int*)); + auto bytes = bsp::as_bytes(s); static_assert(decltype(bytes)::extent == 3 * sizeof(int)); EXPECT_EQ(bytes.size(), 3 * sizeof(int)); } @@ -621,6 +649,7 @@ static constexpr int kConstexprArr[] = {1, 2, 3}; TEST(SpanConstexpr, size_and_access) { constexpr bsp::span s(kConstexprArr); + static_assert(sizeof(s) == sizeof(int*)); static_assert(s.size() == 3); static_assert(s[0] == 1); static_assert(s.front() == 1);