CollectionView.h
Go to the documentation of this file.
1 /**
2  * @file lardata/Utilities/CollectionView.h
3  * @brief Provides the features of a collections, from begin and end iterators.
4  * @author Gianluca Petrillo (petrillo@fnal.gov)
5  * @date August 3rd, 2017
6  *
7  * This library is header-only.
8  *
9  * @note It is likely that a skilful use of Nibbler's range library will
10  * provide the same functionality (and loads more).
11  *
12  *
13  *
14  * @section InterfaceSubstitution Interface substitution technique
15  *
16  * A technique that is used in this implementation is to replace (or extend) the
17  * interface of an existing object.
18  * A key requirement is that the new interface object must not have any
19  * additional state.
20  *
21  * The interface class is superimposed to the _existing_ data without
22  * replication by _reinterpreting_ its content. This is achieved deriving the
23  * new interface class from the data class:
24  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
25  *
26  * class Data {
27  * double chiSq;
28  * double NDF;
29  * public:
30  * Data(double chiSq, double NDF): chiSq(chiSq), NDF(NDF) {}
31  *
32  * double chiSquare() const { return chiSq; }
33  * double DegreesOfFreedom() const { return NDF; }
34  *
35  * }; // class Data
36  *
37  *
38  * class DataInterface: private Data {
39  * Data const& asData() const { return static_cast<Data const&>(*this); }
40  * public:
41  *
42  * double normalizedChiSquare() const
43  * { return asData().chiSquare() / asData().DegreesOfFreedom(); }
44  *
45  * protected:
46  * DataInterface(Data const& from): Data(data) {}
47  *
48  * friend DataInterface const& makeDataInterface(Data const&);
49  *
50  * }; // class DataInterface
51  *
52  *
53  * DataInterface const& makeDataInterface(Data const& data)
54  * { return static_const<DataInterface const&>(data); }
55  *
56  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57  * With this pattern, an interface object can be obtained only by calling
58  * `makeDataInterface()` on the base object, and in this way it will be returned
59  * only as a reference (in this case, constant). The interface object can't
60  * be copied, and it must be passed around as reference. It's not possible to
61  * convert it back to `Data`, because the base class is private.
62  * There is a single protected constructor. This choice, compared to deleting
63  * all constructors, allows for a derived class to acquire the same interface:
64  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
65  * struct DataWithInterface: public DataInterface {
66  * DataWithInterface(Data const& from): DataInterface(from) {}
67  * }; // class DataWithInterface
68  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
69  * This simple class provides the storage for `Data` in addition to exposing
70  * `DataInterface`.
71  * There are other ways to achieve the same goal (e.g., multiple inheritance).
72  *
73  * The presence of a friend function should raise a warning. Friendship is
74  * required because the function is attempting a downcast from a private base
75  * class. If it is intended that the full `Data` interface is exposed, then
76  * the inheritance can be public and no special friendship will be needed.
77  * Another way is to replace the `static_cast` with a `reinterpret_cast`.
78  *
79  *
80  */
81 
82 #ifndef LARDATA_UTILITIES_COLLECTIONVIEW_H
83 #define LARDATA_UTILITIES_COLLECTIONVIEW_H
84 
85 
86 // C/C++ standard libraries
87 #include <stdexcept> // std::out_of_range
88 #include <iterator> // std::iterator_traits, std::reverse_iterator
89 #include <string> // std::to_string()
90 #include <type_traits> // std::declval(), ...
91 #include <cstddef> // std::size_t
92 
93 
94 namespace lar {
95 
96  // forward declarations
97  template <typename Range>
99 
100 
101  namespace details {
102 
103  //--------------------------------------------------------------------------
104  template <typename Range>
105  struct RangeTraits {
106  using range_t = Range;
108  static auto getCBegin(range_t const& range)
109  { using std::cbegin; return cbegin(range); }
110  static auto getCEnd(range_t const& range)
111  { using std::cend; return cend(range); }
112  using begin_iterator_t
113  = std::decay_t<decltype(traits_t::getCBegin(std::declval<range_t>()))>;
114  using end_iterator_t
115  = std::decay_t<decltype(traits_t::getCEnd(std::declval<range_t>()))>;
116  }; // RangeTraits<>
117 
118 
119  //--------------------------------------------------------------------------
120  /// Class storing a begin and a end iterator.
121  template <typename BeginIter, typename EndIter = BeginIter>
123  public:
124  using begin_iterator_t = BeginIter;
125  using end_iterator_t = EndIter;
126 
127  struct FromContainerTag {};
128  static constexpr FromContainerTag fromContainer{};
129 
130  /// Constructor: copies the specified iterators.
131  CollectionExtremes(BeginIter b, EndIter e): b(b), e(e) {}
132 
133  /// Returns the stored begin iterator.
134  begin_iterator_t const& begin() const { return b; }
135 
136  /// Returns the stored end iterator.
137  end_iterator_t const& end() const { return e; }
138 
139 
140  private:
141  begin_iterator_t b; ///< Stored copy of begin iterator.
142  end_iterator_t e; ///< Stored copy of end iterator.
143  }; // class CollectionExtremes<>
144 
145 
146  //--------------------------------------------------------------------------
147  /// Helper to create a CollectionExtremes object from two iterators.
148  template <typename BeginIter, typename EndIter>
149  auto makeCollectionExtremes(BeginIter const& b, EndIter const& e)
151 
152  //--------------------------------------------------------------------------
153  /// Helper to create a CollectionExtremes object from a range object.
154  template <typename Range>
155  auto makeCollectionExtremes(Range const& range)
156  {
157  using std::cbegin;
158  using std::cend;
159  return makeCollectionExtremes(cbegin(range), cend(range));
160  } // makeCollectionExtremes(range)
161 
162  //--------------------------------------------------------------------------
163  // forward declaration
164  template <typename Range>
166  //--------------------------------------------------------------------------
167 
168  } // namespace details
169 
170 
171  //----------------------------------------------------------------------------
172  /**
173  * @brief Provides features of a collections, from begin and end iterators.
174  * @tparam Range the type of collection to be wrapped
175  *
176  * A collection view is a class that offers a collection-like interface,
177  * mostly like the C+ standard library containers, based on two iterators.
178  *
179  * The base, wrapped collection is required to respond to `begin()` and a
180  * `end()` global functions. If the desired view is not described by such
181  * an object, a temporary one must be created (see `makeCollectionView()`).
182  *
183  * There are two ways to use this class:
184  * 1. to wrap an existing `Range`-like container
185  * 2. to turn two iterators into a container
186  *
187  * The two use cases are both addressed by this class, but helper functions
188  * are provided to make it easier to create them as needed.
189  *
190  * @note While the object is currently copiable and moveable, this is not
191  * guaranteed for the future.
192  *
193  *
194  * Wrap an existing `Range`-like container
195  * ----------------------------------------
196  *
197  * Here we assume there is somewhere an instance of the object `range` which
198  * fulfills the requirement of the `Range` type above, that is it responds
199  * to the requests of a `begin()` and a `end()` iterator.
200  *
201  * To create a collection view of `range`, the easiest way is to use
202  * `wrapCollectionIntoView()`. In the following example the `range` object
203  * is a STL vector (which does not really need any added interface...):
204  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
205  * std::vector<int> range(5);
206  * std::iota(range.begin(), range.end(), 1); // { 1, 2, 3, 4, 5 }
207  *
208  * for (int d: lar::wrapCollectionIntoView(range)) {
209  * std::cout << d << " ";
210  * }
211  * std::cout << std::endl;
212  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
213  * which will print "1 2 3 4 5 ".
214  * Here the wrapped collection object, returned by `wrapCollectionIntoView()`,
215  * is insubstantial. It can be saved with
216  *
217  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
218  * decltype(auto) view = lar::wrapCollectionIntoView(range);
219  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
220  * or even:
221  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
222  * auto const& view = lar::wrapCollectionIntoView(range);
223  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224  * but it will be just a (constant) reference to something else.
225  *
226  *
227  * Turn two iterators into a container
228  * ------------------------------------
229  *
230  * In this approach, we have two iterators to an existing collection, and we
231  * want to use them as extremes of a "virtual" collection.
232  * Again we use a STL vector as a base container for the example. Here we want
233  * to see a subrange of it as a new collection. We use `makeCollectionView()`.
234  *
235  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
236  * std::vector<int> v(10);
237  * std::iota(v.begin(), v.end(), 0); // { 0, 1, ..., 9 }
238  *
239  * for (int d: lar::makeCollectionView(v.begin() + 4, v.begin() + 7)) {
240  * std::cout << d << " ";
241  * }
242  * std::cout << std::endl;
243  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
244  * will print "0 1 2 3 4 5 6 7 8 9 ".
245  *
246  *
247  * Declaring a wrapper class
248  * --------------------------
249  *
250  * The function `lar::makeCollectionView()` creates a view owning the
251  * information the view requires. Similarly, a new class can be defined which
252  * does the same, by simply deriving it from `lar::CollectionView`:
253  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
254  * class IntVector {
255  * using vector_t = std::vector<int>;
256  *
257  * vector_t data;
258  *
259  * public:
260  * IntVector(vector_t&& data): data(std::move(data)) {}
261  *
262  * auto begin() const -> decltype(auto) { return data.cbegin(); }
263  * auto end() const -> decltype(auto) { return data.cend(); }
264  *
265  * }; // struct IntVector
266  *
267  * using IntViewBase_t = lar::CollectionView<IntVector>;
268  *
269  * struct MyCollection: public IntViewBase_t {
270  * MyCollection(std::vector<int>&& data) : IntViewBase_t(std::move(data)) {}
271  * }; // class MyCollection
272  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
273  * aftter which `MyCollection` interface can be enriched as needed.
274  * The `IntViewBase_t` alias is a way to overcome the fact that the name
275  * `IntVector` can't be used inside `MyCollection` because it's actually a
276  * private base class (the base class, even if not direct, will hide
277  * `IntVector`, even if, being private, it can't even be used). Another way
278  * is to fully qualify its name (e.g. `::IntVector`).
279  *
280  * Note that to avoid accidental copies, `lar::CollectionView` objects can't
281  * be directly instantiated: using directly `IntViewBase_t` will _not_ be
282  * allowed.
283  *
284  */
285  template <typename Range>
286  class CollectionView: private Range {
287  using this_t = CollectionView<Range>; ///< This type.
288  using range_t = Range; ///< Type of the range being wrapped.
289  using traits_t = details::RangeTraits<range_t>; ///< Range traits.
290 
291  /// Type of the begin iterator.
293  /// Type of the end iterator.
295 
296  /// Type of traits of iterator.
297  using iter_traits_t = std::iterator_traits<begin_iter_t>;
298 
299  public:
300  using collection_type = range_t; ///< Type of collection being wrapped.
301 
302  using value_type = typename iter_traits_t::value_type;
303  // reference not provided
304  using const_reference = typename iter_traits_t::reference;
305  // pointer not provided
306  using const_pointer = typename iter_traits_t::pointer;
307  // iterator not provided
309  // reverse_iterator not provided
310  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
311  using difference_type = typename iter_traits_t::difference_type;
312  using size_type = std::size_t;
313 
314  /// @{
315  /// @name Forward access.
316 
317  /// Returns whether the collection is empty.
318  bool empty() const noexcept { return cbegin() == cend(); }
319 
320  /// Returns the size of the collection.
321  size_type size() const noexcept { return std::distance(cbegin(), cend()); }
322 
323  /// Returns an iterator to the begin of the collection.
325  { using std::cbegin; return cbegin(asRange()); }
326 
327  /// Returns an iterator past the end of the collection.
328  end_iter_t cend() const noexcept
329  { using std::cend; return cend(asRange()); }
330 
331  /// Returns an iterator to the begin of the collection.
332  const_iterator begin() const noexcept { return cbegin(); }
333 
334  /// Returns an iterator past the end of the collection.
335  end_iter_t end() const noexcept { return cend(); }
336 
337  /// Returns the first element in the collection.
338  auto front() const -> decltype(auto) { return *cbegin(); }
339 
340  /// @}
341 
342  /// @{
343  /// @name Backward access.
344 
345  /// Returns a reverse iterator to the begin of the collection.
346  const_reverse_iterator rbegin() const noexcept { return crbegin(); }
347 
348  /// Returns a reverse iterator past the end of the collection.
349  const_reverse_iterator rend() const noexcept { return crend(); }
350 
351  /// Returns a reverse iterator to the begin of the collection.
353  { return const_reverse_iterator(cend()); }
354 
355  /// Returns a reverse iterator past the end of the collection.
357  { return const_reverse_iterator(cbegin()); }
358 
359  /// Returns the last element in the collection.
360  auto back() const -> decltype(auto) { return *crbegin(); }
361 
362  /// @}
363 
364  /// @{
365  /// @name Random access.
366 
367  /// Returns the content of the `i`-th element.
368  auto operator[] (size_type i) const -> decltype(auto)
369  { return cbegin()[i]; }
370 
371  /// Returns the content of the `i`-th element.
372  auto at(size_type i) const -> decltype(auto)
373  {
374  if (i >= size()) {
375  throw std::out_of_range(
376  "CollectionView index out of range: "
377  + std::to_string(i) + " >= " + std::to_string(size())
378  );
379  }
380  return operator[](i);
381  }
382 
383  /// @}
384 
385  /// @{
386  /// @name Contiguous access.
387 
388  const_pointer data() const { return &front(); }
389 
390  /// @}
391 
392  protected:
393  /*
394  /// @{
395  /// @name This object can't be directly constructed.
396  ///
397  /// A class deriving from this will be used directly as the base collection
398  /// to extract the iterators from.
399  CollectionViewWrapper() = default;
400  CollectionViewWrapper(this_t const&) = default;
401  CollectionViewWrapper(this_t&&) = default;
402  CollectionViewWrapper& operator=(this_t const&) = default;
403  CollectionViewWrapper& operator=(this_t&&) = default;
404  /// @}
405  */
406 
407  /// Constructor: steals the data from the specified range.
408  explicit CollectionView(range_t&& range): range_t(std::move(range)) {}
409 
410  // we license the creation of this class from ranges to a single function
411  // (and to derived classes)
412  friend this_t details::makeCollectionView<range_t>(range_t&&);
413 
414  private:
415  /// Returns this very object, cast back to `range_t`.
416  range_t const& asRange() const
417  { return static_cast<range_t const&>(*this); }
418 
419  }; // class CollectionView<>
420 
421 
422  //----------------------------------------------------------------------------
423  /// Returns the specified container, wrapped in the view.
424  template <typename Range>
426  { return reinterpret_cast<CollectionView<Range> const&>(c); }
427 
428 
429  //----------------------------------------------------------------------------
430  /// Creates a `CollectionView` from the specified iterators.
431  template <typename BeginIter, typename EndIter>
432  auto makeCollectionView(BeginIter const& b, EndIter const& e)
433  {
435  }
436 
437  /// Type of collection view owning the two range boundary iterators.
438  template <typename BeginIter, typename EndIter = BeginIter>
439  using RangeAsCollection_t = decltype
440  (makeCollectionView(std::declval<BeginIter>(), std::declval<EndIter>()));
441 
442 
443  //----------------------------------------------------------------------------
444  namespace details {
445  template <typename Range>
447  { return CollectionView<Range>(std::move(range)); }
448  } // namespace details
449  //----------------------------------------------------------------------------
450 
451 } // namespace lar
452 
453 
454 #endif // LARDATA_UTILITIES_COLLECTIONVIEW_H
455 
auto at(size_type i) const -> decltype(auto)
Returns the content of the i-th element.
Provides features of a collections, from begin and end iterators.
auto makeCollectionExtremes(BeginIter const &b, EndIter const &e)
Helper to create a CollectionExtremes object from two iterators.
decltype(auto) constexpr cend(T &&obj)
ADL-aware version of std::cend.
Definition: StdUtils.h:87
end_iter_t cend() const noexcept
Returns an iterator past the end of the collection.
const_reverse_iterator crbegin() const noexcept
Returns a reverse iterator to the begin of the collection.
std::decay_t< decltype(traits_t::getCBegin(std::declval< range_t >()))> begin_iterator_t
range_t collection_type
Type of collection being wrapped.
decltype(makeCollectionView(std::declval< BeginIter >(), std::declval< EndIter >())) RangeAsCollection_t
Type of collection view owning the two range boundary iterators.
const_iterator cbegin() const noexcept
Returns an iterator to the begin of the collection.
STL namespace.
const_iterator begin() const noexcept
Returns an iterator to the begin of the collection.
begin_iterator_t b
Stored copy of begin iterator.
typename iter_traits_t::difference_type difference_type
typename traits_t::end_iterator_t end_iter_t
Type of the end iterator.
CollectionExtremes(BeginIter b, EndIter e)
Constructor: copies the specified iterators.
end_iterator_t const & end() const
Returns the stored end iterator.
BoundaryListRangeBase< BoundaryIter > range_t
Type of the range being wrapped.
CollectionView(range_t &&range)
Constructor: steals the data from the specified range.
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
CollectionView< Range > makeCollectionView(Range &&)
const double e
Class storing a begin and a end iterator.
auto front() const -> decltype(auto)
Returns the first element in the collection.
def move(depos, offset)
Definition: depos.py:107
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
size_type size() const noexcept
Returns the size of the collection.
range_t const & asRange() const
Returns this very object, cast back to range_t.
std::reverse_iterator< const_iterator > const_reverse_iterator
typename traits_t::begin_iterator_t begin_iter_t
Type of the begin iterator.
LArSoft-specific namespace.
begin_iterator_t const & begin() const
Returns the stored begin iterator.
std::iterator_traits< begin_iter_t > iter_traits_t
Type of traits of iterator.
auto back() const -> decltype(auto)
Returns the last element in the collection.
const_reverse_iterator rbegin() const noexcept
Returns a reverse iterator to the begin of the collection.
static auto getCEnd(range_t const &range)
decltype(auto) constexpr cbegin(T &&obj)
ADL-aware version of std::cbegin.
Definition: StdUtils.h:82
static bool * b
Definition: config.cpp:1043
const_reverse_iterator crend() const noexcept
Returns a reverse iterator past the end of the collection.
end_iter_t end() const noexcept
Returns an iterator past the end of the collection.
const_reverse_iterator rend() const noexcept
Returns a reverse iterator past the end of the collection.
std::string to_string(ModuleType const mt)
Definition: ModuleType.h:34
end_iterator_t e
Stored copy of end iterator.
CollectionView< Range > const & wrapCollectionIntoView(Range const &c)
Returns the specified container, wrapped in the view.
std::decay_t< decltype(traits_t::getCEnd(std::declval< range_t >()))> end_iterator_t
const_pointer data() const
static auto getCBegin(range_t const &range)
bool empty() const noexcept
Returns whether the collection is empty.