CollectionProxy.h
Go to the documentation of this file.
1 /**
2  * @file lardata/RecoBaseProxy/ProxyBase/CollectionProxy.h
3  * @brief Utilities for the collection proxy object.
4  * @author Gianluca Petrillo (petrillo@fnal.gov)
5  * @date July 27, 2017
6  * @see lardata/RecoBaseProxy/ProxyBase.h
7  *
8  * This library is header-only.
9  */
10 
11 #ifndef LARDATA_RECOBASEPROXY_PROXYBASE_COLLECTIONPROXY_H
12 #define LARDATA_RECOBASEPROXY_PROXYBASE_COLLECTIONPROXY_H
13 
14 // LArSoft libraries
17 #include "lardata/Utilities/TupleLookupByTag.h" // util::type_with_tag_t, ...
18 #include "larcorealg/CoreUtils/ContainerMeta.h" // util::collection_value_t, ...
19 
20 // C/C++ standard
21 #include <vector>
22 #include <tuple>
23 #include <utility> // std::move()
24 #include <limits> // std::numeric_limits<>
25 #include <cstdlib> // std::size_t
26 
27 
28 namespace proxy {
29 
30  namespace details {
31 
32  template <typename Cont>
34 
35  template <template <typename, typename...> class F, typename...>
37 
38  } // namespace details
39 
40 
41  // --- BEGIN Collection proxy infrastructure ---------------------------------
42  /**
43  * @defgroup LArSoftProxyCollections Collection proxy infrastructure
44  * @ingroup LArSoftProxyCustom
45  * @brief Infrastructure to define a proxy of collection data product.
46  *
47  * A data collection proxy connects a main collection data product with other
48  * data products whose elements have relation with one of the main product
49  * elements.
50  *
51  * A collection proxy is created via a call to `getCollection()`, in a fashion
52  * non dissimilar from calling, e.g., `art::Event::getValidHandle()`.
53  *
54  * A proxy is characterized by a proxy tag, which is the type used in the
55  * `getCollection()` call, but that is not necessarily in direct relation with
56  * the type of the collection proxy itself. In other words, calling
57  * `proxy::getCollection<proxy::Tracks>(event, trackTag)` will return a proxy
58  * object whose type is likely not `proxy::Tracks`.
59  *
60  * Proxy collections are created by _merging_ auxiliary data into a main
61  * collection data product. See `getCollection()` for more details.
62  *
63  * Proxies can be customized, meaning that collection proxies may be written
64  * to have a specific interface. There are different levels of customization,
65  * of the collection proxy itself, of its element type, and of the auxiliary
66  * data merged. Customization may require quite a bit of coding, and the
67  * easiest approach is to start from an already customized proxy implementing
68  * features similar to the desired ones.
69  *
70  * It's worth stressing @ref LArSoftProxyQuirks "again" that collection
71  * proxies composed by merging different auxiliary data have different C++
72  * types, and that if such proxies need to be propagated as function arguments
73  * those arguments need to be of template type.
74  *
75  * @{
76  */
77 
78 
79  //----------------------------------------------------------------------------
80  /**
81  * @brief Base representation of a collection of proxied objects.
82  * @tparam Element type of element of the collection proxy
83  * @tparam MainColl type of the collection of the main data product
84  * @tparam AuxColls type of all included auxiliary data proxies
85  * @see proxy::CollectionProxyElement
86  *
87  * This object exposes a collection interface.
88  * The collection proxy is driven by a data product containing the main
89  * objects. The size of the collection proxy is the same as the one of this
90  * main data product, and all associated data is referring to its elements.
91  *
92  * Thus, the elements of this collection proxy are objects that collect the
93  * information of a single element in the main data product and all the data
94  * associated with it.
95  *
96  * The `AuxColls` types are tagged types: all must define a `tag` type.
97  * Their data is accessed specifying that tag, i.e. via `get<Tag>()`.
98  * Therefore, tags must be unique.
99  *
100  * The type `Element` is expected to expose the same interface of
101  * `CollectionProxyElement`, from which it can derive. It is a template
102  * that needs to take as only argument the type of collection proxy it is the
103  * element of. This is a way to customize the interface of access to single
104  * element of proxy.
105  *
106  * @note This class depends on an `Element` type, which indirectly depends on
107  * this class for discovering some relevant data types. This is a
108  * circular dependency that might be solved by introducing a third class
109  * with the definition of the types that both classes need.
110  *
111  */
112  template <
113  template <typename CollProxy> class Element,
114  typename MainColl,
115  typename... AuxColls
116  >
118  : public details::MainCollectionProxy<MainColl>, public AuxColls...
119  {
120  /// This type.
121  using collection_proxy_t
122  = CollectionProxyBase<Element, MainColl, AuxColls...>;
123 
124  /// Type of wrapper used for the main data product.
126 
127  public:
128  /// Type of element of the main data product.
130 
131  /// Type of collection in the main data product.
133 
134  /// Type of element of this collection proxy.
135  using element_proxy_t = Element<collection_proxy_t>;
136 
137  /// Tuple of all auxiliary data collections (wrappers).
138  using aux_collections_t = std::tuple<AuxColls...>;
139 
140  /// Type of element of this collection proxy.
142 
143  /// Type of iterator to this collection (constant).
145 
146  /// Type of iterator to this collection (still constant).
148 
149  /**
150  * @brief Constructor: uses the specified data.
151  * @param main the original main data product collection
152  * @param aux all auxiliary data collections and structures
153  *
154  * The auxiliary data structures are stolen (moved) from the arguments.
155  * They are expected to be wrappers around the original associated data,
156  * not owning the auxiliary data itself.
157  */
158  CollectionProxyBase(main_collection_t const& main, AuxColls&&... aux)
159  : main_collection_proxy_t(main), AuxColls(std::move(aux))...
160  {}
161 
162  /**
163  * @brief Returns the element of this collection with the specified index.
164  * @param i the index in the collection
165  * @return a value representing an element of the collection
166  *
167  * The returned value is an object created on the spot, not a reference to
168  * an existing structure.
169  * The structure exposes the `i`-th element in the main collection, plus all
170  * objects that are associated to it.
171  */
172  element_proxy_t const operator[] (std::size_t i) const
173  {
174  return details::makeCollectionProxyElement<element_proxy_t>
175  (i, getMainAt(i), aux<AuxColls>().operator[](i)...);
176  }
177 
178  /// Returns an iterator to the first element of the collection.
179  const_iterator begin() const { return makeIterator(0U); }
180 
181  /// Returns an iterator past the last element of the collection.
182  const_iterator end() const { return makeIterator(size()); }
183 
184  /// Returns whether this collection is empty.
185  bool empty() const { return main().empty(); }
186 
187  /// Returns the size of this collection.
188  std::size_t size() const { return main().size(); }
189 
190 
191  /// Returns the associated data proxy specified by `AuxTag`.
192  template <typename AuxTag>
193  auto get() const -> decltype(auto) { return auxByTag<AuxTag>(); }
194 
195 
196  /**
197  * @brief Returns the auxiliary data specified by type (`Tag`).
198  * @tparam Tag tag of the data to fetch (usually, its type)
199  * @tparam T exact type returned by the method (by default a vector of tags)
200  * @return the auxiliary data specified by type (`Tag`).
201  * @throw std::logic_error if the tag is not available.
202  * @see get(), has()
203  *
204  * @deprecated C++17 `if constexpr` should be used instead
205  * (see the example below)
206  *
207  * This method is a `get()` which forgives when the requested type is not
208  * available (because this proxy was configured not to hold it).
209  *
210  * The difference with `get()` is the following:
211  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
212  * if (tracks.has<recob::Hit>()) {
213  * auto hits = tracks.get<recob::Hit>();
214  * // ...
215  * }
216  * if (tracks.has<recob::SpacePoint>()) {
217  * auto spacepoints = tracks.getIf<recob::SpacePoint>();
218  * // ...
219  * }
220  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
221  * If the proxy `tracks` has _not_ been coded with `recob::Hit` data,
222  * the code snippet will _not_ compile, because `get()` will not compile
223  * for tags that were not coded in. On the other end, if `recob::Hit`
224  * is coded in `tracks` but `recob::SpacePoint` is not, the snippet _will_
225  * compile. In that case, if the `has()` check had been omitted, `getIt()`
226  * would throw a `std::logic_error` exception when executed.
227  * With C++17, this will not be necessary any more by using "constexpr if":
228  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
229  * if constexpr (tracks.has<recob::Hit>()) {
230  * auto hits = tracks.get<recob::Hit>();
231  * // ...
232  * }
233  * if constexpr (tracks.has<recob::SpacePoint>()) {
234  * auto spacepoints = tracks.get<recob::SpacePoint>();
235  * // ...
236  * }
237  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238  *
239  * @note If the wrapped data product is something different than a vector
240  * of space points (which in the example is likely, if space points
241  * are associated to tracks), the almost-correct type of return value
242  * needs to be specified as second template parameter `T`.
243  *
244  * @warning This functionality is not trivial to use!
245  * It's mostly meant for implementation of higher level wrappers.
246  *
247  * @deprecated Use C++17 constexpr if instead.
248  */
249  template <typename Tag, typename T = std::vector<Tag> const&>
250  [[deprecated("Use C++17 constexpr if instead and get() instead")]]
251  auto getIf() const -> decltype(auto);
252 
253 
254  /// Returns whether this class knowns about the specified type (`Tag`).
255  template <typename Tag>
256  static constexpr bool has()
257  { return util::has_tag_v<Tag, aux_collections_t>; }
258 
259 
260  protected:
262  using main_collection_proxy_t::mainProxy;
263  using main_collection_proxy_t::getMainAt;
264 
265  /// Returns the auxiliary data specified by type.
266  template <typename AuxColl>
267  AuxColl const& aux() const { return static_cast<AuxColl const&>(*this); }
268 
269  /// Returns the auxiliary data specified by type.
270  template <typename AuxTag>
271  auto auxByTag() const -> decltype(auto)
272  { return aux<util::type_with_tag_t<AuxTag, aux_collections_t>>(); }
273 
274 
275  template <typename Tag, typename>
276  auto getIfHas(std::bool_constant<true>) const -> decltype(auto);
277  template <typename Tag, typename T>
278  [[noreturn]] auto getIfHas(std::bool_constant<false>) const -> T;
279 
280  /// Returns an iterator pointing to the specified index of this collection.
281  const_iterator makeIterator(std::size_t i) const { return { *this, i }; }
282 
283 
285  "Some auxiliary data collections share the same tag. They should not.");
286 
287  }; // struct CollectionProxyBase
288 
289 
290  //----------------------------------------------------------------------------
291  /**
292  * @brief Base representation of a collection of proxied objects.
293  * @tparam MainColl type of the collection of the main data product
294  * @tparam AuxColls type of all included auxiliary data proxies
295  * @see proxy::CollectionProxyElement
296  *
297  * This object is a "specialization" of `proxy::CollectionProxyBase` using
298  * `proxy::CollectionProxyElement` as element type.
299  */
300  template <typename MainColl, typename... AuxColls>
301  using CollectionProxy
302  = CollectionProxyBase<CollectionProxyElement, MainColl, AuxColls...>;
303 
304 
305  // this joke is necessary because expanding directly CollectionProxy<Args...>
306  // into CollectionProxy<Main, Aux...> template arguments does not work
307  template <typename... Args>
310 
311 
312  /// @}
313  // --- END Collection proxy infrastructure -----------------------------------
314 
315  //----------------------------------------------------------------------------
316  namespace details {
317 
318  //--------------------------------------------------------------------------
319  /// Creates a collection proxy of a specified type with the given arguments.
320  template <
321  template <typename...> class CollProxy,
322  typename MainColl, typename... AuxColl
323  >
324  auto createCollectionProxy(MainColl const& main, AuxColl&&... aux)
325  {
326  return CollProxy<MainColl, AuxColl...>
327  (main, std::forward<AuxColl>(aux)...);
328  }
329 
330  //--------------------------------------------------------------------------
331  /// Creates a `CollectionProxy` object with the given arguments.
332  template <typename MainColl, typename... AuxColl>
333  auto makeCollectionProxy(MainColl const& main, AuxColl&&... aux)
334  {
335  return createCollectionProxy<CollectionProxy>
336  (main, std::forward<AuxColl>(aux)...);
337  }
338 
339  //--------------------------------------------------------------------------
340  /**
341  * @brief Iterator to random access collection storing a current index.
342  * @tparam Cont type of random-access container to iterate
343  *
344  * `Cont` is a type providing a public `operator[](std::size_t)` method.
345  */
346  template <typename Cont>
347  class IndexBasedIterator {
348 
349  public:
350  using container_t = Cont;
351 
354 
355  /// Default constructor (required by iterator protocol): an unusable iterator.
356  IndexBasedIterator() = default;
357 
358  /// Constructor: initializes from an iterator of the proxy main collection.
359  IndexBasedIterator(container_t const& cont, std::size_t index = 0)
360  : fCont(&cont), fIndex(index) {}
361 
362  /// Returns the value pointed by this iterator.
363  auto operator* () const -> decltype(auto)
364  { return fCont->operator[](fIndex); }
365 
366  /// Returns the value pointed by this iterator.
367  const_iterator& operator++ () { ++fIndex; return *this; }
368 
369  /// Returns whether the iterators point to the same element.
370  bool operator!= (const_iterator const& other) const
371  { return (other.fIndex != fIndex) || (other.fCont != fCont); }
372 
373  protected:
374  container_t const* fCont = nullptr; ///< Pointer to the original container.
375 
376  /// Current index in the main collection.
378 
379  }; // IndexBasedIterator<>
380 
381  } // namespace details
382 
383 } // namespace proxy
384 
385 
386 //------------------------------------------------------------------------------
387 //--- template implementation
388 //------------------------------------------------------------------------------
389 namespace proxy {
390 
391  namespace details {
392 
393  //--------------------------------------------------------------------------
394  template <
395  template <typename, typename...> class F,
396  typename First, typename... Others
397  >
398  struct TemplateAdaptorOnePlus<F, First, Others...>
399  { using type = F<First, Others...>; };
400 
401  } // namespace details
402 
403 
404  //----------------------------------------------------------------------------
405  //--- CollectionProxyBase
406  //----------------------------------------------------------------------------
407  template <
408  template <typename CollProxy> class Element,
409  typename MainColl,
410  typename... AuxColls
411  >
412  template <typename Tag, typename T>
414  -> decltype(auto)
415  { return getIfHas<Tag, T>(std::bool_constant<has<Tag>()>{}); }
416 
417 
418  template <
419  template <typename CollProxy> class Element,
420  typename MainColl,
421  typename... AuxColls
422  >
423  template <typename Tag, typename>
425  (std::bool_constant<true>) const -> decltype(auto)
426  { return get<Tag>(); }
427 
428  template <
429  template <typename CollProxy> class Element,
430  typename MainColl,
431  typename... AuxColls
432  >
433  template <typename Tag, typename T>
435  (std::bool_constant<false>) const -> T
436  {
437  throw std::logic_error
438  ("Tag '" + lar::debug::demangle<Tag>() + "' not available.");
439  }
440 
441 
442  //----------------------------------------------------------------------------
443 
444 } // namespace proxy
445 
446 
447 #endif // LARDATA_RECOBASEPROXY_PROXYBASE_COLLECTIONPROXY_H
const_iterator begin() const
Returns an iterator to the first element of the collection.
std::tuple< AuxColls... > aux_collections_t
Tuple of all auxiliary data collections (wrappers).
util::collection_value_t< MainColl > main_element_t
Type of the elements in the original collection.
std::size_t fIndex
Current index in the main collection.
CollectionProxyBase< CollectionProxyElement, MainColl, AuxColls... > CollectionProxy
Base representation of a collection of proxied objects.
typename details::TemplateAdaptorOnePlus< CollectionProxy, Args... >::type CollectionProxyFromArgs
CollectionProxyBase(main_collection_t const &main, AuxColls &&...aux)
Constructor: uses the specified data.
STL namespace.
intermediate_table::const_iterator const_iterator
auto auxByTag() const -> decltype(auto)
Returns the auxiliary data specified by type.
Iterator to random access collection storing a current index.
Base representation of a collection of proxied objects.
Wrapper for the main collection of a proxy.
auto makeCollectionProxy(MainColl const &main, AuxColl &&...aux)
Creates a CollectionProxy object with the given arguments.
auto createCollectionProxy(MainColl const &main, AuxColl &&...aux)
Creates a collection proxy of a specified type with the given arguments.
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
const_iterator makeIterator(std::size_t i) const
Returns an iterator pointing to the specified index of this collection.
Proxy class for charged space point proxy elements.
bool empty() const
Returns whether this collection is empty.
Utilities for the main collection of a collection proxy.
Utilities for a single element of a collection proxy.
def move(depos, offset)
Definition: depos.py:107
static int max(int a, int b)
MainColl main_collection_t
Type of the original collection.
bool operator!=(AssnsNode< ArtAssnsIterValue > const &A, typename AssnsNode< ArtAssnsIterValue >::valueptr_t const &B)
const_iterator end() const
Returns an iterator past the last element of the collection.
Traits holding whether elements of Tuple have duplicate types.
container_t const * fCont
Pointer to the original container.
IndexBasedIterator(container_t const &cont, std::size_t index=0)
Constructor: initializes from an iterator of the proxy main collection.
std::size_t size() const
Returns the size of this collection.
static constexpr bool has()
Returns whether this class knowns about the specified type (Tag).
An element of a collection proxy.
auto getIfHas(std::bool_constant< true >) const -> decltype(auto)
Utilities to address elements of a tuple-like class by tag.
util::collection_value_t< container_t > value_type
static QCString type
Definition: declinfo.cpp:672
AuxColl const & aux() const
Returns the auxiliary data specified by type.
typename collection_value_type< Coll >::type collection_value_t
Type contained in the collection Coll.
Definition: ContainerMeta.h:65
C++ metaprogramming utilities for dealing with containers.
QuadExpr operator*(double v, const QuadExpr &e)
Definition: QuadExpr.h:39
auto getIf() const -> decltype(auto)
Returns the auxiliary data specified by type (Tag).