From 687385fc043e461fcb1f26c13e5f99b1bae50392 Mon Sep 17 00:00:00 2001 From: Sriram Katta Date: Sat, 1 Aug 2026 20:33:55 +0200 Subject: [PATCH 1/4] fix to remove size in the cases of static size extent --- include/beman/span/span.hpp | 57 ++++++++++++++++++++++++---------- tests/beman/span/span.test.cpp | 23 ++++++++++++++ 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/include/beman/span/span.hpp b/include/beman/span/span.hpp index 7ddf4f2..627f0f8 100644 --- a/include/beman/span/span.hpp +++ b/include/beman/span/span.hpp @@ -56,11 +56,34 @@ 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 remove_prefix() {} + constexpr void remove_suffix() {} +}; + +template <> +struct compressed_size { + constexpr explicit compressed_size(std::size_t sz) : size_(sz) {} + constexpr std::size_t size() const { return size_; } + constexpr void remove_prefix(std::size_t N) { size_ -= N; } + constexpr void remove_suffix(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 +106,11 @@ 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 +118,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 +133,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 +180,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 +194,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 +261,19 @@ class span { { assert(n <= size()); data_ += n; - size_ -= n; + size_holder::remove_prefix(n); } constexpr void remove_suffix(size_type n) noexcept requires(Extent == dynamic_extent) { assert(n <= size()); - size_ -= n; + size_holder::remove_suffix(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 +312,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..cc02d06 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); } @@ -95,6 +101,7 @@ TEST(SpanConstruction, c_array_static) { 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 +113,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 +121,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 +130,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 +147,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 +155,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 +170,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 +183,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 +193,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,6 +203,8 @@ 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); } @@ -219,6 +240,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 +251,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); } From b2f654a31a909ef6d95345c27aedbe9173448882 Mon Sep 17 00:00:00 2001 From: Sriram Katta Date: Sat, 1 Aug 2026 20:40:52 +0200 Subject: [PATCH 2/4] Generalize the remove_suffix and remove_prefix in compressed_size --- include/beman/span/span.hpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/include/beman/span/span.hpp b/include/beman/span/span.hpp index 627f0f8..823c398 100644 --- a/include/beman/span/span.hpp +++ b/include/beman/span/span.hpp @@ -62,16 +62,14 @@ struct compressed_size { constexpr explicit compressed_size(std::size_t) {} constexpr std::size_t size() const { return Extent; } - constexpr void remove_prefix() {} - constexpr void remove_suffix() {} + 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 remove_prefix(std::size_t N) { size_ -= N; } - constexpr void remove_suffix(std::size_t N) { size_ -= N; } + constexpr void reduce_size(std::size_t N) { size_ -= N; } private: std::size_t size_; @@ -109,8 +107,7 @@ class span : public detail::compressed_size { 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_holder(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); } @@ -261,14 +258,14 @@ class span : public detail::compressed_size { { assert(n <= size()); data_ += n; - size_holder::remove_prefix(n); + size_holder::reduce_size(n); } constexpr void remove_suffix(size_type n) noexcept requires(Extent == dynamic_extent) { assert(n <= size()); - size_holder::remove_suffix(n); + size_holder::reduce_size(n); } // 26.7.3.4 Observers [span.obs] From a3a172ff594d57dbb2bd0d700355f51accddbf1d Mon Sep 17 00:00:00 2001 From: Sriram Katta Date: Sat, 1 Aug 2026 20:45:44 +0200 Subject: [PATCH 3/4] add sizeof(span) test for few missing cases --- tests/beman/span/span.test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/beman/span/span.test.cpp b/tests/beman/span/span.test.cpp index cc02d06..b1f7e7c 100644 --- a/tests/beman/span/span.test.cpp +++ b/tests/beman/span/span.test.cpp @@ -95,6 +95,7 @@ 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); } @@ -210,6 +211,7 @@ TEST(SpanConstruction, dynamic_from_static) { 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); @@ -220,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); @@ -229,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]); @@ -629,6 +633,7 @@ TEST(SpanObjectRepresentation, as_writable_bytes) { TEST(SpanObjectRepresentation, as_bytes_fixed_extent) { int arr[3] = {1, 2, 3}; bsp::span s(arr); + 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)); @@ -644,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); From 09a18b966b93723551d35ba1dada0cfc2e4aac79 Mon Sep 17 00:00:00 2001 From: Sriram <87057612+SriramKatta@users.noreply.github.com> Date: Sat, 1 Aug 2026 20:49:15 +0200 Subject: [PATCH 4/4] Update tests/beman/span/span.test.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/beman/span/span.test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beman/span/span.test.cpp b/tests/beman/span/span.test.cpp index b1f7e7c..3e54341 100644 --- a/tests/beman/span/span.test.cpp +++ b/tests/beman/span/span.test.cpp @@ -634,7 +634,7 @@ TEST(SpanObjectRepresentation, as_bytes_fixed_extent) { int arr[3] = {1, 2, 3}; bsp::span s(arr); static_assert(sizeof(s) == sizeof(int*)); - auto bytes = bsp::as_bytes(s); + auto bytes = bsp::as_bytes(s); static_assert(decltype(bytes)::extent == 3 * sizeof(int)); EXPECT_EQ(bytes.size(), 3 * sizeof(int)); }