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 {
169  return (*this)(aColl, bColl, dummy);
170 }
171 
172 // 2.
173 ////////////////////////////////////////////////////////////////////////
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,
197 ////////////////////////////////////////////////////////////////////////
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 operator()(Acoll const& aColl, Bcoll& bColl, dataColl_t& dColl) const
208 {
209  detail::BcollHelper<ProdB> bh(assnsTag_);
211  typename EVENT::template HandleT<Assns<ProdA, ProdB, Data>> assnsHandle;
212  event_.getByLabel(assnsTag_, assnsHandle);
213  if (!assnsHandle.isValid()) {
214  return assnsHandle.whyFailed(); // Failed to get Assns product.
215  }
216  bh.init(aColl.size(), bColl);
217  dh.init(aColl.size(), dColl);
218  // Answer cache.
219  std::unordered_multimap<typename Ptr<ProdA>::const_pointer,
220  std::pair<Ptr<ProdB>, ptrdiff_t>>
221  lookupCache;
222  ptrdiff_t counter{0};
223  for (auto const& apair : *assnsHandle) {
224  if (apair.first.isAvailable()) {
225  lookupCache.emplace(
226  apair.first.get(),
227  typename decltype(lookupCache)::mapped_type(apair.second, counter));
228  }
229  ++counter;
230  }
231  // Now use the cache.
232  size_t bIndex{0};
233  for (typename Acoll::const_iterator i = aColl.begin(), e = aColl.end();
234  i != e;
235  ++i, ++bIndex) {
236  auto foundItems = lookupCache.equal_range(
238  if (foundItems.first != lookupCache.cend()) {
239  std::for_each(
240  foundItems.first,
241  foundItems.second,
242  [&bh, &dh, &bColl, bIndex, &assnsHandle, &dColl](auto const& itemPair) {
243  bh.fill(bIndex, itemPair.second.first, bColl);
244  dh.fill(itemPair.second.second, *assnsHandle, bIndex, dColl);
245  });
246  }
247  }
248  return shared_exception_t();
249 }
250 
251 template <typename DATA>
252 inline void
254  std::vector<DATA const*>& data) const
255 {
256  data.assign(size, 0);
257 }
258 
259 template <typename DATA>
260 template <typename ASSNS>
261 inline void
262 art::detail::DataCollHelper<DATA>::fill(ptrdiff_t const assns_index,
263  ASSNS const& assns,
264  size_t const data_index,
265  std::vector<DATA const*>& data) const
266 {
267  data[data_index] = &assns.data(assns_index);
268 }
269 
270 template <typename DATA>
271 inline void
273  size_t const size,
274  std::vector<std::vector<DATA const*>>& data) const
275 {
276  data.resize(size);
277 }
278 
279 template <typename DATA>
280 template <typename ASSNS>
281 inline void
283  ptrdiff_t const assns_index,
284  ASSNS const& assns,
285  size_t const data_index,
286  std::vector<std::vector<DATA const*>>& data) const
287 {
288  data[data_index].push_back(&assns.data(assns_index));
289 }
290 
291 template <typename DATA>
292 inline void
294 {}
295 
296 template <typename DATA>
297 template <typename ASSNS>
298 inline void
300  ASSNS const&,
301  size_t,
302  IPRHelperDef&) const
303 {}
304 
305 template <typename ProdB>
307  : assnsTag_{assnsTag}, seen_()
308 {}
309 
310 template <typename ProdB>
311 template <typename Bcoll>
312 inline void
313 art::detail::BcollHelper<ProdB>::init(size_t const size, Bcoll& bColl)
314 {
315  // This works if BColl is a collection of pointers or Ptrs.
316  bColl.assign(size, typename Bcoll::value_type{});
317  seen_.assign(size, uint8_t{});
318 }
319 
320 // 1.
321 template <typename ProdB>
322 template <typename Bcoll>
323 inline std::enable_if_t<
324  std::is_same_v<typename Bcoll::value_type, ProdB const*>>
326  Ptr<ProdB> const& item,
327  Bcoll& bColl)
328 {
329  // This works if BColl is a collection of pointers or Ptrs.
330  if (seen_[index] == uint8_t(1u)) {
332  << "Attempted to create a FindOne object for a one-many or many-many"
333  << " association specified in collection " << assnsTag_ << ".\n";
334  } else if (item) {
335  bColl[index] = item.get();
336  seen_[index] = uint8_t(1u);
337  } else {
339  << "Attempted to create a FindOne object where an associated item is "
340  << "\nunavailable.\n";
341  }
342 }
343 
344 // 2.
345 template <typename ProdB>
346 template <typename Bcoll>
347 inline std::enable_if_t<
348  std::is_convertible_v<typename Bcoll::value_type, art::Ptr<ProdB>>>
350  Ptr<ProdB> const& item,
351  Bcoll& bColl)
352 {
353  // This works if BColl is a collection of pointers or Ptrs.
354  if (seen_[index] == uint8_t(1u)) {
356  << "Attempted to create a FindOne object for a one-many or many-many"
357  << " association specified in collection " << assnsTag_ << ".\n";
358  }
359  bColl[index] = item;
360  seen_[index] = uint8_t(1u);
361 }
362 
363 template <typename ProdB>
364 template <typename Bcoll>
365 inline void
367  std::vector<Bcoll>& bColls) const
368 {
369  bColls.resize(size);
370 }
371 
372 // 3.
373 template <typename ProdB>
374 template <typename Bcoll>
375 inline std::enable_if_t<
376  std::is_same_v<typename Bcoll::value_type, ProdB const*>>
378  Ptr<ProdB> const& item,
379  std::vector<Bcoll>& bColls) const
380 {
381  bColls[index].push_back(item ? item.get() : nullptr);
382 }
383 
384 // 4.
385 template <typename ProdB>
386 template <typename Bcoll>
387 inline std::enable_if_t<
388  std::is_convertible_v<typename Bcoll::value_type, art::Ptr<ProdB>>>
390  Ptr<ProdB> const& item,
391  std::vector<Bcoll>& bColls) const
392 {
393  bColls[index].push_back(item);
394 }
395 
396 #endif /* canvas_Persistency_Common_detail_IPRHelper_h */
397 
398 // Local Variables:
399 // mode: c++
400 // End:
BcollHelper(InputTag const &assnsTag)
Definition: IPRHelper.h:306
void init(size_t size, std::vector< DATA const * > &data) const
Definition: IPRHelper.h:253
void init(size_t size, Bcoll &bColl)
Definition: IPRHelper.h:313
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:262
InputTag const assnsTag_
Definition: IPRHelper.h:153
const XML_Char const XML_Char * data
Definition: expat.h:268
InputTag input_tag(Tag const &tag)
Definition: IPRHelper.h:30
EVENT const & event_
Definition: IPRHelper.h:152
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
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
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:325
Float_t e
Definition: plot.C:35
T const * get() const
Definition: Ptr.h:149
::xsd::cxx::tree::token< char, normalized_string > token
Definition: Database.h:156
T const * const_pointer
Definition: Ptr.h:81
Definition: fwd.h:29
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