Skip to content
Merged
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
5 changes: 1 addition & 4 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,5 @@ message("Examples to be built: ${ALL_EXAMPLES}")
foreach(example ${ALL_EXAMPLES})
add_executable(beman.span.examples.${example})
target_sources(beman.span.examples.${example} PRIVATE ${example}.cpp)
target_link_libraries(
beman.span.examples.${example}
PRIVATE beman::span
)
target_link_libraries(beman.span.examples.${example} PRIVATE beman::span)
endforeach()
8 changes: 4 additions & 4 deletions examples/span_basic_usage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ template <class T, std::size_t E>
void print_span(beman::span::span<T, E> s) {
std::cout << "[";
for (std::size_t i = 0; i < s.size(); ++i) {
if (i) std::cout << ", ";
if (i)
std::cout << ", ";
std::cout << s[i];
}
std::cout << "] (size=" << s.size() << ")\n";
Expand All @@ -34,7 +35,7 @@ int main() {
// ----------------------------------------------------------------
// 1. Construct from a C-array (fixed extent deduced)
// ----------------------------------------------------------------
int c_arr[] = {10, 20, 30, 40, 50};
int c_arr[] = {10, 20, 30, 40, 50};
beman::span::span s1(c_arr); // span<int, 5>
std::cout << "From C-array: ";
print_span(s1);
Expand Down Expand Up @@ -73,6 +74,5 @@ int main() {
// ----------------------------------------------------------------
// 6. size_bytes
// ----------------------------------------------------------------
std::cout << "size_bytes=" << s3.size_bytes() << " (=" << s3.size()
<< " * " << sizeof(int) << ")\n";
std::cout << "size_bytes=" << s3.size_bytes() << " (=" << s3.size() << " * " << sizeof(int) << ")\n";
}
14 changes: 6 additions & 8 deletions examples/span_bytes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,25 @@ int main() {
// ----------------------------------------------------------------
// as_bytes: read-only view over the raw bytes of any span
// ----------------------------------------------------------------
int arr[] = {0x01020304, 0x05060708};
int arr[] = {0x01020304, 0x05060708};
bsp::span<int> s(arr);

auto bytes = bsp::as_bytes(s);
std::cout << "as_bytes extent (fixed): " << decltype(bytes)::extent
<< " size: " << bytes.size() << "\n";
std::cout << "as_bytes extent (fixed): " << decltype(bytes)::extent << " size: " << bytes.size() << "\n";

std::cout << "Raw bytes of {0x01020304, 0x05060708}: ";
for (auto b : bytes)
std::cout << std::hex << static_cast<unsigned>(b) << " ";
std::cout << std::dec << "\n";

std::cout << "Checksum: 0x" << std::hex
<< static_cast<unsigned>(checksum(bytes)) << std::dec << "\n";
std::cout << "Checksum: 0x" << std::hex << static_cast<unsigned>(checksum(bytes)) << std::dec << "\n";

// ----------------------------------------------------------------
// as_writable_bytes: write-only (mutable) view over raw bytes
// ----------------------------------------------------------------
int value = 0;
bsp::span<int> vs(&value, 1);
auto wb = bsp::as_writable_bytes(vs);
int value = 0;
bsp::span<int> vs(&value, 1);
auto wb = bsp::as_writable_bytes(vs);

// Write 0xDEADBEEF byte by byte (little-endian on most platforms)
const std::uint8_t pattern[] = {0xEF, 0xBE, 0xAD, 0xDE};
Expand Down
5 changes: 3 additions & 2 deletions examples/span_subviews.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ template <class T, std::size_t E>
void print(const char* label, bsp::span<T, E> s) {
std::cout << label << ": [";
for (std::size_t i = 0; i < s.size(); ++i) {
if (i) std::cout << ", ";
if (i)
std::cout << ", ";
std::cout << s[i];
}
std::cout << "] extent=";
Expand Down Expand Up @@ -47,7 +48,7 @@ int main() {

// Dynamic variants
bsp::span<int> dyn(arr);
auto df = dyn.first(4);
auto df = dyn.first(4);
print("dyn.first(4) ", df);

auto dl = dyn.last(2);
Expand Down
5 changes: 1 addition & 4 deletions include/beman/span/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

target_sources(
beman.span
PUBLIC FILE_SET HEADERS FILES span.hpp
)
target_sources(beman.span PUBLIC FILE_SET HEADERS FILES span.hpp)
144 changes: 56 additions & 88 deletions include/beman/span/span.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@ struct is_std_array<std::array<T, N>> : std::true_type {};

// Range element-type compatibility check (per [span.cons])
template <class Range, class ElementType>
inline constexpr bool is_compatible_element_type_v = std::is_convertible_v<
std::remove_reference_t<std::ranges::range_reference_t<Range>> (*)[],
ElementType (*)[]>;
inline constexpr bool is_compatible_element_type_v =
std::is_convertible_v<std::remove_reference_t<std::ranges::range_reference_t<Range>> (*)[], ElementType (*)[]>;

} // namespace detail

Expand All @@ -62,14 +61,14 @@ template <class ElementType, std::size_t Extent>
class span {
public:
// Member types
using element_type = ElementType;
using value_type = std::remove_cv_t<ElementType>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = ElementType*;
using const_pointer = const ElementType*;
using reference = ElementType&;
using const_reference = const ElementType&;
using element_type = ElementType;
using value_type = std::remove_cv_t<ElementType>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = ElementType*;
using const_pointer = const ElementType*;
using reference = ElementType&;
using const_reference = const ElementType&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
Expand All @@ -81,13 +80,11 @@ class span {
// 26.7.3.2 Constructors, copy, and assignment [span.cons]

// Default constructor: only valid when Extent == 0 or Extent == dynamic_extent
template <std::size_t E = Extent,
std::enable_if_t<E == dynamic_extent || E == 0, int> = 0>
template <std::size_t E = Extent, std::enable_if_t<E == dynamic_extent || E == 0, int> = 0>
constexpr span() noexcept : data_(nullptr), size_(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_(count) {
if constexpr (Extent != dynamic_extent) {
assert(count == Extent);
}
Expand All @@ -102,45 +99,39 @@ class span {
}

// C array constructor (fixed-size)
template <std::size_t N,
std::enable_if_t<Extent == dynamic_extent || Extent == N, int> = 0,
std::enable_if_t<
std::is_convertible_v<std::remove_pointer_t<decltype(std::data(
std::declval<ElementType (&)[N]>()))> (*)[],
ElementType (*)[]>,
int> = 0>
template <
std::size_t N,
std::enable_if_t<Extent == dynamic_extent || Extent == N, int> = 0,
std::enable_if_t<
std::is_convertible_v<std::remove_pointer_t<decltype(std::data(std::declval<ElementType (&)[N]>()))> (*)[],
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_(N) {}

// std::array constructor (fixed-size)
template <class T,
std::size_t N,
std::enable_if_t<Extent == dynamic_extent || Extent == N, int> = 0,
std::enable_if_t<Extent == dynamic_extent || Extent == N, int> = 0,
std::enable_if_t<std::is_convertible_v<T (*)[], ElementType (*)[]>, int> = 0>
constexpr span(std::array<T, N>& arr) noexcept
: data_(arr.data()), size_(N) {}
constexpr span(std::array<T, N>& arr) noexcept : data_(arr.data()), size_(N) {}

// const std::array constructor
template <class T,
std::size_t N,
std::enable_if_t<Extent == dynamic_extent || Extent == N, int> = 0,
std::enable_if_t<std::is_convertible_v<const T (*)[], ElementType (*)[]>,
int> = 0>
constexpr span(const std::array<T, N>& arr) noexcept
: data_(arr.data()), size_(N) {}
std::enable_if_t<Extent == dynamic_extent || Extent == N, int> = 0,
std::enable_if_t<std::is_convertible_v<const T (*)[], ElementType (*)[]>, int> = 0>
constexpr span(const std::array<T, N>& arr) noexcept : data_(arr.data()), size_(N) {}

// Range constructor (generic contiguous range)
template <
class Range,
std::enable_if_t<!detail::is_span<std::remove_cvref_t<Range>>::value, int> = 0,
std::enable_if_t<!detail::is_std_array<std::remove_cvref_t<Range>>::value, int> = 0,
std::enable_if_t<!std::is_array_v<std::remove_cvref_t<Range>>, int> = 0,
std::enable_if_t<std::ranges::contiguous_range<Range>, int> = 0,
std::enable_if_t<std::ranges::sized_range<Range>, int> = 0,
std::enable_if_t<std::ranges::borrowed_range<Range> || std::is_const_v<ElementType>,
int> = 0,
std::enable_if_t<detail::is_compatible_element_type_v<Range, ElementType>, int> = 0>
template <class Range,
std::enable_if_t<!detail::is_span<std::remove_cvref_t<Range>>::value, int> = 0,
std::enable_if_t<!detail::is_std_array<std::remove_cvref_t<Range>>::value, int> = 0,
std::enable_if_t<!std::is_array_v<std::remove_cvref_t<Range>>, int> = 0,
std::enable_if_t<std::ranges::contiguous_range<Range>, int> = 0,
std::enable_if_t<std::ranges::sized_range<Range>, int> = 0,
std::enable_if_t<std::ranges::borrowed_range<Range> || std::is_const_v<ElementType>, int> = 0,
std::enable_if_t<detail::is_compatible_element_type_v<Range, ElementType>, int> = 0>
constexpr explicit(Extent != dynamic_extent) span(Range&& r)
: data_(std::ranges::data(r)), size_(std::ranges::size(r)) {
if constexpr (Extent != dynamic_extent) {
Expand All @@ -151,30 +142,26 @@ class span {
// Cross-span converting constructor.
template <class OtherElementType,
std::size_t OtherExtent,
std::enable_if_t<
(Extent == dynamic_extent || OtherExtent == dynamic_extent ||
Extent == OtherExtent) &&
std::is_convertible_v<OtherElementType (*)[], ElementType (*)[]>,
int> = 0>
constexpr explicit(Extent != dynamic_extent &&
OtherExtent == dynamic_extent) span(const span<OtherElementType,
OtherExtent>& s) noexcept
std::enable_if_t<(Extent == dynamic_extent || OtherExtent == dynamic_extent || Extent == OtherExtent) &&
std::is_convertible_v<OtherElementType (*)[], ElementType (*)[]>,
int> = 0>
constexpr explicit(Extent != dynamic_extent && OtherExtent == dynamic_extent)
span(const span<OtherElementType, OtherExtent>& s) noexcept
: data_(s.data()), size_(s.size()) {
if constexpr (Extent != dynamic_extent) {
assert(s.size() == Extent);
}
}

constexpr span(const span&) noexcept = default;
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span&) noexcept = default;

// 26.7.3.3 Subviews [span.sub]

// first<Count>(): static count.
template <std::size_t Count>
constexpr span<ElementType, Count> first() const noexcept {
static_assert(Count <= Extent || Extent == dynamic_extent,
"Count out of range for fixed-extent span");
static_assert(Count <= Extent || Extent == dynamic_extent, "Count out of range for fixed-extent span");
assert(Count <= size());
return span<ElementType, Count>{data_, Count};
}
Expand All @@ -188,8 +175,7 @@ class span {
// last<Count>(): static count.
template <std::size_t Count>
constexpr span<ElementType, Count> last() const noexcept {
static_assert(Count <= Extent || Extent == dynamic_extent,
"Count out of range for fixed-extent span");
static_assert(Count <= Extent || Extent == dynamic_extent, "Count out of range for fixed-extent span");
assert(Count <= size());
return span<ElementType, Count>{data_ + (size() - Count), Count};
}
Expand All @@ -203,27 +189,21 @@ class span {
// subspan<Offset, Count>(): static offset and count.
template <std::size_t Offset, std::size_t Count = dynamic_extent>
constexpr auto subspan() const noexcept {
static_assert(Offset <= Extent || Extent == dynamic_extent,
"Offset out of range");
static_assert(Count == dynamic_extent || Count <= Extent - Offset ||
Extent == dynamic_extent,
static_assert(Offset <= Extent || Extent == dynamic_extent, "Offset out of range");
static_assert(Count == dynamic_extent || Count <= Extent - Offset || Extent == dynamic_extent,
"Count out of range");
assert(Offset <= size());
assert(Count == dynamic_extent || Count <= size() - Offset);

constexpr std::size_t E =
(Count != dynamic_extent)
? Count
: (Extent != dynamic_extent ? Extent - Offset : dynamic_extent);
(Count != dynamic_extent) ? Count : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent);

return span<ElementType, E>{data_ + Offset,
Count == dynamic_extent ? size() - Offset : Count};
return span<ElementType, E>{data_ + Offset, Count == dynamic_extent ? size() - Offset : Count};
}

// subspan(offset, count): dynamic.
constexpr span<ElementType, dynamic_extent> subspan(size_type offset,
size_type count = dynamic_extent) const
noexcept {
size_type count = dynamic_extent) const noexcept {
assert(offset <= size());
assert(count == dynamic_extent || count <= size() - offset);
return {data_ + offset, count == dynamic_extent ? size() - offset : count};
Expand All @@ -233,9 +213,7 @@ class span {

[[nodiscard]] constexpr size_type size() const noexcept { return size_; }

[[nodiscard]] constexpr size_type size_bytes() const noexcept {
return size() * sizeof(element_type);
}
[[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }

[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }

Expand Down Expand Up @@ -270,14 +248,10 @@ class span {
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_reverse_iterator crbegin() const noexcept {
return const_reverse_iterator(cend());
}
constexpr const_reverse_iterator crend() const noexcept {
return const_reverse_iterator(cbegin());
}
constexpr const_iterator cbegin() const noexcept { return data_; }
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_;
Expand All @@ -303,23 +277,17 @@ span(Range&&) -> span<std::remove_reference_t<decltype(*std::data(std::declval<R
// as_bytes: reinterpret a span as read-only bytes
template <class ElementType, std::size_t Extent>
auto as_bytes(span<ElementType, Extent> s) noexcept
-> span<const std::byte,
Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent> {
-> span<const std::byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent> {
using return_type =
span<const std::byte,
Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>;
span<const std::byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>;
return return_type{reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()};
}

// as_writable_bytes: reinterpret a mutable span as writable bytes
template <class ElementType,
std::size_t Extent,
std::enable_if_t<!std::is_const_v<ElementType>, int> = 0>
template <class ElementType, std::size_t Extent, std::enable_if_t<!std::is_const_v<ElementType>, int> = 0>
auto as_writable_bytes(span<ElementType, Extent> s) noexcept
-> span<std::byte,
Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent> {
using return_type =
span<std::byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>;
-> span<std::byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent> {
using return_type = span<std::byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>;
return return_type{reinterpret_cast<std::byte*>(s.data()), s.size_bytes()};
}

Expand Down
Loading