LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
RangeForWrapper.h
Go to the documentation of this file.
1 
13 #ifndef LARDATA_UTILITIES_RANGEFORWRAPPER_H
14 #define LARDATA_UTILITIES_RANGEFORWRAPPER_H
15 
16 // Boost libraries
17 #include "boost/variant.hpp"
18 
19 // C/C++ standard libraries
20 #include <stdexcept> // std::logic_error
21 #include <utility> // std::move(), std::declval(), ...
22 #include <iterator> // std::iterator_traits
23 #include <type_traits> // std::is_same<>, std::enable_if_t<>, ...
24 
25 
26 namespace util {
27 
28  namespace details {
29 
32  template <typename BeginIter, typename EndIter>
34  struct Dereferencer;
35  struct Incrementer;
36  struct Comparer;
37  struct Dumper;
38 
39  using traits_t = std::iterator_traits<BeginIter>;
40 
41  public:
42  using begin_t = BeginIter;
43  using end_t = EndIter;
45 
48  using difference_type = typename traits_t::difference_type;
49  using value_type = typename traits_t::value_type;
50  using pointer = typename traits_t::pointer;
51  using reference = typename traits_t::reference;
52  //
53  // This wrapper fully supports up to bidirectional iterators;
54  // if the wrapped iterator is a random or contiguous iterator,
55  // the wrapper will still expose only a bidirectional iterator interface.
56  // Supporting random access is possible, but writing a proper unit test
57  // is tedious... open a feature request if needed.
58  //
59  using iterator_category = std::conditional_t<
61  std::bidirectional_iterator_tag,
62  typename traits_t::iterator_category
63  >;
65 
68  : fIter(end_t{})
69  {}
70 
73  : fIter(std::move(begin))
74  {}
75 
78  : fIter(std::move(end))
79  {}
80 
83  { return boost::apply_visitor(Dereferencer(), fIter); }
84 
87  { return boost::apply_visitor(MemberAccessor(), fIter); }
88 
91  { boost::apply_visitor(Incrementer(), fIter); return *this; }
92 
95  { boost::apply_visitor(Decrementer(), fIter); return *this; }
96 
99  { auto old = *this; this_t::operator++(); return old; }
100 
103  { auto old = *this; this_t::operator--(); return old; }
104 
106  bool operator!=(this_t const& other) const
107  { return boost::apply_visitor(Comparer(), fIter, other.fIter); }
108 
110  bool operator==(this_t const& other) const
111  { return !(this->operator!=(other)); }
112 
113 
115  { return boost::apply_visitor(IndexAccessor(offset), fIter); }
116 
118  { return boost::apply_visitor(Difference(), fIter, other.fIter); }
119 
120  private:
122  "RangeForWrapperIterator requires two different iterator types."
123  );
124 
125  boost::variant<begin_t, end_t> fIter;
126 
127  //
128  // We opt for allowing all the operations if the underlying operators do.
129  // While it is true that, for example, an end iterator should not be
130  // dereferenced, if it's bidirectional, its operator--() may make it
131  // useful, but its type will still be the one of the end iterator.
132  // Therefore we don't judge by the type, but by the action.
133  //
134 
136  struct Dereferencer: public boost::static_visitor<reference> {
137 
138  template <typename Iter>
139  auto operator() (Iter& iter) const -> decltype(auto)
141 
142  private:
143  template <typename Result, typename Iter, typename = void>
145 
146  }; // Dereferencer
147 
149  struct MemberAccessor: public boost::static_visitor<pointer> {
150 
151  template <typename Iter>
152  auto operator() (Iter& iter) const -> decltype(auto)
154 
155  private:
156  template <typename Result, typename Iter, typename = void>
158 
159  }; // MemberAccessor
160 
162  struct Incrementer: public boost::static_visitor<> {
163 
164  template <typename Iter>
165  void operator() (Iter& iter) const
167 
168  private:
169  template <typename Iter, typename = void>
171 
172  }; // Incrementer
173 
175  struct Decrementer: public boost::static_visitor<> {
176 
177  template <typename Iter>
178  void operator() (Iter& iter) const
180 
181  private:
182  template <typename Iter, typename = void>
184 
185  }; // Decrementer
186 
187 
189  struct Comparer: public boost::static_visitor<bool> {
190 
191  template <typename A, typename B>
192  bool operator() (A const& left, B const& right) const
193  { return ComparerImpl<A, B>::compare(left, right); }
194 
195  private:
196  template <typename A, typename B, typename = void>
197  struct ComparerImpl;
198 
199  }; // Comparer
200 
202  struct IndexAccessor: public boost::static_visitor<reference> {
203 
205 
206  IndexAccessor(difference_type offset): offset(offset) {}
207 
208  template <typename Iter>
209  bool operator() (Iter& iter) const
210  { return IndexAccessorImpl<reference, Iter>(offset).access(iter); }
211 
212  private:
213  template <typename Result, typename Iter, typename = void>
215 
216  }; // IndexAccessor
217 
219  struct Difference: public boost::static_visitor<difference_type > {
220 
221  template <typename A, typename B>
222  difference_type operator() (A const& minuend, B const& subtrahend) const
223  { return DifferenceImpl<A, B>::subtract(minuend, subtrahend); }
224 
225  private:
226  template <typename A, typename B, typename = void>
228 
229  }; // Difference
230 
231  }; // class RangeForWrapperIterator<>
232 
233 
234 
236  template <typename RangeRef>
238 
239  using RangeRef_t = RangeRef;
240 
242  using Range_t = std::remove_reference_t<RangeRef_t>;
243 
245  static auto extractBegin(RangeRef_t range)
246  { using namespace std; return begin(static_cast<RangeRef_t>(range)); }
247 
249  static auto extractEnd(RangeRef_t range)
250  { using namespace std; return end(static_cast<RangeRef_t>(range)); }
251 
253  using BeginIter_t = decltype(extractBegin(std::declval<RangeRef_t>()));
254 
256  using EndIter_t = decltype(extractEnd(std::declval<RangeRef_t>()));
257 
259  static constexpr bool sameIteratorTypes
260  = std::is_same<BeginIter_t, EndIter_t>();
261 
264 
265  using value_type = typename BeginIter_t::value_type;
266  using size_type = std::size_t;
267  using difference_type = typename BeginIter_t::difference_type;
268  using reference = typename BeginIter_t::value_type;
269  using pointer = typename BeginIter_t::pointer;
270 
271  }; // class RangeForWrapperTraits<>
272 
273 
282  template <typename RangeRef>
284 
286  "RangeForWrapperBox requires a reference type.");
287 
289 
290  public:
291 
292  // Import traits
294  using Range_t = typename Traits_t::Range_t;
295 
298 
300  using size_type = typename Traits_t::size_type;
301 
304 
307  : fRange(range)
308  {}
309 
312  : fRange(std::move(range))
313  {}
314 
317  { return Iterator_t(wrappedBegin()); }
318 
320  Iterator_t end() const
321  { return Iterator_t(wrappedEnd()); }
322 
325 
326  auto size() const { return std::distance(begin(), end()); }
327 
328  bool empty() const { return !(wrappedBegin() != wrappedEnd()); }
329 
330  auto operator[] (difference_type index) const -> decltype(auto)
331  { return wrappedBegin()[index]; }
332 
334 
335 
336  private:
337 
338  struct DataBox {
339 
340  using Stored_t = std::conditional_t<
342  std::remove_reference_t<RangeRef_t>,
343  RangeRef_t
344  >;
345  using Data_t = std::remove_reference_t<Stored_t>;
346 
348 
349  // only one of these is valid...
350  DataBox(Data_t& data): data(data) {}
351  DataBox(Data_t&& data): data(std::move(data)) {}
352 
353  operator RangeRef_t() const { return RangeRef_t(data); }
354  operator RangeRef_t() { return RangeRef_t(data); }
355 
356  }; // DataBox
357 
359 
360  auto wrappedBegin() const -> decltype(auto)
361  { return Traits_t::extractBegin(static_cast<RangeRef_t>(fRange)); }
362  auto wrappedEnd() const -> decltype(auto)
363  { return Traits_t::extractEnd(static_cast<RangeRef_t>(fRange)); }
364 
365  }; // class RangeForWrapperBox<>
366 
367 
369  struct SameIterTag {};
370 
372  struct DiffIterTag {};
373 
376  // the return type decltype(auto) is necessary to preserve the forwarded
377  // referenceness
378  template <
379  typename BaseRange,
380  bool SameIteratorsType
382  >
384 
385  // Template specialization for same iterator types
386  template <typename BaseRange>
387  struct WrapRangeForDispatcher<BaseRange, true> {
388 
389  using BaseRange_t = std::decay_t<BaseRange>;
390 
391  static BaseRange_t wrap(BaseRange_t&& range) { return std::move(range); }
392  static BaseRange_t& wrap(BaseRange_t& range) { return range; }
393  static BaseRange_t const& wrap(BaseRange_t const& range) { return range; }
394  }; // WrapRangeForDispatcher<BaseRange, true>
395 
396 
397  // Template specialization for different-iterator types
398  template <typename BaseRange>
399  struct WrapRangeForDispatcher<BaseRange, false> {
400  template <typename Range>
401  static auto wrap(Range&& range)
402  {
404  (static_cast<decltype(range)>(range));
405  }
406  }; // WrapRangeForDispatcher<BaseRange, false>
407 
408  } // namespace details
409 
410 
427  template <typename Range>
428  auto wrapRangeFor(Range&& range) -> decltype(auto)
429  {
431  (std::forward<Range>(range));
432  }
433 
434 
437 
441 
459  template <typename Range>
460  auto operator| (Range&& range, RangeForWrapperTag) -> decltype(auto)
461  { return wrapRangeFor(std::forward<Range>(range)); }
462 
463 
464 } // namespace util
465 
466 
467 //------------------------------------------------------------------------------
468 //--- template implementation
469 //------------------------------------------------------------------------------
470 namespace util {
471 
472  namespace details {
473 
474  //--------------------------------------------------------------------------
475  template <typename T>
476  struct is_type: public std::true_type {};
477 
478  template <typename T>
479  constexpr bool is_type_v = is_type<T>();
480 
481 
482  //--------------------------------------------------------------------------
483  template <typename BeginIter, typename EndIter>
484  template <typename A, typename B, typename /* = void */>
485  struct RangeForWrapperIterator<BeginIter, EndIter>::Comparer::ComparerImpl {
486  // this would be worth a static_assert(), but apparently boost::variant
487  // visitor instantiates it even when it's not called
488  static bool compare(A const&, B const&)
489  { throw std::logic_error("These iterators can't be compared!"); }
490  }; //
491 
492  //--------------------------------------------------------------------------
493  template <typename BeginIter, typename EndIter>
494  template <typename A, typename B>
495  struct RangeForWrapperIterator<BeginIter, EndIter>::Comparer::ComparerImpl<
496  A, B, std::enable_if_t<
497  std::is_convertible
498  <decltype(std::declval<A>() != std::declval<B>()), bool>::value
499  >
500  >
501  {
502  static bool compare(A const& left, B const& right)
503  { return left != right; }
504  }; //
505 
506 
507  //--------------------------------------------------------------------------
508  template <typename BeginIter, typename EndIter>
509  template <typename Result, typename Iter, typename /* = void */>
510  struct RangeForWrapperIterator<BeginIter, EndIter>::Dereferencer::DereferencerImpl {
511  // this would be worth a static_assert(), but apparently boost::variant
512  // visitor instantiates it even when it's not called
513  [[noreturn]] static Result dereference(Iter const&)
514  { throw std::logic_error("This iterator can't be dereferenced!"); }
515  }; //
516 
517  //--------------------------------------------------------------------------
518  template <typename BeginIter, typename EndIter>
519  template <typename Result, typename Iter>
521  Result, Iter, std::enable_if_t<is_type_v<decltype(*(std::declval<Iter>()))>>
522  >
523  {
524  static Result dereference(Iter const& iter)
525  { return *iter; }
526  }; //
527 
528 
529  //--------------------------------------------------------------------------
530  template <typename BeginIter, typename EndIter>
531  template <typename Result, typename Iter, typename /* = void */>
532  struct RangeForWrapperIterator<BeginIter, EndIter>::MemberAccessor::MemberAccessorImpl {
533  // this would be worth a static_assert(), but apparently boost::variant
534  // visitor instantiates it even when it's not called
535  [[noreturn]] static Result access(Iter const&)
536  { throw std::logic_error("This iterator can't be dereferenced!"); }
537  }; //
538 
539  //--------------------------------------------------------------------------
540  template <typename BeginIter, typename EndIter>
541  template <typename Result, typename Iter>
543  Result, Iter, std::enable_if_t<is_type_v<decltype(std::declval<Iter>().operator->())>>
544  >
545  {
546  static Result access(Iter const& iter)
547  { return iter.operator->(); }
548  }; //
549 
550 
551  //--------------------------------------------------------------------------
552  template <typename BeginIter, typename EndIter>
553  template <typename Iter, typename /* = void */>
554  struct RangeForWrapperIterator<BeginIter, EndIter>::Incrementer::IncrementerImpl {
555  // this would be worth a static_assert(), but apparently boost::variant
556  // visitor instantiates it even when it's not called
557  [[noreturn]] static void increment(Iter&)
558  { throw std::logic_error("This iterator can't be incremented!"); }
559  }; //
560 
561  //--------------------------------------------------------------------------
562  template <typename BeginIter, typename EndIter>
563  template <typename Iter>
565  Iter, std::enable_if_t<is_type_v<decltype(++(std::declval<Iter>()))>>
566  >
567  {
568  static void increment(Iter& iter)
569  { ++iter; }
570  }; //
571 
572 
573  //--------------------------------------------------------------------------
574  template <typename BeginIter, typename EndIter>
575  template <typename Iter, typename /* = void */>
576  struct RangeForWrapperIterator<BeginIter, EndIter>::Decrementer::DecrementerImpl {
577  // this would be worth a static_assert(), but apparently boost::variant
578  // visitor instantiates it even when it's not called
579  [[noreturn]] static void decrement(Iter&)
580  { throw std::logic_error("This iterator can't be decremented!"); }
581  }; //
582 
583  //--------------------------------------------------------------------------
584  template <typename BeginIter, typename EndIter>
585  template <typename Iter>
587  Iter, std::enable_if_t<is_type_v<decltype(--(std::declval<Iter>()))>>
588  >
589  {
590  static void decrement(Iter& iter)
591  { --iter; }
592  }; //
593 
594 
595  //--------------------------------------------------------------------------
596  template <typename BeginIter, typename EndIter>
597  template <typename Result, typename Iter, typename /* = void */>
598  struct RangeForWrapperIterator<BeginIter, EndIter>::IndexAccessor::IndexAccessorImpl {
599  // this would be worth a static_assert(), but apparently boost::variant
600  // visitor instantiates it even when it's not called
601 
603 
604  [[noreturn]] Result access(Iter const&) const
605  { throw std::logic_error("This iterator can't be indexed!"); }
606  }; //
607 
608  //--------------------------------------------------------------------------
609  template <typename BeginIter, typename EndIter>
610  template <typename Result, typename Iter>
612  Result, Iter, std::enable_if_t<is_type_v<decltype((std::declval<Iter>())[0])>>
613  >
614  {
616 
617  IndexAccessorImpl(difference_type offset): offset(offset) {}
618 
619  Result dereference(Iter const& iter) const
620  { return iter[offset]; }
621  }; //
622 
623 
624  //--------------------------------------------------------------------------
625  template <typename BeginIter, typename EndIter>
626  template <typename A, typename B, typename /* = void */>
627  struct RangeForWrapperIterator<BeginIter, EndIter>::Difference::DifferenceImpl {
628  // this would be worth a static_assert(), but apparently boost::variant
629  // visitor instantiates it even when it's not called
630  static difference_type subtract(A const&, B const&)
631  { throw std::logic_error("These iterators can't be subtracted!"); }
632  }; //
633 
634  //--------------------------------------------------------------------------
635  template <typename BeginIter, typename EndIter>
636  template <typename A, typename B>
638  A, B, std::enable_if_t<
639  std::is_convertible<
640  decltype(std::declval<A>() - std::declval<B>()),
641  typename RangeForWrapperIterator<BeginIter, EndIter>::difference_type>::value
642  >
643  >
644  {
645  static difference_type subtract(A const& minuend, B const& subtrahend)
646  { return minuend - subtrahend; }
647  }; //
648 
649 
650  //--------------------------------------------------------------------------
651 
652 
653  } // namespace details
654 } // namespace util
655 
656 
657 //------------------------------------------------------------------------------
658 
659 
660 #endif // LARDATA_UTILITIES_RANGEFORWRAPPER_H
reference operator*() const
Returns the pointed value (just like the original iterator).
static BaseRange_t wrap(BaseRange_t &&range)
std::remove_reference_t< RangeRef_t > Range_t
DataBox fRange
A reference to the original range.
static BaseRange_t & wrap(BaseRange_t &range)
std::iterator_traits< BeginIter > traits_t
auto operator|(Range &&range, RangeForWrapperTag) -> decltype(auto)
Transforms a range so that it can be used in a range-for loop.
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:17
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
this_t & operator++()
Increments the iterator (prefix operator).
bool operator==(this_t const &other) const
Returns whether the other iterator is equal to this one.
auto wrappedEnd() const -> decltype(auto)
Tag for internal use.
static auto extractEnd(RangeRef_t range)
Extracts the end iterator from a range object.
this_t operator--(int)
Decrements the iterator (postfix operator).
Int_t B
Definition: plot.C:25
typename Traits_t::difference_type difference_type
Type of difference between element positions.
typename Traits_t::Iterator_t Iterator_t
Type of wrapper iterators (same for begin and end iterators).
difference_type operator-(this_t const &other) const
Visitor to compare iterators (returns whether they differ).
RangeForWrapperIterator(begin_t &&begin)
Constructor: initializes with a begin-type iterator.
bool operator!=(this_t const &other) const
Returns whether the other iterator is not equal to this one.
RangeRef RangeRef_t
Type of the stored range (constantness is preserved).
Class offering begin/end iterators of the same type out of a range of iterators of different types...
typename traits_t::reference reference
Iterator traits, imported from the wrapped begin iterator.
STL namespace.
reference operator[](difference_type offset) const
static auto extractBegin(RangeRef_t range)
Extractor of the begin iterator from a range.
Iterator_t begin() const
Returns a begin-of-range iterator.
static difference_type subtract(A const &, B const &)
typename BeginIter_t::pointer pointer
typename BeginIter_t::value_type reference
static BaseRange_t const & wrap(BaseRange_t const &range)
EndIter end_t
Type of end iterator we can store.
boost::variant< begin_t, end_t > fIter
The actual iterator we store.
auto wrapRangeFor(Range &&range) -> decltype(auto)
Wraps an object for use in a range-for loop.
std::remove_reference_t< Stored_t > Data_t
constexpr RangeForWrapperTag range_for
pointer operator->() const
Returns the pointed value (just like the original iterator).
typename Traits_t::Range_t Range_t
typename traits_t::difference_type difference_type
Iterator traits, imported from the wrapped begin iterator.
std::vector< evd::details::RawDigitInfo_t >::const_iterator begin(RawDigitCacheDataClass const &cache)
Tag for internal use.
RangeForWrapperBox(Range_t &range)
Constructor: references the specified range (lvalue reference).
typename BeginIter_t::value_type value_type
Visitor to access a data member of the pointed class.
this_t operator++(int)
Increments the iterator (postfix operator).
typename traits_t::pointer pointer
Iterator traits, imported from the wrapped begin iterator.
typename Traits_t::RangeRef_t RangeRef_t
auto wrappedBegin() const -> decltype(auto)
decltype(extractBegin(std::declval< RangeRef_t >())) BeginIter_t
Type of wrapped begin iterator.
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:104
std::string value(boost::any const &)
typename traits_t::value_type value_type
Iterator traits, imported from the wrapped begin iterator.
Iterator_t end() const
Returns a end-of-range iterator.
represents a "Range" w/ notion of ordering. A range is defined by a pair of "start" and "end" values...
Definition: Range.h:35
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
typename Traits_t::size_type size_type
Type of number of stored elements.
std::conditional_t< std::is_base_of< std::bidirectional_iterator_tag, typename traits_t::iterator_category >::value, std::bidirectional_iterator_tag, typename traits_t::iterator_category > iterator_category
Iterator traits, imported from the wrapped begin iterator.
RangeForWrapperIterator()
Constructor: initializes with a end-type default-constructed iterator.
BeginIter begin_t
Type of begin iterator we can store.
typename BeginIter_t::difference_type difference_type
this_t & operator--()
Decrements the iterator (prefix operator).
RangeForWrapperBox(Range_t &&range)
Constructor: references the specified range (rvalue reference).
constexpr bool is_type_v
decltype(extractEnd(std::declval< RangeRef_t >())) EndIter_t
Type of wrapped end iterator.
Class defining types and traits for RangeForWrapperBox.
RangeForWrapperIterator(end_t &&end)
Constructor: initializes with a end-type iterator.
Tag marking the use of RangeForWrapperBox.
auto operator()(Iter &iter) const -> decltype(auto)
std::conditional_t< std::is_rvalue_reference< RangeRef_t >::value, std::remove_reference_t< RangeRef_t >, RangeRef_t > Stored_t
Visitor to compare iterators (returns whether they differ).