LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
RangeForWrapper.h
Go to the documentation of this file.
1 
11 #ifndef LARDATA_UTILITIES_RANGEFORWRAPPER_H
12 #define LARDATA_UTILITIES_RANGEFORWRAPPER_H
13 
14 // Boost libraries
15 #include "boost/variant.hpp"
16 
17 // C/C++ standard libraries
18 #include <iterator> // std::iterator_traits
19 #include <stdexcept> // std::logic_error
20 #include <type_traits> // std::is_same<>, std::enable_if_t<>, ...
21 #include <utility> // std::move(), std::declval(), ...
22 
23 namespace util {
24 
25  namespace details {
26 
29  template <typename BeginIter, typename EndIter>
31 
32  using traits_t = std::iterator_traits<BeginIter>;
33 
34  public:
35  using begin_t = BeginIter;
36  using end_t = EndIter;
38 
41  using difference_type = typename traits_t::difference_type;
42  using value_type = typename traits_t::value_type;
43  using pointer = typename traits_t::pointer;
44  using reference = typename traits_t::reference;
45  //
46  // This wrapper fully supports up to bidirectional iterators;
47  // if the wrapped iterator is a random or contiguous iterator,
48  // the wrapper will still expose only a bidirectional iterator interface.
49  // Supporting random access is possible, but writing a proper unit test
50  // is tedious... open a feature request if needed.
51  //
52  using iterator_category =
53  std::conditional_t<std::is_base_of<std::bidirectional_iterator_tag,
54  typename traits_t::iterator_category>::value,
55  std::bidirectional_iterator_tag,
56  typename traits_t::iterator_category>;
58 
60  explicit RangeForWrapperIterator() : fIter(end_t{}) {}
61 
64 
66  explicit RangeForWrapperIterator(end_t&& end) : fIter(std::move(end)) {}
67 
69  reference operator*() const { return boost::apply_visitor(Dereferencer(), fIter); }
70 
72  pointer operator->() const { return boost::apply_visitor(MemberAccessor(), fIter); }
73 
76  {
77  boost::apply_visitor(Incrementer(), fIter);
78  return *this;
79  }
80 
83  {
84  boost::apply_visitor(Decrementer(), fIter);
85  return *this;
86  }
87 
90  {
91  auto old = *this;
93  return old;
94  }
95 
98  {
99  auto old = *this;
101  return old;
102  }
103 
105  bool operator!=(this_t const& other) const
106  {
107  return boost::apply_visitor(Comparer(), fIter, other.fIter);
108  }
109 
111  bool operator==(this_t const& other) const { return !(this->operator!=(other)); }
112 
114  {
115  return boost::apply_visitor(IndexAccessor(offset), fIter);
116  }
117 
119  {
120  return boost::apply_visitor(Difference(), fIter, other.fIter);
121  }
122 
123  private:
125  "RangeForWrapperIterator requires two different iterator types.");
126 
127  boost::variant<begin_t, end_t> fIter;
128 
129  //
130  // We opt for allowing all the operations if the underlying operators do.
131  // While it is true that, for example, an end iterator should not be
132  // dereferenced, if it's bidirectional, its operator--() may make it
133  // useful, but its type will still be the one of the end iterator.
134  // Therefore we don't judge by the type, but by the action.
135  //
136 
138  struct Dereferencer : public boost::static_visitor<reference> {
139 
140  template <typename Iter>
141  auto operator()(Iter& iter) const -> decltype(auto)
142  {
144  }
145 
146  private:
147  template <typename Result, typename Iter, typename = void>
149 
150  }; // Dereferencer
151 
153  struct MemberAccessor : public boost::static_visitor<pointer> {
154 
155  template <typename Iter>
156  auto operator()(Iter& iter) const -> decltype(auto)
157  {
159  }
160 
161  private:
162  template <typename Result, typename Iter, typename = void>
164 
165  }; // MemberAccessor
166 
168  struct Incrementer : public boost::static_visitor<> {
169 
170  template <typename Iter>
171  void operator()(Iter& iter) const
172  {
174  }
175 
176  private:
177  template <typename Iter, typename = void>
179 
180  }; // Incrementer
181 
183  struct Decrementer : public boost::static_visitor<> {
184 
185  template <typename Iter>
186  void operator()(Iter& iter) const
187  {
189  }
190 
191  private:
192  template <typename Iter, typename = void>
194 
195  }; // Decrementer
196 
198  struct Comparer : public boost::static_visitor<bool> {
199 
200  template <typename A, typename B>
201  bool operator()(A const& left, B const& right) const
202  {
203  return ComparerImpl<A, B>::compare(left, right);
204  }
205 
206  private:
207  template <typename A, typename B, typename = void>
208  struct ComparerImpl;
209 
210  }; // Comparer
211 
213  struct IndexAccessor : public boost::static_visitor<reference> {
214 
216 
217  IndexAccessor(difference_type offset) : offset(offset) {}
218 
219  template <typename Iter>
220  bool operator()(Iter& iter) const
221  {
222  return IndexAccessorImpl<reference, Iter>(offset).access(iter);
223  }
224 
225  private:
226  template <typename Result, typename Iter, typename = void>
228 
229  }; // IndexAccessor
230 
232  struct Difference : public boost::static_visitor<difference_type> {
233 
234  template <typename A, typename B>
235  difference_type operator()(A const& minuend, B const& subtrahend) const
236  {
237  return DifferenceImpl<A, B>::subtract(minuend, subtrahend);
238  }
239 
240  private:
241  template <typename A, typename B, typename = void>
243 
244  }; // Difference
245 
246  }; // class RangeForWrapperIterator<>
247 
249  template <typename RangeRef>
251 
253  "RangeForWrapperBox requires a reference type.");
254 
255  using RangeRef_t = RangeRef;
256 
258  using Range_t = std::remove_reference_t<RangeRef_t>;
259 
261  static auto extractBegin(RangeRef_t range)
262  {
263  using namespace std;
264  return begin(range);
265  }
266 
268  static auto extractEnd(RangeRef_t range)
269  {
270  using namespace std;
271  return end(range);
272  }
273 
275  using BeginIter_t = decltype(extractBegin(std::declval<RangeRef_t>()));
276 
278  using EndIter_t = decltype(extractEnd(std::declval<RangeRef_t>()));
279 
281  static constexpr bool sameIteratorTypes = std::is_same<BeginIter_t, EndIter_t>();
282 
285 
286  using value_type = typename BeginIter_t::value_type;
287  using size_type = std::size_t;
288  using difference_type = typename BeginIter_t::difference_type;
289  using reference = typename BeginIter_t::value_type;
290  using pointer = typename BeginIter_t::pointer;
291 
292  }; // class RangeForWrapperTraits<>
293 
302  template <typename RangeRef>
304 
306  "RangeForWrapperBox requires a reference type.");
307 
309 
310  public:
311  // Import traits
313  using Range_t = typename Traits_t::Range_t;
314 
317 
319  using size_type = typename Traits_t::size_type;
320 
323 
326 
328  RangeForWrapperBox(Range_t& range) : fRange(range) {}
329 
331  RangeForWrapperBox(Range_t&& range) : fRange(std::move(range)) {}
332 
334  Iterator_t begin() const { return Iterator_t(wrappedBegin()); }
335 
337  Iterator_t end() const { return Iterator_t(wrappedEnd()); }
338 
341 
342  auto size() const { return std::distance(begin(), end()); }
343 
344  bool empty() const { return !(wrappedBegin() != wrappedEnd()); }
345 
346  auto operator[](difference_type index) const -> decltype(auto)
347  {
348  return wrappedBegin()[index];
349  }
350 
352 
353  private:
354  struct DataBox {
355 
357  std::remove_reference_t<RangeRef_t>,
359  using Data_t = std::remove_reference_t<Stored_t>;
360 
362 
363  // only one of these is valid...
364  DataBox(Data_t& data) : data(data) {}
365  DataBox(Data_t&& data) : data(std::move(data)) {}
366 
367  operator RangeRef_t() const { return RangeRef_t(data); }
368  operator RangeRef_t() { return RangeRef_t(data); }
369 
370  }; // DataBox
371 
373 
374  auto wrappedBegin() const -> decltype(auto)
375  {
376  return Traits_t::extractBegin(static_cast<RangeRef_t>(fRange));
377  }
378  auto wrappedEnd() const -> decltype(auto)
379  {
380  return Traits_t::extractEnd(static_cast<RangeRef_t>(fRange));
381  }
382 
383  }; // class RangeForWrapperBox<>
384 
386  struct SameIterTag {};
387 
389  struct DiffIterTag {};
390 
393  // the return type decltype(auto) is necessary to preserve the forwarded
394  // referenceness
395  template <typename BaseRange,
396  bool SameIteratorsType =
399 
400  // Template specialization for same iterator types
401  template <typename BaseRange>
402  struct WrapRangeForDispatcher<BaseRange, true> {
403 
404  using BaseRange_t = std::decay_t<BaseRange>;
405 
406  static BaseRange_t wrap(BaseRange_t&& range) { return std::move(range); }
407  static BaseRange_t& wrap(BaseRange_t& range) { return range; }
408  static BaseRange_t const& wrap(BaseRange_t const& range) { return range; }
409  }; // WrapRangeForDispatcher<BaseRange, true>
410 
411  // Template specialization for different-iterator types
412  template <typename BaseRange>
413  struct WrapRangeForDispatcher<BaseRange, false> {
414  template <typename Range>
415  static auto wrap(Range&& range)
416  {
417  return RangeForWrapperBox<decltype(range)>(static_cast<decltype(range)>(range));
418  }
419  }; // WrapRangeForDispatcher<BaseRange, false>
420 
421  } // namespace details
422 
439  template <typename Range>
440  auto wrapRangeFor(Range&& range) -> decltype(auto)
441  {
442  return details::WrapRangeForDispatcher<Range>::wrap(std::forward<Range>(range));
443  }
444 
447 
451 
469  template <typename Range>
470  auto operator|(Range&& range, RangeForWrapperTag) -> decltype(auto)
471  {
472  return wrapRangeFor(std::forward<Range>(range));
473  }
474 
475 } // namespace util
476 
477 //------------------------------------------------------------------------------
478 //--- template implementation
479 //------------------------------------------------------------------------------
480 namespace util {
481 
482  namespace details {
483 
484  //--------------------------------------------------------------------------
485  template <typename T>
486  struct is_type : public std::true_type {};
487 
488  template <typename T>
489  constexpr bool is_type_v = is_type<T>();
490 
491  //--------------------------------------------------------------------------
492  template <typename BeginIter, typename EndIter>
493  template <typename A, typename B, typename /* = void */>
494  struct RangeForWrapperIterator<BeginIter, EndIter>::Comparer::ComparerImpl {
495  // this would be worth a static_assert(), but apparently boost::variant
496  // visitor instantiates it even when it's not called
497  static bool compare(A const&, B const&)
498  {
499  throw std::logic_error("These iterators can't be compared!");
500  }
501  }; //
502 
503  //--------------------------------------------------------------------------
504  template <typename BeginIter, typename EndIter>
505  template <typename A, typename B>
506  struct RangeForWrapperIterator<BeginIter, EndIter>::Comparer::ComparerImpl<
507  A,
508  B,
509  std::enable_if_t<
510  std::is_convertible<decltype(std::declval<A>() != std::declval<B>()), bool>::value>> {
511  static bool compare(A const& left, B const& right) { return left != right; }
512  }; //
513 
514  //--------------------------------------------------------------------------
515  template <typename BeginIter, typename EndIter>
516  template <typename Result, typename Iter, typename /* = void */>
517  struct RangeForWrapperIterator<BeginIter, EndIter>::Dereferencer::DereferencerImpl {
518  // this would be worth a static_assert(), but apparently boost::variant
519  // visitor instantiates it even when it's not called
520  [[noreturn]] static Result dereference(Iter const&)
521  {
522  throw std::logic_error("This iterator can't be dereferenced!");
523  }
524  }; //
525 
526  //--------------------------------------------------------------------------
527  template <typename BeginIter, typename EndIter>
528  template <typename Result, typename Iter>
530  Result,
531  Iter,
532  std::enable_if_t<is_type_v<decltype(*(std::declval<Iter>()))>>> {
533  static Result dereference(Iter const& iter) { return *iter; }
534  }; //
535 
536  //--------------------------------------------------------------------------
537  template <typename BeginIter, typename EndIter>
538  template <typename Result, typename Iter, typename /* = void */>
539  struct RangeForWrapperIterator<BeginIter, EndIter>::MemberAccessor::MemberAccessorImpl {
540  // this would be worth a static_assert(), but apparently boost::variant
541  // visitor instantiates it even when it's not called
542  [[noreturn]] static Result access(Iter const&)
543  {
544  throw std::logic_error("This iterator can't be dereferenced!");
545  }
546  }; //
547 
548  //--------------------------------------------------------------------------
549  template <typename BeginIter, typename EndIter>
550  template <typename Result, typename Iter>
552  Result,
553  Iter,
554  std::enable_if_t<is_type_v<decltype(std::declval<Iter>().operator->())>>> {
555  static Result access(Iter const& iter) { return iter.operator->(); }
556  }; //
557 
558  //--------------------------------------------------------------------------
559  template <typename BeginIter, typename EndIter>
560  template <typename Iter, typename /* = void */>
561  struct RangeForWrapperIterator<BeginIter, EndIter>::Incrementer::IncrementerImpl {
562  // this would be worth a static_assert(), but apparently boost::variant
563  // visitor instantiates it even when it's not called
564  [[noreturn]] static void increment(Iter&)
565  {
566  throw std::logic_error("This iterator can't be incremented!");
567  }
568  }; //
569 
570  //--------------------------------------------------------------------------
571  template <typename BeginIter, typename EndIter>
572  template <typename Iter>
573  struct RangeForWrapperIterator<BeginIter, EndIter>::Incrementer::
574  IncrementerImpl<Iter, std::enable_if_t<is_type_v<decltype(++(std::declval<Iter>()))>>> {
575  static void increment(Iter& iter) { ++iter; }
576  }; //
577 
578  //--------------------------------------------------------------------------
579  template <typename BeginIter, typename EndIter>
580  template <typename Iter, typename /* = void */>
581  struct RangeForWrapperIterator<BeginIter, EndIter>::Decrementer::DecrementerImpl {
582  // this would be worth a static_assert(), but apparently boost::variant
583  // visitor instantiates it even when it's not called
584  [[noreturn]] static void decrement(Iter&)
585  {
586  throw std::logic_error("This iterator can't be decremented!");
587  }
588  }; //
589 
590  //--------------------------------------------------------------------------
591  template <typename BeginIter, typename EndIter>
592  template <typename Iter>
593  struct RangeForWrapperIterator<BeginIter, EndIter>::Decrementer::
594  DecrementerImpl<Iter, std::enable_if_t<is_type_v<decltype(--(std::declval<Iter>()))>>> {
595  static void decrement(Iter& iter) { --iter; }
596  }; //
597 
598  //--------------------------------------------------------------------------
599  template <typename BeginIter, typename EndIter>
600  template <typename Result, typename Iter, typename /* = void */>
601  struct RangeForWrapperIterator<BeginIter, EndIter>::IndexAccessor::IndexAccessorImpl {
602  // this would be worth a static_assert(), but apparently boost::variant
603  // visitor instantiates it even when it's not called
604 
606 
607  [[noreturn]] Result access(Iter const&) const
608  {
609  throw std::logic_error("This iterator can't be indexed!");
610  }
611  }; //
612 
613  //--------------------------------------------------------------------------
614  template <typename BeginIter, typename EndIter>
615  template <typename Result, typename Iter>
617  Result,
618  Iter,
619  std::enable_if_t<is_type_v<decltype((std::declval<Iter>())[0])>>> {
621 
622  IndexAccessorImpl(difference_type offset) : offset(offset) {}
623 
624  Result dereference(Iter const& iter) const { return iter[offset]; }
625  }; //
626 
627  //--------------------------------------------------------------------------
628  template <typename BeginIter, typename EndIter>
629  template <typename A, typename B, typename /* = void */>
630  struct RangeForWrapperIterator<BeginIter, EndIter>::Difference::DifferenceImpl {
631  // this would be worth a static_assert(), but apparently boost::variant
632  // visitor instantiates it even when it's not called
633  static difference_type subtract(A const&, B const&)
634  {
635  throw std::logic_error("These iterators can't be subtracted!");
636  }
637  }; //
638 
639  //--------------------------------------------------------------------------
640  template <typename BeginIter, typename EndIter>
641  template <typename A, typename B>
643  A,
644  B,
645  std::enable_if_t<std::is_convertible<
646  decltype(std::declval<A>() - std::declval<B>()),
647  typename RangeForWrapperIterator<BeginIter, EndIter>::difference_type>::value>> {
648  static difference_type subtract(A const& minuend, B const& subtrahend)
649  {
650  return minuend - subtrahend;
651  }
652  }; //
653 
654  //--------------------------------------------------------------------------
655 
656  } // namespace details
657 } // namespace util
658 
659 //------------------------------------------------------------------------------
660 
661 #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
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:26
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:102
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.
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).
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.
auto wrapRangeFor(Range &&range) -> decltype(auto)
Wraps an object for use in a range-for loop.
STL namespace.
reference operator[](difference_type offset) const
auto wrappedBegin() const -> decltype(auto)
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.
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 Traits_t::value_type value_type
Type of value contained in the wrapped sequence.
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.
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
boost::variant< begin_t, end_t > fIter
The actual iterator we store.
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.
bool operator()(A const &left, B const &right) const
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).
std::conditional_t< std::is_rvalue_reference< RangeRef_t >::value, std::remove_reference_t< RangeRef_t >, RangeRef_t > Stored_t
typename traits_t::pointer pointer
Iterator traits, imported from the wrapped begin iterator.
double value
Definition: spectrum.C:18
typename Traits_t::RangeRef_t RangeRef_t
decltype(extractBegin(std::declval< RangeRef_t >())) BeginIter_t
Type of wrapped begin iterator.
auto operator()(Iter &iter) const -> decltype(auto)
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:94
auto operator()(Iter &iter) const -> decltype(auto)
typename traits_t::value_type value_type
Iterator traits, imported from the wrapped begin iterator.
auto operator[](difference_type index) const -> decltype(auto)
auto operator|(Range &&range, RangeForWrapperTag) -> decltype(auto)
Transforms a range so that it can be used in a range-for loop.
Iterator_t end() const
Returns a end-of-range iterator.
difference_type operator()(A const &minuend, B const &subtrahend) const
auto wrappedEnd() const -> decltype(auto)
represents a "Range" w/ notion of ordering. A range is defined by a pair of "start" and "end" values...
Definition: Range.h:35
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
typename Traits_t::size_type size_type
Type of number of stored elements.
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.
Visitor to compare iterators (returns whether they differ).