diff options
Diffstat (limited to 'include/EASTL/any.h')
-rw-r--r-- | include/EASTL/any.h | 652 |
1 files changed, 652 insertions, 0 deletions
diff --git a/include/EASTL/any.h b/include/EASTL/any.h new file mode 100644 index 0000000..c2ef638 --- /dev/null +++ b/include/EASTL/any.h @@ -0,0 +1,652 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// This file implements the eastl::any which is part of the C++ standard STL +// library specification. +// +// eastl::any is a type-safe container for single values of any type. Our +// implementation makes use of the "small local buffer" optimization to avoid +// unnecessary dynamic memory allocation if the specified type is eligible to +// be stored in its local buffer. The user type must satisfy the size +// requirements and must be no-throw move-constructible to qualify for the local +// buffer optimization. +// +// To consider: Implement a fixed_any<SIZE> variant to allow users to customize +// the size of the "small local buffer" optimization. +// +// http://en.cppreference.com/w/cpp/utility/any +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ANY_H +#define EASTL_ANY_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #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. +#endif + +#include <EASTL/internal/config.h> +#include <EASTL/internal/in_place_t.h> +#if EASTL_RTTI_ENABLED + #include <typeinfo> +#endif +#if EASTL_EXCEPTIONS_ENABLED + #include <exception> +#endif + + +namespace eastl +{ + /////////////////////////////////////////////////////////////////////////////// + // bad_any_cast + // + // The type thrown by any_cast on failure. + // + // http://en.cppreference.com/w/cpp/utility/any/bad_any_cast + // + #if EASTL_EXCEPTIONS_ENABLED + struct bad_cast : std::exception + { + const char* what() const EA_NOEXCEPT EA_OVERRIDE + { return "bad cast"; } + }; + + struct bad_any_cast : public bad_cast + { + const char* what() const EA_NOEXCEPT EA_OVERRIDE + { return "bad_any_cast"; } + }; + #endif + + namespace Internal + { + // utility to switch between exceptions and asserts + inline void DoBadAnyCast() + { + #if EASTL_EXCEPTIONS_ENABLED + throw bad_any_cast(); + #else + EASTL_ASSERT_MSG(false, "bad_any_cast\n"); + + // NOTE(rparolin): CRASH! + // You crashed here because you requested a type that was not contained in the object. + // We choose to intentionally crash here instead of returning invalid data to the calling + // code which could cause hard to track down bugs. + *((volatile int*)0) = 0xDEADC0DE; + #endif + } + + template<typename T, typename... Args> + void* DefaultConstruct(Args&&... args) + { + auto* pMem = EASTLAllocatorDefault()->allocate(sizeof(T), alignof(T), 0); + + return ::new(pMem) T(eastl::forward<Args>(args)...); + } + + template<typename T> + void DefaultDestroy(T* p) + { + p->~T(); + + EASTLAllocatorDefault()->deallocate(static_cast<void*>(p), sizeof(T)); + } + } + + + /////////////////////////////////////////////////////////////////////////////// + // 20.7.3, class any + // + class any + { + ////////////////////////////////////////////////////////////////////////////////////////// + // storage_operation + // + // operations supported by the storage handler + // + enum class storage_operation + { + GET, + DESTROY, + COPY, + MOVE, + TYPE_INFO + }; + + + ////////////////////////////////////////////////////////////////////////////////////////// + // storage + // + // the underlying storage type which enables the switching between objects stored in + // the heap and objects stored within the any type. + // + union storage + { + typedef aligned_storage_t<4 * sizeof(void*), alignment_of<void*>::value> internal_storage_t; + + void* external_storage = nullptr; + internal_storage_t internal_storage; + }; + + + ////////////////////////////////////////////////////////////////////////////////////////// + // use_internal_storage + // + // determines when the "local buffer optimization" is used + // + template <typename T> + using use_internal_storage = bool_constant + < + is_nothrow_move_constructible<T>::value + && (sizeof(T) <= sizeof(storage)) && + (alignment_of<storage>::value % alignment_of<T>::value == 0) + >; + + + ////////////////////////////////////////////////////////////////////////////////////////// + // non-member friend functions + // + template <class ValueType> friend const ValueType* any_cast(const any* pAny) EA_NOEXCEPT; + template <class ValueType> friend ValueType* any_cast(any* pAny) EA_NOEXCEPT; + template <class ValueType> friend ValueType any_cast(const any& operand); + template <class ValueType> friend ValueType any_cast(any& operand); + template <class ValueType> friend ValueType any_cast(any&& operand); + + //Adding Unsafe any cast operations + template <class ValueType> friend const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT; + template <class ValueType> friend ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT; + + + ////////////////////////////////////////////////////////////////////////////////////////// + // internal storage handler + // + template <typename T> + struct storage_handler_internal + { + template <typename V> + static void construct(storage& s, V&& v) + { + ::new(&s.internal_storage) T(eastl::forward<V>(v)); + } + + template <typename... Args> + static void construct_inplace(storage& s, Args... args) + { + ::new(&s.internal_storage) T(eastl::forward<Args>(args)...); + } + + template <class NT, class U, class... Args> + static void construct_inplace(storage& s, std::initializer_list<U> il, Args&&... args) + { + ::new(&s.internal_storage) NT(il, eastl::forward<Args>(args)...); + } + + static inline void destroy(any& refAny) + { + T& t = *static_cast<T*>(static_cast<void*>(&refAny.m_storage.internal_storage)); + EA_UNUSED(t); + t.~T(); + + refAny.m_handler = nullptr; + } + + static void* handler_func(storage_operation op, const any* pThis, any* pOther) + { + switch (op) + { + case storage_operation::GET: + { + EASTL_ASSERT(pThis); + return (void*)(&pThis->m_storage.internal_storage); + } + break; + + case storage_operation::DESTROY: + { + EASTL_ASSERT(pThis); + destroy(const_cast<any&>(*pThis)); + } + break; + + case storage_operation::COPY: + { + EASTL_ASSERT(pThis); + EASTL_ASSERT(pOther); + construct(pOther->m_storage, *(T*)(&pThis->m_storage.internal_storage)); + } + break; + + case storage_operation::MOVE: + { + EASTL_ASSERT(pThis); + EASTL_ASSERT(pOther); + construct(pOther->m_storage, eastl::move(*(T*)(&pThis->m_storage.internal_storage))); + destroy(const_cast<any&>(*pThis)); + } + break; + + case storage_operation::TYPE_INFO: + { + #if EASTL_RTTI_ENABLED + return (void*)&typeid(T); + #endif + } + break; + + default: + { + EASTL_ASSERT_MSG(false, "unknown storage operation\n"); + } + break; + }; + + return nullptr; + } + }; + + + + ////////////////////////////////////////////////////////////////////////////////////////// + // external storage handler + // + template <typename T> + struct storage_handler_external + { + template <typename V> + static inline void construct(storage& s, V&& v) + { + s.external_storage = Internal::DefaultConstruct<T>(eastl::forward<V>(v)); + } + + template <typename... Args> + static inline void construct_inplace(storage& s, Args... args) + { + s.external_storage = Internal::DefaultConstruct<T>(eastl::forward<Args>(args)...); + } + + template <class NT, class U, class... Args> + static inline void construct_inplace(storage& s, std::initializer_list<U> il, Args&&... args) + { + s.external_storage = Internal::DefaultConstruct<NT>(il, eastl::forward<Args>(args)...); + } + + static inline void destroy(any& refAny) + { + Internal::DefaultDestroy(static_cast<T*>(refAny.m_storage.external_storage)); + + refAny.m_handler = nullptr; + } + + static void* handler_func(storage_operation op, const any* pThis, any* pOther) + { + switch (op) + { + case storage_operation::GET: + { + EASTL_ASSERT(pThis); + EASTL_ASSERT(pThis->m_storage.external_storage); + return static_cast<void*>(pThis->m_storage.external_storage); + } + break; + + case storage_operation::DESTROY: + { + EASTL_ASSERT(pThis); + destroy(*const_cast<any*>(pThis)); + } + break; + + case storage_operation::COPY: + { + EASTL_ASSERT(pThis); + EASTL_ASSERT(pOther); + construct(pOther->m_storage, *static_cast<T*>(pThis->m_storage.external_storage)); + } + break; + + case storage_operation::MOVE: + { + EASTL_ASSERT(pThis); + EASTL_ASSERT(pOther); + construct(pOther->m_storage, eastl::move(*(T*)(pThis->m_storage.external_storage))); + destroy(const_cast<any&>(*pThis)); + } + break; + + case storage_operation::TYPE_INFO: + { + #if EASTL_RTTI_ENABLED + return (void*)&typeid(T); + #endif + } + break; + + default: + { + EASTL_ASSERT_MSG(false, "unknown storage operation\n"); + } + break; + }; + + return nullptr; + } + }; + + + ////////////////////////////////////////////////////////////////////////////////////////// + // storage_handler_ptr + // + // defines the function signature of the storage handler that both the internal and + // external storage handlers must implement to retrieve the underlying type of the any + // object. + // + using storage_handler_ptr = void* (*)(storage_operation, const any*, any*); + + + ////////////////////////////////////////////////////////////////////////////////////////// + // storage_handler + // + // based on the specified type T we select the appropriate underlying storage handler + // based on the 'use_internal_storage' trait. + // + template <typename T> + using storage_handler = typename conditional<use_internal_storage<T>::value, + storage_handler_internal<T>, + storage_handler_external<T>>::type; + + + ////////////////////////////////////////////////////////////////////////////////////////// + // data layout + // + storage m_storage; + storage_handler_ptr m_handler; + + public: + #ifndef EA_COMPILER_GNUC + // TODO(rparolin): renable constexpr for GCC + EA_CONSTEXPR + #endif + any() EA_NOEXCEPT + : m_storage(), m_handler(nullptr) {} + + any(const any& other) : m_handler(nullptr) + { + if (other.m_handler) + { + // NOTE(rparolin): You can not simply copy the underlying + // storage because it could hold a pointer to an object on the + // heap which breaks the copy semantics of the language. + other.m_handler(storage_operation::COPY, &other, this); + m_handler = other.m_handler; + } + } + + any(any&& other) EA_NOEXCEPT : m_handler(nullptr) + { + if(other.m_handler) + { + // NOTE(rparolin): You can not simply move the underlying + // storage because because the storage class has effectively + // type erased user type so we have to defer to the handler + // function to get the type back and pass on the move request. + m_handler = eastl::move(other.m_handler); + other.m_handler(storage_operation::MOVE, &other, this); + } + } + + ~any() { reset(); } + + template <class ValueType> + any(ValueType&& value, + typename eastl::enable_if<!eastl::is_same<typename eastl::decay<ValueType>::type, any>::value>::type* = 0) + { + typedef decay_t<ValueType> DecayedValueType; + static_assert(is_copy_constructible<DecayedValueType>::value, "ValueType must be copy-constructible"); + storage_handler<DecayedValueType>::construct(m_storage, eastl::forward<ValueType>(value)); + m_handler = &storage_handler<DecayedValueType>::handler_func; + } + + template <class T, class... Args> + explicit any(in_place_type_t<T>, Args&&... args) + { + typedef storage_handler<decay_t<T>> StorageHandlerT; + static_assert(eastl::is_constructible<T, Args...>::value, "T must be constructible with Args..."); + + StorageHandlerT::construct_inplace(m_storage, eastl::forward<Args>(args)...); + m_handler = &StorageHandlerT::handler_func; + } + + template <class T, class U, class... Args> + explicit any(in_place_type_t<T>, + std::initializer_list<U> il, + Args&&... args, + typename eastl::enable_if<eastl::is_constructible<T, std::initializer_list<U>&, Args...>::value, + void>::type* = 0) + { + typedef storage_handler<decay_t<T>> StorageHandlerT; + + StorageHandlerT::construct_inplace(m_storage, il, eastl::forward<Args>(args)...); + m_handler = &StorageHandlerT::handler_func; + } + + // 20.7.3.2, assignments + template <class ValueType> + any& operator=(ValueType&& value) + { + static_assert(is_copy_constructible<decay_t<ValueType>>::value, "ValueType must be copy-constructible"); + any(eastl::forward<ValueType>(value)).swap(*this); + return *this; + } + + any& operator=(const any& other) + { + any(other).swap(*this); + return *this; + } + + any& operator=(any&& other) EA_NOEXCEPT + { + any(eastl::move(other)).swap(*this); + return *this; + } + + // 20.7.3.3, modifiers + #if EASTL_VARIADIC_TEMPLATES_ENABLED + template <class T, class... Args> + void emplace(Args&&... args) + { + typedef storage_handler<decay_t<T>> StorageHandlerT; + static_assert(eastl::is_constructible<T, Args...>::value, "T must be constructible with Args..."); + + reset(); + StorageHandlerT::construct_inplace(m_storage, eastl::forward<Args>(args)...); + m_handler = &StorageHandlerT::handler_func; + } + + template <class NT, class U, class... Args> + typename eastl::enable_if<eastl::is_constructible<NT, std::initializer_list<U>&, Args...>::value, void>::type + emplace(std::initializer_list<U> il, Args&&... args) + { + typedef storage_handler<decay_t<NT>> StorageHandlerT; + + reset(); + StorageHandlerT::construct_inplace(m_storage, il, eastl::forward<Args>(args)...); + m_handler = &StorageHandlerT::handler_func; + } + #endif + + void reset() EA_NOEXCEPT + { + if(m_handler) + m_handler(storage_operation::DESTROY, this, nullptr); + } + + void swap(any& other) EA_NOEXCEPT + { + if(this == &other) + return; + + if(m_handler && other.m_handler) + { + any tmp; + tmp.m_handler = other.m_handler; + other.m_handler(storage_operation::MOVE, &other, &tmp); + + other.m_handler = m_handler; + m_handler(storage_operation::MOVE, this, &other); + + m_handler = tmp.m_handler; + tmp.m_handler(storage_operation::MOVE, &tmp, this); + } + else if (m_handler == nullptr && other.m_handler) + { + eastl::swap(m_handler, other.m_handler); + m_handler(storage_operation::MOVE, &other, this); + } + else if(m_handler && other.m_handler == nullptr) + { + eastl::swap(m_handler, other.m_handler); + other.m_handler(storage_operation::MOVE, this, &other); + } + //else if (m_handler == nullptr && other.m_handler == nullptr) + //{ + // // nothing to swap + //} + } + + // 20.7.3.4, observers + bool has_value() const EA_NOEXCEPT { return m_handler != nullptr; } + + #if EASTL_RTTI_ENABLED + inline const std::type_info& type() const EA_NOEXCEPT + { + if(m_handler) + { + auto* pTypeInfo = m_handler(storage_operation::TYPE_INFO, this, nullptr); + return *static_cast<const std::type_info*>(pTypeInfo); + } + else + { + return typeid(void); + } + } + #endif + }; + + + + ////////////////////////////////////////////////////////////////////////////////////////// + // 20.7.4, non-member functions + // + inline void swap(any& rhs, any& lhs) EA_NOEXCEPT { rhs.swap(lhs); } + + + ////////////////////////////////////////////////////////////////////////////////////////// + // 20.7.4, The non-member any_cast functions provide type-safe access to the contained object. + // + template <class ValueType> + inline ValueType any_cast(const any& operand) + { + static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value, + "ValueType must be a reference or copy constructible"); + + auto* p = any_cast<typename add_const<typename remove_reference<ValueType>::type>::type>(&operand); + + if(p == nullptr) + Internal::DoBadAnyCast(); + + return *p; + } + + template <class ValueType> + inline ValueType any_cast(any& operand) + { + static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value, + "ValueType must be a reference or copy constructible"); + + auto* p = any_cast<typename remove_reference<ValueType>::type>(&operand); + + if(p == nullptr) + Internal::DoBadAnyCast(); + + return *p; + } + + template <class ValueType> + inline ValueType any_cast(any&& operand) + { + static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value, + "ValueType must be a reference or copy constructible"); + + auto* p = any_cast<typename remove_reference<ValueType>::type>(&operand); + + if (p == nullptr) + Internal::DoBadAnyCast(); + + return *p; + } + + // NOTE(rparolin): The runtime type check was commented out because in DLL builds the templated function pointer + // value will be different -- completely breaking the validation mechanism. Due to the fact that eastl::any uses + // type erasure we can't refresh (on copy/move) the cached function pointer to the internal handler function because + // we don't statically know the type. + template <class ValueType> + inline const ValueType* any_cast(const any* pAny) EA_NOEXCEPT + { + return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler<decay_t<ValueType>>::handler_func) + #if EASTL_RTTI_ENABLED + && pAny->type() == typeid(typename remove_reference<ValueType>::type) + #endif + ) ? + static_cast<const ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) : + nullptr; + } + + template <class ValueType> + inline ValueType* any_cast(any* pAny) EA_NOEXCEPT + { + return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler<decay_t<ValueType>>::handler_func) + #if EASTL_RTTI_ENABLED + && pAny->type() == typeid(typename remove_reference<ValueType>::type) + #endif + ) ? + static_cast<ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) : + nullptr; + } + + //Unsafe operations - use with caution + template <class ValueType> + inline const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT + { + return unsafe_any_cast<ValueType>(const_cast<any*>(pAny)); + } + + template <class ValueType> + inline ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT + { + return static_cast<ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // make_any + // + #if EASTL_VARIADIC_TEMPLATES_ENABLED + template <class T, class... Args> + inline any make_any(Args&&... args) + { + return any(eastl::in_place<T>, eastl::forward<Args>(args)...); + } + + template <class T, class U, class... Args> + inline any make_any(std::initializer_list<U> il, Args&&... args) + { + return any(eastl::in_place<T>, il, eastl::forward<Args>(args)...); + } + #endif + +} // namespace eastl + +#endif // EASTL_ANY_H |