diff --git a/include/stdx/iterator.hpp b/include/stdx/iterator.hpp index a127a98..fda8c56 100644 --- a/include/stdx/iterator.hpp +++ b/include/stdx/iterator.hpp @@ -4,6 +4,9 @@ #include #include +#include +#include +#include namespace stdx { inline namespace v1 { @@ -25,6 +28,125 @@ template constexpr auto ct_capacity_v = ct_capacity_v; template constexpr auto ct_capacity(T &&) -> std::size_t { return ct_capacity_v>; } + +template >> +struct counting_iterator { + using difference_type = decltype(std::declval() - std::declval()); + using value_type = T; + using reference = T &; + using const_reference = T const &; + using pointer = T *; + using const_pointer = T const *; +#if __cplusplus >= 202002L + using iterator_category = std::contiguous_iterator_tag; +#else + using iterator_category = std::random_access_iterator_tag; +#endif + + auto operator*() -> reference { return i; } + auto operator*() const -> const_reference { return i; } + + constexpr auto operator++() -> counting_iterator & { + ++i; + return *this; + } + [[nodiscard]] constexpr auto operator++(int) -> counting_iterator { + auto tmp = *this; + ++(*this); + return tmp; + } + constexpr auto operator--() -> counting_iterator & { + --i; + return *this; + } + [[nodiscard]] constexpr auto operator--(int) -> counting_iterator { + auto tmp = *this; + --(*this); + return tmp; + } + + constexpr auto operator+=(difference_type d) -> counting_iterator & { + i += d; + return *this; + } + constexpr auto operator-=(difference_type d) -> counting_iterator & { + i -= d; + return *this; + } + constexpr auto advance(difference_type d) -> counting_iterator & { + i += d; + return *this; + } + + [[nodiscard]] friend constexpr auto operator+(counting_iterator ci, + difference_type d) + -> counting_iterator { + ci += d; + return ci; + } + [[nodiscard]] friend constexpr auto operator+(difference_type d, + counting_iterator ci) + -> counting_iterator { + ci += d; + return ci; + } + [[nodiscard]] friend constexpr auto operator-(counting_iterator ci, + difference_type d) + -> counting_iterator { + ci -= d; + return ci; + } + [[nodiscard]] friend constexpr auto operator-(counting_iterator x, + counting_iterator y) + -> difference_type { + return x.i - y.i; + } + + [[nodiscard]] friend constexpr auto operator==(counting_iterator const &x, + counting_iterator const &y) + -> bool { + return x.i == y.i; + } + +#if __cpp_impl_three_way_comparison >= 201907L + [[nodiscard]] friend constexpr auto + operator<=>(counting_iterator const &x, counting_iterator const &y) { + return x.i <=> y.i; + } +#else + [[nodiscard]] friend constexpr auto operator!=(counting_iterator const &x, + counting_iterator const &y) + -> bool { + return not(x == y); + } + + [[nodiscard]] friend constexpr auto operator<(counting_iterator const &x, + counting_iterator const &y) + -> bool { + return x.i < y.i; + } + [[nodiscard]] friend constexpr auto operator<=(counting_iterator const &x, + counting_iterator const &y) + -> bool { + return not(y < x); + } + [[nodiscard]] friend constexpr auto operator>(counting_iterator const &x, + counting_iterator const &y) + -> bool { + return y < x; + } + [[nodiscard]] friend constexpr auto operator>=(counting_iterator const &x, + counting_iterator const &y) + -> bool { + return not(x < y); + } +#endif + + T i{}; +}; + +template counting_iterator(T) -> counting_iterator; + } // namespace v1 } // namespace stdx diff --git a/test/iterator.cpp b/test/iterator.cpp index ab82d50..a20ba1e 100644 --- a/test/iterator.cpp +++ b/test/iterator.cpp @@ -7,6 +7,7 @@ #include #include +#include #if __has_include() #include @@ -67,3 +68,110 @@ TEST_CASE("compile-time capacity variable template (const)", "[iterator]") { std::array const a{1, 2, 3, 4}; STATIC_REQUIRE(stdx::ct_capacity_v == 4u); } + +TEST_CASE("default counting_iterator traits", "[iterator]") { + using T = std::iterator_traits>; + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + +#if __cplusplus >= 202002L + STATIC_REQUIRE(std::is_same_v); +#else + STATIC_REQUIRE(std::is_same_v); +#endif +} + +TEST_CASE("default counting_iterator value is 0, increment is 1", + "[iterator]") { + auto i = stdx::counting_iterator{}; + CHECK(*i == 0); + ++i; + CHECK(*i == 1); +} + +TEST_CASE("counting_iterator is an input iterator", "[iterator]") { + auto i = stdx::counting_iterator{}; + auto v = *i; + CHECK(v == 0); + ++i; + CHECK(*i != v); +} + +TEST_CASE("counting_iterator is a forward iterator", "[iterator]") { + auto i = stdx::counting_iterator{}; + auto v = *i; + auto j = i++; + CHECK(*j == v); + CHECK(*i - v == 1); + ++j; + CHECK(*i == *j); +} + +TEST_CASE("counting_iterator is a bidi iterator", "[iterator]") { + auto i = stdx::counting_iterator{}; + auto v = *i; + auto j = i--; + CHECK(*j == v); + CHECK(v - *i == 1); + --j; + CHECK(*i == *j); +} + +TEST_CASE("counting_iterator is a random access iterator", "[iterator]") { + auto i = stdx::counting_iterator{}; + CHECK(*(i + 1) - *i == 1); + CHECK(*i - *(i - 1) == 1); + + i += 1; + CHECK(*i == 1); + CHECK(i - stdx::counting_iterator{} == 1); + i -= 1; + CHECK(*i == 0); +} + +TEST_CASE("counting_iterator equality", "[iterator]") { + auto i = stdx::counting_iterator{}; + CHECK(i == stdx::counting_iterator{}); +} + +TEST_CASE("counting_iterator comparison", "[iterator]") { + auto i = stdx::counting_iterator{}; + auto j = i++; + CHECK(j < i); + CHECK(j <= i); + CHECK(i > j); + CHECK(i >= j); +} + +#if __cpp_impl_three_way_comparison >= 201907L +TEST_CASE("counting_iterator spaceship comparison", "[iterator]") { + auto i = stdx::counting_iterator{}; + auto j = i++; + CHECK(i <=> i == std::strong_ordering::equal); + CHECK(j <=> i == std::strong_ordering::less); +} +#endif + +TEST_CASE("counting_iterator can be given a starting value", "[iterator]") { + auto i = stdx::counting_iterator{17}; + CHECK(*i == 17); + ++i; + CHECK(*i == 18); +} + +TEST_CASE("counting_iterator can be given a different type", "[iterator]") { + auto i = stdx::counting_iterator{'A'}; + CHECK(*i == 'A'); + ++i; + CHECK(*i == 'B'); + + using T = std::iterator_traits; + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); +}