Chemical Data Processing Library C++ API - Version 1.2.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 <cstring>
34 #include <typeinfo>
35 #include <type_traits>
36 #include <stdexcept>
37 #include <utility>
38 #include <new>
39 
40 #include "CDPL/Base/Exceptions.hpp"
41 
42 
43 namespace CDPL
44 {
45 
46  namespace Base
47  {
48 
59  class Any final
60  {
61 
62  public:
67  Any() noexcept : vtable(nullptr) {}
68 
73  Any(const Any& rhs):
74  vtable(rhs.vtable)
75  {
76  if (!rhs.isEmpty())
77  rhs.vtable->copy(rhs.storage, this->storage);
78  }
79 
87  Any(Any&& rhs) noexcept : vtable(rhs.vtable)
88  {
89  if (!rhs.isEmpty()) {
90  rhs.vtable->move(rhs.storage, this->storage);
91  rhs.vtable = nullptr;
92  }
93  }
94 
101  {
102  this->clear();
103  }
104 
113  template <typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, Any>::value>::type>
114  Any(ValueType&& val)
115  {
116  static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
117  "ValueType shall satisfy CopyConstructible requirements.");
118 
119  this->construct(std::forward<ValueType>(val));
120  }
121 
127  Any& operator=(const Any& rhs)
128  {
129  Any(rhs).swap(*this);
130  return *this;
131  }
132 
141  Any& operator=(Any&& rhs) noexcept
142  {
143  Any(std::move(rhs)).swap(*this);
144  return *this;
145  }
146 
156  template <typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, Any>::value>::type>
157  Any& operator=(ValueType&& val)
158  {
159  static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
160  "T shall satisfy CopyConstructible requirements.");
161 
162  Any(std::forward<ValueType>(val)).swap(*this);
163  return *this;
164  }
165 
170  void clear() noexcept
171  {
172  if (!isEmpty()) {
173  this->vtable->destroy(storage);
174  this->vtable = nullptr;
175  }
176  }
177 
183  bool isEmpty() const noexcept
184  {
185  return (this->vtable == nullptr);
186  }
187 
194  const std::type_info& getTypeID() const noexcept
195  {
196  return (isEmpty() ? typeid(void) : this->vtable->type());
197  }
198 
204  void swap(Any& rhs) noexcept
205  {
206  if (this->vtable != rhs.vtable) {
207  Any tmp(std::move(rhs));
208 
209  // move from *this to rhs.
210  rhs.vtable = this->vtable;
211 
212  if (this->vtable != nullptr) {
213  this->vtable->move(this->storage, rhs.storage);
214  //this->vtable = nullptr; -- unneeded, see below
215  }
216 
217  // move from tmp (previously rhs) to *this.
218  this->vtable = tmp.vtable;
219 
220  if (tmp.vtable != nullptr) {
221  tmp.vtable->move(tmp.storage, this->storage);
222  tmp.vtable = nullptr;
223  }
224 
225  } else { // same types
226  if (this->vtable != nullptr)
227  this->vtable->swap(this->storage, rhs.storage);
228  }
229  }
230 
237  template <typename ValueType>
238  const ValueType& getData() const
239  {
240  if (this->isEmpty())
241  throw BadCast("Any: empty");
242 
243  using T = typename std::decay<ValueType>::type;
244 
245  if (this->isTyped(typeid(T)))
246  return *this->cast<T>();
247 
248  throw BadCast(std::string("Any: extraction of stored value of type '") + this->getTypeID().name() +
249  std::string("' as type '") + typeid(T).name() + "' not possible");
250  }
251 
258  const void* getDataPointer() const noexcept
259  {
260  if (isEmpty())
261  return nullptr;
262 
263  return this->vtable->data(this->storage);
264  }
265 
266  private:
267  // Storage and Virtual Method Table
268  union Storage
269  {
270  using StackStorageT = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
271 
272  void* dynamic;
273  StackStorageT stack; // 2 words for e.g. shared_ptr
274  };
275 
276  // Base VTable specification.
277  struct VTable
278  {
279  // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
280  // such as destroy() and/or move().
281 
282  // The type of the object this vtable is for.
283  const std::type_info& (*type)() noexcept;
284 
285  // Destroys the object in the union.
286  // The state of the union after this call is unspecified, caller must ensure not to use src anymore.
287  void (*destroy)(Storage&) noexcept;
288 
289  // Copies the **inner** content of the src union into the yet unitialized dest union.
290  // As such, both inner objects will have the same state, but on separate memory locations.
291  void (*copy)(const Storage& src, Storage& dest);
292 
293  // Moves the storage from src to the yet unitialized dest union.
294  // The state of src after this call is unspecified, caller must ensure not to use src anymore.
295  void (*move)(Storage& src, Storage& dest) noexcept;
296 
297  // Exchanges the storage between lhs and rhs.
298  void (*swap)(Storage& lhs, Storage& rhs) noexcept;
299 
300  const void* (*data)(const Storage& storage)noexcept;
301  };
302 
303  // VTable for dynamically allocated storage.
304  template <typename T>
305  struct VTableDynamic
306  {
307 
308  static const std::type_info& type() noexcept
309  {
310  return typeid(T);
311  }
312 
313  static void destroy(Storage& storage) noexcept
314  {
315  //assert(reinterpret_cast<T*>(storage.dynamic));
316  delete reinterpret_cast<T*>(storage.dynamic);
317  }
318 
319  static void copy(const Storage& src, Storage& dest)
320  {
321  dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
322  }
323 
324  static void move(Storage& src, Storage& dest) noexcept
325  {
326  dest.dynamic = src.dynamic;
327  src.dynamic = nullptr;
328  }
329 
330  static void swap(Storage& lhs, Storage& rhs) noexcept
331  {
332  // just exchage the storage pointers.
333  std::swap(lhs.dynamic, rhs.dynamic);
334  }
335 
336  static const void* data(const Storage& storage) noexcept
337  {
338  return storage.dynamic;
339  }
340  };
341 
342  // VTable for stack allocated storage.
343  template <typename T>
344  struct VTableStack
345  {
346 
347  static const std::type_info& type() noexcept
348  {
349  return typeid(T);
350  }
351 
352  static void destroy(Storage& storage) noexcept
353  {
354  reinterpret_cast<T*>(&storage.stack)->~T();
355  }
356 
357  static void copy(const Storage& src, Storage& dest)
358  {
359  new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
360  }
361 
362  static void move(Storage& src, Storage& dest) noexcept
363  {
364  // one of the conditions for using VTableStack is a nothrow move constructor,
365  // so this move constructor will never throw a exception.
366  new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
367  destroy(src);
368  }
369 
370  static void swap(Storage& lhs, Storage& rhs) noexcept
371  {
372  Storage tmp_storage;
373 
374  move(rhs, tmp_storage);
375  move(lhs, rhs);
376  move(tmp_storage, lhs);
377  }
378 
379  static const void* data(const Storage& storage) noexcept
380  {
381  return &storage.stack;
382  }
383  };
384 
385  // Whether the type T must be dynamically allocated or can be stored on the stack.
386  template <typename T>
387  struct RequiresAlloc : std::integral_constant<bool,
388  !(std::is_nothrow_move_constructible<T>::value // N4562 ยง6.3/3 [any.class]
389  && sizeof(T) <= sizeof(Storage::stack) && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorageT>::value)>
390  {};
391 
392  // Returns the pointer to the vtable of the type T.
393  template <typename T>
394  static VTable* getVTableForType()
395  {
396 
397  using VTableType = typename std::conditional<RequiresAlloc<T>::value, VTableDynamic<T>, VTableStack<T> >::type;
398 
399  static VTable table = {
400  VTableType::type,
401  VTableType::destroy,
402  VTableType::copy,
403  VTableType::move,
404  VTableType::swap,
405  VTableType::data,
406  };
407 
408  return &table;
409  }
410 
411  // Same effect as is_same(this->getTypeID(), t);
412  bool isTyped(const std::type_info& t) const
413  {
414  if (&this->getTypeID() == &t)
415  return true;
416 
417  auto n1 = this->getTypeID().name();
418  auto n2 = t.name();
419 
420  if (n1 == n2)
421  return true;
422 
423  return (std::strcmp(this->getTypeID().name(), t.name()) == 0);
424  }
425 
426  // Casts (with no type_info checks) the storage pointer as const T*.
427  template <typename T>
428  const T* cast() const noexcept
429  {
430  return RequiresAlloc<T>::value ? reinterpret_cast<const T*>(storage.dynamic) :
431  reinterpret_cast<const T*>(&storage.stack);
432  }
433 
434  template <typename ValueType, typename T>
435  typename std::enable_if<RequiresAlloc<T>::value>::type
436  doConstruct(ValueType&& value)
437  {
438  storage.dynamic = new T(std::forward<ValueType>(value));
439  }
440 
441  template <typename ValueType, typename T>
442  typename std::enable_if<!RequiresAlloc<T>::value>::type
443  doConstruct(ValueType&& value)
444  {
445  new (&storage.stack) T(std::forward<ValueType>(value));
446  }
447 
448  // Chooses between stack and dynamic allocation for the type decay_t<ValueType>,
449  // assigns the correct vtable, and constructs the object on our storage.
450  template <typename ValueType>
451  void construct(ValueType&& value)
452  {
453  using T = typename std::decay<ValueType>::type;
454 
455  this->vtable = getVTableForType<T>();
456 
457  doConstruct<ValueType, T>(std::forward<ValueType>(value));
458  }
459 
460  Storage storage; // on offset(0) so no padding for align
461  VTable* vtable;
462  };
463 
464  template <>
465  inline const Any& Any::getData<Any>() const
466  {
467  return *this;
468  }
469  } // namespace Base
470 } // namespace CDPL
471 
472 
473 // \cond DOC_IMPL_DETAILS
474 
475 namespace std
476 {
477 
478  inline void swap(CDPL::Base::Any& lhs, CDPL::Base::Any& rhs) noexcept
479  {
480  lhs.swap(rhs);
481  }
482 } // namespace std
483 
484 // \endcond
485 
486 #endif // CDPL_BASE_ANY_HPP
Definition of exception classes.
A safe, type checked container for arbitrary data of variable type.
Definition: Any.hpp:60
const ValueType & getData() const
Returns a const reference to the stored object of type ValueType, or *this if ValueType is Any.
Definition: Any.hpp:238
void swap(Any &rhs) noexcept
Exchange the states of *this and rhs.
Definition: Any.hpp:204
~Any()
Destructor.
Definition: Any.hpp:100
Any & operator=(const Any &rhs)
Has the same effect as Any(rhs).swap(*this). No effects if an exception is thrown.
Definition: Any.hpp:127
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:157
Any(Any &&rhs) noexcept
Constructs an object of type Any with a state equivalent to the original state of rhs.
Definition: Any.hpp:87
Any(const Any &rhs)
Constructs an object of type Any with an equivalent state as rhs.
Definition: Any.hpp:73
void clear() noexcept
If not empty, destroys the contained object.
Definition: Any.hpp:170
const void * getDataPointer() const noexcept
Returns a raw pointer to the memory occupied by the stored data.
Definition: Any.hpp:258
Any & operator=(Any &&rhs) noexcept
Has the same effect as Any(std::move(rhs)).swap(*this).
Definition: Any.hpp:141
const std::type_info & getTypeID() const noexcept
Returns information about the type of the stored object.
Definition: Any.hpp:194
Any() noexcept
Constructs an object of type Any with an empty state.
Definition: Any.hpp:67
bool isEmpty() const noexcept
Tells whether the Any instance stores any data.
Definition: Any.hpp:183
Any(ValueType &&val)
Constructs an object of type Any that contains an object of type ValueType direct-initialized with st...
Definition: Any.hpp:114
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
constexpr unsigned int T
Specifies Hydrogen (Tritium).
Definition: AtomType.hpp:67
The namespace of the Chemical Data Processing Library.