Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RDirectory.hxx
Go to the documentation of this file.
1 /// \file ROOT/RDirectory.h
2 /// \ingroup Base ROOT7
3 /// \author Axel Naumann <axel@cern.ch>
4 /// \date 2015-07-31
5 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6 /// is welcome!
7 
8 /*************************************************************************
9  * Copyright (C) 1995-2015, Rene Brun and Fons Rademakers. *
10  * All rights reserved. *
11  * *
12  * For the licensing terms see $ROOTSYS/LICENSE. *
13  * For the list of contributors see $ROOTSYS/README/CREDITS. *
14  *************************************************************************/
15 
16 #ifndef ROOT7_RDirectory
17 #define ROOT7_RDirectory
18 
19 #include "ROOT/RLogger.hxx"
20 #include "ROOT/RDirectoryEntry.hxx"
21 
22 #include <iterator>
23 #include <memory>
24 #include <type_traits>
25 #include <unordered_map>
26 #include <string>
27 #include <ROOT/RStringView.hxx>
28 
29 namespace ROOT {
30 namespace Experimental {
31 
32 /**
33  Objects of this class are thrown to signal that no key with that name exists.
34  */
35 class RDirectoryUnknownKey: public std::exception {
36  std::string fKeyName;
37 
38 public:
39  RDirectoryUnknownKey(std::string_view keyName): fKeyName(keyName) {}
40  const char *what() const noexcept final { return fKeyName.c_str(); }
41 };
42 
43 /**
44  Objects of this class are thrown to signal that the value known under the
45  given name .
46  */
47 class RDirectoryTypeMismatch: public std::exception {
48  std::string fKeyName;
49  // FIXME: add expected and actual type names.
50 public:
51  RDirectoryTypeMismatch(std::string_view keyName): fKeyName(keyName) {}
52  const char *what() const noexcept final { return fKeyName.c_str(); }
53 };
54 
55 /**
56  Key/value store of objects.
57 
58  Given a name, a `RDirectory` can store and retrieve an object. It will manage
59  shared ownership through a `shared_ptr`.
60 
61  Example:
62  RDirectory dirMC;
63  RDirectory dirHiggs;
64 
65  dirMC.Add("higgs", histHiggsMC);
66  dirHiggs.Add("mc", histHiggsMC);
67 
68  */
69 
70 class RDirectory {
71  // TODO: ContentMap_t should allow lookup by string_view while still providing
72  // storage of names.
73 
74  /// The directory content is a hashed map of name => `Internal::RDirectoryEntry`.
75  using ContentMap_t = std::unordered_map<std::string, Internal::RDirectoryEntry>;
76 
77  /// The `RDirectory`'s content.
78  ContentMap_t fContent;
79 
80  template <class T>
81  struct ToContentType {
82  using decaytype = typename std::decay<T>::type;
83  using type =
84  typename std::enable_if<!std::is_pointer<decaytype>::value && !std::is_member_pointer<decaytype>::value &&
85  !std::is_void<decaytype>::value,
86  decaytype>::type;
87  };
88  template <class T>
89  using ToContentType_t = typename ToContentType<T>::type;
90 
91 public:
92  /// Create an object of type `T` (passing some arguments to its constructor).
93  /// The `RDirectory` will have shared ownership of the object.
94  ///
95  /// \param name - Key of the object.
96  /// \param args - arguments to be passed to the constructor of `T`
97  template <class T, class... ARGS>
98  std::shared_ptr<ToContentType_t<T>> Create(std::string_view name, ARGS &&... args)
99  {
100  auto ptr = std::make_shared<ToContentType_t<T>>(std::forward<ARGS>(args)...);
101  Add(name, ptr);
102  return ptr;
103  }
104 
105  /// Find the RDirectoryEntry associated to the name. Returns empty RDirectoryEntry if
106  /// nothing is found.
107  Internal::RDirectoryEntry Find(std::string_view name) const
108  {
109  auto idx = fContent.find(std::string(name));
110  if (idx == fContent.end())
111  return nullptr;
112  return idx->second;
113  }
114 
115  /**
116  Status of the call to Find<T>(name).
117  */
118  enum class EFindStatus {
119  kValidValue, ///< Value known for this key name and type
120  kValidValueBase, ///< Value known for this key name and base type
121  kKeyNameNotFound, ///< No key is known for this name
122  kTypeMismatch ///< The provided type does not match the value's type.
123  };
124 
125  /// Find the RDirectoryEntry associated with the name.
126  /// \returns empty RDirectoryEntry in `first` if nothing is found, or if the type does not
127  /// match the expected type. `second` contains the reason.
128  /// \note if `second` is kValidValue, then static_pointer_cast<`T`>(`first`.GetPointer())
129  /// is shared_ptr<`T`> to initially stored object
130  /// \note if `second` is kValidValueBase, then `first`.CastPointer<`T`>()
131  /// is a valid cast to base class `T` of the stored object
132  template <class T>
133  std::pair<Internal::RDirectoryEntry, EFindStatus> Find(std::string_view name) const
134  {
135  auto idx = fContent.find(std::string(name));
136  if (idx == fContent.end())
137  return {nullptr, EFindStatus::kKeyNameNotFound};
138  if (idx->second.GetTypeInfo() == typeid(ToContentType_t<T>))
139  return {idx->second, EFindStatus::kValidValue};
140  if (idx->second.CastPointer<ToContentType_t<T>>())
141  return {idx->second, EFindStatus::kValidValueBase};
142  return {nullptr, EFindStatus::kTypeMismatch};
143  }
144 
145  /// Get the object for a key. `T` can be the object's type or a base class.
146  /// The `RDirectory` will return the same object for subsequent calls to
147  /// `Get().`
148  /// \returns a `shared_ptr` to the object or its base.
149  /// \throws RDirectoryUnknownKey if no object is stored under this name.
150  /// \throws RDirectoryTypeMismatch if the object stored under this name is of
151  /// a type that is not a derived type of `T`.
152  template <class T>
153  std::shared_ptr<ToContentType_t<T>> Get(std::string_view name)
154  {
155  const auto &pair = Find<T>(name);
156  const Internal::RDirectoryEntry &entry = pair.first;
157  EFindStatus status = pair.second;
158  switch (status) {
159  case EFindStatus::kValidValue: return std::static_pointer_cast<ToContentType_t<T>>(entry.GetPointer());
160  case EFindStatus::kValidValueBase: return entry.CastPointer<ToContentType_t<T>>();
161  case EFindStatus::kTypeMismatch:
162  // FIXME: add expected versus actual type name as c'tor args
163  throw RDirectoryTypeMismatch(name);
164  case EFindStatus::kKeyNameNotFound: throw RDirectoryUnknownKey(name);
165  }
166  return nullptr; // never happens
167  }
168 
169  /// Add an existing object (rather a `shared_ptr` to it) to the RDirectory.
170  /// The RDirectory will have shared ownership.
171  template <class T>
172  void Add(std::string_view name, const std::shared_ptr<T> &ptr)
173  {
174  Internal::RDirectoryEntry entry(ptr);
175  // FIXME: CXX17: insert_or_assign
176  std::string sName(name);
177  auto idx = fContent.find(sName);
178  if (idx != fContent.end()) {
179  R__LOG_HERE(ELogLevel::kWarning, "CORE") << "Replacing object with name \"" << name << "\"" << std::endl;
180  idx->second.swap(entry);
181  } else {
182  fContent[sName].swap(entry);
183  }
184  }
185 
186  /// Remove entry from RDirectory (if exists)
187  bool Remove(std::string_view name)
188  {
189  std::string sName(name);
190  auto idx = fContent.find(sName);
191  if (idx != fContent.end()) {
192  fContent.erase(idx);
193  return true;
194  }
195  return false;
196  }
197 
198 
199  /// Dedicated, process-wide RDirectory.
200  ///
201  /// \note This is *not* thread-safe. You will need to syncronize yourself. In
202  /// general it's a bad idea to use a global collection in a multi-threaded
203  /// environment; ROOT itself does not make use of it. It is merely offered for
204  /// historical, process-wide object registration by name. Instead, pass a
205  /// pointer to the object where you need to access it - this is also much
206  /// faster than a lookup by name.
207  static RDirectory &Heap();
208 };
209 
210 } // namespace Experimental
211 } // namespace ROOT
212 
213 #endif