Nugget
any.h
1 // Copyright (c) Electronic Arts Inc. All rights reserved.
4 
5 
7 // This file implements the eastl::any which is part of the C++ standard STL
8 // library specification.
9 //
10 // eastl::any is a type-safe container for single values of any type. Our
11 // implementation makes use of the "small local buffer" optimization to avoid
12 // unnecessary dynamic memory allocation if the specified type is eligible to
13 // be stored in its local buffer. The user type must satisfy the size
14 // requirements and must be no-throw move-constructible to qualify for the local
15 // buffer optimization.
16 //
17 // To consider: Implement a fixed_any<SIZE> variant to allow users to customize
18 // the size of the "small local buffer" optimization.
19 //
20 // http://en.cppreference.com/w/cpp/utility/any
22 
23 
24 #ifndef EASTL_ANY_H
25 #define EASTL_ANY_H
26 
27 #if defined(EA_PRAGMA_ONCE_SUPPORTED)
28  #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
29 #endif
30 
31 #include <EASTL/internal/config.h>
32 #include <EASTL/internal/in_place_t.h>
33 #if EASTL_RTTI_ENABLED
34  #include <typeinfo>
35 #endif
36 #if EASTL_EXCEPTIONS_ENABLED
37  #include <exception>
38 #endif
39 
40 
41 namespace eastl
42 {
44  // bad_any_cast
45  //
46  // The type thrown by any_cast on failure.
47  //
48  // http://en.cppreference.com/w/cpp/utility/any/bad_any_cast
49  //
50  #if EASTL_EXCEPTIONS_ENABLED
51  struct bad_cast : std::exception
52  {
53  const char* what() const EA_NOEXCEPT EA_OVERRIDE
54  { return "bad cast"; }
55  };
56 
57  struct bad_any_cast : public bad_cast
58  {
59  const char* what() const EA_NOEXCEPT EA_OVERRIDE
60  { return "bad_any_cast"; }
61  };
62  #endif
63 
64  namespace Internal
65  {
66  // utility to switch between exceptions and asserts
67  inline void DoBadAnyCast()
68  {
69  #if EASTL_EXCEPTIONS_ENABLED
70  throw bad_any_cast();
71  #else
72  EASTL_ASSERT_MSG(false, "bad_any_cast\n");
73 
74  // NOTE(rparolin): CRASH!
75  // You crashed here because you requested a type that was not contained in the object.
76  // We choose to intentionally crash here instead of returning invalid data to the calling
77  // code which could cause hard to track down bugs.
78  *((volatile int*)0) = 0xDEADC0DE;
79  #endif
80  }
81 
82  template<typename T, typename... Args>
83  void* DefaultConstruct(Args&&... args)
84  {
85  auto* pMem = EASTLAllocatorDefault()->allocate(sizeof(T), alignof(T), 0);
86 
87  return ::new(pMem) T(eastl::forward<Args>(args)...);
88  }
89 
90  template<typename T>
91  void DefaultDestroy(T* p)
92  {
93  p->~T();
94 
95  EASTLAllocatorDefault()->deallocate(static_cast<void*>(p), sizeof(T));
96  }
97  }
98 
99 
101  // 20.7.3, class any
102  //
103  class any
104  {
106  // storage_operation
107  //
108  // operations supported by the storage handler
109  //
110  enum class storage_operation
111  {
112  GET,
113  DESTROY,
114  COPY,
115  MOVE,
116  TYPE_INFO
117  };
118 
119 
121  // storage
122  //
123  // the underlying storage type which enables the switching between objects stored in
124  // the heap and objects stored within the any type.
125  //
126  union storage
127  {
128  typedef aligned_storage_t<4 * sizeof(void*), alignment_of<void*>::value> internal_storage_t;
129 
130  void* external_storage = nullptr;
131  internal_storage_t internal_storage;
132  };
133 
134 
136  // use_internal_storage
137  //
138  // determines when the "local buffer optimization" is used
139  //
140  template <typename T>
142  <
144  && (sizeof(T) <= sizeof(storage)) &&
146  >;
147 
148 
150  // non-member friend functions
151  //
152  template <class ValueType> friend const ValueType* any_cast(const any* pAny) EA_NOEXCEPT;
153  template <class ValueType> friend ValueType* any_cast(any* pAny) EA_NOEXCEPT;
154  template <class ValueType> friend ValueType any_cast(const any& operand);
155  template <class ValueType> friend ValueType any_cast(any& operand);
156  template <class ValueType> friend ValueType any_cast(any&& operand);
157 
158  //Adding Unsafe any cast operations
159  template <class ValueType> friend const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT;
160  template <class ValueType> friend ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT;
161 
162 
164  // internal storage handler
165  //
166  template <typename T>
167  struct storage_handler_internal
168  {
169  template <typename V>
170  static void construct(storage& s, V&& v)
171  {
172  ::new(&s.internal_storage) T(eastl::forward<V>(v));
173  }
174 
175  template <typename... Args>
176  static void construct_inplace(storage& s, Args... args)
177  {
178  ::new(&s.internal_storage) T(eastl::forward<Args>(args)...);
179  }
180 
181  template <class NT, class U, class... Args>
182  static void construct_inplace(storage& s, std::initializer_list<U> il, Args&&... args)
183  {
184  ::new(&s.internal_storage) NT(il, eastl::forward<Args>(args)...);
185  }
186 
187  static inline void destroy(any& refAny)
188  {
189  T& t = *static_cast<T*>(static_cast<void*>(&refAny.m_storage.internal_storage));
190  EA_UNUSED(t);
191  t.~T();
192 
193  refAny.m_handler = nullptr;
194  }
195 
196  static void* handler_func(storage_operation op, const any* pThis, any* pOther)
197  {
198  switch (op)
199  {
200  case storage_operation::GET:
201  {
202  EASTL_ASSERT(pThis);
203  return (void*)(&pThis->m_storage.internal_storage);
204  }
205  break;
206 
207  case storage_operation::DESTROY:
208  {
209  EASTL_ASSERT(pThis);
210  destroy(const_cast<any&>(*pThis));
211  }
212  break;
213 
214  case storage_operation::COPY:
215  {
216  EASTL_ASSERT(pThis);
217  EASTL_ASSERT(pOther);
218  construct(pOther->m_storage, *(T*)(&pThis->m_storage.internal_storage));
219  }
220  break;
221 
222  case storage_operation::MOVE:
223  {
224  EASTL_ASSERT(pThis);
225  EASTL_ASSERT(pOther);
226  construct(pOther->m_storage, eastl::move(*(T*)(&pThis->m_storage.internal_storage)));
227  destroy(const_cast<any&>(*pThis));
228  }
229  break;
230 
231  case storage_operation::TYPE_INFO:
232  {
233  #if EASTL_RTTI_ENABLED
234  return (void*)&typeid(T);
235  #endif
236  }
237  break;
238 
239  default:
240  {
241  EASTL_ASSERT_MSG(false, "unknown storage operation\n");
242  }
243  break;
244  };
245 
246  return nullptr;
247  }
248  };
249 
250 
251 
253  // external storage handler
254  //
255  template <typename T>
256  struct storage_handler_external
257  {
258  template <typename V>
259  static inline void construct(storage& s, V&& v)
260  {
261  s.external_storage = Internal::DefaultConstruct<T>(eastl::forward<V>(v));
262  }
263 
264  template <typename... Args>
265  static inline void construct_inplace(storage& s, Args... args)
266  {
267  s.external_storage = Internal::DefaultConstruct<T>(eastl::forward<Args>(args)...);
268  }
269 
270  template <class NT, class U, class... Args>
271  static inline void construct_inplace(storage& s, std::initializer_list<U> il, Args&&... args)
272  {
273  s.external_storage = Internal::DefaultConstruct<NT>(il, eastl::forward<Args>(args)...);
274  }
275 
276  static inline void destroy(any& refAny)
277  {
278  Internal::DefaultDestroy(static_cast<T*>(refAny.m_storage.external_storage));
279 
280  refAny.m_handler = nullptr;
281  }
282 
283  static void* handler_func(storage_operation op, const any* pThis, any* pOther)
284  {
285  switch (op)
286  {
287  case storage_operation::GET:
288  {
289  EASTL_ASSERT(pThis);
290  EASTL_ASSERT(pThis->m_storage.external_storage);
291  return static_cast<void*>(pThis->m_storage.external_storage);
292  }
293  break;
294 
295  case storage_operation::DESTROY:
296  {
297  EASTL_ASSERT(pThis);
298  destroy(*const_cast<any*>(pThis));
299  }
300  break;
301 
302  case storage_operation::COPY:
303  {
304  EASTL_ASSERT(pThis);
305  EASTL_ASSERT(pOther);
306  construct(pOther->m_storage, *static_cast<T*>(pThis->m_storage.external_storage));
307  }
308  break;
309 
310  case storage_operation::MOVE:
311  {
312  EASTL_ASSERT(pThis);
313  EASTL_ASSERT(pOther);
314  construct(pOther->m_storage, eastl::move(*(T*)(pThis->m_storage.external_storage)));
315  destroy(const_cast<any&>(*pThis));
316  }
317  break;
318 
319  case storage_operation::TYPE_INFO:
320  {
321  #if EASTL_RTTI_ENABLED
322  return (void*)&typeid(T);
323  #endif
324  }
325  break;
326 
327  default:
328  {
329  EASTL_ASSERT_MSG(false, "unknown storage operation\n");
330  }
331  break;
332  };
333 
334  return nullptr;
335  }
336  };
337 
338 
340  // storage_handler_ptr
341  //
342  // defines the function signature of the storage handler that both the internal and
343  // external storage handlers must implement to retrieve the underlying type of the any
344  // object.
345  //
346  using storage_handler_ptr = void* (*)(storage_operation, const any*, any*);
347 
348 
350  // storage_handler
351  //
352  // based on the specified type T we select the appropriate underlying storage handler
353  // based on the 'use_internal_storage' trait.
354  //
355  template <typename T>
356  using storage_handler = typename conditional<use_internal_storage<T>::value,
357  storage_handler_internal<T>,
358  storage_handler_external<T>>::type;
359 
360 
362  // data layout
363  //
364  storage m_storage;
365  storage_handler_ptr m_handler;
366 
367  public:
368  #ifndef EA_COMPILER_GNUC
369  // TODO(rparolin): renable constexpr for GCC
370  EA_CONSTEXPR
371  #endif
372  any() EA_NOEXCEPT
373  : m_storage(), m_handler(nullptr) {}
374 
375  any(const any& other) : m_handler(nullptr)
376  {
377  if (other.m_handler)
378  {
379  // NOTE(rparolin): You can not simply copy the underlying
380  // storage because it could hold a pointer to an object on the
381  // heap which breaks the copy semantics of the language.
382  other.m_handler(storage_operation::COPY, &other, this);
383  m_handler = other.m_handler;
384  }
385  }
386 
387  any(any&& other) EA_NOEXCEPT : m_handler(nullptr)
388  {
389  if(other.m_handler)
390  {
391  // NOTE(rparolin): You can not simply move the underlying
392  // storage because because the storage class has effectively
393  // type erased user type so we have to defer to the handler
394  // function to get the type back and pass on the move request.
395  m_handler = eastl::move(other.m_handler);
396  other.m_handler(storage_operation::MOVE, &other, this);
397  }
398  }
399 
400  ~any() { reset(); }
401 
402  template <class ValueType>
403  any(ValueType&& value,
404  typename eastl::enable_if<!eastl::is_same<typename eastl::decay<ValueType>::type, any>::value>::type* = 0)
405  {
406  typedef decay_t<ValueType> DecayedValueType;
407  static_assert(is_copy_constructible<DecayedValueType>::value, "ValueType must be copy-constructible");
408  storage_handler<DecayedValueType>::construct(m_storage, eastl::forward<ValueType>(value));
409  m_handler = &storage_handler<DecayedValueType>::handler_func;
410  }
411 
412  template <class T, class... Args>
413  explicit any(in_place_type_t<T>, Args&&... args)
414  {
415  typedef storage_handler<decay_t<T>> StorageHandlerT;
416  static_assert(eastl::is_constructible<T, Args...>::value, "T must be constructible with Args...");
417 
418  StorageHandlerT::construct_inplace(m_storage, eastl::forward<Args>(args)...);
419  m_handler = &StorageHandlerT::handler_func;
420  }
421 
422  template <class T, class U, class... Args>
423  explicit any(in_place_type_t<T>,
425  Args&&... args,
427  void>::type* = 0)
428  {
429  typedef storage_handler<decay_t<T>> StorageHandlerT;
430 
431  StorageHandlerT::construct_inplace(m_storage, il, eastl::forward<Args>(args)...);
432  m_handler = &StorageHandlerT::handler_func;
433  }
434 
435  // 20.7.3.2, assignments
436  template <class ValueType>
437  any& operator=(ValueType&& value)
438  {
439  static_assert(is_copy_constructible<decay_t<ValueType>>::value, "ValueType must be copy-constructible");
440  any(eastl::forward<ValueType>(value)).swap(*this);
441  return *this;
442  }
443 
444  any& operator=(const any& other)
445  {
446  any(other).swap(*this);
447  return *this;
448  }
449 
450  any& operator=(any&& other) EA_NOEXCEPT
451  {
452  any(eastl::move(other)).swap(*this);
453  return *this;
454  }
455 
456  // 20.7.3.3, modifiers
457  #if EASTL_VARIADIC_TEMPLATES_ENABLED
458  template <class T, class... Args>
459  void emplace(Args&&... args)
460  {
461  typedef storage_handler<decay_t<T>> StorageHandlerT;
462  static_assert(eastl::is_constructible<T, Args...>::value, "T must be constructible with Args...");
463 
464  reset();
465  StorageHandlerT::construct_inplace(m_storage, eastl::forward<Args>(args)...);
466  m_handler = &StorageHandlerT::handler_func;
467  }
468 
469  template <class NT, class U, class... Args>
470  typename eastl::enable_if<eastl::is_constructible<NT, std::initializer_list<U>&, Args...>::value, void>::type
471  emplace(std::initializer_list<U> il, Args&&... args)
472  {
473  typedef storage_handler<decay_t<NT>> StorageHandlerT;
474 
475  reset();
476  StorageHandlerT::construct_inplace(m_storage, il, eastl::forward<Args>(args)...);
477  m_handler = &StorageHandlerT::handler_func;
478  }
479  #endif
480 
481  void reset() EA_NOEXCEPT
482  {
483  if(m_handler)
484  m_handler(storage_operation::DESTROY, this, nullptr);
485  }
486 
487  void swap(any& other) EA_NOEXCEPT
488  {
489  if(this == &other)
490  return;
491 
492  if(m_handler && other.m_handler)
493  {
494  any tmp;
495  tmp.m_handler = other.m_handler;
496  other.m_handler(storage_operation::MOVE, &other, &tmp);
497 
498  other.m_handler = m_handler;
499  m_handler(storage_operation::MOVE, this, &other);
500 
501  m_handler = tmp.m_handler;
502  tmp.m_handler(storage_operation::MOVE, &tmp, this);
503  }
504  else if (m_handler == nullptr && other.m_handler)
505  {
506  eastl::swap(m_handler, other.m_handler);
507  m_handler(storage_operation::MOVE, &other, this);
508  }
509  else if(m_handler && other.m_handler == nullptr)
510  {
511  eastl::swap(m_handler, other.m_handler);
512  other.m_handler(storage_operation::MOVE, this, &other);
513  }
514  //else if (m_handler == nullptr && other.m_handler == nullptr)
515  //{
516  // // nothing to swap
517  //}
518  }
519 
520  // 20.7.3.4, observers
521  bool has_value() const EA_NOEXCEPT { return m_handler != nullptr; }
522 
523  #if EASTL_RTTI_ENABLED
524  inline const std::type_info& type() const EA_NOEXCEPT
525  {
526  if(m_handler)
527  {
528  auto* pTypeInfo = m_handler(storage_operation::TYPE_INFO, this, nullptr);
529  return *static_cast<const std::type_info*>(pTypeInfo);
530  }
531  else
532  {
533  return typeid(void);
534  }
535  }
536  #endif
537  };
538 
539 
540 
542  // 20.7.4, non-member functions
543  //
544  inline void swap(any& rhs, any& lhs) EA_NOEXCEPT { rhs.swap(lhs); }
545 
546 
548  // 20.7.4, The non-member any_cast functions provide type-safe access to the contained object.
549  //
550  template <class ValueType>
551  inline ValueType any_cast(const any& operand)
552  {
554  "ValueType must be a reference or copy constructible");
555 
556  auto* p = any_cast<typename add_const<typename remove_reference<ValueType>::type>::type>(&operand);
557 
558  if(p == nullptr)
559  Internal::DoBadAnyCast();
560 
561  return *p;
562  }
563 
564  template <class ValueType>
565  inline ValueType any_cast(any& operand)
566  {
568  "ValueType must be a reference or copy constructible");
569 
570  auto* p = any_cast<typename remove_reference<ValueType>::type>(&operand);
571 
572  if(p == nullptr)
573  Internal::DoBadAnyCast();
574 
575  return *p;
576  }
577 
578  template <class ValueType>
579  inline ValueType any_cast(any&& operand)
580  {
582  "ValueType must be a reference or copy constructible");
583 
584  auto* p = any_cast<typename remove_reference<ValueType>::type>(&operand);
585 
586  if (p == nullptr)
587  Internal::DoBadAnyCast();
588 
589  return *p;
590  }
591 
592  // NOTE(rparolin): The runtime type check was commented out because in DLL builds the templated function pointer
593  // value will be different -- completely breaking the validation mechanism. Due to the fact that eastl::any uses
594  // type erasure we can't refresh (on copy/move) the cached function pointer to the internal handler function because
595  // we don't statically know the type.
596  template <class ValueType>
597  inline const ValueType* any_cast(const any* pAny) EA_NOEXCEPT
598  {
599  return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler<decay_t<ValueType>>::handler_func)
600  #if EASTL_RTTI_ENABLED
601  && pAny->type() == typeid(typename remove_reference<ValueType>::type)
602  #endif
603  ) ?
604  static_cast<const ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) :
605  nullptr;
606  }
607 
608  template <class ValueType>
609  inline ValueType* any_cast(any* pAny) EA_NOEXCEPT
610  {
611  return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler<decay_t<ValueType>>::handler_func)
612  #if EASTL_RTTI_ENABLED
613  && pAny->type() == typeid(typename remove_reference<ValueType>::type)
614  #endif
615  ) ?
616  static_cast<ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) :
617  nullptr;
618  }
619 
620  //Unsafe operations - use with caution
621  template <class ValueType>
622  inline const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT
623  {
624  return unsafe_any_cast<ValueType>(const_cast<any*>(pAny));
625  }
626 
627  template <class ValueType>
628  inline ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT
629  {
630  return static_cast<ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr));
631  }
632 
634  // make_any
635  //
636  #if EASTL_VARIADIC_TEMPLATES_ENABLED
637  template <class T, class... Args>
638  inline any make_any(Args&&... args)
639  {
640  return any(eastl::in_place<T>, eastl::forward<Args>(args)...);
641  }
642 
643  template <class T, class U, class... Args>
644  inline any make_any(std::initializer_list<U> il, Args&&... args)
645  {
646  return any(eastl::in_place<T>, il, eastl::forward<Args>(args)...);
647  }
648  #endif
649 
650 } // namespace eastl
651 
652 #endif // EASTL_ANY_H
Definition: any.h:104
Definition: initializer_list.h:38
EA Standard Template Library.
Definition: algorithm.h:288
OutputIterator move(InputIterator first, InputIterator last, OutputIterator result)
Definition: copy_help.h:170
void destroy(ForwardIterator first, ForwardIterator last)
Definition: memory.h:1395
Definition: type_properties.h:234
Definition: type_traits.h:473
Definition: type_traits.h:494
Definition: type_traits.h:442
Definition: type_traits.h:263
Definition: type_pod.h:792
Definition: type_pod.h:1175
Definition: type_pod.h:1934
Definition: type_traits.h:669
Definition: type_traits.h:604