diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 10a43eb1..8d2e6257 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -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 { return {_ptr, _pos, _end_pos}; } @@ -199,6 +200,25 @@ 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); @@ -206,6 +226,13 @@ namespace details 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); @@ -256,6 +283,11 @@ namespace details return dyn_array_iterator{_ptr, gsl::narrow(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 @@ -265,16 +297,7 @@ namespace details return gsl::narrow(_pos) - gsl::narrow(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(*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) @@ -292,6 +315,8 @@ namespace details friend class dyn_array_iterator; template friend class ::gsl::dyn_array; + template + friend struct std::pointer_traits; }; } // namespace details @@ -445,4 +470,17 @@ dyn_array(std::from_range_t, InputRg&&, Alloc = {}) #endif /* __cpp_deduction_guides >= 201703L */ } // namespace gsl +namespace std +{ +template +struct pointer_traits<::gsl::details::dyn_array_iterator> +{ + using pointer = ::gsl::details::dyn_array_iterator; + using element_type = Type; + using difference_type = ptrdiff_t; + + static constexpr element_type* to_address(const pointer i) noexcept { return i._ptr + i._pos; } +}; +} // namespace std + #endif /* defined(GSL_DYN_ARRAY_H) */ diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 51e7fb08..a5cc5092 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -9,6 +9,10 @@ #include #include #include +#include +#if defined(__cplusplus) && __cplusplus >= 202002L +#include +#endif /* __cplusplus >= 202002L */ // Despite using and utilities in this test, they // are not being included directly by this file as a test to ensure @@ -37,11 +41,21 @@ static_assert(std::is_copy_constructible::iterator>::value, #if defined(__cpp_lib_concepts) && (__cpp_lib_concepts >= 202002L) static_assert(std::input_iterator::iterator>, "gsl::dyn_array should expose a valid input_iterator"); +static_assert(std::random_access_iterator::iterator>, + "gsl::dyn_array should expose a valid random_access_iterator"); +static_assert(std::random_access_iterator::const_iterator>, + "gsl::dyn_array should expose a valid random_access const_iterator"); +static_assert(std::contiguous_iterator::iterator>, + "gsl::dyn_array should expose a valid contiguous_iterator"); +static_assert(std::contiguous_iterator::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 should be a valid input range"); +static_assert(std::ranges::contiguous_range>, + "gsl::dyn_array should be a valid contiguous range"); #endif /* __cpp_lib_ranges >= 201911L */ TEST(dyn_array_tests, default_ctor) @@ -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 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> rockies{{'a', 1}, {'b', 2}}; + EXPECT_EQ(rockies.begin()->second, 1); + EXPECT_EQ((rockies.begin() + 1)->first, 'b'); + + gsl::dyn_array::iterator it{}; + gsl::dyn_array::const_iterator cit{}; + EXPECT_TRUE(it == gsl::dyn_array::iterator{}); + EXPECT_TRUE(cit == gsl::dyn_array::const_iterator{}); +} + +#if defined(__cpp_lib_span) && (__cpp_lib_span >= 202002L) +TEST(dyn_array_tests, converts_to_a_span) +{ + gsl::dyn_array giants{'a', 'b', 'c'}; + const std::span 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"};