CollectionProxyElement.h
Go to the documentation of this file.
1 /**
2  * @file lardata/RecoBaseProxy/ProxyBase/CollectionProxyElement.h
3  * @brief Utilities for a single element of a collection proxy.
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_COLLECTIONPROXYELEMENT_H
12 #define LARDATA_RECOBASEPROXY_PROXYBASE_COLLECTIONPROXYELEMENT_H
13 
14 // LArSoft libraries
15 #include "lardata/Utilities/TupleLookupByTag.h" // util::index_of_tag_v, ...
16 #include "larcorealg/CoreUtils/MetaUtils.h" // util::always_true_type, ...
17 #include "larcorealg/CoreUtils/DebugUtils.h" // lar::debug::demangle()
18 
19 // C/C++ standard
20 #include <tuple> // also std::tuple_element_t<>, std::get()
21 #include <utility> // std::move()
22 #include <stdexcept> // std::logic_error
23 #include <type_traits> // std::integral_constant<>
24 #include <cstdlib> // std::size_t
25 
26 
27 
28 namespace proxy {
29 
30 
31  namespace details {
32 
33  template <typename AuxCollTuple>
35 
36  } // namespace details
37 
38 
39  //--- BEGIN Proxy element infrastructure -------------------------------------
40  /**
41  * @defgroup LArSoftProxiesElement Proxy element infrastructure
42  * @ingroup LArSoftProxyCustom
43  * @brief Infrastructure to describe the element of a proxy
44  *
45  * A collection proxy element is a complex entity in that it has to figure out
46  * all the data it can return, and their type.
47  *
48  * The default type of a collection proxy element is
49  * `proxy::CollectionProxyElement`.
50  *
51  * @{
52  */
53 
54  //----------------------------------------------------------------------------
55  /**
56  * @brief An element of a collection proxy.
57  * @tparam CollProxy the type of collection proxy this is the element of
58  *
59  * The collection proxy element represents, unsurprisingly, a single element
60  * of the collection proxy.
61  * It exposes all the connections between the components merged into the
62  * collection proxy, for a specific element.
63  *
64  * The interface of a proxy element allows it to access the main and auxiliary
65  * data as follows:
66  * * main data element (always present!):
67  * * the whole object, by dereference method (`*track`)
68  * * methods and fields, by member access (`track->Length()`)
69  * * auxiliary data: accessed by tag (usually it's the default, that is the
70  * same as the data type):
71  * * check if available: `has()` method, static
72  * (`track.has<recob::Hit>()`)
73  * * get the data:
74  * * `get()` method; the data *must* be available or this
75  * call will not compile (`track.get<recob::Hit>()`)
76  * * `getIf()` method; if the data is not available, an exception is
77  * thrown
78  * * _index_ of the element in the collection (a bonus!): `index()` method
79  *
80  * The for loop block illustrates how to use some of them:
81  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
82  * auto tracks = proxy::getCollection<std::vector<recob::Track>>
83  * (event, tracksTag, proxy::withAssociated<recob::Hit>());
84  *
85  * for (auto track: tracks) {
86  *
87  * recob::Track const& trackObj = *track; // access to the track
88  *
89  * double const length = track->Length(); // access to track members
90  *
91  * // access to associated data
92  * // (returns random-access collection-like object)
93  * decltype(auto) hits = track.get<recob::Hit>();
94  * double charge = 0.0;
95  * for (auto const& hitPtr: hits) charge += hitPtr->Integral();
96  *
97  * std::vector<recob::TrackFitHitInfo> const* fitInfo
98  * = track.has<std::vector<recob::TrackFitHitInfo>>()
99  * ? &(track.getIf<std::vector<recob::TrackFitHitInfo>>())
100  * : nullptr
101  * ;
102  *
103  * mf::LogVerbatim("Info")
104  * << "[#" << trackInfo.index() << "] track ID=" << trackObj.ID()
105  * << " (" << length << " cm) deposited charge=" << charge
106  * << " with " << hits.size() << " hits and"
107  * << (fitInfo? "": " no") << " fit information";
108  *
109  * } // for tracks
110  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
111  * Please note that `getIf()` method is _not easy to use correctly_.
112  * In this example, we rely on the knowledge that `track.getIf()` would return
113  * a reference to a (non-temporary) object, and we take the address of that
114  * object: this is not necessarily the case (e.g., it is not the case for
115  * `getIf<recob::Hit>()`).
116  *
117  *
118  * Customization
119  * ==============
120  *
121  * A proxy element class can (and should) be derived from
122  * `proxy::CollectionProxyElement`.
123  * On top of it, the derived class can extend or completely rewrite the
124  * interface:
125  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
126  * template <typename CollProxy>
127  * class TrackProxy: public proxy::CollectionProxyElement<CollProxy> {
128  * using base_t = proxy::CollectionProxyElement<CollProxy>;
129  * public:
130  * unsigned int nHits() const
131  * { return base_t::template get<recob::Hit>().size(); }
132  * }; // class TrackProxy
133  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
134  * Specialization is also possible, but discouraged.
135  *
136  * A `proxy::CollectionProxyElement` has knowledge of the type of collection
137  * proxy it's an element of, but it has no connection to a collection proxy
138  * object and in fact it never interacts with any, not even during
139  * construction.
140  *
141  * Once the element is customized, a `proxy::CollectionProxy` can use it by
142  * having it as first template argument:
143  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
144  * template <typename MainColl, typename... AuxColl>
145  * using Proxy_t
146  * = proxy::CollectionProxyBase<TrackProxy, MainColl, AuxColl...>;
147  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148  * A `CollectionProxyMaker` specialization will have to `make()` such objects.
149  *
150  *
151  * @note There is an indirect dependency of this class on the whole
152  * collection proxy class it is an element of. The type of that
153  * collection proxy is the template argument `CollProxy`.
154  * This class uses `CollProxy` to discovers some types it needs, but it
155  * does not contain or link to `CollProxy` itself.
156  * It should be possible therefore to have `CollProxy` types and
157  * `CollectionProxyElement` depend on a third, "trait" type delivering
158  * the relevant data types to both objects.
159  *
160  */
161  template <typename CollProxy>
163 
164  public:
165  using collection_proxy_t = CollProxy;
166  using main_element_t = typename collection_proxy_t::main_element_t;
167 
168  /// Tuple of elements (expected to be tagged types).
170  <typename collection_proxy_t::aux_collections_t>::type;
171 
172  /// Constructor: sets the element index, the main element and steals
173  /// auxiliary data.
175  (std::size_t index, main_element_t const& main, aux_elements_t&& auxData)
176  : fIndex(index), fMain(&main) , fAuxData(std::move(auxData))
177  {}
178 
179  /// Returns a pointer to the main element.
180  main_element_t const* operator->() const { return fMain; }
181 
182  /// Returns a reference to the main element.
183  main_element_t const& operator*() const { return *fMain; }
184 
185  /// Returns the index of this element in the collection.
186  std::size_t index() const { return fIndex; };
187 
188  /// Returns the auxiliary data specified by type (`Tag`).
189  template <typename Tag>
190  auto get() const -> decltype(auto)
191  { return std::get<util::index_of_tag_v<Tag, aux_elements_t>>(fAuxData); }
192 
193 
194  /**
195  * @brief Returns the auxiliary data specified by type (`Tag`).
196  * @tparam Tag tag of the data to fetch (usually, its type)
197  * @tparam T type to return (by default, a constant reference to `Tag`)
198  * @return the auxiliary data specified by type (`Tag`).
199  * @throw std::logic_error if the tag is not available.
200  * @see get(), has()
201  *
202  * @deprecated C++17 `if constexpr` should be used instead
203  * (see the example below)
204  *
205  * This method is a `get()` which forgives when the requested type is not
206  * available (because this proxy was configured not to hold it).
207  *
208  * The difference with `get()` is the following:
209  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
210  * decltype(auto) elem = tracks[0];
211  * if (elem.has<recob::Hit>()) {
212  * auto hits = elem.get<recob::Hit>();
213  * // ...
214  * }
215  * if (elem.has<recob::SpacePoint>()) {
216  * auto spacepoints = elem.getIf<recob::SpacePoint>();
217  * // ...
218  * }
219  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
220  * If the proxy `tracks` has _not_ been coded with `recob::Hit` data,
221  * the code snippet will _not_ compile, because `get()` will not compile
222  * for tags that were not coded in. On the other end, if `recob::Hit`
223  * is coded in `tracks` but `recob::SpacePoint` is not, the snippet _will_
224  * compile. In that case, if the `has()` check had been omitted, `getIt()`
225  * would throw a `std::logic_error` exception when executed.
226  * With C++17, this will not be necessary any more by using "constexpr if":
227  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
228  * decltype(auto) elem = tracks[0];
229  * if constexpr (elem.has<recob::Hit>()) {
230  * auto hits = elem.get<recob::Hit>();
231  * // ...
232  * }
233  * if constexpr (elem.has<recob::SpacePoint>()) {
234  * auto spacepoints = elem.get<recob::SpacePoint>();
235  * // ...
236  * }
237  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238  *
239  * @note The second template argument contains the *exact* type returned by
240  * the function. That information is needed because this method needs
241  * to return the same type (or compatible types) whether the required
242  * tag is present or not, in order for user code like
243  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
244  * if (elem.has<recob::SpacePoint>()) {
245  * recob::SpacePoint const* spacepoints
246  * = elem.getIf<recob::SpacePoint>(); // won't do
247  * // ...
248  * }
249  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
250  * to work; it becomes:
251  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
252  * if (elem.has<recob::SpacePoint>()) {
253  * recob::SpacePoint const* spacepoints
254  * = elem.getIf<recob::SpacePoint, recob::SpacePoint const*>();
255  * // ...
256  * }
257  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
258  * This is necessary because when the tag (in the example,
259  * `recob::SpacePoint`) is not registered in the proxy, `getIf()` has
260  * no clue of what the return value should be ("which is the type of
261  * the data that does not exist?").
262  *
263  */
264  template <typename Tag, typename T = Tag const&>
265  [[deprecated("Use C++17 constexpr if instead and get() instead")]]
266  auto getIf() const -> decltype(auto);
267 
268 
269  /// Returns whether this class knowns about the specified type (`Tag`).
270  template <typename Tag>
271  static constexpr bool has() { return util::has_tag_v<Tag, aux_elements_t>; }
272 
273  private:
274 
275  std::size_t fIndex; ///< Index of this element in the proxy.
276  main_element_t const* fMain; ///< Pointer to the main object of the element.
277 
278  // note that the auxiliary data is not tagged, we need to learn which
279  // the tags are from the collection.
280  aux_elements_t fAuxData; ///< Data associated to the main object.
281 
282  template <typename Tag, typename>
283  auto getIfHas(std::bool_constant<true>) const -> decltype(auto);
284 
285  template <typename Tag, typename T>
286  [[noreturn]] auto getIfHas(std::bool_constant<false>) const -> T;
287 
288  }; // CollectionProxyElement<>
289 
290 
291  /// @}
292  //--- END Proxy element infrastructure ---------------------------------------
293 
294 
295  //----------------------------------------------------------------------------
296  namespace details {
297 
298  //--------------------------------------------------------------------------
299  //--- Stuff for the whole collection proxy
300  //--------------------------------------------------------------------------
301  /**
302  * @brief Creates a collection proxy element object from data structures.
303  * @tparam ProxyElement type of proxy element to be created
304  * @tparam AuxData types of auxiliary data structures being included
305  * @param index index in main collection of the element being represented
306  * @param main main collection proxy data
307  * @param auxData auxiliary data collections
308  * @return a `ProxyElement` object bound to the specified data element
309  */
310  template <typename ProxyElement, typename... AuxData>
312  std::size_t index,
313  typename ProxyElement::main_element_t const& main,
314  AuxData&&... auxData
315  ) {
316  return ProxyElement(
317  index, main,
318  typename ProxyElement::aux_elements_t(std::forward<AuxData>(auxData)...)
319  );
320  } // makeCollectionProxyElement()
321 
322 
323  //--------------------------------------------------------------------------
324 
325  } // namespace details
326 
327 } // namespace proxy
328 
329 
330 //------------------------------------------------------------------------------
331 //--- template implementation
332 //------------------------------------------------------------------------------
333 namespace proxy {
334 
335  namespace details {
336 
337  //--------------------------------------------------------------------------
338  //--- stuff for auxiliary data
339  //--------------------------------------------------------------------------
340  // Trait replacing each element of the specified tuple with its
341  // `auxiliary_data_t`
342  template <typename Tuple>
343  struct SubstituteWithAuxList {
344  static_assert
345  (util::always_true_type<Tuple>(), "Template argument must be a tuple");
346  }; // SubstituteWithAuxList<>
347 
348  template <typename... T>
349  struct SubstituteWithAuxList<std::tuple<T...>> {
350  using type = std::tuple<typename T::auxiliary_data_t...>;
351  }; // SubstituteWithAuxList<tuple>
352 
353  //--------------------------------------------------------------------------
354 
355  } // namespace details
356 
357 
358  //----------------------------------------------------------------------------
359  //--- CollectionProxyElement
360  //----------------------------------------------------------------------------
361  template <typename CollProxy>
362  template <typename Tag, typename T>
364  { return getIfHas<Tag, T>(std::bool_constant<has<Tag>()>{}); }
365 
366 
367  //----------------------------------------------------------------------------
368  template <typename CollProxy>
369  template <typename Tag, typename>
371  (std::bool_constant<true>) const -> decltype(auto)
372  { return get<Tag>(); }
373 
374  template <typename CollProxy>
375  template <typename Tag, typename T>
377  (std::bool_constant<false>) const -> T
378  {
379  throw std::logic_error
380  ("Tag '" + lar::debug::demangle<Tag>() + "' not available.");
381  }
382 
383 
384  //----------------------------------------------------------------------------
385 
386 } // namespace proxy
387 
388 
389 #endif // LARDATA_RECOBASEPROXY_PROXYBASE_COLLECTIONPROXYELEMENT_H
main_element_t const * operator->() const
Returns a pointer to the main element.
auto makeCollectionProxyElement(std::size_t index, typename ProxyElement::main_element_t const &main, AuxData &&...auxData)
Creates a collection proxy element object from data structures.
Basic C++ metaprogramming utilities.
Definition: tag.cpp:4
typename collection_proxy_t::main_element_t main_element_t
STL namespace.
Functions to help debugging by instrumenting code.
std::integral_constant< bool, Value > bool_constant
Definition: ProviderPack.h:314
auto getIfHas(std::bool_constant< true >) const -> decltype(auto)
aux_elements_t fAuxData
Data associated to the main object.
def move(depos, offset)
Definition: depos.py:107
static constexpr bool has()
Returns whether this class knowns about the specified type (Tag).
main_element_t const * fMain
Pointer to the main object of the element.
std::size_t index() const
Returns the index of this element in the collection.
std::size_t fIndex
Index of this element in the proxy.
A std::true_type with a template argument.
Definition: MetaUtils.h:145
An element of a collection proxy.
typename details::SubstituteWithAuxList< typename collection_proxy_t::aux_collections_t >::type aux_elements_t
Tuple of elements (expected to be tagged types).
Utilities to address elements of a tuple-like class by tag.
static QCString type
Definition: declinfo.cpp:672
auto getIf() const -> decltype(auto)
Returns the auxiliary data specified by type (Tag).
main_element_t const & operator*() const
Returns a reference to the main element.