LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
zip.h
Go to the documentation of this file.
1 
10 #ifndef LARCOREALG_COREUTILS_ZIP_H
11 #define LARCOREALG_COREUTILS_ZIP_H
12 
14 
15 // C/C++ libraries
16 #include <cstddef> // std::size_t
17 #include <iterator> // std::begin(), std::end()
18 #include <tuple>
19 #include <type_traits> // std::remove_cv_t<>, ...
20 #include <utility> // std::forward(), std::index_sequence_for(), ...
21 
22 namespace util {
23 
24  // -- BEGIN -- Parallel iterations -------------------------------------------
27 
73  template <std::size_t Lead, typename... Iterables>
74  auto zip(Iterables&&... iterables);
75 
77  template <typename... Iterables>
78  auto zip(Iterables&&... iterables)
79  {
80  return zip<0U>(std::forward<Iterables>(iterables)...);
81  }
82 
84  // -- END -- Parallel iterations ---------------------------------------------
85 
86 } // namespace util
87 
88 //==============================================================================
89 //=== template implementation
90 //==============================================================================
91 //------------------------------------------------------------------------------
92 //--- util::zip()
93 //------------------------------------------------------------------------------
94 namespace util::details {
95 
96  //----------------------------------------------------------------------------
97  template <std::size_t Lead, typename... Iters>
98  class zip_iterator {
99 
100  static_assert(Lead < sizeof...(Iters), "The index (Lead) of the leading iterator is invalid.");
101 
103  using this_iterator_t = zip_iterator<Lead, Iters...>;
104 
105  public:
106  // --- BEGIN -- Data types -------------------------------------------------
109 
110  using difference_type = std::ptrdiff_t;
111  using reference = std::tuple<typename std::iterator_traits<Iters>::reference...>;
112  using value_type = std::remove_cv_t<reference>;
113  using pointer = std::add_pointer_t<std::remove_reference_t<reference>>;
114  using iterator_category = std::forward_iterator_tag;
115 
117  // --- END -- Data types ---------------------------------------------------
118 
119  // --- BEGIN -- Constructors -----------------------------------------------
122 
124  zip_iterator() = default;
125 
127  zip_iterator(Iters&&... iterators) : fIterators(std::forward<Iters>(iterators)...) {}
128 
130  // --- END -- Constructors -------------------------------------------------
131 
132  // --- BEGIN -- Access -----------------------------------------------------
135 
137  auto operator*() const { return dereference_impl(std::index_sequence_for<Iters...>()); }
138 
140  template <std::size_t Index>
141  decltype(auto) get() const
142  {
143  return std::get<Index>(fIterators);
144  }
145 
147  // --- END -- Access -------------------------------------------------------
148 
149  // --- BEGIN -- Modification -----------------------------------------------
152 
155  {
156  increment_impl(std::index_sequence_for<Iters...>());
157  return *this;
158  }
159 
163  {
164  this_iterator_t old(*this);
165  operator++();
166  return old;
167  }
168 
170  // --- END -- Modification -------------------------------------------------
171 
172  // --- BEGIN -- Comparisons ------------------------------------------------
175 
177  template <std::size_t OtherLead, typename... OtherIter>
179  {
180  return get<Lead>() != other.template get<OtherLead>();
181  }
182 
184  template <std::size_t OtherLead, typename... OtherIter>
186  {
187  return get<Lead>() == other.template get<OtherLead>();
188  }
189 
191  // --- END -- Comparisons --------------------------------------------------
192 
193  private:
194  std::tuple<Iters...> fIterators;
195 
197  template <typename... Args>
198  static void expandStatements(Args&... args)
199  {}
200 
201  template <std::size_t... Indices>
202  void increment_impl(std::index_sequence<Indices...>)
203  {
204  expandStatements(++std::get<Indices>(fIterators)...);
205  }
206 
207  template <std::size_t... Indices>
208  auto dereference_impl(std::index_sequence<Indices...>) const
209  {
210  // this complicate syntax appears to guarantee that the tuple types
211  // include a l-value reference when the dereference operator returns
212  // a l-value reference, and a r-value when the dereference operator
213  // returns one. Using `std::forward_as_reference()` instead,
214  // r-values are saved as r-value references. Using `std::tuple()`
215  // instead, all referenceness is stripped away, including l-value ones.
216  return std::tuple<decltype(*std::get<Indices>(fIterators))...>(
217  *std::get<Indices>(fIterators)...);
218  }
219 
220  }; // class zip_iterator
221 
222  //----------------------------------------------------------------------------
223  // This is more of a curiosity than anything else.
224  template <std::size_t Lead>
225  class zip_iterator<Lead> {
226 
229 
230  public:
231  using difference_type = std::ptrdiff_t;
232  using reference = std::tuple<>;
233  using value_type = std::remove_cv_t<reference>;
234  using pointer = std::add_pointer_t<std::remove_reference_t<reference>>;
235  using iterator_category = std::forward_iterator_tag;
236 
237  zip_iterator() = default;
238 
239  std::tuple<> operator*() const { return {}; }
240 
242  this_iterator_t& operator++() { return *this; }
243 
245  {
246  this_iterator_t old(*this);
247  operator++();
248  return old;
249  }
250 
251  // All these iterators look the same.
252  template <std::size_t OtherLead, typename... OtherIter>
254  {
255  return false;
256  }
257 
258  // All these iterators look the same.
259  template <std::size_t OtherLead, typename... OtherIter>
261  {
262  return true;
263  }
264 
265  }; // class zip_iterator<>
266 
267  //----------------------------------------------------------------------------
268  template <std::size_t Lead, typename... Iterables>
269  auto make_zip_begin_iterator(Iterables&&... iterables)
270  {
271 
272  using std::begin;
273  return zip_iterator<Lead, decltype(begin(iterables))...>{begin(iterables)...};
274 
275  } // make_zip_begin_iterator()
276 
277  //----------------------------------------------------------------------------
278  template <std::size_t Lead, typename... Iterables>
279  auto make_zip_end_iterator(Iterables&&... iterables)
280  {
281 
282  using std::end;
283  return zip_iterator<Lead, decltype(end(iterables))...>{end(iterables)...};
284 
285  } // make_zip_end_iterator()
286 
287  //----------------------------------------------------------------------------
288 
289 } // namespace util::details
290 
291 //------------------------------------------------------------------------------
292 template <std::size_t Lead /* = 0U */, typename... Iterables>
293 auto util::zip(Iterables&&... iterables)
294 {
295 
296  return util::span(details::make_zip_begin_iterator<Lead>(iterables...),
297  details::make_zip_end_iterator<Lead>(iterables...));
298 
299 } // util::zip()
300 
301 //------------------------------------------------------------------------------
302 
303 #endif // LARCOREALG_COREUTILS_ZIP_H
std::tuple operator*() const
Definition: zip.h:239
void increment_impl(std::index_sequence< Indices... >)
Definition: zip.h:202
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:26
zip_iterator()=default
Constructor: all iterators are default-constructed.
bool operator!=(zip_iterator< OtherLead, OtherIter... > const &other) const
Comparison (based on the Lead iterator only).
Definition: zip.h:178
An object with a begin and end iterator.
std::tuple< Iters... > fIterators
Tuple of all zipped iterators.
Definition: zip.h:194
bool operator!=(zip_iterator< OtherLead, OtherIter... > const &other) const
Definition: zip.h:253
std::add_pointer_t< std::remove_reference_t< reference >> pointer
Definition: zip.h:113
auto dereference_impl(std::index_sequence< Indices... >) const
Definition: zip.h:208
bool operator==(zip_iterator< OtherLead, OtherIter... > const &other) const
Comparison (based on the Lead iterator only).
Definition: zip.h:185
auto make_zip_begin_iterator(Iterables &&...iterables)
Definition: zip.h:269
STL namespace.
decltype(auto) get() const
Returns the iterator at the specified Index.
Definition: zip.h:141
std::ptrdiff_t difference_type
Definition: zip.h:231
std::remove_cv_t< reference > value_type
Definition: zip.h:233
std::tuple< typename std::iterator_traits< Iters >::reference... > reference
Definition: zip.h:111
std::add_pointer_t< std::remove_reference_t< reference >> pointer
Definition: zip.h:234
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
static void expandStatements(Args &...args)
Helper to trigger parameter pack expansion in expressions.
Definition: zip.h:198
this_iterator_t operator++(int)
Definition: zip.h:162
std::forward_iterator_tag iterator_category
Definition: zip.h:235
auto operator*() const
Returns a tuple with values from all dereferenced iterators.
Definition: zip.h:137
bool operator==(zip_iterator< OtherLead, OtherIter... > const &other) const
Definition: zip.h:260
this_iterator_t & operator++()
Increments all the iterators.
Definition: zip.h:242
std::ptrdiff_t difference_type
Definition: zip.h:110
std::remove_cv_t< reference > value_type
Definition: zip.h:112
span(IterB &&b, IterE &&e, Adaptor &&adaptor) -> span< decltype(adaptor(std::forward< IterB >(b))), decltype(adaptor(std::forward< IterE >(e))) >
this_iterator_t & operator++()
Increments all the iterators.
Definition: zip.h:154
auto zip(Iterables &&...iterables)
Range-for loop helper iterating across many collections at the same time.
Definition: zip.h:293
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
zip_iterator(Iters &&...iterators)
Constructor: copies all iterator values.
Definition: zip.h:127
auto make_zip_end_iterator(Iterables &&...iterables)
Definition: zip.h:279
this_iterator_t operator++(int)
Definition: zip.h:244
std::forward_iterator_tag iterator_category
Definition: zip.h:114