Skip to content

Commit 9a8e288

Browse files
committed
Propagate DoubleEndedIterator through chained iterators
1 parent ef36811 commit 9a8e288

File tree

16 files changed

+300
-189
lines changed

16 files changed

+300
-189
lines changed

subspace/containers/__private/slice_iter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ struct [[sus_trivial_abi]] SliceIterMut final
107107
return SliceIterMut(start, len);
108108
}
109109

110+
// sus::iter::Iterator trait.
110111
Option<Item> next() noexcept final {
111112
if (ptr_ == end_) [[unlikely]]
112113
return Option<Item>::none();

subspace/containers/__private/vec_iter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct VecIntoIter final
3939
return VecIntoIter(::sus::move(vec));
4040
}
4141

42+
// sus::iter::Iterator trait.
4243
Option<Item> next() noexcept final {
4344
if (front_index_ == back_index_) [[unlikely]]
4445
return Option<Item>::none();

subspace/iter/__private/adaptors.h

Lines changed: 0 additions & 58 deletions
This file was deleted.

subspace/iter/__private/iterator_loop.h

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,24 @@
2020
namespace sus::iter::__private {
2121

2222
/// An adaptor for range-based for loops.
23-
template <class Iterator>
23+
template <class Iter>
2424
class IteratorLoop final {
25-
using Item = typename std::remove_reference_t<Iterator>::Item;
25+
using Item = typename std::remove_reference_t<Iter>::Item;
2626

2727
public:
28-
IteratorLoop(Iterator iter) noexcept
29-
: iter_(static_cast<Iterator&&>(iter)), item_(iter_.next()) {}
28+
IteratorLoop(Iter&& iter) noexcept
29+
: iter_(::sus::forward<Iter>(iter)), item_(iter_.next()) {}
3030

3131
inline bool operator==(const __private::IteratorEnd&) const noexcept {
32-
return item_.is_nome();
33-
}
34-
inline bool operator!=(const __private::IteratorEnd&) const noexcept {
35-
return item_.is_some();
32+
return item_.is_none();
3633
}
3734
inline void operator++() & noexcept { item_ = iter_.next(); }
38-
inline Item operator*() & noexcept { return item_.take().unwrap(); }
35+
inline Item operator*() & noexcept {
36+
return item_.take().unwrap_unchecked(::sus::marker::unsafe_fn);
37+
}
3938

4039
private:
41-
/* TODO: NonNull<IteratorBase<Item>> */ Iterator iter_;
40+
Iter iter_;
4241
Option<Item> item_;
4342
};
4443

subspace/iter/boxed_iterator.h

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,51 +34,92 @@ class IteratorImpl;
3434
///
3535
/// BoxedIterator is only constructible from an iterator that is not trivially
3636
/// relocatable.
37-
template <class ItemT, size_t SubclassSize, size_t SubclassAlign>
37+
template <class ItemT, size_t SubclassSize, size_t SubclassAlign,
38+
bool DoubleEnded>
3839
class [[sus_trivial_abi]] BoxedIterator final
39-
: public IteratorImpl<BoxedIterator<ItemT, SubclassSize, SubclassAlign>,
40-
ItemT> {
40+
: public IteratorImpl<
41+
BoxedIterator<ItemT, SubclassSize, SubclassAlign, DoubleEnded>,
42+
ItemT> {
4143
public:
4244
using Item = ItemT;
4345

44-
template <::sus::mem::Move IteratorSubclass>
45-
static BoxedIterator with(IteratorSubclass&& subclass) noexcept
46-
requires(::sus::convert::SameOrSubclassOf<
47-
IteratorSubclass*, IteratorImpl<IteratorSubclass, ItemT>*> &&
48-
!::sus::mem::relocate_by_memcpy<IteratorSubclass>)
46+
template <::sus::mem::Move Iter>
47+
static BoxedIterator with(Iter&& iter) noexcept
48+
requires(
49+
::sus::convert::SameOrSubclassOf<Iter*, IteratorImpl<Iter, Item>*> &&
50+
!::sus::mem::relocate_by_memcpy<Iter>)
4951
{
50-
return BoxedIterator(
51-
*new IteratorSubclass(::sus::move(subclass)), // Move it to the heap.
52-
[](IteratorBase<Item>& iter) {
53-
delete static_cast<IteratorSubclass*>(&iter);
54-
});
52+
// IteratorImpl also checks this. It's needed for correctness of the move
53+
// onto the heap.
54+
static_assert(std::is_final_v<Iter>);
55+
56+
if constexpr (DoubleEnded) {
57+
return BoxedIterator(
58+
new Iter(::sus::move(iter)), // Move it to the heap.
59+
[](void* boxed_iter) { delete static_cast<Iter*>(boxed_iter); },
60+
[](void* boxed_iter) {
61+
return static_cast<Iter*>(boxed_iter)->next();
62+
},
63+
[](void* boxed_iter) {
64+
return static_cast<Iter*>(boxed_iter)->next_back();
65+
});
66+
} else {
67+
return BoxedIterator(
68+
new Iter(::sus::move(iter)), // Move it to the heap.
69+
[](void* boxed_iter) { delete static_cast<Iter*>(boxed_iter); },
70+
[](void* boxed_iter) {
71+
return static_cast<Iter*>(boxed_iter)->next();
72+
});
73+
}
5574
}
5675

5776
BoxedIterator(BoxedIterator&& o) noexcept
5877
: iter_(::sus::mem::replace_ptr(mref(o.iter_), nullptr)),
59-
destroy_(::sus::mem::replace_ptr(mref(o.destroy_), nullptr)) {}
78+
destroy_(::sus::mem::replace_ptr(mref(o.destroy_), nullptr)),
79+
next_(::sus::mem::replace_ptr(mref(o.next_), nullptr)),
80+
next_back_(::sus::mem::replace_ptr(mref(o.next_back_), nullptr)) {}
6081
BoxedIterator& operator=(BoxedIterator&& o) noexcept {
61-
if (destroy_) destroy_(*iter_);
82+
if (destroy_) destroy_(iter_);
6283
iter_ = ::sus::mem::replace_ptr(mref(o.iter_), nullptr);
6384
destroy_ = ::sus::mem::replace_ptr(mref(o.destroy_), nullptr);
85+
next_ = ::sus::mem::replace_ptr(mref(o.next_), nullptr);
86+
next_back_ = ::sus::mem::replace_ptr(mref(o.next_back_), nullptr);
6487
}
6588

6689
~BoxedIterator() {
67-
if (destroy_) destroy_(*iter_);
90+
if (destroy_) destroy_(iter_);
6891
}
6992

70-
Option<Item> next() noexcept final { return iter_->next(); }
93+
// sus::iter::Iterator trait.
94+
Option<Item> next() noexcept final { return next_(iter_); }
95+
// sus::iter::DoubleEndedIterator trait.
96+
Option<Item> next_back() noexcept
97+
requires(DoubleEnded)
98+
{
99+
return next_back_(iter_);
100+
}
71101

72102
private:
73-
BoxedIterator(IteratorBase<Item>& iter,
74-
void (*destroy)(IteratorBase<Item>& boxed))
75-
: iter_(&iter), destroy_(destroy) {}
103+
// DoubleEnded constructor.
104+
BoxedIterator(void* iter, void (*destroy)(void* boxed_iter),
105+
Option<Item> (*next)(void* boxed_iter),
106+
Option<Item> (*next_back)(void* boxed_iter))
107+
: iter_(iter), destroy_(destroy), next_(next), next_back_(next_back) {}
108+
// Not-DoubleEnded constructor.
109+
BoxedIterator(void* iter, void (*destroy)(void* boxed_iter),
110+
Option<Item> (*next)(void* boxed_iter))
111+
: iter_(iter), destroy_(destroy), next_(next), next_back_(nullptr) {}
76112

77-
IteratorBase<Item>* iter_;
78-
void (*destroy_)(IteratorBase<Item>& boxed);
113+
void* iter_;
114+
void (*destroy_)(void* boxed_iter);
115+
Option<Item> (*next_)(void* boxed_iter);
116+
// TODO: We could remove this field with a nested struct + template
117+
// specialization when DoubleEnded is false.
118+
Option<Item> (*next_back_)(void* boxed_iter);
79119

80120
sus_class_trivially_relocatable(::sus::marker::unsafe_fn, decltype(iter_),
81-
decltype(destroy_));
121+
decltype(destroy_), decltype(next_),
122+
decltype(next_back_));
82123
};
83124

84125
} // namespace sus::iter

subspace/iter/empty.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ class [[sus_trivial_abi]] Empty final
2828

2929
constexpr Empty() = default;
3030

31+
// sus::iter::Iterator trait.
3132
constexpr Option<Item> next() noexcept final {
3233
return sus::Option<Item>::none();
3334
}
35+
// sus::iter::DoubleEndedIterator trait.
36+
Option<Item> next_back() noexcept { return sus::Option<Item>::none(); }
3437

3538
private:
3639
sus_class_trivially_relocatable(::sus::marker::unsafe_fn);

subspace/iter/filter.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,45 @@ namespace sus::iter {
2424

2525
using ::sus::mem::relocate_by_memcpy;
2626

27-
template <class ItemT, size_t InnerIterSize, size_t InnerIterAlign>
28-
class Filter final
29-
: public IteratorImpl<Filter<ItemT, InnerIterSize, InnerIterAlign>, ItemT> {
27+
template <class InnerSizedIter>
28+
class [[sus_trivial_abi]] Filter final
29+
: public IteratorImpl<Filter<InnerSizedIter>,
30+
typename InnerSizedIter::Item> {
3031
using Pred = ::sus::fn::FnMut<bool(
3132
// TODO: write a sus::const_ref<T>?
32-
const std::remove_reference_t<const std::remove_reference_t<ItemT>&>&)>;
33-
using InnerSizedIter = SizedIterator<ItemT, InnerIterSize, InnerIterAlign>;
33+
const std::remove_reference_t<typename InnerSizedIter::Item>&)>;
3434

3535
public:
36-
using Item = ItemT;
36+
using Item = InnerSizedIter::Item;
3737

3838
static Filter with(Pred&& pred, InnerSizedIter&& next_iter) noexcept {
3939
return Filter(::sus::move(pred), ::sus::move(next_iter));
4040
}
4141

42+
// sus::iter::Iterator trait.
4243
Option<Item> next() noexcept final {
43-
IteratorBase<Item>& next_iter = next_iter_.iterator_mut();
44+
InnerSizedIter& iter = next_iter_;
4445
Pred& pred = pred_;
4546

4647
// TODO: Just call find(pred) on itself?
4748
while (true) {
48-
Option<Item> item = next_iter.next();
49+
Option<Item> item = iter.next();
50+
if (item.is_none() ||
51+
pred(item.as_ref().unwrap_unchecked(::sus::marker::unsafe_fn)))
52+
return item;
53+
}
54+
}
55+
56+
// sus::iter::DoubleEndedIterator trait.
57+
Option<Item> next_back() noexcept
58+
requires(InnerSizedIter::DoubleEnded)
59+
{
60+
InnerSizedIter& iter = next_iter_;
61+
Pred& pred = pred_;
62+
63+
// TODO: Just call find(pred) on itself?
64+
while (true) {
65+
Option<Item> item = iter.next_back();
4966
if (item.is_none() ||
5067
pred(item.as_ref().unwrap_unchecked(::sus::marker::unsafe_fn)))
5168
return item;

subspace/iter/iterator_concept.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919

2020
#include "subspace/convert/subclass.h"
2121
#include "subspace/mem/forward.h"
22-
#include "subspace/option/option.h"
22+
23+
namespace sus::option {
24+
template <class T>
25+
class Option;
26+
}
2327

2428
namespace sus::iter {
2529

@@ -28,7 +32,7 @@ class IteratorImpl;
2832

2933
/// A concept for all implementations of iterators.
3034
///
31-
/// An iterator has a method, `next()`, which when called, returns an
35+
/// An iterator has one required method, `next()`, which when called, returns an
3236
/// `Option<Item>`. Calling next will return an `Option` containing the next
3337
/// `Item` as long as there are elements, and once they've all been exhausted,
3438
/// will return `None` to indicate that iteration is finished. Individual
@@ -45,14 +49,16 @@ class IteratorImpl;
4549
/// Iterators are also composable, and it's possible to chain them together to
4650
/// do more complex forms of processing.
4751
template <class T, class Item>
48-
concept Iterator = requires {
52+
concept Iterator = requires(T& t) {
4953
// Has a T::Item typename.
5054
requires(!std::is_void_v<typename std::decay_t<T>::Item>);
5155
// The T::Item matches the concept input.
5256
requires(std::same_as<typename std::decay_t<T>::Item, Item>);
5357
// Subclasses from IteratorImpl<T, T::Item>.
5458
requires ::sus::convert::SameOrSubclassOf<
5559
std::decay_t<T>*, IteratorImpl<std::decay_t<T>, Item>*>;
60+
// Required methods.
61+
{ t.next() } -> std::same_as<::sus::option::Option<Item>>;
5662
};
5763

5864
/// An `Iterator` able to yield elements from both ends.

0 commit comments

Comments
 (0)