Chemical Data Processing Library C++ API - Version 1.1.1
Any.hpp
Go to the documentation of this file.
1 /*
2  * Any.hpp
3  *
4  * This file is part of the Chemical Data Processing Toolkit
5  *
6  * Copyright (C) 2003 Thomas Seidel <thomas.seidel@univie.ac.at>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this library; see the file COPYING. If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
29 #ifndef CDPL_BASE_ANY_HPP
30 #define CDPL_BASE_ANY_HPP
31 
32 #include <string>
33 #include <typeinfo>
34 #include <type_traits>
35 #include <stdexcept>
36 #include <utility>
37 #include <new>
38 
39 #include "CDPL/Base/Exceptions.hpp"
40 
41 
42 namespace CDPL
43 {
44 
45  namespace Base
46  {
47 
58  class Any final
59  {
60 
61  public:
66  Any() noexcept : vtable(nullptr) {}
67 
72  Any(const Any& rhs):
73  vtable(rhs.vtable)
74  {
75  if (!rhs.isEmpty())
76  rhs.vtable->copy(rhs.storage, this->storage);
77  }
78 
86  Any(Any&& rhs) noexcept : vtable(rhs.vtable)
87  {
88  if (!rhs.isEmpty()) {
89  rhs.vtable->move(rhs.storage, this->storage);
90  rhs.vtable = nullptr;
91  }
92  }
93 
99  ~Any()
100  {
101  this->clear();
102  }
103 
112  template <typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, Any>::value>::type>
113  Any(ValueType&& val)
114  {
115  static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
116  "ValueType shall satisfy CopyConstructible requirements.");
117 
118  this->construct(std::forward<ValueType>(val));
119  }
120 
126  Any& operator=(const Any& rhs)
127  {
128  Any(rhs).swap(*this);
129  return *this;
130  }
131 
140  Any& operator=(Any&& rhs) noexcept
141  {
142  Any(std::move(rhs)).swap(*this);
143  return *this;
144  }
145 
155  template <typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, Any>::value>::type>
156  Any& operator=(ValueType&& val)
157  {
158  static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
159  "T shall satisfy CopyConstructible requirements.");
160 
161  Any(std::forward<ValueType>(val)).swap(*this);
162  return *this;
163  }
164 
169  void clear() noexcept
170  {
171  if (!isEmpty()) {
172  this->vtable->destroy(storage);
173  this->vtable = nullptr;
174  }
175  }
176 
182  bool isEmpty() const noexcept
183  {
184  return (this->vtable == nullptr);
185  }
186 
193  const std::type_info& getTypeID() const noexcept
194  {
195  return (isEmpty() ? typeid(void) : this->vtable->type());
196  }
197 
203  void swap(Any& rhs) noexcept
204  {
205  if (this->vtable != rhs.vtable) {
206  Any tmp(std::move(rhs));
207 
208  // move from *this to rhs.
209  rhs.vtable = this->vtable;
210 
211  if (this->vtable != nullptr) {
212  this->vtable->move(this->storage, rhs.storage);
213  //this->vtable = nullptr; -- unneeded, see below
214  }
215 
216  // move from tmp (previously rhs) to *this.
217  this->vtable = tmp.vtable;
218 
219  if (tmp.vtable != nullptr) {
220  tmp.vtable->move(tmp.storage, this->storage);
221  tmp.vtable = nullptr;
222  }
223 
224  } else { // same types
225  if (this->vtable != nullptr)
226  this->vtable->swap(this->storage, rhs.storage);
227  }
228  }
229 
236  template <typename ValueType>
237  const ValueType& getData() const
238  {
239  if (this->isEmpty())
240  throw BadCast("Any: empty");
241 
242  using T = typename std::decay<ValueType>::type;
243 
244  if (this->isTyped(typeid(T)))
245  return *this->cast<T>();
246 
247  throw BadCast(std::string("Any: extraction of stored value of type '") + this->getTypeID().name() +
248  std::string("' as type '") + typeid(T).name() + "' not possible");
249  }
250 
257  const void* getDataPointer() const noexcept
258  {
259  if (isEmpty())
260  return nullptr;
261 
262  return this->vtable->data(this->storage);
263  }
264 
265  private:
266  // Storage and Virtual Method Table
267  union Storage
268  {
269  using StackStorageT = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
270 
271  void* dynamic;
272  StackStorageT stack; // 2 words for e.g. shared_ptr
273  };
274 
275  // Base VTable specification.
276  struct VTable
277  {
278  // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
279  // such as destroy() and/or move().
280 
281  // The type of the object this vtable is for.
282  const std::type_info& (*type)() noexcept;
283 
284  // Destroys the object in the union.
285  // The state of the union after this call is unspecified, caller must ensure not to use src anymore.
286  void (*destroy)(Storage&) noexcept;
287 
288  // Copies the **inner** content of the src union into the yet unitialized dest union.
289  // As such, both inner objects will have the same state, but on separate memory locations.
290  void (*copy)(const Storage& src, Storage& dest);
291 
292  // Moves the storage from src to the yet unitialized dest union.
293  // The state of src after this call is unspecified, caller must ensure not to use src anymore.
294  void (*move)(Storage& src, Storage& dest) noexcept;
295 
296  // Exchanges the storage between lhs and rhs.
297  void (*swap)(Storage& lhs, Storage& rhs) noexcept;
298 
299  const void* (*data)(const Storage& storage)noexcept;
300  };
301 
302  // VTable for dynamically allocated storage.
303  template <typename T>
304  struct VTableDynamic
305  {
306 
307  static const std::type_info& type() noexcept
308  {
309  return typeid(T);
310  }
311 
312  static void destroy(Storage& storage) noexcept
313  {
314  //assert(reinterpret_cast<T*>(storage.dynamic));
315  delete reinterpret_cast<T*>(storage.dynamic);
316  }
317 
318  static void copy(const Storage& src, Storage& dest)
319  {
320  dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
321  }
322 
323  static void move(Storage& src, Storage& dest) noexcept
324  {
325  dest.dynamic = src.dynamic;
326  src.dynamic = nullptr;
327  }
328 
329  static void swap(Storage& lhs, Storage& rhs) noexcept
330  {
331  // just exchage the storage pointers.
332  std::swap(lhs.dynamic, rhs.dynamic);
333  }
334 
335  static const void* data(const Storage& storage) noexcept
336  {
337  return storage.dynamic;
338  }
339  };
340 
341  // VTable for stack allocated storage.
342  template <typename T>
343  struct VTableStack
344  {
345 
346  static const std::type_info& type() noexcept
347  {
348  return typeid(T);
349  }
350 
351  static void destroy(Storage& storage) noexcept
352  {
353  reinterpret_cast<T*>(&storage.stack)->~T();
354  }
355 
356  static void copy(const Storage& src, Storage& dest)
357  {
358  new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
359  }
360 
361  static void move(Storage& src, Storage& dest) noexcept
362  {
363  // one of the conditions for using VTableStack is a nothrow move constructor,
364  // so this move constructor will never throw a exception.
365  new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
366  destroy(src);
367  }
368 
369  static void swap(Storage& lhs, Storage& rhs) noexcept
370  {
371  Storage tmp_storage;
372 
373  move(rhs, tmp_storage);
374  move(lhs, rhs);
375  move(tmp_storage, lhs);
376  }
377 
378  static const void* data(const Storage& storage) noexcept
379  {
380  return &storage.stack;
381  }
382  };
383 
384  // Whether the type T must be dynamically allocated or can be stored on the stack.
385  template <typename T>
386  struct RequiresAlloc : std::integral_constant<bool,
387  !(std::is_nothrow_move_constructible<T>::value // N4562 ยง6.3/3 [any.class]
388  && sizeof(T) <= sizeof(Storage::stack) && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorageT>::value)>
389  {};
390 
391  // Returns the pointer to the vtable of the type T.
392  template <typename T>
393  static VTable* getVTableForType()
394  {
395 
396  using VTableType = typename std::conditional<RequiresAlloc<T>::value, VTableDynamic<T>, VTableStack<T> >::type;
397 
398  static VTable table = {
399  VTableType::type,
400  VTableType::destroy,
401  VTableType::copy,
402  VTableType::move,
403  VTableType::swap,
404  VTableType::data,
405  };
406 
407  return &table;
408  }
409 
410  // Same effect as is_same(this->getTypeID(), t);
411  bool isTyped(const std::type_info& t) const
412  {
413  return (this->getTypeID() == t);
414  }
415 
416  // Casts (with no type_info checks) the storage pointer as const T*.
417  template <typename T>
418  const T* cast() const noexcept
419  {
420  return RequiresAlloc<T>::value ? reinterpret_cast<const T*>(storage.dynamic) :
421  reinterpret_cast<const T*>(&storage.stack);
422  }
423 
424  template <typename ValueType, typename T>
425  typename std::enable_if<RequiresAlloc<T>::value>::type
426  doConstruct(ValueType&& value)
427  {
428  storage.dynamic = new T(std::forward<ValueType>(value));
429  }
430 
431  template <typename ValueType, typename T>
432  typename std::enable_if<!RequiresAlloc<T>::value>::type
433  doConstruct(ValueType&& value)
434  {
435  new (&storage.stack) T(std::forward<ValueType>(value));
436  }
437 
438  // Chooses between stack and dynamic allocation for the type decay_t<ValueType>,
439  // assigns the correct vtable, and constructs the object on our storage.
440  template <typename ValueType>
441  void construct(ValueType&& value)
442  {
443  using T = typename std::decay<ValueType>::type;
444 
445  this->vtable = getVTableForType<T>();
446 
447  doConstruct<ValueType, T>(std::forward<ValueType>(value));
448  }
449 
450  Storage storage; // on offset(0) so no padding for align
451  VTable* vtable;
452  };
453 
454  template <>
455  inline const Any& Any::getData<Any>() const
456  {
457  return *this;
458  }
459  } // namespace Base
460 } // namespace CDPL
461 
462 
463 // \cond DOC_IMPL_DETAILS
464 
465 namespace std
466 {
467 
468  inline void swap(CDPL::Base::Any& lhs, CDPL::Base::Any& rhs) noexcept
469  {
470  lhs.swap(rhs);
471  }
472 } // namespace std
473 
474 // \endcond
475 
476 #endif // CDPL_BASE_ANY_HPP
CDPL::Base::Any::getDataPointer
const void * getDataPointer() const noexcept
Returns a raw pointer to the memory occupied by the stored data.
Definition: Any.hpp:257
CDPL::Base::Any::~Any
~Any()
Destructor.
Definition: Any.hpp:99
CDPL::Base::Any::isEmpty
bool isEmpty() const noexcept
Tells whether the Any instance stores any data.
Definition: Any.hpp:182
CDPL::Base::Any::operator=
Any & operator=(ValueType &&val)
Has the same effect as Any(std::forward<ValueType>(val)).swap(*this). No effect if a exception is thr...
Definition: Any.hpp:156
CDPL::Base::Any::Any
Any(Any &&rhs) noexcept
Constructs an object of type Any with a state equivalent to the original state of rhs.
Definition: Any.hpp:86
CDPL::Base::Any::Any
Any(const Any &rhs)
Constructs an object of type Any with an equivalent state as rhs.
Definition: Any.hpp:72
CDPL::Base::Any::getTypeID
const std::type_info & getTypeID() const noexcept
Returns information about the type of the stored object.
Definition: Any.hpp:193
CDPL::Base::Any::operator=
Any & operator=(const Any &rhs)
Has the same effect as Any(rhs).swap(*this). No effects if an exception is thrown.
Definition: Any.hpp:126
CDPL::Base::BadCast
Thrown to indicate that a cast or conversion from a given source type to a requested target type is n...
Definition: Base/Exceptions.hpp:191
CDPL::Base::Any
A safe, type checked container for arbitrary data of variable type.
Definition: Any.hpp:59
CDPL::Chem::AtomType::T
const unsigned int T
Specifies Hydrogen (Tritium).
Definition: AtomType.hpp:67
CDPL::Base::Any::operator=
Any & operator=(Any &&rhs) noexcept
Has the same effect as Any(std::move(rhs)).swap(*this).
Definition: Any.hpp:140
Exceptions.hpp
Definition of exception classes.
CDPL
The namespace of the Chemical Data Processing Library.
CDPL::Base::Any::Any
Any(ValueType &&val)
Constructs an object of type Any that contains an object of type ValueType direct-initialized with st...
Definition: Any.hpp:113
CDPL::Base::Any::getData
const ValueType & getData() const
Returns a const reference to the stored object of type ValueType, or *this if ValueType is Any.
Definition: Any.hpp:237
CDPL::Base::Any::Any
Any() noexcept
Constructs an object of type Any with an empty state.
Definition: Any.hpp:66
CDPL::Base::Any::swap
void swap(Any &rhs) noexcept
Exchange the states of *this and rhs.
Definition: Any.hpp:203
CDPL::Base::Any::clear
void clear() noexcept
If not empty, destroys the contained object.
Definition: Any.hpp:169