///////////////////////////////////////////////////////////////////////////// // 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 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 #include #if EASTL_RTTI_ENABLED #include #endif #if EASTL_EXCEPTIONS_ENABLED #include #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 void* DefaultConstruct(Args&&... args) { auto* pMem = EASTLAllocatorDefault()->allocate(sizeof(T), alignof(T), 0); return ::new(pMem) T(eastl::forward(args)...); } template void DefaultDestroy(T* p) { p->~T(); EASTLAllocatorDefault()->deallocate(static_cast(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::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 using use_internal_storage = bool_constant < is_nothrow_move_constructible::value && (sizeof(T) <= sizeof(storage)) && (alignment_of::value % alignment_of::value == 0) >; ////////////////////////////////////////////////////////////////////////////////////////// // non-member friend functions // template friend const ValueType* any_cast(const any* pAny) EA_NOEXCEPT; template friend ValueType* any_cast(any* pAny) EA_NOEXCEPT; template friend ValueType any_cast(const any& operand); template friend ValueType any_cast(any& operand); template friend ValueType any_cast(any&& operand); //Adding Unsafe any cast operations template friend const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT; template friend ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT; ////////////////////////////////////////////////////////////////////////////////////////// // internal storage handler // template struct storage_handler_internal { template static void construct(storage& s, V&& v) { ::new(&s.internal_storage) T(eastl::forward(v)); } template static void construct_inplace(storage& s, Args... args) { ::new(&s.internal_storage) T(eastl::forward(args)...); } template static void construct_inplace(storage& s, std::initializer_list il, Args&&... args) { ::new(&s.internal_storage) NT(il, eastl::forward(args)...); } static inline void destroy(any& refAny) { T& t = *static_cast(static_cast(&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(*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(*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 struct storage_handler_external { template static inline void construct(storage& s, V&& v) { s.external_storage = Internal::DefaultConstruct(eastl::forward(v)); } template static inline void construct_inplace(storage& s, Args... args) { s.external_storage = Internal::DefaultConstruct(eastl::forward(args)...); } template static inline void construct_inplace(storage& s, std::initializer_list il, Args&&... args) { s.external_storage = Internal::DefaultConstruct(il, eastl::forward(args)...); } static inline void destroy(any& refAny) { Internal::DefaultDestroy(static_cast(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(pThis->m_storage.external_storage); } break; case storage_operation::DESTROY: { EASTL_ASSERT(pThis); destroy(*const_cast(pThis)); } break; case storage_operation::COPY: { EASTL_ASSERT(pThis); EASTL_ASSERT(pOther); construct(pOther->m_storage, *static_cast(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(*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 using storage_handler = typename conditional::value, storage_handler_internal, storage_handler_external>::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 any(ValueType&& value, typename eastl::enable_if::type, any>::value>::type* = 0) { typedef decay_t DecayedValueType; static_assert(is_copy_constructible::value, "ValueType must be copy-constructible"); storage_handler::construct(m_storage, eastl::forward(value)); m_handler = &storage_handler::handler_func; } template explicit any(in_place_type_t, Args&&... args) { typedef storage_handler> StorageHandlerT; static_assert(eastl::is_constructible::value, "T must be constructible with Args..."); StorageHandlerT::construct_inplace(m_storage, eastl::forward(args)...); m_handler = &StorageHandlerT::handler_func; } template explicit any(in_place_type_t, std::initializer_list il, Args&&... args, typename eastl::enable_if&, Args...>::value, void>::type* = 0) { typedef storage_handler> StorageHandlerT; StorageHandlerT::construct_inplace(m_storage, il, eastl::forward(args)...); m_handler = &StorageHandlerT::handler_func; } // 20.7.3.2, assignments template any& operator=(ValueType&& value) { static_assert(is_copy_constructible>::value, "ValueType must be copy-constructible"); any(eastl::forward(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 void emplace(Args&&... args) { typedef storage_handler> StorageHandlerT; static_assert(eastl::is_constructible::value, "T must be constructible with Args..."); reset(); StorageHandlerT::construct_inplace(m_storage, eastl::forward(args)...); m_handler = &StorageHandlerT::handler_func; } template typename eastl::enable_if&, Args...>::value, void>::type emplace(std::initializer_list il, Args&&... args) { typedef storage_handler> StorageHandlerT; reset(); StorageHandlerT::construct_inplace(m_storage, il, eastl::forward(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(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 inline ValueType any_cast(const any& operand) { static_assert(eastl::is_reference::value || eastl::is_copy_constructible::value, "ValueType must be a reference or copy constructible"); auto* p = any_cast::type>::type>(&operand); if(p == nullptr) Internal::DoBadAnyCast(); return *p; } template inline ValueType any_cast(any& operand) { static_assert(eastl::is_reference::value || eastl::is_copy_constructible::value, "ValueType must be a reference or copy constructible"); auto* p = any_cast::type>(&operand); if(p == nullptr) Internal::DoBadAnyCast(); return *p; } template inline ValueType any_cast(any&& operand) { static_assert(eastl::is_reference::value || eastl::is_copy_constructible::value, "ValueType must be a reference or copy constructible"); auto* p = any_cast::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 inline const ValueType* any_cast(const any* pAny) EA_NOEXCEPT { return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler>::handler_func) #if EASTL_RTTI_ENABLED && pAny->type() == typeid(typename remove_reference::type) #endif ) ? static_cast(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) : nullptr; } template inline ValueType* any_cast(any* pAny) EA_NOEXCEPT { return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler>::handler_func) #if EASTL_RTTI_ENABLED && pAny->type() == typeid(typename remove_reference::type) #endif ) ? static_cast(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) : nullptr; } //Unsafe operations - use with caution template inline const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT { return unsafe_any_cast(const_cast(pAny)); } template inline ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT { return static_cast(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)); } ////////////////////////////////////////////////////////////////////////////////////////// // make_any // #if EASTL_VARIADIC_TEMPLATES_ENABLED template inline any make_any(Args&&... args) { return any(eastl::in_place, eastl::forward(args)...); } template inline any make_any(std::initializer_list il, Args&&... args) { return any(eastl::in_place, il, eastl::forward(args)...); } #endif } // namespace eastl #endif // EASTL_ANY_H