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
62 changes: 50 additions & 12 deletions include/gsl/dyn_array
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,11 @@ namespace details
using reference = T&;
using const_reference = const T&;
using iterator_category = std::random_access_iterator_tag;
#if defined(__cpp_lib_ranges) || (defined(_MSVC_STL_VERSION) && defined(__cpp_lib_concepts))
using iterator_concept = std::contiguous_iterator_tag;
#endif /* __cpp_lib_ranges || (_MSVC_STL_VERSION && __cpp_lib_concepts) */

#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
constexpr dyn_array_iterator() = default;
#endif /* __cpp_lib_ranges >= 201911L */

constexpr operator dyn_array_iterator<const T>() const { return {_ptr, _pos, _end_pos}; }

Expand All @@ -199,13 +200,39 @@ namespace details
return !(*this == other);
}

constexpr auto operator<(const dyn_array_iterator& other) const
{
Expects(_ptr == other._ptr);
Expects(_end_pos == other._end_pos);
return _pos < other._pos;
}

constexpr auto operator>(const dyn_array_iterator& other) const { return other < *this; }

constexpr auto operator<=(const dyn_array_iterator& other) const
{
return !(other < *this);
}

constexpr auto operator>=(const dyn_array_iterator& other) const
{
return !(*this < other);
}

constexpr auto operator*() const -> reference
{
Expects(_ptr != nullptr);
Expects(_pos < _end_pos);
return _ptr[_pos];
}

constexpr auto operator->() const -> pointer
{
Expects(_ptr != nullptr);
Expects(_pos < _end_pos);
return _ptr + _pos;
}

constexpr auto operator++() -> dyn_array_iterator&
{
Expects(_pos < _end_pos);
Expand Down Expand Up @@ -256,6 +283,11 @@ namespace details
return dyn_array_iterator{_ptr, gsl::narrow<size_type>(new_pos), _end_pos};
}

friend constexpr auto operator+(difference_type diff, const dyn_array_iterator& it)
{
return it + diff;
}

constexpr auto operator-(difference_type diff) const { return *this + (-diff); }

constexpr auto operator-(const dyn_array_iterator& other) const
Expand All @@ -265,16 +297,7 @@ namespace details
return gsl::narrow<difference_type>(_pos) - gsl::narrow<difference_type>(other._pos);
}

constexpr auto operator[](size_type pos) -> reference
{
Expects(_pos + pos < _end_pos);
return _ptr[_pos + pos];
}

constexpr auto operator[](size_type pos) const -> const_reference
{
return const_cast<dyn_array_iterator&>(*this).operator[](pos);
}
constexpr auto operator[](difference_type pos) const -> reference { return *(*this + pos); }

private:
constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos)
Expand All @@ -292,6 +315,8 @@ namespace details
friend class dyn_array_iterator;
template <typename, typename>
friend class ::gsl::dyn_array;
template <typename Ptr>
friend struct std::pointer_traits;
};
} // namespace details

Expand Down Expand Up @@ -445,4 +470,17 @@ dyn_array(std::from_range_t, InputRg&&, Alloc = {})
#endif /* __cpp_deduction_guides >= 201703L */
} // namespace gsl

namespace std
{
template <class Type>
struct pointer_traits<::gsl::details::dyn_array_iterator<Type>>
{
using pointer = ::gsl::details::dyn_array_iterator<Type>;
using element_type = Type;
using difference_type = ptrdiff_t;

static constexpr element_type* to_address(const pointer i) noexcept { return i._ptr + i._pos; }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A null pointer plus 0 is a null pointer value in C++ (C++20 [expr.add]/4.1; C++17 [expr.add]/7), so this returns null for default and empty iterators. static_assert(std::to_address(gsl::dyn_array<int>::iterator{}) == nullptr) compiles on gcc 15 and clang 17.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A null pointer plus 0 is a null pointer value in C++

Agreed. Copilot is wrong about UB.

};
} // namespace std

#endif /* defined(GSL_DYN_ARRAY_H) */
59 changes: 59 additions & 0 deletions tests/dyn_array_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include <iostream>
#include <sstream>
#include <type_traits>
#include <utility>
#if defined(__cplusplus) && __cplusplus >= 202002L
#include <span>
#endif /* __cplusplus >= 202002L */

// Despite using <algorithm> and <ranges> utilities in this test, they
// are not being included directly by this file as a test to ensure
Expand Down Expand Up @@ -37,11 +41,21 @@ static_assert(std::is_copy_constructible<gsl::dyn_array<int>::iterator>::value,
#if defined(__cpp_lib_concepts) && (__cpp_lib_concepts >= 202002L)
static_assert(std::input_iterator<gsl::dyn_array<int>::iterator>,
"gsl::dyn_array should expose a valid input_iterator");
static_assert(std::random_access_iterator<gsl::dyn_array<int>::iterator>,
"gsl::dyn_array should expose a valid random_access_iterator");
static_assert(std::random_access_iterator<gsl::dyn_array<int>::const_iterator>,
"gsl::dyn_array should expose a valid random_access const_iterator");
static_assert(std::contiguous_iterator<gsl::dyn_array<int>::iterator>,
"gsl::dyn_array should expose a valid contiguous_iterator");
static_assert(std::contiguous_iterator<gsl::dyn_array<int>::const_iterator>,
"gsl::dyn_array should expose a valid contiguous const_iterator");
#endif /* __cpp_lib_concepts >= 202002L */

#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
static_assert(std::ranges::input_range<gsl::dyn_array<int>>,
"gsl::dyn_array should be a valid input range");
static_assert(std::ranges::contiguous_range<gsl::dyn_array<int>>,
"gsl::dyn_array should be a valid contiguous range");
#endif /* __cpp_lib_ranges >= 201911L */

TEST(dyn_array_tests, default_ctor)
Expand Down Expand Up @@ -631,6 +645,51 @@ TEST(dyn_array_tests, random_access_iterator_arithmetic_accepts_negative_offsets
EXPECT_EQ(next, 'd');
}

TEST(dyn_array_tests, random_access_iterator_comparisons)
{
gsl::dyn_array<char> giants{'c', 'a', 'b'};

const auto first = giants.begin();
const auto last = giants.end();

EXPECT_TRUE(first < last);
EXPECT_TRUE(last > first);
EXPECT_TRUE(first <= first);
EXPECT_TRUE(first >= first);
EXPECT_FALSE(last <= first);
EXPECT_EQ(2 + first, last - 1);
EXPECT_EQ(*(1 + first), 'a');

std::sort(giants.begin(), giants.end());
EXPECT_EQ(giants[0], 'a');
EXPECT_EQ(giants[1], 'b');
EXPECT_EQ(giants[2], 'c');
}

TEST(dyn_array_tests, iterator_member_access_and_default_construction)
{
gsl::dyn_array<std::pair<char, int>> rockies{{'a', 1}, {'b', 2}};
EXPECT_EQ(rockies.begin()->second, 1);
EXPECT_EQ((rockies.begin() + 1)->first, 'b');

gsl::dyn_array<int>::iterator it{};
gsl::dyn_array<int>::const_iterator cit{};
EXPECT_TRUE(it == gsl::dyn_array<int>::iterator{});
EXPECT_TRUE(cit == gsl::dyn_array<int>::const_iterator{});
}

#if defined(__cpp_lib_span) && (__cpp_lib_span >= 202002L)
TEST(dyn_array_tests, converts_to_a_span)
{
gsl::dyn_array<char> giants{'a', 'b', 'c'};
const std::span<char> span{giants};

EXPECT_EQ(span.size(), giants.size());
EXPECT_EQ(span.data(), giants.data());
EXPECT_EQ(std::to_address(giants.end()), giants.data() + giants.size());
}
#endif /* __cpp_lib_span >= 202002L */

TEST(dyn_array_tests, input_iterator_constructor)
{
std::istringstream stream{"n a t s"};
Expand Down
Loading