SMTK  @SMTK_VERSION@
Simulation Modeling Tool Kit
TypeContainer.h
1 //=========================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //=========================================================================
10 
11 #ifndef smtk_common_TypeContainer_h
12 #define smtk_common_TypeContainer_h
13 
14 #include "smtk/CoreExports.h"
15 
16 #include "smtk/SystemConfig.h"
17 
18 #include "smtk/common/CompilerInformation.h"
19 #include "smtk/common/TypeName.h"
20 
21 #include "smtk/string/Token.h"
22 
23 #include <memory>
24 #include <stdexcept>
25 #include <string>
26 #include <typeinfo>
27 #include <unordered_map>
28 
29 namespace smtk
30 {
31 namespace common
32 {
37 class SMTKCORE_EXPORT TypeContainer
38 {
39  struct Wrapper
40  {
41  virtual ~Wrapper() = default;
42  virtual std::unique_ptr<Wrapper> clone() const = 0;
43  };
44 
45  template<typename Type>
46  struct WrapperFor : Wrapper
47  {
48  template<typename... Args>
49  WrapperFor(Args&&... v)
50  : value(std::forward<Args>(v)...)
51  {
52  }
53 
54  std::unique_ptr<Wrapper> clone() const override
55  {
56 #ifdef SMTK_HAVE_CXX_14
57  return std::make_unique<WrapperFor<Type>>(std::make_unique<Type>(*value));
58 #else
59  return std::unique_ptr<Wrapper>(new WrapperFor<Type>(new Type(*value)));
60 #endif
61  }
62 
63  std::unique_ptr<Type> value;
64  };
65 
66 public:
67  class BadTypeError : public std::out_of_range
68  {
69  public:
70  BadTypeError(const std::string& typeName)
71  : std::out_of_range("Type \"" + typeName + "\" not available in this container")
72  {
73  }
74  };
75 
76  using KeyType = smtk::string::Hash;
77 
79  TypeContainer() = default;
80 
84 
86  TypeContainer(TypeContainer&&) = default;
87 
89  TypeContainer& operator=(const TypeContainer&);
90 
92  TypeContainer& operator=(TypeContainer&&) = default;
93 
97  template<
98  typename Arg,
99  typename... Args,
100  typename std::enable_if<!std::is_base_of<TypeContainer, Arg>::value, int>::type = 0>
101  TypeContainer(const Arg& arg, const Args&... args)
102  {
103  insertAll(arg, args...);
104  }
105 
106  virtual ~TypeContainer();
107 
109  template<typename Type>
110  KeyType keyId() const
111  {
112  (void)this; // Prevent clang-tidy from complaining this could be class-static.
113  std::string keyName = smtk::common::typeName<Type>();
114  KeyType value = smtk::string::Token(keyName).id();
115  return value;
116  }
117 
119  template<typename Type>
120  bool contains() const
121  {
122  return (m_container.find(this->keyId<Type>()) != m_container.end());
123  }
124 
127  template<typename Type>
128  bool insert(const Type& value)
129  {
130  return m_container
131  .emplace(std::make_pair(
132  this->keyId<Type>(),
133 #ifdef SMTK_HAVE_CXX_14
134  std::make_unique<WrapperFor<Type>>(std::make_unique<Type>(value))
135 #else
136  std::unique_ptr<Wrapper>(new WrapperFor<Type>(std::unique_ptr<Type>(new Type((value)))))
137 #endif
138  ))
139  .second;
140  }
141 
143  template<typename Type>
144  bool insert_or_assign(const Type& value)
145  {
146  if (this->contains<Type>())
147  {
148  this->erase<Type>();
149  }
150  return this->insert<Type>(value);
151  }
152 
154  template<typename Type, typename... Args>
155  bool emplace(Args&&... args)
156  {
157  return m_container
158  .emplace(std::make_pair(
159  this->keyId<Type>(),
160 #ifdef SMTK_HAVE_CXX_14
161  std::make_unique<WrapperFor<Type>>(std::make_unique<Type>(std::forward<Args>(args)...))
162 #else
163  std::unique_ptr<Wrapper>(
164  new WrapperFor<Type>(std::unique_ptr<Type>(new Type(std::forward<Args>(args)...))))
165 #endif
166  ))
167  .second;
168  }
169 
171  template<typename Type>
172  const Type& get() const
173  {
174  auto search = m_container.find(this->keyId<Type>());
175  if (search == m_container.end())
176  {
177  throw BadTypeError(smtk::common::typeName<Type>());
178  }
179 
180  return *(static_cast<WrapperFor<Type>*>(search->second.get()))->value;
181  }
182 
185  template<typename Type>
186  typename std::enable_if<std::is_default_constructible<Type>::value, Type&>::type get() noexcept
187  {
188  auto search = m_container.find(this->keyId<Type>());
189  if (search == m_container.end())
190  {
191  search = m_container
192  .emplace(std::make_pair(
193  this->keyId<Type>(),
194 #ifdef SMTK_HAVE_CXX_14
195  std::make_unique<WrapperFor<Type>>(std::make_unique<Type>())
196 #else
197  std::unique_ptr<Wrapper>(new WrapperFor<Type>(std::unique_ptr<Type>(new Type)))
198 #endif
199  ))
200  .first;
201  }
202 
203  return *(static_cast<WrapperFor<Type>*>(search->second.get()))->value;
204  }
205 
208  template<typename Type>
209  typename std::enable_if<!std::is_default_constructible<Type>::value, Type&>::type get()
210  {
211  auto search = m_container.find(this->keyId<Type>());
212  if (search == m_container.end())
213  {
214  throw BadTypeError(smtk::common::typeName<Type>());
215  }
216 
217  return *(static_cast<WrapperFor<Type>*>(search->second.get()))->value;
218  }
219 
221  template<typename Type>
222  bool erase()
223  {
224  return m_container.erase(this->keyId<Type>()) > 0;
225  }
226 
228  bool empty() const noexcept { return m_container.empty(); }
229 
231  std::size_t size() const noexcept { return m_container.size(); }
232 
234  void clear() noexcept { m_container.clear(); }
235 
242  std::set<smtk::string::Token> keys() const
243  {
244  std::set<smtk::string::Token> result;
245  for (const auto& entry : m_container)
246  {
247  try
248  {
249  result.insert(smtk::string::Token::fromHash(entry.first));
250  }
251  catch (std::invalid_argument&)
252  {
253  // Ignore entries with no matching string.
254  }
255  }
256  return result;
257  }
258 
259 private:
260  template<typename Arg, typename... Args>
261  typename std::enable_if<!std::is_base_of<TypeContainer, Arg>::value, bool>::type insertAll(
262  const Arg& arg,
263  const Args&... args)
264  {
265  return insert<Arg>(arg) && insertAll(args...);
266  }
267  bool insertAll() { return true; }
268 
269  std::unordered_map<std::size_t, std::unique_ptr<Wrapper>> m_container;
270 };
271 } // namespace common
272 } // namespace smtk
273 
274 #endif
smtk::common::TypeContainer::empty
bool empty() const noexcept
Return true if the container holds no objects and false otherwise.
Definition: TypeContainer.h:228
smtk
The main namespace for the Simulation Modeling Tool Kit (SMTK).
Definition: doc.h:33
smtk::common::TypeContainer::contains
bool contains() const
Check if a Type is present in the TypeContainer.
Definition: TypeContainer.h:120
smtk::common::TypeContainer::insert_or_assign
bool insert_or_assign(const Type &value)
Insert a Type instance into the TypeContainer if it does not exist already or replace it if it does.
Definition: TypeContainer.h:144
smtk::common::TypeContainer::clear
void clear() noexcept
Erase all objects held by the container.
Definition: TypeContainer.h:234
smtk::common::TypeContainer::get
const Type & get() const
Access a Type instance, and throw if it is not in the TypeContainer.
Definition: TypeContainer.h:172
smtk::string::Token::fromHash
static Token fromHash(Hash h)
Construct a token given only its hash.
Definition: Token.cxx:105
smtk::common::TypeContainer::get
std::enable_if<!std::is_default_constructible< Type >::value, Type & >::type get()
For non-default-constructible types, access a Type instance; throw if it is not in the TypeContainer.
Definition: TypeContainer.h:209
smtk::common::TypeContainer::get
std::enable_if< std::is_default_constructible< Type >::value, Type & >::type get() noexcept
For default-constructible types, access a Type instance, creating one if it is not in the TypeContain...
Definition: TypeContainer.h:186
smtk::common::TypeContainer::keyId
KeyType keyId() const
Return the ID used to index a given Type.
Definition: TypeContainer.h:110
smtk::string::Hash
std::size_t Hash
A fixed-size integer type used to represent an arbitrary-length string.
Definition: Manager.h:34
TypeName.h
Named type functions.
smtk::common::TypeContainer::emplace
bool emplace(Args &&... args)
Emplace a Type instance into the TypeContainer.
Definition: TypeContainer.h:155
smtk::string::Token::id
Hash id() const
Return the token's ID (usually its hash but possibly not in the case of collisions).
Definition: Token.h:48
smtk::common::TypeContainer::erase
bool erase()
Remove a specific type of object from the container.
Definition: TypeContainer.h:222
smtk::common::typeName
std::string typeName()
Return the name of a class.
Definition: TypeName.h:286
smtk::common::TypeContainer::BadTypeError
Definition: TypeContainer.h:67
smtk::common::TypeContainer::insert
bool insert(const Type &value)
Insert a Type instance into the TypeContainer.
Definition: TypeContainer.h:128
smtk::common::TypeContainer::size
std::size_t size() const noexcept
Return the nubmer of objects held by the container.
Definition: TypeContainer.h:231
smtk::string::Token
A string token identified by an integer.
Definition: Token.h:30
smtk::common::TypeContainer::TypeContainer
TypeContainer(const Arg &arg, const Args &... args)
Construct a TypeContainer instance from any number of elements.
Definition: TypeContainer.h:101
smtk::common::TypeContainer::keys
std::set< smtk::string::Token > keys() const
Return a set of keys corresponding to the values in the container.
Definition: TypeContainer.h:242
smtk::common::TypeContainer
A container for caching and retrieving instances of types.
Definition: TypeContainer.h:37