LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
IPRHelper.h
Go to the documentation of this file.
1 #ifndef canvas_Persistency_Common_detail_IPRHelper_h
2 #define canvas_Persistency_Common_detail_IPRHelper_h
3 
4 // Helper class and associated gubbins for populating the FindOne and
5 // FindMany query objects for inter-product references.
6 
13 
14 #include <type_traits>
15 #include <unordered_map>
16 
17 namespace art::detail {
18 
19  template <typename ProdA, typename ProdB, typename Data>
20  struct safe_input_tag {
21  safe_input_tag(InputTag const& input_tag) : tag{input_tag} {}
23  : tag{token.inputTag_}
24  {}
26  };
27 
28  template <typename ProdA, typename ProdB, typename Data, typename Tag>
29  InputTag
30  input_tag(Tag const& tag)
31  {
32  static_assert(
33  std::is_convertible_v<Tag, InputTag> ||
34  std::is_same_v<Tag, ProductToken<Assns<ProdA, ProdB>>> ||
35  std::is_same_v<Tag, ProductToken<Assns<ProdA, ProdB, Data>>>,
36  "\n\nart error: The input tag or product token provided to the "
37  "smart-query object\n"
38  " constructor has a type that conflicts with that of the "
39  "smart-query object.\n");
41  }
42 
43  class IPRHelperDef {};
44 
45  template <typename ProdA,
46  typename ProdB,
47  typename Data,
48  typename DATACOLL,
49  typename EVENT>
50  class IPRHelper;
51 
52  template <typename DATA>
54  public:
55  void init(size_t size, std::vector<DATA const*>& data) const;
56  template <typename ASSNS>
57  void fill(ptrdiff_t assns_index,
58  ASSNS const& assns,
59  size_t data_index,
60  std::vector<DATA const*>& data) const;
61 
62  void init(size_t size, std::vector<std::vector<DATA const*>>& data) const;
63  template <typename ASSNS>
64  void fill(ptrdiff_t assns_index,
65  ASSNS const& assns,
66  size_t data_index,
67  std::vector<std::vector<DATA const*>>& data) const;
68 
69  void init(size_t, IPRHelperDef&) const;
70  template <typename ASSNS>
71  void fill(ptrdiff_t, ASSNS const&, size_t, IPRHelperDef&) const;
72  };
73 
74  // Note that the template parameter Bcoll is determined by the
75  // IPRHelper's use by the FindOne and FindMany classes, and is not
76  // as free-ranging as one might naively imagine.
77  template <typename ProdB>
78  class BcollHelper {
79  public:
80  BcollHelper(InputTag const& assnsTag);
81  template <typename Bcoll>
82  void init(size_t size, Bcoll& bColl);
83 
84  // 1. When Bcoll is a collection of pointer to const B -- one to one.
85  template <typename Bcoll>
86  std::enable_if_t<std::is_same_v<typename Bcoll::value_type, ProdB const*>>
87  fill(size_t index, Ptr<ProdB> const& item, Bcoll& bColl);
88 
89  // 2. When Bcoll is a collection of Ptr<B> -- one to one.
90  template <typename Bcoll>
91  std::enable_if_t<
92  std::is_convertible_v<typename Bcoll::value_type, Ptr<ProdB>>>
93  fill(size_t index, Ptr<ProdB> const& item, Bcoll& bColl);
94 
95  template <typename Bcoll>
96  void init(size_t size, std::vector<Bcoll>& bColls) const;
97 
98  // 3. When Bcoll is a collection of pointer to const B -- one to many.
99  template <typename Bcoll>
100  std::enable_if_t<std::is_same_v<typename Bcoll::value_type, ProdB const*>>
101  fill(size_t index,
102  Ptr<ProdB> const& item,
103  std::vector<Bcoll>& bColls) const;
104 
105  // 4. When Bcoll is a collection of Ptr<B> -- one to many.
106  template <typename Bcoll>
107  std::enable_if_t<
108  std::is_convertible_v<typename Bcoll::value_type, Ptr<ProdB>>>
109  fill(size_t index,
110  Ptr<ProdB> const& item,
111  std::vector<Bcoll>& bColls) const;
112 
113  private:
115  std::vector<uint8_t> seen_;
116  };
117 }
118 
119 template <typename ProdA,
120  typename ProdB,
121  typename Data,
122  typename DATACOLL,
123  typename EVENT>
125 private:
126  // We use IPRHelperDef in place of DATACOLL if Data is void.
127  using dataColl_t =
128  std::conditional_t<std::is_void_v<Data>, IPRHelperDef, DATACOLL>;
129 
130 public:
131  using shared_exception_t = std::shared_ptr<art::Exception const>;
132 
133  IPRHelper(EVENT const& e, InputTag const& tag) : event_{e}, assnsTag_{tag} {}
134 
135  // template <typename A, typename B> shared_exception_t operator()(A const& a,
136  // B const& b) const
137  // (1) fills in b, and
138  // (2) returns a (shared pointer to) an exception. The pointer is
139  // non-null on failure. Note that the returned 'b' might be empty.
140  //
141  // 1. When dColl not wanted.
142  template <typename Acoll, typename Bcoll>
143  shared_exception_t operator()(Acoll const& aColl, Bcoll& bColl) const;
144 
145  // 2. Algorithm useful when dealing with collections of Ptrs.
146  template <typename Acoll, typename Bcoll>
147  shared_exception_t operator()(Acoll const& aColl,
148  Bcoll& bColl,
149  dataColl_t& dColl) const;
150 
151 private:
152  EVENT const& event_;
154 };
155 
156 // 1.
157 template <typename ProdA,
158  typename ProdB,
159  typename Data,
160  typename DATACOLL,
161  typename EVENT>
162 template <typename Acoll, typename Bcoll>
163 inline auto
165  Acoll const& aColl,
166  Bcoll& bColl) const -> shared_exception_t
167 {
168  IPRHelperDef dummy;
169  return (*this)(aColl, bColl, dummy);
170 }
171 
172 // 2.
174 // Implementation notes.
175 //
176 // The current implementation does not verify that the ProductID of the
177 // item in the association collection matches that of the item in the
178 // reference collection before attempting to dereference its Ptr
179 // (although it does verify ptr.isAvailable()). This means that in the
180 // case where an association collection refers to multiple available
181 // AProd collections, all of those collections will be read from file
182 // even if the reference collection does not include items from one or
183 // more of those AProd collections.
184 //
185 // If one were to provide an implementation that did this, one would
186 // change the unordered_multimap to key on the full ptr instead of the
187 // pointer. There is a specialization of std::hash<T> for T =
188 // art::Ptr<X> to support this.
189 //
190 // However, it would be problematic to do the lookup if the reference
191 // item was not in fact a Ptr. Maybe it would be relatively efficient if
192 // one were able to do a lookup in the table against an entity not a Ptr
193 // for which I could write a comparison function that compared the
194 // ProductID and only if they matched, the pointer with suitable get().
195 //
196 // For now however, no-one has requested this,
198 template <typename ProdA,
199  typename ProdB,
200  typename Data,
201  typename DATACOLL,
202  typename EVENT>
203 template <typename Acoll, typename Bcoll>
204 auto
206  Acoll const& aColl,
207  Bcoll& bColl,
208  dataColl_t& dColl) const -> shared_exception_t
209 {
210  detail::BcollHelper<ProdB> bh(assnsTag_);
212  typename EVENT::template HandleT<Assns<ProdA, ProdB, Data>> assnsHandle;
213  event_.getByLabel(assnsTag_, assnsHandle);
214  if (!assnsHandle.isValid()) {
215  return assnsHandle.whyFailed(); // Failed to get Assns product.
216  }
217  bh.init(aColl.size(), bColl);
218  dh.init(aColl.size(), dColl);
219  // Answer cache.
220  std::unordered_multimap<typename Ptr<ProdA>::const_pointer,
221  std::pair<Ptr<ProdB>, ptrdiff_t>>
222  lookupCache;
223  ptrdiff_t counter{0};
224  for (auto const& apair : *assnsHandle) {
225  if (apair.first.isAvailable()) {
226  lookupCache.emplace(
227  apair.first.get(),
228  typename decltype(lookupCache)::mapped_type(apair.second, counter));
229  }
230  ++counter;
231  }
232  // Now use the cache.
233  size_t bIndex{0};
234  using std::cbegin;
235  using std::cend;
236  for (auto i = cbegin(aColl), e = cend(aColl); i != e; ++i, ++bIndex) {
237  auto foundItems = lookupCache.equal_range(
239  if (foundItems.first != lookupCache.cend()) {
240  std::for_each(
241  foundItems.first,
242  foundItems.second,
243  [&bh, &dh, &bColl, bIndex, &assnsHandle, &dColl](auto const& itemPair) {
244  bh.fill(bIndex, itemPair.second.first, bColl);
245  dh.fill(itemPair.second.second, *assnsHandle, bIndex, dColl);
246  });
247  }
248  }
249  return shared_exception_t();
250 }
251 
252 template <typename DATA>
253 inline void
255  std::vector<DATA const*>& data) const
256 {
257  data.assign(size, 0);
258 }
259 
260 template <typename DATA>
261 template <typename ASSNS>
262 inline void
263 art::detail::DataCollHelper<DATA>::fill(ptrdiff_t const assns_index,
264  ASSNS const& assns,
265  size_t const data_index,
266  std::vector<DATA const*>& data) const
267 {
268  data[data_index] = &assns.data(assns_index);
269 }
270 
271 template <typename DATA>
272 inline void
274  size_t const size,
275  std::vector<std::vector<DATA const*>>& data) const
276 {
277  data.resize(size);
278 }
279 
280 template <typename DATA>
281 template <typename ASSNS>
282 inline void
284  ptrdiff_t const assns_index,
285  ASSNS const& assns,
286  size_t const data_index,
287  std::vector<std::vector<DATA const*>>& data) const
288 {
289  data[data_index].push_back(&assns.data(assns_index));
290 }
291 
292 template <typename DATA>
293 inline void
295 {}
296 
297 template <typename DATA>
298 template <typename ASSNS>
299 inline void
301  ASSNS const&,
302  size_t,
303  IPRHelperDef&) const
304 {}
305 
306 template <typename ProdB>
308  : assnsTag_{assnsTag}, seen_()
309 {}
310 
311 template <typename ProdB>
312 template <typename Bcoll>
313 inline void
314 art::detail::BcollHelper<ProdB>::init(size_t const size, Bcoll& bColl)
315 {
316  // This works if BColl is a collection of pointers or Ptrs.
317  bColl.assign(size, typename Bcoll::value_type{});
318  seen_.assign(size, uint8_t{});
319 }
320 
321 // 1.
322 template <typename ProdB>
323 template <typename Bcoll>
324 inline std::enable_if_t<
325  std::is_same_v<typename Bcoll::value_type, ProdB const*>>
327  Ptr<ProdB> const& item,
328  Bcoll& bColl)
329 {
330  // This works if BColl is a collection of pointers or Ptrs.
331  if (seen_[index] == uint8_t(1u)) {
333  << "Attempted to create a FindOne object for a one-many or many-many"
334  << " association specified in collection " << assnsTag_ << ".\n";
335  } else if (item) {
336  bColl[index] = item.get();
337  seen_[index] = uint8_t(1u);
338  } else {
340  << "Attempted to create a FindOne object where an associated item is "
341  << "\nunavailable.\n";
342  }
343 }
344 
345 // 2.
346 template <typename ProdB>
347 template <typename Bcoll>
348 inline std::enable_if_t<
349  std::is_convertible_v<typename Bcoll::value_type, art::Ptr<ProdB>>>
351  Ptr<ProdB> const& item,
352  Bcoll& bColl)
353 {
354  // This works if BColl is a collection of pointers or Ptrs.
355  if (seen_[index] == uint8_t(1u)) {
357  << "Attempted to create a FindOne object for a one-many or many-many"
358  << " association specified in collection " << assnsTag_ << ".\n";
359  }
360  bColl[index] = item;
361  seen_[index] = uint8_t(1u);
362 }
363 
364 template <typename ProdB>
365 template <typename Bcoll>
366 inline void
368  std::vector<Bcoll>& bColls) const
369 {
370  bColls.resize(size);
371 }
372 
373 // 3.
374 template <typename ProdB>
375 template <typename Bcoll>
376 inline std::enable_if_t<
377  std::is_same_v<typename Bcoll::value_type, ProdB const*>>
379  Ptr<ProdB> const& item,
380  std::vector<Bcoll>& bColls) const
381 {
382  bColls[index].push_back(item ? item.get() : nullptr);
383 }
384 
385 // 4.
386 template <typename ProdB>
387 template <typename Bcoll>
388 inline std::enable_if_t<
389  std::is_convertible_v<typename Bcoll::value_type, art::Ptr<ProdB>>>
391  Ptr<ProdB> const& item,
392  std::vector<Bcoll>& bColls) const
393 {
394  bColls[index].push_back(item);
395 }
396 
397 #endif /* canvas_Persistency_Common_detail_IPRHelper_h */
398 
399 // Local Variables:
400 // mode: c++
401 // End:
decltype(auto) constexpr cend(T &&obj)
ADL-aware version of std::cend.
Definition: StdUtils.h:93
BcollHelper(InputTag const &assnsTag)
Definition: IPRHelper.h:307
void init(size_t size, std::vector< DATA const * > &data) const
Definition: IPRHelper.h:254
void init(size_t size, Bcoll &bColl)
Definition: IPRHelper.h:314
std::conditional_t< std::is_void_v< Data >, IPRHelperDef, DATACOLL > dataColl_t
Definition: IPRHelper.h:128
void fill(ptrdiff_t assns_index, ASSNS const &assns, size_t data_index, std::vector< DATA const * > &data) const
Definition: IPRHelper.h:263
InputTag const assnsTag_
Definition: IPRHelper.h:153
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:289
InputTag input_tag(Tag const &tag)
Definition: IPRHelper.h:30
EVENT const & event_
Definition: IPRHelper.h:152
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
Definition: counter.h:295
shared_exception_t operator()(Acoll const &aColl, Bcoll &bColl) const
safe_input_tag(InputTag const &input_tag)
Definition: IPRHelper.h:21
std::vector< uint8_t > seen_
Definition: IPRHelper.h:115
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
InputTag const assnsTag_
Definition: IPRHelper.h:114
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
std::shared_ptr< art::Exception const > shared_exception_t
Definition: IPRHelper.h:131
decltype(auto) constexpr cbegin(T &&obj)
ADL-aware version of std::cbegin.
Definition: StdUtils.h:85
std::enable_if_t< std::is_same_v< typename Bcoll::value_type, ProdB const * > > fill(size_t index, Ptr< ProdB > const &item, Bcoll &bColl)
Definition: IPRHelper.h:326
Float_t e
Definition: plot.C:35
T const * get() const
Definition: Ptr.h:138
T const * const_pointer
Definition: Ptr.h:80
Definition: fwd.h:26
WANTED_POINTER ensurePointer(InputIterator it)
Definition: ensurePointer.h:77
safe_input_tag(ProductToken< Assns< ProdA, ProdB, Data >> const &token)
Definition: IPRHelper.h:22
IPRHelper(EVENT const &e, InputTag const &tag)
Definition: IPRHelper.h:133