1313#include < stdexcept>
1414#include < vector>
1515
16- // TODO:
17- // - use std::ranges::subrange instead of std::span
18- // - define container_type
19- // - use std::ptrdiff_t for offsets
20-
2116namespace gl {
2217
2318// / @brief A flattened 2D vector (jagged array) providing efficient storage for variable-length segments.
@@ -34,21 +29,24 @@ namespace gl {
3429// / @todo Implement assign, and swap methods.
3530// / @todo Implement iterator-based insert, emplace and erase methods.
3631// / @todo Add `operator<<` overload for `std::ostream` and specialize `std::formatter`.
32+ // / @todo Use std::ptrdiff_t instead of std::size_t for offset values
3733template <std::semiregular T>
3834class flat_jagged_vector {
3935public:
4036 // / @brief Type of elements stored in segments
4137 using value_type = T;
4238 // / @brief Unsigned integral type used for sizes and indices
4339 using size_type = std::size_t ;
40+ // / @brief The underlying contiguous storage container
41+ using container_type = std::vector<value_type>;
4442 // / @brief Reference to an element
45- using reference = value_type& ;
43+ using reference = typename container_type::reference ;
4644 // / @brief Const reference to an element
47- using const_reference = const value_type& ;
48- // / @brief Span type representing a non-owning segment of elements
49- using segment_type = std::span<value_type >;
50- // / @brief Const span type representing a non-owning const segment of elements
51- using const_segment_type = std::span< const value_type >;
45+ using const_reference = typename container_type::const_reference ;
46+ // / @brief Subrange type representing a non-owning segment of elements
47+ using segment_type = std::ranges::subrange< typename container_type::iterator >;
48+ // / @brief Const subrange type representing a non-owning const segment of elements
49+ using const_segment_type = std::ranges::subrange< typename container_type::const_iterator >;
5250
5351 // --- iterators ---
5452
@@ -63,7 +61,10 @@ class flat_jagged_vector {
6361 // / @warning Invalidated when the `flat_jagged_vector` is modified (structure changes or element insertions/deletions).
6462 template <bool Const>
6563 class segment_iterator {
66- using data_ptr_type = std::conditional_t <Const, const T*, T*>;
64+ using data_iter_type = std::conditional_t <
65+ Const,
66+ typename container_type::const_iterator,
67+ typename container_type::iterator>;
6768 using offset_ptr_type = const size_type*;
6869
6970 public:
@@ -84,25 +85,25 @@ class flat_jagged_vector {
8485 segment_iterator () = default ;
8586
8687 // / @brief Constructs an iterator pointing to a specific segment.
87- // / @param data_ptr Pointer to the underlying element data (may be null for null iterator)
88+ // / @param data_iter Iterator to the underlying element data (may be null for null iterator)
8889 // / @param offset_ptr Pointer to the offsets array at the position of this segment
89- segment_iterator (data_ptr_type data_ptr , offset_ptr_type offset_ptr) noexcept
90- : _data_ptr(data_ptr ), _offset_ptr(offset_ptr) {}
90+ segment_iterator (data_iter_type data_iter , offset_ptr_type offset_ptr) noexcept
91+ : _data_iter(data_iter ), _offset_ptr(offset_ptr) {}
9192
9293 // / @brief Implicit conversion from mutable to const iterator
9394 // / @return A const iterator pointing to the same segment
9495 operator segment_iterator<true >() const noexcept
9596 requires (not Const)
9697 {
97- return segment_iterator<true >(this ->_data_ptr , this ->_offset_ptr );
98+ return segment_iterator<true >(this ->_data_iter , this ->_offset_ptr );
9899 }
99100
100101 // / @brief Dereferences the iterator to the current segment.
101102 // / @return A span representing the segment at the current position
102103 [[nodiscard]] reference operator *() const noexcept {
103104 const auto beg = *this ->_offset_ptr ;
104105 const auto end = *(this ->_offset_ptr + 1uz);
105- return reference (this ->_data_ptr + beg, end - beg );
106+ return reference (this ->_data_iter + beg, this -> _data_iter + end );
106107 }
107108
108109 // / @brief Random access to a segment at offset from current position.
@@ -220,7 +221,7 @@ class flat_jagged_vector {
220221 }
221222
222223 private:
223- data_ptr_type _data_ptr{ nullptr } ;
224+ data_iter_type _data_iter ;
224225 offset_ptr_type _offset_ptr{nullptr };
225226 };
226227
@@ -476,7 +477,7 @@ class flat_jagged_vector {
476477 [[nodiscard]] segment_type operator [](size_type i) {
477478 const auto beg = this ->_offsets [i];
478479 const auto end = this ->_offsets [i + 1uz];
479- return segment_type (this ->_data .data () + beg, end - beg );
480+ return segment_type (this ->_data .begin () + beg, this -> _data . begin () + end );
480481 }
481482
482483 // / @brief Returns a const segment at the given index without bounds checking.
@@ -488,7 +489,7 @@ class flat_jagged_vector {
488489 [[nodiscard]] const_segment_type operator [](size_type i) const {
489490 const auto beg = this ->_offsets [i];
490491 const auto end = this ->_offsets [i + 1uz];
491- return const_segment_type (this ->_data .data () + beg, end - beg );
492+ return const_segment_type (this ->_data .begin () + beg, this -> _data . begin () + end );
492493 }
493494
494495 // / @brief Returns a reference to an element within a segment without bounds checking.
@@ -696,14 +697,20 @@ class flat_jagged_vector {
696697
697698 // / @brief Returns a raw pointer to the underlying flat data array.
698699 // / @return A raw pointer to the first element in the `_data` array.
699- // / @warning No bounds checking is performed. Structural modifications (like resizing) cannot be done via this pointer; use `data_storage()` instead.
700- [[nodiscard]] value_type* data_ptr () noexcept {
700+ // / @warning Structural modifications (like resizing) cannot be done via this pointer; use `data_storage()` instead.
701+ // / @warning Not available for boolean vectors.
702+ [[nodiscard]] value_type* data_ptr () noexcept
703+ requires(not std::same_as<value_type, bool >)
704+ {
701705 return this ->_data .data ();
702706 }
703707
704708 // / @brief Returns a const raw pointer to the underlying flat data array.
705709 // / @return A const raw pointer to the first element in the `_data` array.
706- [[nodiscard]] const value_type* data_ptr () const noexcept {
710+ // / @warning Not available for boolean vectors.
711+ [[nodiscard]] const value_type* data_ptr () const noexcept
712+ requires(not std::same_as<value_type, bool >)
713+ {
707714 return this ->_data .data ();
708715 }
709716
@@ -752,28 +759,28 @@ class flat_jagged_vector {
752759 // / @return Iterator to the first segment
753760 // / @note Iterator invalidated by structural modifications
754761 [[nodiscard]] iterator begin () noexcept {
755- return iterator (this ->_data .data (), this ->_offsets .data ());
762+ return iterator (this ->_data .begin (), this ->_offsets .data ());
756763 }
757764
758765 // / @brief Returns a mutable iterator past the last segment (end sentinel).
759766 // / @return Iterator one position past the last segment
760767 // / @note Iterator invalidated by structural modifications
761768 [[nodiscard]] iterator end () noexcept {
762- return iterator (this ->_data .data (), this ->_offsets .data () + this ->size ());
769+ return iterator (this ->_data .begin (), this ->_offsets .data () + this ->size ());
763770 }
764771
765772 // / @brief Returns a const iterator to the first segment.
766773 // / @return Const iterator to the first segment
767774 // / @note Iterator invalidated by structural modifications
768775 [[nodiscard]] const_iterator begin () const noexcept {
769- return const_iterator (this ->_data .data (), this ->_offsets .data ());
776+ return const_iterator (this ->_data .begin (), this ->_offsets .data ());
770777 }
771778
772779 // / @brief Returns a const iterator past the last segment (end sentinel).
773780 // / @return Const iterator one position past the last segment
774781 // / @note Iterator invalidated by structural modifications
775782 [[nodiscard]] const_iterator end () const noexcept {
776- return const_iterator (this ->_data .data (), this ->_offsets .data () + this ->size ());
783+ return const_iterator (this ->_data .begin (), this ->_offsets .data () + this ->size ());
777784 }
778785
779786 // / @brief Returns a const iterator to the first segment (explicit const form).
0 commit comments