Skip to content

Commit 0d77b76

Browse files
authored
Merge pull request #292 from elbeno/counting-iterator
✨ Add `counting_iterator`
2 parents aed7974 + d9e31c9 commit 0d77b76

File tree

2 files changed

+230
-0
lines changed

2 files changed

+230
-0
lines changed

include/stdx/iterator.hpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
#include <array>
66
#include <cstddef>
7+
#include <iterator>
8+
#include <type_traits>
9+
#include <utility>
710

811
namespace stdx {
912
inline namespace v1 {
@@ -25,6 +28,125 @@ template <typename T> constexpr auto ct_capacity_v<T const> = ct_capacity_v<T>;
2528
template <typename T> constexpr auto ct_capacity(T &&) -> std::size_t {
2629
return ct_capacity_v<remove_cvref_t<T>>;
2730
}
31+
32+
template <typename T = int, typename = std::enable_if_t<std::is_integral_v<T>>>
33+
struct counting_iterator {
34+
using difference_type = decltype(std::declval<T>() - std::declval<T>());
35+
using value_type = T;
36+
using reference = T &;
37+
using const_reference = T const &;
38+
using pointer = T *;
39+
using const_pointer = T const *;
40+
#if __cplusplus >= 202002L
41+
using iterator_category = std::contiguous_iterator_tag;
42+
#else
43+
using iterator_category = std::random_access_iterator_tag;
44+
#endif
45+
46+
auto operator*() -> reference { return i; }
47+
auto operator*() const -> const_reference { return i; }
48+
49+
constexpr auto operator++() -> counting_iterator & {
50+
++i;
51+
return *this;
52+
}
53+
[[nodiscard]] constexpr auto operator++(int) -> counting_iterator {
54+
auto tmp = *this;
55+
++(*this);
56+
return tmp;
57+
}
58+
constexpr auto operator--() -> counting_iterator & {
59+
--i;
60+
return *this;
61+
}
62+
[[nodiscard]] constexpr auto operator--(int) -> counting_iterator {
63+
auto tmp = *this;
64+
--(*this);
65+
return tmp;
66+
}
67+
68+
constexpr auto operator+=(difference_type d) -> counting_iterator & {
69+
i += d;
70+
return *this;
71+
}
72+
constexpr auto operator-=(difference_type d) -> counting_iterator & {
73+
i -= d;
74+
return *this;
75+
}
76+
constexpr auto advance(difference_type d) -> counting_iterator & {
77+
i += d;
78+
return *this;
79+
}
80+
81+
[[nodiscard]] friend constexpr auto operator+(counting_iterator ci,
82+
difference_type d)
83+
-> counting_iterator {
84+
ci += d;
85+
return ci;
86+
}
87+
[[nodiscard]] friend constexpr auto operator+(difference_type d,
88+
counting_iterator ci)
89+
-> counting_iterator {
90+
ci += d;
91+
return ci;
92+
}
93+
[[nodiscard]] friend constexpr auto operator-(counting_iterator ci,
94+
difference_type d)
95+
-> counting_iterator {
96+
ci -= d;
97+
return ci;
98+
}
99+
[[nodiscard]] friend constexpr auto operator-(counting_iterator x,
100+
counting_iterator y)
101+
-> difference_type {
102+
return x.i - y.i;
103+
}
104+
105+
[[nodiscard]] friend constexpr auto operator==(counting_iterator const &x,
106+
counting_iterator const &y)
107+
-> bool {
108+
return x.i == y.i;
109+
}
110+
111+
#if __cpp_impl_three_way_comparison >= 201907L
112+
[[nodiscard]] friend constexpr auto
113+
operator<=>(counting_iterator const &x, counting_iterator const &y) {
114+
return x.i <=> y.i;
115+
}
116+
#else
117+
[[nodiscard]] friend constexpr auto operator!=(counting_iterator const &x,
118+
counting_iterator const &y)
119+
-> bool {
120+
return not(x == y);
121+
}
122+
123+
[[nodiscard]] friend constexpr auto operator<(counting_iterator const &x,
124+
counting_iterator const &y)
125+
-> bool {
126+
return x.i < y.i;
127+
}
128+
[[nodiscard]] friend constexpr auto operator<=(counting_iterator const &x,
129+
counting_iterator const &y)
130+
-> bool {
131+
return not(y < x);
132+
}
133+
[[nodiscard]] friend constexpr auto operator>(counting_iterator const &x,
134+
counting_iterator const &y)
135+
-> bool {
136+
return y < x;
137+
}
138+
[[nodiscard]] friend constexpr auto operator>=(counting_iterator const &x,
139+
counting_iterator const &y)
140+
-> bool {
141+
return not(x < y);
142+
}
143+
#endif
144+
145+
T i{};
146+
};
147+
148+
template <typename T> counting_iterator(T) -> counting_iterator<T>;
149+
28150
} // namespace v1
29151
} // namespace stdx
30152

test/iterator.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <stdx/span.hpp>
88

99
#include <array>
10+
#include <type_traits>
1011

1112
#if __has_include(<span>)
1213
#include <span>
@@ -67,3 +68,110 @@ TEST_CASE("compile-time capacity variable template (const)", "[iterator]") {
6768
std::array const a{1, 2, 3, 4};
6869
STATIC_REQUIRE(stdx::ct_capacity_v<decltype(a)> == 4u);
6970
}
71+
72+
TEST_CASE("default counting_iterator traits", "[iterator]") {
73+
using T = std::iterator_traits<stdx::counting_iterator<>>;
74+
STATIC_REQUIRE(std::is_same_v<typename T::difference_type, int>);
75+
STATIC_REQUIRE(std::is_same_v<typename T::value_type, int>);
76+
STATIC_REQUIRE(std::is_same_v<typename T::pointer, int *>);
77+
STATIC_REQUIRE(std::is_same_v<typename T::reference, int &>);
78+
79+
#if __cplusplus >= 202002L
80+
STATIC_REQUIRE(std::is_same_v<typename T::iterator_category,
81+
std::contiguous_iterator_tag>);
82+
#else
83+
STATIC_REQUIRE(std::is_same_v<typename T::iterator_category,
84+
std::random_access_iterator_tag>);
85+
#endif
86+
}
87+
88+
TEST_CASE("default counting_iterator value is 0, increment is 1",
89+
"[iterator]") {
90+
auto i = stdx::counting_iterator{};
91+
CHECK(*i == 0);
92+
++i;
93+
CHECK(*i == 1);
94+
}
95+
96+
TEST_CASE("counting_iterator is an input iterator", "[iterator]") {
97+
auto i = stdx::counting_iterator{};
98+
auto v = *i;
99+
CHECK(v == 0);
100+
++i;
101+
CHECK(*i != v);
102+
}
103+
104+
TEST_CASE("counting_iterator is a forward iterator", "[iterator]") {
105+
auto i = stdx::counting_iterator{};
106+
auto v = *i;
107+
auto j = i++;
108+
CHECK(*j == v);
109+
CHECK(*i - v == 1);
110+
++j;
111+
CHECK(*i == *j);
112+
}
113+
114+
TEST_CASE("counting_iterator is a bidi iterator", "[iterator]") {
115+
auto i = stdx::counting_iterator{};
116+
auto v = *i;
117+
auto j = i--;
118+
CHECK(*j == v);
119+
CHECK(v - *i == 1);
120+
--j;
121+
CHECK(*i == *j);
122+
}
123+
124+
TEST_CASE("counting_iterator is a random access iterator", "[iterator]") {
125+
auto i = stdx::counting_iterator{};
126+
CHECK(*(i + 1) - *i == 1);
127+
CHECK(*i - *(i - 1) == 1);
128+
129+
i += 1;
130+
CHECK(*i == 1);
131+
CHECK(i - stdx::counting_iterator{} == 1);
132+
i -= 1;
133+
CHECK(*i == 0);
134+
}
135+
136+
TEST_CASE("counting_iterator equality", "[iterator]") {
137+
auto i = stdx::counting_iterator{};
138+
CHECK(i == stdx::counting_iterator{});
139+
}
140+
141+
TEST_CASE("counting_iterator comparison", "[iterator]") {
142+
auto i = stdx::counting_iterator{};
143+
auto j = i++;
144+
CHECK(j < i);
145+
CHECK(j <= i);
146+
CHECK(i > j);
147+
CHECK(i >= j);
148+
}
149+
150+
#if __cpp_impl_three_way_comparison >= 201907L
151+
TEST_CASE("counting_iterator spaceship comparison", "[iterator]") {
152+
auto i = stdx::counting_iterator{};
153+
auto j = i++;
154+
CHECK(i <=> i == std::strong_ordering::equal);
155+
CHECK(j <=> i == std::strong_ordering::less);
156+
}
157+
#endif
158+
159+
TEST_CASE("counting_iterator can be given a starting value", "[iterator]") {
160+
auto i = stdx::counting_iterator{17};
161+
CHECK(*i == 17);
162+
++i;
163+
CHECK(*i == 18);
164+
}
165+
166+
TEST_CASE("counting_iterator can be given a different type", "[iterator]") {
167+
auto i = stdx::counting_iterator{'A'};
168+
CHECK(*i == 'A');
169+
++i;
170+
CHECK(*i == 'B');
171+
172+
using T = std::iterator_traits<decltype(i)>;
173+
STATIC_REQUIRE(std::is_same_v<typename T::difference_type, int>);
174+
STATIC_REQUIRE(std::is_same_v<typename T::value_type, char>);
175+
STATIC_REQUIRE(std::is_same_v<typename T::pointer, char *>);
176+
STATIC_REQUIRE(std::is_same_v<typename T::reference, char &>);
177+
}

0 commit comments

Comments
 (0)