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
122 changes: 122 additions & 0 deletions include/stdx/iterator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

#include <array>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <utility>

namespace stdx {
inline namespace v1 {
Expand All @@ -25,6 +28,125 @@ template <typename T> constexpr auto ct_capacity_v<T const> = ct_capacity_v<T>;
template <typename T> constexpr auto ct_capacity(T &&) -> std::size_t {
return ct_capacity_v<remove_cvref_t<T>>;
}

template <typename T = int, typename = std::enable_if_t<std::is_integral_v<T>>>
struct counting_iterator {
using difference_type = decltype(std::declval<T>() - std::declval<T>());
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 <typename T> counting_iterator(T) -> counting_iterator<T>;

} // namespace v1
} // namespace stdx

Expand Down
108 changes: 108 additions & 0 deletions test/iterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <stdx/span.hpp>

#include <array>
#include <type_traits>

#if __has_include(<span>)
#include <span>
Expand Down Expand Up @@ -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<decltype(a)> == 4u);
}

TEST_CASE("default counting_iterator traits", "[iterator]") {
using T = std::iterator_traits<stdx::counting_iterator<>>;
STATIC_REQUIRE(std::is_same_v<typename T::difference_type, int>);
STATIC_REQUIRE(std::is_same_v<typename T::value_type, int>);
STATIC_REQUIRE(std::is_same_v<typename T::pointer, int *>);
STATIC_REQUIRE(std::is_same_v<typename T::reference, int &>);

#if __cplusplus >= 202002L
STATIC_REQUIRE(std::is_same_v<typename T::iterator_category,
std::contiguous_iterator_tag>);
#else
STATIC_REQUIRE(std::is_same_v<typename T::iterator_category,
std::random_access_iterator_tag>);
#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<decltype(i)>;
STATIC_REQUIRE(std::is_same_v<typename T::difference_type, int>);
STATIC_REQUIRE(std::is_same_v<typename T::value_type, char>);
STATIC_REQUIRE(std::is_same_v<typename T::pointer, char *>);
STATIC_REQUIRE(std::is_same_v<typename T::reference, char &>);
}