libnabo  1.1.2
any.hpp
1 //
2 // Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers.
3 //
4 // See also:
5 // + http://en.cppreference.com/w/cpp/any
6 // + http://en.cppreference.com/w/cpp/experimental/any
7 // + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
8 // + https://cplusplus.github.io/LWG/lwg-active.html#2509
9 //
10 //
11 // Copyright (c) 2016 Denilson das Merc�s Amorim
12 //
13 // Distributed under the Boost Software License, Version 1.0. (See accompanying
14 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
15 //
16 #ifndef LINB_ANY_HPP
17 #define LINB_ANY_HPP
18 #pragma once
19 #include <typeinfo>
20 #include <type_traits>
21 #include <stdexcept>
22 
23 namespace linb
24 {
25 
26 class bad_any_cast : public std::bad_cast
27 {
28 public:
29  const char* what() const noexcept override
30  {
31  return "bad any cast";
32  }
33 };
34 
35 class any final
36 {
37 public:
39  any() :
40  vtable(nullptr)
41  {
42  }
43 
45  any(const any& rhs) :
46  vtable(rhs.vtable)
47  {
48  if(!rhs.empty())
49  {
50  rhs.vtable->copy(rhs.storage, this->storage);
51  }
52  }
53 
56  any(any&& rhs) noexcept :
57  vtable(rhs.vtable)
58  {
59  if(!rhs.empty())
60  {
61  rhs.vtable->move(rhs.storage, this->storage);
62  rhs.vtable = nullptr;
63  }
64  }
65 
67  ~any()
68  {
69  this->clear();
70  }
71 
76  template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
77  any(ValueType&& value)
78  {
79  static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
80  "T shall satisfy the CopyConstructible requirements.");
81  this->construct(std::forward<ValueType>(value));
82  }
83 
85  any& operator=(const any& rhs)
86  {
87  any(rhs).swap(*this);
88  return *this;
89  }
90 
95  any& operator=(any&& rhs) noexcept
96  {
97  any(std::move(rhs)).swap(*this);
98  return *this;
99  }
100 
105  template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
106  any& operator=(ValueType&& value)
107  {
108  static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
109  "T shall satisfy the CopyConstructible requirements.");
110  any(std::forward<ValueType>(value)).swap(*this);
111  return *this;
112  }
113 
115  void clear() noexcept
116  {
117  if(!empty())
118  {
119  this->vtable->destroy(storage);
120  this->vtable = nullptr;
121  }
122  }
123 
125  bool empty() const noexcept
126  {
127  return this->vtable == nullptr;
128  }
129 
131  const std::type_info& type() const noexcept
132  {
133  return empty()? typeid(void) : this->vtable->type();
134  }
135 
137  void swap(any& rhs) noexcept
138  {
139  if(this->vtable != rhs.vtable)
140  {
141  any tmp(std::move(rhs));
142 
143  // move from *this to rhs.
144  rhs.vtable = this->vtable;
145  if(this->vtable != nullptr)
146  {
147  this->vtable->move(this->storage, rhs.storage);
148  //this->vtable = nullptr; -- uneeded, see below
149  }
150 
151  // move from tmp (previously rhs) to *this.
152  this->vtable = tmp.vtable;
153  if(tmp.vtable != nullptr)
154  {
155  tmp.vtable->move(tmp.storage, this->storage);
156  tmp.vtable = nullptr;
157  }
158  }
159  else // same types
160  {
161  if(this->vtable != nullptr)
162  this->vtable->swap(this->storage, rhs.storage);
163  }
164  }
165 
166 private: // Storage and Virtual Method Table
167 
168  union storage_union
169  {
170  using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
171 
172  void* dynamic;
173  stack_storage_t stack; // 2 words for e.g. shared_ptr
174  };
175 
177  struct vtable_type
178  {
179  // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
180  // such as destroy() and/or move().
181 
183  const std::type_info& (*type)() noexcept;
184 
187  void(*destroy)(storage_union&) noexcept;
188 
191  void(*copy)(const storage_union& src, storage_union& dest);
192 
195  void(*move)(storage_union& src, storage_union& dest) noexcept;
196 
198  void(*swap)(storage_union& lhs, storage_union& rhs) noexcept;
199  };
200 
202  template<typename T>
203  struct vtable_dynamic
204  {
205  static const std::type_info& type() noexcept
206  {
207  return typeid(T);
208  }
209 
210  static void destroy(storage_union& storage) noexcept
211  {
212  //assert(reinterpret_cast<T*>(storage.dynamic));
213  delete reinterpret_cast<T*>(storage.dynamic);
214  }
215 
216  static void copy(const storage_union& src, storage_union& dest)
217  {
218  dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
219  }
220 
221  static void move(storage_union& src, storage_union& dest) noexcept
222  {
223  dest.dynamic = src.dynamic;
224  src.dynamic = nullptr;
225  }
226 
227  static void swap(storage_union& lhs, storage_union& rhs) noexcept
228  {
229  // just exchage the storage pointers.
230  std::swap(lhs.dynamic, rhs.dynamic);
231  }
232  };
233 
235  template<typename T>
236  struct vtable_stack
237  {
238  static const std::type_info& type() noexcept
239  {
240  return typeid(T);
241  }
242 
243  static void destroy(storage_union& storage) noexcept
244  {
245  reinterpret_cast<T*>(&storage.stack)->~T();
246  }
247 
248  static void copy(const storage_union& src, storage_union& dest)
249  {
250  new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
251  }
252 
253  static void move(storage_union& src, storage_union& dest) noexcept
254  {
255  // one of the conditions for using vtable_stack is a nothrow move constructor,
256  // so this move constructor will never throw a exception.
257  new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
258  destroy(src);
259  }
260 
261  static void swap(storage_union& lhs, storage_union& rhs) noexcept
262  {
263  std::swap(reinterpret_cast<T&>(lhs.stack), reinterpret_cast<T&>(rhs.stack));
264  }
265  };
266 
268  template<typename T>
269  struct requires_allocation :
270  std::integral_constant<bool,
271  !(std::is_nothrow_move_constructible<T>::value // N4562 �6.3/3 [any.class]
272  && sizeof(T) <= sizeof(storage_union::stack)
273  && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)>
274  {};
275 
277  template<typename T>
278  static vtable_type* vtable_for_type()
279  {
280  using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type;
281  static vtable_type table = {
282  VTableType::type, VTableType::destroy,
283  VTableType::copy, VTableType::move,
284  VTableType::swap,
285  };
286  return &table;
287  }
288 
289 protected:
290  template<typename T>
291  friend const T* any_cast(const any* operand) noexcept;
292  template<typename T>
293  friend T* any_cast(any* operand) noexcept;
294 
296  bool is_typed(const std::type_info& t) const
297  {
298  return is_same(this->type(), t);
299  }
300 
307  static bool is_same(const std::type_info& a, const std::type_info& b)
308  {
309 #ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
310  return &a == &b;
311 #else
312  return a == b;
313 #endif
314  }
315 
317  template<typename T>
318  const T* cast() const noexcept
319  {
320  return requires_allocation<typename std::decay<T>::type>::value?
321  reinterpret_cast<const T*>(storage.dynamic) :
322  reinterpret_cast<const T*>(&storage.stack);
323  }
324 
326  template<typename T>
327  T* cast() noexcept
328  {
329  return requires_allocation<typename std::decay<T>::type>::value?
330  reinterpret_cast<T*>(storage.dynamic) :
331  reinterpret_cast<T*>(&storage.stack);
332  }
333 
334 private:
335  storage_union storage; // on offset(0) so no padding for align
336  vtable_type* vtable;
337 
338  template<typename ValueType, typename T>
339  typename std::enable_if<requires_allocation<T>::value>::type
340  do_construct(ValueType&& value)
341  {
342  storage.dynamic = new T(std::forward<ValueType>(value));
343  }
344 
345  template<typename ValueType, typename T>
346  typename std::enable_if<!requires_allocation<T>::value>::type
347  do_construct(ValueType&& value)
348  {
349  new (&storage.stack) T(std::forward<ValueType>(value));
350  }
351 
354  template<typename ValueType>
355  void construct(ValueType&& value)
356  {
357  using T = typename std::decay<ValueType>::type;
358 
359  this->vtable = vtable_for_type<T>();
360 
361  do_construct<ValueType,T>(std::forward<ValueType>(value));
362  }
363 };
364 
365 
366 
367 namespace detail
368 {
369  template<typename ValueType>
370  inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::true_type)
371  {
372  return std::move(*p);
373  }
374 
375  template<typename ValueType>
376  inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::false_type)
377  {
378  return *p;
379  }
380 }
381 
383 template<typename ValueType>
384 inline ValueType any_cast(const any& operand)
385 {
386  auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand);
387  if(p == nullptr) throw bad_any_cast();
388  return *p;
389 }
390 
392 template<typename ValueType>
393 inline ValueType any_cast(any& operand)
394 {
395  auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
396  if(p == nullptr) throw bad_any_cast();
397  return *p;
398 }
399 
409 template<typename ValueType>
410 inline ValueType any_cast(any&& operand)
411 {
412 #ifdef ANY_IMPL_ANY_CAST_MOVEABLE
413  // https://cplusplus.github.io/LWG/lwg-active.html#2509
414  using can_move = std::integral_constant<bool,
415  std::is_move_constructible<ValueType>::value
416  && !std::is_lvalue_reference<ValueType>::value>;
417 #else
418  using can_move = std::false_type;
419 #endif
420 
421  auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
422  if(p == nullptr) throw bad_any_cast();
423  return detail::any_cast_move_if_true<ValueType>(p, can_move());
424 }
425 
428 template<typename T>
429 inline const T* any_cast(const any* operand) noexcept
430 {
431  if(operand == nullptr || !operand->is_typed(typeid(T)))
432  return nullptr;
433  else
434  return operand->cast<T>();
435 }
436 
439 template<typename T>
440 inline T* any_cast(any* operand) noexcept
441 {
442  if(operand == nullptr || !operand->is_typed(typeid(T)))
443  return nullptr;
444  else
445  return operand->cast<T>();
446 }
447 
448 }
449 
450 namespace std
451 {
452  inline void swap(linb::any& lhs, linb::any& rhs) noexcept
453  {
454  lhs.swap(rhs);
455  }
456 }
457 
458 #endif
Definition: any.hpp:36
any & operator=(ValueType &&value)
Has the same effect as any(std::forward<ValueType>(value)).swap(*this).
Definition: any.hpp:106
void clear() noexcept
If not empty, destroys the contained object.
Definition: any.hpp:115
any()
Constructs an object of type any with an empty state.
Definition: any.hpp:39
any(any &&rhs) noexcept
Constructs an object of type any with a state equivalent to the original state of other.
Definition: any.hpp:56
any(const any &rhs)
Constructs an object of type any with an equivalent state as other.
Definition: any.hpp:45
void swap(any &rhs) noexcept
Exchange the states of *this and rhs.
Definition: any.hpp:137
friend const T * any_cast(const any *operand) noexcept
If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object contained by o...
Definition: any.hpp:429
bool empty() const noexcept
Returns true if *this has no contained object, otherwise false.
Definition: any.hpp:125
any & operator=(const any &rhs)
Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown.
Definition: any.hpp:85
any & operator=(any &&rhs) noexcept
Has the same effect as any(std::move(rhs)).swap(*this).
Definition: any.hpp:95
any(ValueType &&value)
Constructs an object of type any that contains an object of type T direct-initialized with std::forwa...
Definition: any.hpp:77
const std::type_info & type() const noexcept
If *this has a contained object of type T, typeid(T); otherwise typeid(void).
Definition: any.hpp:131
~any()
Same effect as this->clear().
Definition: any.hpp:67
Definition: any.hpp:27