ProviderList.h
Go to the documentation of this file.
1 /**
2  * @file ProviderList.h
3  * @brief Container for service providers used in a test
4  * @date April 22nd, 2016
5  * @author Gianluca Petrillo (petrillo@fnal.gov)
6  * @see ProviderTesterHelpers.h
7  *
8  * This is a header-only library.
9  * It depends only on standard C++ and on LArSoft pure headers with that same
10  * feature, and therefore does not require additional linkage.
11  *
12  * This library provides:
13  *
14  * - ProviderList, an object managing service providers
15  *
16  */
17 
18 #ifndef LARCORE_TESTUTILS_PROVIDERLIST_H
19 #define LARCORE_TESTUTILS_PROVIDERLIST_H 1
20 
21 // LArSoft libraries
23 
24 // C/C++ standard libraries
25 #include <unordered_map>
26 #include <memory> // std::unique_ptr()
27 #include <utility> // std::forward()
28 #include <typeinfo>
29 
30 
31 
32 namespace testing {
33 
34  namespace details {
35 
36  /// A base class with a virtual table
37  struct MovableClassWrapperBase {
38  virtual ~MovableClassWrapperBase() = default;
39 
40  }; // struct MovableClassWrapperBase
41 
42  /**
43  * @brief A class containing an owned object
44  * @tparam T type of the contained object
45  *
46  * Differently from std::any, `T` does not have to be copiable nor
47  * movable. The price is that the object is dynamically allocated.
48  *
49  * Given that we don't require T to be movable and we don't require it to
50  * be polymorphic, we need some way to move T and have T recognized as
51  * such when put in a mixed container. Hence this wrapper.
52  * The price is quite high: two chained dynamic allocations per object
53  * (one in the wrapper to provide mobility, the other in the container
54  * not to lose polymorphism).
55  *
56  */
57  template <typename T>
58  class MovableClassWrapper: public details::MovableClassWrapperBase {
59  using datum_t = T; ///< contained type
60  using this_t = MovableClassWrapper<T>; ///< this type
61  using pointer_t = std::shared_ptr<datum_t>; ///< pointer storing datum
62 
63 
64  template<typename U>
65  friend class MovableClassWrapper;
66 
67  public:
68  struct share_t {}; /// type to specify a share constructor
69 
70  static constexpr share_t share = {};
71 
72  /// Default constructor: no datum present (move one in later on)
73  MovableClassWrapper(): ptr(std::make_unique<datum_t>()) {}
74 
75  template <typename U>
76  MovableClassWrapper(std::unique_ptr<U>&& from): ptr(std::move(from)) {}
77 
78  //@{
79  /// Constructor and assignment from a unique pointer: steal the content
80  MovableClassWrapper(pointer_t&& from): ptr(std::move(from)) {}
81  MovableClassWrapper& operator= (pointer_t&& from)
82  { ptr = std::move(from); return *this; }
83  //@}
84 
85  /// Share constructor (kind of copy)
86  template<typename U>
88  ptr(from.ptr) {}
89 
90 
91  /// Returns a reference to the datum with the correct type
92  datum_t& ref() { return *ptr; }
93 
94  /// Returns a constant reference to the datum with the correct type
95  datum_t const& ref() const { return *ptr; }
96 
97  /// Returns a pointer to the datum with the correct type
98  datum_t* get() { return ptr.get(); }
99 
100  /// Returns a constant pointer to the datum with the correct type
101  datum_t const* get() const { return ptr.get(); }
102 
103  //@{
104  /// Returns whether there is a valid pointer
105  bool valid() const { return bool(ptr); }
106 
107  operator bool() const { return valid(); }
108  //@}
109 
110  private:
111  pointer_t ptr;
112 
113  }; // namespace testing
114 
115  } // namespace details
116 
117 
118 
119  /** *************************************************************************
120  * @brief Container of service providers accessed by type and optional label
121  *
122  * This container is expected to contain elements that are service providers
123  * of different types. Each provider is accessed by its class type and
124  * an optional instance label to discriminate between providers of the same
125  * type.
126  *
127  * The list owns the providers. A provider is created with `setup()`
128  * (or `setup_instance()` if a instance label is needed). This method
129  * relies on a class `testing::ProvideSetupClass` to create and
130  * correctly set up the provider. For example, to set up the provider
131  * `LArPropertiesStandard`:
132  *
133  * provList.setup<LArPropertiesStandard>(pset);
134  *
135  * assuming that `LArPropertiesStandard` provider has a constructor with
136  * as only argument `pset` (supposedly, a `fhicl::ParameterSet`).
137  * If a custom setup is needed, the methods `custom_setup_instance()` and
138  * `custom_setup()` take as argument a setup function, which can do
139  * whatever it takes to perform the set up.
140  *
141  * After a provider is set up, a reference to it can be obtained by `get()`:
142  *
143  * auto& larp = provList.get<LArPropertiesStandard>();
144  *
145  * If no such class is available, an exception will be thrown.
146  * The presence of a provider can be checked beforehand with `has()`.
147  *
148  * @note The presence of multiple providers of the same type, supported via
149  * instance names, is not useful in the current art/LArSoft.
150  *
151  *
152  * How can a provider support the `setup()` construction method?
153  * --------------------------------------------------------------
154  *
155  * A provider can specify its own setup by specialising the class
156  * `testing::ProvideSetupClass`. A default implementation is provided,
157  * that constructs the provider with a parameter set.
158  *
159  */
160  class ProviderList {
161  // Sparse implementation notes:
162  // - we use MovableClassWrapperBase in place of std::any because our
163  // providers are recommended to be not copiable
164 
165  /// type of smart pointer we use to store elements
166  template <typename T>
167  using smart_pointer_t = std::unique_ptr<T>;
168 
169  /// Type of objects contained in the list
171 
172  /// Type of list element with explicit element type memory
173  template <typename T>
175  /// Type of smart pointer to typed list element
176  template <typename T>
178 
179  public:
180  /// base exception class for ProviderList
181  struct exception: public std::runtime_error
182  { using std::runtime_error::runtime_error; };
183  /// Exception thrown on a request about an unregistered type
184  struct provider_not_available: public exception
185  { using exception::exception; };
186  /// Exception thrown on when object is not available any more
187  struct provider_deleted: public exception
188  { using exception::exception; };
189  /// Exception thrown on a invalid type request
190  struct provider_wrong: public exception
191  { using exception::exception; };
192 
193 
194  /**
195  * @brief Construct and register an object of type T
196  * @tparam T type of the object to be constructed (caller specifies it)
197  * @tparam SetupProc type of functor performing the actual setup
198  * @tparam Args type of constructor arguments (compiler fills them in)
199  * @param label name of this instance of object type T (can be empty)
200  * @param provSetup functor performing the setup
201  * @param args arguments to provSetup for the construction of T
202  *
203  * An object is instantiated and associates it with the specified instance
204  * label. It can then be accessed with a `get<T>(label)` call.
205  *
206  * The functor `provSetup` is expected to return a unique pointer to the
207  * newly created provider, `std::unique_ptr<T>`.
208  */
209  template <typename T, typename SetupProc, typename... Args>
210  bool custom_setup_instance
211  (std::string label, SetupProc&& provSetup, Args&&... args)
212  {
213  auto k = key<T>(label); // key
214  auto it = data.find(k);
215  if (it != data.end()) return false;
216 
217  pointer_t ptr = std::make_unique<concrete_type_t<T>>
218  (provSetup(std::forward<Args>(args)...));
219  data.emplace_hint(it, std::move(k), std::move(ptr));
220  return true;
221  } // custom_setup_instance()
222 
223  /// Construct and register an object of type T with specified arguments
224  template <typename T, typename SetupProc, typename... Args>
225  bool custom_setup(SetupProc&& provSetup, Args&&... args)
226  {
227  return custom_setup_instance<T, SetupProc, Args...>(
228  "",
229  std::forward<SetupProc>(provSetup),
230  std::forward<Args>(args)...
231  );
232  } // custom_setup()
233 
234  template <typename T, typename... Args>
235  bool setup_instance
236  (std::string label, Args&&... args)
237  {
238  auto k = key<T>(label); // key
239  auto it = data.find(k);
240  if (it != data.end()) return false;
241 
242  pointer_t ptr = std::make_unique<concrete_type_t<T>>
243  (setupProvider<T>(std::forward<Args>(args)...));
244  data.emplace_hint(it, std::move(k), std::move(ptr));
245  return true;
246  // return custom_setup_instance<T>
247  // (label, setupProvider<T, Args...>, std::forward<Args>(args)...);
248  } // setup_instance()
249 
250  /// Construct and register an object of type T with specified arguments
251  template <typename T, typename... Args>
252  bool setup(Args&&... args)
253  { return setup_instance<T>("", std::forward<Args>(args)...); }
254 
255 
256  /**
257  * @brief Registers and gets ownership of the specified object
258  * @tparam T type of object being acquired
259  * @param obj_ptr pointer to the object to be acquired
260  * @param label name of the object instance
261  * @return whether the object was acquired or not
262  *
263  * The ProviderList takes ownership of the specified provider.
264  * If an object of type T is already registered, the pointer is left
265  * untouched and `false` is returned.
266  */
267  template <typename T>
268  bool acquire(std::unique_ptr<T>&& obj_ptr, std::string label = "")
269  {
270  auto k = key<T>(label); // key
271  auto it = data.find(k);
272  if (it != data.end()) return false;
273 
274  pointer_t ptr
275  = std::make_unique<concrete_type_t<T>>(std::move(obj_ptr));
276  data.emplace_hint(it, std::move(k), std::move(ptr));
277  return true;
278  } // acquire()
279 
280 
281  /**
282  * @brief Drops the object with the specified type and label
283  * @tparam T type of object being acquired
284  * @param label name of the object instance
285  * @return whether the object was present or not
286  *
287  * If present, the object is destroyed
288  */
289  template <typename T>
290  bool erase(std::string label = "")
291  {
292  auto k = key<T>(label); // key
293  auto target_it = data.find(k);
294  if (target_it == data.end()) return false;
295 
296  // erase this and all the aliases pointing to it
297  auto const* target_ptr = target_it->second.get();
298  auto it = data.begin();
299  while (it != data.end()) {
300  if (it->second.get() == target_ptr) it = data.erase(it);
301  else ++it;
302  } // while
303  return true;
304  } // erase()
305 
306 
307  /// Sets the Alias type as an alias of the Prov provider (with labels)
308  template <typename Prov, typename Alias>
309  bool set_alias(std::string alias_label = "", std::string prov_label = "")
310  {
311  // find the alias location
312  auto alias_k = key<Alias>(alias_label); // key
313  auto alias_it = data.find(alias_k);
314  if (alias_it != data.end()) return false;
315 
316  // find the original provider location
317  auto prov_elem = get_elem<Prov>(prov_label);
318 
319  // register the shared object to the alias
320  data.emplace_hint(alias_it, std::move(alias_k),
321  std::make_unique<concrete_type_t<Alias>>
322  (prov_elem, typename concrete_type_t<Alias>::share_t())
323  );
324  return true;
325  } // set_alias()
326 
327 
328  /// @{
329  /**
330  * @brief Retrieve the object of type T stored with the specified label
331  * @tparam T type of the object to be retrieved
332  * @param label optional label used when the object was inserted
333  * @return the specified object as a reference to type T
334  * @throw provider_not_available no type T class was stored with label
335  * @throw provider_deleted the object that was stored is not present
336  * @throw provider_wrong the object is not compatible with the type T
337  */
338  template <typename T>
339  T const& get(std::string label = "") const
340  { return get_elem<T>(label).ref(); }
341 
342  template <typename T>
343  T& get(std::string label = "")
344  { return get_elem<T>(label).ref(); }
345  /// @}
346 
347  /// @{
348  /**
349  * @brief Retrieve the object of type T stored with the specified label
350  * @tparam T type of the object to be retrieved
351  * @param label optional label used when the object was inserted
352  * @return the specified object as a pointer to type T
353  * @throw provider_not_available no type T class was stored with label
354  * @throw provider_deleted the object that was stored is not present
355  * @throw provider_wrong the object is not compatible with the type T
356  */
357  template <typename T>
358  T const* getPointer(std::string label = "") const
359  { return get_elem<T>(label).get(); }
360 
361  template <typename T>
362  T* getPointer(std::string label = "")
363  { return get_elem<T>(label).get(); }
364  /// @}
365 
366  /// Returns whether we have a slot for this object
367  template <typename T>
368  bool known(std::string label = "") const
369  { return find<T>(label) != data.end(); }
370 
371  /// Returns whether the specified object is available
372  template <typename T>
373  bool valid(std::string label = "") const
374  {
375  auto it = find<T>(label);
376  return (it != data.end()) && bool(it->second);
377  }
378 
379  private:
380  using key_type = size_t; ///< type used for key in the internal registry
381 
382  std::unordered_map<key_type, pointer_t> data; ///< all our singletons
383 
384  /// Convert a type into a (ugly) type name
385  template <typename T>
386  static std::string type_name() { return typeid(T).name(); }
387 
388  /// Convert a pointer to object into a (ugly) type name
389  template <typename T>
390  static std::string type_name(T const* ptr) { return typeid(*ptr).name(); }
391 
392  /// @{
393  /// Returns an iterator pointing to the requested key, or data.end()
394  template <typename T>
395  auto find(std::string label = "") const
396  { return data.find(key<T>(label)); }
397 
398  template <typename T>
399  auto find(std::string label = "") { return data.find(key<T>(label)); }
400  /// @}
401 
402  template <typename T>
403  concrete_type_t<T> const& get_elem(std::string label = "") const
404  {
405  auto it = find<T>(label);
406  if (it == data.end())
407  throw provider_not_available("Not available: " + type_name<T>());
408  if (!(it->second))
409  throw provider_deleted("Deleted: " + type_name<T>());
410  auto* ptr = dynamic_cast<details::MovableClassWrapper<T>*>
411  (it->second.get());
412  if (!ptr) {
413  throw provider_wrong("Wrong: " + type_name(it->second.get())
414  + " [requested: " + type_name<T>() + "]");
415  }
416  return *ptr;
417  } // get_elem()
418 
419  template <typename T>
421  {
422  auto it = find<T>(label);
423  if (it == data.end())
424  throw provider_not_available("Not available: " + type_name<T>());
425  if (!(it->second))
426  throw provider_deleted("Deleted: " + type_name<T>());
427  auto* ptr = dynamic_cast<details::MovableClassWrapper<T>*>
428  (it->second.get());
429  if (!ptr) {
430  throw provider_wrong("Wrong: " + type_name(it->second.get())
431  + " [requested: " + type_name<T>() + "]");
432  }
433  return *ptr;
434  } // get_elem()
435 
436  /// Extracts and returns the key out of a type and label
437  template <typename T>
438  static key_type key(std::string label = "")
439  {
440  return typeid(std::decay_t<T>).hash_code()
441  ^ std::hash<std::string>()(label);
442  }
443 
444  }; // class ProviderList
445 
446 
447 } // namespace testing
448 
449 
450 
451 #endif // LARCORE_TESTUTILS_PROVIDERLIST_H
static QCString name
Definition: declinfo.cpp:673
smart_pointer_t< details::MovableClassWrapperBase > pointer_t
Type of objects contained in the list.
Definition: ProviderList.h:170
Container of service providers accessed by type and optional label.
Definition: ProviderList.h:160
LArSoft test utilities.
T const * getPointer(std::string label="") const
Retrieve the object of type T stored with the specified label.
Definition: ProviderList.h:358
MovableClassWrapper(MovableClassWrapper< U > const &from, share_t)
Share constructor (kind of copy)
Definition: ProviderList.h:87
Exception thrown on a request about an unregistered type.
Definition: ProviderList.h:184
concrete_type_t< T > & get_elem(std::string label="")
Definition: ProviderList.h:420
std::string string
Definition: nybbler.cc:12
Exception thrown on a invalid type request.
Definition: ProviderList.h:190
MovableClassWrapper(std::unique_ptr< U > &&from)
Definition: ProviderList.h:76
datum_t const & ref() const
Returns a constant reference to the datum with the correct type.
Definition: ProviderList.h:95
bool known(std::string label="") const
Returns whether we have a slot for this object.
Definition: ProviderList.h:368
STL namespace.
std::unique_ptr< T > smart_pointer_t
type of smart pointer we use to store elements
Definition: ProviderList.h:167
MovableClassWrapper()
Default constructor: no datum present (move one in later on)
Definition: ProviderList.h:73
static QCString args
Definition: declinfo.cpp:674
smart_pointer_t< concrete_type_t< T >> concrete_pointer_t
Type of smart pointer to typed list element.
Definition: ProviderList.h:177
datum_t & ref()
Returns a reference to the datum with the correct type.
Definition: ProviderList.h:92
bool acquire(std::unique_ptr< T > &&obj_ptr, std::string label="")
Registers and gets ownership of the specified object.
Definition: ProviderList.h:268
bool erase(std::string label="")
Drops the object with the specified type and label.
Definition: ProviderList.h:290
bool valid() const
Returns whether there is a valid pointer.
Definition: ProviderList.h:105
concrete_type_t< T > const & get_elem(std::string label="") const
Definition: ProviderList.h:403
def move(depos, offset)
Definition: depos.py:107
std::shared_ptr< datum_t > pointer_t
pointer storing datum
Definition: ProviderList.h:61
bool set_alias(std::string alias_label="", std::string prov_label="")
Sets the Alias type as an alias of the Prov provider (with labels)
Definition: ProviderList.h:309
T * getPointer(std::string label="")
Definition: ProviderList.h:362
static std::string type_name(T const *ptr)
Convert a pointer to object into a (ugly) type name.
Definition: ProviderList.h:390
bool valid(std::string label="") const
Returns whether the specified object is available.
Definition: ProviderList.h:373
bool custom_setup(SetupProc &&provSetup, Args &&...args)
Construct and register an object of type T with specified arguments.
Definition: ProviderList.h:225
base exception class for ProviderList
Definition: ProviderList.h:181
bool setup(Args &&...args)
Construct and register an object of type T with specified arguments.
Definition: ProviderList.h:252
Exception thrown on when object is not available any more.
Definition: ProviderList.h:187
auto find(std::string label="")
Definition: ProviderList.h:399
auto find(std::string label="") const
Definition: ProviderList.h:395
static key_type key(std::string label="")
Extracts and returns the key out of a type and label.
Definition: ProviderList.h:438
static std::string type_name()
Convert a type into a (ugly) type name.
Definition: ProviderList.h:386
MovableClassWrapper(pointer_t &&from)
Constructor and assignment from a unique pointer: steal the content.
Definition: ProviderList.h:80
A class containing an owned object.
Definition: ProviderList.h:58
size_t key_type
type used for key in the internal registry
Definition: ProviderList.h:377
int bool
Definition: qglobal.h:345
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33