Skip to content

Commit 163fc76

Browse files
committed
Add join_with. Implement join as join_with that uses an empty joiner range.
1 parent c23cc73 commit 163fc76

File tree

6 files changed

+456
-191
lines changed

6 files changed

+456
-191
lines changed

source/containers/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ target_sources(containers PUBLIC
5050
algorithms/generate.cpp
5151
algorithms/generate_until.cpp
5252
algorithms/join.cpp
53+
algorithms/join_with.cpp
5354
algorithms/keyed_binary_search.cpp
5455
algorithms/keyed_erase.cpp
5556
algorithms/keyed_insert.cpp
@@ -233,6 +234,7 @@ target_sources(containers_test PRIVATE
233234
test/algorithms/filter.cpp
234235
test/algorithms/find.cpp
235236
test/algorithms/join.cpp
237+
test/algorithms/join_with.cpp
236238
test/algorithms/keyed_binary_search.cpp
237239
test/algorithms/set.cpp
238240
test/algorithms/split.cpp

source/containers/algorithms/join.cpp

Lines changed: 10 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -9,203 +9,23 @@ module;
99

1010
export module containers.algorithms.join;
1111

12-
import containers.begin_end;
13-
export import containers.common_iterator_functions;
14-
import containers.is_empty;
15-
import containers.iter_difference_t;
16-
import containers.iter_reference_t;
17-
import containers.iter_value_t;
18-
import containers.iterator_t;
12+
import containers.algorithms.join_with;
13+
import containers.array;
1914
import containers.range;
20-
import containers.range_reference_t;
21-
import containers.range_size_t;
22-
import containers.sentinel_t;
15+
import containers.range_value_t;
2316

2417
import bounded;
25-
import std_module;
26-
import tv;
2718

2819
namespace containers {
2920
using namespace bounded::literal;
3021

31-
template<typename Range>
32-
struct inner_range {
33-
inner_range() = default;
34-
inner_range(inner_range &&) = default;
35-
inner_range(inner_range const &) = delete;
36-
auto operator=(inner_range &&) & -> inner_range & = default;
37-
auto operator=(inner_range const &) & -> inner_range & = delete;
38-
39-
constexpr auto set(auto const & outer_it) & -> void {
40-
m_range.emplace([&] { return *outer_it; });
41-
m_it = containers::begin(std::move(*m_range));
42-
}
43-
44-
constexpr auto begin() const -> decltype(auto) {
45-
return m_it;
46-
}
47-
// Requires that `set` has already been called
48-
constexpr auto end() const -> decltype(auto) {
49-
return containers::end(*m_range);
50-
}
51-
52-
constexpr auto advance() & -> void {
53-
++m_it;
54-
}
55-
56-
private:
57-
tv::optional<Range> m_range{};
58-
iterator_t<Range> m_it{};
59-
};
60-
61-
template<typename Range>
62-
struct inner_range_iterator {
63-
constexpr inner_range_iterator(inner_range<Range> & range):
64-
m_range(range)
65-
{
66-
}
67-
constexpr auto set(auto const & outer_it) const -> void {
68-
m_range.get().set(outer_it);
69-
}
70-
71-
constexpr auto begin() const -> decltype(auto) {
72-
return m_range.get().begin();
73-
}
74-
// Requires that `set` has already been called
75-
constexpr auto end() const -> decltype(auto) {
76-
return m_range.get().end();
77-
}
78-
79-
constexpr auto advance() const -> void {
80-
m_range.get().advance();
81-
}
82-
83-
private:
84-
std::reference_wrapper<inner_range<Range>> m_range;
85-
};
86-
87-
template<typename Range> requires std::is_reference_v<Range>
88-
struct inner_range<Range> {
89-
};
90-
91-
template<typename Range> requires std::is_reference_v<Range>
92-
struct inner_range_iterator<Range> {
93-
constexpr inner_range_iterator(inner_range<Range>):
94-
m_it(),
95-
m_last()
96-
{
97-
}
98-
constexpr auto set(auto const & outer_it) & -> void {
99-
auto && range = *outer_it;
100-
m_it = containers::begin(range);
101-
m_last = containers::end(range);
102-
}
103-
104-
constexpr auto begin() const -> iterator_t<Range> const & {
105-
return m_it;
106-
}
107-
constexpr auto end() const -> sentinel_t<Range> const & {
108-
return m_last;
109-
}
110-
111-
constexpr auto advance() & -> void {
112-
++m_it;
113-
}
114-
115-
private:
116-
iterator_t<Range> m_it;
117-
sentinel_t<Range> m_last;
118-
};
119-
120-
// TODO: It's possible to more efficiently add and subtract iterators
121-
template<typename Iterator, typename Sentinel>
122-
struct join_iterator {
123-
using difference_type = decltype(
124-
bounded::declval<iter_difference_t<Iterator>>() *
125-
bounded::declval<range_size_t<iter_value_t<Iterator>>>()
126-
);
127-
explicit constexpr join_iterator(
128-
Iterator it,
129-
Sentinel last,
130-
inner_range<iter_reference_t<Iterator>> & inner
131-
):
132-
m_outer_it(std::move(it)),
133-
m_outer_last(std::move(last)),
134-
m_inner(inner)
135-
{
136-
if (m_outer_it != m_outer_last) {
137-
m_inner.set(m_outer_it);
138-
normalize();
139-
}
140-
}
141-
142-
constexpr auto operator*() const -> decltype(auto) {
143-
return *m_inner.begin();
144-
}
145-
friend constexpr auto operator+(join_iterator lhs, bounded::constant_t<1>) {
146-
lhs.m_inner.advance();
147-
lhs.normalize();
148-
return lhs;
149-
}
150-
friend constexpr auto operator==(join_iterator const & lhs, join_iterator const & rhs) -> bool {
151-
return
152-
lhs.m_outer_it == rhs.m_outer_it and
153-
lhs.m_inner.begin() == rhs.m_inner.begin();
154-
}
155-
friend constexpr auto operator==(join_iterator const & lhs, std::default_sentinel_t) -> bool {
156-
return lhs.m_outer_it == lhs.m_outer_last;
157-
}
158-
159-
private:
160-
constexpr auto normalize() & -> void {
161-
while (containers::is_empty(m_inner)) {
162-
++m_outer_it;
163-
if (m_outer_it == m_outer_last) {
164-
return;
165-
}
166-
m_inner.set(m_outer_it);
167-
}
168-
}
169-
170-
Iterator m_outer_it;
171-
Sentinel m_outer_last;
172-
inner_range_iterator<iter_reference_t<Iterator>> m_inner;
173-
};
174-
17522
export template<range Range>
176-
struct join {
177-
explicit constexpr join(Range && range):
178-
m_range(OPERATORS_FORWARD(range))
179-
{
180-
}
181-
constexpr auto begin() const & requires std::is_reference_v<range_reference_t<Range>> {
182-
return join_iterator(
183-
containers::begin(m_range),
184-
containers::end(m_range),
185-
m_inner
186-
);
187-
}
188-
constexpr auto begin() & requires std::is_reference_v<range_reference_t<Range>> {
189-
return join_iterator(
190-
containers::begin(m_range),
191-
containers::end(m_range),
192-
m_inner
193-
);
194-
}
195-
constexpr auto begin() && {
196-
return join_iterator(
197-
containers::begin(std::move(m_range)),
198-
containers::end(std::move(m_range)),
199-
m_inner
200-
);
201-
}
202-
// TODO: Support bidirectional?
203-
static constexpr auto end() {
204-
return std::default_sentinel;
205-
}
206-
private:
207-
[[no_unique_address]] Range m_range;
208-
[[no_unique_address]] inner_range<range_reference_t<Range>> m_inner;
209-
};
23+
constexpr auto join(Range && r) {
24+
using value_type = containers::range_value_t<containers::range_value_t<Range>>;
25+
return ::containers::join_with(
26+
OPERATORS_FORWARD(r),
27+
containers::array<value_type, 0_bi>()
28+
);
29+
}
21030

21131
} // namespace containers

0 commit comments

Comments
 (0)