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 GArSoft 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 TESTUTILS_PROVIDERLIST_H
19 #define TESTUTILS_PROVIDERLIST_H 1
20 
21 // GArSoft libraries
22 #include "TestUtils/ProviderTestHelpers.h"
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
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 boost::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>
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:
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/GArSoft.
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 boost::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
185  { using exception::exception; };
186  /// Exception thrown on when object is not available any more
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  for (auto it = data.begin(); it != data.end(); ++it)
299  if (it->second.get() == target_ptr) data.erase(it);
300  return true;
301  } // erase()
302 
303 
304  /// Sets the Alias type as an alias of the Prov provider (with labels)
305  template <typename Prov, typename Alias>
306  bool set_alias(std::string alias_label = "", std::string prov_label = "")
307  {
308  // find the alias location
309  auto alias_k = key<Alias>(alias_label); // key
310  auto alias_it = data.find(alias_k);
311  if (alias_it != data.end()) return false;
312 
313  // find the original provider location
314  auto prov_elem = get_elem<Prov>(prov_label);
315 
316  // register the shared object to the alias
317  data.emplace_hint(alias_it, std::move(alias_k),
318  std::make_unique<concrete_type_t<Alias>>
319  (prov_elem, typename concrete_type_t<Alias>::share_t())
320  );
321  return true;
322  } // set_alias()
323 
324 
325  /// @{
326  /**
327  * @brief Retrieve the object of type T stored with the specified label
328  * @tparam T type of the object to be retrieved
329  * @param label optional label used when the object was inserted
330  * @return the specified object as a reference to type T
331  * @throw provider_not_available no type T class was stored with label
332  * @throw provider_deleted the object that was stored is not present
333  * @throw provider_wrong the object is not compatible with the type T
334  */
335  template <typename T>
336  T const& get(std::string label = "") const
337  { return get_elem<T>(label).ref(); }
338 
339  template <typename T>
340  T& get(std::string label = "")
341  { return get_elem<T>(label).ref(); }
342  /// @}
343 
344  /// @{
345  /**
346  * @brief Retrieve the object of type T stored with the specified label
347  * @tparam T type of the object to be retrieved
348  * @param label optional label used when the object was inserted
349  * @return the specified object as a pointer to type T
350  * @throw provider_not_available no type T class was stored with label
351  * @throw provider_deleted the object that was stored is not present
352  * @throw provider_wrong the object is not compatible with the type T
353  */
354  template <typename T>
355  T const* getPointer(std::string label = "") const
356  { return get_elem<T>(label).get(); }
357 
358  template <typename T>
359  T* getPointer(std::string label = "")
360  { return get_elem<T>(label).get(); }
361  /// @}
362 
363  /// Returns whether we have a slot for this object
364  template <typename T>
365  bool known(std::string label = "") const
366  { return find<T>(label) != data.end(); }
367 
368  /// Returns whether the specified object is available
369  template <typename T>
370  bool valid(std::string label = "") const
371  {
372  auto it = find<T>(label);
373  return (it != data.end()) && bool(it->second);
374  }
375 
376  private:
377  using key_type = size_t; ///< type used for key in the internal registry
378 
379  std::unordered_map<key_type, pointer_t> data; ///< all our singletons
380 
381  /// Convert a type into a (ugly) type name
382  template <typename T>
383  static std::string type_name() { return typeid(T).name(); }
384 
385  /// Convert a pointer to object into a (ugly) type name
386  template <typename T>
387  static std::string type_name(T const* ptr) { return typeid(*ptr).name(); }
388 
389  /// @{
390  /// Returns an iterator pointing to the requested key, or data.end()
391  template <typename T>
392  auto find(std::string label = "") const
393  { return data.find(key<T>(label)); }
394 
395  template <typename T>
396  auto find(std::string label = "") { return data.find(key<T>(label)); }
397  /// @}
398 
399  template <typename T>
400  concrete_type_t<T> const& get_elem(std::string label = "") const
401  {
402  auto it = find<T>(label);
403  if (it == data.end())
404  throw provider_not_available("Not available: " + type_name<T>());
405  if (!(it->second))
406  throw provider_deleted("Deleted: " + type_name<T>());
407  auto* ptr = dynamic_cast<details::MovableClassWrapper<T>*>
408  (it->second.get());
409  if (!ptr) {
410  throw provider_wrong("Wrong: " + type_name(it->second.get())
411  + " [requested: " + type_name<T>() + "]");
412  }
413  return *ptr;
414  } // get_elem()
415 
416  template <typename T>
418  {
419  auto it = find<T>(label);
420  if (it == data.end())
421  throw provider_not_available("Not available: " + type_name<T>());
422  if (!(it->second))
423  throw provider_deleted("Deleted: " + type_name<T>());
424  auto* ptr = dynamic_cast<details::MovableClassWrapper<T>*>
425  (it->second.get());
426  if (!ptr) {
427  throw provider_wrong("Wrong: " + type_name(it->second.get())
428  + " [requested: " + type_name<T>() + "]");
429  }
430  return *ptr;
431  } // get_elem()
432 
433  /// Extracts and returns the key out of a type and label
434  template <typename T>
435  static key_type key(std::string label = "")
436  {
437  return typeid(std::decay_t<T>).hash_code()
438  ^ std::hash<std::string>()(label);
439  }
440 
441  }; // class ProviderList
442 
443 
444 } // namespace testing
445 
446 
447 
448 #endif // 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:355
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:417
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:365
STL namespace.
std::unordered_map< key_type, pointer_t > data
all our singletons
Definition: ProviderList.h:379
std::unique_ptr< T > smart_pointer_t
type of smart pointer we use to store elements
Definition: ProviderList.h:167
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
MovableClassWrapper()
Default constructor: no datum present (move one in later on)
Definition: ProviderList.h:73
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:400
A base class with a virtual table.
Definition: ProviderList.h:37
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:306
T * getPointer(std::string label="")
Definition: ProviderList.h:359
static std::string type_name(T const *ptr)
Convert a pointer to object into a (ugly) type name.
Definition: ProviderList.h:387
bool valid(std::string label="") const
Returns whether the specified object is available.
Definition: ProviderList.h:370
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:396
auto find(std::string label="") const
Definition: ProviderList.h:392
static key_type key(std::string label="")
Extracts and returns the key out of a type and label.
Definition: ProviderList.h:435
static std::string type_name()
Convert a type into a (ugly) type name.
Definition: ProviderList.h:383
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