Coverage Report

Created: 2026-01-09 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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