C++ zip
zip.hpp
1 #ifndef MSD_ZIP_ZIP_HPP
2 #define MSD_ZIP_ZIP_HPP
3 
4 #include <algorithm>
5 #include <cassert>
6 #include <cstddef>
7 #include <iterator>
8 #include <tuple>
9 #include <type_traits>
10 #include <utility>
11 
12 namespace msd {
13 
20 template <typename... Iterators>
21 class zip_iterator {
22  public:
26  using iterator_category = std::bidirectional_iterator_tag;
27 
31  using difference_type = std::ptrdiff_t;
32 
36  using value_type = std::tuple<typename std::iterator_traits<Iterators>::reference...>;
37 
41  using pointer = std::tuple<typename std::iterator_traits<Iterators>::pointer...>;
42 
46  using reference = std::tuple<typename std::iterator_traits<Iterators>::reference...>;
47 
53  explicit zip_iterator(Iterators... iterators) : iterators_{iterators...} {}
54 
60  value_type operator*() const { return dereference(std::index_sequence_for<Iterators...>{}); }
61 
68  bool operator==(const zip_iterator& other) const { return equal(std::index_sequence_for<Iterators...>{}, other); }
69 
76  bool operator!=(const zip_iterator& other) const { return !equal(std::index_sequence_for<Iterators...>{}, other); }
77 
84  {
85  advance(std::index_sequence_for<Iterators...>{}, 1);
86  return *this;
87  }
88 
95  zip_iterator operator+(const std::size_t offset) const
96  {
97  auto iterator = *this;
98  iterator.advance(std::index_sequence_for<Iterators...>{}, offset);
99  return iterator;
100  }
101 
108  zip_iterator operator+(const zip_iterator& other) const
109  {
110  auto iterator = *this;
111  const auto distance = std::distance(iterator, other);
112  iterator.advance(std::index_sequence_for<Iterators...>{}, distance);
113  return iterator;
114  }
115 
122  {
123  advance(std::index_sequence_for<Iterators...>{}, -1);
124  return *this;
125  }
126 
133  zip_iterator operator-(const int offset) const
134  {
135  auto iterator = *this;
136  iterator.advance(std::index_sequence_for<Iterators...>{}, -offset);
137  return iterator;
138  }
139 
146  zip_iterator operator-(const zip_iterator& other) const
147  {
148  auto iterator = *this;
149  const auto distance = std::distance(other, iterator);
150  iterator.advance(std::index_sequence_for<Iterators...>{}, -distance);
151  return iterator;
152  }
153 
154  private:
162  template <std::size_t... I>
163  value_type dereference(std::index_sequence<I...>) const
164  {
165  return std::tie(*std::get<I>(iterators_)...);
166  }
167 
176  template <std::size_t... I>
177  bool equal(std::index_sequence<I...>, const zip_iterator& other) const
178  {
179  return ((std::get<I>(iterators_) == std::get<I>(other.iterators_)) || ...);
180  }
181 
189  template <std::size_t... I>
190  void advance(std::index_sequence<I...>, const int offset)
191  {
192  ((std::advance(std::get<I>(iterators_), offset)), ...);
193  }
194 
198  std::tuple<Iterators...> iterators_;
199 };
200 
211 template <typename... Containers>
212 class zip {
213  public:
214  static_assert(sizeof...(Containers) > 1, "zip requires at least 2 containers");
215 
222  using iterator =
224  typename Containers::iterator>...>;
230  using const_iterator = zip_iterator<typename Containers::const_iterator...>;
231 
237 
244  explicit zip(Containers&... containers) : containers_{containers...} {}
245 
251  iterator begin() const { return begin_impl<iterator>(std::index_sequence_for<Containers...>{}); }
252 
258  iterator end() const { return end_impl<iterator>(std::index_sequence_for<Containers...>{}); }
259 
265  const_iterator cbegin() const { return begin_impl<const_iterator>(std::index_sequence_for<Containers...>{}); }
266 
272  const_iterator cend() const { return end_impl<const_iterator>(std::index_sequence_for<Containers...>{}); }
273 
279  [[nodiscard]] std::size_t size() const { return size_impl(std::index_sequence_for<Containers...>{}); }
280 
286  [[nodiscard]] bool empty() const { return begin() == end(); }
287 
294  explicit operator bool() const { return !empty(); }
295 
303  {
304  assert(!empty()); // LCOV_EXCL_LINE
305  return *begin();
306  }
307 
315  {
316  assert(!empty()); // LCOV_EXCL_LINE
317  return *begin();
318  }
319 
327  {
328  assert(!empty()); // LCOV_EXCL_LINE
329  return *std::prev(begin() + size());
330  }
331 
338  value_type back() const
339  {
340  assert(!empty()); // LCOV_EXCL_LINE
341  return *std::prev(begin() + size());
342  }
343 
351  value_type operator[](const std::size_t offset) const
352  {
353  assert(offset < size());
354  return *std::next(begin(), offset);
355  }
356 
357  private:
366  template <typename Iterator, std::size_t... I>
367  Iterator begin_impl(std::index_sequence<I...>) const
368  {
369  return Iterator{std::get<I>(containers_).begin()...};
370  }
371 
380  template <typename Iterator, std::size_t... I>
381  Iterator end_impl(std::index_sequence<I...>) const
382  {
383  return std::next(Iterator{std::get<I>(containers_).begin()...}, size());
384  }
385 
393  template <std::size_t... I>
394  std::size_t size_impl(std::index_sequence<I...>) const
395  {
396  return std::min({std::distance(std::get<I>(containers_).begin(), std::get<I>(containers_).end())...});
397  }
398 
402  std::tuple<Containers&...> containers_;
403 };
404 
405 } // namespace msd
406 
407 #endif // MSD_ZIP_ZIP_HPP
Bidirectional iterator over multiple iterators simultaneously.
Definition: zip.hpp:21
bool operator==(const zip_iterator &other) const
Checks if two zip_iterator instances are equal.
Definition: zip.hpp:68
zip_iterator & operator++()
Advances the zip_iterator by one position.
Definition: zip.hpp:83
zip_iterator & operator--()
Moves the zip_iterator back by one position.
Definition: zip.hpp:121
std::bidirectional_iterator_tag iterator_category
Supports bidirectional traversal.
Definition: zip.hpp:26
bool operator!=(const zip_iterator &other) const
Checks if two zip_iterator instances are not equal.
Definition: zip.hpp:76
std::tuple< typename std::iterator_traits< Iterators >::reference... > value_type
A tuple of references from each of the zipped iterators.
Definition: zip.hpp:36
zip_iterator operator+(const std::size_t offset) const
Returns a new zip_iterator advanced by a specified offset.
Definition: zip.hpp:95
zip_iterator operator-(const int offset) const
Returns a new zip_iterator moved back by a specified offset.
Definition: zip.hpp:133
std::ptrdiff_t difference_type
The difference between two iterators.
Definition: zip.hpp:31
value_type operator*() const
Dereferences the zip_iterator to obtain a tuple of references from each iterator.
Definition: zip.hpp:60
zip_iterator(Iterators... iterators)
Constructs a zip_iterator from the provided iterators.
Definition: zip.hpp:53
zip_iterator operator+(const zip_iterator &other) const
Returns a new zip_iterator advanced by the distance between two iterators.
Definition: zip.hpp:108
zip_iterator operator-(const zip_iterator &other) const
Returns the distance between two zip_iterator instances.
Definition: zip.hpp:146
std::tuple< typename std::iterator_traits< Iterators >::pointer... > pointer
A tuple of pointers from each of the zipped iterators.
Definition: zip.hpp:41
std::tuple< typename std::iterator_traits< Iterators >::reference... > reference
A tuple of references from each of the zipped iterators.
Definition: zip.hpp:46
A view over multiple containers simultaneously. It allows iterating through multiple containers at on...
Definition: zip.hpp:212
const_iterator cbegin() const
Returns a const iterator pointing to the beginning of the zipped containers.
Definition: zip.hpp:265
value_type back() const
Returns the last element in the zipped sequence (const overload).
Definition: zip.hpp:338
value_type front()
Returns the first element in the zipped sequence.
Definition: zip.hpp:302
const_iterator cend() const
Returns a const iterator pointing to the end of the zipped containers.
Definition: zip.hpp:272
value_type operator[](const std::size_t offset) const
Returns the element at the specified offset in the zipped sequence.
Definition: zip.hpp:351
iterator begin() const
Returns an iterator pointing to the beginning of the zipped containers.
Definition: zip.hpp:251
value_type front() const
Returns the first element in the zipped sequence (const overload).
Definition: zip.hpp:314
value_type back()
Returns the last element in the zipped sequence.
Definition: zip.hpp:326
zip(Containers &... containers)
Constructs a zip object from the provided containers.
Definition: zip.hpp:244
bool empty() const
Checks if the zipped sequence is empty.
Definition: zip.hpp:286
std::size_t size() const
Returns the size of the zipped sequence, which is the size of the smallest container.
Definition: zip.hpp:279
typename iterator::value_type value_type
The value_type is the type of the element returned by the iterator, which is a tuple of elements from...
Definition: zip.hpp:236
iterator end() const
Returns an iterator pointing to the end of the zipped containers.
Definition: zip.hpp:258