From d153910373cb1d50f72e1ab077794e177c15500b Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Wed, 26 Oct 2016 07:46:48 -0700 Subject: [PATCH] 3.04.00 release --- .appveyor.yml | 36 +- .clang-format | 2 +- .gitignore | 6 +- .p4ignore | 1 + include/EASTL/algorithm.h | 21 + include/EASTL/any.h | 568 ++++++++++++++++++ include/EASTL/internal/config.h | 4 +- include/EASTL/internal/hashtable.h | 9 + include/EASTL/internal/in_place_t.h | 73 +++ include/EASTL/internal/type_pod.h | 12 +- include/EASTL/internal/type_properties.h | 11 +- include/EASTL/optional.h | 31 +- include/EASTL/segmented_vector.h | 520 ++++++++++++++++ include/EASTL/shared_array.h | 2 +- include/EASTL/string_hash_map.h | 174 ++++++ include/EASTL/string_map.h | 165 +++++ include/EASTL/type_traits.h | 8 + include/EASTL/utility.h | 4 +- include/EASTL/vector.h | 37 +- .../include/Common/EABase/config/eacompiler.h | 4 +- .../Common/EABase/config/eacompilertraits.h | 91 ++- .../EABase/include/Common/EABase/version.h | 4 +- test/packages/EATest/source/EATest.cpp | 3 + test/source/EASTLTest.h | 6 +- test/source/TestAlgorithm.cpp | 36 ++ test/source/TestAny.cpp | 389 ++++++++++++ test/source/TestExtra.cpp | 6 +- test/source/TestFixedHash.cpp | 16 + test/source/TestHash.cpp | 9 + test/source/TestMap.h | 19 +- test/source/TestSegmentedVector.cpp | 72 +++ test/source/TestString.inl | 2 +- test/source/TestStringHashMap.cpp | 221 +++++++ test/source/TestStringMap.cpp | 209 +++++++ test/source/TestTuple.cpp | 4 + test/source/TestVector.cpp | 92 ++- test/source/main.cpp | 6 +- 37 files changed, 2805 insertions(+), 68 deletions(-) create mode 100644 include/EASTL/any.h create mode 100644 include/EASTL/internal/in_place_t.h create mode 100644 include/EASTL/segmented_vector.h create mode 100644 include/EASTL/string_hash_map.h create mode 100644 include/EASTL/string_map.h create mode 100644 test/source/TestAny.cpp create mode 100644 test/source/TestSegmentedVector.cpp create mode 100644 test/source/TestStringHashMap.cpp create mode 100644 test/source/TestStringMap.cpp diff --git a/.appveyor.yml b/.appveyor.yml index dc3e3ebc..580ee5d4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,18 +1,18 @@ -version: 1.0.{build} - -platform: - - MSVC_2013_x86 - - MSVC_2013_x64 - - MSVC_2015_x86 - - MSVC_2015_x64 - - MinGW_x86 - - MinGW_x64 - -install: - - scripts\ci-pre.cmd - -build_script: - - scripts\ci-build.cmd - -test_script: - - scripts\ci-test.cmd +version: 1.0.{build} + +platform: + - MSVC_2013_x86 + - MSVC_2013_x64 + - MSVC_2015_x86 + - MSVC_2015_x64 + - MinGW_x86 + - MinGW_x64 + +install: + - scripts\ci-pre.cmd + +build_script: + - scripts\ci-build.cmd + +test_script: + - scripts\ci-test.cmd diff --git a/.clang-format b/.clang-format index ccd11282..1680c896 100644 --- a/.clang-format +++ b/.clang-format @@ -21,7 +21,7 @@ DerivePointerBinding : false IndentWidth : 4 KeepEmptyLinesAtTheStartOfBlocks : true MaxEmptyLinesToKeep : 2 -NamespaceIndentation : Inner +NamespaceIndentation : All PointerBindsToType : true SpacesBeforeTrailingComments : 1 SpacesInAngles : false diff --git a/.gitignore b/.gitignore index 17f6d516..b7d8e6c7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ tags .swp *.swp .swo +.TMP -.d eastl_build_out build_bench @@ -23,8 +24,6 @@ cmake_install.cmake **/*.vcxproj.filters *.VC.opendb *.sdf -**/*.suo -**/*.user .vs/* **/Debug/* CMakeFiles/* @@ -34,5 +33,6 @@ Release/* Win32/* x64/* MinSizeRel/* -build*/* +build/* Testing/* +%ALLUSERSPROFILE%/* diff --git a/.p4ignore b/.p4ignore index f4eb09f3..224d0ca3 100644 --- a/.p4ignore +++ b/.p4ignore @@ -1,2 +1,3 @@ /.git/ tags +.gitignore diff --git a/include/EASTL/algorithm.h b/include/EASTL/algorithm.h index 19c64016..7e710c48 100644 --- a/include/EASTL/algorithm.h +++ b/include/EASTL/algorithm.h @@ -4146,6 +4146,27 @@ namespace eastl + /// clamp + /// + /// Returns a reference to a clamped value within the range of [lo, hi]. + /// + /// http://en.cppreference.com/w/cpp/algorithm/clamp + /// + template + EA_CONSTEXPR const T& clamp(const T& v, const T& lo, const T& hi) + { + return clamp(v, lo, hi, eastl::less<>()); + } + + template + EA_CONSTEXPR const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) + { + // code collapsed to a single line due to constexpr requirements + return [&] { EASTL_ASSERT(!comp(hi, lo)); }(), + comp(v, lo) ? lo : comp(hi, v) ? hi : v; + } + + } // namespace eastl diff --git a/include/EASTL/any.h b/include/EASTL/any.h new file mode 100644 index 00000000..82fe4f68 --- /dev/null +++ b/include/EASTL/any.h @@ -0,0 +1,568 @@ +///////////////////////////////////////////////////////////////////////////// +// 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 a 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 + +EA_ONCE() + +#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 + 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 + } + } + + + /////////////////////////////////////////////////////////////////////////////// + // 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); + + + ////////////////////////////////////////////////////////////////////////////////////////// + // 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: + { + destroy(const_cast(*pThis)); + } + break; + + case storage_operation::COPY: + { + construct(pOther->m_storage, *(T*)(&pThis->m_storage.internal_storage)); + } + break; + + case storage_operation::MOVE: + { + construct(pOther->m_storage, eastl::move(*(T*)(&pThis->m_storage.internal_storage))); + } + 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 = ::new T(eastl::forward(v)); + } + + template + static inline void construct_inplace(storage& s, Args... args) + { + s.external_storage = ::new T(eastl::forward(args)...); + } + + template + static inline void construct_inplace(storage& s, std::initializer_list il, Args&&... args) + { + s.external_storage = ::new NT(il, eastl::forward(args)...); + } + + static inline void destroy(any& refAny) + { + delete 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: + { + construct(pOther->m_storage, *static_cast(pThis->m_storage.external_storage)); + } + break; + + case storage_operation::MOVE: + { + construct(pOther->m_storage, eastl::move(*(T*)(pThis->m_storage.external_storage))); + } + 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: + EA_CONSTEXPR 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. + other.m_handler(storage_operation::MOVE, &other, this); + m_handler = eastl::move(other.m_handler); + } + } + + ~any() { reset(); } + + template + any(ValueType&& value, + typename eastl::enable_if::type, any>::value>::type* = 0) + { + 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 + { + eastl::swap(m_storage, other.m_storage); + eastl::swap(m_handler, other.m_handler); + } + + // 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 + // + 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; + } + + template + inline const ValueType* any_cast(const any* pAny) EA_NOEXCEPT + { + return (pAny && pAny->m_handler == &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 == &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; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // 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 diff --git a/include/EASTL/internal/config.h b/include/EASTL/internal/config.h index 815eb0b3..bfc00960 100644 --- a/include/EASTL/internal/config.h +++ b/include/EASTL/internal/config.h @@ -105,8 +105,8 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef EASTL_VERSION - #define EASTL_VERSION "3.02.01" - #define EASTL_VERSION_N 30201 + #define EASTL_VERSION "3.04.00" + #define EASTL_VERSION_N 30400 #endif diff --git a/include/EASTL/internal/hashtable.h b/include/EASTL/internal/hashtable.h index f9c4c0bf..a49869fd 100644 --- a/include/EASTL/internal/hashtable.h +++ b/include/EASTL/internal/hashtable.h @@ -1036,6 +1036,7 @@ namespace eastl void clear(bool clearBuckets); // If clearBuckets is true, we free the bucket memory and set the bucket count back to the newly constructed count. void reset_lose_memory() EA_NOEXCEPT; // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. void rehash(size_type nBucketCount); + void reserve(size_type nElementCount); #if EASTL_RESET_ENABLED void reset() EA_NOEXCEPT; // This function name is deprecated; use reset_lose_memory instead. @@ -2780,6 +2781,14 @@ namespace eastl } + template + inline void hashtable::reserve(size_type nElementCount) + { + rehash(mRehashPolicy.GetBucketCount(uint32_t(nElementCount))); + } + + template diff --git a/include/EASTL/internal/in_place_t.h b/include/EASTL/internal/in_place_t.h new file mode 100644 index 00000000..b3d9696d --- /dev/null +++ b/include/EASTL/internal/in_place_t.h @@ -0,0 +1,73 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_IN_PLACE_T_H +#define EASTL_INTERNAL_IN_PLACE_T_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +namespace eastl +{ + namespace Internal + { + struct in_place_tag {}; + template struct in_place_type_tag {}; + template struct in_place_index_tag {}; + } + + + /////////////////////////////////////////////////////////////////////////////// + /// in_place_tag + /// + /// http://en.cppreference.com/w/cpp/utility/in_place_tag + /// + struct in_place_tag { in_place_tag() = delete; }; + + + /////////////////////////////////////////////////////////////////////////////// + /// in_place_t / in_place_type_t / in_place_index_t + /// + /// used to disambiguate overloads that take arguments (possibly a parameter + /// pack) for in-place construction of some value. + /// + /// http://en.cppreference.com/w/cpp/utility/optional/in_place_t + /// + using in_place_t = in_place_tag(&)(Internal::in_place_tag); + + template + using in_place_type_t = in_place_tag(&)(Internal::in_place_type_tag); + + template + using in_place_index_t = in_place_tag(&)(Internal::in_place_index_tag); + + + /////////////////////////////////////////////////////////////////////////////// + /// in_place / in_place / in_place + /// + /// http://en.cppreference.com/w/cpp/utility/in_place + /// + inline in_place_tag in_place(Internal::in_place_tag) { return {}; } + + template + inline in_place_tag in_place(Internal::in_place_type_tag) { return {}; } + + template + inline in_place_tag in_place(Internal::in_place_index_tag){ return {}; } + + +} // namespace eastl + + +#endif // Header include guard + + + + + + diff --git a/include/EASTL/internal/type_pod.h b/include/EASTL/internal/type_pod.h index cad041a8..916218f0 100644 --- a/include/EASTL/internal/type_pod.h +++ b/include/EASTL/internal/type_pod.h @@ -286,7 +286,7 @@ namespace eastl #define EASTL_TYPE_TRAIT_has_trivial_assign_CONFORMANCE 1 // has_trivial_assign is conforming. template - struct has_trivial_assign : public integral_constant::value) && !eastl::is_const::value && !eastl::is_volatile::value>{}; + struct has_trivial_assign : public integral_constant::value) && !eastl::is_const::value && !eastl::is_volatile::value && !eastl::is_hat_type::value>{}; #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) #define EASTL_TYPE_TRAIT_has_trivial_assign_CONFORMANCE 1 // has_trivial_assign is conforming. @@ -335,7 +335,7 @@ namespace eastl #define EASTL_TYPE_TRAIT_has_trivial_destructor_CONFORMANCE 1 // has_trivial_destructor is conforming. template - struct has_trivial_destructor : public eastl::integral_constant::value>{}; + struct has_trivial_destructor : public eastl::integral_constant::value) && !eastl::is_hat_type::value>{}; #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) #define EASTL_TYPE_TRAIT_has_trivial_destructor_CONFORMANCE 1 // has_trivial_destructor is conforming. @@ -1667,7 +1667,7 @@ namespace eastl // For a complete type T and given // template // struct test { U u; }; - // test::˜test() is not deleted (C++11 "= delete"). + // test::˜test() is not deleted (C++11 "= delete"). // T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound. // /////////////////////////////////////////////////////////////////////// @@ -1759,7 +1759,7 @@ namespace eastl template struct is_trivially_destructible // Can't use just __has_trivial_destructor(T) because some compilers give it slightly different meaning, and are just plain broken, such as VC++'s __has_trivial_destructor, which says false for fundamental types. - : public integral_constant::value && ((__has_trivial_destructor(T) && !eastl::is_hat_type::value)|| eastl::is_scalar::type>::value)> {}; + : public integral_constant::value && ((__has_trivial_destructor(T) && !eastl::is_hat_type::value)|| eastl::is_scalar::type>::value)> {}; #else #define EASTL_TYPE_TRAIT_is_trivially_destructible_CONFORMANCE 0 @@ -1907,6 +1907,10 @@ namespace eastl : public eastl::is_nothrow_constructible::type> {}; #endif + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_move_constructible_v = is_nothrow_move_constructible::value; + #endif } // namespace eastl diff --git a/include/EASTL/internal/type_properties.h b/include/EASTL/internal/type_properties.h index 7c0b88d0..500c0ad8 100644 --- a/include/EASTL/internal/type_properties.h +++ b/include/EASTL/internal/type_properties.h @@ -166,9 +166,13 @@ namespace eastl template struct alignment_of : public integral_constant::value>{}; + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR size_t alignment_of_v = alignment_of::value; + #endif - /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// // is_aligned // // Defined as true if the type has alignment requirements greater @@ -184,6 +188,11 @@ namespace eastl template struct is_aligned : public integral_constant::value>{}; + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR size_t is_aligned_v = is_aligned::value; + #endif + /////////////////////////////////////////////////////////////////////// diff --git a/include/EASTL/optional.h b/include/EASTL/optional.h index 8d0eade3..a7cf6471 100644 --- a/include/EASTL/optional.h +++ b/include/EASTL/optional.h @@ -31,6 +31,7 @@ #include #include #include // eastl::addressof +#include // eastl::in_place_t #if defined(EASTL_OPTIONAL_ENABLED) && EASTL_OPTIONAL_ENABLED @@ -38,6 +39,11 @@ EA_DISABLE_VC_WARNING(4583) // destructor is not implicitly called namespace eastl { + #ifdef EASTL_EXCEPTIONS_ENABLED + #define EASTL_OPTIONAL_NOEXCEPT + #else + #define EASTL_OPTIONAL_NOEXCEPT EA_NOEXCEPT + #endif /////////////////////////////////////////////////////////////////////////////// /// nullopt_t @@ -51,15 +57,6 @@ namespace eastl EA_CONSTEXPR nullopt_t nullopt{0}; - /////////////////////////////////////////////////////////////////////////////// - /// in_place_t - /// - /// used to disambiguate overloads that take arguments (possibly a parameter pack) for in-place construction of some value. - /// - struct in_place_t {}; - EA_CONSTEXPR in_place_t in_place{}; - - /////////////////////////////////////////////////////////////////////////////// /// bad_optional_access /// @@ -67,7 +64,7 @@ namespace eastl struct bad_optional_access : public std::logic_error { bad_optional_access() : std::logic_error("eastl::bad_optional_access exception") {} - virtual ~bad_optional_access() EA_NOEXCEPT; + virtual ~bad_optional_access() EA_NOEXCEPT {} }; #endif @@ -204,14 +201,14 @@ namespace eastl #if EASTL_VARIADIC_TEMPLATES_ENABLED template inline explicit optional(in_place_t, Args&&... args) - : base_type(in_place_t{}, eastl::forward(args)...) {} + : base_type(in_place, eastl::forward(args)...) {} template &, Args&&...>::value>::type> inline explicit optional(in_place_t, std::initializer_list ilist, Args&&... args) - : base_type(in_place_t{}, ilist, eastl::forward(args)...) {} + : base_type(in_place, ilist, eastl::forward(args)...) {} #endif inline optional& operator=(nullopt_t) @@ -332,7 +329,7 @@ namespace eastl } } - inline T* get_value_address() EA_NOEXCEPT + inline T* get_value_address() EASTL_OPTIONAL_NOEXCEPT { #if EASTL_EXCEPTIONS_ENABLED if(!engaged) @@ -343,7 +340,7 @@ namespace eastl return eastl::addressof(val); } - inline const T* get_value_address() const EA_NOEXCEPT + inline const T* get_value_address() const EASTL_OPTIONAL_NOEXCEPT { #if EASTL_EXCEPTIONS_ENABLED if(!engaged) @@ -354,7 +351,7 @@ namespace eastl return eastl::addressof(val); } - inline value_type& get_value_ref() EA_NOEXCEPT + inline value_type& get_value_ref() EASTL_OPTIONAL_NOEXCEPT { #if EASTL_EXCEPTIONS_ENABLED if(!engaged) @@ -365,7 +362,7 @@ namespace eastl return val; } - inline const value_type& get_value_ref() const EA_NOEXCEPT + inline const value_type& get_value_ref() const EASTL_OPTIONAL_NOEXCEPT { #if EASTL_EXCEPTIONS_ENABLED if(!engaged) @@ -582,6 +579,8 @@ namespace eastl return optional::type>(eastl::forward(value)); } + #undef EASTL_OPTIONAL_NOEXCEPT + } // namespace eastl EA_RESTORE_VC_WARNING() diff --git a/include/EASTL/segmented_vector.h b/include/EASTL/segmented_vector.h new file mode 100644 index 00000000..071eb1fa --- /dev/null +++ b/include/EASTL/segmented_vector.h @@ -0,0 +1,520 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_SEGMENTED_VECTOR_H +#define EASTL_SEGMENTED_VECTOR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include + +namespace eastl +{ + template + class segment + { + public: + typedef eastl_size_t size_type; + typedef segment this_type; + typedef T* iterator; + typedef const T* const_iterator; + + const this_type* next_segment() const; + this_type* next_segment(); + + const_iterator begin() const; + iterator begin(); + + const_iterator end() const; + iterator end(); + + private: + static const uintptr_t kIsLastSegment = 1 << 0; + uintptr_t mPrev; + + union + { + this_type* mNext; + size_type mSize; + }; + T mData[Count]; + template friend class segmented_vector; + template friend struct segmented_vector_iterator; + }; + + + template + struct segmented_vector_iterator + { + public: + typedef segmented_vector_iterator this_type; + typedef segment segment_type; + + T* operator->() const; + T& operator*() const; + + this_type& operator++(); + this_type operator++(int); + + public: + T* mCurrent; + T* mEnd; + segment_type* mSegment; + }; + + + template + class segmented_vector + { + public: + typedef eastl_size_t size_type; + typedef segmented_vector this_type; + typedef segment segment_type; + typedef Allocator allocator_type; + typedef segmented_vector_iterator const_iterator; + typedef segmented_vector_iterator iterator; + + + segmented_vector(const Allocator& allocator = Allocator()); + ~segmented_vector(); + + allocator_type& get_allocator(); + + const segment_type* first_segment() const; + segment_type* first_segment(); + const_iterator begin() const; + iterator begin(); + + const_iterator end() const; + iterator end(); + + size_type size() const; + size_type segment_count() const; + T& front(); + T& back(); + + bool empty() const; + void clear(); + + T& push_back(); + T& push_back(const T& value); + void* push_back_uninitialized(); + + void pop_back(); + + void erase_unsorted(segment_type& segment, typename segment_type::iterator it); + iterator erase_unsorted(const iterator& i); + + void swap(this_type& other); + + protected: + segment_type* DoAllocSegment(segment_type* prevSegment); + void* DoPushBack(); + + allocator_type mAllocator; + segment_type* mFirstSegment; + segment_type* mLastSegment; + size_type mSegmentCount; + }; + + + template + inline const segment* + segment::next_segment() const + { + if (mPrev & kIsLastSegment) + return 0; + else + return mNext; + } + + template + inline segment* + segment::next_segment() + { + if (mPrev & kIsLastSegment) + return 0; + else + return mNext; + } + + template + inline typename segment::const_iterator + segment::begin() const + { + return mData; + } + + template + inline typename segment::iterator + segment::begin() + { + return mData; + } + + template + inline typename segment::const_iterator + segment::end() const + { + if (mPrev & kIsLastSegment) + return mData + mSize; + else + return mData + Count; + } + + template + inline typename segment::iterator + segment::end() + { + if (mPrev & kIsLastSegment) + return mData + mSize; + else + return mData + Count; + } + + template + T* + segmented_vector_iterator::operator->() const + { + return mCurrent; + } + + template + T& + segmented_vector_iterator::operator*() const + { + return *mCurrent; + } + + template + segmented_vector_iterator& + segmented_vector_iterator::operator++() + { + ++mCurrent; + if(EASTL_UNLIKELY(mCurrent == mEnd)) + { + if (!(mSegment->mPrev & segment_type::kIsLastSegment)) + { + mSegment = mSegment->mNext; + mCurrent = mSegment->begin(); + mEnd = mSegment->end(); + } + else + mCurrent = 0; + } + return *this; + } + + template + segmented_vector_iterator + segmented_vector_iterator::operator++(int) + { + this_type i(*this); + return ++i; + } + + + template + inline segmented_vector::segmented_vector(const Allocator& allocator) + : mAllocator(allocator) + , mFirstSegment(0) + , mLastSegment(0) + , mSegmentCount(0) + { + } + + template + inline segmented_vector::~segmented_vector() + { + clear(); + } + + template + inline typename segmented_vector::allocator_type& + segmented_vector::get_allocator() + { + return mAllocator; + } + + template + inline const typename segmented_vector::segment_type* + segmented_vector::first_segment() const + { + return mFirstSegment; + } + + template + inline typename segmented_vector::segment_type* + segmented_vector::first_segment() + { + return mFirstSegment; + } + + template + inline typename segmented_vector::const_iterator + segmented_vector::begin() const + { + iterator i; + i.mSegment = mFirstSegment; + if (mFirstSegment) + { + i.mCurrent = mFirstSegment->begin(); + i.mEnd = mFirstSegment->end(); + } + else + i.mCurrent = 0; + return (const_iterator&)i; + } + + template + inline typename segmented_vector::iterator + segmented_vector::begin() + { + iterator i; + i.mSegment = mFirstSegment; + if (mFirstSegment) + { + i.mCurrent = mFirstSegment->begin(); + i.mEnd = mFirstSegment->end(); + } + else + i.mCurrent = 0; + return i; + } + + template + inline typename segmented_vector::const_iterator + segmented_vector::end() const + { + iterator i; + i.mCurrent = 0; + return (const_iterator&)i; + } + + template + inline typename segmented_vector::iterator + segmented_vector::end() + { + iterator i; + i.mCurrent = 0; + return i; + } + + template + inline typename segmented_vector::size_type + segmented_vector::size() const + { + if (segment_type* segment = mLastSegment) + return (mSegmentCount-1)*Count + segment->mSize; + return 0; + } + + template + inline typename segmented_vector::size_type + segmented_vector::segment_count() const + { + return mSegmentCount; + } + + template + inline T& + segmented_vector::front() + { + return mFirstSegment->mData[0]; + } + + template + inline T& + segmented_vector::back() + { + segment_type* lastSegment = mLastSegment; + return lastSegment->mData[lastSegment->mSize-1]; + } + + template + inline bool + segmented_vector::empty() const + { + return mFirstSegment == 0; + } + + template + inline void + segmented_vector::clear() + { + if (segment_type* segment = mFirstSegment) + { + while (segment != mLastSegment) + { + segment_type* nextSegment = segment->mNext; + segment->~segment_type(); + EASTLFree(mAllocator, segment, sizeof(segment_type)); + segment = nextSegment; + } + for (T* i = segment->mData, *e = segment->mData + segment->mSize; i!=e; ++i) + i->~T(); + EASTLFree(mAllocator, segment, sizeof(segment_type)); + mFirstSegment = 0; + mLastSegment = 0; + mSegmentCount = 0; + } + } + + template + inline T& + segmented_vector::push_back() + { + return *(new (DoPushBack()) T()); + } + + template + inline T& + segmented_vector::push_back(const T& value) + { + return *(new (DoPushBack()) T(value)); + } + + template + inline void* + segmented_vector::push_back_uninitialized() + { + return DoPushBack(); + } + + template + inline void + segmented_vector::pop_back() + { + segment_type* lastSegment = mLastSegment; + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!lastSegment)) + EASTL_FAIL_MSG("segmented_vector::pop_back -- segmented vector is empty"); + #endif + --lastSegment->mSize; + (lastSegment->mData + lastSegment->mSize)->T::~T(); + + if (!lastSegment->mSize) + { + --mSegmentCount; + mLastSegment = (segment_type*)(lastSegment->mPrev & (~segment_type::kIsLastSegment)); + EASTLFree(mAllocator, lastSegment, sizeof(segment_type)); + if (mLastSegment) + { + mLastSegment->mPrev |= segment_type::kIsLastSegment; + mLastSegment->mSize = Count; + } + else + mFirstSegment = 0; + } + } + + template + inline void + segmented_vector::erase_unsorted(segment_type& segment, typename segment_type::iterator it) + { + *it = back(); + pop_back(); + } + + template + inline typename segmented_vector::iterator + segmented_vector::erase_unsorted(const iterator& i) + { + iterator ret(i); + *i = back(); + if (i.mSegment == mLastSegment && mLastSegment->mSize == 1) + ret.mCurrent = 0; + pop_back(); + return ret; + } + + template + void + segmented_vector::swap(this_type& other) + { + allocator_type tempAllocator(mAllocator); + segment_type* tempFirstSegment = mFirstSegment; + segment_type* tempLastSegment = mLastSegment; + size_type tempSegmentCount = mSegmentCount; + + mAllocator = other.mAllocator; + mFirstSegment = other.mFirstSegment; + mLastSegment = other.mLastSegment; + mSegmentCount = other.mSegmentCount; + + other.mAllocator = tempAllocator; + other.mFirstSegment = tempFirstSegment; + other.mLastSegment = tempLastSegment; + other.mSegmentCount = tempSegmentCount; + } + + template + segment* + segmented_vector::DoAllocSegment(segment_type* prevSegment) + { + ++mSegmentCount; + segment_type* segment = (segment_type*)allocate_memory(mAllocator, sizeof(segment_type), EASTL_ALIGN_OF(segment_type), 0); + segment->mPrev = uintptr_t(prevSegment) | segment_type::kIsLastSegment; + segment->mSize = 1; + return segment; + } + + template + inline void* + segmented_vector::DoPushBack() + { + if (segment_type* segment = mLastSegment) + { + size_type size = segment->mSize; + if (size < Count) + { + ++segment->mSize; + return segment->mData + size; + } + else + { + segment_type* lastSegment = mLastSegment; + segment_type* newSegment = mLastSegment = DoAllocSegment(mLastSegment); + lastSegment->mPrev &= ~segment_type::kIsLastSegment; + lastSegment->mNext = newSegment; + return newSegment->mData; + } + } + else + { + segment = mFirstSegment = mLastSegment = DoAllocSegment(0); + return segment->mData; + } + } + + template + inline bool operator==(const segmented_vector_iterator& a, const segmented_vector_iterator& b) + { + return a.mCurrent == b.mCurrent; + } + + + template + inline bool operator!=(const segmented_vector_iterator& a, const segmented_vector_iterator& b) + { + return a.mCurrent != b.mCurrent; + } + + template + inline bool operator==(const segmented_vector_iterator& a, const segmented_vector_iterator& b) + { + return a.mCurrent == b.mCurrent; + } + + + template + inline bool operator!=(const segmented_vector_iterator& a, const segmented_vector_iterator& b) + { + return a.mCurrent != b.mCurrent; + } +} + +#endif diff --git a/include/EASTL/shared_array.h b/include/EASTL/shared_array.h index 25ec6a23..91c1e739 100644 --- a/include/EASTL/shared_array.h +++ b/include/EASTL/shared_array.h @@ -133,7 +133,7 @@ namespace eastl /// If an exception occurs during the allocation of the shared /// reference count, the owned pointer is deleted and the exception /// is rethrown. A null pointer is given a reference count of 1. - explicit shared_array(T* pArray = NULL, const allocator& allocator = EASTL_SHARED_ARRAY_DEFAULT_ALLOCATOR) + explicit shared_array(T* pArray = NULL, const allocator_type& allocator = EASTL_SHARED_ARRAY_DEFAULT_ALLOCATOR) : mpArray(pArray), mpRefCount(NULL), mAllocator(allocator) diff --git a/include/EASTL/string_hash_map.h b/include/EASTL/string_hash_map.h new file mode 100644 index 00000000..1d442fc3 --- /dev/null +++ b/include/EASTL/string_hash_map.h @@ -0,0 +1,174 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_STRING_HASH_MAP_H +#define EASTL_STRING_HASH_MAP_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include + +namespace eastl +{ + + +template, typename Predicate = equal_to, typename Allocator = EASTLAllocatorType> +class string_hash_map : public eastl::hash_map +{ +public: + typedef eastl::hash_map base; + typedef string_hash_map this_type; + typedef typename base::base_type::allocator_type allocator_type; + typedef typename base::base_type::insert_return_type insert_return_type; + typedef typename base::base_type::iterator iterator; + //typedef typename base::base_type::reverse_iterator reverse_iterator; + typedef typename base::base_type::const_iterator const_iterator; + typedef typename base::base_type::size_type size_type; + typedef typename base::base_type::value_type value_type; + typedef typename base::mapped_type mapped_type; + + string_hash_map(const allocator_type& allocator = allocator_type()) : base(allocator) {} + string_hash_map(const string_hash_map& src, const allocator_type& allocator = allocator_type()); + ~string_hash_map(); + void clear(); + void clear(bool clearBuckets); + + this_type& operator=(const this_type& x); + + insert_return_type insert(const char* key, const T& value); + insert_return_type insert(const char* key); + iterator erase(const_iterator position); + size_type erase(const char* key); + mapped_type& operator[](const char* key); + +private: + char* strduplicate(const char* str); + + // Not implemented right now + //insert_return_type insert(const value_type& value); + //iterator insert(iterator position, const value_type& value); + //reverse_iterator erase(reverse_iterator position); + //reverse_iterator erase(reverse_iterator first, reverse_iterator last); +}; + + + +template +string_hash_map::string_hash_map(const string_hash_map& src, const allocator_type& allocator) : base(allocator) +{ + for (const_iterator i=src.begin(), e=src.end(); i!=e; ++i) + base::base_type::insert(eastl::make_pair(strduplicate(i->first), i->second)); +} + +template +string_hash_map::~string_hash_map() +{ + clear(); +} + +template +void +string_hash_map::clear() +{ + allocator_type& allocator = base::base_type::get_allocator(); + for (const_iterator i=base::base_type::begin(), e=base::base_type::end(); i!=e; ++i) + allocator.deallocate((void*)i->first, 0); + base::base_type::clear(); +} + +template +void +string_hash_map::clear(bool clearBuckets) +{ + allocator_type& allocator = base::base_type::get_allocator(); + for (const_iterator i=base::base_type::begin(), e=base::base_type::end(); i!=e; ++i) + allocator.deallocate((void*)i->first, 0); + base::base_type::clear(clearBuckets); +} + +template +typename string_hash_map::this_type& +string_hash_map::operator=(const this_type& x) +{ + allocator_type allocator = base::base_type::get_allocator(); + this->~this_type(); + new (this) this_type(x, allocator); + return *this; +} + +template +typename string_hash_map::insert_return_type +string_hash_map::insert(const char* key) +{ + return insert(key, mapped_type()); +} + +template +typename string_hash_map::insert_return_type +string_hash_map::insert(const char* key, const T& value) +{ + EASTL_ASSERT(key); + iterator i = base::base_type::find(key); + if (i != base::base_type::end()) + { + insert_return_type ret; + ret.first = i; + ret.second = false; + return ret; + } + return base::base_type::insert(eastl::make_pair(strduplicate(key), value)); +} + +template +typename string_hash_map::iterator +string_hash_map::erase(const_iterator position) +{ + const char* key = position->first; + iterator result = base::base_type::erase(position); + base::base_type::get_allocator().deallocate((void*)key, 0); + return result; +} + +template +typename string_hash_map::size_type +string_hash_map::erase(const char* key) +{ + const iterator it(base::base_type::find(key)); + + if(it != base::base_type::end()) + { + erase(it); + return 1; + } + return 0; +} + +template +typename string_hash_map::mapped_type& +string_hash_map::operator[](const char* key) +{ + EASTL_ASSERT(key); + iterator i = base::base_type::find(key); + if (i != base::base_type::end()) + return i->second; + return base::base_type::insert(strduplicate(key)).first->second; +} + +template +char* +string_hash_map::strduplicate(const char* str) +{ + size_t len = strlen(str); + char* result = (char*)base::base_type::get_allocator().allocate(len + 1); + memcpy(result, str, len+1); + return result; +} + + +} + +#endif diff --git a/include/EASTL/string_map.h b/include/EASTL/string_map.h new file mode 100644 index 00000000..411de993 --- /dev/null +++ b/include/EASTL/string_map.h @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_STRING_MAP_H +#define EASTL_STRING_MAP_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include + +namespace eastl +{ + + +template, typename Allocator = EASTLAllocatorType> +class string_map : public eastl::map +{ +public: + typedef eastl::map base; + typedef string_map this_type; + typedef typename base::base_type::allocator_type allocator_type; + typedef typename base::base_type::insert_return_type insert_return_type; + typedef typename base::base_type::iterator iterator; + typedef typename base::base_type::reverse_iterator reverse_iterator; + typedef typename base::base_type::const_iterator const_iterator; + typedef typename base::base_type::size_type size_type; + typedef typename base::base_type::key_type key_type; + typedef typename base::base_type::value_type value_type; + typedef typename base::mapped_type mapped_type; + + string_map(const allocator_type& allocator = allocator_type()) : base(allocator) {} + string_map(const string_map& src, const allocator_type& allocator = allocator_type()); + ~string_map(); + void clear(); + + this_type& operator=(const this_type& x); + + insert_return_type insert(const char* key, const T& value); + insert_return_type insert(const char* key); + iterator erase(iterator position); + size_type erase(const char* key); + mapped_type& operator[](const char* key); + +private: + char* strduplicate(const char* str); + + // Not implemented right now + //insert_return_type insert(const value_type& value); + //iterator insert(iterator position, const value_type& value); + //reverse_iterator erase(reverse_iterator position); + //reverse_iterator erase(reverse_iterator first, reverse_iterator last); + //void erase(const key_type* first, const key_type* last); +}; + + + +template +string_map::string_map(const string_map& src, const allocator_type& allocator) : base(allocator) +{ + for (const_iterator i=src.begin(), e=src.end(); i!=e; ++i) + base::base_type::insert(eastl::make_pair(strduplicate(i->first), i->second)); +} + +template +string_map::~string_map() +{ + clear(); +} + +template +void +string_map::clear() +{ + allocator_type& allocator = base::base_type::get_allocator(); + for (const_iterator i=base::base_type::begin(), e=base::base_type::end(); i!=e; ++i) + allocator.deallocate((void*)i->first, 0); + base::base_type::clear(); +} + +template +typename string_map::this_type& +string_map::operator=(const this_type& x) +{ + allocator_type allocator = base::base_type::get_allocator(); + this->~this_type(); + new (this) this_type(x, allocator); + return *this; +} + +template +typename string_map::insert_return_type +string_map::insert(const char* key) +{ + return insert(key, mapped_type()); +} + +template +typename string_map::insert_return_type +string_map::insert(const char* key, const T& value) +{ + EASTL_ASSERT(key); + iterator i = base::base_type::find(key); + if (i != base::base_type::end()) + { + insert_return_type ret; + ret.first = i; + ret.second = false; + return ret; + } + return base::base_type::insert(eastl::make_pair(strduplicate(key), value)); +} + +template +typename string_map::iterator +string_map::erase(iterator position) +{ + const char* key = position->first; + iterator result = base::base_type::erase(position); + base::base_type::get_allocator().deallocate((void*)key, 0); + return result; +} + +template +typename string_map::size_type +string_map::erase(const char* key) +{ + const iterator it(base::base_type::find(key)); + + if(it != base::base_type::end()) + { + erase(it); + return 1; + } + return 0; +} + +template +typename string_map::mapped_type& +string_map::operator[](const char* key) +{ + EASTL_ASSERT(key); + iterator i = base::base_type::find(key); + if (i != base::base_type::end()) + return i->second; + return base::base_type::insert(strduplicate(key)).first->second; +} + +template +char* +string_map::strduplicate(const char* str) +{ + size_t len = strlen(str); + char* result = (char*)base::base_type::get_allocator().allocate(len + 1); + memcpy(result, str, len+1); + return result; +} + + +} + +#endif diff --git a/include/EASTL/type_traits.h b/include/EASTL/type_traits.h index 66b6d3fa..03c348f4 100644 --- a/include/EASTL/type_traits.h +++ b/include/EASTL/type_traits.h @@ -457,6 +457,10 @@ namespace eastl template struct conditional { typedef F type; }; + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + using conditional_t = typename conditional::type; + #endif /////////////////////////////////////////////////////////////////////// // identity @@ -750,6 +754,10 @@ namespace eastl template struct remove_reference{ typedef T type; }; #endif + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + using remove_reference_t = typename remove_reference::type; + #endif /////////////////////////////////////////////////////////////////////// diff --git a/include/EASTL/utility.h b/include/EASTL/utility.h index b71138b0..5305815f 100644 --- a/include/EASTL/utility.h +++ b/include/EASTL/utility.h @@ -16,6 +16,7 @@ #include #include +#include #ifdef _MSC_VER #pragma warning(push) // VC++ generates a bogus warning that you cannot code away. @@ -809,7 +810,8 @@ namespace eastl #endif // EASTL_TUPLE_ENABLED - } // namespace eastl + +} // namespace eastl #ifdef _MSC_VER #pragma warning(pop) diff --git a/include/EASTL/vector.h b/include/EASTL/vector.h index d1d62aea..6c04fbf6 100644 --- a/include/EASTL/vector.h +++ b/include/EASTL/vector.h @@ -590,7 +590,7 @@ namespace eastl #if EASTL_MOVE_SEMANTICS_ENABLED template inline vector::vector(this_type&& x) - : base_type(x.mAllocator) + : base_type(eastl::move(x.mAllocator)) // vector requires move-construction of allocator in this case. { DoSwap(x); } @@ -600,7 +600,13 @@ namespace eastl inline vector::vector(this_type&& x, const allocator_type& allocator) : base_type(allocator) { - swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. + if (mAllocator == x.mAllocator) // If allocators are equivalent... + DoSwap(x); + else + { + this_type temp(eastl::move(*this)); // move construct so we don't require the use of copy-ctors that prevent the use of move-only types. + temp.swap(x); + } } #endif @@ -1381,10 +1387,10 @@ namespace eastl // allocator_traits::propagate_on_container_swap::value is true (propagate_on_container_swap // is false by default). EASTL doesn't have allocator_traits and so this doesn't directly apply, // but EASTL has the effective behavior of propagate_on_container_swap = false for all allocators. - // So EASTL swap exchanges contents but not allocators, and swap is more efficient if allocators are equivalent. template inline void vector::swap(this_type& x) { + #if EASTL_VECTOR_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR if(mAllocator == x.mAllocator) // If allocators are equivalent... DoSwap(x); else // else swap the contents. @@ -1393,6 +1399,27 @@ namespace eastl *this = x; // itself call this member swap function. x = temp; } + #else + // NOTE(rparolin): The previous implementation required T to be copy-constructible in the fall-back case where + // allocators with unique instances copied elements. This was an unnecessary restriction and prevented the common + // usage of vector with non-copyable types (eg. eastl::vector or eastl::vector). + // + // The previous implementation violated the following requirements of vector::swap so the fall-back code has + // been removed. EASTL implicitly defines 'propagate_on_container_swap = false' therefore the fall-back case is + // undefined behaviour. We simply swap the contents and the allocator as that is the common expectation of + // users and does not put the container into an invalid state since it can not free its memory via its current + // allocator instance. + // + // http://en.cppreference.com/w/cpp/container/vector/swap + // "Exchanges the contents of the container with those of other. Does not invoke any move, copy, or swap + // operations on individual elements." + // + // http://en.cppreference.com/w/cpp/concept/AllocatorAwareContainer + // "Swapping two containers with unequal allocators if propagate_on_container_swap is false is undefined + // behavior." + + DoSwap(x); + #endif } @@ -1445,7 +1472,7 @@ namespace eastl inline void vector::DoInitFromIterator(InputIterator first, InputIterator last, EASTL_ITC_NS::input_iterator_tag) { // To do: Use emplace_back instead of push_back(). Our emplace_back will work below without any ifdefs. - for(; first < last; ++first) // InputIterators by definition actually only allow you to iterate through them once. + for(; first != last; ++first) // InputIterators by definition actually only allow you to iterate through them once. push_back(*first); // Thus the standard *requires* that we do this (inefficient) implementation. } // Luckily, InputIterators are in practice almost never used, so this code will likely never get executed. @@ -1730,7 +1757,7 @@ namespace eastl void vector::DoClearCapacity() // This function exists because set_capacity() currently indirectly requires value_type to be default-constructible, { // and some functions that need to clear our capacity (e.g. operator=) aren't supposed to require default-constructibility. clear(); - this_type temp(*this); // This is the simplest way to accomplish this, + this_type temp(eastl::move(*this)); // This is the simplest way to accomplish this, swap(temp); // and it is as efficient as any other. } diff --git a/test/packages/EABase/include/Common/EABase/config/eacompiler.h b/test/packages/EABase/include/Common/EABase/config/eacompiler.h index c1806993..f345b13f 100644 --- a/test/packages/EABase/include/Common/EABase/config/eacompiler.h +++ b/test/packages/EABase/include/Common/EABase/config/eacompiler.h @@ -1054,9 +1054,9 @@ #if !defined(EA_COMPILER_NO_VARIABLE_TEMPLATES) #if defined(_MSC_VER) && (_MSC_FULL_VER >= 190023918) // VS2015 Update 2 and above. // supported. - #elif defined(__clang__) && (EA_COMPILER_VERSION >= 340) && !defined(__APPLE__) // Clang 3.4+, not including Apple's Clang. + #elif defined(EA_COMPILER_CPP14_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 304) && !defined(__APPLE__) // Clang 3.4+, not including Apple's Clang. // supported. - #elif defined(__GNUC__) && (EA_COMPILER_VERSION >= 5000) // GCC 5+ + #elif defined(EA_COMPILER_CPP14_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 5000) // GCC 5+ // supported. #elif !defined(EA_COMPILER_CPP14_ENABLED) #define EA_COMPILER_NO_VARIABLE_TEMPLATES 1 diff --git a/test/packages/EABase/include/Common/EABase/config/eacompilertraits.h b/test/packages/EABase/include/Common/EABase/config/eacompilertraits.h index 3b6b9e7f..74410d5f 100644 --- a/test/packages/EABase/include/Common/EABase/config/eacompilertraits.h +++ b/test/packages/EABase/include/Common/EABase/config/eacompilertraits.h @@ -68,6 +68,12 @@ * EA_DISABLE_EDG_WARNING / EA_RESTORE_EDG_WARNING * EA_DISABLE_CW_WARNING / EA_RESTORE_CW_WARNING * + * EA_DISABLE_DEFAULT_CTOR + * EA_DISABLE_COPY_CTOR + * EA_DISABLE_MOVE_CTOR + * EA_DISABLE_ASSIGNMENT_OPERATOR + * EA_DISABLE_MOVE_OPERATOR + * * Todo: * Find a way to reliably detect wchar_t size at preprocessor time and * implement it below for EA_WCHAR_SIZE. @@ -1022,7 +1028,7 @@ #define EA_WCHAR_T_NON_NATIVE 1 #endif #endif - #elif defined(EA_COMPILER_MSVC) || defined(EA_COMPILER_BORLAND) + #elif defined(EA_COMPILER_MSVC) || defined(EA_COMPILER_BORLAND) || (defined(EA_COMPILER_CLANG) && defined(EA_PLATFORM_WINDOWS)) #ifndef _NATIVE_WCHAR_T_DEFINED #define EA_WCHAR_T_NON_NATIVE 1 #endif @@ -1796,6 +1802,85 @@ #define EA_FUNCTION_DELETE = delete #endif + // ------------------------------------------------------------------------ + // EA_DISABLE_DEFAULT_CTOR + // + // Disables the compiler generated default constructor. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_DEFAULT_CTOR(Example); + // }; + // + #define EA_DISABLE_DEFAULT_CTOR(ClassName) ClassName() EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EA_DISABLE_COPY_CTOR + // + // Disables the compiler generated copy constructor. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_COPY_CTOR(Example); + // }; + // + #define EA_DISABLE_COPY_CTOR(ClassName) ClassName(const ClassName &) EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EA_DISABLE_MOVE_CTOR + // + // Disables the compiler generated move constructor. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_MOVE_CTOR(Example); + // }; + // + #define EA_DISABLE_MOVE_CTOR(ClassName) ClassName(ClassName&&) EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EA_DISABLE_ASSIGNMENT_OPERATOR + // + // Disables the compiler generated assignment operator. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_ASSIGNMENT_OPERATOR(Example); + // }; + // + #define EA_DISABLE_ASSIGNMENT_OPERATOR(ClassName) ClassName & operator=(const ClassName &) EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EA_DISABLE_MOVE_OPERATOR + // + // Disables the compiler generated move operator. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_MOVE_OPERATOR(Example); + // }; + // + #define EA_DISABLE_MOVE_OPERATOR(ClassName) ClassName & operator=(ClassName&&) EA_FUNCTION_DELETE // ------------------------------------------------------------------------ // EANonCopyable @@ -1867,7 +1952,7 @@ #define EA_OPTIMIZE_OFF() \ _Pragma("GCC push_options") \ _Pragma("GCC optimize 0") - #elif defined(EA_COMPILER_CLANG) + #elif defined(EA_COMPILER_CLANG) && !defined(EA_PLATFORM_ANDROID) // android clang 305 compiler crashes when this pragma is used #define EA_OPTIMIZE_OFF() \ EA_DISABLE_CLANG_WARNING(-Wunknown-pragmas) \ _Pragma("clang optimize off") \ @@ -1882,7 +1967,7 @@ #define EA_OPTIMIZE_ON() __pragma(optimize("", on)) #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION > 4004) && (defined(__i386__) || defined(__x86_64__)) // GCC 4.4+ - Seems to work only on x86/Linux so far. However, GCC 4.4 itself appears broken and screws up parameter passing conventions. #define EA_OPTIMIZE_ON() _Pragma("GCC pop_options") - #elif defined(EA_COMPILER_CLANG) + #elif defined(EA_COMPILER_CLANG) && !defined(EA_PLATFORM_ANDROID) // android clang 305 compiler crashes when this pragma is used #define EA_OPTIMIZE_ON() \ EA_DISABLE_CLANG_WARNING(-Wunknown-pragmas) \ _Pragma("clang optimize on") \ diff --git a/test/packages/EABase/include/Common/EABase/version.h b/test/packages/EABase/include/Common/EABase/version.h index 713a8246..c1780ce2 100644 --- a/test/packages/EABase/include/Common/EABase/version.h +++ b/test/packages/EABase/include/Common/EABase/version.h @@ -29,8 +29,8 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef EABASE_VERSION - #define EABASE_VERSION "2.07.00" - #define EABASE_VERSION_N 20700 + #define EABASE_VERSION "2.07.01" + #define EABASE_VERSION_N 20701 #endif #endif diff --git a/test/packages/EATest/source/EATest.cpp b/test/packages/EATest/source/EATest.cpp index 9182113c..8e05136a 100644 --- a/test/packages/EATest/source/EATest.cpp +++ b/test/packages/EATest/source/EATest.cpp @@ -1163,6 +1163,9 @@ int TestSuite::Run() { ResultInfo& resultInfo = *it; + eastl::string test_name; + resultInfo.mpTest->GetName(test_name); + // If we already have a result for this test, we don't need to run it again. if(resultInfo.mnResult != kTestResultNone) continue; diff --git a/test/source/EASTLTest.h b/test/source/EASTLTest.h index 1d8a3ec7..4416f2c1 100644 --- a/test/source/EASTLTest.h +++ b/test/source/EASTLTest.h @@ -54,13 +54,16 @@ int TestFixedString(); int TestArray(); int TestVector(); int TestFixedVector(); +int TestSegmentedVector(); int TestDeque(); int TestMap(); int TestFixedMap(); +int TestStringMap(); int TestSet(); int TestFixedSet(); int TestHash(); int TestFixedHash(); +int TestStringHashMap(); int TestIntrusiveHash(); int TestVectorMap(); int TestVectorSet(); @@ -75,6 +78,7 @@ int TestIterator(); int TestRatio(); int TestChrono(); int TestOptional(); +int TestAny(); // Now enable warnings as desired. @@ -371,7 +375,7 @@ struct TestObject int mX; // Value for the TestObject. bool mbThrowOnCopy; // Throw an exception of this object is copied, moved, or assigned to another. int64_t mId; // Unique id for each object, equal to its creation number. This value is not coped from other TestObjects during any operations, including moves. - uint32_t mMagicValue; // Used to verify that an instance is valid and that it is not corrupted. It should alwasy be kMagicValue. + uint32_t mMagicValue; // Used to verify that an instance is valid and that it is not corrupted. It should always be kMagicValue. static int64_t sTOCount; // Count of all current existing TestObjects. static int64_t sTOCtorCount; // Count of times any ctor was called. static int64_t sTODtorCount; // Count of times dtor was called. diff --git a/test/source/TestAlgorithm.cpp b/test/source/TestAlgorithm.cpp index 4a5d1585..87506621 100644 --- a/test/source/TestAlgorithm.cpp +++ b/test/source/TestAlgorithm.cpp @@ -626,6 +626,40 @@ static int TestMinMax() } +static int TestClamp() +{ + using namespace eastl; + + int nErrorCount = 0; + + EATEST_VERIFY(eastl::clamp(42, 1, 100) == 42); + EATEST_VERIFY(eastl::clamp(-42, 1, 100) == 1); + EATEST_VERIFY(eastl::clamp(420, 1, 100) == 100); + EATEST_VERIFY(eastl::clamp(1, 1, 100) == 1); + EATEST_VERIFY(eastl::clamp(100, 1, 100) == 100); + + EATEST_VERIFY(eastl::clamp(42.f, 1.f, 100.f, less()) == 42.f); + EATEST_VERIFY(eastl::clamp(-42.f, 1.f, 100.f, less()) == 1.f); + EATEST_VERIFY(eastl::clamp(420.f, 1.f, 100.f, less()) == 100.f); + EATEST_VERIFY(eastl::clamp(1.f, 1.f, 100.f, less()) == 1.f); + EATEST_VERIFY(eastl::clamp(100.f, 1.f, 100.f, less()) == 100.f); + + EATEST_VERIFY(eastl::clamp(42., 1., 100., less()) == 42.); + EATEST_VERIFY(eastl::clamp(-42., 1., 100., less()) == 1.); + EATEST_VERIFY(eastl::clamp(420., 1., 100., less()) == 100.); + EATEST_VERIFY(eastl::clamp(1., 1., 100., less()) == 1.); + EATEST_VERIFY(eastl::clamp(100., 1., 100., less()) == 100.); + + EATEST_VERIFY(eastl::clamp(A(42), A(1), A(100), LessStruct()).a == A(42).a); + EATEST_VERIFY(eastl::clamp(A(-42), A(1), A(100), LessStruct()).a == A(1).a); + EATEST_VERIFY(eastl::clamp(A(420), A(1), A(100), LessStruct()).a == A(100).a); + EATEST_VERIFY(eastl::clamp(A(1), A(1), A(100), LessStruct()).a == A(1).a); + EATEST_VERIFY(eastl::clamp(A(100), A(1), A(100), LessStruct()).a == A(100).a); + + return nErrorCount; +} + + /////////////////////////////////////////////////////////////////////////////// // TestAlgorithm // @@ -642,6 +676,8 @@ int TestAlgorithm() TestObject::Reset(); nErrorCount += TestMinMax(); + nErrorCount += TestClamp(); + // bool all_of (InputIterator first, InputIterator last, Predicate p); // bool any_of (InputIterator first, InputIterator last, Predicate p); diff --git a/test/source/TestAny.cpp b/test/source/TestAny.cpp new file mode 100644 index 00000000..2f95f77f --- /dev/null +++ b/test/source/TestAny.cpp @@ -0,0 +1,389 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLTest.h" +#include +#include +#include +#include +#include + + +// SmallTestObject +// +struct SmallTestObject +{ + static int mCtorCount; + + SmallTestObject() EA_NOEXCEPT { mCtorCount++; } + SmallTestObject(const SmallTestObject&) EA_NOEXCEPT { mCtorCount++; } + SmallTestObject(SmallTestObject&&) EA_NOEXCEPT { mCtorCount++; } + SmallTestObject& operator=(const SmallTestObject&) EA_NOEXCEPT { mCtorCount++; return *this; } + ~SmallTestObject() EA_NOEXCEPT { mCtorCount--; } + + static void Reset() { mCtorCount = 0; } + static bool IsClear() { return mCtorCount == 0; } +}; + +int SmallTestObject::mCtorCount = 0; + + +// RequiresInitList +// +struct RequiresInitList +{ + RequiresInitList(std::initializer_list ilist) + : sum(eastl::accumulate(begin(ilist), end(ilist), 0)) {} + + int sum; +}; + + +int TestAny() +{ + using namespace eastl; + EASTLTest_Printf("TestAny\n"); + int nErrorCount = 0; + + // NOTE(rparolin): Ensure 'any' is at least the size of an eastl::string and an eastl::vector to prevent heap + // allocation of handle objects (objects that point to a heap allocation). This will reduce memory pressure since + // eastl::string will be a commonly used type. We could also test with a vector. + { + static_assert(sizeof(eastl::string) <= sizeof(eastl::any), "ensure that 'any' has enough local memory to store a string"); + static_assert(sizeof(eastl::vector) <= sizeof(eastl::any), "ensure that 'any' has enough local memory to store a vector"); + } + + { + // default construct + any a; + VERIFY(a.has_value() == false); + } + + { + // test object ctors & dtors are called for a large object + TestObject::Reset(); + { any a{TestObject()}; } + VERIFY(TestObject::IsClear()); + } + + { + // test object ctors & dtors are called for a small object + SmallTestObject::Reset(); + { any a{SmallTestObject()}; } + VERIFY(SmallTestObject::IsClear()); + } + + { + any a(42); + VERIFY(a.has_value() == true); + + VERIFY(any_cast(a) == 42); + VERIFY(any_cast(a) != 1337); + any_cast(a) = 10; + VERIFY(any_cast(a) == 10); + + a = 1.f; + any_cast(a) = 1337.f; + VERIFY(any_cast(a) == 1337.f); + + a = 4343; + VERIFY(any_cast(a) == 4343); + + a = string("hello world"); + VERIFY(any_cast(a) == "hello world"); + VERIFY(any_cast(a) == "hello world"); + } + + { + struct custom_type { int data; }; + + any a = custom_type{}; + any_cast(a).data = 42; + VERIFY(any_cast(a).data == 42); + } + + { + any a = 42; + VERIFY(any_cast(a) == 42); + + #if EASTL_EXCEPTIONS_ENABLED + int throwCount = 0; + try { VERIFY(any_cast(a) == 42); } + catch (bad_any_cast) { throwCount++; } + VERIFY(throwCount != 0); + #endif + } + + { + vector va = {42, 'a', 42.f, 3333u, 4444ul, 5555ull, 6666.0}; + + VERIFY(any_cast(va[0]) == 42); + VERIFY(any_cast(va[1]) == 'a'); + VERIFY(any_cast(va[2]) == 42.f); + VERIFY(any_cast(va[3]) == 3333u); + VERIFY(any_cast(va[4]) == 4444ul); + VERIFY(any_cast(va[5]) == 5555ull); + VERIFY(any_cast(va[6]) == 6666.0); + } + + { + any a(string("test string")); + VERIFY(a.has_value()); + VERIFY(any_cast(a) == "test string"); + } + + { + vector va = {42, string("rob"), 'a', 42.f}; + VERIFY(any_cast(va[0]) == 42); + VERIFY(any_cast(va[1]) == "rob"); + VERIFY(any_cast(va[2]) == 'a'); + VERIFY(any_cast(va[3]) == 42.f); + } + + { + vector va; + va.push_back(42); + va.push_back(string("rob")); + va.push_back('a'); + va.push_back(42.f); + + VERIFY(any_cast(va[0]) == 42); + VERIFY(any_cast(va[1]) == "rob"); + VERIFY(any_cast(va[2]) == 'a'); + VERIFY(any_cast(va[3]) == 42.f); + } + + // NOTE(rparolin): Replaces a small 'any' object with a large one and make sure it doesn't corrupt + // the surrounding memory in the vector. + { + TestObject::Reset(); + { + vector va = {42, 'a', 42.f, 3333u, 4444ul, 5555ull, 6666.0}; + + VERIFY(any_cast(va[0]) == 42); + VERIFY(any_cast(va[1]) == 'a'); + VERIFY(any_cast(va[2]) == 42.f); + VERIFY(any_cast(va[3]) == 3333u); + VERIFY(any_cast(va[4]) == 4444ul); + VERIFY(any_cast(va[5]) == 5555ull); + VERIFY(any_cast(va[6]) == 6666.0); + + va[3] = TestObject(3333); // replace a small integral with a large heap allocated object. + + VERIFY(any_cast(va[0]) == 42); + VERIFY(any_cast(va[1]) == 'a'); + VERIFY(any_cast(va[2]) == 42.f); + VERIFY(any_cast(va[3]).mX == 3333); // not 3333u because TestObject ctor takes a signed type. + VERIFY(any_cast(va[4]) == 4444ul); + VERIFY(any_cast(va[5]) == 5555ull); + VERIFY(any_cast(va[6]) == 6666.0); + } + VERIFY(TestObject::IsClear()); + } + + { + any a(string("test string")); + VERIFY(a.has_value()); + a.reset(); + VERIFY(!a.has_value()); + } + + { + any a1 = 42; + any a2 = a1; + + VERIFY(a1.has_value()); + VERIFY(a2.has_value()); + VERIFY(any_cast(a1) == any_cast(a2)); + } + + { + any a1; + VERIFY(!a1.has_value()); + { + any a2(string("test string")); + a1 = any_cast(a2); + + VERIFY(a1.has_value()); + } + VERIFY(any_cast(a1) == "test string"); + VERIFY(a1.has_value()); + } + + { + any a1; + VERIFY(!a1.has_value()); + { + any a2(string("test string")); + a1 = a2; + VERIFY(a1.has_value()); + } + VERIFY(any_cast(a1) == "test string"); + VERIFY(a1.has_value()); + } + + { + any a1 = 42; + any a2 = 24; + VERIFY(any_cast(a1) == 42); + VERIFY(any_cast(a2) == 24); + + a1.swap(a2); + VERIFY(any_cast(a1) == 24); + VERIFY(any_cast(a2) == 42); + + eastl::swap(a1, a2); + VERIFY(any_cast(a1) == 42); + VERIFY(any_cast(a2) == 24); + } + + #if EASTL_RTTI_ENABLED + { + VERIFY(EA::StdC::Strcmp(any(42).type().name(), "int") == 0); + VERIFY(EA::StdC::Strcmp(any(42.f).type().name(), "float") == 0); + VERIFY(EA::StdC::Strcmp(any(42u).type().name(), "unsigned int") == 0); + VERIFY(EA::StdC::Strcmp(any(42ul).type().name(), "unsigned long") == 0); + VERIFY(EA::StdC::Strcmp(any(42l).type().name(), "long") == 0); + } + #endif + + // emplace, small object tests + { + any a; + + a.emplace(42); + VERIFY(a.has_value()); + VERIFY(any_cast(a) == 42); + + a.emplace((short)8); // no way to define a short literal we must cast here. + VERIFY(any_cast(a) == 8); + VERIFY(a.has_value()); + + a.reset(); + VERIFY(!a.has_value()); + } + + // emplace, large object tests + { + TestObject::Reset(); + { + any a; + a.emplace(); + VERIFY(a.has_value()); + } + VERIFY(TestObject::IsClear()); + } + + // emplace, initializer_list + { + { + any a; + a.emplace(std::initializer_list{1,2,3,4,5,6}); + + VERIFY(a.has_value()); + VERIFY(any_cast(a).sum == 21); + } + } + + // equivalence tests + { + any a, b; + VERIFY(!a.has_value() == !b.has_value()); + + #if EASTL_EXCEPTIONS_ENABLED + int bad_any_cast_thrown = 0; + try + { + VERIFY(any_cast(a) == any_cast(b)); + } + catch (eastl::bad_any_cast) + { + bad_any_cast_thrown++; + } + VERIFY(bad_any_cast_thrown != 0); + #endif + + + a = 42; b = 24; + VERIFY(any_cast(a) != any_cast(b)); + VERIFY(a.has_value() == b.has_value()); + + a = 42; b = 42; + VERIFY(any_cast(a) == any_cast(b)); + VERIFY(a.has_value() == b.has_value()); + } + + // move tests + { + any a = string("hello world"); + VERIFY(any_cast(a) == "hello world"); + + auto s = move(any_cast(a)); // move string out + VERIFY(s == "hello world"); + VERIFY(any_cast(a).empty()); + + any_cast(a) = move(s); // move string in + VERIFY(any_cast(a) == "hello world"); + } + + // nullptr tests + { + any* a = nullptr; + VERIFY(any_cast(a) == nullptr); + VERIFY(any_cast(a) == nullptr); + VERIFY(any_cast(a) == nullptr); + VERIFY(any_cast(a) == nullptr); + + any b; + VERIFY(any_cast(&b) == nullptr); + VERIFY(any_cast(&b) == nullptr); + VERIFY(any_cast(&b) == nullptr); + VERIFY(any_cast(&b) == nullptr); + + VERIFY(any_cast(&b) == nullptr); + VERIFY(any_cast(&b) == nullptr); + VERIFY(any_cast(&b) == nullptr); + VERIFY(any_cast(&b) == nullptr); + } + + // Aligned type tests + { + // TODO(rparolin): Aligned types that do not fit into the local buffer optimization require use of global + // operator new that takes the alignment parameter. I'm hesitant to add this dependency as it is not widely + // used in EASTL containers. Consider for a future release. + // + // { + // any a = Align16(1337); + // VERIFY(any_cast(a) == Align16(1337)); + // } + // + // { + // any a = Align32(1337); + // VERIFY(any_cast(a) == Align32(1337)); + // } + // + // { + // any a = Align64(1337); + // VERIFY(any_cast(a) == Align64(1337)); + // } + } + + // make_any + { + { + auto a = make_any(42); + VERIFY(any_cast(a) == 42); + } + + { + auto a = make_any(std::initializer_list{1,2,3,4,5,6,7,8}); + VERIFY(any_cast(a).sum == 36); + } + } + + return nErrorCount; +} + + diff --git a/test/source/TestExtra.cpp b/test/source/TestExtra.cpp index b0904b77..f3f02085 100644 --- a/test/source/TestExtra.cpp +++ b/test/source/TestExtra.cpp @@ -12,9 +12,9 @@ namespace eastl class allocator; template class basic_string; - typedef basic_string string8; + typedef basic_string local_string8; // collides with eastl::string8 in bulkbuilds - static void UseForwardDeclaredString(string8*) + static void UseForwardDeclaredString(local_string8*) { } @@ -162,7 +162,7 @@ static int TestForwardDeclarations() { int nErrorCount = 0; - eastl::string8 s8; + eastl::local_string8 s8; UseForwardDeclaredString(&s8); eastl::vector8 v8; diff --git a/test/source/TestFixedHash.cpp b/test/source/TestFixedHash.cpp index 531a6de4..2f3ba7c1 100644 --- a/test/source/TestFixedHash.cpp +++ b/test/source/TestFixedHash.cpp @@ -560,6 +560,22 @@ int TestFixedHash() nErrorCount += TestMultisetCpp11 >(); } + { + // void reserve(size_type nElementCount); + + // test with overflow enabled. + nErrorCount += HashContainerReserveTest>()(); + nErrorCount += HashContainerReserveTest>()(); + nErrorCount += HashContainerReserveTest>()(); + nErrorCount += HashContainerReserveTest>()(); + + // API prevents testing fixed size hash container reservation without overflow enabled. + // + // nErrorCount += HashContainerReserveTest>()(); + // nErrorCount += HashContainerReserveTest>()(); + // nErrorCount += HashContainerReserveTest>()(); + // nErrorCount += HashContainerReserveTest>()(); + } { // initializer_list support. diff --git a/test/source/TestHash.cpp b/test/source/TestHash.cpp index 90581cf6..8889e872 100644 --- a/test/source/TestHash.cpp +++ b/test/source/TestHash.cpp @@ -335,6 +335,15 @@ int TestHash() } + { + // void reserve(size_type nElementCount); + nErrorCount += HashContainerReserveTest>()(); + nErrorCount += HashContainerReserveTest>()(); + nErrorCount += HashContainerReserveTest>()(); + nErrorCount += HashContainerReserveTest>()(); + } + + { // Test hash_set with cached hash code. // insert_return_type insert(const value_type& value) ; diff --git a/test/source/TestMap.h b/test/source/TestMap.h index fcca6737..36f48fc3 100644 --- a/test/source/TestMap.h +++ b/test/source/TestMap.h @@ -1000,11 +1000,28 @@ int TestMultimapCpp11() return nErrorCount; } +template +struct HashContainerReserveTest +{ + int operator()() + { + int nErrorCount = 0; + HashContainer hashContainer; + const typename HashContainer::size_type reserve_sizes[] = {16, 128, 4096, 32768}; + for (auto& reserve_size : reserve_sizes) + { + hashContainer.reserve(reserve_size); + // verify bucket count and hashtable load_factor requirements + VERIFY(hashContainer.bucket_count() >= reserve_size); + VERIFY(hashContainer.load_factor() <= ceilf(reserve_size / hashContainer.get_max_load_factor())); + } - + return nErrorCount; + } +}; diff --git a/test/source/TestSegmentedVector.cpp b/test/source/TestSegmentedVector.cpp new file mode 100644 index 00000000..7986bba2 --- /dev/null +++ b/test/source/TestSegmentedVector.cpp @@ -0,0 +1,72 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#include "EASTLTest.h" +#include +#include + +// Template instantations. +// These tell the compiler to compile all the functions for the given class. +template class eastl::segmented_vector; +template class eastl::segmented_vector; +template class eastl::segmented_vector; +template class eastl::segmented_vector; + + +int TestSegmentedVector() +{ + EASTLTest_Printf("TestSegmentedVector\n"); + + int nErrorCount = 0; + + TestObject::Reset(); + + { + using namespace eastl; + + // Construct segmented_vectors of different types. + + segmented_vector vectorOfInt; + segmented_vector vectorOfTO; + segmented_vector, 8> vectorOfListOfTO; + + EATEST_VERIFY(vectorOfInt.empty()); + EATEST_VERIFY(vectorOfTO.empty()); + EATEST_VERIFY(vectorOfListOfTO.empty()); + } + + { + using namespace eastl; + + // Test basic segmented_vector operations. + + segmented_vector vectorOfInt; + + vectorOfInt.push_back(42); + EATEST_VERIFY(vectorOfInt.size() == 1); + EATEST_VERIFY(vectorOfInt.segment_count() == 1); + EATEST_VERIFY(vectorOfInt.empty() == false); + + vectorOfInt.push_back(43); + vectorOfInt.push_back(44); + vectorOfInt.push_back(45); + vectorOfInt.push_back(46); + EATEST_VERIFY(vectorOfInt.size() == 5); + EATEST_VERIFY(vectorOfInt.segment_count() == 2); + + EATEST_VERIFY(vectorOfInt.front() == 42); + EATEST_VERIFY(vectorOfInt.back() == 46); + + vectorOfInt.pop_back(); + EATEST_VERIFY(vectorOfInt.size() == 4); + EATEST_VERIFY(vectorOfInt.segment_count() == 1); + + vectorOfInt.clear(); + EATEST_VERIFY(vectorOfInt.empty()); + EATEST_VERIFY(vectorOfInt.size() == 0); + EATEST_VERIFY(vectorOfInt.segment_count() == 0); + } + + return nErrorCount; +} diff --git a/test/source/TestString.inl b/test/source/TestString.inl index 14e6c021..97368e85 100644 --- a/test/source/TestString.inl +++ b/test/source/TestString.inl @@ -28,7 +28,7 @@ int TEST_STRING_NAME() // explicit basic_string(const allocator_type& allocator); { - eastl::allocator alloc; + typename StringType::allocator_type alloc; StringType str(alloc); VERIFY(str.validate()); } diff --git a/test/source/TestStringHashMap.cpp b/test/source/TestStringHashMap.cpp new file mode 100644 index 00000000..6a37d750 --- /dev/null +++ b/test/source/TestStringHashMap.cpp @@ -0,0 +1,221 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLTest.h" +#include +#include + +using namespace eastl; + + +// Template instantations. +// These tell the compiler to compile all the functions for the given class. +template class eastl::string_hash_map; +template class eastl::string_hash_map; + +static const char* strings[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}; +static const size_t kStringCount = 10; // This is intentionally half the length of strings, so that we can test with strings that are not inserted to the map. + + +int TestStringHashMap() +{ + EASTLTest_Printf("TestStringHashMap\n"); + + int nErrorCount = 0; + + { // Test declarations + string_hash_map stringHashMap; + + string_hash_map stringHashMap2(stringHashMap); + EATEST_VERIFY(stringHashMap2.size() == stringHashMap.size()); + EATEST_VERIFY(stringHashMap2 == stringHashMap); + + + // allocator_type& get_allocator(); + // void set_allocator(const allocator_type& allocator); + string_hash_map::allocator_type& allocator = stringHashMap.get_allocator(); + stringHashMap.set_allocator(EASTLAllocatorType()); + stringHashMap.set_allocator(allocator); + // To do: Try to find something better to test here. + + + // const key_equal& key_eq() const; + // key_equal& key_eq(); + string_hash_map hs; + const string_hash_map hsc; + + const string_hash_map::key_equal& ke = hsc.key_eq(); + hs.key_eq() = ke; + + + // const char* get_name() const; + // void set_name(const char* pName); + #if EASTL_NAME_ENABLED + stringHashMap.get_allocator().set_name("test"); + const char* pName = stringHashMap.get_allocator().get_name(); + EATEST_VERIFY(equal(pName, pName + 5, "test")); + #endif + } + + + { + string_hash_map stringHashMap; + + // Clear a newly constructed, already empty container. + stringHashMap.clear(true); + EATEST_VERIFY(stringHashMap.validate()); + EATEST_VERIFY(stringHashMap.size() == 0); + EATEST_VERIFY(stringHashMap.bucket_count() == 1); + + for (int i = 0; i < (int)kStringCount; i++) + stringHashMap.insert(strings[i], i); + + EATEST_VERIFY(stringHashMap.validate()); + EATEST_VERIFY(stringHashMap.size() == kStringCount); + + stringHashMap.clear(true); + EATEST_VERIFY(stringHashMap.validate()); + EATEST_VERIFY(stringHashMap.size() == 0); + EATEST_VERIFY(stringHashMap.bucket_count() == 1); + + for (int i = 0; i < (int)kStringCount; i++) + stringHashMap.insert(strings[i], i); + EATEST_VERIFY(stringHashMap.validate()); + EATEST_VERIFY(stringHashMap.size() == kStringCount); + + stringHashMap.clear(true); + EATEST_VERIFY(stringHashMap.validate()); + EATEST_VERIFY(stringHashMap.size() == 0); + EATEST_VERIFY(stringHashMap.bucket_count() == 1); + } + + + { // Test string_hash_map + + // size_type size() const + // bool empty() const + // insert_return_type insert(const value_type& value); + // insert_return_type insert(const value_type& value, hash_code_t c, node_type* pNodeNew = NULL); + // iterator insert(const_iterator, const value_type& value); + // iterator find(const key_type& k); + // const_iterator find(const key_type& k) const; + // size_type count(const key_type& k) const; + + typedef string_hash_map StringHashMapInt; + + StringHashMapInt stringHashMap; + + EATEST_VERIFY(stringHashMap.empty()); + EATEST_VERIFY(stringHashMap.size() == 0); + EATEST_VERIFY(stringHashMap.count(strings[0]) == 0); + + for (int i = 0; i < (int)kStringCount; i++) + stringHashMap.insert(strings[i], i); + + EATEST_VERIFY(!stringHashMap.empty()); + EATEST_VERIFY(stringHashMap.size() == kStringCount); + EATEST_VERIFY(stringHashMap.count(strings[0]) == 1); + + int j = 0; + for (StringHashMapInt::iterator it = stringHashMap.begin(); it != stringHashMap.end(); ++it, ++j) + { + int value = (*it).second; + EATEST_VERIFY(value < (int)kStringCount); + } + + for(int i = 0; i < (int)kStringCount * 2; i++) + { + StringHashMapInt::iterator it = stringHashMap.find(strings[i]); + + if (i < (int)kStringCount) + { + EATEST_VERIFY(it != stringHashMap.end()); + const char* k = (*it).first; + int v = (*it).second; + EATEST_VERIFY(EA::StdC::Strcmp(k, strings[i]) == 0); + EATEST_VERIFY(v == i); + } + else + EATEST_VERIFY(it == stringHashMap.end()); + } + + StringHashMapInt::insert_return_type result = stringHashMap.insert("EASTLTEST"); + EATEST_VERIFY(result.second == true); + result = stringHashMap.insert("EASTLTEST"); + EATEST_VERIFY(result.second == false); + result.first->second = 0; + + // iterator erase(const_iterator); + size_t nExpectedSize = stringHashMap.size(); + + StringHashMapInt::iterator itD = stringHashMap.find("d"); + EATEST_VERIFY(itD != stringHashMap.end()); + + // erase the element and verify that the size has decreased + stringHashMap.erase(itD); + nExpectedSize--; + EATEST_VERIFY(stringHashMap.size() == nExpectedSize); + + // verify that erased element is gone + itD = stringHashMap.find(strings[3]); + EATEST_VERIFY(itD == stringHashMap.end()); + + // iterator erase(const char*) + StringHashMapInt::size_type n = stringHashMap.erase(strings[4]); + nExpectedSize--; + EATEST_VERIFY(n == 1); + EATEST_VERIFY(stringHashMap.size() == nExpectedSize); + + + // mapped_type& operator[](const key_type& key) + stringHashMap.clear(); + + int x = stringHashMap["A"]; // A default-constructed int (i.e. 0) should be returned. + EATEST_VERIFY(x == 0); + + stringHashMap["B"] = 1; + x = stringHashMap["B"]; + EATEST_VERIFY(x == 1); // Verify that the value we assigned is returned and a default-constructed value is not returned. + + stringHashMap["A"] = 10; // Overwrite our previous 0 with 10. + stringHashMap["B"] = 11; + x = stringHashMap["A"]; + EATEST_VERIFY(x == 10); // Verify the value is as expected. + x = stringHashMap["B"]; + EATEST_VERIFY(x == 11); + + } + + + { + // string_hash_map(const allocator_type& allocator); + // string_hash_map& operator=(const this_type& x); + // bool validate() const; + + string_hash_map stringHashMap1(EASTLAllocatorType("TestStringHashMap")); + string_hash_map stringHashMap2(stringHashMap1); + + for (int i = 0; i < (int)kStringCount; i++) + { + stringHashMap1.insert(strings[i], i); + } + + stringHashMap2 = stringHashMap1; + string_hash_map stringHashMap3(stringHashMap1); + + EATEST_VERIFY(stringHashMap1.validate()); + EATEST_VERIFY(stringHashMap2.validate()); + EATEST_VERIFY(stringHashMap3.validate()); + + for (int i = 0; i < (int)kStringCount; i++) + { + EATEST_VERIFY(stringHashMap1[strings[i]] == stringHashMap2[strings[i]]); + EATEST_VERIFY(stringHashMap1[strings[i]] == stringHashMap3[strings[i]]); + } + + } + + return nErrorCount; +} \ No newline at end of file diff --git a/test/source/TestStringMap.cpp b/test/source/TestStringMap.cpp new file mode 100644 index 00000000..1b4aca04 --- /dev/null +++ b/test/source/TestStringMap.cpp @@ -0,0 +1,209 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLTest.h" +#include +#include + +using namespace eastl; + + +// Template instantations. +// These tell the compiler to compile all the functions for the given class. +template class eastl::string_map; +template class eastl::string_map; + +static const char* strings[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t" }; +static const size_t kStringCount = 10; // This is intentionally half the length of strings, so that we can test with strings that are not inserted to the map. + + +int TestStringMap() +{ + EASTLTest_Printf("TestStringMap\n"); + + int nErrorCount = 0; + + { // Test declarations + string_map stringMap; + + string_map stringMap2(stringMap); + EATEST_VERIFY(stringMap2.size() == stringMap.size()); + EATEST_VERIFY(stringMap2 == stringMap); + + + // allocator_type& get_allocator(); + // void set_allocator(const allocator_type& allocator); + string_map::allocator_type& allocator = stringMap.get_allocator(); + stringMap.set_allocator(EASTLAllocatorType()); + stringMap.set_allocator(allocator); + // To do: Try to find something better to test here. + + + // const char* get_name() const; + // void set_name(const char* pName); +#if EASTL_NAME_ENABLED + stringMap.get_allocator().set_name("test"); + const char* pName = stringMap.get_allocator().get_name(); + EATEST_VERIFY(equal(pName, pName + 5, "test")); +#endif + } + + + { + string_map stringMap; + + // Clear a newly constructed, already empty container. + stringMap.clear(); + EATEST_VERIFY(stringMap.validate()); + EATEST_VERIFY(stringMap.size() == 0); + + for (int i = 0; i < (int)kStringCount; i++) + stringMap.insert(strings[i], i); + + EATEST_VERIFY(stringMap.validate()); + EATEST_VERIFY(stringMap.size() == kStringCount); + + stringMap.clear(); + EATEST_VERIFY(stringMap.validate()); + EATEST_VERIFY(stringMap.size() == 0); + + for (int i = 0; i < (int)kStringCount; i++) + stringMap.insert(strings[i], i); + EATEST_VERIFY(stringMap.validate()); + EATEST_VERIFY(stringMap.size() == kStringCount); + + stringMap.clear(); + EATEST_VERIFY(stringMap.validate()); + EATEST_VERIFY(stringMap.size() == 0); + } + + + { // Test string_map + + // size_type size() const + // bool empty() const + // insert_return_type insert(const value_type& value); + // insert_return_type insert(const value_type& value, hash_code_t c, node_type* pNodeNew = NULL); + // iterator insert(const_iterator, const value_type& value); + // iterator find(const key_type& k); + // const_iterator find(const key_type& k) const; + // size_type count(const key_type& k) const; + + typedef string_map StringMapInt; + + StringMapInt stringMap; + + EATEST_VERIFY(stringMap.empty()); + EATEST_VERIFY(stringMap.size() == 0); + EATEST_VERIFY(stringMap.count(strings[0]) == 0); + + for (int i = 0; i < (int)kStringCount; i++) + stringMap.insert(strings[i], i); + + EATEST_VERIFY(!stringMap.empty()); + EATEST_VERIFY(stringMap.size() == kStringCount); + EATEST_VERIFY(stringMap.count(strings[0]) == 1); + + int j = 0; + for (StringMapInt::iterator it = stringMap.begin(); it != stringMap.end(); ++it, ++j) + { + int value = (*it).second; + EATEST_VERIFY(value < (int)kStringCount); + } + + for (int i = 0; i < (int)kStringCount * 2; i++) + { + StringMapInt::iterator it = stringMap.find(strings[i]); + + if (i < (int)kStringCount) + { + EATEST_VERIFY(it != stringMap.end()); + const char* k = (*it).first; + int v = (*it).second; + EATEST_VERIFY(EA::StdC::Strcmp(k, strings[i]) == 0); + EATEST_VERIFY(v == i); + } + else + EATEST_VERIFY(it == stringMap.end()); + } + + StringMapInt::insert_return_type result = stringMap.insert("EASTLTEST"); + EATEST_VERIFY(result.second == true); + result = stringMap.insert("EASTLTEST"); + EATEST_VERIFY(result.second == false); + result.first->second = 0; + + // iterator erase(const_iterator); + size_t nExpectedSize = stringMap.size(); + + StringMapInt::iterator itD = stringMap.find("d"); + EATEST_VERIFY(itD != stringMap.end()); + + // erase the element and verify that the size has decreased + stringMap.erase(itD); + nExpectedSize--; + EATEST_VERIFY(stringMap.size() == nExpectedSize); + + // verify that erased element is gone + itD = stringMap.find(strings[3]); + EATEST_VERIFY(itD == stringMap.end()); + + // iterator erase(const char*) + StringMapInt::size_type n = stringMap.erase(strings[4]); + nExpectedSize--; + EATEST_VERIFY(n == 1); + EATEST_VERIFY(stringMap.size() == nExpectedSize); + + + // mapped_type& operator[](const key_type& key) + stringMap.clear(); + + int x = stringMap["A"]; // A default-constructed int (i.e. 0) should be returned. + EATEST_VERIFY(x == 0); + + stringMap["B"] = 1; + x = stringMap["B"]; + EATEST_VERIFY(x == 1); // Verify that the value we assigned is returned and a default-constructed value is not returned. + + stringMap["A"] = 10; // Overwrite our previous 0 with 10. + stringMap["B"] = 11; + x = stringMap["A"]; + EATEST_VERIFY(x == 10); // Verify the value is as expected. + x = stringMap["B"]; + EATEST_VERIFY(x == 11); + + } + + + { + // string_map(const allocator_type& allocator); + // string_map& operator=(const this_type& x); + // bool validate() const; + + string_map stringMap1(EASTLAllocatorType("TestStringMap")); + string_map stringMap2(stringMap1); + + for (int i = 0; i < (int)kStringCount; i++) + { + stringMap1.insert(strings[i], i); + } + + stringMap2 = stringMap1; + string_map stringMap3(stringMap1); + + EATEST_VERIFY(stringMap1.validate()); + EATEST_VERIFY(stringMap2.validate()); + EATEST_VERIFY(stringMap3.validate()); + + for (int i = 0; i < (int)kStringCount; i++) + { + EATEST_VERIFY(stringMap1[strings[i]] == stringMap2[strings[i]]); + EATEST_VERIFY(stringMap1[strings[i]] == stringMap3[strings[i]]); + } + + } + + return nErrorCount; +} \ No newline at end of file diff --git a/test/source/TestTuple.cpp b/test/source/TestTuple.cpp index ef004135..19d9e17b 100644 --- a/test/source/TestTuple.cpp +++ b/test/source/TestTuple.cpp @@ -306,6 +306,10 @@ int TestTuple() return nErrorCount; } +#else + +int TestTuple() { return 0; } + #endif // EASTL_TUPLE_ENABLED EA_RESTORE_VC_WARNING() diff --git a/test/source/TestVector.cpp b/test/source/TestVector.cpp index e3c11767..bb81e562 100644 --- a/test/source/TestVector.cpp +++ b/test/source/TestVector.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "ConceptImpls.h" @@ -120,6 +121,19 @@ struct testmovable testmovable& operator=(testmovable&&) EA_NOEXCEPT { return *this; } }; + +#if EASTL_VARIABLE_TEMPLATES_ENABLED + /// custom type-trait which checks if a type is comparable via the > + struct is_less_comparable : eastl::false_type { }; + template + struct is_less_comparable() < eastl::declval())>> : eastl::true_type { }; +#else + // bypass the test since the compiler doesn't support variable templates. + template struct is_less_comparable : eastl::false_type { }; +#endif + + int TestVector() { EASTLTest_Printf("TestVector\n"); @@ -398,7 +412,7 @@ int TestVector() TestObject& r01 = vec01.at(6); EATEST_VERIFY(!(r01 == TestObject(0))); // Should not get here, as exception thrown. } - catch (std::out_of_range& err) { EATEST_VERIFY(true); } + catch (std::out_of_range&) { EATEST_VERIFY(true); } catch (...) { EATEST_VERIFY(false); } #endif } @@ -1400,5 +1414,81 @@ int TestVector() #endif // EASTL_TEST_CONCEPT_IMPLS + { + // validates our vector implementation does not use 'operator<' on input iterators during vector construction. + // + struct container_value_type { int data; }; + struct container_with_custom_iterator + { + struct iterator + { + typedef eastl::input_iterator_tag iterator_category; + typedef int value_type; + typedef ptrdiff_t difference_type; + typedef int* pointer; + typedef int& reference; + + bool operator!=(const iterator& rhs) const { return false; } + iterator& operator++() { return *this; } + iterator operator++(int) { return *this; } + container_value_type operator*() { return {}; } + }; + + container_with_custom_iterator() {} + + iterator begin() const { return {}; } + iterator end() const { return {}; } + bool empty() const { return false; } + + private: + eastl::vector m_vector; + }; + + static_assert(!is_less_comparable::value, "type cannot support comparison by '<' for this test"); + container_with_custom_iterator ci; + eastl::vector v2(ci.begin(), ci.end()); + } + + + // unique_ptr tests + { + // Simple move-assignment test to prevent regressions where eastl::vector utilizes operations on T that are not necessary. + { + eastl::vector> v1; + eastl::vector> v2; + v2 = eastl::move(v1); + } + + { + // This test verifies that eastl::vector can handle the move-assignment case where its utilizes two + // different allocator instances that do not compare equal. An example of an allocator that compares equal + // but isn't the same object instance is an allocator that shares the same memory allocation mechanism (eg. + // malloc). The memory allocated from one instance can be freed by another instance in the case where + // allocators compare equal. This test is verifying functionality in the opposite case where allocators + // instances do not compare equal and must clean up its own allocated memory. + InstanceAllocator::reset_all(); + { + InstanceAllocator a1(uint8_t(0)), a2(uint8_t(1)); + eastl::vector, InstanceAllocator> v1(a1); + eastl::vector, InstanceAllocator> v2(a2); + + VERIFY(v1.get_allocator() != v2.get_allocator()); + + // add some data in the vector so we can move it to the other vector. + v1.push_back(nullptr); + v1.push_back(nullptr); + v1.push_back(nullptr); + v1.push_back(nullptr); + + VERIFY(!v1.empty() && v2.empty()); + v2 = eastl::move(v1); + VERIFY(v1.empty() && !v2.empty()); + v1.swap(v2); + VERIFY(!v1.empty() && v2.empty()); + } + VERIFY(InstanceAllocator::mMismatchCount == 0); + } + } + return nErrorCount; } diff --git a/test/source/main.cpp b/test/source/main.cpp index 06150c51..fb9bd9a9 100644 --- a/test/source/main.cpp +++ b/test/source/main.cpp @@ -83,15 +83,14 @@ int EAMain(int argc, char* argv[]) TestApplication testSuite("EASTL Unit Tests", argc, argv); + testSuite.AddTest("Any", TestAny); testSuite.AddTest("Optional", TestOptional); testSuite.AddTest("TypeTraits", TestTypeTraits); testSuite.AddTest("TestCppCXTypeTraits", TestCppCXTypeTraits); testSuite.AddTest("Extra", TestExtra); testSuite.AddTest("Functional", TestFunctional); testSuite.AddTest("Utility", TestUtility); -#if EASTL_TUPLE_ENABLED testSuite.AddTest("Tuple", TestTuple); -#endif // EASTL_TUPLE_ENABLED testSuite.AddTest("Memory", TestMemory); testSuite.AddTest("Allocator", TestAllocator); testSuite.AddTest("Random", TestRandom); @@ -111,13 +110,16 @@ int EAMain(int argc, char* argv[]) testSuite.AddTest("Array", TestArray); testSuite.AddTest("Vector", TestVector); testSuite.AddTest("FixedVector", TestFixedVector); + testSuite.AddTest("SegmentedVector", TestSegmentedVector); testSuite.AddTest("Deque", TestDeque); testSuite.AddTest("Map", TestMap); testSuite.AddTest("FixedMap", TestFixedMap); + testSuite.AddTest("StringMap", TestStringMap); testSuite.AddTest("Set", TestSet); testSuite.AddTest("FixedSet", TestFixedSet); testSuite.AddTest("Hash", TestHash); testSuite.AddTest("FixedHash", TestFixedHash); + testSuite.AddTest("FixedHash", TestStringHashMap); testSuite.AddTest("IntrusiveHash", TestIntrusiveHash); testSuite.AddTest("VectorMap", TestVectorMap); testSuite.AddTest("VectorSet", TestVectorSet);