All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
zip.h
Go to the documentation of this file.
1 /**
2  * @file larcorealg/CoreUtils/zip.h
3  * @brief Definition of `util::zip()`.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date April 14, 2019
6  *
7  * This is a header-only library.
8  */
9 
10 #ifndef LARCOREALG_COREUTILS_ZIP_H
11 #define LARCOREALG_COREUTILS_ZIP_H
12 
14 
15 // C/C++ libraries
16 #include <iterator> // std::begin(), std::end()
17 #include <utility> // std::forward(), std::index_sequence_for(), ...
18 #include <tuple>
19 #include <type_traits> // std::remove_cv_t<>, ...
20 #include <cstddef> // std::size_t
21 
22 namespace util {
23 
24 
25  // -- BEGIN -- Parallel iterations -------------------------------------------
26  /// @name Parallel iterations
27  /// @{
28 
29  /**
30  * @brief Range-for loop helper iterating across many collections at the
31  * same time.
32  * @tparam Lead index of the parameter driving the start and end of the loop
33  * @tparam Iterables type of objects to be iterated together
34  * @param iterables all iterable objects to be iterated together
35  * @return an object suitable for range-for loop
36  * @see `util::enumerate()`
37  *
38  * In the range-for loop, at each iteration this object yields a `tuple` of
39  * values, each of the type returned by dereferencing `begin(iterable)`.
40  * For example:
41  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
42  * constexpr std::size_t N = 4;
43  * std::array<int, N> twice;
44  * std::vector<double> thrice(N + 1);
45  *
46  * unsigned int i = 0;
47  * for (auto&& [ a, b]: util::zip(twice, thrice)) {
48  *
49  * a = 2 * i;
50  * b = 3.0 * i;
51  *
52  * ++i;
53  *
54  * } // for
55  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56  * In this example, `N` iterations will be run because that is the size of
57  * the first iterable given to `enumerate`. If a different leading iterable
58  * is needed, that has to be specified as an argument. The following loop
59  * is completely equivalent to the former one:
60  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
61  * unsigned int i = 0;
62  * for (auto&& [ b, a]: util::zip<1U>(thrice, twice)) {
63  *
64  * a = 2 * i;
65  * b = 3.0 * i;
66  *
67  * ++i;
68  *
69  * } // for
70  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
71  * (the index is zero-based, so `1U` refers to the second argument).
72  *
73  */
74  template <std::size_t Lead, typename... Iterables>
75  auto zip(Iterables&&... iterables);
76 
77 
78  /// Version of `zip()` with first iterator implicitly leading the iteration.
79  template <typename... Iterables>
80  auto zip(Iterables&&... iterables)
81  { return zip<0U>(std::forward<Iterables>(iterables)...); }
82 
83 
84  /// @}
85  // -- END -- Parallel iterations ---------------------------------------------
86 
87 
88 } // namespace util
89 
90 
91 //==============================================================================
92 //=== template implementation
93 //==============================================================================
94 //------------------------------------------------------------------------------
95 //--- util::zip()
96 //------------------------------------------------------------------------------
97 namespace util::details {
98 
99  //----------------------------------------------------------------------------
100  template <std::size_t Lead, typename... Iters>
101  class zip_iterator {
102 
103  static_assert(Lead < sizeof...(Iters),
104  "The index (Lead) of the leading iterator is invalid.");
105 
106  /// Type of this object.
107  using this_iterator_t = zip_iterator<Lead, Iters...>;
108 
109 
110  public:
111 
112  // --- BEGIN -- Data types -------------------------------------------------
113  /// @name Data types
114  /// @{
115 
116  using difference_type = std::ptrdiff_t;
117  using reference
118  = std::tuple<typename std::iterator_traits<Iters>::reference...>;
119  using value_type = std::remove_cv_t<reference>;
120  using pointer = std::add_pointer_t<std::remove_reference_t<reference>>;
121  using iterator_category = std::forward_iterator_tag;
122 
123  /// @}
124  // --- END -- Data types ---------------------------------------------------
125 
126 
127  // --- BEGIN -- Constructors -----------------------------------------------
128  /// @name Constructors
129  /// @{
130 
131  /// Constructor: all iterators are default-constructed.
132  zip_iterator() = default;
133 
134  /// Constructor: copies all iterator values.
135  zip_iterator(Iters&&... iterators)
136  : fIterators(std::forward<Iters>(iterators)...)
137  {}
138 
139  /// @}
140  // --- END -- Constructors -------------------------------------------------
141 
142 
143  // --- BEGIN -- Access -----------------------------------------------------
144  /// @name Access
145  /// @{
146 
147  /// Returns a tuple with values from all dereferenced iterators.
148  auto operator* () const
149  { return dereference_impl(std::index_sequence_for<Iters...>()); }
150 
151  /// Returns the iterator at the specified `Index`.
152  template <std::size_t Index>
153  decltype(auto) get() const { return std::get<Index>(fIterators); }
154 
155  /// @}
156  // --- END -- Access -------------------------------------------------------
157 
158 
159  // --- BEGIN -- Modification -----------------------------------------------
160  /// @name Modification
161  /// @{
162 
163  /// Increments all the iterators.
165  { increment_impl(std::index_sequence_for<Iters...>()); return *this; }
166 
167  /// Returns a copy of the current iterators, then increments all of the
168  /// iterators in this object.
170  { this_iterator_t old(*this); operator++(); return old; }
171 
172  /// @}
173  // --- END -- Modification -------------------------------------------------
174 
175 
176  // --- BEGIN -- Comparisons ------------------------------------------------
177  /// @name Comparisons
178  /// @{
179 
180  /// Comparison (based on the `Lead` iterator only).
181  template <std::size_t OtherLead, typename... OtherIter>
183  { return get<Lead>() != other.template get<OtherLead>(); }
184 
185  /// Comparison (based on the `Lead` iterator only).
186  template <std::size_t OtherLead, typename... OtherIter>
188  { return get<Lead>() == other.template get<OtherLead>(); }
189 
190 
191  /// @}
192  // --- END -- Comparisons --------------------------------------------------
193 
194 
195  private:
196 
197  std::tuple<Iters...> fIterators; ///< Tuple of all zipped iterators.
198 
199 
200  /// Helper to trigger parameter pack expansion in expressions.
201  template <typename... Args>
202  static void expandStatements(Args&... args) {}
203 
204  template <std::size_t... Indices>
205  void increment_impl(std::index_sequence<Indices...>)
206  { expandStatements(++std::get<Indices>(fIterators)...); }
207 
208  template <std::size_t... Indices>
209  auto dereference_impl(std::index_sequence<Indices...>) const
210  {
211  // this complicate syntax appears to guarantee that the tuple types
212  // include a l-value reference when the dereference operator returns
213  // a l-value reference, and a r-value when the dereference operator
214  // returns one. Using `std::forward_as_reference()` instead,
215  // r-values are saved as r-value references. Using `std::tuple()`
216  // instead, all referenceness is stripped away, including l-value ones.
217  return std::tuple<decltype(*std::get<Indices>(fIterators))...>
218  (*std::get<Indices>(fIterators)...);
219  }
220 
221 
222  }; // class zip_iterator
223 
224 
225  //----------------------------------------------------------------------------
226  // This is more of a curiosity than anything else.
227  template <std::size_t Lead>
228  class zip_iterator<Lead> {
229 
230  /// Type of this object.
232 
233 
234  public:
235 
236  using difference_type = std::ptrdiff_t;
237  using reference = std::tuple<>;
238  using value_type = std::remove_cv_t<reference>;
239  using pointer = std::add_pointer_t<std::remove_reference_t<reference>>;
240  using iterator_category = std::forward_iterator_tag;
241 
242  zip_iterator() = default;
243 
244  std::tuple<> operator* () const { return {}; }
245 
246  /// Increments all the iterators.
247  this_iterator_t& operator++ () { return *this; }
248 
250  { this_iterator_t old(*this); operator++(); return old; }
251 
252 
253  // All these iterators look the same.
254  template <std::size_t OtherLead, typename... OtherIter>
256  { return false; }
257 
258  // All these iterators look the same.
259  template <std::size_t OtherLead, typename... OtherIter>
261  { return true; }
262 
263  }; // class zip_iterator<>
264 
265 
266  //----------------------------------------------------------------------------
267  template <std::size_t Lead, typename... Iterables>
268  auto make_zip_begin_iterator(Iterables&&... iterables) {
269 
270  using std::begin;
271  return zip_iterator<Lead, decltype(begin(iterables))...>
272  { begin(iterables)... };
273 
274  } // make_zip_begin_iterator()
275 
276 
277  //----------------------------------------------------------------------------
278  template <std::size_t Lead, typename... Iterables>
279  auto make_zip_end_iterator(Iterables&&... iterables) {
280 
281  using std::end;
282  return zip_iterator<Lead, decltype(end(iterables))...>
283  { end(iterables)... };
284 
285  } // make_zip_end_iterator()
286 
287 
288  //----------------------------------------------------------------------------
289 
290 } // namespace util::details
291 
292 
293 //------------------------------------------------------------------------------
294 template <std::size_t Lead /* = 0U */, typename... Iterables>
295 auto util::zip(Iterables&&... iterables) {
296 
297  return util::span(
298  details::make_zip_begin_iterator<Lead>(iterables...),
299  details::make_zip_end_iterator<Lead>(iterables...)
300  );
301 
302 } // util::zip()
303 
304 
305 //------------------------------------------------------------------------------
306 
307 
308 #endif // LARCOREALG_COREUTILS_ZIP_H
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
zip_iterator()=default
Constructor: all iterators are default-constructed.
An object with a begin and end iterator.
bool operator!=(zip_iterator< OtherLead, OtherIter...> const &other) const
Comparison (based on the Lead iterator only).
Definition: zip.h:182
std::tuple< Iters...> fIterators
Tuple of all zipped iterators.
Definition: zip.h:197
std::add_pointer_t< std::remove_reference_t< reference >> pointer
Definition: zip.h:120
auto make_zip_begin_iterator(Iterables &&...iterables)
Definition: zip.h:268
decltype(auto) get() const
Returns the iterator at the specified Index.
Definition: zip.h:153
void increment_impl(std::index_sequence< Indices...>)
Definition: zip.h:205
std::ptrdiff_t difference_type
Definition: zip.h:236
Simple class with a begin and an end.
Definition: span.h:125
std::remove_cv_t< reference > value_type
Definition: zip.h:238
std::tuple< typename std::iterator_traits< Iters >::reference...> reference
Definition: zip.h:118
std::add_pointer_t< std::remove_reference_t< reference >> pointer
Definition: zip.h:239
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:202
auto dereference_impl(std::index_sequence< Indices...>) const
Definition: zip.h:209
std::forward_iterator_tag iterator_category
Definition: zip.h:240
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
auto operator*() const
Returns a tuple with values from all dereferenced iterators.
Definition: zip.h:148
auto begin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:573
std::ptrdiff_t difference_type
Definition: zip.h:116
std::remove_cv_t< reference > value_type
Definition: zip.h:119
this_iterator_t & operator++()
Increments all the iterators.
Definition: zip.h:164
auto zip(Iterables &&...iterables)
Range-for loop helper iterating across many collections at the same time.
Definition: zip.h:295
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:72
bool operator==(zip_iterator< OtherLead, OtherIter...> const &other) const
Comparison (based on the Lead iterator only).
Definition: zip.h:187
zip_iterator(Iters &&...iterators)
Constructor: copies all iterator values.
Definition: zip.h:135
auto make_zip_end_iterator(Iterables &&...iterables)
Definition: zip.h:279
std::forward_iterator_tag iterator_category
Definition: zip.h:121