Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions include/beman/span/span.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <limits>
#include <ranges>
#include <stdexcept>
#include <tuple>
#include <type_traits>

namespace beman::span {
Expand Down Expand Up @@ -315,6 +316,33 @@ auto as_writable_bytes(span<ElementType, Extent> s) noexcept
return return_type{reinterpret_cast<std::byte*>(s.data()), s.size_bytes()};
}

// [span.tuple] Tuple interface for fixed-size span (P3786R2).
// Lives in beman::span so ADL picks it up for structured bindings on span<T, N>.
template <std::size_t I, class ElementType, std::size_t Extent>
constexpr ElementType& get(span<ElementType, Extent> s) noexcept {
static_assert(Extent != dynamic_extent, "beman::span::get<I> requires a fixed-extent span");
static_assert(I < Extent, "beman::span::get<I>: index out of range");
return s[I];
}

} // namespace beman::span

// std::tuple_size / std::tuple_element specializations for fixed-size span (P3786R2).
// dynamic_extent is excluded via constrained partial specialization, so
// std::tuple_size<span<T>> falls back to the (undefined) primary template -
// SFINAE-friendly for the tuple-like concept.
namespace std {

template <class ElementType, std::size_t Extent>
requires(Extent != ::beman::span::dynamic_extent)
struct tuple_size<::beman::span::span<ElementType, Extent>> : integral_constant<std::size_t, Extent> {};

template <std::size_t I, class ElementType, std::size_t Extent>
requires(Extent != ::beman::span::dynamic_extent && I < Extent)
struct tuple_element<I, ::beman::span::span<ElementType, Extent>> {
using type = ElementType&;
};

} // namespace std

#endif // BEMAN_SPAN_SPAN_HPP
53 changes: 53 additions & 0 deletions tests/beman/span/span.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <array>
#include <cstddef>
#include <numeric>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -549,6 +550,58 @@ TEST(SpanConstexpr, size_and_access) {
EXPECT_EQ(s.size(), 3u);
}

namespace tuple_test_detail {
template <class T, class = void>
struct has_tuple_size : std::false_type {};

template <class T>
struct has_tuple_size<T, std::void_t<decltype(std::tuple_size<T>::value)>> : std::true_type {};
} // namespace tuple_test_detail

TEST(SpanTuple, tuple_size_value) {
static_assert(std::tuple_size_v<bsp::span<int, 3>> == 3);
static_assert(std::tuple_size_v<bsp::span<const double, 5>> == 5);
static_assert(std::tuple_size_v<bsp::span<int, 0>> == 0);
}

TEST(SpanTuple, tuple_size_top_level_const_ignored) { static_assert(std::tuple_size_v<const bsp::span<int, 3>> == 3); }

TEST(SpanTuple, tuple_element_yields_reference) {
static_assert(std::is_same_v<std::tuple_element_t<0, bsp::span<int, 3>>, int&>);
static_assert(std::is_same_v<std::tuple_element_t<2, bsp::span<const double, 5>>, const double&>);
static_assert(std::is_same_v<std::tuple_element_t<0, const bsp::span<int, 3>>, int&>);
}

TEST(SpanTuple, get_returns_reference_to_underlying_element) {
int arr[] = {10, 20, 30};
bsp::span<int, 3> s(arr);

static_assert(std::is_same_v<decltype(get<0>(s)), int&>);
EXPECT_EQ(get<0>(s), 10);
EXPECT_EQ(get<2>(s), 30);

get<1>(s) = 99;
EXPECT_EQ(arr[1], 99);
}

TEST(SpanTuple, structured_binding) {
int arr[] = {1, 2, 3};
bsp::span<int, 3> s(arr);

auto& [a, b, c] = s;
EXPECT_EQ(a, 1);
EXPECT_EQ(b, 2);
EXPECT_EQ(c, 3);

a = 100;
EXPECT_EQ(arr[0], 100);
}

TEST(SpanTuple, dynamic_extent_excluded_from_tuple_protocol) {
static_assert(tuple_test_detail::has_tuple_size<bsp::span<int, 3>>::value);
static_assert(!tuple_test_detail::has_tuple_size<bsp::span<int>>::value);
}

// ---------------------------------------------------------------------------
// Comparison with std::span (when available)
// ---------------------------------------------------------------------------
Expand Down