/src/cppitertools/cppitertools/chain.hpp
Line | Count | Source |
1 | | #ifndef ITER_CHAIN_HPP_ |
2 | | #define ITER_CHAIN_HPP_ |
3 | | |
4 | | #include <array> |
5 | | #include <iterator> |
6 | | #include <optional> |
7 | | #include <tuple> |
8 | | #include <type_traits> |
9 | | #include <utility> |
10 | | |
11 | | #include "internal/iter_tuples.hpp" |
12 | | #include "internal/iterator_wrapper.hpp" |
13 | | #include "internal/iterbase.hpp" |
14 | | |
15 | | namespace iter { |
16 | | namespace impl { |
17 | | template <typename TupType, std::size_t... Is> |
18 | | class Chained; |
19 | | |
20 | | template <typename Container> |
21 | | class ChainedFromIterable; |
22 | | |
23 | | using ChainFromIterableFn = IterToolFn<ChainedFromIterable>; |
24 | | |
25 | | // rather than a chain function, use a callable object to support |
26 | | // from_iterable |
27 | | class ChainMaker; |
28 | | |
29 | | template <typename> |
30 | | struct AsTupleOfConstImpl; |
31 | | |
32 | | template <typename... Ts> |
33 | | struct AsTupleOfConstImpl<std::tuple<Ts...>> |
34 | | : type_is<std::tuple<AsConst<Ts>...>> {}; |
35 | | |
36 | | template <typename T> |
37 | | using AsTupleOfConst = typename AsTupleOfConstImpl<T>::type; |
38 | | } |
39 | | } |
40 | | |
41 | | template <typename TupType, std::size_t... Is> |
42 | | class iter::impl::Chained { |
43 | | private: |
44 | | friend ChainMaker; |
45 | | |
46 | | template <typename TupTypeTA, typename TupTypeTB> |
47 | | class IteratorDataPair { |
48 | | IteratorDataPair() = delete; |
49 | | |
50 | | public: |
51 | | using IterTupTypeA = iterator_tuple_type<TupTypeTA>; |
52 | | using IterTupTypeB = iterator_tuple_type<TupTypeTB>; |
53 | | |
54 | | template <std::size_t Idx> |
55 | | static bool get_and_check_not_equal( |
56 | 160k | const IterTupTypeA& lhs, const IterTupTypeB& rhs) { |
57 | 160k | return std::get<Idx>(lhs) != std::get<Idx>(rhs); |
58 | 160k | } bool iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorDataPair<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&>, std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_check_not_equal<0ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> > const&, std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> > const&) Line | Count | Source | 56 | 53.5k | const IterTupTypeA& lhs, const IterTupTypeB& rhs) { | 57 | 53.5k | return std::get<Idx>(lhs) != std::get<Idx>(rhs); | 58 | 53.5k | } |
bool iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorDataPair<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&>, std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_check_not_equal<1ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> > const&, std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> > const&) Line | Count | Source | 56 | 53.5k | const IterTupTypeA& lhs, const IterTupTypeB& rhs) { | 57 | 53.5k | return std::get<Idx>(lhs) != std::get<Idx>(rhs); | 58 | 53.5k | } |
bool iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorDataPair<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&>, std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_check_not_equal<2ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> > const&, std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> > const&) Line | Count | Source | 56 | 53.5k | const IterTupTypeA& lhs, const IterTupTypeB& rhs) { | 57 | 53.5k | return std::get<Idx>(lhs) != std::get<Idx>(rhs); | 58 | 53.5k | } |
|
59 | | |
60 | | using NeqFunc = bool (*)(const IterTupTypeA&, const IterTupTypeB&); |
61 | | |
62 | | constexpr static std::array<NeqFunc, sizeof...(Is)> neq_comparers{ |
63 | | {get_and_check_not_equal<Is>...}}; |
64 | | }; |
65 | | |
66 | | template <typename TupTypeT> |
67 | | class IteratorData { |
68 | | IteratorData() = delete; |
69 | | static_assert( |
70 | | std::tuple_size<std::decay_t<TupTypeT>>::value == sizeof...(Is), |
71 | | "tuple size != sizeof Is"); |
72 | | |
73 | | static_assert( |
74 | | are_same<iterator_deref<std::tuple_element_t<Is, TupTypeT>>...>::value, |
75 | | "All chained iterables must have iterators that " |
76 | | "dereference to the same type, including cv-qualifiers " |
77 | | "and references."); |
78 | | |
79 | | public: |
80 | | using IterTupType = iterator_tuple_type<TupTypeT>; |
81 | | using DerefType = iterator_deref<std::tuple_element_t<0, TupTypeT>>; |
82 | | using ArrowType = iterator_arrow<std::tuple_element_t<0, TupTypeT>>; |
83 | | |
84 | | template <std::size_t Idx> |
85 | 158k | static DerefType get_and_deref(IterTupType& iters) { |
86 | 158k | return *std::get<Idx>(iters); |
87 | 158k | } char const& iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorData<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_deref<0ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> >&) Line | Count | Source | 85 | 52.9k | static DerefType get_and_deref(IterTupType& iters) { | 86 | 52.9k | return *std::get<Idx>(iters); | 87 | 52.9k | } |
char const& iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorData<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_deref<1ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> >&) Line | Count | Source | 85 | 52.9k | static DerefType get_and_deref(IterTupType& iters) { | 86 | 52.9k | return *std::get<Idx>(iters); | 87 | 52.9k | } |
char const& iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorData<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_deref<2ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> >&) Line | Count | Source | 85 | 52.9k | static DerefType get_and_deref(IterTupType& iters) { | 86 | 52.9k | return *std::get<Idx>(iters); | 87 | 52.9k | } |
|
88 | | |
89 | | template <std::size_t Idx> |
90 | | static ArrowType get_and_arrow(IterTupType& iters) { |
91 | | return apply_arrow(std::get<Idx>(iters)); |
92 | | } |
93 | | |
94 | | template <std::size_t Idx> |
95 | 158k | static void get_and_increment(IterTupType& iters) { |
96 | 158k | ++std::get<Idx>(iters); |
97 | 158k | } void iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorData<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_increment<0ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> >&) Line | Count | Source | 95 | 52.9k | static void get_and_increment(IterTupType& iters) { | 96 | 52.9k | ++std::get<Idx>(iters); | 97 | 52.9k | } |
void iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorData<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_increment<1ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> >&) Line | Count | Source | 95 | 52.9k | static void get_and_increment(IterTupType& iters) { | 96 | 52.9k | ++std::get<Idx>(iters); | 97 | 52.9k | } |
void iter::impl::Chained<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&, std::__1::vector<char, std::__1::allocator<char> >&>, 0ul, 1ul, 2ul>::IteratorData<std::__1::tuple<std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&, std::__1::vector<char, std::__1::allocator<char> > const&> >::get_and_increment<2ul>(std::__1::tuple<std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*> >&) Line | Count | Source | 95 | 52.9k | static void get_and_increment(IterTupType& iters) { | 96 | 52.9k | ++std::get<Idx>(iters); | 97 | 52.9k | } |
|
98 | | |
99 | | using DerefFunc = DerefType (*)(IterTupType&); |
100 | | using ArrowFunc = ArrowType (*)(IterTupType&); |
101 | | using IncFunc = void (*)(IterTupType&); |
102 | | |
103 | | constexpr static std::array<DerefFunc, sizeof...(Is)> derefers{ |
104 | | {get_and_deref<Is>...}}; |
105 | | |
106 | | constexpr static std::array<ArrowFunc, sizeof...(Is)> arrowers{ |
107 | | {get_and_arrow<Is>...}}; |
108 | | |
109 | | constexpr static std::array<IncFunc, sizeof...(Is)> incrementers{ |
110 | | {get_and_increment<Is>...}}; |
111 | | |
112 | | using TraitsValue = |
113 | | iterator_traits_deref<std::tuple_element_t<0, TupTypeT>>; |
114 | | }; |
115 | | |
116 | 605 | Chained(TupType&& t) : tup_(std::move(t)) {} |
117 | | TupType tup_; |
118 | | |
119 | | public: |
120 | | Chained(Chained&&) = default; |
121 | | |
122 | | template <typename TupTypeT> |
123 | | class Iterator { |
124 | | private: |
125 | | using IterData = IteratorData<TupTypeT>; |
126 | | std::size_t index_; |
127 | | typename IterData::IterTupType iters_; |
128 | | typename IterData::IterTupType ends_; |
129 | | |
130 | 160k | void check_for_end_and_adjust() { |
131 | 161k | while (index_ < sizeof...(Is) |
132 | 160k | && !(IteratorDataPair<TupTypeT, TupTypeT>::neq_comparers[index_]( |
133 | 160k | iters_, ends_))) { |
134 | 1.81k | ++index_; |
135 | 1.81k | } |
136 | 160k | } |
137 | | |
138 | | public: |
139 | | template <typename> |
140 | | friend class Iterator; |
141 | | |
142 | | using iterator_category = std::input_iterator_tag; |
143 | | using value_type = typename IteratorData<TupTypeT>::TraitsValue; |
144 | | using difference_type = std::ptrdiff_t; |
145 | | using pointer = typename IteratorData<TupTypeT>::ArrowType; |
146 | | using reference = typename IteratorData<TupTypeT>::DerefType; |
147 | | |
148 | | Iterator(std::size_t i, typename IterData::IterTupType&& iters, |
149 | | typename IterData::IterTupType&& ends) |
150 | 1.21k | : index_{i}, iters_(std::move(iters)), ends_(std::move(ends)) { |
151 | 1.21k | check_for_end_and_adjust(); |
152 | 1.21k | } |
153 | | |
154 | 158k | decltype(auto) operator*() { |
155 | 158k | return IterData::derefers[index_](iters_); |
156 | 158k | } |
157 | | |
158 | | decltype(auto) operator->() { |
159 | | return IterData::arrowers[index_](iters_); |
160 | | } |
161 | | |
162 | 158k | Iterator& operator++() { |
163 | 158k | IterData::incrementers[index_](iters_); |
164 | 158k | check_for_end_and_adjust(); |
165 | 158k | return *this; |
166 | 158k | } |
167 | | |
168 | | Iterator operator++(int) { |
169 | | auto ret = *this; |
170 | | ++*this; |
171 | | return ret; |
172 | | } |
173 | | |
174 | | template <typename T> |
175 | 159k | bool operator!=(const Iterator<T>& other) const { |
176 | 159k | return index_ != other.index_ |
177 | 605 | || (index_ != sizeof...(Is) |
178 | 0 | && IteratorDataPair<TupTypeT, T>::neq_comparers[index_]( |
179 | 0 | iters_, other.iters_)); |
180 | 159k | } |
181 | | |
182 | | template <typename T> |
183 | | bool operator==(const Iterator<T>& other) const { |
184 | | return !(*this != other); |
185 | | } |
186 | | }; |
187 | | |
188 | | Iterator<TupType> begin() { |
189 | | return {0, {get_begin(std::get<Is>(tup_))...}, |
190 | | {get_end(std::get<Is>(tup_))...}}; |
191 | | } |
192 | | |
193 | | Iterator<TupType> end() { |
194 | | return {sizeof...(Is), {get_end(std::get<Is>(tup_))...}, |
195 | | {get_end(std::get<Is>(tup_))...}}; |
196 | | } |
197 | | |
198 | 605 | Iterator<AsTupleOfConst<TupType>> begin() const { |
199 | 605 | return {0, {get_begin(std::as_const(std::get<Is>(tup_)))...}, |
200 | 605 | {get_end(std::as_const(std::get<Is>(tup_)))...}}; |
201 | 605 | } |
202 | | |
203 | 605 | Iterator<AsTupleOfConst<TupType>> end() const { |
204 | 605 | return {sizeof...(Is), {get_end(std::as_const(std::get<Is>(tup_)))...}, |
205 | 605 | {get_end(std::as_const(std::get<Is>(tup_)))...}}; |
206 | 605 | } |
207 | | }; |
208 | | |
209 | | template <typename Container> |
210 | | class iter::impl::ChainedFromIterable { |
211 | | private: |
212 | | friend ChainFromIterableFn; |
213 | | Container container_; |
214 | | ChainedFromIterable(Container&& container) |
215 | | : container_(std::forward<Container>(container)) {} |
216 | | |
217 | | public: |
218 | | ChainedFromIterable(ChainedFromIterable&&) = default; |
219 | | template <typename ContainerT> |
220 | | class Iterator { |
221 | | private: |
222 | | template <typename> |
223 | | friend class Iterator; |
224 | | using SubContainer = iterator_deref<ContainerT>; |
225 | | using SubIter = IteratorWrapper<SubContainer>; |
226 | | |
227 | | IteratorWrapper<ContainerT> top_level_iter_; |
228 | | IteratorWrapper<ContainerT> top_level_end_; |
229 | | DerefHolder<SubContainer> sub_iterable_; |
230 | | std::optional<SubIter> sub_iter_p_; |
231 | | std::optional<SubIter> sub_end_p_; |
232 | | |
233 | | void advance_while_empty_sub_iterable() { |
234 | | while (top_level_iter_ != top_level_end_ && sub_iter_p_ == sub_end_p_) { |
235 | | ++top_level_iter_; |
236 | | update_sub_iterable(); |
237 | | } |
238 | | } |
239 | | |
240 | | void update_sub_iterable() { |
241 | | if (top_level_iter_ != top_level_end_) { |
242 | | sub_iterable_.reset(*top_level_iter_); |
243 | | sub_iter_p_ = |
244 | | std::make_optional<SubIter>(get_begin(sub_iterable_.get())); |
245 | | sub_end_p_ = std::make_optional<SubIter>(get_end(sub_iterable_.get())); |
246 | | } else { |
247 | | sub_iter_p_.reset(); |
248 | | sub_end_p_.reset(); |
249 | | } |
250 | | } |
251 | | |
252 | | void next_sub_iterable() { |
253 | | update_sub_iterable(); |
254 | | advance_while_empty_sub_iterable(); |
255 | | } |
256 | | |
257 | | public: |
258 | | using iterator_category = std::input_iterator_tag; |
259 | | using value_type = iterator_traits_deref<iterator_deref<ContainerT>>; |
260 | | using difference_type = std::ptrdiff_t; |
261 | | using pointer = value_type*; |
262 | | using reference = value_type&; |
263 | | |
264 | | Iterator(IteratorWrapper<ContainerT>&& top_iter, |
265 | | IteratorWrapper<ContainerT>&& top_end) |
266 | | : top_level_iter_{std::move(top_iter)}, |
267 | | top_level_end_{std::move(top_end)} { |
268 | | next_sub_iterable(); |
269 | | } |
270 | | |
271 | | Iterator& operator++() { |
272 | | ++*sub_iter_p_; |
273 | | if (!(*sub_iter_p_ != *sub_end_p_)) { |
274 | | ++top_level_iter_; |
275 | | next_sub_iterable(); |
276 | | } |
277 | | return *this; |
278 | | } |
279 | | |
280 | | Iterator operator++(int) { |
281 | | auto ret = *this; |
282 | | ++*this; |
283 | | return ret; |
284 | | } |
285 | | |
286 | | template <typename T> |
287 | | bool operator!=(const Iterator<T>& other) const { |
288 | | return top_level_iter_ != other.top_level_iter_ |
289 | | || sub_iter_p_ != other.sub_iter_p_; |
290 | | } |
291 | | |
292 | | template <typename T> |
293 | | bool operator==(const Iterator<T>& other) const { |
294 | | return !(*this != other); |
295 | | } |
296 | | |
297 | | iterator_deref<iterator_deref<ContainerT>> operator*() { |
298 | | return **sub_iter_p_; |
299 | | } |
300 | | |
301 | | iterator_arrow<iterator_deref<ContainerT>> operator->() { |
302 | | return apply_arrow(*sub_iter_p_); |
303 | | } |
304 | | }; |
305 | | |
306 | | Iterator<Container> begin() { |
307 | | return {get_begin(container_), get_end(container_)}; |
308 | | } |
309 | | |
310 | | Iterator<Container> end() { |
311 | | return {get_end(container_), get_end(container_)}; |
312 | | } |
313 | | |
314 | | Iterator<AsConst<Container>> begin() const { |
315 | | return {get_begin(std::as_const(container_)), |
316 | | get_end(std::as_const(container_))}; |
317 | | } |
318 | | |
319 | | Iterator<AsConst<Container>> end() const { |
320 | | return { |
321 | | get_end(std::as_const(container_)), get_end(std::as_const(container_))}; |
322 | | } |
323 | | }; |
324 | | |
325 | | class iter::impl::ChainMaker { |
326 | | private: |
327 | | template <typename TupleType, std::size_t... Is> |
328 | | Chained<TupleType, Is...> chain_impl( |
329 | 605 | TupleType&& containers, std::index_sequence<Is...>) const { |
330 | 605 | return {std::move(containers)}; |
331 | 605 | } |
332 | | |
333 | | public: |
334 | | // expose regular call operator to provide usual chain() |
335 | | template <typename... Containers> |
336 | 605 | auto operator()(Containers&&... cs) const { |
337 | 605 | return chain_impl( |
338 | 605 | std::tuple<Containers...>{std::forward<Containers>(cs)...}, |
339 | 605 | std::index_sequence_for<Containers...>{}); |
340 | 605 | } |
341 | | |
342 | | ChainFromIterableFn from_iterable; |
343 | | }; |
344 | | |
345 | | namespace iter { |
346 | | inline constexpr auto chain = iter::impl::ChainMaker{}; |
347 | | } |
348 | | |
349 | | #endif |