Skip to content

Commit e38fd0d

Browse files
committed
Make array iterable
1 parent 64e450d commit e38fd0d

File tree

11 files changed

+276
-40
lines changed

11 files changed

+276
-40
lines changed

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ add_library(subspace STATIC
2929
"assertions/unreachable.h"
3030
"assertions/windows.h"
3131
"containers/array.h"
32+
"containers/array_iter.h"
3233
"construct/from.h"
3334
"construct/into.h"
3435
"construct/make_default.h"
@@ -39,9 +40,10 @@ add_library(subspace STATIC
3940
"fn/fn_bind.h"
4041
"fn/fn_defn.h"
4142
"fn/fn_impl.h"
43+
"iter/__private/iterator_end.h"
44+
"iter/__private/iterator_loop.h"
4245
"iter/filter.h"
4346
"iter/iterator.h"
44-
"iter/iterator.cc"
4547
"iter/iterator_defn.h"
4648
"iter/iterator_impl.h"
4749
"iter/once.h"

containers/array.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "assertions/check.h"
2424
#include "construct/make_default.h"
25+
#include "containers/array_iter.h"
2526
#include "fn/callable.h"
2627
#include "marker/unsafe.h"
2728
#include "mem/move.h"
@@ -125,6 +126,19 @@ class Array final {
125126
return storage_.data_;
126127
}
127128

129+
::sus::iter::Iterator<ArrayIter<T, N>> iter() const& {
130+
return ArrayIter<T, N>::with(*this);
131+
}
132+
::sus::iter::Iterator<ArrayIter<T, N>> iter() && = delete;
133+
134+
::sus::iter::Iterator<ArrayIterMut<T, N>> iter_mut() & {
135+
return ArrayIterMut<T, N>::with(*this);
136+
}
137+
138+
::sus::iter::Iterator<ArrayIntoIter<T, N>> into_iter() && {
139+
return ArrayIntoIter<T, N>::with(static_cast<Array&&>(*this));
140+
}
141+
128142
/// sus::num::Eq<Array<U, N>> trait.
129143
template <::sus::num::Eq<T> U>
130144
constexpr bool operator==(const Array<U, N>& r) const& noexcept {
@@ -222,6 +236,10 @@ constexpr inline auto operator<=>(const Array<T, N>& l,
222236
std::make_index_sequence<N>());
223237
}
224238

239+
// Implicit for-ranged loop iteration via `Array::iter()`.
240+
using ::sus::iter::__private::begin;
241+
using ::sus::iter::__private::end;
242+
225243
} // namespace sus::containers
226244

227245
namespace sus {

containers/array_iter.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <stdint.h>
18+
19+
#include <type_traits>
20+
21+
#include "iter/iterator_defn.h"
22+
#include "mem/move.h"
23+
#include "num/unsigned_integer.h"
24+
25+
namespace sus::containers {
26+
27+
template <class T, size_t N>
28+
requires(N <= PTRDIFF_MAX)
29+
class Array;
30+
31+
template <class Item, size_t N>
32+
requires(std::is_move_constructible_v<Item>)
33+
struct ArrayIter : public ::sus::iter::IteratorBase<const Item&> {
34+
public:
35+
static constexpr auto with(const Array<Item, N>& array) noexcept {
36+
return ::sus::iter::Iterator<ArrayIter>(array);
37+
}
38+
39+
Option<const Item&> next() noexcept final {
40+
if (next_index.primitive_value == N) return Option<const Item&>::none();
41+
return Option<const Item&>::some(
42+
array.get(::sus::mem::replace(mref(next_index), next_index + 1_usize)));
43+
}
44+
45+
protected:
46+
ArrayIter(const Array<Item, N>& array) noexcept : array(array) {}
47+
48+
private:
49+
usize next_index = 0_usize;
50+
const Array<Item, N>& array;
51+
};
52+
53+
template <class Item, size_t N>
54+
requires(std::is_move_constructible_v<Item>)
55+
struct ArrayIterMut : public ::sus::iter::IteratorBase<Item&> {
56+
public:
57+
static constexpr auto with(Array<Item, N>& array) noexcept {
58+
return ::sus::iter::Iterator<ArrayIterMut>(array);
59+
}
60+
61+
Option<Item&> next() noexcept final {
62+
if (next_index.primitive_value == N) return Option<Item&>::none();
63+
return Option<Item&>::some(mref(array.get_mut(
64+
::sus::mem::replace(mref(next_index), next_index + 1_usize))));
65+
}
66+
67+
protected:
68+
ArrayIterMut(Array<Item, N>& array) noexcept : array(array) {}
69+
70+
private:
71+
usize next_index = 0_usize;
72+
Array<Item, N>& array;
73+
};
74+
75+
template <class Item, size_t N>
76+
requires(std::is_move_constructible_v<Item>)
77+
struct ArrayIntoIter : public ::sus::iter::IteratorBase<Item> {
78+
public:
79+
static constexpr auto with(Array<Item, N>&& array) noexcept {
80+
return ::sus::iter::Iterator<ArrayIntoIter>(move(array));
81+
}
82+
83+
Option<Item> next() noexcept final {
84+
if (next_index.primitive_value == N) return Option<Item>::none();
85+
return Option<Item>::some(move(array.get_mut(
86+
::sus::mem::replace(mref(next_index), next_index + 1_usize))));
87+
}
88+
89+
protected:
90+
ArrayIntoIter(Array<Item, N>&& array) noexcept : array(move(array)) {}
91+
92+
private:
93+
usize next_index = 0_usize;
94+
Array<Item, N> array;
95+
};
96+
97+
} // namespace sus::containers

containers/array_unittest.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
#include <type_traits>
1818

1919
#include "construct/into.h"
20+
#include "iter/iterator.h"
2021
#include "marker/unsafe.h"
22+
#include "mem/move.h"
2123
#include "mem/relocate.h"
2224
#include "num/types.h"
2325
#include "third_party/googletest/googletest/include/gtest/gtest.h"
@@ -255,4 +257,47 @@ TEST(Array, PartialOrder) {
255257
EXPECT_EQ(std::partial_order(a, b), std::partial_ordering::less);
256258
}
257259

260+
TEST(Array, Iter) {
261+
const auto a = Array<usize, 5>::with_value(3u);
262+
auto sum = 0_usize;
263+
for (const usize& i : a.iter()) {
264+
sum += i;
265+
}
266+
EXPECT_EQ(sum, 15_usize);
267+
}
268+
269+
TEST(Array, IterMut) {
270+
auto a = Array<usize, 5>::with_value(3u);
271+
auto sum = 0_usize;
272+
for (usize& i : a.iter_mut()) {
273+
sum += i;
274+
i += 1_usize;
275+
}
276+
EXPECT_EQ(sum, 15_usize);
277+
278+
sum = 0_usize;
279+
for (const usize& i : a.iter()) {
280+
sum += i;
281+
}
282+
EXPECT_EQ(sum, 20_usize);
283+
}
284+
285+
TEST(Array, IntoIter) {
286+
auto a = Array<usize, 5>::with_value(3u);
287+
auto sum = 0_usize;
288+
for (usize i : sus::move(a).into_iter()) {
289+
sum += i;
290+
}
291+
EXPECT_EQ(sum, 15_usize);
292+
}
293+
294+
TEST(Array, ImplicitIter) {
295+
const auto a = Array<usize, 5>::with_value(3u);
296+
auto sum = 0_usize;
297+
for (const usize& i : a) {
298+
sum += i;
299+
}
300+
EXPECT_EQ(sum, 15_usize);
301+
}
302+
258303
} // namespace

iter/iterator.cc renamed to iter/__private/iterator_end.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
#include "iter/iterator.h"
15+
#pragma once
1616

17-
namespace sus::iter {
18-
const IteratorEnd iterator_end;
19-
}
17+
namespace sus::iter::__private {
18+
19+
struct IteratorEnd {};
20+
21+
} // namespace sus::iter::__private

iter/__private/iterator_loop.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "option/option.h"
18+
19+
namespace sus::iter::__private {
20+
21+
/// An adaptor for range-based for loops.
22+
template <class Iterator>
23+
class IteratorLoop final {
24+
using Item = typename std::remove_reference_t<Iterator>::Item;
25+
26+
public:
27+
IteratorLoop(Iterator iter)
28+
: iter_(static_cast<Iterator&&>(iter)), item_(iter_.next()) {}
29+
30+
inline bool operator==(const __private::IteratorEnd&) const {
31+
return item_.is_nome();
32+
}
33+
inline bool operator!=(const __private::IteratorEnd&) const {
34+
return item_.is_some();
35+
}
36+
inline void operator++() & { item_ = iter_.next(); }
37+
inline Item operator*() & { return item_.take().unwrap(); }
38+
39+
private:
40+
/* TODO: NonNull<IteratorBase<Item>> */ Iterator iter_;
41+
Option<Item> item_;
42+
};
43+
44+
// ADL helpers to call T::iter() in a range-based for loop, which will call
45+
// `begin(T)`.
46+
47+
template <class T>
48+
constexpr auto begin(const T& t) noexcept {
49+
return IteratorLoop(t.iter());
50+
}
51+
52+
template <class T>
53+
constexpr auto end(const T&) noexcept {
54+
return ::sus::iter::__private::IteratorEnd();
55+
}
56+
57+
} // namespace sus::iter::__private

iter/iterator_defn.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
#pragma once
1616

1717
#include "fn/fn_defn.h"
18-
#include "option/option.h"
1918
#include "num/unsigned_integer.h"
19+
#include "option/option.h"
2020

2121
namespace sus::iter {
2222

@@ -26,10 +26,20 @@ using ::sus::option::Option;
2626
template <class Item, size_t InnerIterSize, size_t InnerIterAlign>
2727
class Filter;
2828

29+
namespace __private {
2930
template <class IteratorBase>
3031
class IteratorLoop;
32+
33+
template <class Iterator>
34+
class IteratorImplicitLoop;
3135
struct IteratorEnd;
3236

37+
template <class T>
38+
constexpr auto begin(const T& t) noexcept;
39+
template <class T>
40+
constexpr auto end(const T& t) noexcept;
41+
} // namespace __private
42+
3343
// TODO: Do we want to be able to pass IteratorBase& as a "generic" iterator?
3444
// Then it needs access to the adapator methods of Iterator<T>, so make them
3545
// virtual methods on IteratorBase?
@@ -89,9 +99,9 @@ class IteratorBase {
8999
virtual usize count() noexcept;
90100

91101
/// Adaptor for use in ranged for loops.
92-
IteratorLoop<IteratorBase<Item>> begin() & noexcept;
102+
__private::IteratorLoop<IteratorBase<Item>&> begin() & noexcept;
93103
/// Adaptor for use in ranged for loops.
94-
IteratorEnd end() & noexcept;
104+
__private::IteratorEnd end() & noexcept;
95105

96106
protected:
97107
IteratorBase() = default;
@@ -101,7 +111,9 @@ template <class I>
101111
class Iterator final : public I {
102112
private:
103113
using typename I::Item;
104-
friend I; // I::foo() can construct Iterator<I>.
114+
template <class T>
115+
friend class __private::IteratorLoop; // Can see Item.
116+
friend I; // I::foo() can construct Iterator<I>.
105117

106118
template <class J>
107119
friend class Iterator; // Iterator<J>::foo() can construct Iterator<I>.

iter/iterator_impl.h

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#pragma once
1616

1717
#include "fn/fn.h"
18+
#include "iter/__private/iterator_end.h"
19+
#include "iter/__private/iterator_loop.h"
1820
#include "iter/iterator_defn.h"
1921
#include "iter/sized_iterator.h"
2022
#include "mem/move.h"
@@ -25,35 +27,15 @@
2527

2628
namespace sus::iter {
2729

28-
struct IteratorEnd {};
29-
extern const IteratorEnd iterator_end;
30-
31-
/// An adaptor for range-based for loops.
32-
template <class IteratorBase>
33-
class IteratorLoop final {
34-
using Item = typename IteratorBase::Item;
35-
36-
public:
37-
IteratorLoop(IteratorBase& iter) : iter_(iter), item_(iter_.next()) {}
38-
39-
inline bool operator==(const IteratorEnd&) const { return item_.is_nome(); }
40-
inline bool operator!=(const IteratorEnd&) const { return item_.is_some(); }
41-
inline void operator++() & { item_ = iter_.next(); }
42-
inline Item operator*() & { return item_.take().unwrap(); }
43-
44-
private:
45-
/* TODO: NonNull<IteratorBase<Item>> */ IteratorBase& iter_;
46-
Option<Item> item_;
47-
};
48-
4930
template <class Item>
50-
IteratorLoop<IteratorBase<Item>> IteratorBase<Item>::begin() & noexcept {
31+
__private::IteratorLoop<IteratorBase<Item>&>
32+
IteratorBase<Item>::begin() & noexcept {
5133
return {*this};
5234
}
5335

5436
template <class Item>
55-
IteratorEnd IteratorBase<Item>::end() & noexcept {
56-
return iterator_end;
37+
__private::IteratorEnd IteratorBase<Item>::end() & noexcept {
38+
return __private::IteratorEnd();
5739
}
5840

5941
template <class Item>

mem/mref.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ struct Mref<T&> final {
8686
constexpr Mref& operator=(Mref&&) noexcept = default;
8787

8888
// Prevent constructing an Mref argument without writing mref().
89-
Mref(T& t) = delete;
89+
consteval Mref(T& t) = delete;
9090
// Prevent passing an Mref argument along without writing mref() again.
9191
Mref(Mref&) = delete;
9292

0 commit comments

Comments
 (0)