From 7fadbf0da01e6f6e0e7038b1b34343a069b8fc51 Mon Sep 17 00:00:00 2001 From: Galo Rojo <100883049+grojo-ea@users.noreply.github.com> Date: Sat, 17 Aug 2024 23:10:53 -0700 Subject: [PATCH] EASTL version 3.21.23 (#536) * 3.21.23 * Add Cmake options to disable deprecations. * Bump EABase git-tag which includes necessary changes for EASTL. * Remove a bunch of unused parameters. * Disable deprecations when specializing templates and template variables using deprecated templates to fix gcc deprecation warnings. * Remove some tests which do not pass on GCC versions prior to 14. --- CMakeLists.txt | 21 +- benchmark/source/BenchmarkAlgorithm.cpp | 56 +- benchmark/source/BenchmarkDeque.cpp | 19 +- benchmark/source/BenchmarkHash.cpp | 12 +- benchmark/source/BenchmarkList.cpp | 28 +- benchmark/source/BenchmarkMap.cpp | 12 +- benchmark/source/BenchmarkSet.cpp | 16 +- benchmark/source/BenchmarkSort.cpp | 12 +- benchmark/source/BenchmarkString.cpp | 2 +- benchmark/source/BenchmarkTupleVector.cpp | 18 +- benchmark/source/BenchmarkVector.cpp | 6 +- benchmark/source/EASTLBenchmark.cpp | 2 +- benchmark/source/EASTLBenchmark.h | 4 +- doc/BestPractices.md | 83 +- doc/Design.md | 2 +- doc/FAQ.md | 2 +- doc/Gotchas.md | 4 +- include/EASTL/algorithm.h | 12 +- include/EASTL/any.h | 28 +- include/EASTL/array.h | 43 +- include/EASTL/bit.h | 1 + include/EASTL/bitset.h | 709 +++---- include/EASTL/bitvector.h | 20 + include/EASTL/bonus/intrusive_sdlist.h | 4 +- include/EASTL/bonus/list_map.h | 22 +- include/EASTL/bonus/lru_cache.h | 27 +- include/EASTL/bonus/overloaded.h | 7 +- include/EASTL/bonus/ring_buffer.h | 4 +- include/EASTL/bonus/tuple_vector.h | 64 +- include/EASTL/chrono.h | 90 +- include/EASTL/deque.h | 182 +- include/EASTL/fixed_vector.h | 22 +- include/EASTL/functional.h | 103 + include/EASTL/hash_map.h | 158 +- include/EASTL/hash_set.h | 46 + include/EASTL/internal/char_traits.h | 154 +- include/EASTL/internal/concepts.h | 48 + include/EASTL/internal/config.h | 227 +-- include/EASTL/internal/copy_help.h | 19 +- include/EASTL/internal/function.h | 4 +- include/EASTL/internal/function_detail.h | 8 +- include/EASTL/internal/functional_base.h | 4 + include/EASTL/internal/generic_iterator.h | 10 +- include/EASTL/internal/hashtable.h | 38 +- include/EASTL/internal/intrusive_hashtable.h | 1 + include/EASTL/internal/red_black_tree.h | 22 +- include/EASTL/internal/type_pod.h | 8 +- include/EASTL/internal/type_properties.h | 12 + include/EASTL/iterator.h | 145 +- include/EASTL/list.h | 58 +- include/EASTL/map.h | 65 + include/EASTL/memory.h | 106 +- include/EASTL/meta.h | 1 + include/EASTL/numeric.h | 183 +- include/EASTL/numeric_limits.h | 1854 ++++++++---------- include/EASTL/optional.h | 200 +- include/EASTL/priority_queue.h | 41 +- include/EASTL/queue.h | 42 +- include/EASTL/random.h | 4 +- include/EASTL/segmented_vector.h | 782 ++++++-- include/EASTL/set.h | 63 + include/EASTL/shared_ptr.h | 49 +- include/EASTL/slist.h | 87 +- include/EASTL/sort.h | 28 +- include/EASTL/span.h | 79 +- include/EASTL/stack.h | 30 +- include/EASTL/string.h | 265 +-- include/EASTL/string_hash_map.h | 119 +- include/EASTL/type_traits.h | 32 + include/EASTL/utility.h | 229 ++- include/EASTL/variant.h | 70 +- include/EASTL/vector.h | 206 +- include/EASTL/vector_map.h | 25 + source/numeric_limits.cpp | 1106 +++++------ test/CMakeLists.txt | 2 +- test/source/EASTLTest.h | 45 +- test/source/EASTLTestIterators.h | 3 + test/source/TestAllocatorPropagate.cpp | 181 ++ test/source/TestAny.cpp | 38 +- test/source/TestAtomicBasic.cpp | 8 +- test/source/TestAtomicMultiThreaded.cpp | 252 +++ test/source/TestBit.cpp | 144 ++ test/source/TestBitset.cpp | 589 +++--- test/source/TestConcepts.cpp | 126 ++ test/source/TestDeque.cpp | 122 +- test/source/TestExtra.cpp | 152 +- test/source/TestFixedVector.cpp | 102 +- test/source/TestFunctional.cpp | 116 +- test/source/TestHash.cpp | 42 +- test/source/TestIntrusiveSDList.cpp | 6 +- test/source/TestIterator.cpp | 22 +- test/source/TestList.cpp | 41 +- test/source/TestLruCache.cpp | 52 +- test/source/TestMemory.cpp | 8 +- test/source/TestNumericLimits.cpp | 21 - test/source/TestOptional.cpp | 438 ++++- test/source/TestRingBuffer.cpp | 39 +- test/source/TestSList.cpp | 38 +- test/source/TestSegmentedVector.cpp | 323 ++- test/source/TestSmartPtr.cpp | 62 + test/source/TestString.inl | 43 + test/source/TestStringHashMap.cpp | 122 ++ test/source/TestStringView.inl | 42 + test/source/TestTypeTraits.cpp | 7 +- test/source/TestUtility.cpp | 208 +- test/source/TestVariant.cpp | 33 +- test/source/TestVector.cpp | 96 +- test/source/main.cpp | 11 +- 108 files changed, 7719 insertions(+), 4080 deletions(-) create mode 100644 include/EASTL/internal/concepts.h create mode 100644 test/source/TestAllocatorPropagate.cpp create mode 100644 test/source/TestAtomicMultiThreaded.cpp create mode 100644 test/source/TestBit.cpp create mode 100644 test/source/TestConcepts.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a57fa595..a5870f8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,11 @@ option(EASTL_BUILD_BENCHMARK "Enable generation of build files for benchmark" OF option(EASTL_BUILD_TESTS "Enable generation of build files for tests" OFF) option(EASTL_STD_ITERATOR_CATEGORY_ENABLED "Enable compatibility with std:: iterator categories" OFF) + +option(EASTL_DISABLE_APRIL_2024_DEPRECATIONS "Enable use of API marked for removal in April 2024." OFF) +option(EASTL_DISABLE_SEPT_2024_DEPRECATIONS "Enable use of API marked for removal in September 2024." OFF) +option(EASTL_DISABLE_APRIL_2025_DEPRECATIONS "Enable use of API marked for removal in April 2025." OFF) + #------------------------------------------------------------------------------------------- # Compiler Flags #------------------------------------------------------------------------------------------- @@ -68,7 +73,7 @@ target_include_directories(EASTL PUBLIC include) FetchContent_Declare( EABase GIT_REPOSITORY https://github.com/electronicarts/EABase.git - GIT_TAG 521cb053d9320636f53226ffc616216cf532f0ef + GIT_TAG 123363eb82e132c0181ac53e43226d8ee76dea12 GIT_SUBMODULES "" # This should be temporary until we update the cyclic submodule dependencies in EABase. ) @@ -76,6 +81,20 @@ FetchContent_MakeAvailable(EABase) target_link_libraries(EASTL EABase) +#------------------------------------------------------------------------------------------- +# Deprecations +#------------------------------------------------------------------------------------------- +if(EASTL_DISABLE_APRIL_2024_DEPRECATIONS) + target_compile_definitions(EASTL PUBLIC EA_DEPRECATIONS_FOR_2024_APRIL=EA_DISABLED) +endif() +if(EASTL_DISABLE_SEPT_2024_DEPRECATIONS) + target_compile_definitions(EASTL PUBLIC EA_DEPRECATIONS_FOR_2024_SEPT=EA_DISABLED) +endif() +if(EASTL_DISABLE_APRIL_2025_DEPRECATIONS) + target_compile_definitions(EASTL PUBLIC EA_DEPRECATIONS_FOR_2025_APRIL=EA_DISABLED) +endif() + + #------------------------------------------------------------------------------------------- # Installation #------------------------------------------------------------------------------------------- diff --git a/benchmark/source/BenchmarkAlgorithm.cpp b/benchmark/source/BenchmarkAlgorithm.cpp index 4c960ba8..e87aa3d3 100644 --- a/benchmark/source/BenchmarkAlgorithm.cpp +++ b/benchmark/source/BenchmarkAlgorithm.cpp @@ -79,7 +79,7 @@ namespace std::string::const_iterator it = std::find_end(sTest.begin(), sTest.end(), pSearchStringBegin, pSearchStringEnd); stopwatch.Stop(); if(it != sTest.end()) - sprintf(Benchmark::gScratchBuffer, "%c", *it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%c", *it); } void TestFindEndEa(EA::StdC::Stopwatch& stopwatch, const eastl::string& sTest, const char* pSearchStringBegin, const char* pSearchStringEnd) @@ -88,7 +88,7 @@ namespace eastl::string::const_iterator it = eastl::find_end(sTest.begin(), sTest.end(), pSearchStringBegin, pSearchStringEnd); stopwatch.Stop(); if(it != sTest.end()) - sprintf(Benchmark::gScratchBuffer, "%c", *it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%c", *it); } @@ -99,7 +99,7 @@ namespace std::string::const_iterator it = std::search(sTest.begin(), sTest.end(), pSearchStringBegin, pSearchStringEnd); stopwatch.Stop(); if(it != sTest.end()) - sprintf(Benchmark::gScratchBuffer, "%c", *it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%c", *it); } void TestSearchEa(EA::StdC::Stopwatch& stopwatch, const eastl::string& sTest, const char* pSearchStringBegin, const char* pSearchStringEnd) @@ -108,7 +108,7 @@ namespace eastl::string::const_iterator it = eastl::search(sTest.begin(), sTest.end(), pSearchStringBegin, pSearchStringEnd); stopwatch.Stop(); if(it != sTest.end()) - sprintf(Benchmark::gScratchBuffer, "%c", *it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%c", *it); } @@ -119,7 +119,7 @@ namespace std::string::const_iterator it = std::search_n(sTest.begin(), sTest.end(), n, c); stopwatch.Stop(); if(it != sTest.end()) - sprintf(Benchmark::gScratchBuffer, "%c", *it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%c", *it); } void TestSearchNEa(EA::StdC::Stopwatch& stopwatch, const eastl::string& sTest, int n, char c) @@ -128,7 +128,7 @@ namespace eastl::string::const_iterator it = eastl::search_n(sTest.begin(), sTest.end(), n, c); stopwatch.Stop(); if(it != sTest.end()) - sprintf(Benchmark::gScratchBuffer, "%c", *it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%c", *it); } @@ -159,7 +159,7 @@ namespace stopwatch.Restart(); const typename Container::const_iterator it = std::min_element(c.begin(), c.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &it); } template @@ -168,7 +168,7 @@ namespace stopwatch.Restart(); const typename Container::const_iterator it = eastl::min_element(c.begin(), c.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &it); } @@ -179,7 +179,7 @@ namespace stopwatch.Restart(); const typename Container::difference_type n = std::count(c.begin(), c.end(), (typename Container::value_type)999999); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%d", (int)n); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", (int)n); } template @@ -188,7 +188,7 @@ namespace stopwatch.Restart(); const typename Container::difference_type n = eastl::count(c.begin(), c.end(), (typename Container::value_type)999999); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%d", (int)n); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", (int)n); } @@ -199,7 +199,7 @@ namespace stopwatch.Restart(); const typename Container::const_iterator it = std::adjacent_find(c.begin(), c.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &it); } template @@ -208,7 +208,7 @@ namespace stopwatch.Restart(); const typename Container::const_iterator it = eastl::adjacent_find(c.begin(), c.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &it); } @@ -301,7 +301,7 @@ namespace stopwatch.Restart(); const bool bResult = std::lexicographical_compare(first1, last1, first2, last2); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%d", bResult ? (int)1 : (int)0); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", bResult ? (int)1 : (int)0); } template @@ -310,7 +310,7 @@ namespace stopwatch.Restart(); const bool bResult = eastl::lexicographical_compare(first1, last1, first2, last2); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%d", bResult ? (int)1 : (int)0); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", bResult ? (int)1 : (int)0); } @@ -321,7 +321,7 @@ namespace stopwatch.Restart(); std::copy(first, last, result); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%d", (int)*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", (int)*first); } template @@ -330,7 +330,7 @@ namespace stopwatch.Restart(); eastl::copy(first, last, result); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%d", (int)*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", (int)*first); } @@ -341,7 +341,7 @@ namespace stopwatch.Restart(); std::copy_backward(first, last, result); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%d", (int)*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", (int)*first); } template @@ -350,7 +350,7 @@ namespace stopwatch.Restart(); eastl::copy_backward(first, last, result); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%d", (int)*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", (int)*first); } @@ -361,7 +361,7 @@ namespace stopwatch.Restart(); std::fill(first, last, v); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*first); } template @@ -370,7 +370,7 @@ namespace stopwatch.Restart(); eastl::fill(first, last, v); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*first); } @@ -381,7 +381,7 @@ namespace stopwatch.Restart(); std::fill_n(first, n, v); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*first); } template @@ -390,7 +390,7 @@ namespace stopwatch.Restart(); eastl::fill_n(first, n, v); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*first); } @@ -401,7 +401,7 @@ namespace stopwatch.Restart(); std::reverse(first, last); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*first); } template @@ -410,7 +410,7 @@ namespace stopwatch.Restart(); eastl::reverse(first, last); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*first); } @@ -421,7 +421,7 @@ namespace stopwatch.Restart(); std::rotate(first, middle, last); // C++11 specifies that rotate has a return value, but not all std implementations return it. stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*first); } template @@ -430,7 +430,7 @@ namespace stopwatch.Restart(); eastl::rotate(first, middle, last); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*first); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*first); } template @@ -439,7 +439,7 @@ namespace stopwatch.Restart(); std::merge(firstIn1, lastIn1, firstIn2, lastIn2, out); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*out); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*out); } template @@ -448,7 +448,7 @@ namespace stopwatch.Restart(); eastl::merge(firstIn1, lastIn1, firstIn2, lastIn2, out); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p", &*out); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*out); } } // namespace diff --git a/benchmark/source/BenchmarkDeque.cpp b/benchmark/source/BenchmarkDeque.cpp index d3c69dea..2d06bf79 100644 --- a/benchmark/source/BenchmarkDeque.cpp +++ b/benchmark/source/BenchmarkDeque.cpp @@ -56,15 +56,6 @@ namespace } -EASTL_DECLARE_POD(ValuePair) -EASTL_DECLARE_TRIVIAL_CONSTRUCTOR(ValuePair) -EASTL_DECLARE_TRIVIAL_COPY(ValuePair) -EASTL_DECLARE_TRIVIAL_ASSIGN(ValuePair) -EASTL_DECLARE_TRIVIAL_DESTRUCTOR(ValuePair) -EASTL_DECLARE_TRIVIAL_RELOCATE(ValuePair) - - - typedef std::deque StdDeque; typedef eastl::deque EaDeque; // What value do we pick for the subarray size to make the comparison fair? Using the default isn't ideal because it results in this test measuring speed efficiency and ignoring memory efficiency. @@ -106,7 +97,7 @@ namespace for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += c[j].key; stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(temp & 0xffffffff)); } @@ -119,7 +110,7 @@ namespace ++it; stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(*it).key); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(*it).key); /* Alternative way to measure: const eastl_size_t n = c.size(); @@ -128,7 +119,7 @@ namespace ++it; stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(*it).key); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(*it).key); */ } @@ -143,7 +134,7 @@ namespace typename Container::iterator it = eastl::find(c.begin(), c.end(), vp); stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(*it).key); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(*it).key); } @@ -156,7 +147,7 @@ namespace stopwatch.Restart(); eastl::quick_sort(c.begin(), c.end(), vpCompare); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c[0].key); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c[0].key); } diff --git a/benchmark/source/BenchmarkHash.cpp b/benchmark/source/BenchmarkHash.cpp index 35470e76..6465954d 100644 --- a/benchmark/source/BenchmarkHash.cpp +++ b/benchmark/source/BenchmarkHash.cpp @@ -81,7 +81,7 @@ namespace typename Container::const_iterator it = eastl::find(c.begin(), c.end(), findValue); // It shouldn't matter what find implementation we use here, as it merely iterates values. stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%p", &*it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*it); } @@ -151,7 +151,7 @@ namespace ++pArrayBegin; } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)temp); } @@ -165,7 +165,7 @@ namespace ++pArrayBegin; } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); } @@ -196,7 +196,7 @@ namespace } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p %p", &c, &it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p %p", &c, &it); } @@ -213,7 +213,7 @@ namespace stopwatch.Restart(); c.erase(it1, it2); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p %p %p", &c, &it1, &it2); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p %p %p", &c, &it1, &it2); } @@ -223,7 +223,7 @@ namespace stopwatch.Restart(); c.clear(); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); } diff --git a/benchmark/source/BenchmarkList.cpp b/benchmark/source/BenchmarkList.cpp index 1d22ad8a..04869d45 100644 --- a/benchmark/source/BenchmarkList.cpp +++ b/benchmark/source/BenchmarkList.cpp @@ -46,7 +46,7 @@ namespace stopwatch.Restart(); Container c(cs.begin(), cs.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.back().mX); } @@ -56,7 +56,7 @@ namespace stopwatch.Restart(); Container c(10000); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.back().mX); } @@ -67,7 +67,7 @@ namespace while(pTOBegin != pTOEnd) c.push_back(*pTOBegin++); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.back().mX); } @@ -86,7 +86,7 @@ namespace it = c.begin(); } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.back().mX); } @@ -103,36 +103,36 @@ namespace template void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c, const TestObject& to) { - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); stopwatch.Restart(); typename Container::iterator it = eastl::find(c.begin(), c.end(), to); stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%d", (*it).mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%d", (*it).mX); } template void TestReverse(EA::StdC::Stopwatch& stopwatch, Container& c) { - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); stopwatch.Restart(); c.reverse(); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.back().mX); } template void TestRemove(EA::StdC::Stopwatch& stopwatch, Container& c, const TestObject* pTOBegin, const TestObject* const pTOEnd) { - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); stopwatch.Restart(); while(pTOBegin != pTOEnd) c.remove(*pTOBegin++); stopwatch.Stop(); if(!c.empty()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.back().mX); } @@ -141,12 +141,12 @@ namespace { typename Container::iterator it = c.begin(); int i = 0, iEnd = (int)cSource.size() - 5; - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); stopwatch.Restart(); while(i++ != iEnd) c.splice(it, cSource, cSource.begin()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.back().mX); } @@ -155,7 +155,7 @@ namespace { typename Container::iterator it = c.begin(); int i = 0, iEnd = (int)c.size() - 5; - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); stopwatch.Restart(); while(i++ != iEnd) { @@ -167,7 +167,7 @@ namespace it = c.begin(); } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.back().mX); } } // namespace diff --git a/benchmark/source/BenchmarkMap.cpp b/benchmark/source/BenchmarkMap.cpp index d2fc35e3..afebd11d 100644 --- a/benchmark/source/BenchmarkMap.cpp +++ b/benchmark/source/BenchmarkMap.cpp @@ -42,7 +42,7 @@ namespace typename Container::const_iterator it = eastl::find(c.begin(), c.end(), findValue); // It shouldn't matter what find implementation we use here, as it merely iterates values. stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%p", &*it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p", &*it); } @@ -83,7 +83,7 @@ namespace ++pArrayBegin; } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)temp); } @@ -136,7 +136,7 @@ namespace ++pArrayBegin; } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); } @@ -166,7 +166,7 @@ namespace ++it; } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p %p", &c, &it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p %p", &c, &it); } @@ -183,7 +183,7 @@ namespace stopwatch.Restart(); c.erase(it1, it2); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%p %p %p", &c, &it1, &it2); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%p %p %p", &c, &it1, &it2); } @@ -193,7 +193,7 @@ namespace stopwatch.Restart(); c.clear(); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); } diff --git a/benchmark/source/BenchmarkSet.cpp b/benchmark/source/BenchmarkSet.cpp index 4a58b1a7..4eb8aebf 100644 --- a/benchmark/source/BenchmarkSet.cpp +++ b/benchmark/source/BenchmarkSet.cpp @@ -45,7 +45,7 @@ namespace typename Container::const_iterator it = eastl::find(c.begin(), c.end(), uint32_t(9999999)); stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)*it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)*it); } @@ -61,7 +61,7 @@ namespace temp += *it; } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)temp); } @@ -73,7 +73,7 @@ namespace while(pArrayBegin != pArrayEnd) temp += c.count(*pArrayBegin++); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)temp); } @@ -89,7 +89,7 @@ namespace temp += *it; // We know that it != end because earlier we inserted 0xffffffff. } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)temp); } @@ -105,7 +105,7 @@ namespace temp += *it; // We know that it != end because earlier we inserted 0xffffffff. } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)temp); } @@ -119,7 +119,7 @@ namespace temp += *(c.equal_range(*pArrayBegin++).first); // We know that it != end because earlier we inserted 0xffffffff. } stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)temp); } @@ -130,7 +130,7 @@ namespace while(pArrayBegin != pArrayEnd) c.erase(*pArrayBegin++); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); } @@ -185,7 +185,7 @@ namespace stopwatch.Restart(); c.clear(); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size()); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)c.size()); } diff --git a/benchmark/source/BenchmarkSort.cpp b/benchmark/source/BenchmarkSort.cpp index ccd2f436..93fb43ad 100644 --- a/benchmark/source/BenchmarkSort.cpp +++ b/benchmark/source/BenchmarkSort.cpp @@ -95,7 +95,7 @@ namespace stopwatch.Restart(); std::sort(stdVectorVP.begin(), stdVectorVP.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)stdVectorVP[0].key); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)stdVectorVP[0].key); } @@ -104,7 +104,7 @@ namespace stopwatch.Restart(); eastl::quick_sort(eaVectorVP.begin(), eaVectorVP.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eaVectorVP[0].key); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)eaVectorVP[0].key); } @@ -113,7 +113,7 @@ namespace stopwatch.Restart(); std::sort(stdVectorInt.begin(), stdVectorInt.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)stdVectorInt[0]); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)stdVectorInt[0]); } @@ -122,7 +122,7 @@ namespace stopwatch.Restart(); eastl::quick_sort(eaVectorInt.begin(), eaVectorInt.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eaVectorInt[0]); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)eaVectorInt[0]); } @@ -131,7 +131,7 @@ namespace stopwatch.Restart(); std::sort(stdVectorTO.begin(), stdVectorTO.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)stdVectorTO[0].mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)stdVectorTO[0].mX); } @@ -140,7 +140,7 @@ namespace stopwatch.Restart(); eastl::quick_sort(eaVectorTO.begin(), eaVectorTO.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eaVectorTO[0].mX); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)eaVectorTO[0].mX); } } // namespace diff --git a/benchmark/source/BenchmarkString.cpp b/benchmark/source/BenchmarkString.cpp index 5dfefbcb..11ad2c0e 100644 --- a/benchmark/source/BenchmarkString.cpp +++ b/benchmark/source/BenchmarkString.cpp @@ -95,7 +95,7 @@ namespace for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += c[j]; stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)temp); } diff --git a/benchmark/source/BenchmarkTupleVector.cpp b/benchmark/source/BenchmarkTupleVector.cpp index 3a8e79dd..07996550 100644 --- a/benchmark/source/BenchmarkTupleVector.cpp +++ b/benchmark/source/BenchmarkTupleVector.cpp @@ -212,7 +212,7 @@ namespace for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += c[j]; stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(temp & 0xffffffff)); } void TestBracket(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c) @@ -222,7 +222,7 @@ namespace for (typename EaTupleVectorUint64::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += eastl::get<0>(c[j]); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(temp & 0xffffffff)); } template @@ -233,7 +233,7 @@ namespace iterator_t it = eastl::find(c.begin(), c.end(), UINT64_C(0xffffffffffff)); stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)*it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)*it); } void TestFind(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c) @@ -243,7 +243,7 @@ namespace EaTupleVectorUint64::iterator it = eastl::find(c.begin(), c.end(), val); stopwatch.Stop(); if (it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eastl::get<0>(*it)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)eastl::get<0>(*it)); } template @@ -254,7 +254,7 @@ namespace stopwatch.Restart(); eastl::quick_sort(c.begin(), c.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(c[0] & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(c[0] & 0xffffffff)); } void TestSort(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c) @@ -264,7 +264,7 @@ namespace stopwatch.Restart(); eastl::quick_sort(c.begin(), c.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(eastl::get<0>(c[0]) & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(eastl::get<0>(c[0]) & 0xffffffff)); } @@ -354,7 +354,7 @@ namespace for (typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += eastl::get<0>(c[j]); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(temp & 0xffffffff)); } @@ -366,7 +366,7 @@ namespace iterator_t it = eastl::find_if(c.begin(), c.end(), [](auto tup) { return eastl::get<0>(tup) == 0xFFFFFFFF; }); stopwatch.Stop(); if (it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eastl::get<0>(*it)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)eastl::get<0>(*it)); } template @@ -377,7 +377,7 @@ namespace stopwatch.Restart(); eastl::quick_sort(c.begin(), c.end(), [](auto a, auto b) { return eastl::get<0>(a) < eastl::get<0>(b); }); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(eastl::get<0>(c[0]) & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(eastl::get<0>(c[0]) & 0xffffffff)); } template diff --git a/benchmark/source/BenchmarkVector.cpp b/benchmark/source/BenchmarkVector.cpp index 93315309..12bcc973 100644 --- a/benchmark/source/BenchmarkVector.cpp +++ b/benchmark/source/BenchmarkVector.cpp @@ -200,7 +200,7 @@ namespace for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += c[j]; stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(temp & 0xffffffff)); } @@ -212,7 +212,7 @@ namespace iterator_t it = eastl::find(c.begin(), c.end(), UINT64_C(0xffffffffffff)); stopwatch.Stop(); if(it != c.end()) - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)*it); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)*it); } @@ -224,7 +224,7 @@ namespace stopwatch.Restart(); eastl::quick_sort(c.begin(), c.end()); stopwatch.Stop(); - sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(c[0] & 0xffffffff)); + EA::StdC::Snprintf(Benchmark::gScratchBuffer, Benchmark::kScratchBufferSize, "%u", (unsigned)(c[0] & 0xffffffff)); } diff --git a/benchmark/source/EASTLBenchmark.cpp b/benchmark/source/EASTLBenchmark.cpp index 8e4d3ae8..0b5ad986 100644 --- a/benchmark/source/EASTLBenchmark.cpp +++ b/benchmark/source/EASTLBenchmark.cpp @@ -91,7 +91,7 @@ namespace Benchmark // Scratch sprintf buffer - char gScratchBuffer[1024]; + char gScratchBuffer[kScratchBufferSize]; void DoNothing(...) diff --git a/benchmark/source/EASTLBenchmark.h b/benchmark/source/EASTLBenchmark.h index a0833e62..c69ee46a 100644 --- a/benchmark/source/EASTLBenchmark.h +++ b/benchmark/source/EASTLBenchmark.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -92,7 +93,8 @@ namespace Benchmark // Scratch sprintf buffer - extern char gScratchBuffer[1024]; + const int kScratchBufferSize = 1024; + extern char gScratchBuffer[kScratchBufferSize]; diff --git a/doc/BestPractices.md b/doc/BestPractices.md index cadb7fa7..bb42b8c5 100644 --- a/doc/BestPractices.md +++ b/doc/BestPractices.md @@ -28,11 +28,11 @@ The descriptions here are intentionally terse; this is to make them easier to vi 20. [Consider bitvector or bitset instead of vector\.](#consider-bitvector-or-bitset-instead-of-vector) 21. [Vectors can be treated as contiguous memory.](#vectors-can-be-treated-as-contiguous-memory) 22. [Search hash_map\ via find_as() instead of find().](#search-hash_map-via-find_as-instead-of-find) -23. [Take advantage of type_traits (e.g. EASTL_DECLARE_TRIVIAL_RELOCATE).](#take-advantage-of-type_traits-eg-eastl_declare_trivial_relocate) +23. [Take advantage of type_traits.](#take-advantage-of-type_traits) 24. [Name containers to track memory usage.](#name-containers-to-track-memory-usage) 25. [Learn the algorithms.](#learn-the-algorithms) 26. [Pass and return containers by reference instead of value.](#pass-and-return-containers-by-reference-instead-of-value) -27. [Consider using reset() for fast container teardown.](#consider-using-reset-for-fast-container-teardown) +27. [Consider using reset_lose_memory() for fast container teardown.](#consider-using-reset_lose_memory-for-fast-container-teardown) 28. [Consider using fixed_substring instead of copying strings.](#consider-using-fixed_substring-instead-of-copying-strings) 29. [Consider using vector::push_back(void).](#consider-using-vectorpush_backvoid) @@ -464,7 +464,7 @@ hash_map hashMap; hash_map::iterator it = hashMap.find_as("hello"); // Using default hash and compare. ``` -### Take advantage of type_traits (e.g. EASTL_DECLARE_TRIVIAL_RELOCATE). +### Take advantage of type_traits. EASTL includes a fairly serious type traits library that is on par with the one found in Boost but offers some additional performance-enhancing help as well. The type_traits library provides information about class *types*, as opposed to class instances. For example, the is_integral type trait tells if a type is one of int, short, long, char, uint64_t, etc. @@ -474,8 +474,6 @@ There are three primary uses of type traits: * Allowing for different logic pathways based on data types. * Allowing for compile-type assertions about data type expectations. -Most of the type traits are automatically detected and implemented by the compiler. However, EASTL allows for the user to explicitly give the compiler hints about type traits that the compiler cannot know, via the EASTL_DECLARE declarations. If the user has a class that is relocatable (i.e. can safely use memcpy to copy values), the user can use the EASTL_DECLARE_TRIVIAL_RELOCATE declaration to tell the compiler that the class can be copied via memcpy. This will automatically significantly speed up some containers and algorithms that use that class. - Here is an example of using type traits to tell if a value is a floating point value or not: ```cpp @@ -488,62 +486,15 @@ template } ``` -Here is an example of declaring a class as relocatable and using it in a vector. - -```cpp -EASTL_DECLARE_TRIVIAL_RELOCATE(Widget); // Usually you put this at the Widget class declaration. - - vector wVector; - - wVector.erase(wVector.begin()); // This operation will be optimized via using memcpy. -``` - -The following is a full list of the currently recognized type traits. Most of these are implemented as of this writing, but if there is one that is missing, feel free to contact the maintainer of this library and request that it be completed. - -* is_void -* is_integral -* is_floating_point -* is_arithmetic -* is_fundamental -* is_const -* is_volatile -* is_abstract -* is_signed -* is_unsigned -* is_array -* is_pointer -* is_reference -* is_member_object_pointer -* is_member_function_pointer -* is_member_pointer -* is_enum -* is_union -* is_class -* is_polymorphic -* is_function -* is_object -* is_scalar -* is_compound -* is_same -* is_convertible -* is_base_of -* is_empty -* is_pod -* is_aligned -* has_trivial_constructor -* has_trivial_copy -* has_trivial_assign -* has_trivial_destructor -* has_trivial_relocate1 -* has_nothrow_constructor -* has_nothrow_copy -* has_nothrow_assign -* has_virtual_destructor -* alignment_of -* rank -* extent -* -1 has_trivial_relocate is not found in Boost nor the C++ standard update proposal. However, it is very useful in allowing for the generation of optimized object moving operations. It is similar to the is_pod type trait, but goes further and allows non-pod classes to be categorized as relocatable. Such categorization is something that no compiler can do, as only the user can know if it is such. Thus EASTL_DECLARE_TRIVIAL_RELOCATE is provided to allow the user to give the compiler a hint. +Here is an example of declaring a class that is trivially copyable and using it in a vector. + +```cpp + class Widget { ... }; // Anything conforming to the trivially copyable rules: https://en.cppreference.com/w/cpp/language/classes#Trivially_copyable_class + + vector wVector{ ... some elements ... }; + + wVector.erase(wVector.begin()); // This operation will be optimized to memcpy the elements into place. +``` ### Name containers to track memory usage. @@ -667,20 +618,20 @@ void DoSomething(list widgetList) { The problem with the above is that widgetList is passed by value and not by reference. Thus the a copy of the container is made and passed instead of a reference of the container being passed. This may seem obvious to some but this happens periodically and the compiler gives no warning and the code will often execute properly, but inefficiently. Of course there are some occasions where you really do want to pass values instead of references. -### Consider using reset() for fast container teardown. +### Consider using reset_lose_memory() for fast container teardown. -EASTL containers have a reset function which unilaterally resets the container to a newly constructed state. The contents of the container are forgotten; no destructors are called and no memory is freed. This is a risky but power function for the purpose of implementing very fast temporary containers. There are numerous cases in high performance programming when you want to create a temporary container out of a scratch buffer area, use the container, and then just "vaporize" it, as it would be waste of time to go through the trouble of clearing the container and destroying and freeing the objects. Such functionality is often used with hash tables or maps and with a stack allocator (a.k.a. linear allocator). +EASTL containers have a reset_lose_memory function which unilaterally resets the container to a newly constructed state. The contents of the container are forgotten; no destructors are called and no memory is freed. This is a risky but powerful function for the purpose of implementing very fast temporary containers. There are numerous cases in high performance programming when you want to create a temporary container out of a scratch buffer area, use the container, and then just "vaporize" it, as it would be waste of time to go through the trouble of clearing the container and destroying and freeing the objects. Such functionality is often used with hash tables or maps and with a stack allocator (a.k.a. linear allocator). Here's an example of usage of the reset function and a PPMalloc-like StackAllocator: ```cpp -pStackAllocator->push_bookmark(); + pStackAllocator->push_bookmark(); hash_set, StackAllocator> wSet(pStackAllocator); - + - wSet.reset(); + wSet.reset_lose_memory(); pStackAllocator->pop_bookmark(); ``` diff --git a/doc/Design.md b/doc/Design.md index 5877bb78..bda7378e 100644 --- a/doc/Design.md +++ b/doc/Design.md @@ -306,7 +306,7 @@ EASTL algorithms very much follow the philosophy of standard C++ algorithms, as EASTL algorithms are optimized at least as well as the best STL algorithms found in commercial libraries and are significantly optimized over the algorithms that come with the first-party STLs that come with compilers. Most significantly, EASTL algorithms take advantage of type traits of contained classes and take advantage of iterator types to optimize code generation. For example, if you resize an array of integers (or other "pod" type), EASTL will detect that this can be done with a memcpy instead of a slow object-by-object move as would Micrsoft STL. -The optimizations found in EASTL algorithms and the supporting code in EASTL type traits consists of some fairly tricky advanced C++ and while it is fairly easy to read, it requires a C++ expert (language lawyer, really) to implement confidently. The result of this is that it takes more effort to develop and maintain EASTL than it would to maintain a simpler library. However, the performance advantages have been deemed worth the tradeoff. +The optimizations found in EASTL algorithms and the supporting code in EASTL type traits consistts of some fairly tricky advanced C++ and while it is fairly easy to read, it requires a C++ expert (language lawyer, really) to implement confidently. The result of this is that it takes more effort to develop and maintain EASTL than it would to maintain a simpler library. However, the performance advantages have been deemed worth the tradeoff. ## Smart Pointer Design diff --git a/doc/FAQ.md b/doc/FAQ.md index 1444c483..e80172b9 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -211,7 +211,7 @@ EASTL additions/amendments to std STL * vector and string have set_capacity(). * string has sprintf(), append_sprintf(), trim(), compare_i(), make_lower(), make_upper(). * deque allows you to specify the subarray size. -* list has a push_front(void) and push_back(void) function. +* list has a push_back(void) and push_back(void) function. * hash_map, hash_set, etc. have find_as(). EASTL coverage of TR1 (tr1 refers to proposed additions for the next C++ standard library, ~2008) diff --git a/doc/Gotchas.md b/doc/Gotchas.md index aefe362e..610994fe 100644 --- a/doc/Gotchas.md +++ b/doc/Gotchas.md @@ -47,7 +47,7 @@ With some containers, modifications of them may invalidate iterators into them. ### Vector resizing may cause ctor/dtor cascades. -If elements are inserted into a vector in middle of the sequence, the elements from the insertion point to the end will be copied upward. This will necessarily cause a series of element constructions and destructions as the elements are copied upward. Similarly, if an element is appended to a vector but the vector capacity is exhausted and needs to be reallocated, the entire vector will undergo a construction and destruction pass as the values are copied to the new storage. This issue exists for deque as well, though to a lesser degree. For vector, the resolution is to reserve enough space in your vector to prevent such reallocation. For deque the resolution is to set its subarray size to enough to prevent such reallocation. Another solution that can often be used is to take advantage of the has_trivial_relocate type trait, which can cause such moves to happen via memcpy instead of via ctor/dtor calls. If your class can be safely memcpy'd, you can use EASTL_DECLARE_TRIVIAL_RELOCATE to tell the compiler it can be memcpy'd. Note that built-in scalars (e.g. int) already are automatically memcpy'd by EASTL. +If elements are inserted into a vector in middle of the sequence, the elements from the insertion point to the end will be copied upward. This will necessarily cause a series of element constructions and destructions as the elements are copied upward. Similarly, if an element is appended to a vector but the vector capacity is exhausted and needs to be reallocated, the entire vector will undergo a construction and destruction pass as the values are copied to the new storage. This issue exists for deque as well, though to a lesser degree. For vector, the resolution is to reserve enough space in your vector to prevent such reallocation. For deque the resolution is to set its subarray size to enough to prevent such reallocation. Another solution that can often be used is to design your element type to be trivially copyable. A good way to communicate this requirement/optimization is to static assert is_trivially_copyable. eastl optimizes such types to a memcpy/memmove, eliding a call to the copy constructor. ### Vector and string insert/push_back/resize can reallocate. @@ -89,7 +89,7 @@ Some containers (vector and deque in particular) calculate their size by pointer ### Be careful making custom Compare functions. -A Compare function compares two values and returns true if the first is less than the second. This is easy to understand for integers and strings, but harder to get right for more complex structures. Many a time have people decided to come up with a fancy mechanism for comparing values and made mistakes. The FAQ has a couple entries related to this. See http://blogs.msdn.com/oldnewthing/archive/2003/10/23/55408.aspx for a story about how this can go wrong by being overly clever. +A Compare function compares two values and returns true if the first is less than the second. This is easy to understand for integers and strings, but harder to get right for more complex structures. Many a time have people decided to come up with a fancy mechanism for comparing values and made mistakes. The FAQ has a couple entries related to this. See ["Writing a sort comparison function"](https://devblogs.microsoft.com/oldnewthing/20031023-00/?p=42063) for a story about how this can go wrong by being overly clever. All comparisons in std STL and eastl, including user defined comparisons, must have strict weak ordering. See the [Compare](https://en.cppreference.com/w/cpp/named_req/Compare) named requirement for more info. ### Comparisons involving floating point are dangerous. diff --git a/include/EASTL/algorithm.h b/include/EASTL/algorithm.h index 06f1e504..2101231b 100644 --- a/include/EASTL/algorithm.h +++ b/include/EASTL/algorithm.h @@ -250,7 +250,6 @@ #include #include #include -#include #include #include @@ -1282,11 +1281,12 @@ namespace eastl } - // We have a second layer of unwrap_iterator calls because the original iterator might be something like move_iterator > (i.e. doubly-wrapped). template - inline BidirectionalIterator2 move_and_copy_backward_unwrapper(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + EASTL_REMOVE_AT_2024_SEPT inline BidirectionalIterator2 move_and_copy_backward_unwrapper(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) { + EASTL_INTERNAL_DISABLE_DEPRECATED() // 'unwrap_iterator': was declared deprecated return BidirectionalIterator2(eastl::move_and_copy_backward_chooser(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), eastl::unwrap_iterator(resultEnd))); // Have to convert to BidirectionalIterator2 because result.base() could be a T* + EASTL_INTERNAL_RESTORE_DEPRECATED() } @@ -1314,7 +1314,7 @@ namespace eastl template inline BidirectionalIterator2 move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) { - return eastl::move_and_copy_backward_unwrapper(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), resultEnd); + return eastl::move_and_copy_backward_chooser(first, last, resultEnd); } @@ -1335,9 +1335,7 @@ namespace eastl template inline BidirectionalIterator2 copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) { - const bool isMove = eastl::is_move_iterator::value; EA_UNUSED(isMove); - - return eastl::move_and_copy_backward_unwrapper(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), resultEnd); + return eastl::move_and_copy_backward_chooser(first, last, resultEnd); } diff --git a/include/EASTL/any.h b/include/EASTL/any.h index c2ef6388..ca89e580 100644 --- a/include/EASTL/any.h +++ b/include/EASTL/any.h @@ -193,14 +193,19 @@ namespace eastl refAny.m_handler = nullptr; } + static void* get(const any* pThis) + { + EASTL_ASSERT(pThis); + return (void*)(&pThis->m_storage.internal_storage); + } + 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); + return get(pThis); } break; @@ -280,15 +285,20 @@ namespace eastl refAny.m_handler = nullptr; } + static void* get(const any* pThis) + { + EASTL_ASSERT(pThis); + EASTL_ASSERT(pThis->m_storage.external_storage); + return static_cast(pThis->m_storage.external_storage); + } + 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); + return get(pThis); } break; @@ -456,18 +466,19 @@ namespace eastl // 20.7.3.3, modifiers #if EASTL_VARIADIC_TEMPLATES_ENABLED template - void emplace(Args&&... args) + typename eastl::enable_if, Args...> && eastl::is_copy_constructible_v>, eastl::decay_t&>::type + 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; + return *static_cast*>(StorageHandlerT::get(this)); } template - typename eastl::enable_if&, Args...>::value, void>::type + typename eastl::enable_if, std::initializer_list&, Args...> && eastl::is_copy_constructible_v>, eastl::decay_t&>::type emplace(std::initializer_list il, Args&&... args) { typedef storage_handler> StorageHandlerT; @@ -475,6 +486,7 @@ namespace eastl reset(); StorageHandlerT::construct_inplace(m_storage, il, eastl::forward(args)...); m_handler = &StorageHandlerT::handler_func; + return *static_cast*>(StorageHandlerT::get(this)); } #endif diff --git a/include/EASTL/array.h b/include/EASTL/array.h index dce1ae82..64297fc0 100644 --- a/include/EASTL/array.h +++ b/include/EASTL/array.h @@ -29,6 +29,9 @@ EA_RESTORE_ALL_VC_WARNINGS() #endif +// 4512/4626 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. +EA_DISABLE_VC_WARNING(4512 4626); + #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. #endif @@ -152,11 +155,11 @@ namespace eastl // We intentionally provide no constructor, destructor, or assignment operator. - void fill(const value_type& value) {} + void fill(const value_type&) {} // Unlike the swap function for other containers, array::swap takes linear time, // may exit via an exception, and does not cause iterators to become associated with the other container. - void swap(this_type& x) EA_NOEXCEPT {} + void swap(this_type&) EA_NOEXCEPT {} EA_CPP14_CONSTEXPR iterator begin() EA_NOEXCEPT { return nullptr; } EA_CPP14_CONSTEXPR const_iterator begin() const EA_NOEXCEPT { return nullptr; } @@ -181,9 +184,11 @@ namespace eastl EA_CPP14_CONSTEXPR T* data() EA_NOEXCEPT { return nullptr; } EA_CPP14_CONSTEXPR const T* data() const EA_NOEXCEPT { return nullptr; } - EA_CPP14_CONSTEXPR reference operator[](size_type i) { return *data(); } - EA_CPP14_CONSTEXPR const_reference operator[](size_type i) const { return *data(); } - EA_CPP14_CONSTEXPR const_reference at(size_type i) const + EA_CPP14_CONSTEXPR reference operator[](size_type) { return *data(); } + EA_CPP14_CONSTEXPR const_reference operator[](size_type) const { return *data(); } + + EA_DISABLE_VC_WARNING(4702); // unreachable code + EA_CPP14_CONSTEXPR const_reference at(size_type) const { #if EASTL_EXCEPTIONS_ENABLED throw std::out_of_range("array::at -- out of range"); @@ -192,7 +197,10 @@ namespace eastl #endif return *data(); } - EA_CPP14_CONSTEXPR reference at(size_type i) + EA_RESTORE_VC_WARNING(); + + EA_DISABLE_VC_WARNING(4702); // unreachable code + EA_CPP14_CONSTEXPR reference at(size_type) { #if EASTL_EXCEPTIONS_ENABLED throw std::out_of_range("array::at -- out of range"); @@ -201,6 +209,7 @@ namespace eastl #endif return *data(); } + EA_RESTORE_VC_WARNING(); EA_CPP14_CONSTEXPR reference front() { return *data(); } EA_CPP14_CONSTEXPR const_reference front() const { return *data(); } @@ -209,7 +218,7 @@ namespace eastl EA_CPP14_CONSTEXPR const_reference back() const { return *data(); } bool validate() const { return true; } - int validate_iterator(const_iterator i) const { return isf_none; } + int validate_iterator(const_iterator) const { return isf_none; } }; // class array @@ -365,6 +374,15 @@ namespace eastl EA_CPP14_CONSTEXPR inline typename array::reference array::operator[](size_type i) { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(i >= N)) + EASTL_FAIL_MSG("array::operator[] -- out of range"); + #elif EASTL_ASSERT_ENABLED + // We allow taking a reference to arr[0] + if (EASTL_UNLIKELY((i != 0) && i >= N)) + EASTL_FAIL_MSG("array::operator[] -- out of range"); + #endif + return mValue[i]; } @@ -373,6 +391,15 @@ namespace eastl EA_CPP14_CONSTEXPR inline typename array::const_reference array::operator[](size_type i) const { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(i >= N)) + EASTL_FAIL_MSG("array::operator[] -- out of range"); + #elif EASTL_ASSERT_ENABLED + // We allow taking a reference to arr[0] + if (EASTL_UNLIKELY((i != 0) && i >= N)) + EASTL_FAIL_MSG("array::operator[] -- out of range"); + #endif + return mValue[i]; } @@ -649,6 +676,8 @@ struct tuple_element> : public eastl::tuple_element #include +#include #include // memcpy namespace eastl diff --git a/include/EASTL/bitset.h b/include/EASTL/bitset.h index c31831a9..13e12c84 100644 --- a/include/EASTL/bitset.h +++ b/include/EASTL/bitset.h @@ -4,7 +4,7 @@ /////////////////////////////////////////////////////////////////////////////// // This file implements a bitset much like the C++ std::bitset class. -// The primary distinctions between this list and std::bitset are: +// The primary distinctions between this bitset and std::bitset are: // - bitset is more efficient than some other std::bitset implementations, // notably the bitset that comes with Microsoft and other 1st party platforms. // - bitset is savvy to an environment that doesn't have exception handling, @@ -62,6 +62,7 @@ namespace eastl /// nBitCount refers to the number of bits in a bitset. /// WordType refers to the type of integer word which stores bitet data. By default it is BitsetWordType. /// + /// Note: for nBitCount == 0, returns 1! #if !defined(__GNUC__) || (__GNUC__ >= 3) // GCC 2.x can't handle the simpler declaration below. #define BITSET_WORD_COUNT(nBitCount, WordType) (nBitCount == 0 ? 1 : ((nBitCount - 1) / (8 * sizeof(WordType)) + 1)) #else @@ -80,7 +81,109 @@ namespace eastl #define EASTL_DISABLE_BITSET_ARRAYBOUNDS_WARNING 0 #endif + template + class bitset; + + namespace detail + { + template + struct is_word_type : std::bool_constant && !is_volatile_v && !is_same_v && is_integral_v && is_unsigned_v> {}; + + template + constexpr bool is_word_type_v = is_word_type::value; + + // slices the min(N, UInt) lowest significant bits from value. + template + eastl::enable_if_t> from_unsigned_integral(bitset& bs, UInt value) + { + constexpr size_t numWords = (N > 0) ? ((N - 1) / (CHAR_BIT * sizeof(WordType)) + 1) : 0; // BITSET_WORD_COUNT(N, WordType) but 0 for N == 0 + WordType* data = bs.data(); + + EA_CONSTEXPR_IF (numWords > 0) + { + // copy everything from value into our word array: + constexpr size_t bytes_to_copy = eastl::min_alt(numWords * sizeof(WordType), sizeof(UInt)); + memcpy(data, &value, bytes_to_copy); + + // zero any remaining elements in our array: + memset(reinterpret_cast(data) + bytes_to_copy, 0, numWords * sizeof(WordType) - bytes_to_copy); + + // we may have copied bits into the final element that are unusable (ie. bit positions > N). + // zero these bits out, as this is an invariant for our implementation. + EA_CONSTEXPR_IF (N % (CHAR_BIT * sizeof(WordType)) != 0) + { + constexpr WordType lastElemUsedBitsMask = (WordType(1) << (N % (CHAR_BIT * sizeof(WordType)))) - 1; + data[numWords - 1] &= lastElemUsedBitsMask; + } + } + else + { + data[0] = 0; // our bitset implementation has a single element even when N == 0. + } + } + + template + eastl::enable_if_t, UInt> to_unsigned_integral(const bitset& bs) + { + constexpr size_t numWords = (N > 0) ? ((N - 1) / (CHAR_BIT * sizeof(WordType)) + 1) : 0; // BITSET_WORD_COUNT(N, WordType) but 0 for N == 0 + + EA_CONSTEXPR_IF (numWords > 0) + { + const WordType* data = bs.data(); + + UInt result = 0; + + size_t numWordsCopied; + EA_CONSTEXPR_IF (sizeof(UInt) < sizeof(WordType)) + { + constexpr size_t bytes_to_copy = sizeof(UInt); + memcpy(&result, data, bytes_to_copy); + + // check remaining uncopied bits from the first word are zero: + constexpr WordType lastElemOverflowBitsMask = static_cast(~((WordType(1) << (CHAR_BIT * sizeof(UInt))) - 1)); + if ((data[0] & lastElemOverflowBitsMask) != 0) + { +#if EASTL_EXCEPTIONS_ENABLED + throw std::overflow_error("target type cannot represent the full bitset."); +#elif EASTL_ASSERT_ENABLED + EA_CONSTEXPR_IF(bAssertOnOverflow) + EASTL_FAIL_MSG("overflow_error"); +#endif + } + + numWordsCopied = 1; + } + else + { + constexpr size_t bytes_to_copy = eastl::min_alt(numWords * sizeof(WordType), sizeof(UInt)); + memcpy(&result, data, bytes_to_copy); + + numWordsCopied = bytes_to_copy / sizeof(WordType); + } + + // check any remaining uncopied words are zero (don't contain any useful information). + for (size_t wordIndex = numWordsCopied; wordIndex < numWords; ++wordIndex) + { + if (data[wordIndex] != 0) + { +#if EASTL_EXCEPTIONS_ENABLED + throw std::overflow_error("target type cannot represent the full bitset."); +#elif EASTL_ASSERT_ENABLED + EA_CONSTEXPR_IF (bAssertOnOverflow) + EASTL_FAIL_MSG("overflow_error"); +#endif + } + } + + return result; + } + else + { + return 0; + } + } + } // namespace detail /// BitsetBase /// @@ -104,13 +207,11 @@ namespace eastl }; public: + // invariant: we keep any high bits in the last word that are unneeded set to 0 + // so that our to_ulong() conversion can simply copy the words into the target type. word_type mWord[NW]; public: - BitsetBase(); - BitsetBase(uint32_t value); // This exists only for compatibility with std::bitset, which has a 'long' constructor. - //BitsetBase(uint64_t value); // Disabled because it causes conflicts with the 32 bit version with existing user code. Use from_uint64 to init from a uint64_t instead. - void operator&=(const this_type& x); void operator|=(const this_type& x); void operator^=(const this_type& x); @@ -128,13 +229,6 @@ namespace eastl bool any() const; size_type count() const; - void from_uint32(uint32_t value); - void from_uint64(uint64_t value); - - unsigned long to_ulong() const; - uint32_t to_uint32() const; - uint64_t to_uint64() const; - word_type& DoGetWord(size_type i); word_type DoGetWord(size_type i) const; @@ -173,10 +267,6 @@ namespace eastl word_type mWord[1]; // Defined as an array of 1 so that bitset can treat this BitsetBase like others. public: - BitsetBase(); - BitsetBase(uint32_t value); - //BitsetBase(uint64_t value); // Disabled because it causes conflicts with the 32 bit version with existing user code. Use from_uint64 instead. - void operator&=(const this_type& x); void operator|=(const this_type& x); void operator^=(const this_type& x); @@ -194,13 +284,6 @@ namespace eastl bool any() const; size_type count() const; - void from_uint32(uint32_t value); - void from_uint64(uint64_t value); - - unsigned long to_ulong() const; - uint32_t to_uint32() const; - uint64_t to_uint64() const; - word_type& DoGetWord(size_type); word_type DoGetWord(size_type) const; @@ -240,10 +323,6 @@ namespace eastl word_type mWord[2]; public: - BitsetBase(); - BitsetBase(uint32_t value); - //BitsetBase(uint64_t value); // Disabled because it causes conflicts with the 32 bit version with existing user code. Use from_uint64 instead. - void operator&=(const this_type& x); void operator|=(const this_type& x); void operator^=(const this_type& x); @@ -261,13 +340,6 @@ namespace eastl bool any() const; size_type count() const; - void from_uint32(uint32_t value); - void from_uint64(uint64_t value); - - unsigned long to_ulong() const; - uint32_t to_uint32() const; - uint64_t to_uint64() const; - word_type& DoGetWord(size_type); word_type DoGetWord(size_type) const; @@ -295,14 +367,16 @@ namespace eastl /// /// - N can be any unsigned (non-zero) value, though memory usage is /// linear with respect to N, so large values of N use large amounts of memory. - /// - WordType must be one of [uint16_t, uint32_t, uint64_t, uint128_t] - /// and the compiler must support the type. By default the WordType is - /// the largest native register type that the target platform supports. + /// - WordType must be a non-cv qualified unsigned integral other than bool. + /// By default the WordType is the largest native register type that the + /// target platform supports. /// - template + template class bitset : private BitsetBase { public: + static_assert(detail::is_word_type_v, "Word type must be a non-cv qualified, unsigned integral other than bool."); + typedef BitsetBase base_type; typedef bitset this_type; typedef WordType word_type; @@ -318,15 +392,14 @@ namespace eastl kWordCount = BITSET_WORD_COUNT(N, WordType) // The number of words the bitset uses to hold the bits. sizeof(bitset) == kWordSize * kWordCount. }; + // internal implementation details. do not use. using base_type::mWord; using base_type::DoGetWord; using base_type::DoFindFirst; using base_type::DoFindNext; using base_type::DoFindLast; using base_type::DoFindPrev; - using base_type::to_ulong; - using base_type::to_uint32; - using base_type::to_uint64; + using base_type::count; using base_type::any; @@ -365,8 +438,13 @@ namespace eastl friend class reference; bitset(); + +#if EA_IS_ENABLED(EASTL_DEPRECATIONS_FOR_2024_SEPT) + // note: this constructor will only copy the minimum of N or unsigned long long's size least significant bits. + bitset(unsigned long long value); +#else bitset(uint32_t value); - //bitset(uint64_t value); // Disabled because it causes conflicts with the 32 bit version with existing user code. Use from_uint64 instead. +#endif // We don't define copy constructor and operator= because // the compiler-generated versions will suffice. @@ -394,12 +472,62 @@ namespace eastl const word_type* data() const; word_type* data(); - void from_uint32(uint32_t value); - void from_uint64(uint64_t value); + // Deprecated: use the bitset(unsigned long long) constructor instead. + // this was a workaround for when our constructor was defined as bitset(uint32_t) and could cause a narrowing conversion. + EASTL_REMOVE_AT_2024_SEPT void from_uint32(uint32_t value); + EASTL_REMOVE_AT_2024_SEPT void from_uint64(uint64_t value); + + /// to_xxx() + /// + /// Not recommended: Use one of + /// as_xxx() which is a compile time error if the target type cannot represent the entire bitset, or + /// to_xxx_assert_convertible() which is the standard conformant version of this function, or + /// to_xxx_no_assert_convertible() which has the same behaviour, explicit naming + /// + /// Different from the standard: + /// Does *NOT* assert that the bitset can be represented as the target integer type (has bits set outside the target type). + /// However, if exceptions are enabled, it does throw an exception if the bitset cannot be represented as the target integer type. + unsigned long to_ulong() const; + uint32_t to_uint32() const; + uint64_t to_uint64() const; - //unsigned long to_ulong() const; // We inherit this from the base class. - //uint32_t to_uint32() const; - //uint64_t to_uint64() const; + /// to_xxx_assert_convertible() + /// + /// Equivalent to the standard library's to_ulong() / to_ullong(). + /// Asserts / throws an exception if the bitset cannot be represented as the target integer type. + uint32_t to_uint32_assert_convertible() const { return detail::to_unsigned_integral(*this); } + uint64_t to_uint64_assert_convertible() const { return detail::to_unsigned_integral(*this); } + unsigned long to_ulong_assert_convertible() const { return detail::to_unsigned_integral(*this); } + unsigned long long to_ullong_assert_convertible() const { return detail::to_unsigned_integral(*this); } + + /// to_xxx_no_assert_convertible() + /// + /// Prefer to_xxx_assert_convertible() instead of these functions. + /// + /// Different from the standard: + /// Does *NOT* assert that the bitset can be represented as the target integer type (has bits set outside the target type). + /// However, if exceptions are enabled, it does throw an exception if the bitset cannot be represented as the target integer type. + uint32_t to_uint32_no_assert_convertible() const { return detail::to_unsigned_integral(*this); } + uint64_t to_uint64_no_assert_convertible() const { return detail::to_unsigned_integral(*this); } + unsigned long to_ulong_no_assert_convertible() const { return detail::to_unsigned_integral(*this); } + unsigned long long to_ullong_no_assert_convertible() const { return detail::to_unsigned_integral(*this); } + + /// as_uint() / as_xxx() + /// + /// Extension to the standard: Cast to a unsigned integral that can represent the entire bitset. + /// If the target type cannot represent the entire bitset, then issue a compile error (overload does not exist). + /// Never throws / asserts. + template + eastl::enable_if_t && N <= (CHAR_BIT * sizeof(UInt)), UInt> as_uint() const noexcept { return detail::to_unsigned_integral(*this); } + + template + eastl::enable_if_t as_uint32() const noexcept { return to_uint32_assert_convertible(); } + template + eastl::enable_if_t as_uint64() const noexcept { return to_uint64_assert_convertible(); } + template + eastl::enable_if_t as_ulong() const noexcept { return to_ulong_assert_convertible(); } + template + eastl::enable_if_t as_ullong() const noexcept { return to_ullong_assert_convertible(); } //size_type count() const; // We inherit this from the base class. size_type size() const; @@ -441,7 +569,8 @@ namespace eastl /// /// This is a fast trick way to count bits without branches nor memory accesses. /// - inline uint32_t BitsetCountBits(uint64_t x) + template + eastl::enable_if_t && sizeof(UInt64) == 8, uint32_t> BitsetCountBits(UInt64 x) { // GCC 3.x's implementation of UINT64_C is broken and fails to deal with // the code below correctly. So we make a workaround for it. Earlier and @@ -460,7 +589,8 @@ namespace eastl #endif } - inline uint32_t BitsetCountBits(uint32_t x) + template + eastl::enable_if_t && sizeof(UInt32) == 4, uint32_t> BitsetCountBits(UInt32 x) { x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); @@ -468,12 +598,8 @@ namespace eastl return (uint32_t)((x * 0x01010101) >> 24); } - inline uint32_t BitsetCountBits(uint16_t x) - { - return BitsetCountBits((uint32_t)x); - } - - inline uint32_t BitsetCountBits(uint8_t x) + template + eastl::enable_if_t< detail::is_word_type_v && sizeof(SmallUInt) < 4, uint32_t> BitsetCountBits(SmallUInt x) { return BitsetCountBits((uint32_t)x); } @@ -483,7 +609,8 @@ namespace eastl #define EASTL_BITSET_COUNT_STRING "\0\1\1\2\1\2\2\3\1\2\2\3\2\3\3\4" - inline uint32_t GetFirstBit(uint8_t x) + template + eastl::enable_if_t && sizeof(UInt8) == 1, uint32_t> GetFirstBit(UInt8 x) { if(x) { @@ -498,7 +625,12 @@ namespace eastl return 8; } - inline uint32_t GetFirstBit(uint16_t x) // To do: Update this to use VC++ _BitScanForward, _BitScanForward64; GCC __builtin_ctz, __builtin_ctzl. VC++ __lzcnt16, __lzcnt, __lzcnt64 requires recent CPUs (2013+) and probably can't be used. http://en.wikipedia.org/wiki/Haswell_%28microarchitecture%29#New_features + // To do: Update this to use VC++ _BitScanForward, _BitScanForward64; + // GCC __builtin_ctz, __builtin_ctzl. + // VC++ __lzcnt16, __lzcnt, __lzcnt64 requires recent CPUs (2013+) and probably can't be used. + // http://en.wikipedia.org/wiki/Haswell_%28microarchitecture%29#New_features + template + eastl::enable_if_t && sizeof(UInt16) == 2, uint32_t> GetFirstBit(UInt16 x) { if(x) { @@ -514,8 +646,20 @@ namespace eastl return 16; } - inline uint32_t GetFirstBit(uint32_t x) + template + eastl::enable_if_t && sizeof(UInt32) == 4, uint32_t> GetFirstBit(UInt32 x) { +#if defined(EA_COMPILER_MSVC) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + // This has been benchmarked as significantly faster than the generic code below. + unsigned char isNonZero; + unsigned long index; + isNonZero = _BitScanForward(&index, x); + return isNonZero ? (int)index : 32; +#elif (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && !defined(EA_COMPILER_EDG) + if (x) + return __builtin_ctz(x); + return 32; +#else if(x) { uint32_t n = 1; @@ -529,10 +673,23 @@ namespace eastl } return 32; +#endif } - inline uint32_t GetFirstBit(uint64_t x) + template + eastl::enable_if_t && sizeof(UInt64) == 8, uint32_t> GetFirstBit(UInt64 x) { +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86_64) + // This has been benchmarked as significantly faster than the generic code below. + unsigned char isNonZero; + unsigned long index; + isNonZero = _BitScanForward64(&index, x); + return isNonZero ? (int)index : 64; +#elif (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && !defined(EA_COMPILER_EDG) + if (x) + return __builtin_ctzll(x); + return 64; +#else if(x) { uint32_t n = 1; @@ -547,6 +704,7 @@ namespace eastl } return 64; +#endif } @@ -571,7 +729,8 @@ namespace eastl } #endif - inline uint32_t GetLastBit(uint8_t x) + template + eastl::enable_if_t && sizeof(UInt8) == 1, uint32_t> GetLastBit(UInt8 x) { if(x) { @@ -587,7 +746,8 @@ namespace eastl return 8; } - inline uint32_t GetLastBit(uint16_t x) + template + eastl::enable_if_t && sizeof(UInt16) == 2, uint32_t> GetLastBit(UInt16 x) { if(x) { @@ -604,8 +764,20 @@ namespace eastl return 16; } - inline uint32_t GetLastBit(uint32_t x) + template + eastl::enable_if_t && sizeof(UInt32) == 4, uint32_t> GetLastBit(UInt32 x) { +#if defined(EA_COMPILER_MSVC) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + // This has been benchmarked as significantly faster than the generic code below. + unsigned char isNonZero; + unsigned long index; + isNonZero = _BitScanReverse(&index, x); + return isNonZero ? (int)index : 32; +#elif (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && !defined(EA_COMPILER_EDG) + if (x) + return 31 - __builtin_clz(x); + return 32; +#else if(x) { uint32_t n = 0; @@ -620,10 +792,23 @@ namespace eastl } return 32; +#endif } - inline uint32_t GetLastBit(uint64_t x) + template + eastl::enable_if_t && sizeof(UInt64) == 8, uint32_t> GetLastBit(UInt64 x) { +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86_64) + // This has been benchmarked as significantly faster than the generic code below. + unsigned char isNonZero; + unsigned long index; + isNonZero = _BitScanReverse64(&index, x); + return isNonZero ? (int)index : 64; +#elif (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && !defined(EA_COMPILER_EDG) + if (x) + return 63 - __builtin_clzll(x); + return 64; +#else if(x) { uint32_t n = 0; @@ -639,6 +824,7 @@ namespace eastl } return 64; +#endif } #if EASTL_INT128_SUPPORTED @@ -648,7 +834,7 @@ namespace eastl { uint32_t n = 0; - eastl_uint128_t mask(UINT64_C(0xFFFFFFFF00000000)); // There doesn't seem to exist compiler support for INT128_C() by any compiler. EAStdC's int128_t supports it though. + eastl_uint128_t mask(UINT64_C(0xFFFFFFFFFFFFFFFF)); // There doesn't seem to exist compiler support for INT128_C() by any compiler. EAStdC's int128_t supports it though. mask <<= 64; if(x & mask) { n += 64; x >>= 64; } @@ -682,42 +868,6 @@ namespace eastl // For our tests (~NW < 16), the latter (using []) access resulted in faster code. /////////////////////////////////////////////////////////////////////////// - template - inline BitsetBase::BitsetBase() - { - reset(); - } - - - template - inline BitsetBase::BitsetBase(uint32_t value) - { - // This implementation assumes that sizeof(value) <= sizeof(word_type). - //EASTL_CT_ASSERT(sizeof(value) <= sizeof(word_type)); Disabled because we now have support for uint8_t and uint16_t word types. It would be nice to have a runtime assert that tested this. - - reset(); - mWord[0] = static_cast(value); - } - - - /* - template - inline BitsetBase::BitsetBase(uint64_t value) - { - reset(); - - #if(EA_PLATFORM_WORD_SIZE == 4) - mWord[0] = static_cast(value); - - EASTL_CT_ASSERT(NW > 2); // We can assume this because we have specializations of BitsetBase for <1> and <2>. - //if(NW > 1) // NW is a template constant, but it would be a little messy to take advantage of it's const-ness. - mWord[1] = static_cast(value >> 32); - #else - mWord[0] = static_cast(value); - #endif - } - */ - template inline void BitsetBase::operator&=(const this_type& x) @@ -884,89 +1034,6 @@ namespace eastl } - template - inline void BitsetBase::from_uint32(uint32_t value) - { - reset(); - mWord[0] = static_cast(value); - } - - - template - inline void BitsetBase::from_uint64(uint64_t value) - { - reset(); - - #if(EA_PLATFORM_WORD_SIZE == 4) - mWord[0] = static_cast(value); - - EASTL_CT_ASSERT(NW > 2); // We can assume this because we have specializations of BitsetBase for <1> and <2>. - //if(NW > 1) // NW is a template constant, but it would be a little messy to take advantage of it's const-ness. - mWord[1] = static_cast(value >> 32); - #else - mWord[0] = static_cast(value); - #endif - } - - - template - inline unsigned long BitsetBase::to_ulong() const - { - #if EASTL_EXCEPTIONS_ENABLED - for(size_t i = 1; i < NW; ++i) - { - if(mWord[i]) - throw std::overflow_error("BitsetBase::to_ulong"); - } - #endif - return (unsigned long)mWord[0]; // Todo: We need to deal with the case whereby sizeof(word_type) < sizeof(unsigned long) - } - - - template - inline uint32_t BitsetBase::to_uint32() const - { - #if EASTL_EXCEPTIONS_ENABLED - // Verify that high words or bits are not set and thus that to_uint32 doesn't lose information. - for(size_t i = 1; i < NW; ++i) - { - if(mWord[i]) - throw std::overflow_error("BitsetBase::to_uint32"); - } - - #if(EA_PLATFORM_WORD_SIZE > 4) // if we have 64 bit words... - if(mWord[0] >> 32) - throw std::overflow_error("BitsetBase::to_uint32"); - #endif - #endif - - return (uint32_t)mWord[0]; - } - - - template - inline uint64_t BitsetBase::to_uint64() const - { - #if EASTL_EXCEPTIONS_ENABLED - // Verify that high words are not set and thus that to_uint64 doesn't lose information. - - EASTL_CT_ASSERT(NW > 2); // We can assume this because we have specializations of BitsetBase for <1> and <2>. - for(size_t i = 2; i < NW; ++i) - { - if(mWord[i]) - throw std::overflow_error("BitsetBase::to_uint64"); - } - #endif - - #if(EA_PLATFORM_WORD_SIZE == 4) - EASTL_CT_ASSERT(NW > 2); // We can assume this because we have specializations of BitsetBase for <1> and <2>. - return (mWord[1] << 32) | mWord[0]; - #else - return (uint64_t)mWord[0]; - #endif - } - - template inline typename BitsetBase::word_type& BitsetBase::DoGetWord(size_type i) @@ -1018,7 +1085,7 @@ EA_DISABLE_GCC_WARNING(-Warray-bounds) if(word_index < NW) { // Mask off previous bits of the word so our search becomes a "find first". - word_type this_word = mWord[word_index] & (~static_cast(0) << bit_index); + word_type this_word = mWord[word_index] & (static_cast(~0) << bit_index); for(;;) { @@ -1070,7 +1137,11 @@ EA_RESTORE_GCC_WARNING() size_type bit_index = static_cast(last_find & kBitsPerWordMask); // Mask off subsequent bits of the word so our search becomes a "find last". - word_type mask = (~static_cast(0) >> (kBitsPerWord - 1 - bit_index)) >> 1; // We do two shifts here because many CPUs ignore requests to shift 32 bit integers by 32 bits, which could be the case above. + // We do two shifts here because it's undefined behaviour to right shift greater than or equal to the number of bits in the integer. + // + // Note: operator~() is an arithmetic operator and performs integral promotions, ie. small integrals are promoted to an int. + // Because the promotion is before applying operator~() we need to cast back to our word type otherwise we end up with extraneous set bits. + word_type mask = (static_cast(~static_cast(0)) >> (kBitsPerWord - 1 - bit_index)) >> 1; word_type this_word = mWord[word_index] & mask; for(;;) @@ -1096,36 +1167,6 @@ EA_RESTORE_GCC_WARNING() // BitsetBase<1, WordType> /////////////////////////////////////////////////////////////////////////// - template - inline BitsetBase<1, WordType>::BitsetBase() - { - mWord[0] = 0; - } - - - template - inline BitsetBase<1, WordType>::BitsetBase(uint32_t value) - { - // This implementation assumes that sizeof(value) <= sizeof(word_type). - //EASTL_CT_ASSERT(sizeof(value) <= sizeof(word_type)); Disabled because we now have support for uint8_t and uint16_t word types. It would be nice to have a runtime assert that tested this. - - mWord[0] = static_cast(value); - } - - - /* - template - inline BitsetBase<1, WordType>::BitsetBase(uint64_t value) - { - #if(EA_PLATFORM_WORD_SIZE == 4) - EASTL_ASSERT(value <= 0xffffffff); - mWord[0] = static_cast(value); // This potentially loses data, but that's what the user is requesting. - #else - mWord[0] = static_cast(value); - #endif - } - */ - template inline void BitsetBase<1, WordType>::operator&=(const this_type& x) @@ -1231,63 +1272,6 @@ EA_RESTORE_GCC_WARNING() } - template - inline void BitsetBase<1, WordType>::from_uint32(uint32_t value) - { - mWord[0] = static_cast(value); - } - - - template - inline void BitsetBase<1, WordType>::from_uint64(uint64_t value) - { - #if(EA_PLATFORM_WORD_SIZE == 4) - EASTL_ASSERT(value <= 0xffffffff); - mWord[0] = static_cast(value); // This potentially loses data, but that's what the user is requesting. - #else - mWord[0] = static_cast(value); - #endif - } - - - template - inline unsigned long BitsetBase<1, WordType>::to_ulong() const - { - #if EASTL_EXCEPTIONS_ENABLED - #if((EA_PLATFORM_WORD_SIZE > 4) && defined(EA_PLATFORM_MICROSOFT)) // If we are using 64 bit words but ulong is less than 64 bits... Microsoft platforms alone use a 32 bit long under 64 bit platforms. - // Verify that high bits are not set and thus that to_ulong doesn't lose information. - if(mWord[0] >> 32) - throw std::overflow_error("BitsetBase::to_ulong"); - #endif - #endif - - return static_cast(mWord[0]); - } - - - template - inline uint32_t BitsetBase<1, WordType>::to_uint32() const - { - #if EASTL_EXCEPTIONS_ENABLED - #if(EA_PLATFORM_WORD_SIZE > 4) // If we are using 64 bit words... - // Verify that high bits are not set and thus that to_uint32 doesn't lose information. - if(mWord[0] >> 32) - throw std::overflow_error("BitsetBase::to_uint32"); - #endif - #endif - - return static_cast(mWord[0]); - } - - - template - inline uint64_t BitsetBase<1, WordType>::to_uint64() const - { - // This implementation is the same regardless of the word size, and there is no possibility of overflow_error. - return static_cast(mWord[0]); - } - - template inline typename BitsetBase<1, WordType>::word_type& BitsetBase<1, WordType>::DoGetWord(size_type) @@ -1319,7 +1303,7 @@ EA_RESTORE_GCC_WARNING() if(++last_find < kBitsPerWord) { // Mask off previous bits of word so our search becomes a "find first". - const word_type this_word = mWord[0] & ((~static_cast(0)) << last_find); + const word_type this_word = mWord[0] & (static_cast(~0) << last_find); return GetFirstBit(this_word); } @@ -1343,7 +1327,7 @@ EA_RESTORE_GCC_WARNING() if(last_find > 0) { // Mask off previous bits of word so our search becomes a "find first". - const word_type this_word = mWord[0] & ((~static_cast(0)) >> (kBitsPerWord - last_find)); + const word_type this_word = mWord[0] & (static_cast(~static_cast(0)) >> (kBitsPerWord - last_find)); return GetLastBit(this_word); } @@ -1358,39 +1342,6 @@ EA_RESTORE_GCC_WARNING() // BitsetBase<2, WordType> /////////////////////////////////////////////////////////////////////////// - template - inline BitsetBase<2, WordType>::BitsetBase() - { - mWord[0] = 0; - mWord[1] = 0; - } - - - template - inline BitsetBase<2, WordType>::BitsetBase(uint32_t value) - { - // This implementation assumes that sizeof(value) <= sizeof(word_type). - //EASTL_CT_ASSERT(sizeof(value) <= sizeof(word_type)); Disabled because we now have support for uint8_t and uint16_t word types. It would be nice to have a runtime assert that tested this. - - mWord[0] = static_cast(value); - mWord[1] = 0; - } - - - /* - template - inline BitsetBase<2, WordType>::BitsetBase(uint64_t value) - { - #if(EA_PLATFORM_WORD_SIZE == 4) - mWord[0] = static_cast(value); - mWord[1] = static_cast(value >> 32); - #else - mWord[0] = static_cast(value); - mWord[1] = 0; - #endif - } - */ - template inline void BitsetBase<2, WordType>::operator&=(const this_type& x) @@ -1465,8 +1416,12 @@ EA_RESTORE_GCC_WARNING() template inline void BitsetBase<2, WordType>::set() { + EA_DISABLE_VC_WARNING(4245); // '=': conversion from 'int' to 'unsigned short', signed/unsigned mismatch + // https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4245?view=msvc-170 + // MSVC incorrectly believes 0 is a negative value. mWord[0] = ~static_cast(0); mWord[1] = ~static_cast(0); + EA_RESTORE_VC_WARNING(); // We let the parent class turn off any upper bits. } @@ -1520,75 +1475,6 @@ EA_RESTORE_GCC_WARNING() } - template - inline void BitsetBase<2, WordType>::from_uint32(uint32_t value) - { - mWord[0] = static_cast(value); - mWord[1] = 0; - } - - - template - inline void BitsetBase<2, WordType>::from_uint64(uint64_t value) - { - #if(EA_PLATFORM_WORD_SIZE == 4) - mWord[0] = static_cast(value); - mWord[1] = static_cast(value >> 32); - #else - mWord[0] = static_cast(value); - mWord[1] = 0; - #endif - } - - - template - inline unsigned long BitsetBase<2, WordType>::to_ulong() const - { - #if EASTL_EXCEPTIONS_ENABLED - if(mWord[1]) - throw std::overflow_error("BitsetBase::to_ulong"); - #endif - return (unsigned long)mWord[0]; // Todo: We need to deal with the case whereby sizeof(word_type) < sizeof(unsigned long) - } - - - template - inline uint32_t BitsetBase<2, WordType>::to_uint32() const - { - #if EASTL_EXCEPTIONS_ENABLED - // Verify that high words or bits are not set and thus that to_uint32 doesn't lose information. - - #if(EA_PLATFORM_WORD_SIZE == 4) - if(mWord[1]) - throw std::overflow_error("BitsetBase::to_uint32"); - #else - if(mWord[1] || (mWord[0] >> 32)) - throw std::overflow_error("BitsetBase::to_uint32"); - #endif - #endif - - return (uint32_t)mWord[0]; - } - - - template - inline uint64_t BitsetBase<2, WordType>::to_uint64() const - { - #if(EA_PLATFORM_WORD_SIZE == 4) - // There can't possibly be an overflow_error here. - - return ((uint64_t)mWord[1] << 32) | mWord[0]; - #else - #if EASTL_EXCEPTIONS_ENABLED - if(mWord[1]) - throw std::overflow_error("BitsetBase::to_uint64"); - #endif - - return (uint64_t)mWord[0]; - #endif - } - - template inline typename BitsetBase<2, WordType>::word_type& BitsetBase<2, WordType>::DoGetWord(size_type i) @@ -1631,7 +1517,7 @@ EA_RESTORE_GCC_WARNING() if(++last_find < (size_type)kBitsPerWord) { // Mask off previous bits of word so our search becomes a "find first". - word_type this_word = mWord[0] & ((~static_cast(0)) << last_find); + word_type this_word = mWord[0] & (static_cast(~0) << last_find); // Step through words. size_type fbiw = GetFirstBit(this_word); @@ -1650,7 +1536,7 @@ EA_RESTORE_GCC_WARNING() last_find -= kBitsPerWord; // Mask off previous bits of word so our search becomes a "find first". - word_type this_word = mWord[1] & ((~static_cast(0)) << last_find); + word_type this_word = mWord[1] & (static_cast(~0) << last_find); const size_type fbiw = GetFirstBit(this_word); @@ -1691,7 +1577,7 @@ EA_RESTORE_GCC_WARNING() last_find -= kBitsPerWord; // Mask off previous bits of word so our search becomes a "find first". - word_type this_word = mWord[1] & ((~static_cast(0)) >> (kBitsPerWord - last_find)); + word_type this_word = mWord[1] & (static_cast(~static_cast(0)) >> (kBitsPerWord - last_find)); // Step through words. size_type lbiw = GetLastBit(this_word); @@ -1707,7 +1593,7 @@ EA_RESTORE_GCC_WARNING() else if(last_find != 0) { // Mask off previous bits of word so our search becomes a "find first". - word_type this_word = mWord[0] & ((~static_cast(0)) >> (kBitsPerWord - last_find)); + word_type this_word = mWord[0] & (static_cast(~static_cast(0)) >> (kBitsPerWord - last_find)); const size_type lbiw = GetLastBit(this_word); @@ -1789,30 +1675,25 @@ EA_RESTORE_GCC_WARNING() template inline bitset::bitset() - : base_type() { - // Empty. The base class will set all bits to zero. + reset(); } EA_DISABLE_VC_WARNING(6313) +#if EA_IS_ENABLED(EASTL_DEPRECATIONS_FOR_2024_SEPT) template - inline bitset::bitset(uint32_t value) - : base_type(value) + inline bitset::bitset(unsigned long long value) { - if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. - mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. + detail::from_unsigned_integral(*this, value); } - EA_RESTORE_VC_WARNING() - - /* +#else template - inline bitset::bitset(uint64_t value) - : base_type(value) + inline bitset::bitset(uint32_t value) { - if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... - mWord[kWordCount - 1] &= ~(~static_cast(0) << (N & kBitsPerWordMask)); // This clears any high unused bits. + detail::from_unsigned_integral(*this, value); } - */ +#endif + EA_RESTORE_VC_WARNING() template @@ -2020,42 +1901,36 @@ EA_RESTORE_GCC_WARNING() template inline void bitset::from_uint32(uint32_t value) { - base_type::from_uint32(value); - - if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. - mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. We need to do this so that shift operations proceed correctly. + detail::from_unsigned_integral(*this, value); } template inline void bitset::from_uint64(uint64_t value) { - base_type::from_uint64(value); - - if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. - mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. We need to do this so that shift operations proceed correctly. + detail::from_unsigned_integral(*this, value); } - // template - // inline unsigned long bitset::to_ulong() const - // { - // return base_type::to_ulong(); - // } + template + inline unsigned long bitset::to_ulong() const + { + return detail::to_unsigned_integral(*this); + } - // template - // inline uint32_t bitset::to_uint32() const - // { - // return base_type::to_uint32(); - // } + template + inline uint32_t bitset::to_uint32() const + { + return detail::to_unsigned_integral(*this); + } - // template - // inline uint64_t bitset::to_uint64() const - // { - // return base_type::to_uint64(); - // } + template + inline uint64_t bitset::to_uint64() const + { + return detail::to_unsigned_integral(*this); + } // template diff --git a/include/EASTL/bitvector.h b/include/EASTL/bitvector.h index 7dcb9381..b2f3ab5f 100644 --- a/include/EASTL/bitvector.h +++ b/include/EASTL/bitvector.h @@ -21,6 +21,9 @@ #include #include #include +#if EASTL_EXCEPTIONS_ENABLED +#include +#endif EA_DISABLE_VC_WARNING(4480); // nonstandard extension used: specifying underlying type for enum @@ -64,6 +67,7 @@ namespace eastl public: typedef eastl_size_t size_type; bitvector_reference(Element* ptr, eastl_size_t i); + bitvector_reference(const bitvector_reference& other); bitvector_reference& operator=(bool value); bitvector_reference& operator=(const bitvector_reference& rhs); @@ -112,6 +116,7 @@ namespace eastl bitvector_const_iterator(); bitvector_const_iterator(const element_type* p, eastl_size_t i); bitvector_const_iterator(const reference_type& referenceType); + bitvector_const_iterator(const bitvector_const_iterator& other); bitvector_const_iterator& operator++(); bitvector_const_iterator operator++(int); @@ -345,6 +350,14 @@ namespace eastl } + template + bitvector_reference::bitvector_reference(const bitvector_reference& other) + : mpBitWord(other.mpBitWord), + mnBitIndex(other.mnBitIndex) + { + } + + template bitvector_reference& bitvector_reference::operator=(bool value) @@ -403,6 +416,13 @@ namespace eastl } + template + bitvector_const_iterator::bitvector_const_iterator(const bitvector_const_iterator& other) + : mReference(other.mReference) + { + } + + template bitvector_const_iterator& bitvector_const_iterator::operator++() diff --git a/include/EASTL/bonus/intrusive_sdlist.h b/include/EASTL/bonus/intrusive_sdlist.h index 1b126d43..ab51f1b4 100644 --- a/include/EASTL/bonus/intrusive_sdlist.h +++ b/include/EASTL/bonus/intrusive_sdlist.h @@ -173,8 +173,8 @@ namespace eastl const_iterator end() const; ///< Returns a const_iterator pointing one-after the last element in the list. const_iterator cend() const; ///< Returns a const_iterator pointing one-after the last element in the list. - reference front(); ///< Returns a reference to the first element. The list must be empty. - const_reference front() const; ///< Returns a const reference to the first element. The list must be empty. + reference front(); ///< Returns a reference to the first element. The list must not be empty. + const_reference front() const; ///< Returns a const reference to the first element. The list must not be empty. void push_front(value_type& value); ///< Adds an element to the front of the list; O(1). The element is not copied. The element must not be in any other list. void push_back(value_type& value); ///< Adds an element to the back of the list; O(N). The element is not copied. The element must not be in any other list. diff --git a/include/EASTL/bonus/list_map.h b/include/EASTL/bonus/list_map.h index 3c3f74ca..21d69fd8 100644 --- a/include/EASTL/bonus/list_map.h +++ b/include/EASTL/bonus/list_map.h @@ -9,6 +9,8 @@ #include +// 4512/4626 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. +EA_DISABLE_VC_WARNING(4512 4626); namespace eastl { @@ -86,8 +88,15 @@ namespace eastl public: list_map_iterator(); list_map_iterator(const base_node_type* pNode); - // Note: this isn't always a copy constructor, iterator is not always equal to this_type - list_map_iterator(const iterator& x); + + // This is the converting constructor of a non-const iterator to a const iterator + // This is never a copy constructor (due to enable_if) + template , bool> = true> + inline list_map_iterator(const iterator& x) + : mpNode(x.mpNode) + { + // Empty + } reference operator*() const; pointer operator->() const; @@ -434,14 +443,6 @@ namespace eastl } - template - inline list_map_iterator::list_map_iterator(const iterator& x) - : mpNode(x.mpNode) - { - // Empty - } - - template inline typename list_map_iterator::reference list_map_iterator::operator*() const @@ -956,6 +957,7 @@ namespace eastl } // namespace eastl +EA_RESTORE_VC_WARNING(); #endif // Header include guard diff --git a/include/EASTL/bonus/lru_cache.h b/include/EASTL/bonus/lru_cache.h index a8d7c338..42719fd1 100644 --- a/include/EASTL/bonus/lru_cache.h +++ b/include/EASTL/bonus/lru_cache.h @@ -31,6 +31,8 @@ #include #include #include +#include // for pair +#include // for function, hash, equal_to namespace eastl { @@ -53,14 +55,14 @@ namespace eastl /// /// Implements a caching map based off of a key and data. /// LRUList parameter is any container that guarantees the validity of its iterator even after a modification (e.g. list) - /// LRUMap is any mapping container that can map a key to some data. By default, we use unordered_set, but it might be better + /// LRUMap is any associative container that can map a key to some data. By default, we use unordered_map, but it might be better /// to use hash_map or some other structure depending on your key/data combination. For example, you may want to swap the - /// map backing if using strings as keys or if the data objects are small. In any case, unordered_set is a good default and should + /// map backing if using strings as keys or if the data objects are small. In any case, unordered_map is a good default and should /// work well enough since the purpose of this class is to cache results of expensive, order of milliseconds, operations /// /// Algorithmic Performance (default data structures): /// touch() -> O(1) - /// insert() / update(), get() / operator[] -> equivalent to unordered_set (O(1) on average, O(n) worst) + /// insert() / update(), get() / operator[] -> equivalent to unordered_map (O(1) on average, O(n) worst) /// size() -> O(1) /// /// All accesses to a given key (insert, update, get) will push that key to most recently used. @@ -157,14 +159,23 @@ namespace eastl /// emplace /// /// Places a new object in place k created with args - /// If the key already exists, it is replaced. + /// If the key already exists, no change is made. + /// return value is a pair of the iterator to the emplaced or already-existing element and a bool denoting whether insertion took place. template - void emplace(const key_type& k, Args&&... args) + eastl::pair emplace(const key_type& k, Args&&... args) { - make_space(); + auto it = m_map.find(k); + if (it == m_map.end()) + { + make_space(); - m_list.push_front(k); - m_map.emplace(k, data_container_type(eastl::forward(args)..., m_list.begin())); + m_list.push_front(k); + return m_map.emplace(k, data_container_type(piecewise_construct, eastl::forward_as_tuple(eastl::forward(args)...), make_tuple(m_list.begin()))); + } + else + { + return make_pair(it, false); + } } /// insert_or_assign diff --git a/include/EASTL/bonus/overloaded.h b/include/EASTL/bonus/overloaded.h index 55ca1581..32feab47 100644 --- a/include/EASTL/bonus/overloaded.h +++ b/include/EASTL/bonus/overloaded.h @@ -14,6 +14,9 @@ // improvements in apps as a result. #endif +// 4512/4626 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. +EA_DISABLE_VC_WARNING(4512 4626); + namespace eastl { /////////////////////////////////////////////////////////////////////////// @@ -78,4 +81,6 @@ namespace eastl } // namespace eastl -#endif // EASTL_OVERLOADED_H \ No newline at end of file +EA_RESTORE_VC_WARNING(); + +#endif // EASTL_OVERLOADED_H diff --git a/include/EASTL/bonus/ring_buffer.h b/include/EASTL/bonus/ring_buffer.h index 99187610..99c23463 100644 --- a/include/EASTL/bonus/ring_buffer.h +++ b/include/EASTL/bonus/ring_buffer.h @@ -299,10 +299,10 @@ namespace eastl /* To do: template - void emplace_front(Args&&... args); + reference emplace_front(Args&&... args); template - void emplace_back(Args&&... args); + reference emplace_back(Args&&... args); template iterator emplace(const_iterator position, Args&&... args); diff --git a/include/EASTL/bonus/tuple_vector.h b/include/EASTL/bonus/tuple_vector.h index 9cbb14b4..6c238426 100644 --- a/include/EASTL/bonus/tuple_vector.h +++ b/include/EASTL/bonus/tuple_vector.h @@ -29,6 +29,9 @@ #include #include #include +#if EASTL_EXCEPTIONS_ENABLED +#include +#endif #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. @@ -244,7 +247,7 @@ struct TupleVecLeaf { T* pBegin = mpData + begin; T* pEnd = mpData + end; - eastl::uninitialized_move_ptr_if_noexcept(pBegin, pEnd, pDest); + eastl::uninitialized_move_if_noexcept(pBegin, pEnd, pDest); eastl::destruct(pBegin, pEnd); } @@ -256,14 +259,14 @@ struct TupleVecLeaf const size_type nExtra = (numElements - pos); if (n < nExtra) // If the inserted values are entirely within initialized memory (i.e. are before mpEnd)... { - eastl::uninitialized_move_ptr(pDataEnd - n, pDataEnd, pDataEnd); + eastl::uninitialized_move(pDataEnd - n, pDataEnd, pDataEnd); eastl::move_backward(pDest, pDataEnd - n, pDataEnd); // We need move_backward because of potential overlap issues. eastl::fill(pDest, pDest + n, temp); } else { - eastl::uninitialized_fill_n_ptr(pDataEnd, n - nExtra, temp); - eastl::uninitialized_move_ptr(pDest, pDataEnd, pDataEnd + n - nExtra); + eastl::uninitialized_fill_n(pDataEnd, n - nExtra, temp); + eastl::uninitialized_move(pDest, pDataEnd, pDataEnd + n - nExtra); eastl::fill(pDest, pDataEnd, temp); } } @@ -276,14 +279,14 @@ struct TupleVecLeaf const size_type nExtra = numDataElements - pos; if (n < nExtra) // If the inserted values are entirely within initialized memory (i.e. are before mpEnd)... { - eastl::uninitialized_move_ptr(pDataEnd - n, pDataEnd, pDataEnd); + eastl::uninitialized_move(pDataEnd - n, pDataEnd, pDataEnd); eastl::move_backward(pDestBegin, pDataEnd - n, pDataEnd); // We need move_backward because of potential overlap issues. eastl::copy(pSrcBegin, pSrcEnd, pDestBegin); } else { eastl::uninitialized_copy(pSrcEnd - (n - nExtra), pSrcEnd, pDataEnd); - eastl::uninitialized_move_ptr(pDestBegin, pDataEnd, pDataEnd + n - nExtra); + eastl::uninitialized_move(pDestBegin, pDataEnd, pDataEnd + n - nExtra); eastl::copy(pSrcBegin, pSrcEnd - (n - nExtra), pDestBegin); } } @@ -293,7 +296,7 @@ struct TupleVecLeaf T* pDest = mpData + pos; T* pDataEnd = mpData + numElements; - eastl::uninitialized_move_ptr(pDataEnd - 1, pDataEnd, pDataEnd); + eastl::uninitialized_move(pDataEnd - 1, pDataEnd, pDataEnd); eastl::move_backward(pDest, pDataEnd - 1, pDataEnd); // We need move_backward because of potential overlap issues. eastl::destruct(pDest); ::new (pDest) T(eastl::forward(arg)); @@ -591,7 +594,7 @@ class TupleVecImpl, Ts...> : public TupleV { size_type oldNumElements = mNumElements; swallow((eastl::fill(TupleVecLeaf::mpData, TupleVecLeaf::mpData + oldNumElements, args), 0)...); - swallow((eastl::uninitialized_fill_ptr(TupleVecLeaf::mpData + oldNumElements, + swallow((eastl::uninitialized_fill(TupleVecLeaf::mpData + oldNumElements, TupleVecLeaf::mpData + n, args), 0)...); mNumElements = n; } @@ -625,7 +628,7 @@ class TupleVecImpl, Ts...> : public TupleV swallow((eastl::copy((Ts*)(ppOtherData[Indices]) + firstIdx, (Ts*)(ppOtherData[Indices]) + firstIdx + oldNumElements, TupleVecLeaf::mpData), 0)...); - swallow((eastl::uninitialized_copy_ptr((Ts*)(ppOtherData[Indices]) + firstIdx + oldNumElements, + swallow((eastl::uninitialized_copy((Ts*)(ppOtherData[Indices]) + firstIdx + oldNumElements, (Ts*)(ppOtherData[Indices]) + lastIdx, TupleVecLeaf::mpData + oldNumElements), 0)...); mNumElements = newNumElements; @@ -781,7 +784,7 @@ class TupleVecImpl, Ts...> : public TupleV 0, firstIdx, (Ts*)ppNewLeaf[Indices]), 0)...); swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( firstIdx, oldNumElements, (Ts*)ppNewLeaf[Indices] + lastIdx), 0)...); - swallow((eastl::uninitialized_fill_ptr((Ts*)ppNewLeaf[Indices] + firstIdx, (Ts*)ppNewLeaf[Indices] + lastIdx, args), 0)...); + swallow((eastl::uninitialized_fill((Ts*)ppNewLeaf[Indices] + firstIdx, (Ts*)ppNewLeaf[Indices] + lastIdx, args), 0)...); swallow(TupleVecLeaf::mpData = (Ts*)ppNewLeaf[Indices]...); EASTLFree(get_allocator(), mpData, internalDataSize()); @@ -796,7 +799,7 @@ class TupleVecImpl, Ts...> : public TupleV } else { - swallow((eastl::uninitialized_fill_ptr(TupleVecLeaf::mpData + oldNumElements, + swallow((eastl::uninitialized_fill(TupleVecLeaf::mpData + oldNumElements, TupleVecLeaf::mpData + newNumElements, args), 0)...); } return begin() + firstIdx; @@ -833,7 +836,7 @@ class TupleVecImpl, Ts...> : public TupleV 0, posIdx, (Ts*)ppNewLeaf[Indices]), 0)...); swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( posIdx, oldNumElements, (Ts*)ppNewLeaf[Indices] + posIdx + numToInsert), 0)...); - swallow((eastl::uninitialized_copy_ptr((Ts*)(ppOtherData[Indices]) + firstIdx, + swallow((eastl::uninitialized_copy((Ts*)(ppOtherData[Indices]) + firstIdx, (Ts*)(ppOtherData[Indices]) + lastIdx, (Ts*)ppNewLeaf[Indices] + posIdx), 0)...); swallow(TupleVecLeaf::mpData = (Ts*)ppNewLeaf[Indices]...); @@ -852,7 +855,7 @@ class TupleVecImpl, Ts...> : public TupleV } else { - swallow((eastl::uninitialized_copy_ptr((Ts*)(ppOtherData[Indices]) + firstIdx, + swallow((eastl::uninitialized_copy((Ts*)(ppOtherData[Indices]) + firstIdx, (Ts*)(ppOtherData[Indices]) + lastIdx, TupleVecLeaf::mpData + posIdx), 0)...); } @@ -905,7 +908,7 @@ class TupleVecImpl, Ts...> : public TupleV void* ppDataBegin[sizeof...(Ts)] = { (void*)(TupleVecLeaf::mpData + posIdx)... }; if (numToInsert < nExtra) // If the inserted values are entirely within initialized memory (i.e. are before mpEnd)... { - swallow((eastl::uninitialized_move_ptr((Ts*)ppDataEnd[Indices] - numToInsert, + swallow((eastl::uninitialized_move((Ts*)ppDataEnd[Indices] - numToInsert, (Ts*)ppDataEnd[Indices], (Ts*)ppDataEnd[Indices]), 0)...); // We need move_backward because of potential overlap issues. swallow((eastl::move_backward((Ts*)ppDataBegin[Indices], @@ -916,7 +919,7 @@ class TupleVecImpl, Ts...> : public TupleV else { size_type numToInitialize = numToInsert - nExtra; - swallow((eastl::uninitialized_move_ptr((Ts*)ppDataBegin[Indices], + swallow((eastl::uninitialized_move((Ts*)ppDataBegin[Indices], (Ts*)ppDataEnd[Indices], (Ts*)ppDataEnd[Indices] + numToInitialize), 0)...); DoCopyFromTupleArray(pos, begin() + oldNumElements, first); @@ -1003,7 +1006,7 @@ class TupleVecImpl, Ts...> : public TupleV { DoReallocate(oldNumElements, eastl::max(GetNewCapacity(oldNumCapacity), n)); } - swallow((eastl::uninitialized_fill_ptr(TupleVecLeaf::mpData + oldNumElements, + swallow((eastl::uninitialized_fill(TupleVecLeaf::mpData + oldNumElements, TupleVecLeaf::mpData + n, args), 0)...); } else @@ -1152,11 +1155,9 @@ class TupleVecImpl, Ts...> : public TupleV reference_tuple back() { - #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + #if EASTL_ASSERT_ENABLED if (EASTL_UNLIKELY(mNumElements == 0)) // We don't allow the user to reference an empty container. EASTL_FAIL_MSG("tuple_vector::back -- empty vector"); - #else - // We allow the user to reference an empty container. #endif return at(size() - 1); @@ -1164,11 +1165,9 @@ class TupleVecImpl, Ts...> : public TupleV const_reference_tuple back() const { - #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + #if EASTL_ASSERT_ENABLED if (EASTL_UNLIKELY(mNumElements == 0)) // We don't allow the user to reference an empty container. EASTL_FAIL_MSG("tuple_vector::back -- empty vector"); - #else - // We allow the user to reference an empty container. #endif return at(size() - 1); @@ -1253,11 +1252,15 @@ class TupleVecImpl, Ts...> : public TupleV return (first.mIndex <= last.mIndex) && variadicAnd(first.mpData[Indices] == last.mpData[Indices]...); } + EASTL_INTERNAL_DISABLE_DEPRECATED() // 'unwrap_iterator': was declared deprecated + template ::value, bool>::type> - int validate_iterator(Iterator iter) const EA_NOEXCEPT { return validate_iterator(unwrap_iterator(iter)); } + EASTL_REMOVE_AT_2024_SEPT int validate_iterator(Iterator iter) const EA_NOEXCEPT { return validate_iterator(unwrap_iterator(iter)); } template ::value, bool>::type> - static bool validate_iterator_pair(Iterator first, Iterator last) EA_NOEXCEPT { return validate_iterator_pair(unwrap_iterator(first), unwrap_iterator(last)); } + EASTL_REMOVE_AT_2024_SEPT static bool validate_iterator_pair(Iterator first, Iterator last) EA_NOEXCEPT { return validate_iterator_pair(unwrap_iterator(first), unwrap_iterator(last)); } + + EASTL_INTERNAL_RESTORE_DEPRECATED() allocator_type& get_allocator() EA_NOEXCEPT { return mDataSizeAndAllocator.second(); } const allocator_type& get_allocator() const EA_NOEXCEPT { return mDataSizeAndAllocator.second(); } @@ -1283,7 +1286,7 @@ class TupleVecImpl, Ts...> : public TupleV void DoInitFromIterator(move_iterator begin, move_iterator end) { #if EASTL_ASSERT_ENABLED - if (EASTL_UNLIKELY(!validate_iterator_pair(begin, end))) + if (EASTL_UNLIKELY(!validate_iterator_pair(begin.base(), end.base()))) EASTL_FAIL_MSG("tuple_vector::erase -- invalid iterator pair"); #endif size_type newNumElements = (size_type)(end - begin); @@ -1292,7 +1295,7 @@ class TupleVecImpl, Ts...> : public TupleV size_type endIdx = end.base().mIndex; DoConditionalReallocate(0, mNumCapacity, newNumElements); mNumElements = newNumElements; - swallow((eastl::uninitialized_move_ptr(eastl::move_iterator((Ts*)(ppOtherData[Indices]) + beginIdx), + swallow((eastl::uninitialized_move(eastl::move_iterator((Ts*)(ppOtherData[Indices]) + beginIdx), eastl::move_iterator((Ts*)(ppOtherData[Indices]) + endIdx), TupleVecLeaf::mpData), 0)...); } @@ -1309,7 +1312,7 @@ class TupleVecImpl, Ts...> : public TupleV size_type endIdx = end.mIndex; DoConditionalReallocate(0, mNumCapacity, newNumElements); mNumElements = newNumElements; - swallow((eastl::uninitialized_copy_ptr((Ts*)(ppOtherData[Indices]) + beginIdx, + swallow((eastl::uninitialized_copy((Ts*)(ppOtherData[Indices]) + beginIdx, (Ts*)(ppOtherData[Indices]) + endIdx, TupleVecLeaf::mpData), 0)...); } @@ -1320,7 +1323,7 @@ class TupleVecImpl, Ts...> : public TupleV { DoConditionalReallocate(0, mNumCapacity, n); mNumElements = n; - swallow((eastl::uninitialized_fill_ptr(TupleVecLeaf::mpData, TupleVecLeaf::mpData + n, args), 0)...); + swallow((eastl::uninitialized_fill(TupleVecLeaf::mpData, TupleVecLeaf::mpData + n, args), 0)...); } void DoInitDefaultFill(size_type n) @@ -1408,7 +1411,6 @@ class move_iterator, T { public: typedef TupleVecInternal::TupleVecIter, Ts...> iterator_type; - // a wrapping iterator type. typedef iterator_traits traits_type; typedef typename traits_type::iterator_category iterator_category; typedef typename traits_type::value_type value_type; @@ -1474,12 +1476,14 @@ class move_iterator, T return reference(eastl::move(((Ts*)mIterator.mpData[Indices])[mIterator.mIndex])...); } + EASTL_INTERNAL_DISABLE_DEPRECATED() // 'is_iterator_wrapper': was declared deprecated // Unwrapping interface, not part of the public API. - iterator_type unwrap() const { return mIterator; } + EASTL_REMOVE_AT_2024_SEPT iterator_type unwrap() const { return mIterator; } // The unwrapper helpers need access to unwrap(). friend is_iterator_wrapper_helper; friend is_iterator_wrapper; + EASTL_INTERNAL_RESTORE_DEPRECATED() }; template diff --git a/include/EASTL/chrono.h b/include/EASTL/chrono.h index 4b94fe4a..024e8098 100644 --- a/include/EASTL/chrono.h +++ b/include/EASTL/chrono.h @@ -16,7 +16,7 @@ #define EASTL_CHRONO_H #if defined(EA_PRAGMA_ONCE_SUPPORTED) - #pragma once + #pragma once #endif #include @@ -50,9 +50,12 @@ #if defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_MINGW) // Nothing to do +#elif defined(EA_PLATFORM_SONY) + #include + #include #elif defined(EA_PLATFORM_APPLE) #include -#elif defined(EA_PLATFORM_POSIX) || defined(EA_PLATFORM_MINGW) || defined(EA_PLATFORM_ANDROID) +#elif defined(EA_PLATFORM_POSIX) || defined(EA_PLATFORM_MINGW) || defined(EA_PLATFORM_ANDROID) // Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms). #if defined(EA_PLATFORM_MINGW) #include @@ -101,7 +104,7 @@ namespace chrono namespace Internal { /////////////////////////////////////////////////////////////////////////////// - // IsRatio + // IsRatio /////////////////////////////////////////////////////////////////////////////// template struct IsRatio : eastl::false_type {}; template struct IsRatio> : eastl::true_type {}; @@ -111,7 +114,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // IsDuration + // IsDuration /////////////////////////////////////////////////////////////////////////////// template struct IsDuration : eastl::false_type{}; template struct IsDuration> : eastl::true_type{}; @@ -121,7 +124,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // RatioGCD + // RatioGCD /////////////////////////////////////////////////////////////////////////////// template struct RatioGCD @@ -194,10 +197,10 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // duration_cast + // duration_cast /////////////////////////////////////////////////////////////////////////////// template - inline typename eastl::enable_if::value, ToDuration>::type + inline typename eastl::enable_if::value, ToDuration>::type duration_cast(const duration& d) { typedef typename duration::this_type FromDuration; @@ -206,12 +209,12 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // duration + // duration /////////////////////////////////////////////////////////////////////////////// template class duration { - Rep mRep; + Rep mRep; public: typedef Rep rep; @@ -219,7 +222,7 @@ namespace chrono typedef duration this_type; #if defined(EA_COMPILER_NO_DEFAULTED_FUNCTIONS) - EA_CONSTEXPR duration() + EA_CONSTEXPR duration() : mRep() {} duration(const duration& other) @@ -235,7 +238,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // conversion constructors + // conversion constructors /////////////////////////////////////////////////////////////////////////////// template inline EA_CONSTEXPR explicit duration( @@ -255,12 +258,12 @@ namespace chrono : mRep(duration_cast(d2).count()) {} /////////////////////////////////////////////////////////////////////////////// - // returns the count of ticks + // returns the count of ticks /////////////////////////////////////////////////////////////////////////////// EA_CONSTEXPR Rep count() const { return mRep; } /////////////////////////////////////////////////////////////////////////////// - // static accessors of special duration values + // static accessors of special duration values /////////////////////////////////////////////////////////////////////////////// EA_CONSTEXPR inline static duration zero() { return duration(duration_values::zero()); } EA_CONSTEXPR inline static duration min() { return duration(duration_values::min()); } @@ -418,7 +421,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// // 20.12.6, time_point /////////////////////////////////////////////////////////////////////////////// - template + template class time_point { Duration mDuration; @@ -440,7 +443,7 @@ namespace chrono EA_CONSTEXPR Duration time_since_epoch() const { return mDuration; } - time_point& operator+=(const Duration& d) { mDuration += d; return *this; } + time_point& operator+=(const Duration& d) { mDuration += d; return *this; } time_point& operator-=(const Duration& d) { mDuration -= d; return *this; } static EA_CONSTEXPR time_point min() { return time_point(Duration::min()); } @@ -543,7 +546,7 @@ namespace chrono namespace Internal { #if defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_MINGW) - #define EASTL_NS_PER_TICK 1 + #define EASTL_NS_PER_TICK 1 #elif defined EA_PLATFORM_SONY #define EASTL_NS_PER_TICK 1 #elif defined EA_PLATFORM_POSIX @@ -552,17 +555,17 @@ namespace chrono #define EASTL_NS_PER_TICK 100 #endif - #if defined(EA_PLATFORM_POSIX) + #if defined(EA_PLATFORM_POSIX) typedef chrono::nanoseconds::period SystemClock_Period; typedef chrono::nanoseconds::period SteadyClock_Period; #else - typedef eastl::ratio_multiply, nano>::type SystemClock_Period; - typedef eastl::ratio_multiply, nano>::type SteadyClock_Period; + typedef eastl::ratio_multiply, nano>::type SystemClock_Period; + typedef eastl::ratio_multiply, nano>::type SteadyClock_Period; #endif /////////////////////////////////////////////////////////////////////////////// - // Internal::GetTicks + // Internal::GetTicks /////////////////////////////////////////////////////////////////////////////// inline uint64_t GetTicks() { @@ -586,8 +589,21 @@ namespace chrono EA_RESTORE_VC_WARNING() return uint64_t(frequency * (double)queryCounter()); #elif defined EA_PLATFORM_SONY - static_assert(false, "Implementing GetTicks() requires first party support"); - return 0; + auto queryFrequency = [] + { + // nanoseconds/seconds / ticks/seconds + return double(1000000000.0L / (long double)sceKernelGetProcessTimeCounterFrequency()); // nanoseconds per tick + }; + + auto queryCounter = [] + { + return sceKernelGetProcessTimeCounter(); + }; + + EA_DISABLE_VC_WARNING(4640) // warning C4640: construction of local static object is not thread-safe (VS2013) + static auto frequency = queryFrequency(); // cache cpu frequency on first call + EA_RESTORE_VC_WARNING() + return uint64_t(frequency * (double)queryCounter()); #elif defined(EA_PLATFORM_APPLE) auto queryTimeInfo = [] { @@ -595,7 +611,7 @@ namespace chrono mach_timebase_info(&info); return info; }; - + static auto timeInfo = queryTimeInfo(); uint64_t t = mach_absolute_time(); t *= timeInfo.numer; @@ -625,7 +641,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // system_clock + // system_clock /////////////////////////////////////////////////////////////////////////////// class system_clock { @@ -639,15 +655,15 @@ namespace chrono EA_CONSTEXPR_OR_CONST static bool is_steady = false; // returns a time point representing the current point in time. - static time_point now() EA_NOEXCEPT - { - return time_point(duration(Internal::GetTicks())); + static time_point now() EA_NOEXCEPT + { + return time_point(duration(Internal::GetTicks())); } }; /////////////////////////////////////////////////////////////////////////////// - // steady_clock + // steady_clock /////////////////////////////////////////////////////////////////////////////// class steady_clock { @@ -661,24 +677,24 @@ namespace chrono EA_CONSTEXPR_OR_CONST static bool is_steady = true; // returns a time point representing the current point in time. - static time_point now() EA_NOEXCEPT - { - return time_point(duration(Internal::GetTicks())); + static time_point now() EA_NOEXCEPT + { + return time_point(duration(Internal::GetTicks())); } }; /////////////////////////////////////////////////////////////////////////////// - // high_resolution_clock + // high_resolution_clock /////////////////////////////////////////////////////////////////////////////// typedef system_clock high_resolution_clock; -} // namespace chrono +} // namespace chrono /////////////////////////////////////////////////////////////////////////////// - // duration common_type specialization + // duration common_type specialization /////////////////////////////////////////////////////////////////////////////// template struct common_type, chrono::duration> @@ -689,7 +705,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // time_point common_type specialization + // time_point common_type specialization /////////////////////////////////////////////////////////////////////////////// template struct common_type, chrono::time_point> @@ -699,7 +715,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // chrono_literals + // chrono_literals /////////////////////////////////////////////////////////////////////////////// #if EASTL_USER_LITERALS_ENABLED && EASTL_INLINE_NAMESPACES_ENABLED // Disabling the Clang/GCC/MSVC warning about using user @@ -756,4 +772,4 @@ namespace chrono #endif -#endif +#endif diff --git a/include/EASTL/deque.h b/include/EASTL/deque.h index c8da31c0..fe361532 100644 --- a/include/EASTL/deque.h +++ b/include/EASTL/deque.h @@ -163,6 +163,7 @@ namespace eastl public: DequeIterator(); DequeIterator(const iterator& x); + DequeIterator& operator=(const iterator& x); pointer operator->() const; reference operator*() const; @@ -237,11 +238,11 @@ namespace eastl DequeIterator(const iterator& x, Increment); DequeIterator(const iterator& x, Decrement); - this_type copy(const iterator& first, const iterator& last, true_type); // true means that value_type has the type_trait is_trivially_copyable, - this_type copy(const iterator& first, const iterator& last, false_type); // false means it does not. + this_type move(const iterator& first, const iterator& last, true_type); // true means that value_type has the type_trait is_trivially_copyable, + this_type move(const iterator& first, const iterator& last, false_type); // false means it does not. - void copy_backward(const iterator& first, const iterator& last, true_type); // true means that value_type has the type_trait is_trivially_copyable, - void copy_backward(const iterator& first, const iterator& last, false_type); // false means it does not. + void move_backward(const iterator& first, const iterator& last, true_type); // true means that value_type has the type_trait is_trivially_copyable, + void move_backward(const iterator& first, const iterator& last, false_type); // false means it does not. void SetSubarray(T** pCurrentArrayPtr); }; @@ -457,10 +458,10 @@ namespace eastl iterator emplace(const_iterator position, Args&&... args); template - void emplace_front(Args&&... args); + reference emplace_front(Args&&... args); template - void emplace_back(Args&&... args); + reference emplace_back(Args&&... args); iterator insert(const_iterator position, const value_type& value); iterator insert(const_iterator position, value_type&& value); @@ -923,6 +924,17 @@ namespace eastl // Empty } + template + DequeIterator& DequeIterator::operator=(const iterator& x) + { + mpCurrent = x.mpCurrent; + mpBegin = x.mpBegin; + mpEnd = x.mpEnd; + mpCurrentArrayPtr = x.mpCurrentArrayPtr; + + return *this; + } + template DequeIterator::DequeIterator(const iterator& x, Increment) @@ -1060,7 +1072,7 @@ namespace eastl template typename DequeIterator::this_type - DequeIterator::copy(const iterator& first, const iterator& last, true_type) + DequeIterator::move(const iterator& first, const iterator& last, true_type) { // To do: Implement this as a loop which does memcpys between subarrays appropriately. // Currently we only do memcpy if the entire operation occurs within a single subarray. @@ -1069,34 +1081,34 @@ namespace eastl memmove(mpCurrent, first.mpCurrent, (size_t)((uintptr_t)last.mpCurrent - (uintptr_t)first.mpCurrent)); return *this + (last.mpCurrent - first.mpCurrent); } - return eastl::copy(eastl::make_move_iterator(first), eastl::make_move_iterator(last), eastl::make_move_iterator(*this)).base(); + return eastl::move(first, last, *this); } template typename DequeIterator::this_type - DequeIterator::copy(const iterator& first, const iterator& last, false_type) + DequeIterator::move(const iterator& first, const iterator& last, false_type) { - return eastl::copy(eastl::make_move_iterator(first), eastl::make_move_iterator(last), eastl::make_move_iterator(*this)).base(); + return eastl::move(first, last, *this); } template - void DequeIterator::copy_backward(const iterator& first, const iterator& last, true_type) + void DequeIterator::move_backward(const iterator& first, const iterator& last, true_type) { // To do: Implement this as a loop which does memmoves between subarrays appropriately. // Currently we only do memcpy if the entire operation occurs within a single subarray. if((first.mpBegin == last.mpBegin) && (first.mpBegin == mpBegin)) // If all operations are within the same subarray, implement the operation as a memcpy. memmove(mpCurrent - (last.mpCurrent - first.mpCurrent), first.mpCurrent, (size_t)((uintptr_t)last.mpCurrent - (uintptr_t)first.mpCurrent)); else - eastl::copy_backward(eastl::make_move_iterator(first), eastl::make_move_iterator(last), eastl::make_move_iterator(*this)); + eastl::move_backward(first, last, *this); } template - void DequeIterator::copy_backward(const iterator& first, const iterator& last, false_type) + void DequeIterator::move_backward(const iterator& first, const iterator& last, false_type) { - eastl::copy_backward(eastl::make_move_iterator(first), eastl::make_move_iterator(last), eastl::make_move_iterator(*this)).base(); + eastl::move_backward(first, last, *this); } @@ -1324,7 +1336,8 @@ namespace eastl { if(this != &x) { - set_capacity(0); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. + this_type temp(mAllocator); + swap(temp); swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. } return *this; @@ -1637,11 +1650,11 @@ namespace eastl typename deque::reference deque::back() { - #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + #if EASTL_ASSERT_ENABLED + // Decrementing an iterator with an empty container will result in undefined behaviour. + // specifically: the iterator decrement will apply pointer arithmetic to a nullptr (depending on the situation either mpCurrentArrayPtr or mpBegin). if (EASTL_UNLIKELY((size_type)(mItEnd == mItBegin))) EASTL_FAIL_MSG("deque::back -- empty deque"); - #else - // We allow the user to reference an empty container. #endif return *iterator(mItEnd, typename iterator::Decrement()); @@ -1652,11 +1665,11 @@ namespace eastl typename deque::const_reference deque::back() const { - #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + #if EASTL_ASSERT_ENABLED + // Decrementing an iterator with an empty container will result in undefined behaviour. + // specifically: the iterator decrement will apply pointer arithmetic to a nullptr (depending on the situation either mpCurrentArrayPtr or mpBegin). if (EASTL_UNLIKELY((size_type)(mItEnd == mItBegin))) EASTL_FAIL_MSG("deque::back -- empty deque"); - #else - // We allow the user to reference an empty container. #endif return *iterator(mItEnd, typename iterator::Decrement()); @@ -1806,7 +1819,7 @@ namespace eastl iterator oldBegin (mItBegin, typename iterator::Increment()); const iterator oldBeginPlus1(oldBegin, typename iterator::Increment()); - oldBegin.copy(oldBeginPlus1, newPosition, eastl::is_trivially_copyable()); + oldBegin.move(oldBeginPlus1, newPosition, eastl::is_trivially_copyable()); } else { @@ -1817,7 +1830,7 @@ namespace eastl iterator oldBack (mItEnd, typename iterator::Decrement()); const iterator oldBackMinus1(oldBack, typename iterator::Decrement()); - oldBack.copy_backward(itPosition, oldBackMinus1, eastl::is_trivially_copyable()); + oldBack.move_backward(itPosition, oldBackMinus1, eastl::is_trivially_copyable()); } *itPosition = eastl::move(valueSaved); @@ -1827,7 +1840,7 @@ namespace eastl template template - void deque::emplace_front(Args&&... args) + typename deque::reference deque::emplace_front(Args&&... args) { if(mItBegin.mpCurrent != mItBegin.mpBegin) // If we have room in the first subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. ::new((void*)--mItBegin.mpCurrent) value_type(eastl::forward(args)...); // Construct in place. If args is a single arg of type value_type&& then it this will be a move construction. @@ -1858,14 +1871,20 @@ namespace eastl } #endif } + + return *mItBegin; // Same as return front(); } template template - void deque::emplace_back(Args&&... args) + typename deque::reference deque::emplace_back(Args&&... args) { - if((mItEnd.mpCurrent + 1) != mItEnd.mpEnd) // If we have room in the last subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. + if ((mItEnd.mpCurrent + 1) != mItEnd.mpEnd) // If we have room in the last subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. + { + reference back = *mItEnd.mpCurrent; ::new((void*)mItEnd.mpCurrent++) value_type(eastl::forward(args)...); // Construct in place. If args is a single arg of type value_type&& then it this will be a move construction. + return back; + } else { // To consider: Detect if value isn't coming from within this container and handle that efficiently. @@ -1891,6 +1910,8 @@ namespace eastl throw; } #endif + + return *iterator(mItEnd, typename iterator::Decrement()); // Same as return back(); } } @@ -1956,12 +1977,12 @@ namespace eastl if(i < (difference_type)(size() / 2)) // Should we move the front entries forward or the back entries backward? We divide the range in half. { - itNext.copy_backward(mItBegin, itPosition, eastl::is_trivially_copyable()); + itNext.move_backward(mItBegin, itPosition, eastl::is_trivially_copyable()); pop_front(); } else { - itPosition.copy(itNext, mItEnd, eastl::is_trivially_copyable()); + itPosition.move(itNext, mItEnd, eastl::is_trivially_copyable()); pop_back(); } @@ -1993,7 +2014,7 @@ namespace eastl const iterator itNewBegin(mItBegin + n); value_type** const pPtrArrayBegin = mItBegin.mpCurrentArrayPtr; - itLast.copy_backward(mItBegin, itFirst, eastl::is_trivially_copyable()); + itLast.move_backward(mItBegin, itFirst, eastl::is_trivially_copyable()); for(; mItBegin != itNewBegin; ++mItBegin) // Question: If value_type is a POD type, will the compiler generate this loop at all? mItBegin.mpCurrent->~value_type(); // If so, then we need to make a specialization for destructing PODs. @@ -2007,7 +2028,7 @@ namespace eastl iterator itNewEnd(mItEnd - n); value_type** const pPtrArrayEnd = itNewEnd.mpCurrentArrayPtr + 1; - itFirst.copy(itLast, mItEnd, eastl::is_trivially_copyable()); + itFirst.move(itLast, mItEnd, eastl::is_trivially_copyable()); for(iterator itTemp(itNewEnd); itTemp != mItEnd; ++itTemp) itTemp.mpCurrent->~value_type(); @@ -2777,6 +2798,107 @@ namespace eastl } + /////////////////////////////////////////////////////////////////////// + // erase_unsorted + // + // This serves a similar purpose as erase above but with the difference + // that it doesn't preserve the relative order of what is left in the + // deque. + // + // Effects: Removes all elements equal to value from the deque while + // optimizing for speed with the potential reordering of elements as a + // side effect. + // + // Complexity: Linear + // + /////////////////////////////////////////////////////////////////////// + template + typename deque::size_type erase_unsorted(deque& c, const U& value) + { + auto itRemove = c.begin(); + auto ritMove = c.rbegin(); + + while(true) + { + itRemove = eastl::find(itRemove, ritMove.base(), value); + if (itRemove == ritMove.base()) // any elements to remove? + break; + + ritMove = eastl::find_if(ritMove, eastl::make_reverse_iterator(itRemove), [&value](const T& elem) { return elem != value; }); + if (itRemove == ritMove.base()) // any elements that can be moved into place? + break; + + *itRemove = eastl::move(*ritMove); + ++itRemove; + ++ritMove; + } + + // now all elements in the range [itRemove, c.end()) are either to be removed or have already been moved from. + + auto origEnd = end(c); + auto numRemoved = distance(itRemove, origEnd); + c.erase(itRemove, origEnd); + + // Note: This is technically a lossy conversion when size_type + // is 32bits and ptrdiff_t is 64bits (could happen on 64bit + // systems when EASTL_SIZE_T_32BIT is set). In practice this + // is fine because if EASTL_SIZE_T_32BIT is set then the deque + // should not have more elements than fit in a uint32_t and so + // the distance here should fit in a size_type. + return static_cast::size_type>(numRemoved); + } + + /////////////////////////////////////////////////////////////////////// + // erase_unsorted_if + // + // This serves a similar purpose as erase_if above but with the + // difference that it doesn't preserve the relative order of what is + // left in the deque. + // + // Effects: Removes all elements that return true for the predicate + // while optimizing for speed with the potential reordering of elements + // as a side effect. + // + // Complexity: Linear + // + /////////////////////////////////////////////////////////////////////// + template + typename deque::size_type erase_unsorted_if(deque& c, Predicate predicate) + { + // Erases all elements that satisfy predicate from the container. + auto itRemove = c.begin(); + auto ritMove = c.rbegin(); + + while(true) + { + itRemove = eastl::find_if(itRemove, ritMove.base(), predicate); + if (itRemove == ritMove.base()) // any elements to remove? + break; + + ritMove = eastl::find_if(ritMove, eastl::make_reverse_iterator(itRemove), not_fn(predicate)); + if (itRemove == ritMove.base()) // any elements that can be moved into place? + break; + + *itRemove = eastl::move(*ritMove); + ++itRemove; + ++ritMove; + } + + // now all elements in the range [itRemove, c.end()) are either to be removed or have already been moved from. + + auto origEnd = end(c); + auto numRemoved = distance(itRemove, origEnd); + c.erase(itRemove, origEnd); + + // Note: This is technically a lossy conversion when size_type + // is 32bits and ptrdiff_t is 64bits (could happen on 64bit + // systems when EASTL_SIZE_T_32BIT is set). In practice this + // is fine because if EASTL_SIZE_T_32BIT is set then the deque + // should not have more elements than fit in a uint32_t and so + // the distance here should fit in a size_type. + return static_cast::size_type>(numRemoved); + } + } // namespace eastl diff --git a/include/EASTL/fixed_vector.h b/include/EASTL/fixed_vector.h index 1fd2c61f..b588d9f1 100644 --- a/include/EASTL/fixed_vector.h +++ b/include/EASTL/fixed_vector.h @@ -238,21 +238,18 @@ namespace eastl inline fixed_vector::fixed_vector(this_type&& x) : base_type(fixed_allocator_type(mBuffer.buffer)) { - // Since we are a fixed_vector, we can't swap pointers. We can possibly so something like fixed_swap or + // Since we are a fixed_vector, we can't swap pointers. We can possibly do something like fixed_swap or // we can just do an assignment from x. If we want to do the former then we need to have some complicated // code to deal with overflow or no overflow, and whether the memory is in the fixed-size buffer or in // the overflow allocator. 90% of the time the memory should be in the fixed buffer, in which case // a simple assignment is no worse than the fancy pathway. - // Since we are a fixed_list, we can't normally swap pointers unless both this and + // Since we are a fixed_vector, we can't normally swap pointers unless both this and // x are using using overflow and the overflow allocators are equal. To do: //if(has_overflowed() && x.has_overflowed() && (get_overflow_allocator() == x.get_overflow_allocator())) //{ // We can swap contents and may need to swap the allocators as well. //} - - // The following is currently identical to the fixed_vector(const this_type& x) code above. If it stays that - // way then we may want to make a shared implementation. get_allocator().copy_overflow_allocator(x.get_allocator()); #if EASTL_NAME_ENABLED @@ -269,19 +266,10 @@ namespace eastl inline fixed_vector::fixed_vector(this_type&& x, const overflow_allocator_type& overflowAllocator) : base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator)) { - // See the discussion above. - - // The following is currently identical to the fixed_vector(const this_type& x) code above. If it stays that - // way then we may want to make a shared implementation. - get_allocator().copy_overflow_allocator(x.get_allocator()); - - #if EASTL_NAME_ENABLED - get_allocator().set_name(x.get_allocator().get_name()); - #endif - + // Since we are not swapping the allocated buffers but simply move the elements, we do not have to care about allocator compatibility. mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0]; internalCapacityPtr() = mpBegin + nodeCount; - base_type::template DoAssign(x.begin(), x.end(), false_type()); + base_type::template DoAssign, true>(eastl::make_move_iterator(x.begin()), eastl::make_move_iterator(x.end()), false_type()); } @@ -399,7 +387,7 @@ namespace eastl { T* const pNewData = (n <= kMaxSize) ? (T*)&mBuffer.buffer[0] : DoAllocate(n); T* const pCopyEnd = (n < nPrevSize) ? (mpBegin + n) : mpEnd; - eastl::uninitialized_move_ptr(mpBegin, pCopyEnd, pNewData); // Move [mpBegin, pCopyEnd) to p. + eastl::uninitialized_move(mpBegin, pCopyEnd, pNewData); // Move [mpBegin, pCopyEnd) to p. eastl::destruct(mpBegin, mpEnd); if((uintptr_t)mpBegin != (uintptr_t)mBuffer.buffer) DoFree(mpBegin, (size_type)(internalCapacityPtr() - mpBegin)); diff --git a/include/EASTL/functional.h b/include/EASTL/functional.h index a39778b7..89312db4 100644 --- a/include/EASTL/functional.h +++ b/include/EASTL/functional.h @@ -18,6 +18,8 @@ #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. #endif +// 4512/4626 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. +EA_DISABLE_VC_WARNING(4512 4626); namespace eastl @@ -332,6 +334,8 @@ namespace eastl return !compare(a, b) || !compare(b, a); // If (a <= b), then !(b <= a) } + // todo: when C++20 support added, add a compare_three_way function object. + template struct logical_and { @@ -383,6 +387,103 @@ namespace eastl { return !eastl::forward(t); } }; + // http://en.cppreference.com/w/cpp/utility/functional/bit_and + template + struct bit_and + { + EA_CPP14_CONSTEXPR T operator()(const T& a, const T& b) const + { + return a & b; + } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/bit_and_void + template <> + struct bit_and + { + typedef int is_transparent; + + template + EA_CPP14_CONSTEXPR auto operator()(T&& a, U&& b) const + -> decltype(eastl::forward(a) & eastl::forward(b)) + { + return eastl::forward(a) & eastl::forward(b); + } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/bit_or + template + struct bit_or + { + EA_CPP14_CONSTEXPR T operator()(const T& a, const T& b) const + { + return a | b; + } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/bit_or_void + template <> + struct bit_or + { + typedef int is_transparent; + + template + EA_CPP14_CONSTEXPR auto operator()(T&& a, U&& b) const + -> decltype(eastl::forward(a) | eastl::forward(b)) + { + return eastl::forward(a) | eastl::forward(b); + } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/bit_xor + template + struct bit_xor + { + EA_CPP14_CONSTEXPR T operator()(const T& a, const T& b) const + { + return a ^ b; + } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/bit_xor_void + template <> + struct bit_xor + { + typedef int is_transparent; + + template + EA_CPP14_CONSTEXPR auto operator()(T&& a, U&& b) const + -> decltype(eastl::forward(a) ^ eastl::forward(b)) + { + return eastl::forward(a) ^ eastl::forward(b); + } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/bit_not + template + struct bit_not + { + EA_CPP14_CONSTEXPR T operator()(const T& a) const + { + return ~a; + } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/bit_not_void + template <> + struct bit_not + { + typedef int is_transparent; + + template + EA_CPP14_CONSTEXPR auto operator()(T&& t) const + -> decltype(~eastl::forward(t)) + { + return ~eastl::forward(t); + } + }; + + // todo: add identity function object after removal of non-standard identity (renamed to type_identity). /////////////////////////////////////////////////////////////////////// @@ -1257,6 +1358,8 @@ namespace eastl #include +EA_RESTORE_VC_WARNING(); + #endif // Header include guard diff --git a/include/EASTL/hash_map.h b/include/EASTL/hash_map.h index 91e7dad0..a959e270 100644 --- a/include/EASTL/hash_map.h +++ b/include/EASTL/hash_map.h @@ -5,9 +5,9 @@ /////////////////////////////////////////////////////////////////////////////// // This file is based on the TR1 (technical report 1) reference implementation // of the unordered_set/unordered_map C++ classes as of about 4/2005. Most likely -// many or all C++ library vendors' implementations of this classes will be +// many or all C++ library vendors' implementations of this classes will be // based off of the reference version and so will look pretty similar to this -// file as well as other vendors' versions. +// file as well as other vendors' versions. /////////////////////////////////////////////////////////////////////////////// @@ -19,6 +19,9 @@ #include #include #include +#if EASTL_EXCEPTIONS_ENABLED +#include +#endif #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. @@ -64,7 +67,7 @@ namespace eastl /// hash_map /// /// Implements a hash_map, which is a hashed associative container. - /// Lookups are O(1) (that is, they are fast) but the container is + /// Lookups are O(1) (that is, they are fast) but the container is /// not sorted. Note that lookups are only O(1) if the hash table /// is well-distributed (non-colliding). The lookup approaches /// O(n) behavior as the table becomes increasingly poorly distributed. @@ -74,17 +77,17 @@ namespace eastl /// call set_max_load_factor with a very high value such as 100000.f. /// /// bCacheHashCode - /// We provide the boolean bCacheHashCode template parameter in order - /// to allow the storing of the hash code of the key within the map. - /// When this option is disabled, the rehashing of the table will - /// call the hash function on the key. Setting bCacheHashCode to true + /// We provide the boolean bCacheHashCode template parameter in order + /// to allow the storing of the hash code of the key within the map. + /// When this option is disabled, the rehashing of the table will + /// call the hash function on the key. Setting bCacheHashCode to true /// is useful for cases whereby the calculation of the hash value for /// a contained object is very expensive. /// /// find_as /// In order to support the ability to have a hashtable of strings but - /// be able to do efficiently lookups via char pointers (i.e. so they - /// aren't converted to string objects), we provide the find_as + /// be able to do efficiently lookups via char pointers (i.e. so they + /// aren't converted to string objects), we provide the find_as /// function. This function allows you to do a find with a key of a /// type other than the hashtable key type. /// @@ -96,16 +99,16 @@ namespace eastl /// hash_map hashMap; /// i = hashMap.find_as("hello", hash(), equal_to<>()); /// - template , typename Predicate = eastl::equal_to, + template , typename Predicate = eastl::equal_to, typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false> class hash_map : public hashtable, Allocator, eastl::use_first >, Predicate, Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, true> { public: - typedef hashtable, Allocator, - eastl::use_first >, - Predicate, Hash, mod_range_hashing, default_ranged_hash, + typedef hashtable, Allocator, + eastl::use_first >, + Predicate, Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, true> base_type; typedef hash_map this_type; typedef typename base_type::size_type size_type; @@ -147,23 +150,29 @@ namespace eastl /// hash_map /// /// Constructor which creates an empty container, but start with nBucketCount buckets. - /// We default to a small nBucketCount value, though the user really should manually + /// We default to a small nBucketCount value, though the user really should manually /// specify an appropriate value in order to prevent memory from being reallocated. /// - explicit hash_map(size_type nBucketCount, const Hash& hashFunction = Hash(), + /// note: difference in explicit keyword from the standard. + explicit hash_map(size_type nBucketCount, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MAP_DEFAULT_ALLOCATOR) - : base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), + : base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_first >(), allocator) { // Empty } + // hash_map(size_type nBucketCount, const allocator_type& allocator) + // hash_map(size_type nBucketCount, const Hash& hashFunction, const allocator_type& allocator) + hash_map(const this_type& x) : base_type(x) { } + // hash_map(const this_type& x, const allocator_type& allocator) + hash_map(this_type&& x) : base_type(eastl::move(x)) @@ -179,32 +188,47 @@ namespace eastl /// hash_map /// - /// initializer_list-based constructor. + /// initializer_list-based constructor. /// Allows for initializing with brace values (e.g. hash_map hm = { {3,"c"}, {4,"d"}, {5,"e"} }; ) - /// - hash_map(std::initializer_list ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), + /// + hash_map(std::initializer_list ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MAP_DEFAULT_ALLOCATOR) - : base_type(ilist.begin(), ilist.end(), nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), + : base_type(ilist.begin(), ilist.end(), nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_first >(), allocator) { // Empty } + hash_map(std::initializer_list ilist, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), 0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_first >(), allocator) + { + // Empty + } + + // hash_map(std::initializer_list ilist, size_type nBucketCount, const allocator_type& allocator) + + // hash_map(std::initializer_list ilist, size_type nBucketCount, const Hash& hashFunction, + // const allocator_type& allocator) /// hash_map /// - /// An input bucket count of <= 1 causes the bucket count to be equal to the number of + /// An input bucket count of <= 1 causes the bucket count to be equal to the number of /// elements in the input range. /// template - hash_map(ForwardIterator first, ForwardIterator last, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), + hash_map(ForwardIterator first, ForwardIterator last, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MAP_DEFAULT_ALLOCATOR) - : base_type(first, last, nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), + : base_type(first, last, nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_first >(), allocator) { // Empty } + // template + // hash_map(ForwardIterator first, ForwardIterator last, size_type nBucketCount, const allocator_type& allocator) + + // template + // hash_map(ForwardIterator first, ForwardIterator last, size_type nBucketCount, const Hash& hashFunction, const allocator_type& allocator) this_type& operator=(const this_type& x) { @@ -226,8 +250,8 @@ namespace eastl /// insert /// - /// This is an extension to the C++ standard. We insert a default-constructed - /// element with the given key. The reason for this is that we can avoid the + /// This is an extension to the C++ standard. We insert a default-constructed + /// element with the given key. The reason for this is that we can avoid the /// potentially expensive operation of creating and/or copying a mapped_type /// object on the stack. insert_return_type insert(const key_type& key) @@ -369,8 +393,8 @@ namespace eastl /// hash_multimap /// - /// Implements a hash_multimap, which is the same thing as a hash_map - /// except that contained elements need not be unique. See the + /// Implements a hash_multimap, which is the same thing as a hash_map + /// except that contained elements need not be unique. See the /// documentation for hash_set for details. /// template , typename Predicate = eastl::equal_to, @@ -380,9 +404,9 @@ namespace eastl Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, false> { public: - typedef hashtable, Allocator, - eastl::use_first >, - Predicate, Hash, mod_range_hashing, default_ranged_hash, + typedef hashtable, Allocator, + eastl::use_first >, + Predicate, Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, false> base_type; typedef hash_multimap this_type; typedef typename base_type::size_type size_type; @@ -405,7 +429,7 @@ namespace eastl /// Default constructor. /// explicit hash_multimap(const allocator_type& allocator = EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR) - : base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), + : base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_first >(), allocator) { // Empty @@ -415,23 +439,29 @@ namespace eastl /// hash_multimap /// /// Constructor which creates an empty container, but start with nBucketCount buckets. - /// We default to a small nBucketCount value, though the user really should manually + /// We default to a small nBucketCount value, though the user really should manually /// specify an appropriate value in order to prevent memory from being reallocated. /// - explicit hash_multimap(size_type nBucketCount, const Hash& hashFunction = Hash(), + /// note: difference in explicit keyword from the standard. + explicit hash_multimap(size_type nBucketCount, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR) - : base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), + : base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_first >(), allocator) { // Empty } + // hash_multimap(size_type nBucketCount, const allocator_type& allocator) + // hash_multimap(size_type nBucketCount, const Hash& hashFunction, const allocator_type& allocator) + hash_multimap(const this_type& x) : base_type(x) { } + // hash_multimap(const this_type& x, const allocator_type& allocator) + hash_multimap(this_type&& x) : base_type(eastl::move(x)) @@ -447,32 +477,48 @@ namespace eastl /// hash_multimap /// - /// initializer_list-based constructor. + /// initializer_list-based constructor. /// Allows for initializing with brace values (e.g. hash_multimap hm = { {3,"c"}, {3,"C"}, {4,"d"} }; ) - /// - hash_multimap(std::initializer_list ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), + /// + hash_multimap(std::initializer_list ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR) - : base_type(ilist.begin(), ilist.end(), nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), + : base_type(ilist.begin(), ilist.end(), nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_first >(), allocator) { // Empty } + hash_multimap(std::initializer_list ilist, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), 0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_first >(), allocator) + { + // Empty + } + + // hash_multimap(std::initializer_list ilist, size_type nBucketCount, const allocator_type& allocator) + + // hash_multimap(std::initializer_list ilist, size_type nBucketCount, const Hash& hashFunction, + // const allocator_type& allocator) + /// hash_multimap /// - /// An input bucket count of <= 1 causes the bucket count to be equal to the number of + /// An input bucket count of <= 1 causes the bucket count to be equal to the number of /// elements in the input range. /// template - hash_multimap(ForwardIterator first, ForwardIterator last, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), + hash_multimap(ForwardIterator first, ForwardIterator last, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR) - : base_type(first, last, nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), + : base_type(first, last, nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_first >(), allocator) { // Empty } + // template + // hash_multimap(ForwardIterator first, ForwardIterator last, size_type nBucketCount, const allocator_type& allocator) + + // template + // hash_multimap(ForwardIterator first, ForwardIterator last, size_type nBucketCount, const Hash& hashFunction, const allocator_type& allocator) this_type& operator=(const this_type& x) { @@ -494,8 +540,8 @@ namespace eastl /// insert /// - /// This is an extension to the C++ standard. We insert a default-constructed - /// element with the given key. The reason for this is that we can avoid the + /// This is an extension to the C++ standard. We insert a default-constructed + /// element with the given key. The reason for this is that we can avoid the /// potentially expensive operation of creating and/or copying a mapped_type /// object on the stack. insert_return_type insert(const key_type& key) @@ -540,7 +586,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////// template - inline bool operator==(const hash_map& a, + inline bool operator==(const hash_map& a, const hash_map& b) { typedef typename hash_map::const_iterator const_iterator; @@ -555,7 +601,7 @@ namespace eastl { const_iterator bi = b.find(ai->first); - if((bi == biEnd) || !(*ai == *bi)) // We have to compare the values, because lookups are done by keys alone but the full value_type of a map is a key/value pair. + if((bi == biEnd) || !(*ai == *bi)) // We have to compare the values, because lookups are done by keys alone but the full value_type of a map is a key/value pair. return false; // It's possible that two elements in the two containers have identical keys but different values. } @@ -564,7 +610,7 @@ namespace eastl #if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template - inline bool operator!=(const hash_map& a, + inline bool operator!=(const hash_map& a, const hash_map& b) { return !(a == b); @@ -572,7 +618,7 @@ namespace eastl #endif template - inline bool operator==(const hash_multimap& a, + inline bool operator==(const hash_multimap& a, const hash_multimap& b) { typedef typename hash_multimap::const_iterator const_iterator; @@ -582,9 +628,9 @@ namespace eastl if(a.size() != b.size()) return false; - // We can't simply search for each element of a in b, as it may be that the bucket for - // two elements in a has those same two elements in b but in different order (which should - // still result in equality). Also it's possible that one bucket in a has two elements which + // We can't simply search for each element of a in b, as it may be that the bucket for + // two elements in a has those same two elements in b but in different order (which should + // still result in equality). Also it's possible that one bucket in a has two elements which // both match a solitary element in the equivalent bucket in b (which shouldn't result in equality). eastl::pair aRange; eastl::pair bRange; @@ -605,12 +651,12 @@ namespace eastl // Implement a fast pathway for the case that there's just a single element. if(aDistance == 1) { - if(!(*aRange.first == *bRange.first)) // We have to compare the values, because lookups are done by keys alone but the full value_type of a map is a key/value pair. + if(!(*aRange.first == *bRange.first)) // We have to compare the values, because lookups are done by keys alone but the full value_type of a map is a key/value pair. return false; // It's possible that two elements in the two containers have identical keys but different values. Ditto for the permutation case below. } else { - // Check to see if these aRange and bRange are any permutation of each other. + // Check to see if these aRange and bRange are any permutation of each other. // This check gets slower as there are more elements in the range. if(!eastl::is_permutation(aRange.first, aRange.second, bRange.first)) return false; @@ -622,7 +668,7 @@ namespace eastl #if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template - inline bool operator!=(const hash_multimap& a, + inline bool operator!=(const hash_multimap& a, const hash_multimap& b) { return !(a == b); @@ -634,3 +680,9 @@ namespace eastl #endif // Header include guard + + + + + + diff --git a/include/EASTL/hash_set.h b/include/EASTL/hash_set.h index 0b470dd2..9b404306 100644 --- a/include/EASTL/hash_set.h +++ b/include/EASTL/hash_set.h @@ -142,6 +142,7 @@ namespace eastl /// We default to a small nBucketCount value, though the user really should manually /// specify an appropriate value in order to prevent memory from being reallocated. /// + /// note: difference in explicit keyword from the standard. explicit hash_set(size_type nBucketCount, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_SET_DEFAULT_ALLOCATOR) : base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_self(), allocator) @@ -149,12 +150,17 @@ namespace eastl // Empty } + // hash_set(size_type nBucketCount, const allocator_type& allocator) + // hash_set(size_type nBucketCount, const Hash& hashFunction, const allocator_type& allocator) + hash_set(const this_type& x) : base_type(x) { } + // hash_set(const this_type& x, const allocator_type& allocator) + hash_set(this_type&& x) : base_type(eastl::move(x)) @@ -180,6 +186,17 @@ namespace eastl // Empty } + hash_set(std::initializer_list ilist, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), 0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_self(), allocator) + { + // Empty + } + + // hash_set(std::initializer_list ilist, size_type nBucketCount, const allocator_type& allocator) + + // hash_set(std::initializer_list ilist, size_type nBucketCount, const Hash& hashFunction, + // const allocator_type& allocator) + /// hash_set /// @@ -194,6 +211,12 @@ namespace eastl // Empty } + // template + // hash_set(ForwardIterator first, ForwardIterator last, size_type nBucketCount, const allocator_type& allocator) + + // template + // hash_set(ForwardIterator first, ForwardIterator last, size_type nBucketCount, const Hash& hashFunction, const allocator_type& allocator) + this_type& operator=(const this_type& x) { @@ -278,6 +301,7 @@ namespace eastl /// We default to a small nBucketCount value, though the user really should manually /// specify an appropriate value in order to prevent memory from being reallocated. /// + /// note: difference in explicit keyword from the standard. explicit hash_multiset(size_type nBucketCount, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTISET_DEFAULT_ALLOCATOR) : base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_self(), allocator) @@ -285,12 +309,17 @@ namespace eastl // Empty } + // hash_multiset(size_type nBucketCount, const allocator_type& allocator) + // hash_multiset(size_type nBucketCount, const Hash& hashFunction, const allocator_type& allocator) + hash_multiset(const this_type& x) : base_type(x) { } + // hash_multiset(const this_type& x, const allocator_type& allocator) + hash_multiset(this_type&& x) : base_type(eastl::move(x)) @@ -316,6 +345,17 @@ namespace eastl // Empty } + hash_multiset(std::initializer_list ilist, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), 0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_self(), allocator) + { + // Empty + } + + // hash_multiset(std::initializer_list ilist, size_type nBucketCount, const allocator_type& allocator) + + // hash_multiset(std::initializer_list ilist, size_type nBucketCount, const Hash& hashFunction, + // const allocator_type& allocator) + /// hash_multiset /// @@ -330,6 +370,12 @@ namespace eastl // Empty } + // template + // hash_multiset(ForwardIterator first, ForwardIterator last, size_type nBucketCount, const allocator_type& allocator) + + // template + // hash_multiset(ForwardIterator first, ForwardIterator last, size_type nBucketCount, const Hash& hashFunction, const allocator_type& allocator) + this_type& operator=(const this_type& x) { diff --git a/include/EASTL/internal/char_traits.h b/include/EASTL/internal/char_traits.h index d6bb690d..2c7b81ef 100644 --- a/include/EASTL/internal/char_traits.h +++ b/include/EASTL/internal/char_traits.h @@ -20,6 +20,7 @@ #include #include +#include EA_DISABLE_ALL_VC_WARNINGS() #include // toupper, etc. @@ -28,6 +29,24 @@ EA_RESTORE_ALL_VC_WARNINGS() namespace eastl { + namespace details + { +#if defined(EA_COMPILER_CPP17_ENABLED) + // Helper to detect if wchar_t is the native type for the current platform or if -fshort-wchar was used. + // When that flag is used all string builtins and C Standard Library functions are not usable. + constexpr bool UseNativeWideChar() + { +#if defined(EA_COMPILER_MSVC) + return true; // Irrelevant flag for windows. +#elif defined(EA_PLATFORM_SONY) && defined(EA_PLATFORM_POSIX) && defined(EA_PLATFORM_CONSOLE) + return true; // Sony consoles use short wchar_t disregarding the flag. +#elif defined(EA_PLATFORM_POSIX) || defined(EA_PLATFORM_UNIX) + return sizeof(wchar_t) == 4; +#endif + } +#endif + } + /////////////////////////////////////////////////////////////////////////////// /// DecodePart /// @@ -228,6 +247,21 @@ namespace eastl return 0; } +#if defined(EA_COMPILER_CPP17_ENABLED) + // All main compilers offer a constexpr __builtin_memcmp as soon as C++17 was available. + constexpr int Compare(const char* p1, const char* p2, size_t n) { return __builtin_memcmp(p1, p2, n); } + +#if !defined(EA_COMPILER_GNUC) + // GCC doesn't offer __builtin_wmemcmp. + constexpr int Compare(const wchar_t* p1, const wchar_t* p2, size_t n) + { + if constexpr (details::UseNativeWideChar()) + return __builtin_wmemcmp(p1, p2, n); + else + return Compare(p1, p2, n); + } +#endif // !defined(EA_COMPILER_GNUC) +#else inline int Compare(const char* p1, const char* p2, size_t n) { if (n > 0) @@ -235,7 +269,7 @@ namespace eastl else return 0; } - +#endif template inline int CompareI(const T* p1, const T* p2, size_t n) @@ -254,7 +288,7 @@ namespace eastl template - inline const T* Find(const T* p, T c, size_t n) + inline EA_CPP14_CONSTEXPR const T* Find(const T* p, T c, size_t n) { for(; n > 0; --n, ++p) { @@ -262,24 +296,45 @@ namespace eastl return p; } - return NULL; + return nullptr; } +#if defined(EA_COMPILER_CPP17_ENABLED) && defined(EA_COMPILER_CLANG) + // Only clang have __builtin_char_memchr. + // __builtin_memchr doesn't work in a constexpr context since we need to cast the returned void* to a char*. + inline constexpr const char* Find(const char* p, char c, size_t n) + { + return __builtin_char_memchr(p, c, n); + } +#else inline const char* Find(const char* p, char c, size_t n) { return (const char*)memchr(p, c, n); } +#endif - - template + template inline EA_CPP14_CONSTEXPR size_t CharStrlen(const T* p) { const auto* pCurrent = p; - while(*pCurrent) + while (*pCurrent) ++pCurrent; return (size_t)(pCurrent - p); } +#if defined(EA_COMPILER_CPP17_ENABLED) && !defined(EA_COMPILER_GNUC) + // So far, GCC seems to struggle with builtin_strlen: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816 + // MSVC and Clang support both builtins as soon as C++17 was available. + constexpr size_t CharStrlen(const char* p) { return __builtin_strlen(p); } + + constexpr size_t CharStrlen(const wchar_t* p) + { + if constexpr (details::UseNativeWideChar()) + return __builtin_wcslen(p); + else + return CharStrlen(p); + } +#endif // If either pDestination or pSource is an invalid or null pointer, the behavior is undefined, even if (pSourceEnd - pSource) is zero. template @@ -290,6 +345,9 @@ namespace eastl } + // CharTypeStringFindEnd + // Specialized char version of STL find() from back function. + // Not the same as RFind because search range is specified as forward iterators. template const T* CharTypeStringFindEnd(const T* pBegin, const T* pEnd, T c) { @@ -303,7 +361,57 @@ namespace eastl return pEnd; } - + + // CharTypeStringSearch + // Specialized value_type version of STL search() function. + // Purpose: find p2 within p1. Return p1End if not found or if either string is zero length. + template + const T* CharTypeStringSearch(const T* p1Begin, const T* p1End, + const T* p2Begin, const T* p2End) + { + // Test for zero length strings, in which case we have a match or a failure, + // but the return value is the same either way. + if((p1Begin == p1End) || (p2Begin == p2End)) + return p1Begin; + + // Test for a pattern of length 1. + if((p2Begin + 1) == p2End) + return eastl::find(p1Begin, p1End, *p2Begin); + + // General case. + const T* pTemp; + const T* pTemp1 = (p2Begin + 1); + const T* pCurrent = p1Begin; + + while(p1Begin != p1End) + { + p1Begin = eastl::find(p1Begin, p1End, *p2Begin); + if(p1Begin == p1End) + return p1End; + + pTemp = pTemp1; + pCurrent = p1Begin; + if(++pCurrent == p1End) + return p1End; + + while(*pCurrent == *pTemp) + { + if(++pTemp == p2End) + return p1Begin; + if(++pCurrent == p1End) + return p1End; + } + + ++p1Begin; + } + + return p1Begin; + } + + + // CharTypeStringRSearch + // Specialized value_type version of STL find_end() function (which really is a reverse search function). + // Purpose: find last instance of p2 within p1. Return p1End if not found or if either string is zero length. template const T* CharTypeStringRSearch(const T* p1Begin, const T* p1End, const T* p2Begin, const T* p2End) @@ -323,33 +431,30 @@ namespace eastl // General case. const T* pSearchEnd = (p1End - (p2End - p2Begin) + 1); - const T* pCurrent1; - const T* pCurrent2; - while(pSearchEnd != p1Begin) + const T* pMatchCandidate; + while((pMatchCandidate = CharTypeStringFindEnd(p1Begin, pSearchEnd, *p2Begin)) != pSearchEnd) { - // Search for the last occurrence of *p2Begin. - pCurrent1 = CharTypeStringFindEnd(p1Begin, pSearchEnd, *p2Begin); - if(pCurrent1 == pSearchEnd) // If the first char of p2 wasn't found, - return p1End; // then we immediately have failure. - - // In this case, *pTemp == *p2Begin. So compare the rest. - pCurrent2 = p2Begin; + // In this case, *pMatchCandidate == *p2Begin. So compare the rest. + const T* pCurrent1 = pMatchCandidate; + const T* pCurrent2 = p2Begin; while(*pCurrent1++ == *pCurrent2++) { if(pCurrent2 == p2End) return (pCurrent1 - (p2End - p2Begin)); } - // A smarter algorithm might know to subtract more than just one, - // but in most cases it won't make much difference anyway. - --pSearchEnd; + // This match failed, search again with this new end. + pSearchEnd = pMatchCandidate; } return p1End; } + // CharTypeStringFindFirstOf + // Specialized value_type version of STL find_first_of() function. + // This function is much like the C runtime strtok function, except the strings aren't null-terminated. template inline const T* CharTypeStringFindFirstOf(const T* p1Begin, const T* p1End, const T* p2Begin, const T* p2End) { @@ -365,6 +470,8 @@ namespace eastl } + // CharTypeStringRFindFirstNotOf + // Specialized value_type version of STL find_first_not_of() function in reverse. template inline const T* CharTypeStringRFindFirstNotOf(const T* p1RBegin, const T* p1REnd, const T* p2Begin, const T* p2End) { @@ -383,6 +490,8 @@ namespace eastl } + // CharTypeStringFindFirstNotOf + // Specialized value_type version of STL find_first_not_of() function. template inline const T* CharTypeStringFindFirstNotOf(const T* p1Begin, const T* p1End, const T* p2Begin, const T* p2End) { @@ -401,6 +510,9 @@ namespace eastl } + // CharTypeStringRFindFirstOf + // Specialized value_type version of STL find_first_of() function in reverse. + // This function is much like the C runtime strtok function, except the strings aren't null-terminated. template inline const T* CharTypeStringRFindFirstOf(const T* p1RBegin, const T* p1REnd, const T* p2Begin, const T* p2End) { @@ -416,6 +528,8 @@ namespace eastl } + // CharTypeStringRFind + // Specialized value_type version of STL find() function in reverse. template inline const T* CharTypeStringRFind(const T* pRBegin, const T* pREnd, const T c) { diff --git a/include/EASTL/internal/concepts.h b/include/EASTL/internal/concepts.h new file mode 100644 index 00000000..eea184e1 --- /dev/null +++ b/include/EASTL/internal/concepts.h @@ -0,0 +1,48 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_CONCEPTS_H +#define EASTL_INTERNAL_CONCEPTS_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) +#pragma once +#endif + +#include + +namespace eastl +{ + namespace internal + { + template + using detect_explicitely_convertible = decltype(static_cast(declval())); + + namespace concepts + { + template + constexpr bool destructible = is_nothrow_destructible_v; + + template + constexpr bool constructible_from = destructible && is_constructible_v; + + template + constexpr bool convertible_to = + is_convertible_v && is_detected_v; + + template + constexpr bool move_constructible = constructible_from && convertible_to; + + template + constexpr bool copy_constructible = + move_constructible && constructible_from && convertible_to && + constructible_from && convertible_to && constructible_from && + convertible_to; + } // namespace concepts + } // namespace internal +} // namespace eastl + +#endif \ No newline at end of file diff --git a/include/EASTL/internal/config.h b/include/EASTL/internal/config.h index bf849dec..6c13117b 100644 --- a/include/EASTL/internal/config.h +++ b/include/EASTL/internal/config.h @@ -59,6 +59,7 @@ #ifndef EASTL_EABASE_DISABLED #include + #include #endif #include @@ -89,8 +90,8 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef EASTL_VERSION - #define EASTL_VERSION "3.21.12" - #define EASTL_VERSION_N 32112 + #define EASTL_VERSION "3.21.23" + #define EASTL_VERSION_N 32123 #endif @@ -436,20 +437,36 @@ namespace eastl // EASTL_EMPTY_REFERENCE_ASSERT_ENABLED // // Defined as 0 or non-zero. Default is same as EASTL_ASSERT_ENABLED. -// This is like EASTL_ASSERT_ENABLED, except it is for empty container -// references. Sometime people like to be able to take a reference to -// the front of the container, but not use it if the container is empty. -// In practice it's often easier and more efficient to do this than to write -// extra code to check if the container is empty. +// This is like EASTL_ASSERT_ENABLED, except it fires asserts specifically for +// a container operation that returns a reference while the container is empty. +// Sometimes people like to be able to take a reference to the front of the +// container, but won't use it if the container is empty. This may or may not +// be undefined behaviour depending on the container. +// +// In practice, for expressions such as &vector[0] this is not an issue - +// at least if the subscript operator is inlined because the expression will +// be equivalent to &*(nullptr) and optimized ti nullptr. MSVC, Clang and GCC +// all have this behaviour and UBSan & ASan report no issues with that code. +// +// Code that relies on this macro being disabled should instead use the +// container's data() member function. The range [data(), data() + size()) +// is always valid, even when the container is empty (in which case data() +// is not dereferencable). +// +// Enabling this macro adds asserts if the container is empty and the function +// invocation is well defined. If the implementation may invoke UB, or the +// container is non-empty, then the assert fires if EASTL_ASSERT_ENABLED is +// enabled, regardless of this macro. // -// NOTE: If this is enabled, EASTL_ASSERT_ENABLED must also be enabled +// NOTE: If this is enabled, EASTL_ASSERT_ENABLED must also be enabled to +// have any effect. // // Example usage: // template // inline typename vector::reference // vector::front() // { -// #if EASTL_ASSERT_ENABLED +// #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED // EASTL_ASSERT(mpEnd > mpBegin); // #endif // @@ -630,6 +647,8 @@ namespace eastl #ifndef EASTL_DEBUG_BREAK #if defined(_MSC_VER) && (_MSC_VER >= 1300) #define EASTL_DEBUG_BREAK() __debugbreak() // This is a compiler intrinsic which will map to appropriate inlined asm for the platform. + #elif defined(EA_PLATFORM_NINTENDO) + #define EASTL_DEBUG_BREAK() __builtin_debugtrap() // Consider using the CLANG define #elif (defined(EA_PROCESSOR_ARM) && !defined(EA_PROCESSOR_ARM64)) && defined(__APPLE__) #define EASTL_DEBUG_BREAK() asm("trap") #elif defined(EA_PROCESSOR_ARM64) && defined(__APPLE__) @@ -859,27 +878,6 @@ namespace eastl #endif - -/////////////////////////////////////////////////////////////////////////////// -// EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED -// -// Defined as 0 or 1. -// Tells if you can use the default EASTL allocator to do aligned allocations, -// which for most uses tells if you can store aligned objects in containers -// that use default allocators. It turns out that when built as a DLL for -// some platforms, EASTL doesn't have a way to do aligned allocations, as it -// doesn't have a heap that supports it. There is a way to work around this -// with dynamically defined allocators, but that's currently a to-do. -// -#ifndef EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED - #if EASTL_DLL - #define EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED 0 - #else - #define EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED 1 - #endif -#endif - - /////////////////////////////////////////////////////////////////////////////// // EASTL_INT128_DEFINED // @@ -1087,6 +1085,10 @@ namespace eastl // of the validity of containers and their iterators. Validation checking is // something that often involves significantly more than basic assertion // checking, and it may sometimes be desirable to disable it. +// +// Validation sub-features are supported and can be enabled / disabled +// individually. +// // This macro would generally be used internally by EASTL. // /////////////////////////////////////////////////////////////////////////////// @@ -1210,69 +1212,6 @@ namespace eastl #endif -/////////////////////////////////////////////////////////////////////////////// -// EASTL_STD_TYPE_TRAITS_AVAILABLE -// -// Defined as 0 or 1; default is based on auto-detection. -// Specifies whether Standard C++11 support exists. -// Sometimes the auto-detection below fails to work properly and the -// user needs to override it. Does not define whether the compiler provides -// built-in compiler type trait support (e.g. __is_abstract()), as some -// compilers will EASTL_STD_TYPE_TRAITS_AVAILABLE = 0, but have built -// in type trait support. -// -#ifndef EASTL_STD_TYPE_TRAITS_AVAILABLE - /* Disabled because we don't currently need it. - #if defined(_MSC_VER) && (_MSC_VER >= 1500) // VS2008 or later - #pragma warning(push, 0) - #include - #pragma warning(pop) - #if ((defined(_HAS_TR1) && _HAS_TR1) || _MSC_VER >= 1700) // VS2012 (1700) and later has built-in type traits support. - #define EASTL_STD_TYPE_TRAITS_AVAILABLE 1 - #include - #else - #define EASTL_STD_TYPE_TRAITS_AVAILABLE 0 - #endif - - #elif defined(EA_COMPILER_CLANG) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003) && !defined(__GCCXML__)) && !defined(EA_COMPILER_NO_STANDARD_CPP_LIBRARY) - #include // This will define __GLIBCXX__ if using GNU's libstdc++ and _LIBCPP_VERSION if using clang's libc++. - - #if defined(EA_COMPILER_CLANG) && !defined(EA_PLATFORM_APPLE) // As of v3.0.0, Apple's clang doesn't support type traits. - // http://clang.llvm.org/docs/LanguageExtensions.html#checking_type_traits - // Clang has some built-in compiler trait support. This support doesn't currently - // directly cover all our type_traits, though the C++ Standard Library that's used - // with clang could fill that in. - #define EASTL_STD_TYPE_TRAITS_AVAILABLE 1 - #endif - - #if !defined(EASTL_STD_TYPE_TRAITS_AVAILABLE) - #if defined(_LIBCPP_VERSION) // This is defined by clang's libc++. - #include - - #elif defined(__GLIBCXX__) && (__GLIBCXX__ >= 20090124) // It's not clear if this is the oldest version that has type traits; probably it isn't. - #define EASTL_STD_TYPE_TRAITS_AVAILABLE 1 - - #if defined(__GXX_EXPERIMENTAL_CXX0X__) // To do: Update this test to include conforming C++11 implementations. - #include - #else - #include - #endif - #else - #define EASTL_STD_TYPE_TRAITS_AVAILABLE 0 - #endif - #endif - - #elif defined(__MSL_CPP__) && (__MSL_CPP__ >= 0x8000) // CodeWarrior compiler. - #define EASTL_STD_TYPE_TRAITS_AVAILABLE 0 - // To do: Implement support for this (via modifying the EASTL type - // traits headers, as CodeWarrior provides this. - #else - #define EASTL_STD_TYPE_TRAITS_AVAILABLE 0 - #endif - */ -#endif - - /////////////////////////////////////////////////////////////////////////////// // EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE @@ -1308,28 +1247,6 @@ namespace eastl -/////////////////////////////////////////////////////////////////////////////// -// EASTL_RESET_ENABLED -// -// Defined as 0 or 1; default is 1 for the time being. -// The reset_lose_memory function works the same as reset, as described below. -// -// Specifies whether the container reset functionality is enabled. If enabled -// then ::reset forgets its memory, otherwise it acts as the clear -// function. The reset function is potentially dangerous, as it (by design) -// causes containers to not free their memory. -// This option has no applicability to the bitset::reset function, as bitset -// isn't really a container. Also it has no applicability to the smart pointer -// wrappers (e.g. intrusive_ptr). -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef EASTL_RESET_ENABLED - #define EASTL_RESET_ENABLED 0 -#endif - - - /////////////////////////////////////////////////////////////////////////////// // EASTL_MINMAX_ENABLED // @@ -1484,29 +1401,6 @@ namespace eastl #endif #endif -/////////////////////////////////////////////////////////////////////////////// -// EASTL_HAVE_CPP11_TYPE_TRAITS -// -// Defined as 0 or 1. -// This is the same as EABase EA_HAVE_CPP11_TYPE_TRAITS except that it -// follows the convention of being always defined, as 0 or 1. Note that this -// identifies if the Standard Library has C++11 type traits and not if EASTL -// has its equivalents to C++11 type traits. -/////////////////////////////////////////////////////////////////////////////// -#if !defined(EASTL_HAVE_CPP11_TYPE_TRAITS) - // To do: Change this to use the EABase implementation once we have a few months of testing - // of this and we are sure it works right. Do this at some point after ~January 2014. - #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ - #define EASTL_HAVE_CPP11_TYPE_TRAITS 1 - #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) // Prior versions of libstdc++ have incomplete support for C++11 type traits. - #define EASTL_HAVE_CPP11_TYPE_TRAITS 1 - #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) - #define EASTL_HAVE_CPP11_TYPE_TRAITS 1 - #else - #define EASTL_HAVE_CPP11_TYPE_TRAITS 0 - #endif -#endif - /////////////////////////////////////////////////////////////////////////////// @@ -1605,31 +1499,6 @@ typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept -/////////////////////////////////////////////////////////////////////////////// -// AddRef / Release -// -// AddRef and Release are used for "intrusive" reference counting. By the term -// "intrusive", we mean that the reference count is maintained by the object -// and not by the user of the object. Given that an object implements referencing -// counting, the user of the object needs to be able to increment and decrement -// that reference count. We do that via the venerable AddRef and Release functions -// which the object must supply. These defines here allow us to specify the name -// of the functions. They could just as well be defined to addref and delref or -// IncRef and DecRef. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef EASTLAddRef - #define EASTLAddRef AddRef -#endif - -#ifndef EASTLRelease - #define EASTLRelease Release -#endif - - - - /////////////////////////////////////////////////////////////////////////////// // EASTL_ALLOCATOR_EXPLICIT_ENABLED // @@ -1794,13 +1663,6 @@ typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept #endif -/// EASTL_FUNCTION_ENABLED -/// -#ifndef EASTL_FUNCTION_ENABLED - #define EASTL_FUNCTION_ENABLED 1 -#endif - - /// EASTL_USER_LITERALS_ENABLED #ifndef EASTL_USER_LITERALS_ENABLED #if defined(EA_COMPILER_CPP14_ENABLED) @@ -1930,7 +1792,11 @@ typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept // All deprecations raised by this macro (when it is EA_ENABLED) are scheduled for removal approximately April 2024. #ifndef EASTL_DEPRECATIONS_FOR_2024_APRIL - #define EASTL_DEPRECATIONS_FOR_2024_APRIL EA_ENABLED + #if defined(EA_DEPRECATIONS_FOR_2024_APRIL) + #define EASTL_DEPRECATIONS_FOR_2024_APRIL EA_DEPRECATIONS_FOR_2024_APRIL + #else + #define EASTL_DEPRECATIONS_FOR_2024_APRIL EA_ENABLED + #endif #endif #if EA_IS_ENABLED(EASTL_DEPRECATIONS_FOR_2024_APRIL) @@ -1939,6 +1805,25 @@ typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept #define EASTL_REMOVE_AT_2024_APRIL #endif +// EASTL_DEPRECATIONS_FOR_2024_SEPT +// This macro is provided as a means to disable warnings temporarily (in particular if a user is compiling with warnings +// as errors). All deprecations raised by this macro (when it is EA_ENABLED) are scheduled for removal approximately +// September 2024. + +#ifndef EASTL_DEPRECATIONS_FOR_2024_SEPT + #if defined(EA_DEPRECATIONS_FOR_2024_SEPT) + #define EASTL_DEPRECATIONS_FOR_2024_SEPT EA_DEPRECATIONS_FOR_2024_SEPT + #else + #define EASTL_DEPRECATIONS_FOR_2024_SEPT EA_ENABLED + #endif +#endif + +#if EA_IS_ENABLED(EASTL_DEPRECATIONS_FOR_2024_SEPT) + #define EASTL_REMOVE_AT_2024_SEPT EA_DEPRECATED +#else + #define EASTL_REMOVE_AT_2024_SEPT +#endif + // For internal (to EASTL) use only (ie. tests). #define EASTL_INTERNAL_DISABLE_DEPRECATED() \ EA_DISABLE_VC_WARNING(4996); \ diff --git a/include/EASTL/internal/copy_help.h b/include/EASTL/internal/copy_help.h index 0dab26bd..a7e5c0d1 100644 --- a/include/EASTL/internal/copy_help.h +++ b/include/EASTL/internal/copy_help.h @@ -43,10 +43,6 @@ namespace eastl /// copy normally differs from move, but there is a case where copy is the same as move: when copy is /// used with a move_iterator. We handle that case here by detecting that copy is being done with a /// move_iterator and redirect it to move (which can take advantage of memmove/memcpy). - /// - /// The generic_iterator class is typically used for wrapping raw memory pointers so they can act like - /// formal iterators. Since pointers provide an opportunity for memmove/memcpy operations, we can - /// detect a generic iterator and use it's wrapped type as a pointer if it happens to be one. // Implementation moving copying both trivial and non-trivial data via a lesser iterator than random-access. template @@ -159,15 +155,18 @@ namespace eastl const bool canBeMemmoved = internal::can_be_memmoved_helper::value; - return eastl::move_and_copy_helper::move_or_copy(first, last, result); // Need to chose based on the input iterator tag and not the output iterator tag, because containers accept input ranges of iterator types different than self. + // Need to choose based on the input iterator tag and not the output iterator tag, because containers accept input ranges of iterator types different than self. + return eastl::move_and_copy_helper::move_or_copy(first, last, result); } - // We have a second layer of unwrap_iterator calls because the original iterator might be something like move_iterator > (i.e. doubly-wrapped). + // We have a second layer of unwrap_iterator calls because the original iterator might be something like move_iterator > (i.e. doubly-wrapped). template - inline OutputIterator move_and_copy_unwrapper(InputIterator first, InputIterator last, OutputIterator result) + EASTL_REMOVE_AT_2024_SEPT inline OutputIterator move_and_copy_unwrapper(InputIterator first, InputIterator last, OutputIterator result) { + EASTL_INTERNAL_DISABLE_DEPRECATED() // 'unwrap_iterator': was declared deprecated return OutputIterator(eastl::move_and_copy_chooser(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), eastl::unwrap_iterator(result))); // Have to convert to OutputIterator because unwrap_iterator(result) could be a T* + EASTL_INTERNAL_RESTORE_DEPRECATED() } @@ -194,7 +193,7 @@ namespace eastl template inline OutputIterator move(InputIterator first, InputIterator last, OutputIterator result) { - return eastl::move_and_copy_unwrapper(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), result); + return eastl::move_and_copy_chooser(first, last, result); } @@ -215,9 +214,7 @@ namespace eastl template inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) { - const bool isMove = eastl::is_move_iterator::value; EA_UNUSED(isMove); - - return eastl::move_and_copy_unwrapper(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), result); + return eastl::move_and_copy_chooser(first, last, result); } } // namespace eastl diff --git a/include/EASTL/internal/function.h b/include/EASTL/internal/function.h index b7b6ab69..166f30d1 100644 --- a/include/EASTL/internal/function.h +++ b/include/EASTL/internal/function.h @@ -117,13 +117,13 @@ namespace eastl template Functor* target() EA_NOEXCEPT { - return Base::target(); + return Base::template target(); } template const Functor* target() const EA_NOEXCEPT { - return Base::target(); + return Base::template target(); } #endif // EASTL_RTTI_ENABLED }; diff --git a/include/EASTL/internal/function_detail.h b/include/EASTL/internal/function_detail.h index da3eb3eb..2b82c3e7 100644 --- a/include/EASTL/internal/function_detail.h +++ b/include/EASTL/internal/function_detail.h @@ -330,7 +330,7 @@ namespace eastl break; case MGROPS_GET_FUNC_PTR: { - return static_cast(Base::GetFunctorPtr(*static_cast(to))); + return static_cast(Base::GetFunctorPtr(*static_cast(to))); } break; default: @@ -600,7 +600,11 @@ namespace eastl { if (HaveManager() && target_type() == typeid(Functor)) { - void* ret = (*mMgrFuncPtr)(static_cast(&mStorage), nullptr, + // Note: the const_cast on &mStorage is "safe" here because we're doing a + // MGROPS_GET_FUNC_PTR operation. We can't change the entire signature + // of mMgrFuncPtr because we use it to modify the storage with other + // operations. + const void* ret = (*mMgrFuncPtr)(static_cast(const_cast(&mStorage)), nullptr, Base::ManagerOperations::MGROPS_GET_FUNC_PTR); return ret ? static_cast(ret) : nullptr; } diff --git a/include/EASTL/internal/functional_base.h b/include/EASTL/internal/functional_base.h index 366c1f75..4fe1c3c1 100644 --- a/include/EASTL/internal/functional_base.h +++ b/include/EASTL/internal/functional_base.h @@ -360,6 +360,7 @@ namespace eastl /// bind1st /// + EASTL_INTERNAL_DISABLE_DEPRECATED() // unariy_function is deprecated template class EASTL_REMOVE_AT_2024_APRIL binder1st : public unary_function { @@ -377,6 +378,7 @@ namespace eastl typename Operation::result_type operator()(typename Operation::second_argument_type& x) const { return op(value, x); } }; + EASTL_INTERNAL_RESTORE_DEPRECATED() EASTL_INTERNAL_DISABLE_DEPRECATED() // 'eastl::binder1st': was declared deprecated @@ -390,6 +392,7 @@ namespace eastl /// bind2nd /// + EASTL_INTERNAL_DISABLE_DEPRECATED() // unariy_function is deprecated template class EASTL_REMOVE_AT_2024_APRIL binder2nd : public unary_function { @@ -407,6 +410,7 @@ namespace eastl typename Operation::result_type operator()(typename Operation::first_argument_type& x) const { return op(x, value); } }; + EASTL_INTERNAL_RESTORE_DEPRECATED() EASTL_INTERNAL_DISABLE_DEPRECATED() // 'eastl::binder2nd': was declared deprecated diff --git a/include/EASTL/internal/generic_iterator.h b/include/EASTL/internal/generic_iterator.h index 0f1e28bd..5d894cbe 100644 --- a/include/EASTL/internal/generic_iterator.h +++ b/include/EASTL/internal/generic_iterator.h @@ -30,6 +30,7 @@ EA_DISABLE_VC_WARNING(4619 4217); namespace eastl { + EASTL_INTERNAL_DISABLE_DEPRECATED() // 'generic_iterator': was declared deprecated /// generic_iterator /// @@ -44,7 +45,7 @@ namespace eastl /// typedef generic_iterator IntArrayIteratorOther; /// template - class generic_iterator + class EASTL_REMOVE_AT_2024_SEPT generic_iterator { protected: Iterator mIterator; @@ -187,10 +188,10 @@ namespace eastl /// A primary reason to do so is that you can get at the pointer within the generic_iterator. /// template - struct is_generic_iterator : public false_type { }; + struct EASTL_REMOVE_AT_2024_SEPT is_generic_iterator : public false_type { }; template - struct is_generic_iterator > : public true_type { }; + struct EASTL_REMOVE_AT_2024_SEPT is_generic_iterator > : public true_type { }; /// unwrap_generic_iterator @@ -203,12 +204,13 @@ namespace eastl /// vector::iterator it = unwrap_generic_iterator(genericIterator); /// template - inline typename eastl::is_iterator_wrapper_helper::value>::iterator_type unwrap_generic_iterator(Iterator it) + EASTL_REMOVE_AT_2024_SEPT inline typename eastl::is_iterator_wrapper_helper::value>::iterator_type unwrap_generic_iterator(Iterator it) { // get_unwrapped(it) -> it.unwrap() which is equivalent to `it.base()` for generic_iterator and to `it` otherwise. return eastl::is_iterator_wrapper_helper::value>::get_unwrapped(it); } + EASTL_INTERNAL_RESTORE_DEPRECATED() } // namespace eastl diff --git a/include/EASTL/internal/hashtable.h b/include/EASTL/internal/hashtable.h index bc64bcf6..0e121462 100644 --- a/include/EASTL/internal/hashtable.h +++ b/include/EASTL/internal/hashtable.h @@ -48,10 +48,10 @@ EA_DISABLE_ALL_VC_WARNINGS() #include EA_RESTORE_ALL_VC_WARNINGS() -// 4512 - 'class' : assignment operator could not be generated. +// 4512/4626 - 'class' : assignment operator could not be generated. // 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc // 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. -EA_DISABLE_VC_WARNING(4512 4530 4571); +EA_DISABLE_VC_WARNING(4512 4626 4530 4571); namespace eastl @@ -327,9 +327,15 @@ namespace eastl hashtable_iterator(node_type** pBucket) : base_type(*pBucket, pBucket) { } + template ::type = 0> hashtable_iterator(const this_type_non_const& x) : base_type(x.mpNode, x.mpBucket) { } + hashtable_iterator(const hashtable_iterator&) = default; + hashtable_iterator(hashtable_iterator&&) = default; + hashtable_iterator& operator=(const hashtable_iterator&) = default; + hashtable_iterator& operator=(hashtable_iterator&&) = default; + reference operator*() const { return base_type::mpNode->mValue; } @@ -889,6 +895,7 @@ namespace eastl hashtable(size_type nBucketCount, const H1&, const H2&, const H&, const Equal&, const ExtractKey&, const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); + // note: standard only requires InputIterator. template hashtable(FowardIterator first, FowardIterator last, size_type nBucketCount, const H1&, const H2&, const H&, const Equal&, const ExtractKey&, @@ -968,9 +975,13 @@ namespace eastl size_type size() const EA_NOEXCEPT { return mnElementCount; } + // size_type max_size() const EA_NOEXCEPT; + size_type bucket_count() const EA_NOEXCEPT { return mnBucketCount; } + // size_type max_bucket_count() const; + size_type bucket_size(size_type n) const EA_NOEXCEPT { return (size_type)eastl::distance(begin(n), end(n)); } @@ -982,6 +993,9 @@ namespace eastl float load_factor() const EA_NOEXCEPT { return (float)mnElementCount / (float)mnBucketCount; } + // float max_load_factor() const; + // void max_load_factor( float ml ); + // Inherited from the base class. // Returns the max load factor, which is the load factor beyond // which we rebuild the container with a new bucket count. @@ -1011,8 +1025,12 @@ namespace eastl insert_return_type insert(const value_type& value); insert_return_type insert(value_type&& otherValue); + // template + // insert_return_type insert(P&& value); // sfinae: is_constructible::value iterator insert(const_iterator hint, const value_type& value); iterator insert(const_iterator hint, value_type&& value); + // template + // insert_return_type insert(const_iterator hint, P&& value); // sfinae: is_constructible::value void insert(std::initializer_list ilist); template void insert(InputIterator first, InputIterator last); //insert_return_type insert(node_type&& nh); @@ -1072,6 +1090,12 @@ namespace eastl iterator find(const key_type& key); const_iterator find(const key_type& key) const; + // missing transparent key support: + // template + // iterator find(const K& key); + // template + // const_iterator find(const K& key) const; + /// Implements a find whereby the user supplies a comparison of a different type /// than the hashtable value_type. A useful case of this is one whereby you have /// a container of string objects but want to do searches via passing in char pointers. @@ -1167,9 +1191,19 @@ namespace eastl size_type count(const key_type& k) const EA_NOEXCEPT; + // transparent key support: + // template + // size_type count(const K& k) const; + eastl::pair equal_range(const key_type& k); eastl::pair equal_range(const key_type& k) const; + // transparent key support: + // template + // eastl::pair equal_range(const K& k); + // template + // eastl::pair equal_range(const K& k) const; + bool validate() const; int validate_iterator(const_iterator i) const; diff --git a/include/EASTL/internal/intrusive_hashtable.h b/include/EASTL/internal/intrusive_hashtable.h index 291a527e..941f0774 100644 --- a/include/EASTL/internal/intrusive_hashtable.h +++ b/include/EASTL/internal/intrusive_hashtable.h @@ -211,6 +211,7 @@ namespace eastl explicit intrusive_hashtable_iterator(value_type** pBucket) : base_type(*pBucket, pBucket) { } + template ::type = false> intrusive_hashtable_iterator(const this_type_non_const& x) : base_type(x.mpNode, x.mpBucket) { } diff --git a/include/EASTL/internal/red_black_tree.h b/include/EASTL/internal/red_black_tree.h index 74962e86..21ea0a7b 100644 --- a/include/EASTL/internal/red_black_tree.h +++ b/include/EASTL/internal/red_black_tree.h @@ -27,10 +27,10 @@ EA_DISABLE_ALL_VC_WARNINGS() EA_RESTORE_ALL_VC_WARNINGS() -// 4512 - 'class' : assignment operator could not be generated +// 4512/4626 - 'class' : assignment operator could not be generated // 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc // 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. -EA_DISABLE_VC_WARNING(4512 4530 4571); +EA_DISABLE_VC_WARNING(4512 4626 4530 4571); namespace eastl @@ -572,6 +572,12 @@ namespace eastl iterator find(const key_type& key); const_iterator find(const key_type& key) const; + // missing transparent key support: + // template + // iterator find(const K& key); + // template + // const_iterator find(const K& key) const; + /// Implements a find whereby the user supplies a comparison of a different type /// than the tree's value_type. A useful case of this is one whereby you have /// a container of string objects but want to do searches via passing in char pointers. @@ -589,9 +595,21 @@ namespace eastl iterator lower_bound(const key_type& key); const_iterator lower_bound(const key_type& key) const; + // missing transparent key support: + // template + // iterator lower_bound(const K& key); + // template + // const_iterator lower_bound(const K& key) const; + iterator upper_bound(const key_type& key); const_iterator upper_bound(const key_type& key) const; + // missing transparent key support: + // template + // iterator upper_bound(const K& key); + // template + // const_iterator upper_bound(const K& key) const; + bool validate() const; int validate_iterator(const_iterator i) const; diff --git a/include/EASTL/internal/type_pod.h b/include/EASTL/internal/type_pod.h index 0cdf5d5f..994bd0a2 100644 --- a/include/EASTL/internal/type_pod.h +++ b/include/EASTL/internal/type_pod.h @@ -401,8 +401,14 @@ namespace eastl #define EASTL_DECLARE_TRIVIAL_DESTRUCTOR(T) namespace eastl{ template <> struct EASTL_REMOVE_AT_2024_APRIL has_trivial_destructor : public true_type{}; template <> struct EASTL_REMOVE_AT_2024_APRIL has_trivial_destructor : public true_type{}; } #if EASTL_VARIABLE_TEMPLATES_ENABLED + // Note: some compilers (notably GCC) trigger deprecation warnings in template variable + // declarations even if the variable is not insantiated here, so turn the warning off + // here. If this varialbe is used, the warning will still trigger in the user code, this + // just disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template EASTL_REMOVE_AT_2024_APRIL EA_CONSTEXPR bool has_trivial_destructor_v = has_trivial_destructor::value; +EASTL_INTERNAL_RESTORE_DEPRECATED() #endif @@ -993,7 +999,7 @@ namespace eastl #else // If the compiler has this trait built-in (which ideally all compilers would have since it's necessary for full conformance) use it. - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_trivially_constructible)) || defined(EA_COMPILER_MSVC)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((EASTL_HAS_INTRINSIC(is_trivially_constructible)) || defined(EA_COMPILER_MSVC)) #define EASTL_TYPE_TRAIT_is_trivially_constructible_CONFORMANCE 1 // We have a problem with clang here as of clang 3.4: __is_trivially_constructible(int[]) is false, yet I believe it should be true. diff --git a/include/EASTL/internal/type_properties.h b/include/EASTL/internal/type_properties.h index 0ea2fef5..b3e16822 100644 --- a/include/EASTL/internal/type_properties.h +++ b/include/EASTL/internal/type_properties.h @@ -376,9 +376,15 @@ namespace eastl template struct EASTL_REMOVE_AT_2024_APRIL result_of; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template struct EASTL_REMOVE_AT_2024_APRIL result_of { typedef decltype(eastl::declval()(eastl::declval()...)) type; }; +EASTL_INTERNAL_RESTORE_DEPRECATED() // result_of_t is the C++14 using typedef for typename result_of::type. @@ -386,9 +392,15 @@ namespace eastl #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) #define EASTL_RESULT_OF_T(T) typename result_of::type #else + // Note: some compilers (notably GCC) trigger deprecation warnings in template variable + // declarations even if the variable is not insantiated here, so turn the warning off + // here. If this varialbe is used, the warning will still trigger in the user code, this + // just disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template using result_of_t EASTL_REMOVE_AT_2024_APRIL = typename result_of::type; #define EASTL_RESULT_OF_T(T) result_of_t +EASTL_INTERNAL_RESTORE_DEPRECATED() #endif diff --git a/include/EASTL/iterator.h b/include/EASTL/iterator.h index dfd5a22c..719615fc 100644 --- a/include/EASTL/iterator.h +++ b/include/EASTL/iterator.h @@ -11,6 +11,7 @@ #include #include #include +#include #include EA_DISABLE_ALL_VC_WARNINGS(); @@ -176,7 +177,7 @@ namespace eastl /// is_iterator_wrapper(eastl::reverse_iterator>)::value => true /// template - class is_iterator_wrapper + class EASTL_REMOVE_AT_2024_SEPT is_iterator_wrapper { #if defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_CLANG_CL) // Using a default template type parameter trick here because @@ -213,7 +214,7 @@ namespace eastl /// MyVector::iterator it = unwrap_iterator(move_iterator(myVector.begin())); /// template - struct is_iterator_wrapper_helper + struct EASTL_REMOVE_AT_2024_SEPT is_iterator_wrapper_helper { using iterator_type = Iterator; @@ -221,8 +222,13 @@ namespace eastl }; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template - struct is_iterator_wrapper_helper + struct EASTL_REMOVE_AT_2024_SEPT is_iterator_wrapper_helper { // get_unwrapped must return by value since we're returning // it.unwrap(), and `it` will be out of scope as soon as @@ -235,8 +241,9 @@ namespace eastl template - inline typename is_iterator_wrapper_helper::value>::iterator_type unwrap_iterator(Iterator it) + EASTL_REMOVE_AT_2024_SEPT inline typename is_iterator_wrapper_helper::value>::iterator_type unwrap_iterator(Iterator it) { return eastl::is_iterator_wrapper_helper::value>::get_unwrapped(it); } +EASTL_INTERNAL_RESTORE_DEPRECATED() @@ -256,11 +263,6 @@ namespace eastl template class reverse_iterator { - private: - using base_wrapped_iterator_type = - typename eastl::is_iterator_wrapper_helper::value>::iterator_type; - public: typedef Iterator iterator_type; typedef typename eastl::iterator_traits::iterator_category iterator_category; @@ -274,21 +276,15 @@ namespace eastl public: EA_CPP14_CONSTEXPR reverse_iterator() // It's important that we construct mIterator, because if Iterator - : mIterator() { } // is a pointer, there's a difference between doing it and not. + : mIterator() { } // is a pointer, there's a difference between doing it and not. EA_CPP14_CONSTEXPR explicit reverse_iterator(iterator_type i) : mIterator(i) { } - EA_CPP14_CONSTEXPR reverse_iterator(const reverse_iterator& ri) - : mIterator(ri.mIterator) { } - template EA_CPP14_CONSTEXPR reverse_iterator(const reverse_iterator& ri) : mIterator(ri.base()) { } - // This operator= isn't in the standard, but the the C++ - // library working group has tentatively approved it, as it - // allows const and non-const reverse_iterators to interoperate. template EA_CPP14_CONSTEXPR reverse_iterator& operator=(const reverse_iterator& ri) { mIterator = ri.base(); return *this; } @@ -345,17 +341,23 @@ namespace eastl EA_CPP14_CONSTEXPR reference operator[](difference_type n) const { return mIterator[-n - 1]; } - + + EASTL_INTERNAL_DISABLE_DEPRECATED() // 'is_iterator_wrapper': was declared deprecated private: + using base_wrapped_iterator_type = + typename eastl::is_iterator_wrapper_helper::value>::iterator_type; + // Unwrapping interface, not part of the public API. template - EA_CPP14_CONSTEXPR typename eastl::enable_if::value, reverse_iterator>::type unwrap() const + EASTL_REMOVE_AT_2024_SEPT EA_CPP14_CONSTEXPR typename eastl::enable_if::value, reverse_iterator>::type unwrap() const { return reverse_iterator(unwrap_iterator(mIterator)); } // The unwrapper helpers need access to unwrap() (when it exists). using this_type = reverse_iterator; friend is_iterator_wrapper_helper::value>; friend is_iterator_wrapper; + EASTL_INTERNAL_RESTORE_DEPRECATED() }; @@ -425,12 +427,18 @@ namespace eastl /// move_iterator so we can detect that underneath it's reverse_iterator. /// template - struct is_reverse_iterator + struct EASTL_REMOVE_AT_2024_SEPT is_reverse_iterator : public eastl::false_type {}; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template - struct is_reverse_iterator< eastl::reverse_iterator > + struct EASTL_REMOVE_AT_2024_SEPT is_reverse_iterator> : public eastl::true_type {}; +EASTL_INTERNAL_RESTORE_DEPRECATED() /// unwrap_reverse_iterator is not implemented since there's no /// good use case and there's some abiguitiy. Note that @@ -461,7 +469,7 @@ namespace eastl public: typedef Iterator iterator_type; typedef iterator_traits traits_type; - typedef typename traits_type::iterator_category iterator_category; + typedef typename traits_type::iterator_category iterator_category; // todo: use is_contiguous_iterator to correctly identify pointers as contiguous? typedef typename traits_type::value_type value_type; typedef typename traits_type::difference_type difference_type; typedef Iterator pointer; @@ -487,6 +495,13 @@ namespace eastl { } + template + move_iterator& operator=(const move_iterator& mi) + { + mIterator = mi.mIterator; + return *this; + } + iterator_type base() const { return mIterator; } @@ -542,15 +557,17 @@ namespace eastl reference operator[](difference_type n) const { return eastl::move(mIterator[n]); } + EASTL_INTERNAL_DISABLE_DEPRECATED() // 'is_iterator_wrapper': was declared deprecated private: // Unwrapping interface, not part of the public API. - iterator_type unwrap() const + EASTL_REMOVE_AT_2024_SEPT iterator_type unwrap() const { return mIterator; } // The unwrapper helpers need access to unwrap(). using this_type = move_iterator; friend is_iterator_wrapper_helper; friend is_iterator_wrapper; + EASTL_INTERNAL_RESTORE_DEPRECATED() }; template @@ -634,12 +651,18 @@ namespace eastl /// bool IsMoveIterator() { return typename eastl::is_move_iterator::value; } /// template - struct is_move_iterator + struct EASTL_REMOVE_AT_2024_SEPT is_move_iterator : public eastl::false_type {}; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template - struct is_move_iterator< eastl::move_iterator > + struct EASTL_REMOVE_AT_2024_SEPT is_move_iterator> : public eastl::true_type {}; +EASTL_INTERNAL_RESTORE_DEPRECATED() /// unwrap_move_iterator @@ -651,12 +674,14 @@ namespace eastl /// eastl::move_iterator::iterator> moveIterator(intVector.begin()); /// vector::iterator it = unwrap_move_iterator(moveIterator); /// +EASTL_INTERNAL_DISABLE_DEPRECATED() // is_iterator_wrapper_helper is deprecated template - inline typename eastl::is_iterator_wrapper_helper::value>::iterator_type unwrap_move_iterator(Iterator it) + EASTL_REMOVE_AT_2024_SEPT inline typename eastl::is_iterator_wrapper_helper::value>::iterator_type unwrap_move_iterator(Iterator it) { // get_unwrapped(it) -> it.unwrap() which is equivalent to `it.base()` for move_iterator and to `it` otherwise. return eastl::is_iterator_wrapper_helper::value>::get_unwrapped(it); } +EASTL_INTERNAL_RESTORE_DEPRECATED() /// back_insert_iterator @@ -678,22 +703,20 @@ namespace eastl typedef void reference; protected: - Container& container; + Container* container; public: - //back_insert_iterator(); // Not valid. Must construct with a Container. - - //back_insert_iterator(const this_type& x) // Compiler-implemented - // : container(x.container) { } - explicit back_insert_iterator(Container& x) - : container(x) { } + : container(eastl::addressof(x)) { } + + back_insert_iterator(const back_insert_iterator&) = default; + back_insert_iterator& operator=(const back_insert_iterator&) = default; - back_insert_iterator& operator=(const_reference value) - { container.push_back(value); return *this; } + back_insert_iterator& operator=(const typename Container::value_type& value) + { container->push_back(value); return *this; } back_insert_iterator& operator=(typename Container::value_type&& value) - { container.push_back(eastl::move(value)); return *this; } + { container->push_back(eastl::move(value)); return *this; } back_insert_iterator& operator*() { return *this; } @@ -703,9 +726,6 @@ namespace eastl back_insert_iterator operator++(int) { return *this; } // This is by design. - - protected: - void operator=(const this_type&){} // Declared to avoid compiler warnings about inability to generate this function. }; @@ -740,19 +760,16 @@ namespace eastl typedef void reference; protected: - Container& container; + Container* container; public: - //front_insert_iterator(); // Not valid. Must construct with a Container. - - //front_insert_iterator(const this_type& x) // Compiler-implemented - // : container(x.container) { } + explicit front_insert_iterator(Container& x) : container(eastl::addressof(x)) {} - explicit front_insert_iterator(Container& x) - : container(x) { } + front_insert_iterator(const front_insert_iterator&) = default; + front_insert_iterator& operator=(const front_insert_iterator&) = default; - front_insert_iterator& operator=(const_reference value) - { container.push_front(value); return *this; } + front_insert_iterator& operator=(const typename Container::value_type& value) + { container->push_front(value); return *this; } front_insert_iterator& operator*() { return *this; } @@ -762,9 +779,6 @@ namespace eastl front_insert_iterator operator++(int) { return *this; } // This is by design. - - protected: - void operator=(const this_type&){} // Declared to avoid compiler warnings about inability to generate this function. }; @@ -810,26 +824,19 @@ namespace eastl typedef void reference; protected: - Container& container; + Container* container; iterator_type it; public: - // This assignment operator is defined more to stop compiler warnings (e.g. VC++ C4512) - // than to be useful. However, it does allow an insert_iterator to be assigned to another - // insert iterator provided that they point to the same container. - insert_iterator& operator=(const insert_iterator& x) - { - EASTL_ASSERT(&x.container == &container); - it = x.it; - return *this; - } - insert_iterator(Container& x, iterator_type itNew) - : container(x), it(itNew) {} + : container(eastl::addressof(x)), it(itNew) {} + + insert_iterator(const insert_iterator&) = default; + insert_iterator& operator=(const insert_iterator&) = default; - insert_iterator& operator=(const_reference value) + insert_iterator& operator=(const typename Container::value_type& value) { - it = container.insert(it, value); + it = container->insert(it, value); ++it; return *this; } @@ -866,12 +873,18 @@ namespace eastl /// If it's a insert_iterator wrapped by another iterator then value is false. /// template - struct is_insert_iterator + struct EASTL_REMOVE_AT_2024_SEPT is_insert_iterator : public eastl::false_type {}; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template - struct is_insert_iterator< eastl::insert_iterator > + struct EASTL_REMOVE_AT_2024_SEPT is_insert_iterator> : public eastl::true_type {}; +EASTL_INTERNAL_RESTORE_DEPRECATED() diff --git a/include/EASTL/list.h b/include/EASTL/list.h index 1daa38dc..8060caa2 100644 --- a/include/EASTL/list.h +++ b/include/EASTL/list.h @@ -138,7 +138,13 @@ namespace eastl public: ListIterator() EA_NOEXCEPT; ListIterator(const ListNodeBase* pNode) EA_NOEXCEPT; - ListIterator(const iterator& x) EA_NOEXCEPT; + + template , bool> = true> + inline ListIterator(const iterator& x) EA_NOEXCEPT + : mpNode(x.mpNode) + { + // Empty + } this_type next() const EA_NOEXCEPT; this_type prev() const EA_NOEXCEPT; @@ -324,10 +330,10 @@ namespace eastl const_reference back() const; template - void emplace_front(Args&&... args); + reference emplace_front(Args&&... args); template - void emplace_back(Args&&... args); + reference emplace_back(Args&&... args); void push_front(const value_type& value); void push_front(value_type&& x); @@ -392,10 +398,10 @@ namespace eastl template void merge(this_type&& x, Compare compare); - void unique(); + size_type unique(); template - void unique(BinaryPredicate); + size_type unique(BinaryPredicate); // Sorting functionality // This is independent of the global sort algorithms, as lists are @@ -563,14 +569,6 @@ namespace eastl } - template - inline ListIterator::ListIterator(const iterator& x) EA_NOEXCEPT - : mpNode(const_cast(x.mpNode)) - { - // Empty - } - - template inline typename ListIterator::this_type ListIterator::next() const EA_NOEXCEPT @@ -1178,16 +1176,18 @@ namespace eastl template template - void list::emplace_front(Args&&... args) + typename list::reference list::emplace_front(Args&&... args) { DoInsertValue(internalNode().mpNext, eastl::forward(args)...); + return static_cast(internalNode().mpNext)->mValue; // Same as return front(); } template template - void list::emplace_back(Args&&... args) + typename list::reference list::emplace_back(Args&&... args) { DoInsertValue(&internalNode(), eastl::forward(args)...); + return static_cast(internalNode().mpPrev)->mValue; // Same as return back(); } @@ -1657,8 +1657,9 @@ namespace eastl template - void list::unique() + typename list::size_type list::unique() { + size_type numRemoved = 0; iterator first(begin()); const iterator last(end()); @@ -1668,20 +1669,28 @@ namespace eastl while(++next != last) { - if(*first == *next) + if (*first == *next) + { DoErase(next.mpNode); + ++numRemoved; + next = first; + } else + { first = next; - next = first; + } } } + + return numRemoved; } template template - void list::unique(BinaryPredicate predicate) + typename list::size_type list::unique(BinaryPredicate predicate) { + size_type numRemoved = 0; iterator first(begin()); const iterator last(end()); @@ -1691,13 +1700,20 @@ namespace eastl while(++next != last) { - if(predicate(*first, *next)) + if (predicate(*first, *next)) + { DoErase(next.mpNode); + ++numRemoved; + next = first; + } else + { first = next; - next = first; + } } } + + return numRemoved; } diff --git a/include/EASTL/map.h b/include/EASTL/map.h index 22637fe4..87d87da6 100644 --- a/include/EASTL/map.h +++ b/include/EASTL/map.h @@ -11,6 +11,9 @@ #include #include #include +#if EASTL_EXCEPTIONS_ENABLED +#include +#endif #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. @@ -126,10 +129,21 @@ namespace eastl map(this_type&& x); map(this_type&& x, const allocator_type& allocator); map(std::initializer_list ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_MAP_DEFAULT_ALLOCATOR); + map(std::initializer_list ilist, const allocator_type& allocator); template map(Iterator itBegin, Iterator itEnd); // allocator arg removed because VC7.1 fails on the default arg. To consider: Make a second version of this function without a default arg. + // missing constructors, to implement: + // + // map(const this_type& x, const allocator_type& allocator); + // + // template + // map(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& alloc = Allocator()); + // + // template + // map(InputIterator first, InputIterator last, const Allocator& alloc); + this_type& operator=(const this_type& x) { return (this_type&)base_type::operator=(x); } this_type& operator=(std::initializer_list ilist) { return (this_type&)base_type::operator=(ilist); } this_type& operator=(this_type&& x) { return (this_type&)base_type::operator=(eastl::move(x)); } @@ -147,9 +161,19 @@ namespace eastl size_type erase(const Key& key); size_type count(const Key& key) const; + // missing transparent key support: + // template + // size_type count(const K& k) const; + eastl::pair equal_range(const Key& key); eastl::pair equal_range(const Key& key) const; + // missing transparent key support: + // template + // eastl::pair equal_range(const K& k); + // template + // eastl::pair equal_range(const K& k) const; + T& operator[](const Key& key); // Of map, multimap, set, and multimap, only map has operator[]. T& operator[](Key&& key); @@ -247,10 +271,21 @@ namespace eastl multimap(this_type&& x); multimap(this_type&& x, const allocator_type& allocator); multimap(std::initializer_list ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_MULTIMAP_DEFAULT_ALLOCATOR); + multimap(std::initializer_list ilist, const allocator_type& allocator); template multimap(Iterator itBegin, Iterator itEnd); // allocator arg removed because VC7.1 fails on the default arg. To consider: Make a second version of this function without a default arg. + // missing constructors, to implement: + // + // multimap(const this_type& x, const allocator_type& allocator); + // + // template + // multimap(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& alloc = Allocator()); + // + // template + // multimap(InputIterator first, InputIterator last, const Allocator& alloc); + this_type& operator=(const this_type& x) { return (this_type&)base_type::operator=(x); } this_type& operator=(std::initializer_list ilist) { return (this_type&)base_type::operator=(ilist); } this_type& operator=(this_type&& x) { return (this_type&)base_type::operator=(eastl::move(x)); } @@ -268,15 +303,31 @@ namespace eastl size_type erase(const Key& key); size_type count(const Key& key) const; + // missing transparent key support: + // template + // size_type count(const K& k) const; + eastl::pair equal_range(const Key& key); eastl::pair equal_range(const Key& key) const; + // missing transparent key support: + // template + // eastl::pair equal_range(const K& k); + // template + // eastl::pair equal_range(const K& k) const; + /// equal_range_small /// This is a special version of equal_range which is optimized for the /// case of there being few or no duplicated keys in the tree. eastl::pair equal_range_small(const Key& key); eastl::pair equal_range_small(const Key& key) const; + // missing transparent key support: + // template + // eastl::pair equal_range_small(const K& k); + // template + // eastl::pair equal_range_small(const K& k) const; + private: // these base member functions are not included in multimaps using base_type::insert_or_assign; @@ -331,6 +382,13 @@ namespace eastl } + template + inline map::map(std::initializer_list ilist, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), Compare(), allocator) + { + } + + template template inline map::map(Iterator itBegin, Iterator itEnd) @@ -627,6 +685,13 @@ namespace eastl } + template + inline multimap::multimap(std::initializer_list ilist, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), Compare(), allocator) + { + } + + template template inline multimap::multimap(Iterator itBegin, Iterator itEnd) diff --git a/include/EASTL/memory.h b/include/EASTL/memory.h index 79e55ff4..39309a28 100644 --- a/include/EASTL/memory.h +++ b/include/EASTL/memory.h @@ -28,11 +28,6 @@ // uninitialized_fill_n // uninitialized_value_construct // uninitialized_value_construct_n -// uninitialized_copy_ptr - Extention to standard functionality. -// uninitialized_move_ptr - Extention to standard functionality. -// uninitialized_move_ptr_if_noexcept- Extention to standard functionality. -// uninitialized_fill_ptr - Extention to standard functionality. -// uninitialized_fill_n_ptr - Extention to standard functionality. // uninitialized_copy_fill - Extention to standard functionality. // uninitialized_fill_copy - Extention to standard functionality. // uninitialized_copy_copy - Extention to standard functionality. @@ -57,10 +52,18 @@ // pointer_traits // // Deprecations: +// (EASTL_REMOVE_AT_2024_APRIL) // uninitialized_relocate - Use one of the other uninitialized_xxxx functions with move semantics. // uninitialized_default_fill - Use uninitialized_value_construct instead. // uninitialized_default_fill_n - Use uninitialized_value_construct_n instead. // +// (EASTL_REMOVE_AT_2024_SEPT) +// uninitialized_copy_ptr - Use uninitialized_copy instead. +// uninitialized_move_ptr - Use uninitialized_move instead. +// uninitialized_move_ptr_if_noexcept- Use uninitialized_move_if_noexcept instead. +// uninitialized_fill_ptr - Use uninitialized_fill instead. +// uninitialized_fill_n_ptr - Use uninitialized_fill_n instead. +// // Deprecated in C++17: // get_temporary_buffer // @@ -73,7 +76,6 @@ #include #include -#include #include #include #include @@ -360,15 +362,15 @@ namespace eastl return *this; } - raw_storage_iterator& operator++() + raw_storage_iterator& operator++() { ++mIterator; return *this; } - raw_storage_iterator operator++(int) + raw_storage_iterator operator++(int) { - raw_storage_iterator tempIterator = *this; + raw_storage_iterator tempIterator = *this; ++mIterator; return tempIterator; } @@ -730,9 +732,9 @@ namespace eastl /// /// This is a specialization of uninitialized_copy for iterators that are pointers. We use it because /// internally it uses generic_iterator to make pointers act like regular eastl::iterator. - /// + /// template - inline Result uninitialized_copy_ptr(First first, Last last, Result result) + EASTL_REMOVE_AT_2024_SEPT inline Result uninitialized_copy_ptr(First first, Last last, Result result) { return eastl::uninitialized_copy(first, last, result); } @@ -743,58 +745,11 @@ namespace eastl /// /// This is a specialization of uninitialized_move for iterators that are pointers. We use it because /// internally it uses generic_iterator to make pointers act like regular eastl::iterator. - /// - namespace Internal - { - template - inline ForwardIterator uninitialized_move_impl(InputIterator first, InputIterator last, ForwardIterator dest, true_type) - { - return eastl::copy(first, last, dest); // The copy() in turn will use memcpy for trivially copyable types. - } - - template - inline ForwardIterator uninitialized_move_impl(InputIterator first, InputIterator last, ForwardIterator dest, false_type) - { - typedef typename eastl::iterator_traits::value_type value_type; - ForwardIterator currentDest(dest); - - // We must run a loop over every element and move-construct it at the new location. - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - for(; first != last; ++first, ++currentDest) - ::new((void*)eastl::addressof(*currentDest)) value_type(eastl::move(*first)); // If value_type has a move constructor then it will be used here. - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - // We have a problem here: If an exception occurs while doing the loop below then we will - // have values that were moved from the source to the dest that may need to be moved back - // in the catch. What does the C++11 Standard say about this? And what happens if there's an - // exception while moving them back? We may want to trace through a conforming C++11 Standard - // Library to see what it does and do something similar. Given that rvalue references are - // objects that are going away, we may not need to move the values back, though that has the - // side effect of a certain kind of lost elements problem. - for(; dest < currentDest; ++dest) - (*dest).~value_type(); - throw; - } - #endif - - return currentDest; - } - } - + /// template - inline Result uninitialized_move_ptr(First first, Last last, Result dest) + EASTL_REMOVE_AT_2024_SEPT inline Result uninitialized_move_ptr(First first, Last last, Result dest) { - typedef typename eastl::iterator_traits >::value_type value_type; - const generic_iterator i(Internal::uninitialized_move_impl(eastl::generic_iterator(first), // generic_iterator makes a pointer act like an iterator. - eastl::generic_iterator(last), - eastl::generic_iterator(dest), - eastl::is_trivially_copyable())); // is_trivially_copyable identifies if (non-overlapping) objects may be safely copied by means of memcpy/memmove. - return i.base(); + return uninitialized_move(first, last, dest); } @@ -831,20 +786,20 @@ namespace eastl template inline ForwardIterator uninitialized_move_if_noexcept(InputIterator first, InputIterator last, ForwardIterator dest) { +#if EASTL_EXCEPTIONS_ENABLED return eastl::uninitialized_copy(eastl::make_move_if_noexcept_iterator(first), eastl::make_move_if_noexcept_iterator(last), dest); +#else + return eastl::uninitialized_move(first, last, dest); +#endif } /// uninitialized_move_ptr_if_noexcept /// template - inline Result uninitialized_move_ptr_if_noexcept(First first, Last last, Result dest) + EASTL_REMOVE_AT_2024_SEPT inline Result uninitialized_move_ptr_if_noexcept(First first, Last last, Result dest) { - #if EASTL_EXCEPTIONS_ENABLED - return eastl::uninitialized_move_if_noexcept(first, last, dest); - #else - return eastl::uninitialized_move_ptr(first, last, dest); - #endif + return eastl::uninitialized_move_if_noexcept(first, last, dest); } @@ -1142,12 +1097,9 @@ namespace eastl /// can't do with a pointer by itself. /// template - inline void uninitialized_fill_ptr(T* first, T* last, const T& value) + EASTL_REMOVE_AT_2024_SEPT inline void uninitialized_fill_ptr(T* first, T* last, const T& value) { - typedef typename eastl::iterator_traits >::value_type value_type; - Internal::uninitialized_fill_impl(eastl::generic_iterator(first), - eastl::generic_iterator(last), value, - eastl::is_trivially_copy_assignable()); + uninitialized_fill(first, last, value); } /// uninitialized_fill_n @@ -1163,13 +1115,14 @@ namespace eastl namespace Internal { template - inline void uninitialized_fill_n_impl(ForwardIterator first, Count n, const T& value, true_type) + inline void uninitialized_fill_n_impl(ForwardIterator first, Count n, const T& value, true_type /* is_trivially_copy_assignable */) { + // todo: implementation uses operator = but should instead be using the copy constructor, as documented. eastl::fill_n(first, n, value); } template - void uninitialized_fill_n_impl(ForwardIterator first, Count n, const T& value, false_type) + void uninitialized_fill_n_impl(ForwardIterator first, Count n, const T& value, false_type /* is_trivially_copy_assignable */) { typedef typename eastl::iterator_traits::value_type value_type; ForwardIterator currentDest(first); @@ -1208,10 +1161,9 @@ namespace eastl /// can't do with a pointer by itself. /// template - inline void uninitialized_fill_n_ptr(T* first, Count n, const T& value) + EASTL_REMOVE_AT_2024_SEPT inline void uninitialized_fill_n_ptr(T* first, Count n, const T& value) { - typedef typename eastl::iterator_traits >::value_type value_type; - Internal::uninitialized_fill_n_impl(eastl::generic_iterator(first), n, value, eastl::is_trivially_copy_assignable()); + uninitialized_fill_n(first, n, value); } diff --git a/include/EASTL/meta.h b/include/EASTL/meta.h index 545354d4..c4b1674e 100644 --- a/include/EASTL/meta.h +++ b/include/EASTL/meta.h @@ -7,6 +7,7 @@ #include #include +#include #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. diff --git a/include/EASTL/numeric.h b/include/EASTL/numeric.h index 200be6cd..3834b9a1 100644 --- a/include/EASTL/numeric.h +++ b/include/EASTL/numeric.h @@ -232,68 +232,163 @@ namespace eastl return result; } +} - #if defined(EA_COMPILER_CPP20_ENABLED) - /// midpoint - /// - /// Computes the midpoint between the LHS and RHS by adding them together, then dividing the sum by 2. - /// If the operands are of integer type and the sum is odd, the result will be rounded closer to the LHS. - /// If the operands are floating points, then at most one inexact operation occurs. - /// - template - constexpr eastl::enable_if_t && !eastl::is_same_v, bool>, T> midpoint(const T lhs, const T rhs) EA_NOEXCEPT + +/// +/// is_constant_evaluated +/// +/// Detects whether the function call occurs within a constant-evaluated context. +/// +/// See: https://en.cppreference.com/w/cpp/types/is_constant_evaluated +/// +#if defined(__cpp_lib_is_constant_evaluated) +namespace eastl +{ + [[nodiscard]] constexpr bool is_constant_evaluated() noexcept + { + // MSVC, Clang, and GCC all use the same builtin name + return __builtin_is_constant_evaluated(); + } +} +#endif + +/// +/// isnan +/// +/// Returns true if the argument is a NaN floating-point value. +/// +#if defined(EA_COMPILER_CPP20_ENABLED) +namespace eastl +{ + [[nodiscard]] constexpr bool isnan(float f) { - // If T is an integral type... + return f != f; + } + + [[nodiscard]] constexpr bool isnan(double d) + { + return d != d; + } + + [[nodiscard]] constexpr bool isnan(long double d) + { + return d != d; + } +} +#endif + + +/// +/// midpoint +/// +/// Computes the midpoint of integers, floating-points, or pointers @lhs and @rhs +/// +/// If the operands are of integer type and the sum is odd, the result will be rounded closer to @lhs +/// If the operands are floating points, then at most one inexact operation occurs. +/// +#if defined(EA_COMPILER_CPP20_ENABLED) +namespace eastl +{ + template && !is_same_v, bool>, int> = 0> + constexpr T midpoint(T lhs, T rhs) noexcept + { + // If T is an integral type if constexpr(eastl::is_integral_v) { using U = eastl::make_unsigned_t; - int sign = 1; - U m = lhs; - U M = rhs; - - if (lhs > rhs) - { - sign = -1; - m = rhs; - M = lhs; - } + bool const lgtr = lhs > rhs; + int const sign = lgtr ? -1 : 1; + U const a = lgtr ? lhs : rhs; + U const b = lgtr ? rhs : lhs; - return lhs + static_cast(sign * static_cast((U(M - m)) / 2 )); + return lhs + static_cast(sign * static_cast(static_cast(a - b) / 2)); } - // otherwise if T is a floating point else { - const T LO = eastl::numeric_limits::min() * 2; - const T HI = eastl::numeric_limits::max() / 2; + if (eastl::is_constant_evaluated()) + { + // almost any operation, including addition, on floating-point + // values that include a signalling-NaN will throw a floating- + // point exception. this is UB when it occurs in constexpr + // expression evaluation. to circumvent this we will simply + // return the NaN value outright. + + if (isnan(lhs)) + { + return lhs; + } + + if (isnan(rhs)) + { + return rhs; + } + } + else if (isnan(lhs) || isnan(rhs)) + { + // when we encounter a NaN at runtime, we will propagate the + // NaNiness immediately and raise FE_INVALID + + return lhs + rhs; + } - const T lhs_abs = (lhs < 0) ? -lhs : lhs; - const T rhs_abs = (rhs < 0) ? -rhs : rhs; + auto lhs_abs = (lhs < 0) ? -lhs : lhs; + auto rhs_abs = (rhs < 0) ? -rhs : rhs; - if (lhs_abs <= HI && rhs_abs <= HI) + constexpr T hi = eastl::numeric_limits::max() / 2; + if (lhs_abs <= hi && rhs_abs <= hi) + { + // lhs and rhs are small enough that this will not overflow return (lhs + rhs) / 2; - if (lhs_abs < LO) - return lhs + (rhs / 2); - if (rhs_abs < LO) - return (lhs / 2) + rhs; - return (lhs / 2) + (rhs / 2); + } + + // either lhs or rhs has a very small magnitude. we divide the + // larger of the two (an inexact operation), and add the small + // value to it. because the small value is so very small (smaller + // than one ULP) we can simply add it directly. + constexpr T lo = eastl::numeric_limits::min() * 2; + if (lhs_abs < lo) + { + return lhs + rhs / 2; + } + else if (rhs_abs < lo) + { + return lhs / 2 + rhs; + } + + // neither lhs nor rhs are small enough to allow for the above + // magic, and they are too large to first add and then perform a + // single division, so we perform the slowest but correct + // operation by dividing both in half first before summing. + return lhs / 2 + rhs / 2; } } - /// midpoint - /// - /// Computes the midpoint address between pointers LHS and RHS. - /// The midpoint address closer to the LHS is chosen. - /// - template - constexpr eastl::enable_if_t, T*> midpoint(T* lhs, T* rhs) + template , int> = 0> + [[nodiscard]] constexpr T* midpoint(T* lhs, T* rhs) noexcept { - return lhs + ((rhs - lhs) / 2); + if (lhs > rhs) + { + return lhs - ((lhs - rhs) >> 1); + } + else + { + return lhs + ((rhs - lhs) >> 1); + } } +} +#endif + + + +namespace eastl +{ + #if defined(EA_COMPILER_CPP20_ENABLED) template constexpr T shared_lerp(const T a, const T b, const T t) EA_NOEXCEPT { @@ -330,15 +425,9 @@ namespace eastl constexpr double lerp(double a, double b, double t) EA_NOEXCEPT { return shared_lerp(a, b, t); } constexpr long double lerp(long double a, long double b, long double t) EA_NOEXCEPT { return shared_lerp(a, b, t); } #endif +} -} // namespace eastl #endif // Header include guard - - - - - - diff --git a/include/EASTL/numeric_limits.h b/include/EASTL/numeric_limits.h index 0d7dc97a..ae17d9c1 100644 --- a/include/EASTL/numeric_limits.h +++ b/include/EASTL/numeric_limits.h @@ -51,18 +51,6 @@ // 4296 - expression is always false EA_DISABLE_VC_WARNING(4310 4296) -// EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED -// -// Defined as 0 or 1. -// Indicates whether we need to define our own implementations of inf, nan, snan, denorm floating point constants. -// -#if !defined(EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED) - #if (defined(EA_COMPILER_GNUC) || defined(__clang__) && defined(__FLT_MIN__)) || defined(_CPPLIB_VER) // __FLT_MIN__ detects if it's really GCC/clang and not a mimic. _CPPLIB_VER (Dinkumware) covers VC++, and Microsoft platforms. - #define EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED 0 - #else - #define EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED 1 - #endif -#endif /////////////////////////////////////////////////////////////////////////////// @@ -83,19 +71,6 @@ EA_DISABLE_VC_WARNING(4310 4296) #endif -// EA_CONSTEXPR -// EA_CONSTEXPR is defined in EABase 2.00.38 and later. -#if !defined(EA_CONSTEXPR) - #define EA_CONSTEXPR -#endif - -// EA_CONSTEXPR_OR_CONST -// EA_CONSTEXPR_OR_CONST is defined in EABase 2.00.39 and later. -#if !defined(EA_CONSTEXPR_OR_CONST) - #define EA_CONSTEXPR_OR_CONST const -#endif - - /////////////////////////////////////////////////////////////////////////////// // EASTL_LIMITS macros // These apply to integral types only. @@ -150,403 +125,266 @@ namespace eastl }; - namespace Internal - { - // Defines default values for numeric_limits, which can be overridden by class specializations. - // See C++11 18.3.2.3 - struct numeric_limits_base - { - // true if the type has an explicit specialization defined in the template class; false if not. - static EA_CONSTEXPR_OR_CONST bool is_specialized = false; - - // Integer types: the number of *bits* in the representation of T. - // Floating types: the number of digits in the mantissa of T (same as FLT_MANT_DIG, DBL_MANT_DIG or LDBL_MANT_DIG). - static EA_CONSTEXPR_OR_CONST int digits = 0; - - // The number of decimal digits that can be represented by T. - // Equivalent to FLT_DIG, DBL_DIG or LDBL_DIG for floating types. - static EA_CONSTEXPR_OR_CONST int digits10 = 0; - - // The number of decimal digits required to make sure that two distinct values of the type have distinct decimal representations. - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - - // True if the type is signed. - static EA_CONSTEXPR_OR_CONST bool is_signed = false; - - // True if the type is integral. - static EA_CONSTEXPR_OR_CONST bool is_integer = false; - - // True if the type uses an exact representation. All integral types are - // exact, but other types can be exact as well. - static EA_CONSTEXPR_OR_CONST bool is_exact = false; - - // Integer types: the base of the representation. Always 2 for integers. - // Floating types: the base of the exponent representation. Always FLT_RADIX (typically 2) for float. - static EA_CONSTEXPR_OR_CONST int radix = 0; - - // The minimum integral radix-based exponent representable by the type. - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - - // The minimum integral base 10 exponent representable by the type. - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - - // The maximum integral radix-based exponent representable by the type. - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - - // The maximum integral base 10 exponent representable by the type. - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - - // True if the type has a representation for positive infinity. - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - - // True if the type has a representation for a quiet (non-signaling) NaN. - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - - // True if the type has a representation for a signaling NaN. - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - - // An enumeration which identifies denormalization behavior. - // In practice the application can change this at runtime via hardware-specific commands. - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - - // True if the loss of accuracy is detected as a denormalization loss. - // Typically false for integer types and true for floating point types. - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - - // True if the type has a bounded set of representable values. Typically true for - // all built-in numerial types (integer and floating point). - static EA_CONSTEXPR_OR_CONST bool is_bounded = false; - - // True if the type has a modulo representation (if it's possible to add two - // positive numbers and have a result that wraps around to a third number - // that is less. Typically true for integers and false for floating types. - static EA_CONSTEXPR_OR_CONST bool is_modulo = false; - - // True if trapping (arithmetic exception generation) is implemented for this type. - // Typically true for integer types (div by zero) and false for floating point types, - // though in practice the application may be able to change floating point to trap at runtime. - static EA_CONSTEXPR_OR_CONST bool traps = false; - - // True if tiny-ness is detected before rounding. - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - - // An enumeration which identifies default rounding behavior. - // In practice the application can change this at runtime via hardware-specific commands. - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - - // True if the type is floating point and follows the IEC 559 standard (IEEE 754). - // In practice the application or OS can change this at runtime via hardware-specific commands or via compiler optimizations. - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - }; - - - #if EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED - extern EASTL_API float gFloatInfinity; - extern EASTL_API float gFloatNaN; - extern EASTL_API float gFloatSNaN; - extern EASTL_API float gFloatDenorm; - - extern EASTL_API double gDoubleInfinity; - extern EASTL_API double gDoubleNaN; - extern EASTL_API double gDoubleSNaN; - extern EASTL_API double gDoubleDenorm; - - extern EASTL_API long double gLongDoubleInfinity; - extern EASTL_API long double gLongDoubleNaN; - extern EASTL_API long double gLongDoubleSNaN; - extern EASTL_API long double gLongDoubleDenorm; - #endif - - } // namespace Internal - - // Default numeric_limits. // See C++11 18.3.2.3 template - class numeric_limits : public Internal::numeric_limits_base - { - public: - typedef T value_type; - - static value_type min() - { return value_type(0); } - - static value_type max() - { return value_type(0); } - - static value_type lowest() - { return min(); } - - static value_type epsilon() - { return value_type(0); } - - static value_type round_error() - { return value_type(0); } - - static value_type denorm_min() - { return value_type(0); } - - static value_type infinity() - { return value_type(0); } - - static value_type quiet_NaN() - { return value_type(0); } - - static value_type signaling_NaN() - { return value_type(0); } - }; + class numeric_limits; // Const/volatile variations of numeric_limits. template - class numeric_limits : public numeric_limits - { - }; + class numeric_limits + : public numeric_limits + {}; template - class numeric_limits : public numeric_limits - { - }; + class numeric_limits + : public numeric_limits + {}; template - class numeric_limits : public numeric_limits - { - }; + class numeric_limits + : public numeric_limits + {}; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef bool value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = 1; // In practice bool is stores as a byte, or sometimes an int. - static EA_CONSTEXPR_OR_CONST int digits10 = 0; - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = false; // In practice bool may be implemented as signed char. - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = false; - static EA_CONSTEXPR_OR_CONST bool traps = true; // Should this be true or false? Given that it's implemented in hardware as an integer type, we use true. - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = 1; // In practice bool is stores as a byte, or sometimes an int. + static constexpr int digits10 = 0; + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = false; // In practice bool may be implemented as signed char. + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr bool traps = true; // Should this be true or false? Given that it's implemented in hardware as an integer type, we use true. + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return false; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return true; } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return false; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return false; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return false; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return value_type(); } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef char value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } // Question: Should we return 0 here or value_type()? - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return (value_type)0; } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef unsigned char value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_U(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = false; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_U(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return 0; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_U(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return 0; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return (value_type)0; } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef signed char value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_S(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_S(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN_S(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_S(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN_S(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return (value_type)0; } }; @@ -557,118 +395,120 @@ namespace eastl // may be to use __wchar_t here for VC++ instead of wchar_t, as __wchar_t is always a true // unique type under VC++. http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/9059330a-7cce-4d0d-a8e0-e1dcb63322bd/ template<> - struct numeric_limits + class numeric_limits { + public: typedef wchar_t value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return (value_type)0; } }; #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE template<> - struct numeric_limits + class numeric_limits { + public: typedef char8_t value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return 0; } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return 0; } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return 0; } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return (value_type)0; } }; #endif @@ -677,59 +517,60 @@ namespace eastl // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef char16_t value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return (value_type)0; } }; @@ -740,59 +581,60 @@ namespace eastl // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef char32_t value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = EASTL_LIMITS_IS_SIGNED(value_type); + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return (value_type)0; } }; @@ -801,118 +643,120 @@ namespace eastl // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef unsigned short value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_U(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = false; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_U(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return 0; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_U(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return 0; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef signed short value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_S(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_S(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN_S(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_S(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN_S(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; @@ -920,354 +764,360 @@ namespace eastl // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef unsigned int value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_U(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = false; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_U(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return 0; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_U(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return 0; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef signed int value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_S(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_S(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return INT_MIN; } // It's hard to get EASTL_LIMITS_MIN_S to work with all compilers here. - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_S(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return INT_MIN; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef unsigned long value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_U(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = false; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_U(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return 0; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_U(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return 0; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef signed long value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_S(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_S(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return LONG_MIN; } // It's hard to get EASTL_LIMITS_MIN_S to work with all compilers here. - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_S(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return LONG_MIN; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef unsigned long long value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_U(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = false; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_U(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return 0; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_U(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return 0; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef signed long long value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_S(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_S(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN_S(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_S(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN_S(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; @@ -1275,118 +1125,120 @@ namespace eastl #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(__clang__)) // If __int128_t/__uint128_t is supported... // numeric_limits<__uint128_t> template<> - struct numeric_limits<__uint128_t> + class numeric_limits<__uint128_t> { + public: typedef __uint128_t value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_U(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = false; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_U(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_U(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return 0; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_U(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return 0; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; // numeric_limits<__int128_t> template<> - struct numeric_limits<__int128_t> + class numeric_limits<__int128_t> { + public: typedef __int128_t value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = EASTL_LIMITS_DIGITS_S(value_type); - static EA_CONSTEXPR_OR_CONST int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); - static EA_CONSTEXPR_OR_CONST int max_digits10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = true; - static EA_CONSTEXPR_OR_CONST bool is_exact = true; - static EA_CONSTEXPR_OR_CONST int radix = 2; - static EA_CONSTEXPR_OR_CONST int min_exponent = 0; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent = 0; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = 0; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = true; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_toward_zero; - static EA_CONSTEXPR_OR_CONST bool has_infinity = false; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = false; - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = false; - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_absent; - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; - static EA_CONSTEXPR_OR_CONST bool is_iec559 = false; - - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = EASTL_LIMITS_DIGITS_S(value_type); + static constexpr int digits10 = EASTL_LIMITS_DIGITS10_S(value_type); + static constexpr int max_digits10 = 0; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr bool is_iec559 = false; + + static constexpr value_type min() { return EASTL_LIMITS_MIN_S(value_type); } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return EASTL_LIMITS_MAX_S(value_type); } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return EASTL_LIMITS_MIN_S(value_type); } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return 0; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return value_type(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return value_type(); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return value_type(); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return static_cast(0); } }; #endif @@ -1394,396 +1246,312 @@ namespace eastl // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef float value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = FLT_MANT_DIG; - static EA_CONSTEXPR_OR_CONST int digits10 = FLT_DIG; - static EA_CONSTEXPR_OR_CONST int max_digits10 = FLT_MANT_DIG; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = false; - static EA_CONSTEXPR_OR_CONST bool is_exact = false; - static EA_CONSTEXPR_OR_CONST int radix = FLT_RADIX; - static EA_CONSTEXPR_OR_CONST int min_exponent = FLT_MIN_EXP; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = FLT_MIN_10_EXP; - static EA_CONSTEXPR_OR_CONST int max_exponent = FLT_MAX_EXP; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = FLT_MAX_10_EXP; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = false; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_to_nearest; - static EA_CONSTEXPR_OR_CONST bool has_infinity = true; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = true; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = true; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_present; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool is_iec559 = has_infinity && has_quiet_NaN && (has_denorm == denorm_present); - - #if EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED - static value_type min() - { return FLT_MIN; } - - static value_type max() - { return FLT_MAX; } - - static value_type lowest() - { return -FLT_MAX; } - - static value_type epsilon() - { return FLT_EPSILON; } - - static value_type round_error() - { return 0.5f; } - - static value_type infinity() - { return Internal::gFloatInfinity; } - - static value_type quiet_NaN() - { return Internal::gFloatNaN; } - - static value_type signaling_NaN() - { return Internal::gFloatSNaN; } - - static value_type denorm_min() - { return Internal::gFloatDenorm; } - - #elif (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__FLT_MIN__) - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = FLT_MANT_DIG; + static constexpr int digits10 = FLT_DIG; + static constexpr int max_digits10 = FLT_MANT_DIG; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr int radix = FLT_RADIX; + static constexpr int min_exponent = FLT_MIN_EXP; + static constexpr int min_exponent10 = FLT_MIN_10_EXP; + static constexpr int max_exponent = FLT_MAX_EXP; + static constexpr int max_exponent10 = FLT_MAX_10_EXP; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_to_nearest; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; // This may be wrong for some platforms. + static constexpr bool has_signaling_NaN = true; // This may be wrong for some platforms. + static constexpr float_denorm_style has_denorm = denorm_present; // This may be wrong for some platforms. + static constexpr bool has_denorm_loss = false; // This may be wrong for some platforms. + static constexpr bool is_iec559 = has_infinity && has_quiet_NaN && (has_denorm == denorm_present); + + #if (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__FLT_MIN__) + static constexpr value_type min() { return __FLT_MIN__; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return __FLT_MAX__; } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return -__FLT_MAX__; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return __FLT_EPSILON__; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0.5f; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return __builtin_huge_valf(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return __builtin_nanf(""); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return __builtin_nansf(""); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return __FLT_DENORM_MIN__; } #elif defined(_CPPLIB_VER) // If using the Dinkumware Standard library... - static value_type min() + static constexpr value_type min() { return FLT_MIN; } - static value_type max() + static constexpr value_type max() { return FLT_MAX; } - static value_type lowest() + static constexpr value_type lowest() { return -FLT_MAX; } - static value_type epsilon() + static constexpr value_type epsilon() { return FLT_EPSILON; } - static value_type round_error() + static constexpr value_type round_error() { return 0.5f; } #if defined(_MSVC_STL_UPDATE) && _MSVC_STL_UPDATE >= 202206L // If using a recent version of MSVC's STL... - static value_type infinity() - { return __builtin_huge_valf(); } + static constexpr value_type infinity() + { return __builtin_huge_valf(); } - static value_type quiet_NaN() - { return __builtin_nanf("0"); } + static constexpr value_type quiet_NaN() + { return __builtin_nanf("0"); } - static value_type signaling_NaN() - { return __builtin_nansf("1"); } + static constexpr value_type signaling_NaN() + { return __builtin_nansf("1"); } - static value_type denorm_min() - { return FLT_TRUE_MIN; } + static constexpr value_type denorm_min() + { return FLT_TRUE_MIN; } #else - static value_type infinity() - { return _CSTD _FInf._Float; } + static constexpr value_type infinity() + { return _CSTD _FInf._Float; } - static value_type quiet_NaN() - { return _CSTD _FNan._Float; } + static constexpr value_type quiet_NaN() + { return _CSTD _FNan._Float; } - static value_type signaling_NaN() - { return _CSTD _FSnan._Float; } + static constexpr value_type signaling_NaN() + { return _CSTD _FSnan._Float; } - static value_type denorm_min() - { return _CSTD _FDenorm._Float; } + static constexpr value_type denorm_min() + { return _CSTD _FDenorm._Float; } #endif - #endif }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef double value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = DBL_MANT_DIG; - static EA_CONSTEXPR_OR_CONST int digits10 = DBL_DIG; - static EA_CONSTEXPR_OR_CONST int max_digits10 = DBL_MANT_DIG; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = false; - static EA_CONSTEXPR_OR_CONST bool is_exact = false; - static EA_CONSTEXPR_OR_CONST int radix = FLT_RADIX; // FLT_RADIX applies to all floating point types. - static EA_CONSTEXPR_OR_CONST int min_exponent = DBL_MIN_EXP; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = DBL_MIN_10_EXP; - static EA_CONSTEXPR_OR_CONST int max_exponent = DBL_MAX_EXP; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = DBL_MAX_10_EXP; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = false; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_to_nearest; - static EA_CONSTEXPR_OR_CONST bool has_infinity = true; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = true; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = true; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_present; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool is_iec559 = has_infinity && has_quiet_NaN && (has_denorm == denorm_present); - - #if EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED - static value_type min() - { return DBL_MIN; } - - static value_type max() - { return DBL_MAX; } - - static value_type lowest() - { return -DBL_MAX; } - - static value_type epsilon() - { return DBL_EPSILON; } - - static value_type round_error() - { return 0.5f; } - - static value_type infinity() - { return Internal::gDoubleInfinity; } - - static value_type quiet_NaN() - { return Internal::gDoubleNaN; } - - static value_type signaling_NaN() - { return Internal::gDoubleSNaN; } - - static value_type denorm_min() - { return Internal::gDoubleDenorm; } - - #elif (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__DBL_MIN__) - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = DBL_MANT_DIG; + static constexpr int digits10 = DBL_DIG; + static constexpr int max_digits10 = DBL_MANT_DIG; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr int radix = FLT_RADIX; // FLT_RADIX applies to all floating point types. + static constexpr int min_exponent = DBL_MIN_EXP; + static constexpr int min_exponent10 = DBL_MIN_10_EXP; + static constexpr int max_exponent = DBL_MAX_EXP; + static constexpr int max_exponent10 = DBL_MAX_10_EXP; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_to_nearest; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; // This may be wrong for some platforms. + static constexpr bool has_signaling_NaN = true; // This may be wrong for some platforms. + static constexpr float_denorm_style has_denorm = denorm_present; // This may be wrong for some platforms. + static constexpr bool has_denorm_loss = false; // This may be wrong for some platforms. + static constexpr bool is_iec559 = has_infinity && has_quiet_NaN && (has_denorm == denorm_present); + + #if (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__DBL_MIN__) + static constexpr value_type min() { return __DBL_MIN__; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return __DBL_MAX__; } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return -__DBL_MAX__; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return __DBL_EPSILON__; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0.5f; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return __builtin_huge_val(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return __builtin_nan(""); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return __builtin_nans(""); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return __DBL_DENORM_MIN__; } #elif defined(_CPPLIB_VER) // If using the Dinkumware Standard library... - static value_type min() + static constexpr value_type min() { return DBL_MIN; } - static value_type max() + static constexpr value_type max() { return DBL_MAX; } - static value_type lowest() + static constexpr value_type lowest() { return -DBL_MAX; } - static value_type epsilon() + static constexpr value_type epsilon() { return DBL_EPSILON; } - static value_type round_error() + static constexpr value_type round_error() { return 0.5f; } #if defined(_MSVC_STL_UPDATE) && _MSVC_STL_UPDATE >= 202206L // If using a recent version of MSVC's STL... - static value_type infinity() - { return __builtin_huge_val(); } + static constexpr value_type infinity() + { return __builtin_huge_val(); } - static value_type quiet_NaN() - { return __builtin_nan("0"); } + static constexpr value_type quiet_NaN() + { return __builtin_nan("0"); } - static value_type signaling_NaN() - { return __builtin_nans("1"); } + static constexpr value_type signaling_NaN() + { return __builtin_nans("1"); } - static value_type denorm_min() - { return DBL_TRUE_MIN; } + static constexpr value_type denorm_min() + { return DBL_TRUE_MIN; } #else - static value_type infinity() - { return _CSTD _Inf._Double; } + static constexpr value_type infinity() + { return _CSTD _Inf._Double; } - static value_type quiet_NaN() - { return _CSTD _Nan._Double; } + static constexpr value_type quiet_NaN() + { return _CSTD _Nan._Double; } - static value_type signaling_NaN() - { return _CSTD _Snan._Double; } + static constexpr value_type signaling_NaN() + { return _CSTD _Snan._Double; } - static value_type denorm_min() - { return _CSTD _Denorm._Double; } + static constexpr value_type denorm_min() + { return _CSTD _Denorm._Double; } #endif - #endif }; // numeric_limits template<> - struct numeric_limits + class numeric_limits { + public: typedef long double value_type; - static EA_CONSTEXPR_OR_CONST bool is_specialized = true; - static EA_CONSTEXPR_OR_CONST int digits = LDBL_MANT_DIG; - static EA_CONSTEXPR_OR_CONST int digits10 = LDBL_DIG; - static EA_CONSTEXPR_OR_CONST int max_digits10 = LDBL_MANT_DIG; - static EA_CONSTEXPR_OR_CONST bool is_signed = true; - static EA_CONSTEXPR_OR_CONST bool is_integer = false; - static EA_CONSTEXPR_OR_CONST bool is_exact = false; - static EA_CONSTEXPR_OR_CONST int radix = FLT_RADIX; // FLT_RADIX applies to all floating point types. - static EA_CONSTEXPR_OR_CONST int min_exponent = LDBL_MIN_EXP; - static EA_CONSTEXPR_OR_CONST int min_exponent10 = LDBL_MIN_10_EXP; - static EA_CONSTEXPR_OR_CONST int max_exponent = LDBL_MAX_EXP; - static EA_CONSTEXPR_OR_CONST int max_exponent10 = LDBL_MAX_10_EXP; - static EA_CONSTEXPR_OR_CONST bool is_bounded = true; - static EA_CONSTEXPR_OR_CONST bool is_modulo = false; - static EA_CONSTEXPR_OR_CONST bool traps = true; - static EA_CONSTEXPR_OR_CONST bool tinyness_before = false; - static EA_CONSTEXPR_OR_CONST float_round_style round_style = round_to_nearest; - static EA_CONSTEXPR_OR_CONST bool has_infinity = true; - static EA_CONSTEXPR_OR_CONST bool has_quiet_NaN = true; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool has_signaling_NaN = true; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST float_denorm_style has_denorm = denorm_present; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool has_denorm_loss = false; // This may be wrong for some platforms. - static EA_CONSTEXPR_OR_CONST bool is_iec559 = has_infinity && has_quiet_NaN && (has_denorm == denorm_present); - - #if EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED - static value_type min() - { return LDBL_MIN; } - - static value_type max() - { return LDBL_MAX; } - - static value_type lowest() - { return -LDBL_MAX; } - - static value_type epsilon() - { return LDBL_EPSILON; } - - static value_type round_error() - { return 0.5f; } - - static value_type infinity() - { return Internal::gLongDoubleInfinity; } - - static value_type quiet_NaN() - { return Internal::gLongDoubleNaN; } - - static value_type signaling_NaN() - { return Internal::gLongDoubleSNaN; } - - static value_type denorm_min() - { return Internal::gLongDoubleDenorm; } - - #elif (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__LDBL_MIN__) - static EA_CONSTEXPR value_type min() + static constexpr bool is_specialized = true; + static constexpr int digits = LDBL_MANT_DIG; + static constexpr int digits10 = LDBL_DIG; + static constexpr int max_digits10 = LDBL_MANT_DIG; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr int radix = FLT_RADIX; // FLT_RADIX applies to all floating point types. + static constexpr int min_exponent = LDBL_MIN_EXP; + static constexpr int min_exponent10 = LDBL_MIN_10_EXP; + static constexpr int max_exponent = LDBL_MAX_EXP; + static constexpr int max_exponent10 = LDBL_MAX_10_EXP; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_to_nearest; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; // This may be wrong for some platforms. + static constexpr bool has_signaling_NaN = true; // This may be wrong for some platforms. + static constexpr float_denorm_style has_denorm = denorm_present; // This may be wrong for some platforms. + static constexpr bool has_denorm_loss = false; // This may be wrong for some platforms. + static constexpr bool is_iec559 = has_infinity && has_quiet_NaN && (has_denorm == denorm_present); + + #if (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__LDBL_MIN__) + static constexpr value_type min() { return __LDBL_MIN__; } - static EA_CONSTEXPR value_type max() + static constexpr value_type max() { return __LDBL_MAX__; } - static EA_CONSTEXPR value_type lowest() + static constexpr value_type lowest() { return -__LDBL_MAX__; } - static EA_CONSTEXPR value_type epsilon() + static constexpr value_type epsilon() { return __LDBL_EPSILON__; } - static EA_CONSTEXPR value_type round_error() + static constexpr value_type round_error() { return 0.5f; } - static EA_CONSTEXPR value_type infinity() + static constexpr value_type infinity() { return __builtin_huge_val(); } - static EA_CONSTEXPR value_type quiet_NaN() + static constexpr value_type quiet_NaN() { return __builtin_nan(""); } - static EA_CONSTEXPR value_type signaling_NaN() + static constexpr value_type signaling_NaN() { return __builtin_nans(""); } - static EA_CONSTEXPR value_type denorm_min() + static constexpr value_type denorm_min() { return __LDBL_DENORM_MIN__; } #elif defined(_CPPLIB_VER) // If using the Dinkumware Standard library... - static value_type min() + static constexpr value_type min() { return LDBL_MIN; } - static value_type max() + static constexpr value_type max() { return LDBL_MAX; } - static value_type lowest() + static constexpr value_type lowest() { return -LDBL_MAX; } - static value_type epsilon() + static constexpr value_type epsilon() { return LDBL_EPSILON; } - static value_type round_error() + static constexpr value_type round_error() { return 0.5f; } #if defined(_MSVC_STL_UPDATE) && _MSVC_STL_UPDATE >= 202206L // If using a recent version of MSVC's STL... - static value_type infinity() - { return __builtin_huge_val(); } + static constexpr value_type infinity() + { return __builtin_huge_val(); } - static value_type quiet_NaN() - { return __builtin_nan("0"); } + static constexpr value_type quiet_NaN() + { return __builtin_nan("0"); } - static value_type signaling_NaN() - { return __builtin_nans("1"); } + static constexpr value_type signaling_NaN() + { return __builtin_nans("1"); } - static value_type denorm_min() - { return LDBL_TRUE_MIN; } + static constexpr value_type denorm_min() + { return LDBL_TRUE_MIN; } #else - static value_type infinity() - { return _CSTD _LInf._Long_double; } + static constexpr value_type infinity() + { return _CSTD _LInf._Long_double; } - static value_type quiet_NaN() - { return _CSTD _LNan._Long_double; } + static constexpr value_type quiet_NaN() + { return _CSTD _LNan._Long_double; } - static value_type signaling_NaN() - { return _CSTD _LSnan._Long_double; } + static constexpr value_type signaling_NaN() + { return _CSTD _LSnan._Long_double; } - static value_type denorm_min() - { return _CSTD _LDenorm._Long_double; } + static constexpr value_type denorm_min() + { return _CSTD _LDenorm._Long_double; } #endif - #endif }; diff --git a/include/EASTL/optional.h b/include/EASTL/optional.h index 2c86515f..4a52fd6d 100644 --- a/include/EASTL/optional.h +++ b/include/EASTL/optional.h @@ -31,8 +31,10 @@ #include #include #include // eastl::addressof +#include #include // eastl::in_place_t + #if EASTL_EXCEPTIONS_ENABLED EA_DISABLE_ALL_VC_WARNINGS() #include // std::logic_error. @@ -77,8 +79,20 @@ namespace eastl }; #endif + template + class optional; // Forward declaration for Internal::is_optional. + namespace Internal { + template + struct is_optional : false_type {}; + + template + struct is_optional> : true_type {}; + + template + constexpr bool is_optional_v = is_optional::value; + /////////////////////////////////////////////////////////////////////////////// /// optional_storage /// @@ -192,17 +206,18 @@ namespace eastl /////////////////////////////////////////////////////////////////////////////// /// optional /// - template - class optional : private Internal::optional_storage - { - typedef Internal::optional_storage base_type; + template + class optional : private Internal::optional_storage> + { + using base_type = Internal::optional_storage>; using base_type::destruct_value; using base_type::engaged; using base_type::val; public: - typedef T value_type; + using value_type = T; + using value_result_type = remove_volatile_t; // (ISOCPP 20.6.3) A program that necessitates the instantiation of template optional for a reference type, or // for possibly cv-qualified types in_place_t or nullopt_t is ill-formed. @@ -346,12 +361,12 @@ namespace eastl EA_CONSTEXPR inline bool has_value() const EA_NOEXCEPT { return engaged; } template - inline value_type value_or(U&& default_value) const - { return engaged ? *get_value_address() : static_cast(eastl::forward(default_value)); } + inline value_result_type value_or(U&& default_value) const& + { return engaged ? value() : static_cast(eastl::forward(default_value)); } template - inline value_type value_or(U&& default_value) - { return engaged ? *get_value_address() : static_cast(eastl::forward(default_value)); } + inline value_result_type value_or(U&& default_value) && + { return engaged ? eastl::move(value()) : static_cast(eastl::forward(default_value)); } inline T& value()& { return get_value_ref(); } inline const T& value() const& { return get_value_ref(); } @@ -365,8 +380,169 @@ namespace eastl inline const T& operator*() const& { return get_value_ref(); } inline const T&& operator*() const&& { return get_rvalue_ref(); } + // Monadic operations + template + constexpr inline auto and_then(F&& f) & + { + using U = eastl::invoke_result_t; + static_assert(Internal::is_optional_v>, + "The supplied callable isn't returning an optional."); + + if (has_value()) + { + return eastl::invoke(eastl::forward(f), value()); + } + + return eastl::remove_cvref_t(); + } + + template + constexpr inline auto and_then(F&& f) const& + { + using U = eastl::invoke_result_t; + static_assert(Internal::is_optional_v>, + "The supplied callable isn't returning an optional."); + + if (has_value()) + { + return eastl::invoke(eastl::forward(f), value()); + } + + return eastl::remove_cvref_t(); + } + + template + constexpr inline auto and_then(F&& f) && + { + using U = eastl::invoke_result_t; + static_assert(Internal::is_optional_v>, + "The supplied callable isn't returning an optional."); + + if (has_value()) + { + return eastl::invoke(eastl::forward(f), eastl::move(value())); + } + + return eastl::remove_cvref_t(); + } + + template + constexpr inline auto and_then(F&& f) const&& + { + using U = eastl::invoke_result_t; + static_assert(Internal::is_optional_v>, + "The supplied callable isn't returning an optional."); + + if (has_value()) + { + return eastl::invoke(eastl::forward(f), eastl::move(value())); + } + + return eastl::remove_cvref_t(); + } + + template + constexpr inline auto transform(F&& f) & + { + using U = eastl::remove_cvref_t>; + + static_assert(!eastl::is_same_v, "The supplied callable cannot return in_place_t."); + static_assert(!eastl::is_same_v, "The supplied callable cannot return nullopt_t."); + static_assert(eastl::is_object_v, "The supplied callable must return an object type."); + static_assert(!eastl::is_array_v, "The supplied callable cannot return an array type."); + + if (has_value()) + { + return eastl::optional(eastl::invoke(eastl::forward(f), value())); + } + + return eastl::optional(); + } + + template + constexpr inline auto transform(F&& f) const& + { + using U = eastl::remove_cvref_t>; + + static_assert(!eastl::is_same_v, "The supplied callable cannot return in_place_t."); + static_assert(!eastl::is_same_v, "The supplied callable cannot return nullopt_t."); + static_assert(eastl::is_object_v, "The supplied callable must return an object type."); + static_assert(!eastl::is_array_v, "The supplied callable cannot return an array type."); + + if (has_value()) + { + return eastl::optional(eastl::invoke(eastl::forward(f), value())); + } + + return eastl::optional(); + } + + template + constexpr inline auto transform(F&& f) && + { + using U = eastl::remove_cvref_t>; + + static_assert(!eastl::is_same_v, "The supplied callable cannot return in_place_t."); + static_assert(!eastl::is_same_v, "The supplied callable cannot return nullopt_t."); + static_assert(eastl::is_object_v, "The supplied callable must return an object type."); + static_assert(!eastl::is_array_v, "The supplied callable cannot return an array type."); + + if (has_value()) + { + return eastl::optional(eastl::invoke(eastl::forward(f), eastl::move(value()))); + } + + return eastl::optional(); + } + + template + constexpr inline auto transform(F&& f) const&& + { + using U = eastl::remove_cvref_t>; + + static_assert(!eastl::is_same_v, "The supplied callable cannot return in_place_t."); + static_assert(!eastl::is_same_v, "The supplied callable cannot return nullopt_t."); + static_assert(eastl::is_object_v, "The supplied callable must return an object type."); + static_assert(!eastl::is_array_v, "The supplied callable cannot return an array type."); + + if (has_value()) + { + return eastl::optional(eastl::invoke(eastl::forward(f), eastl::move(value()))); + } + + return eastl::optional(); + } + + template && internal::concepts::copy_constructible, int> = 0> + constexpr inline optional or_else(F&& f) const& + { + static_assert(eastl::is_same_v>, optional>, + "The supplied callable must return an optional of the same type."); + + if (has_value()) + { + return *this; + } + + return eastl::forward(f)(); + } + + template && internal::concepts::move_constructible, int> = 0> + constexpr inline optional or_else(F&& f) && + { + static_assert(eastl::is_same_v>, optional>, + "The supplied callable must return an optional of the same type."); + + if (has_value()) + { + return eastl::move(*this); + } + + return eastl::forward(f)(); + } + template - void emplace(Args&&... args) + T& emplace(Args&&... args) { if (engaged) { @@ -375,10 +551,11 @@ namespace eastl } construct_value(eastl::forward(args)...); engaged = true; + return get_value_ref(); } template - void emplace(std::initializer_list ilist, Args&&... args) + T& emplace(std::initializer_list ilist, Args&&... args) { if (engaged) { @@ -387,6 +564,7 @@ namespace eastl } construct_value(ilist, eastl::forward(args)...); engaged = true; + return get_value_ref(); } inline void swap(optional& other) diff --git a/include/EASTL/priority_queue.h b/include/EASTL/priority_queue.h index ade625aa..61fafd74 100644 --- a/include/EASTL/priority_queue.h +++ b/include/EASTL/priority_queue.h @@ -164,6 +164,18 @@ namespace eastl // // template // priority_queue(const Compare&, container_type&&, const Allocator&); + // + // template + // priority_queue(InputIterator first, InputIterator last, const Allocator& allocator); + // + // template + // priority_queue(InputIterator first, InputIterator last, const compare_type& compare, const Allocator& allocator); + // + // template + // priority_queue(InputIterator first, InputIterator last, const compare_type& compare, const container_type& x, const Allocator& allocator); + // + // template + // priority_queue(InputIterator first, InputIterator last, const compare_type& compare, container_type&& x, const Allocator& allocator); bool empty() const; size_type size() const; @@ -181,8 +193,8 @@ namespace eastl void pop(value_type& value); // Extension to the C++11 Standard that allows popping a move-only type (e.g. unique_ptr). - void change(size_type n); /// Moves the item at the given array index to a new location based on its current priority. - void remove(size_type n); /// Removes the item at the given array index. + void change(size_type n); /// Extension to the C++ Standard. Moves the item at the given array index to a new location based on its current priority. + void remove(size_type n); /// Extension to the C++ Standard. Removes the item at the given array index. container_type& get_container(); const container_type& get_container() const; @@ -300,6 +312,11 @@ namespace eastl inline typename priority_queue::const_reference priority_queue::top() const { +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("priority_queue::top -- empty container"); +#endif + return c.front(); } @@ -357,6 +374,11 @@ namespace eastl template inline void priority_queue::pop() { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("priority_queue::pop -- empty container"); +#endif + #if EASTL_EXCEPTIONS_ENABLED try { @@ -378,6 +400,11 @@ namespace eastl template inline void priority_queue::pop(value_type& value) { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("priority_queue::pop -- empty container"); +#endif + value = eastl::move(c.front()); // To consider: value = move_if_noexcept_assignable(c.front()); pop(); } @@ -386,6 +413,11 @@ namespace eastl template inline void priority_queue::change(size_type n) // This function is not in the STL std::priority_queue. { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(n >= c.size())) + EASTL_FAIL_MSG("priority_queue::change -- out of range"); +#endif + eastl::change_heap(c.begin(), c.size(), n, comp); } @@ -393,6 +425,11 @@ namespace eastl template inline void priority_queue::remove(size_type n) // This function is not in the STL std::priority_queue. { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(n >= c.size())) + EASTL_FAIL_MSG("priority_queue::remove -- out of range"); +#endif + eastl::remove_heap(c.begin(), c.size(), n, comp); c.pop_back(); } diff --git a/include/EASTL/queue.h b/include/EASTL/queue.h index 76e33452..36c66b13 100644 --- a/include/EASTL/queue.h +++ b/include/EASTL/queue.h @@ -46,16 +46,17 @@ namespace eastl /// queue /// /// queue is an adapter class provides a FIFO (first-in, first-out) interface - /// via wrapping a sequence that provides at least the following operations: + /// via wrapping a sequence container (https://en.cppreference.com/w/cpp/named_req/SequenceContainer) + /// that additionally provides: /// push_back /// pop_front /// front /// back /// - /// In practice this usually means deque, list, intrusive_list. vector and string - /// cannot be used because they don't provide pop-front. This is reasonable because - /// a vector or string pop_front would be inefficient and could lead to - /// silently poor performance. + /// In practice this means deque, list, intrusive_list. vector and (the pseudo-container) string + /// cannot be used because they don't provide pop_front. This is reasonable because + /// a vector or string pop_front would be inefficient as such an operation would have linear complexity + /// (to move elements after removing the front element, maintaining ordering). /// template > class queue @@ -106,6 +107,12 @@ namespace eastl // // template // queue(container_type&& x, const Allocator& allocator); + // + // template + // queue(InputIt first, InputIt last); + // + // template + // queue(InputIt first, InputIt last, const Allocator& allocator); queue(std::initializer_list ilist); // C++11 doesn't specify that std::queue has initializer list support. @@ -204,6 +211,11 @@ namespace eastl inline typename queue::reference queue::front() { +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("queue::front -- empty container"); +#endif + return c.front(); } @@ -212,6 +224,11 @@ namespace eastl inline typename queue::const_reference queue::front() const { +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("queue::front -- empty container"); +#endif + return c.front(); } @@ -220,6 +237,11 @@ namespace eastl inline typename queue::reference queue::back() { +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("queue::back -- empty container"); +#endif + return c.back(); } @@ -228,6 +250,11 @@ namespace eastl inline typename queue::const_reference queue::back() const { +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("queue::back -- empty container"); +#endif + return c.back(); } @@ -264,6 +291,11 @@ namespace eastl template inline void queue::pop() { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("queue::pop -- empty container"); +#endif + c.pop_front(); } diff --git a/include/EASTL/random.h b/include/EASTL/random.h index ca3e20b0..9180cd7d 100644 --- a/include/EASTL/random.h +++ b/include/EASTL/random.h @@ -64,8 +64,8 @@ namespace eastl result_type a() const; result_type b() const; - bool operator==(const param_type& x) { return (x.mA == mA) && (x.mB == mB); } - bool operator!=(const param_type& x) { return (x.mA != mA) || (x.mB != mB); } + bool operator==(const param_type& x) const { return (x.mA == mA) && (x.mB == mB); } + bool operator!=(const param_type& x) const { return (x.mA != mA) || (x.mB != mB); } protected: IntType mA; diff --git a/include/EASTL/segmented_vector.h b/include/EASTL/segmented_vector.h index d46a9428..ff320c6e 100644 --- a/include/EASTL/segmented_vector.h +++ b/include/EASTL/segmented_vector.h @@ -11,8 +11,13 @@ #include +#include +#include +#include + namespace eastl { + // TODO: this really shouldn't be a public class, deprecate it and hide it. template class segment { @@ -31,16 +36,47 @@ namespace eastl const_iterator end() const; iterator end(); + segment() = default; + // TODO: should we implement copy/move ctor/assign? + segment(const segment& other) = delete; + segment& operator=(const segment&) = delete; + // move ctor/assignment implicitly deleted. + + // The owning segment_vector is in charge of destroying the + // data before the destructor of the segment runs, so we leave + // the destructor as trivial. This class can't be copied and + // it's public API doesn't really let you do anything, only + // segmented_vector can really use these. + // + //~segment(); + private: + // Destroys all the elements in the segment. + void DestroyData(); + static const uintptr_t kIsLastSegment = 1 << 0; uintptr_t mPrev; union { + // When the segment is not the last one in the segmented + // vector, mNext is the link to the next segment. this_type* mNext; + // When the segment is the last one in the segmented + // vector, mSize is the number elements in this current + // segment. Note: if a segment is not the last one, it + // must be completely full i.e. it must contain exactly + // Count elements so there's no need to store anything in + // mSize. size_type mSize; }; - T mData[Count]; + + // Storage for the segment. Type-erased because we want to + // support types which are not default constructible. + // TODO: Should we be using std::byte here instead of + // unsigned char? + alignas(T) unsigned char mData[Count * sizeof(T)]; + template friend class segmented_vector; template friend struct segmented_vector_iterator; }; @@ -53,6 +89,8 @@ namespace eastl typedef segmented_vector_iterator this_type; typedef segment segment_type; + // does not conform to any Iterator concept. could be a bidirectional iterator, but not random access iterator because segment is a double-linked list. + T* operator->() const; T& operator*() const; @@ -76,57 +114,167 @@ namespace eastl typedef Allocator allocator_type; typedef segmented_vector_iterator const_iterator; typedef segmented_vector_iterator iterator; + typedef T value_type; - segmented_vector(const Allocator& allocator = Allocator()); - ~segmented_vector(); + segmented_vector(const Allocator& allocator = Allocator()); + segmented_vector(const segmented_vector& other); + segmented_vector(segmented_vector&& other); + segmented_vector& operator=(const segmented_vector& other); + segmented_vector& operator=(segmented_vector&& other); + ~segmented_vector(); - allocator_type& get_allocator(); + // constructors that initialize from a range, etc. missing - const segment_type* first_segment() const; - segment_type* first_segment(); - const_iterator begin() const; - iterator begin(); + segmented_vector(std::initializer_list ilist, const Allocator& allocator = Allocator()); - const_iterator end() const; - iterator end(); + allocator_type& get_allocator() noexcept; - size_type size() const; - size_type segment_count() const; - T& front(); - T& back(); + // TODO: deprecate these? what's the point of having them in + // the API? We don't want people messing around with the + // segments directly. + const segment_type* first_segment() const noexcept; + segment_type* first_segment() noexcept; - bool empty() const; - void clear(); + const_iterator begin() const noexcept; + iterator begin() noexcept; - T& push_back(); - T& push_back(const T& value); - void* push_back_uninitialized(); + const_iterator end() const noexcept; + iterator end() noexcept; - void pop_back(); + // The number of elements in the container. + size_type size() const noexcept; - void erase_unsorted(segment_type& segment, typename segment_type::iterator it); - iterator erase_unsorted(const iterator& i); + // The total capacity of the container. + size_type capacity() const noexcept; - void swap(this_type& other); + // TODO: Deprecate this? + // The number of "active" segments (does not include segments + // in the free list). + size_type segment_count() const noexcept; - protected: - segment_type* DoAllocSegment(segment_type* prevSegment); - void* DoPushBack(); + // These are UB if the container is empty. + T& front() noexcept; + T& back() noexcept; + + // Return true if the container has no elements and false + // otherwise. + bool empty() const noexcept; + + // Destroys the elements in the container, but does not + // relinquish any memory (i.e. capacity() is unchanged) + void clear(); + + // Increase the capacity so it fits at least `n` elements. + // This is less useful than in normal vectors since this will + // allocate multiple segments, resulting in the same number of + // allocations as if you'd pushed the elements one by one + // without reserving. + void reserve(size_type n); + + // Resizes the container to contain exactly `n` elements. + // - If `size() > n` it destroys the last `size() - n` elements. + // - If `size() < n` it inserts `n - size()` copies of `v`. + void resize(size_type n, const value_type& v); + + // Equivalent to resize(n, value_type()); + void resize(size_type n); - allocator_type mAllocator; - segment_type* mFirstSegment; - segment_type* mLastSegment; - size_type mSegmentCount; + // Frees all the segments not currently in use, i.e makes it + // so capacity() is the lowest multiple of Count greater than + // or equal to size(). + void shrink_to_fit() noexcept; + + // missing (could be implemented): + // assign() + // set_allocator() + // set_capacity() + // validate() + // validate_iterator() + + // segmented_vector is almost a deque, but cannot provide: + // operator[] + // at() + // insert() + // push_front() + // emplace() + // emplace_front() + // erase() + // because it is not a random access container and can only push/pop elements from the back. + + T& push_back(); + T& push_back(const T& value); + T& push_back(T&& value); + void* push_back_uninitialized(); + + template + T& emplace_back(Args&&... args); + + 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: + // This does not initialize the entry, it just makes returns a + // pointer to an address where one could initialize one. + void* DoPushBack(); + + // Moves the segment into the free list + void AddToFreeList(segment_type* segment); + + // Get a segment from the free list, or allocate a segment if + // there are none. Sets up the segment's "last segment" flag. + segment_type* GetUnusedSegmentForLastSegment(segment_type* prevSegment); + + // Allocate a new segment. + segment_type* AllocateNewSegment(); + + // Destroys the elements in the container. Optionally also + // frees all the memory. + template + void Clear(); + + // If the last segment is empty, this function will move it to + // the free list and update mLastSegment with the previous + // segment. + void UpdateLastSegment(); + + // Remove the last `n` elements in the container. + void EraseFromBack(size_type n); + + // Pushes `n` copies of `v` + void PushBack(size_type n, const value_type& v); + + template + void InsertRange(ForwardIt begin, ForwardIt end); + + allocator_type mAllocator; + segment_type* mFirstSegment{}; + segment_type* mLastSegment{}; + segment_type* mFreeList{}; + size_type mInUseSegmentCount{}; + + // TODO: consider storing this in the first free segment (if + // there is one), it'd save the memory for this member but + // would make things like `capacity()` less cache coherent and + // branchy (we need to check if there's a free segment) + size_type mFreeListSegmentCount{}; }; + ///////////////////////////////////// + // segment + ///////////////////////////////////// + template inline const segment* segment::next_segment() const { if (mPrev & kIsLastSegment) - return 0; + return nullptr; else return mNext; } @@ -136,7 +284,7 @@ namespace eastl segment::next_segment() { if (mPrev & kIsLastSegment) - return 0; + return nullptr; else return mNext; } @@ -145,14 +293,14 @@ namespace eastl inline typename segment::const_iterator segment::begin() const { - return mData; + return reinterpret_cast(&mData); } template inline typename segment::iterator segment::begin() { - return mData; + return reinterpret_cast(&mData); } template @@ -160,9 +308,9 @@ namespace eastl segment::end() const { if (mPrev & kIsLastSegment) - return mData + mSize; + return begin() + mSize; else - return mData + Count; + return begin() + Count; } template @@ -170,11 +318,32 @@ namespace eastl segment::end() { if (mPrev & kIsLastSegment) - return mData + mSize; + return begin() + mSize; else - return mData + Count; + return begin() + Count; + } + + template + inline void segment::DestroyData() + { + // TODO: Our current call sites know the value of + // (mPrev & kIsLastSegment), consider having 2 implementations + // of this which don't branch or something like that. + T* ptr = begin(); + const size_type count = (mPrev & kIsLastSegment) ? mSize : Count; + for (size_type i = 0; i < count; ++i) + { + eastl::destroy_at(ptr); + ptr++; + } + mSize = 0; } + + ///////////////////////////////////// + // segmented_vector_iterator + ///////////////////////////////////// + template T* segmented_vector_iterator::operator->() const @@ -203,7 +372,7 @@ namespace eastl mEnd = mSegment->end(); } else - mCurrent = 0; + mCurrent = nullptr; } return *this; } @@ -218,45 +387,117 @@ namespace eastl } + ///////////////////////////////////// + // segmented_vector + ///////////////////////////////////// + template inline segmented_vector::segmented_vector(const Allocator& allocator) : mAllocator(allocator) - , mFirstSegment(0) - , mLastSegment(0) - , mSegmentCount(0) { } + template + inline segmented_vector::segmented_vector(const segmented_vector& other) + : mAllocator(other.mAllocator) + { + InsertRange(other.begin(), other.end()); + } + + template + inline segmented_vector::segmented_vector(segmented_vector&& other) + : mAllocator(other.mAllocator) + { + swap(other); + } + + template + inline segmented_vector::segmented_vector(std::initializer_list ilist, const Allocator& allocator) + : mAllocator(allocator) + { + InsertRange(ilist.begin(), ilist.end()); + } + + template + inline segmented_vector& segmented_vector::operator=( + const segmented_vector& other) + { + if (EA_UNLIKELY(this == &other)) + { + return *this; + } + // EASTL behaves as if propagate_on_container_copy_assignment + // is globally false, so we don't propagate the allocator + // here. + if (size() > other.size()) + { + EraseFromBack(size() - other.size()); + } + + // At this point size() <= other.size() + // copy-assign elements which are already initialized. + auto fromIt = other.begin(); + // TODO: consider doing segment copies, which do memcpy on + // trivially copyable types. + for (auto toIt = begin(); toIt != end(); ++toIt, ++fromIt) + { + *toIt = *fromIt; + } + + // Now we insert (copy construct) all the missing elements + // in-place + InsertRange(fromIt, other.end()); + + return *this; + } + + template + inline segmented_vector& segmented_vector::operator=( + segmented_vector&& other) + { + if (EA_UNLIKELY(this == &other)) + { + return *this; + } + + // EASTL behaves as if propagate_on_container_move_assignment + // and propagate_on_container_swap are both globally true, so + // we just swap and clear the one we're returning. + swap(other); + other.Clear(); + return *this; + } + template inline segmented_vector::~segmented_vector() { - clear(); + Clear(); } template inline typename segmented_vector::allocator_type& - segmented_vector::get_allocator() + segmented_vector::get_allocator() noexcept { return mAllocator; } template inline const typename segmented_vector::segment_type* - segmented_vector::first_segment() const + segmented_vector::first_segment() const noexcept { return mFirstSegment; } template inline typename segmented_vector::segment_type* - segmented_vector::first_segment() + segmented_vector::first_segment() noexcept { return mFirstSegment; } template inline typename segmented_vector::const_iterator - segmented_vector::begin() const + segmented_vector::begin() const noexcept { iterator i; i.mSegment = mFirstSegment; @@ -266,13 +507,15 @@ namespace eastl i.mEnd = mFirstSegment->end(); } else - i.mCurrent = 0; + { + i.mCurrent = nullptr; + } return (const_iterator&)i; } template inline typename segmented_vector::iterator - segmented_vector::begin() + segmented_vector::begin() noexcept { iterator i; i.mSegment = mFirstSegment; @@ -282,100 +525,154 @@ namespace eastl i.mEnd = mFirstSegment->end(); } else - i.mCurrent = 0; + i.mCurrent = nullptr; return i; } template inline typename segmented_vector::const_iterator - segmented_vector::end() const + segmented_vector::end() const noexcept { iterator i; - i.mCurrent = 0; + i.mCurrent = nullptr; return (const_iterator&)i; } template inline typename segmented_vector::iterator - segmented_vector::end() + segmented_vector::end() noexcept { iterator i; - i.mCurrent = 0; + i.mCurrent = nullptr; return i; } template inline typename segmented_vector::size_type - segmented_vector::size() const + segmented_vector::size() const noexcept { if (segment_type* segment = mLastSegment) - return (mSegmentCount-1)*Count + segment->mSize; + return (mInUseSegmentCount-1)*Count + segment->mSize; return 0; } template inline typename segmented_vector::size_type - segmented_vector::segment_count() const + segmented_vector::capacity() const noexcept + { + return (mInUseSegmentCount + mFreeListSegmentCount) * Count; + } + + template + inline typename segmented_vector::size_type + segmented_vector::segment_count() const noexcept { - return mSegmentCount; + return mInUseSegmentCount; } template inline T& - segmented_vector::front() + segmented_vector::front() noexcept { - return mFirstSegment->mData[0]; +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(mFirstSegment == nullptr)) // We don't allow the user to reference an empty container. + EASTL_FAIL_MSG("segmented_vector::front -- empty container"); +#endif + + return mFirstSegment->begin()[0]; } template inline T& - segmented_vector::back() + segmented_vector::back() noexcept { +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(mLastSegment == nullptr)) // We don't allow the user to reference an empty container. + EASTL_FAIL_MSG("segmented_vector::back -- empty container"); +#endif + segment_type* lastSegment = mLastSegment; - return lastSegment->mData[lastSegment->mSize-1]; + return lastSegment->begin()[lastSegment->mSize-1]; } template inline bool - segmented_vector::empty() const + segmented_vector::empty() const noexcept { - return mFirstSegment == 0; + return mFirstSegment == nullptr; } template inline void segmented_vector::clear() { - if (segment_type* segment = mFirstSegment) + Clear(); + } + + template + inline void segmented_vector::reserve(size_type n) + { + while (capacity() < n) { - 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; + segment_type* segment = AllocateNewSegment(); + AddToFreeList(segment); + } + } + + template + inline void segmented_vector::resize(size_type n, const value_type& v) + { + if (size() < n) + { + PushBack(n - size(), v); + } + else + { + EraseFromBack(size() - n); + } + } + + template + inline void + segmented_vector::resize(size_type n) + { + resize(n, value_type()); + } + + template + inline void segmented_vector::shrink_to_fit() noexcept + { + static_assert(eastl::is_trivially_destructible_v, + "segment_type doesn't call it's destructor here!"); + while (mFreeList) + { + segment_type* toFree = mFreeList; + mFreeList = reinterpret_cast(toFree->mPrev); + EASTLFree(mAllocator, toFree, sizeof(segment_type)); } + + mFreeListSegmentCount = 0; } template inline T& segmented_vector::push_back() { - return *(new (DoPushBack()) T()); + return *(::new (DoPushBack()) T()); } template inline T& segmented_vector::push_back(const T& value) { - return *(new (DoPushBack()) T(value)); + return *(::new (DoPushBack()) T(value)); + } + + template + inline T& + segmented_vector::push_back(T&& value) + { + return *(::new (DoPushBack()) T(eastl::move(value))); } template @@ -385,6 +682,13 @@ namespace eastl return DoPushBack(); } + template + template + T& segmented_vector::emplace_back(Args&&... args) + { + return *(::new (DoPushBack()) T(eastl::forward(args)... )); + } + template inline void segmented_vector::pop_back() @@ -395,21 +699,10 @@ namespace eastl EASTL_FAIL_MSG("segmented_vector::pop_back -- segmented vector is empty"); #endif --lastSegment->mSize; - (lastSegment->mData + lastSegment->mSize)->T::~T(); + T* const toDestroy = lastSegment->begin() + lastSegment->mSize; + eastl::destroy_at(toDestroy); - 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; - } + UpdateLastSegment(); } template @@ -429,7 +722,7 @@ namespace eastl iterator ret(i); *i = back(); if (i.mSegment == mLastSegment && mLastSegment->mSize == 1) - ret.mCurrent = 0; + ret.mCurrent = nullptr; pop_back(); return ret; } @@ -438,58 +731,243 @@ namespace eastl 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; + using eastl::swap; + // + // EASTL doesn't have allocator_traits it has the effective + // behavior of propagate_on_container_swap = true for all + // allocators. + swap(mAllocator, other.mAllocator); + swap(mFirstSegment, other.mFirstSegment); + swap(mLastSegment, other.mLastSegment); + swap(mFreeList, other.mFreeList); + swap(mInUseSegmentCount, other.mInUseSegmentCount); + swap(mFreeListSegmentCount, other.mFreeListSegmentCount); } template inline void* segmented_vector::DoPushBack() { + // This does not initialize the entry, it just makes room for it. if (segment_type* segment = mLastSegment) { size_type size = segment->mSize; if (size < Count) { ++segment->mSize; - return segment->mData + size; + return segment->begin() + size; } else { segment_type* lastSegment = mLastSegment; - segment_type* newSegment = mLastSegment = DoAllocSegment(mLastSegment); + segment_type* newSegment = mLastSegment = GetUnusedSegmentForLastSegment(mLastSegment); lastSegment->mPrev &= ~segment_type::kIsLastSegment; lastSegment->mNext = newSegment; - return newSegment->mData; + newSegment->mSize = 1; + return newSegment->begin(); } } else { - segment = mFirstSegment = mLastSegment = DoAllocSegment(0); - return segment->mData; + segment = mFirstSegment = mLastSegment = GetUnusedSegmentForLastSegment(nullptr); + segment->mSize = 1; + return segment->begin(); + } + } + + template + inline void + segmented_vector::AddToFreeList(segment_type* segment) + { + segment->mPrev = reinterpret_cast(mFreeList); + mFreeList = segment; + mFreeListSegmentCount++; + } + + template + inline typename segmented_vector::segment_type* + segmented_vector::GetUnusedSegmentForLastSegment(segment_type* prevSegment) + { + segment_type* const newSegment = [&] + { + if (mFreeList) + { + mFreeListSegmentCount--; + segment_type* const freeSegment = mFreeList; + mFreeList = reinterpret_cast(freeSegment->mPrev); + return freeSegment; + } + + return AllocateNewSegment(); + }(); + + mInUseSegmentCount++; + newSegment->mPrev = uintptr_t(prevSegment) | segment_type::kIsLastSegment; + newSegment->mSize = 0; + return newSegment; + } + + + template + inline typename segmented_vector::segment_type* + segmented_vector::AllocateNewSegment() + { + static_assert(eastl::is_trivially_constructible_v, "We're not initializing segment_type here"); + return (segment_type*)allocate_memory(mAllocator, sizeof(segment_type), EASTL_ALIGN_OF(segment_type), 0); + } + + template + template + inline void segmented_vector::Clear() + { + if (bFreeMemory) + { + // Delete what was already in the free list before this + // call. + shrink_to_fit(); + } + + segment_type* segment = mFirstSegment; + if (segment == nullptr) + { + return; + } + + const auto& wrapUp = [this](segment_type* s) + { + s->DestroyData(); + if (!bFreeMemory) + { + AddToFreeList(s); + } + else + { + EASTLFree(mAllocator, s, sizeof(segment_type)); + } + }; + + // Note: the last segment is special because its active member + // is mSize not mNext, so we need to deal with it separately. + while (segment != mLastSegment) + { + segment_type* nextSegment = segment->mNext; + wrapUp(segment); + segment = nextSegment; + } + + wrapUp(segment); + + mFirstSegment = nullptr; + mLastSegment = nullptr; + mInUseSegmentCount = 0; + } + + template + inline void segmented_vector::UpdateLastSegment() + { + // Assumes there is a last segment. + segment_type* lastSegment = mLastSegment; + if (lastSegment->mSize == 0) + { + --mInUseSegmentCount; + mLastSegment = (segment_type*)(lastSegment->mPrev & (~segment_type::kIsLastSegment)); + AddToFreeList(lastSegment); + if (mLastSegment) + { + mLastSegment->mPrev |= segment_type::kIsLastSegment; + mLastSegment->mSize = Count; + } + else + { + mFirstSegment = nullptr; + } + } + } + + template + inline void segmented_vector::EraseFromBack(size_type toRemoveCount) + { + // This is only marginally better than doing consecutive pop_back() calls, is it worth it? + + // No bounds checking for this, we assume we have at least `toRemoveCount` elements. + + // Drop whole segments while we can. + while (toRemoveCount >= mLastSegment->mSize) + { + const size_type removed = mLastSegment->mSize; + mLastSegment->DestroyData(); + UpdateLastSegment(); + toRemoveCount -= removed; + } + + // There's a chance we completely emptied the container here. + if (mLastSegment == nullptr) + { + return; + } + + // at this point we know there's more entries in the last + // segment that there are elements left to remove. + for (size_type i = 1; i <= toRemoveCount; ++i) + { + const size_type toRemoveIndex = mLastSegment->mSize - i; + T* const toRemove = mLastSegment->begin() + toRemoveIndex; + eastl::destroy_at(toRemove); + } + mLastSegment->mSize -= toRemoveCount; + } + + template + inline void segmented_vector::PushBack(size_type toAddCount, const value_type& v) + { + // This is only marginally better than doing consecutive push_back(v) calls, is it worth it? + + if (!mLastSegment && (toAddCount > 0)) + { + mFirstSegment = mLastSegment = GetUnusedSegmentForLastSegment(nullptr); + } + + const auto& fillLastSegment = [&]() + { + const size_type spaceInSegment = Count - mLastSegment->mSize; + const size_type addedThisLoop = eastl::min(toAddCount, spaceInSegment); + for (size_type i = 0; i < addedThisLoop; ++i) + { + T* slot = mLastSegment->begin() + mLastSegment->mSize + i; + new (slot) T(v); + } + mLastSegment->mSize += addedThisLoop; + toAddCount -= addedThisLoop; + }; + + // fill the current last segment. + fillLastSegment(); + + // if there's still stuff to add, we need to add new segments as we go. + while (toAddCount > 0) + { + mLastSegment = GetUnusedSegmentForLastSegment(mLastSegment); + fillLastSegment(); + } + } + + template + template + inline void segmented_vector::InsertRange(ForwardIt begin, ForwardIt end) + { + // TODO: this can be greatly improved, e.g. memcpy entire + // segments when the element types are trivially_copyable and + // things like that, for now just do the trivial thing. + for (auto it = begin; it != end; ++it) + { + if (bDoMove) + { + push_back(eastl::move(*it)); + } + else + { + push_back(*it); + } } } @@ -518,6 +996,60 @@ namespace eastl { return a.mCurrent != b.mCurrent; } + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const segmented_vector& a, const segmented_vector& b) + { + return (a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin()); + } + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template + inline synth_three_way_result operator<=>(const segmented_vector& a, const segmented_vector& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } +#endif + + template + inline bool operator!=(const segmented_vector& a, const segmented_vector& b) + { + return !(a == b); + } + + template + inline bool operator<(const segmented_vector& a, const segmented_vector& b) + { + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + template + inline bool operator>(const segmented_vector& a, const segmented_vector& b) + { + return b < a; + } + + template + inline bool operator<=(const segmented_vector& a, const segmented_vector& b) + { + return !(b < a); + } + + template + inline bool operator>=(const segmented_vector& a, const segmented_vector& b) + { + return !(a < b); + } + + template + inline void swap(segmented_vector& a, segmented_vector& b) + { + a.swap(b); + } } #endif diff --git a/include/EASTL/set.h b/include/EASTL/set.h index 127ecfb9..3f3bbdbf 100644 --- a/include/EASTL/set.h +++ b/include/EASTL/set.h @@ -108,15 +108,27 @@ namespace eastl public: set(const allocator_type& allocator = EASTL_SET_DEFAULT_ALLOCATOR); + // some differences with the standard in what is marked as explicit in these constructors: set(const Compare& compare, const allocator_type& allocator = EASTL_SET_DEFAULT_ALLOCATOR); set(const this_type& x); set(this_type&& x); set(this_type&& x, const allocator_type& allocator); set(std::initializer_list ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_SET_DEFAULT_ALLOCATOR); + set(std::initializer_list ilist, const allocator_type& allocator); template set(Iterator itBegin, Iterator itEnd); // allocator arg removed because VC7.1 fails on the default arg. To do: Make a second version of this function without a default arg. + // missing constructors, to implement: + // + // set(const this_type& x, const allocator_type& allocator); + // + // template + // set(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& alloc = Allocator()); + // + // template + // set(InputIterator first, InputIterator last, const Allocator& alloc); + // The (this_type&& x) ctor above has the side effect of forcing us to make operator= visible in this subclass. this_type& operator=(const this_type& x) { return (this_type&)base_type::operator=(x); } this_type& operator=(std::initializer_list ilist) { return (this_type&)base_type::operator=(ilist); } @@ -134,9 +146,19 @@ namespace eastl size_type count(const Key& k) const; + // missing transparent key support: + // template + // size_type count(const K& k) const; + eastl::pair equal_range(const Key& k); eastl::pair equal_range(const Key& k) const; + // missing transparent key support: + // template + // eastl::pair equal_range(const K& k); + // template + // eastl::pair equal_range(const K& k) const; + }; // set @@ -196,10 +218,21 @@ namespace eastl multiset(this_type&& x); multiset(this_type&& x, const allocator_type& allocator); multiset(std::initializer_list ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_MULTISET_DEFAULT_ALLOCATOR); + multiset(std::initializer_list ilist, const allocator_type& allocator); template multiset(Iterator itBegin, Iterator itEnd); // allocator arg removed because VC7.1 fails on the default arg. To do: Make a second version of this function without a default arg. + // missing constructors, to implement: + // + // multiset(const this_type& x, const allocator_type& allocator); + // + // template + // multiset(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& alloc = Allocator()); + // + // template + // multiset(InputIterator first, InputIterator last, const Allocator& alloc); + // The (this_type&& x) ctor above has the side effect of forcing us to make operator= visible in this subclass. this_type& operator=(const this_type& x) { return (this_type&)base_type::operator=(x); } this_type& operator=(std::initializer_list ilist) { return (this_type&)base_type::operator=(ilist); } @@ -217,15 +250,31 @@ namespace eastl size_type count(const Key& k) const; + // missing transparent key support: + // template + // size_type count(const K& k) const; + eastl::pair equal_range(const Key& k); eastl::pair equal_range(const Key& k) const; + // missing transparent key support: + // template + // eastl::pair equal_range(const K& k); + // template + // eastl::pair equal_range(const K& k) const; + /// equal_range_small /// This is a special version of equal_range which is optimized for the /// case of there being few or no duplicated keys in the tree. eastl::pair equal_range_small(const Key& k); eastl::pair equal_range_small(const Key& k) const; + // missing transparent key support: + // template + // eastl::pair equal_range_small(const K& k); + // template + // eastl::pair equal_range_small(const K& k) const; + }; // multiset @@ -277,6 +326,13 @@ namespace eastl } + template + inline set::set(std::initializer_list ilist, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), Compare(), allocator) + { + } + + template template inline set::set(Iterator itBegin, Iterator itEnd) @@ -472,6 +528,13 @@ namespace eastl } + template + inline multiset::multiset(std::initializer_list ilist, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), Compare(), allocator) + { + } + + template template inline multiset::multiset(Iterator itBegin, Iterator itEnd) diff --git a/include/EASTL/shared_ptr.h b/include/EASTL/shared_ptr.h index 3ac9bc77..9e37a091 100644 --- a/include/EASTL/shared_ptr.h +++ b/include/EASTL/shared_ptr.h @@ -61,8 +61,10 @@ EA_DISABLE_ALL_VC_WARNINGS() #include EA_RESTORE_ALL_VC_WARNINGS() -EA_DISABLE_VC_WARNING(4530); // C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc -EA_DISABLE_VC_WARNING(4571); // catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. +// 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc +// 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. +// 4512/4626 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. +EA_DISABLE_VC_WARNING(4530 4571 4512 4626); #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. @@ -1675,13 +1677,13 @@ namespace eastl { EASTL_REMOVE_AT_2024_APRIL typedef bool result_type; - bool operator()(shared_ptr const& a, shared_ptr const& b) const + bool operator()(shared_ptr const& a, shared_ptr const& b) const EA_NOEXCEPT { return a.owner_before(b); } - bool operator()(shared_ptr const& a, weak_ptr const& b) const + bool operator()(shared_ptr const& a, weak_ptr const& b) const EA_NOEXCEPT { return a.owner_before(b); } - bool operator()(weak_ptr const& a, shared_ptr const& b) const + bool operator()(weak_ptr const& a, shared_ptr const& b) const EA_NOEXCEPT { return a.owner_before(b); } }; @@ -1690,21 +1692,50 @@ namespace eastl { EASTL_REMOVE_AT_2024_APRIL typedef bool result_type; - bool operator()(weak_ptr const& a, weak_ptr const& b) const + bool operator()(weak_ptr const& a, weak_ptr const& b) const EA_NOEXCEPT { return a.owner_before(b); } - bool operator()(weak_ptr const& a, shared_ptr const& b) const + bool operator()(weak_ptr const& a, shared_ptr const& b) const EA_NOEXCEPT { return a.owner_before(b); } - bool operator()(shared_ptr const& a, weak_ptr const& b) const + bool operator()(shared_ptr const& a, weak_ptr const& b) const EA_NOEXCEPT { return a.owner_before(b); } }; + template <> + struct owner_less + { + typedef int is_transparent; + + template + bool operator()(shared_ptr const& a, shared_ptr const& b) const EA_NOEXCEPT + { + return a.owner_before(b); + } + + template + bool operator()(shared_ptr const& a, weak_ptr const& b) const EA_NOEXCEPT + { + return a.owner_before(b); + } + + template + bool operator()(weak_ptr const& a, shared_ptr const& b) const EA_NOEXCEPT + { + return a.owner_before(b); + } + + template + bool operator()(weak_ptr const& a, weak_ptr const& b) const EA_NOEXCEPT + { + return a.owner_before(b); + } + }; + } // namespace eastl -EA_RESTORE_VC_WARNING(); EA_RESTORE_VC_WARNING(); diff --git a/include/EASTL/slist.h b/include/EASTL/slist.h index 3fab11e5..abd61b21 100644 --- a/include/EASTL/slist.h +++ b/include/EASTL/slist.h @@ -116,7 +116,13 @@ namespace eastl public: SListIterator(); SListIterator(const SListNodeBase* pNode); - SListIterator(const iterator& x); + + template , bool> = true> + inline SListIterator(const iterator& x) + : mpNode(x.mpNode) + { + // Empty + } reference operator*() const; pointer operator->() const; @@ -275,7 +281,7 @@ namespace eastl const_reference front() const; template - void emplace_front(Args&&... args); + reference emplace_front(Args&&... args); void push_front(const value_type& value); reference push_front(); @@ -348,6 +354,11 @@ namespace eastl EASTL_REMOVE_AT_2024_APRIL void splice_after(const_iterator position, const_iterator before_first, const_iterator before_last); // before_first and before_last come from a source container. EASTL_REMOVE_AT_2024_APRIL void splice_after(const_iterator position, const_iterator previous); // previous comes from a source container. + size_type unique(); + + template + size_type unique(BinaryPredicate); + // Sorting functionality // This is independent of the global sort algorithms, as lists are // linked nodes and can be sorted more efficiently by moving nodes @@ -509,14 +520,6 @@ namespace eastl } - template - inline SListIterator::SListIterator(const iterator& x) - : mpNode(const_cast(x.mpNode)) - { - // Empty - } - - template inline typename SListIterator::reference SListIterator::operator*() const @@ -889,9 +892,10 @@ namespace eastl template template - void slist::emplace_front(Args&&... args) + typename slist::reference slist::emplace_front(Args&&... args) { DoInsertValueAfter(&internalNode(), eastl::forward(args)...); + return static_cast(internalNode().mpNext)->mValue; // Same as return front(); } @@ -1505,6 +1509,67 @@ namespace eastl } + template + typename slist::size_type slist::unique() + { + size_type numRemoved = 0; + iterator first(begin()); + const iterator last(end()); + + if (first != last) + { + iterator next(first); + + while (++next != last) + { + if (*first == *next) + { + DoEraseAfter(first.mpNode); + ++numRemoved; + next = first; + } + else + { + first = next; + } + } + } + + return numRemoved; + } + + + template + template + typename slist::size_type slist::unique(BinaryPredicate predicate) + { + size_type numRemoved = 0; + iterator first(begin()); + const iterator last(end()); + + if (first != last) + { + iterator next(first); + + while (++next != last) + { + if (predicate(*first, *next)) + { + DoEraseAfter(first.mpNode); + ++numRemoved; + next = first; + } + else + { + first = next; + } + } + } + + return numRemoved; + } + + template inline void slist::sort() { diff --git a/include/EASTL/sort.h b/include/EASTL/sort.h index 1ed646cc..609d8cea 100644 --- a/include/EASTL/sort.h +++ b/include/EASTL/sort.h @@ -294,25 +294,41 @@ namespace eastl while(nSpace < nSize) nSpace = (nSpace * 3) + 1; // This is the Knuth 'h' sequence: 1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 797161, 2391484, 7174453, 21523360, 64570081, 193710244, + // nSpace will iterate from the largest Knuth 'h' element smaller than the size of the range down to 1 (inclusive). for(nSpace = (nSpace - 1) / 3; nSpace >= 1; nSpace = (nSpace - 1) / 3) // Integer division is less than ideal. { for(difference_type i = 0; i < nSpace; i++) { - const RandomAccessIterator iInsertFirst = first + i; + const RandomAccessIterator iInsertFirst = first + i; // range: [first, first + nSpace) + + // After completion of this next loop the elements + // iInsertFirst + K * nSpace + // for K = [0, 1, 2, ...) form a sorted range. + // This loop is essentially an insertion sort. // Note: we can only move the iterator forward if we know we won't overrun the - // end(), otherwise we can invoke undefined behaviour. So we need to check we + // end(), otherwise we can invoke undefined behaviour. So we need to check we // have enough space before moving the iterator. RandomAccessIterator iSorted = iInsertFirst; while(distance(iSorted, last) > nSpace) { + RandomAccessIterator iLeft = iSorted; iSorted += nSpace; + RandomAccessIterator iRight = iSorted; - RandomAccessIterator iCurrent = iSorted; - for(RandomAccessIterator iBack = iSorted - nSpace; (iCurrent != iInsertFirst) && compare(*iCurrent, *iBack); iCurrent = iBack, iBack -= nSpace) + // the elements (with distance nSpace) prior to iRight are sorted. + // move iRight into its sorted position. + while(compare(*iRight, *iLeft)) { - EASTL_VALIDATE_COMPARE(!compare(*iBack, *iCurrent)); // Validate that the compare function is sane. - eastl::iter_swap(iCurrent, iBack); + EASTL_VALIDATE_COMPARE(!compare(*iLeft, *iRight)); // Validate that the compare function is sane. + + eastl::iter_swap(iRight, iLeft); + + if (iLeft == iInsertFirst) // don't iterate iLeft past the valid range. + break; + + iRight = iLeft; + iLeft -= nSpace; } } } diff --git a/include/EASTL/span.h b/include/EASTL/span.h index b0bbee2d..7900946f 100644 --- a/include/EASTL/span.h +++ b/include/EASTL/span.h @@ -64,6 +64,40 @@ namespace eastl template struct is_eastl_array, T> : public eastl::true_type {}; + + // SpanStorage + // + // Holds all of the member variables for span, specialized to remove the size variable when + // given a static extent. + // + template + struct SpanStorage + { + T* mpData = nullptr; + static EA_CONSTEXPR eastl_size_t mnSize = Extent; + + EA_CONSTEXPR SpanStorage() EA_NOEXCEPT + { + static_assert(Extent == 0, "impossible to default construct a span with a fixed Extent different than 0"); + } + + EA_CONSTEXPR SpanStorage(T* ptr, eastl_size_t size) + : mpData(ptr) + { + EA_UNUSED(size); + EASTL_ASSERT_MSG(Extent == size, "impossible to create a span with a fixed Extent different than the size of the supplied buffer"); + } + }; + + template + struct SpanStorage + { + T* mpData = nullptr; + eastl_size_t mnSize = 0; + + EA_CONSTEXPR SpanStorage() EA_NOEXCEPT = default; + EA_CONSTEXPR SpanStorage(T* ptr, eastl_size_t size) : mpData(ptr), mnSize(size) {} + }; } template @@ -86,7 +120,7 @@ namespace eastl static EA_CONSTEXPR size_t extent = Extent; // constructors / destructor - EA_CONSTEXPR span() EA_NOEXCEPT; + EA_CONSTEXPR span() EA_NOEXCEPT = default; EA_CONSTEXPR span(const span& other) EA_NOEXCEPT = default; EA_CONSTEXPR span(pointer ptr, index_type count); EA_CONSTEXPR span(pointer pBegin, pointer pEnd); @@ -160,8 +194,7 @@ namespace eastl EA_CONSTEXPR const_reverse_iterator crend() const EA_NOEXCEPT; private: - pointer mpData = nullptr; - index_type mnSize = 0; + Internal::SpanStorage mStorage; private: EA_CONSTEXPR bool bounds_check(size_t) const; // utility used in asserts @@ -214,24 +247,16 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// - template - EA_CONSTEXPR span::span() EA_NOEXCEPT - { - static_assert(Extent == dynamic_extent || Extent == 0, "impossible to default construct a span with a fixed Extent different than 0"); - } - template EA_CONSTEXPR span::span(pointer ptr, index_type size) - : mpData(ptr), mnSize(size) + : mStorage(ptr, size) { - EASTL_ASSERT_MSG(Extent == dynamic_extent || Extent == mnSize, "impossible to create a span with a fixed Extent different than the size of the supplied buffer"); } template EA_CONSTEXPR span::span(pointer pBegin, pointer pEnd) - : mpData(pBegin), mnSize(static_cast(pEnd - pBegin)) + : mStorage(pBegin, static_cast(pEnd - pBegin)) { - EASTL_ASSERT_MSG(Extent == dynamic_extent || Extent == mnSize, "impossible to create a span with a fixed Extent different than the size of the supplied buffer"); } template @@ -285,13 +310,13 @@ namespace eastl template EA_CONSTEXPR typename span::pointer span::data() const EA_NOEXCEPT { - return mpData; + return mStorage.mpData; } template EA_CONSTEXPR typename span::index_type span::size() const EA_NOEXCEPT { - return mnSize; + return mStorage.mnSize; } template @@ -311,7 +336,7 @@ namespace eastl { EASTL_ASSERT_MSG(!empty(), "undefined behavior accessing an empty span"); - return mpData[0]; + return mStorage.mpData[0]; } template @@ -319,7 +344,7 @@ namespace eastl { EASTL_ASSERT_MSG(!empty(), "undefined behavior accessing an empty span"); - return mpData[mnSize - 1]; + return mStorage.mpData[mStorage.mnSize - 1]; } template @@ -328,7 +353,7 @@ namespace eastl EASTL_ASSERT_MSG(!empty(), "undefined behavior accessing an empty span"); EASTL_ASSERT_MSG(bounds_check(idx), "undefined behavior accessing out of bounds"); - return mpData[idx]; + return mStorage.mpData[idx]; } template @@ -337,55 +362,55 @@ namespace eastl EASTL_ASSERT_MSG(!empty(), "undefined behavior accessing an empty span"); EASTL_ASSERT_MSG(bounds_check(idx), "undefined behavior accessing out of bounds"); - return mpData[idx]; + return mStorage.mpData[idx]; } template EA_CONSTEXPR typename span::iterator span::begin() const EA_NOEXCEPT { - return mpData; + return mStorage.mpData; } template EA_CONSTEXPR typename span::iterator span::end() const EA_NOEXCEPT { - return mpData + mnSize; + return mStorage.mpData + mStorage.mnSize; } template EA_CONSTEXPR typename span::const_iterator span::cbegin() const EA_NOEXCEPT { - return mpData; + return mStorage.mpData; } template EA_CONSTEXPR typename span::const_iterator span::cend() const EA_NOEXCEPT { - return mpData + mnSize; + return mStorage.mpData + mStorage.mnSize; } template EA_CONSTEXPR typename span::reverse_iterator span::rbegin() const EA_NOEXCEPT { - return reverse_iterator(mpData + mnSize); + return reverse_iterator(mStorage.mpData + mStorage.mnSize); } template EA_CONSTEXPR typename span::reverse_iterator span::rend() const EA_NOEXCEPT { - return reverse_iterator(mpData); + return reverse_iterator(mStorage.mpData); } template EA_CONSTEXPR typename span::const_reverse_iterator span::crbegin() const EA_NOEXCEPT { - return const_reverse_iterator(mpData + mnSize); + return const_reverse_iterator(mStorage.mpData + mStorage.mnSize); } template EA_CONSTEXPR typename span::const_reverse_iterator span::crend() const EA_NOEXCEPT { - return const_reverse_iterator(mpData); + return const_reverse_iterator(mStorage.mpData); } template diff --git a/include/EASTL/stack.h b/include/EASTL/stack.h index f060b605..d9264ca3 100644 --- a/include/EASTL/stack.h +++ b/include/EASTL/stack.h @@ -46,12 +46,15 @@ namespace eastl /// stack /// /// stack is an adapter class provides a LIFO (last-in, first-out) interface - /// via wrapping a sequence that provides at least the following operations: + /// via wrapping a sequence container (https://en.cppreference.com/w/cpp/named_req/SequenceContainer) + /// that additionally provides the following operations: /// push_back /// pop_back /// back /// - /// In practice this means vector, deque, string, list, intrusive_list. + /// In practice this means vector, deque, list, intrusive_list and (the pseudo-container) string. + /// + /// Note: the default underlying container is vector, rather than the standard's deque. /// template > class stack @@ -102,6 +105,12 @@ namespace eastl // // template // stack(container_type&& x, const Allocator& allocator); + // + // template + // stack(InputIt first, InputIt last); + // + // template + // stack(InputIt first, InputIt last, const Allocator& allocator); stack(std::initializer_list ilist); // The first item in the initializer list is pushed first. C++11 doesn't specify that std::stack has initializer list support. @@ -114,7 +123,7 @@ namespace eastl void push(const value_type& value); void push(value_type&& x); - template void emplace_back(Args&&... args); // backwards compatibility + template EASTL_REMOVE_AT_2024_SEPT void emplace_back(Args&&... args); // use emplace() instead. they are equivalent. template decltype(auto) emplace(Args&&... args); void pop(); @@ -193,6 +202,11 @@ namespace eastl inline typename stack::reference stack::top() { +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("stack::top -- empty container"); +#endif + return c.back(); } @@ -201,6 +215,11 @@ namespace eastl inline typename stack::const_reference stack::top() const { +#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("stack::top -- empty container"); +#endif + return c.back(); } @@ -238,6 +257,11 @@ namespace eastl template inline void stack::pop() { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(c.empty())) + EASTL_FAIL_MSG("stack::pop -- empty container"); +#endif + c.pop_back(); } diff --git a/include/EASTL/string.h b/include/EASTL/string.h index d6e39216..5f8d7d09 100644 --- a/include/EASTL/string.h +++ b/include/EASTL/string.h @@ -781,17 +781,6 @@ namespace eastl template void DoAssignConvert(const StringType& x, false_type); #endif - - // Replacements for STL template functions. - static const value_type* CharTypeStringFindEnd(const value_type* pBegin, const value_type* pEnd, value_type c); - static const value_type* CharTypeStringRFind(const value_type* pRBegin, const value_type* pREnd, const value_type c); - static const value_type* CharTypeStringSearch(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End); - static const value_type* CharTypeStringRSearch(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End); - static const value_type* CharTypeStringFindFirstOf(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End); - static const value_type* CharTypeStringRFindFirstOf(const value_type* p1RBegin, const value_type* p1REnd, const value_type* p2Begin, const value_type* p2End); - static const value_type* CharTypeStringFindFirstNotOf(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End); - static const value_type* CharTypeStringRFindFirstNotOf(const value_type* p1RBegin, const value_type* p1REnd, const value_type* p2Begin, const value_type* p2End); - }; // basic_string @@ -1535,7 +1524,7 @@ namespace eastl basic_string::front() { #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED - if (EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. + if (EASTL_UNLIKELY(internalLayout().GetSize() == 0)) // We assert if the user references the trailing 0 char. EASTL_FAIL_MSG("basic_string::front -- empty string"); #else // We allow the user to reference the trailing 0 char without asserting. @@ -1550,7 +1539,7 @@ namespace eastl basic_string::front() const { #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED - if (EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. + if (EASTL_UNLIKELY(internalLayout().GetSize() == 0)) // We assert if the user references the trailing 0 char. EASTL_FAIL_MSG("basic_string::front -- empty string"); #else // We allow the user to reference the trailing 0 char without asserting. @@ -1565,7 +1554,7 @@ namespace eastl basic_string::back() { #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED - if (EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. + if (EASTL_UNLIKELY(internalLayout().GetSize() == 0)) // We assert if the user references the trailing 0 char. EASTL_FAIL_MSG("basic_string::back -- empty string"); #else // We allow the user to reference the trailing 0 char without asserting. @@ -1580,7 +1569,7 @@ namespace eastl basic_string::back() const { #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED - if (EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. + if (EASTL_UNLIKELY(internalLayout().GetSize() == 0)) // We assert if the user references the trailing 0 char. EASTL_FAIL_MSG("basic_string::back -- empty string"); #else // We allow the user to reference the trailing 0 char without asserting. @@ -3391,226 +3380,6 @@ namespace eastl #endif } - - // CharTypeStringFindEnd - // Specialized char version of STL find() from back function. - // Not the same as RFind because search range is specified as forward iterators. - template - const typename basic_string::value_type* - basic_string::CharTypeStringFindEnd(const value_type* pBegin, const value_type* pEnd, value_type c) - { - const value_type* pTemp = pEnd; - while(--pTemp >= pBegin) - { - if(*pTemp == c) - return pTemp; - } - - return pEnd; - } - - - // CharTypeStringRFind - // Specialized value_type version of STL find() function in reverse. - template - const typename basic_string::value_type* - basic_string::CharTypeStringRFind(const value_type* pRBegin, const value_type* pREnd, const value_type c) - { - while(pRBegin > pREnd) - { - if(*(pRBegin - 1) == c) - return pRBegin; - --pRBegin; - } - return pREnd; - } - - - // CharTypeStringSearch - // Specialized value_type version of STL search() function. - // Purpose: find p2 within p1. Return p1End if not found or if either string is zero length. - template - const typename basic_string::value_type* - basic_string::CharTypeStringSearch(const value_type* p1Begin, const value_type* p1End, - const value_type* p2Begin, const value_type* p2End) - { - // Test for zero length strings, in which case we have a match or a failure, - // but the return value is the same either way. - if((p1Begin == p1End) || (p2Begin == p2End)) - return p1Begin; - - // Test for a pattern of length 1. - if((p2Begin + 1) == p2End) - return eastl::find(p1Begin, p1End, *p2Begin); - - // General case. - const value_type* pTemp; - const value_type* pTemp1 = (p2Begin + 1); - const value_type* pCurrent = p1Begin; - - while(p1Begin != p1End) - { - p1Begin = eastl::find(p1Begin, p1End, *p2Begin); - if(p1Begin == p1End) - return p1End; - - pTemp = pTemp1; - pCurrent = p1Begin; - if(++pCurrent == p1End) - return p1End; - - while(*pCurrent == *pTemp) - { - if(++pTemp == p2End) - return p1Begin; - if(++pCurrent == p1End) - return p1End; - } - - ++p1Begin; - } - - return p1Begin; - } - - - // CharTypeStringRSearch - // Specialized value_type version of STL find_end() function (which really is a reverse search function). - // Purpose: find last instance of p2 within p1. Return p1End if not found or if either string is zero length. - template - const typename basic_string::value_type* - basic_string::CharTypeStringRSearch(const value_type* p1Begin, const value_type* p1End, - const value_type* p2Begin, const value_type* p2End) - { - // Test for zero length strings, in which case we have a match or a failure, - // but the return value is the same either way. - if((p1Begin == p1End) || (p2Begin == p2End)) - return p1Begin; - - // Test for a pattern of length 1. - if((p2Begin + 1) == p2End) - return CharTypeStringFindEnd(p1Begin, p1End, *p2Begin); - - // Test for search string length being longer than string length. - if((p2End - p2Begin) > (p1End - p1Begin)) - return p1End; - - // General case. - const value_type* pSearchEnd = (p1End - (p2End - p2Begin) + 1); - const value_type* pCurrent1; - const value_type* pCurrent2; - - while(pSearchEnd != p1Begin) - { - // Search for the last occurrence of *p2Begin. - pCurrent1 = CharTypeStringFindEnd(p1Begin, pSearchEnd, *p2Begin); - if(pCurrent1 == pSearchEnd) // If the first char of p2 wasn't found, - return p1End; // then we immediately have failure. - - // In this case, *pTemp == *p2Begin. So compare the rest. - pCurrent2 = p2Begin; - while(*pCurrent1++ == *pCurrent2++) - { - if(pCurrent2 == p2End) - return (pCurrent1 - (p2End - p2Begin)); - } - - // A smarter algorithm might know to subtract more than just one, - // but in most cases it won't make much difference anyway. - --pSearchEnd; - } - - return p1End; - } - - - // CharTypeStringFindFirstOf - // Specialized value_type version of STL find_first_of() function. - // This function is much like the C runtime strtok function, except the strings aren't null-terminated. - template - const typename basic_string::value_type* - basic_string::CharTypeStringFindFirstOf(const value_type* p1Begin, const value_type* p1End, - const value_type* p2Begin, const value_type* p2End) - { - for( ; p1Begin != p1End; ++p1Begin) - { - for(const value_type* pTemp = p2Begin; pTemp != p2End; ++pTemp) - { - if(*p1Begin == *pTemp) - return p1Begin; - } - } - return p1End; - } - - - // CharTypeStringRFindFirstOf - // Specialized value_type version of STL find_first_of() function in reverse. - // This function is much like the C runtime strtok function, except the strings aren't null-terminated. - template - const typename basic_string::value_type* - basic_string::CharTypeStringRFindFirstOf(const value_type* p1RBegin, const value_type* p1REnd, - const value_type* p2Begin, const value_type* p2End) - { - for( ; p1RBegin != p1REnd; --p1RBegin) - { - for(const value_type* pTemp = p2Begin; pTemp != p2End; ++pTemp) - { - if(*(p1RBegin - 1) == *pTemp) - return p1RBegin; - } - } - return p1REnd; - } - - - - // CharTypeStringFindFirstNotOf - // Specialized value_type version of STL find_first_not_of() function. - template - const typename basic_string::value_type* - basic_string::CharTypeStringFindFirstNotOf(const value_type* p1Begin, const value_type* p1End, - const value_type* p2Begin, const value_type* p2End) - { - for( ; p1Begin != p1End; ++p1Begin) - { - const value_type* pTemp; - for(pTemp = p2Begin; pTemp != p2End; ++pTemp) - { - if(*p1Begin == *pTemp) - break; - } - if(pTemp == p2End) - return p1Begin; - } - return p1End; - } - - - // CharTypeStringRFindFirstNotOf - // Specialized value_type version of STL find_first_not_of() function in reverse. - template - const typename basic_string::value_type* - basic_string::CharTypeStringRFindFirstNotOf(const value_type* p1RBegin, const value_type* p1REnd, - const value_type* p2Begin, const value_type* p2End) - { - for( ; p1RBegin != p1REnd; --p1RBegin) - { - const value_type* pTemp; - for(pTemp = p2Begin; pTemp != p2End; ++pTemp) - { - if(*(p1RBegin-1) == *pTemp) - break; - } - if(pTemp == p2End) - return p1RBegin; - } - return p1REnd; - } - - - - // iterator operators template inline bool operator==(const typename basic_string::reverse_iterator& r1, @@ -3766,25 +3535,25 @@ namespace eastl template inline bool operator==(const basic_string& a, const basic_string& b) { - return ((a.size() == b.size()) && (memcmp(a.data(), b.data(), (size_t)a.size() * sizeof(typename basic_string::value_type)) == 0)); + return ((a.size() == b.size()) && (Compare(a.data(), b.data(), (size_t)a.size()) == 0)); } #if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template inline bool operator==(const typename basic_string::value_type* p, const basic_string& b) { - typedef typename basic_string::size_type size_type; - const size_type n = (size_type)CharStrlen(p); - return ((n == b.size()) && (memcmp(p, b.data(), (size_t)n * sizeof(*p)) == 0)); + typedef typename basic_string::size_type string_size_type; + const string_size_type n = (string_size_type)CharStrlen(p); + return ((n == b.size()) && (Compare(p, b.data(), (size_t)n) == 0)); } #endif template inline bool operator==(const basic_string& a, const typename basic_string::value_type* p) { - typedef typename basic_string::size_type size_type; - const size_type n = (size_type)CharStrlen(p); - return ((a.size() == n) && (memcmp(a.data(), p, (size_t)n * sizeof(*p)) == 0)); + typedef typename basic_string::size_type string_size_type; + const string_size_type n = (string_size_type)CharStrlen(p); + return ((a.size() == n) && (Compare(a.data(), p, (size_t)n) == 0)); } #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) @@ -3797,8 +3566,8 @@ namespace eastl template inline auto operator<=>(const basic_string& a, const typename basic_string::value_type* p) { - typedef typename basic_string::size_type size_type; - const size_type n = (size_type)CharStrlen(p); + typedef typename basic_string::size_type string_size_type; + const string_size_type n = (string_size_type)CharStrlen(p); return basic_string::compare(a.begin(), a.end(), p, p + n) <=> 0; } @@ -3886,8 +3655,8 @@ namespace eastl template inline bool operator<(const typename basic_string::value_type* p, const basic_string& b) { - typedef typename basic_string::size_type size_type; - const size_type n = (size_type)CharStrlen(p); + typedef typename basic_string::size_type string_size_type; + const string_size_type n = (string_size_type)CharStrlen(p); return basic_string::compare(p, p + n, b.begin(), b.end()) < 0; } @@ -3895,8 +3664,8 @@ namespace eastl template inline bool operator<(const basic_string& a, const typename basic_string::value_type* p) { - typedef typename basic_string::size_type size_type; - const size_type n = (size_type)CharStrlen(p); + typedef typename basic_string::size_type string_size_type; + const string_size_type n = (string_size_type)CharStrlen(p); return basic_string::compare(a.begin(), a.end(), p, p + n) < 0; } diff --git a/include/EASTL/string_hash_map.h b/include/EASTL/string_hash_map.h index 25bdfaf1..f04b1386 100644 --- a/include/EASTL/string_hash_map.h +++ b/include/EASTL/string_hash_map.h @@ -16,6 +16,32 @@ namespace eastl { +// Note: this class creates a copy of the key on insertion and manages it in its own internal +// buffer this has side effects like: +// +// const char* p = "X"; +// string_hash_map map; +// auto x = map.insert(p, 1); +// const char* the_key = x.first->first; +// the_key != p; <<<< This is true, since we copied the key internally. +// +// +// TODO: This publically inherits from hash_map but really shouldn't since it deliberately uses +// name hiding to change the behaviour of some of its methods in a way that's incompatible with +// what hash_map does. e.g: +// +// void foo(hash_map, str_equal_to>& map) +// { +// map["a"] = 1; +// } +// +// string_hash_map strMap; +// +// // This is very bad! foo() will use hash_map's implementation of operator[], which +// // doesn't copy the char* but strMap assumes it owns all its key pointers, this +// // will cause a crash when strMap is destructed. +// foo(strMap); +// template, typename Predicate = str_equal_to, typename Allocator = EASTLAllocatorType> class string_hash_map : public eastl::hash_map { @@ -31,9 +57,13 @@ class string_hash_map : public eastl::hash_map + insert_return_type emplace(const char* key, Args&&... valArgs); + + template + iterator emplace_hint(const_iterator position, const char* key, Args&&... valArgs); + + template + inline insert_return_type try_emplace(const char* k, Args&&... valArgs); + + template + inline iterator try_emplace(const_iterator, const char* k, Args&&... valArgs); + private: char* strduplicate(const char* str); + void free(const char* str); // Not implemented right now // insert_return_type insert(const value_type& value); @@ -73,18 +119,16 @@ string_hash_map::~string_hash_map() 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) - EASTLFree(allocator, (void*)i->first, 0); + free(i->first); 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) - EASTLFree(allocator, (void*)i->first, 0); + free(i->first); base::base_type::clear(clearBuckets); } @@ -142,7 +186,7 @@ string_hash_map::erase(const_iterator position) { const char* key = position->first; iterator result = base::base_type::erase(position); - EASTLFree(base::base_type::get_allocator(), (void*)key, 0); + free(key); return result; } @@ -173,6 +217,60 @@ string_hash_map::operator[](const char* key) return base::base_type::insert(base_value_type(pair_first_construct, strduplicate(key))).first->second; } +template +template +typename string_hash_map::insert_return_type +string_hash_map::emplace(const char* key, Args&&...valArgs) +{ + return try_emplace(key, eastl::forward(valArgs)...); +} + +template +template +typename string_hash_map::iterator +string_hash_map::emplace_hint(typename string_hash_map::const_iterator hint, const char* key, Args&&...valArgs) +{ + return try_emplace(hint, key, eastl::forward(valArgs)...); +} + +template +template +typename string_hash_map::insert_return_type +string_hash_map::try_emplace(const char* k, Args&&...valArgs) +{ + // This is lifted from hash_map::try_emplace_forwarding. The point is that we don't want to + // allocate space for a copy of `k` unless we know we're going to insert it. + using hashtable_type = typename base::base_type; + const auto key_data = hashtable_type::DoFindKeyData(k); + if (key_data.node) + { + // Node exists, no insertion needed. + return eastl::pair( + iterator(key_data.node, hashtable_type::mpBucketArray + key_data.bucket_index), false); + } + else + { + // We're adding a new node, copy the key. + const char* keyCopy = strduplicate(k); + typename base::node_type* const pNodeNew = hashtable_type::DoAllocateNode( + piecewise_construct, eastl::forward_as_tuple(keyCopy), forward_as_tuple(eastl::forward(valArgs)...)); + return hashtable_type::template DoInsertUniqueNode(keyCopy, key_data.code, key_data.bucket_index, + pNodeNew); + } +} + + +template +template +typename string_hash_map::iterator +string_hash_map::try_emplace(typename string_hash_map::const_iterator hint, const char* key, Args&&...valArgs) +{ + EA_UNUSED(hint); + // The hint is currently ignored in all our implementations :( + auto ret = try_emplace(key, eastl::forward(valArgs)...); + return base::base_type::DoGetResultIterator(true_type(), ret); +} + template char* string_hash_map::strduplicate(const char* str) @@ -183,6 +281,13 @@ string_hash_map::strduplicate(const char* str) return result; } +template +void +string_hash_map::free(const char* str) +{ + EASTLFree(base::base_type::get_allocator(), (void*)str, 0); +} + } diff --git a/include/EASTL/type_traits.h b/include/EASTL/type_traits.h index f09986f7..afc4212d 100644 --- a/include/EASTL/type_traits.h +++ b/include/EASTL/type_traits.h @@ -344,6 +344,11 @@ namespace eastl template struct EASTL_REMOVE_AT_2024_APRIL type_select { typedef ConditionIsTrueType type; }; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template struct EASTL_REMOVE_AT_2024_APRIL type_select { typedef ConditionIsFalseType type; }; @@ -351,6 +356,7 @@ namespace eastl template using type_select_t EASTL_REMOVE_AT_2024_APRIL = typename type_select::type; #endif +EASTL_INTERNAL_RESTORE_DEPRECATED() @@ -379,8 +385,14 @@ namespace eastl template struct EASTL_REMOVE_AT_2024_APRIL type_or { static const bool value = true; }; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template <> struct EASTL_REMOVE_AT_2024_APRIL type_or { static const bool value = false; }; +EASTL_INTERNAL_RESTORE_DEPRECATED() @@ -399,8 +411,14 @@ namespace eastl template struct EASTL_REMOVE_AT_2024_APRIL type_and{ static const bool value = false; }; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template <> struct EASTL_REMOVE_AT_2024_APRIL type_and{ static const bool value = true; }; +EASTL_INTERNAL_RESTORE_DEPRECATED() @@ -436,8 +454,14 @@ namespace eastl template struct EASTL_REMOVE_AT_2024_APRIL type_not{ static const bool value = true; }; + // Note: some compilers (notably GCC) trigger deprecation warnings when doing template + // specialization if the main template is derpecated, so turn the warning off here. If this + // specialization is used, the warning will still trigger in the user code, this just + // disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() template <> struct EASTL_REMOVE_AT_2024_APRIL type_not{ static const bool value = false; }; +EASTL_INTERNAL_RESTORE_DEPRECATED() @@ -571,13 +595,21 @@ namespace eastl // standard. This is a legacy EASTL type we continue to support for // backwards compatibility. // + // todo: add identity function object on removal. + // https://en.cppreference.com/w/cpp/utility/functional/identity template struct EASTL_REMOVE_AT_2024_APRIL identity { using type = T; }; + // Note: some compilers (notably GCC) trigger deprecation warnings in template variable + // declarations even if the variable is not insantiated here, so turn the warning off + // here. If this varialbe is used, the warning will still trigger in the user code, this + // just disables the warning in this declaration. +EASTL_INTERNAL_DISABLE_DEPRECATED() #if EASTL_VARIABLE_TEMPLATES_ENABLED template using identity_t EASTL_REMOVE_AT_2024_APRIL = typename identity::type; #endif +EASTL_INTERNAL_RESTORE_DEPRECATED() /////////////////////////////////////////////////////////////////////// diff --git a/include/EASTL/utility.h b/include/EASTL/utility.h index 1312f0a0..aa35ceb6 100644 --- a/include/EASTL/utility.h +++ b/include/EASTL/utility.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -24,8 +25,8 @@ // 4619 - There is no warning number 'number'. // 4217 - Member template functions cannot be used for copy-assignment or copy-construction. -// 4512 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. -EA_DISABLE_VC_WARNING(4619 4217 4512); +// 4512/4626 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. +EA_DISABLE_VC_WARNING(4619 4217 4512 4626); #if defined(EA_PRAGMA_ONCE_SUPPORTED) @@ -755,20 +756,18 @@ namespace eastl /////////////////////////////////////////////////////////////////////// - /// make_pair / make_pair_ref - /// - /// make_pair is the same as std::make_pair specified by the C++ standard. - /// If you look at the C++ standard, you'll see that it specifies T& instead of T. - /// However, it has been determined that the C++ standard is incorrect and has - /// flagged it as a defect (http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#181). - /// In case you feel that you want a more efficient version that uses references, - /// we provide the make_pair_ref function below, though C++11 move support - /// makes that no longer necessary. + /// make_pair + /// + /// Create a pair, deducing the element types from the types of arguments. + /// + /// Note: You don't need to use make_pair in order to make a pair, it's a convenience for deducing the element types. + /// C++17's class template argument deduction (https://en.cppreference.com/w/cpp/language/class_template_argument_deduction) + /// performs the same purpose as make_unique, make_tuple and similar. /// - /// Note: You don't usually need to use make_pair in order to make a pair. - /// The following code is equivalent, and the latter avoids one more level of inlining: - /// return make_pair(charPtr, charPtr); - /// return pair(charPtr, charPtr); + /// The following code all declare pairs of the same type: + /// pair p1(1, "explicit"); + /// auto p2 = make_pair(2, "deduce types"); + /// pair p3(3, "language feature"); // requires C++17 /// template EA_CPP14_CONSTEXPR inline pair::type>::type, @@ -782,24 +781,31 @@ namespace eastl } - // Without the following, VC++ fails to compile code like this: pair p = eastl::make_pair("hello", 0); - // We define a const reference version alternative to the above. "hello" is of type char const(&)[6] (array of 6 const chars), - // but VC++ decays it to const char* and allows this make_pair to be called with that. VC++ fails below with make_pair("hello", "people") - // because you can't assign arrays and until we have a better solution we just disable this make_pair specialization for when T1 or T2 - // are of type char const(&)[]. - #if defined(_MSC_VER) - template - EA_CPP14_CONSTEXPR inline pair make_pair( - const T1& a, - const T2& b, - typename eastl::enable_if::value && !eastl::is_array::value>::type* = 0) - { - return eastl::pair(a, b); - } - #endif + // Note: DO NOT pass type parameters to make_pair(). + // eg. + // const int myInt = 0; + // const char* myCStr = "example"; + // auto pair1 = eastl::make_pair(myInt, myCStr); + // ^^^^^^^^^^^^^^^^^^^^--- wrong. + // + // use one of the following, depending on the intended pair type: + // eastl::pair pair2 = eastl::make_pair(myInt, myCStr); + // eastl::pair pair3(myInt, myCStr); + // +#if defined(_MSC_VER) + template + EASTL_REMOVE_AT_2024_SEPT EA_CPP14_CONSTEXPR inline pair make_pair( + const DeduceT1& a, + const DeduceT2& b, + typename eastl::enable_if::value && !eastl::is_array::value>::type* = 0) + { + return eastl::pair(a, b); + } +#endif - // For backwards compatibility + // use make_pair() instead. they are equivalent. template + EASTL_REMOVE_AT_2024_SEPT EA_CPP14_CONSTEXPR inline pair::type>::type, typename eastl::remove_reference_wrapper::type>::type> make_pair_ref(T1&& a, T2&& b) @@ -850,69 +856,138 @@ namespace eastl typedef const T2 type; }; - template - struct GetPair; - - template <> - struct GetPair<0> + namespace internal { - template - static EA_CONSTEXPR T1& getInternal(pair& p) - { - return p.first; - } + template + struct GetPair; - template - static EA_CONSTEXPR const T1& getInternal(const pair& p) + template <> + struct GetPair<0> { - return p.first; - } - - template - static EA_CONSTEXPR T1&& getInternal(pair&& p) + template + static EA_CONSTEXPR T1& get(pair& p) + { + return p.first; + } + + template + static EA_CONSTEXPR const T1& get(const pair& p) + { + return p.first; + } + + template + static EA_CONSTEXPR T1&& get(pair&& p) + { + return eastl::forward(p.first); + } + + template + static EA_CONSTEXPR const T1&& get(const pair&& p) + { + return eastl::forward(p.first); + } + }; + + template <> + struct GetPair<1> { - return eastl::forward(p.first); - } - }; + template + static EA_CONSTEXPR T2& get(pair& p) + { + return p.second; + } + + template + static EA_CONSTEXPR const T2& get(const pair& p) + { + return p.second; + } + + template + static EA_CONSTEXPR T2&& get(pair&& p) + { + return eastl::forward(p.second); + } + + template + static EA_CONSTEXPR const T2&& get(const pair&& p) + { + return eastl::forward(p.second); + } + }; + } // namespace internal - template <> - struct GetPair<1> + template + EA_CPP14_CONSTEXPR tuple_element_t>& get(pair& p) { - template - static EA_CONSTEXPR T2& getInternal(pair& p) - { - return p.second; - } - - template - static EA_CONSTEXPR const T2& getInternal(const pair& p) - { - return p.second; - } - - template - static EA_CONSTEXPR T2&& getInternal(pair&& p) - { - return eastl::forward(p.second); - } - }; + return internal::GetPair::get(p); + } template - tuple_element_t>& get(pair& p) + EA_CPP14_CONSTEXPR const tuple_element_t>& get(const pair& p) { - return GetPair::getInternal(p); + return internal::GetPair::get(p); } template - const tuple_element_t>& get(const pair& p) + EA_CPP14_CONSTEXPR tuple_element_t>&& get(pair&& p) { - return GetPair::getInternal(p); + return internal::GetPair::get(eastl::move(p)); } template - tuple_element_t>&& get(pair&& p) + EA_CPP14_CONSTEXPR const tuple_element_t>&& get(const pair&& p) + { + return internal::GetPair::get(eastl::move(p)); + } + + template + EA_CONSTEXPR T1& get(pair& p) EA_NOEXCEPT + { + return internal::GetPair<0>::get(p); + } + + template + EA_CONSTEXPR T2& get(pair& p) EA_NOEXCEPT + { + return internal::GetPair<1>::get(p); + } + + template + EA_CONSTEXPR const T1& get(const pair& p) EA_NOEXCEPT + { + return internal::GetPair<0>::get(p); + } + + template + EA_CONSTEXPR const T2& get(const pair& p) EA_NOEXCEPT + { + return internal::GetPair<1>::get(p); + } + + template + EA_CONSTEXPR T1&& get(pair&& p) EA_NOEXCEPT + { + return internal::GetPair<0>::get(eastl::move(p)); + } + + template + EA_CONSTEXPR T2&& get(pair&& p) EA_NOEXCEPT + { + return internal::GetPair<1>::get(eastl::move(p)); + } + + template + EA_CONSTEXPR const T1&& get(const pair&& p) EA_NOEXCEPT + { + return internal::GetPair<0>::get(eastl::move(p)); + } + + template + EA_CONSTEXPR const T2&& get(const pair&& p) EA_NOEXCEPT { - return GetPair::getInternal(eastl::move(p)); + return internal::GetPair<1>::get(eastl::move(p)); } #endif // EASTL_TUPLE_ENABLED diff --git a/include/EASTL/variant.h b/include/EASTL/variant.h index 50616b6c..1345315b 100644 --- a/include/EASTL/variant.h +++ b/include/EASTL/variant.h @@ -541,31 +541,29 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // get_if // - template + template = true> EA_CONSTEXPR add_pointer_t>> get_if(variant* pv) EA_NOEXCEPT { - static_assert(I < sizeof...(Types), "get_if is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t>>; return (!pv || pv->index() != I) ? nullptr : pv->mStorage.template get_as(); } - template + template = true> EA_CONSTEXPR add_pointer_t>> get_if(const variant* pv) EA_NOEXCEPT { - static_assert(I < sizeof...(Types), "get_if is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t>>; return (!pv || pv->index() != I) ? nullptr : pv->mStorage.template get_as(); } - template > + template , enable_if_t, bool> = true> EA_CONSTEXPR add_pointer_t get_if(variant* pv) EA_NOEXCEPT { return get_if(pv); } - template > + template , enable_if_t, bool> = true> EA_CONSTEXPR add_pointer_t get_if(const variant* pv) EA_NOEXCEPT { return get_if(pv); @@ -575,67 +573,59 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // get // - template + template = true> EA_CONSTEXPR variant_alternative_t>& get(variant& v) { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t>>; return *v.mStorage.template get_as(); } - template + template = true> EA_CONSTEXPR variant_alternative_t>&& get(variant&& v) { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t>>; return eastl::move(*v.mStorage.template get_as()); } - template + template = true> EA_CONSTEXPR const variant_alternative_t>& get(const variant& v) { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t>>; return *v.mStorage.template get_as(); } - template + template = true> EA_CONSTEXPR const variant_alternative_t>&& get(const variant&& v) { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t>>; return eastl::move(*v.mStorage.template get_as()); } - template > + template , enable_if_t, bool> = true> EA_CONSTEXPR T& get(variant& v) { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); return get(v); } - template > + template , enable_if_t, bool> = true> EA_CONSTEXPR T&& get(variant&& v) { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); return get(eastl::move(v)); } - template > + template , enable_if_t, bool> = true> EA_CONSTEXPR const T& get(const variant& v) { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); return get(v); } - template > + template , enable_if_t, bool> = true> EA_CONSTEXPR const T&& get(const variant&& v) { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); return get(v); } @@ -643,10 +633,9 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // 20.7.4, value access // - template > + template , enable_if_t, bool> = true> EA_CONSTEXPR bool holds_alternative(const variant& v) EA_NOEXCEPT { - // ssize_t template parameter because the value can be negative return I == variant_npos ? false : (v.index() == I); } @@ -712,14 +701,11 @@ namespace eastl // Conversion constructor template >, - typename = enable_if_t, variant>>, - size_t I = meta::get_type_index_v, Types...>> + size_t I = meta::get_type_index_v, Types...>, + enable_if_t, variant> && I < sizeof...(Types) && meta::duplicate_type_check_v, bool> = true> EA_CONSTEXPR variant(T&& t) EA_NOEXCEPT(is_nothrow_constructible_v) : mIndex(variant_npos), mStorage() { - static_assert(I >= 0, "T not found in type-list."); - static_assert((meta::type_count_v == 1), "function overload is not unique - duplicate types in type list"); - mIndex = static_cast(I); mStorage.template set_as(eastl::forward(t)); } @@ -810,6 +796,7 @@ namespace eastl // std::is_constructible_v is true. The behavior is undefined if I is not less than // sizeof...(Types). // + EA_DISABLE_VC_WARNING(4702) // unreachable code: suppress warning because set_as() may always throws (because T(args...) throws). template , @@ -830,6 +817,7 @@ namespace eastl mIndex = static_cast(I); return *reinterpret_cast(&mStorage.mBuffer); } + EA_RESTORE_VC_WARNING() // First, destroys the currently contained value (if any). Then direct-initializes the contained value as if // constructing a value of type T_I with the arguments il, std::forward(args).... If an exception is @@ -837,6 +825,7 @@ namespace eastl // std::is_constructible_v&, Args...> is true. The behavior is undefined if I is not // less than sizeof...(Types). // + EA_DISABLE_VC_WARNING(4702) // unreachable code: suppress warning because set_as() may always throws (because T(args...) throws). template (I); return *reinterpret_cast(&mStorage.mBuffer); } + EA_RESTORE_VC_WARNING() /////////////////////////////////////////////////////////////////////////// @@ -864,16 +854,12 @@ namespace eastl // template >, - ssize_t I = meta::get_type_index_v, Types...>, - typename = enable_if_t, variant> && eastl::is_assignable_v && - eastl::is_constructible_v>> + size_t I = meta::get_type_index_v, Types...>, + enable_if_t, variant> && eastl::is_assignable_v && + eastl::is_constructible_v && I < sizeof...(Types) && meta::duplicate_type_check_v, bool> = true> EA_CPP14_CONSTEXPR variant& operator=(T&& t) EA_NOEXCEPT(conjunction_v, is_nothrow_constructible>) { - static_assert(I >= 0, "T not found in type-list."); - static_assert((meta::type_count_v == 1), - "function overload is not unique - duplicate types in type list"); - if (!valueless_by_exception()) mStorage.destroy(); @@ -949,14 +935,14 @@ namespace eastl private: // NOTE(rparolin): get_if accessors require internal access to the variant storage class - template friend EA_CONSTEXPR add_pointer_t< variant_alternative_t>> get_if( variant* pv) EA_NOEXCEPT; - template friend EA_CONSTEXPR add_pointer_t>> get_if(const variant* pv) EA_NOEXCEPT; + template > friend EA_CONSTEXPR add_pointer_t< variant_alternative_t>> get_if( variant* pv) EA_NOEXCEPT; + template > friend EA_CONSTEXPR add_pointer_t>> get_if(const variant* pv) EA_NOEXCEPT; // NOTE(rparolin): get accessors require internal access to the variant storage class - template friend EA_CONSTEXPR variant_alternative_t>& get(variant& v); - template friend EA_CONSTEXPR variant_alternative_t>&& get(variant&& v); - template friend EA_CONSTEXPR const variant_alternative_t>& get(const variant& v); - template friend EA_CONSTEXPR const variant_alternative_t>&& get(const variant&& v); + template > friend EA_CONSTEXPR variant_alternative_t>& get(variant& v); + template > friend EA_CONSTEXPR variant_alternative_t>&& get(variant&& v); + template > friend EA_CONSTEXPR const variant_alternative_t>& get(const variant& v); + template > friend EA_CONSTEXPR const variant_alternative_t>&& get(const variant&& v); }; /////////////////////////////////////////////////////////////////////////// diff --git a/include/EASTL/vector.h b/include/EASTL/vector.h index 509ab31e..142d4893 100644 --- a/include/EASTL/vector.h +++ b/include/EASTL/vector.h @@ -180,6 +180,12 @@ namespace eastl typedef VectorBase base_type; typedef vector this_type; + template + friend typename vector::size_type erase_unsorted(vector& c, const U& value); + + template + friend typename vector::size_type erase_unsorted_if(vector& c, P predicate); + protected: using base_type::mpBegin; using base_type::mpEnd; @@ -541,7 +547,7 @@ namespace eastl inline vector::vector(size_type n, const value_type& value, const allocator_type& allocator) : base_type(n, allocator) { - eastl::uninitialized_fill_n_ptr(mpBegin, n, value); + eastl::uninitialized_fill_n(mpBegin, n, value); mpEnd = mpBegin + n; } @@ -550,7 +556,7 @@ namespace eastl inline vector::vector(const this_type& x) : base_type(x.size(), x.internalAllocator()) { - mpEnd = eastl::uninitialized_copy_ptr(x.mpBegin, x.mpEnd, mpBegin); + mpEnd = eastl::uninitialized_copy(x.mpBegin, x.mpEnd, mpBegin); } @@ -558,7 +564,7 @@ namespace eastl inline vector::vector(const this_type& x, const allocator_type& allocator) : base_type(x.size(), allocator) { - mpEnd = eastl::uninitialized_copy_ptr(x.mpBegin, x.mpEnd, mpBegin); + mpEnd = eastl::uninitialized_copy(x.mpBegin, x.mpEnd, mpBegin); } @@ -1007,11 +1013,11 @@ namespace eastl inline typename vector::reference vector::back() { - #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED - if (EASTL_UNLIKELY((mpBegin == nullptr) || (mpEnd <= mpBegin))) // We don't allow the user to reference an empty container. + #if EASTL_ASSERT_ENABLED + // if mpEnd is nullptr the expression (mpEnd - 1) is undefined behaviour. + // any use of back() with an empty vector is thus conceptually wrong. + if (EASTL_UNLIKELY((mpBegin == nullptr) || (mpEnd <= mpBegin))) EASTL_FAIL_MSG("vector::back -- empty vector"); - #else - // We allow the user to reference an empty container. #endif return *(mpEnd - 1); @@ -1022,11 +1028,11 @@ namespace eastl inline typename vector::const_reference vector::back() const { - #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED - if (EASTL_UNLIKELY((mpBegin == nullptr) || (mpEnd <= mpBegin))) // We don't allow the user to reference an empty container. + #if EASTL_ASSERT_ENABLED + // if mpEnd is nullptr the expression (mpEnd - 1) is undefined behaviour. + // any use of back() with an empty vector is thus conceptually wrong. + if (EASTL_UNLIKELY((mpBegin == nullptr) || (mpEnd <= mpBegin))) EASTL_FAIL_MSG("vector::back -- empty vector"); - #else - // We allow the user to reference an empty container. #endif return *(mpEnd - 1); @@ -1403,7 +1409,7 @@ namespace eastl vector::DoRealloc(size_type n, ForwardIterator first, ForwardIterator last, should_copy_tag) { T* const p = DoAllocate(n); // p is of type T* but is not constructed. - eastl::uninitialized_copy_ptr(first, last, p); // copy-constructs p from [first,last). + eastl::uninitialized_copy(first, last, p); // copy-constructs p from [first,last). return p; } @@ -1414,7 +1420,7 @@ namespace eastl vector::DoRealloc(size_type n, ForwardIterator first, ForwardIterator last, should_move_tag) { T* const p = DoAllocate(n); // p is of type T* but is not constructed. - eastl::uninitialized_move_ptr_if_noexcept(first, last, p); // move-constructs p from [first,last). + eastl::uninitialized_move_if_noexcept(first, last, p); // move-constructs p from [first,last). return p; } @@ -1427,7 +1433,7 @@ namespace eastl internalCapacityPtr() = mpBegin + n; mpEnd = internalCapacityPtr(); - eastl::uninitialized_fill_n_ptr(mpBegin, n, value); + eastl::uninitialized_fill_n(mpBegin, n, value); } @@ -1459,7 +1465,7 @@ namespace eastl internalCapacityPtr() = mpBegin + n; mpEnd = internalCapacityPtr(); - eastl::uninitialized_copy_ptr(first, last, mpBegin); + eastl::uninitialized_copy(first, last, mpBegin); } @@ -1491,7 +1497,7 @@ namespace eastl else if(n > size_type(mpEnd - mpBegin)) // If n > size ... { eastl::fill(mpBegin, mpEnd, value); - eastl::uninitialized_fill_n_ptr(mpEnd, n - size_type(mpEnd - mpBegin), value); + eastl::uninitialized_fill_n(mpEnd, n - size_type(mpEnd - mpBegin), value); mpEnd += n - size_type(mpEnd - mpBegin); } else // else 0 <= n <= size @@ -1547,7 +1553,7 @@ namespace eastl { RandomAccessIterator position = first + (mpEnd - mpBegin); eastl::copy(first, position, mpBegin); // Since we are copying to mpBegin, we don't have to worry about needing copy_backward or a memmove-like copy (as opposed to memcpy-like copy). - mpEnd = eastl::uninitialized_copy_ptr(position, last, mpEnd); + mpEnd = eastl::uninitialized_copy(position, last, mpEnd); } } @@ -1600,7 +1606,7 @@ namespace eastl if(n < nExtra) // If the inserted values are entirely within initialized memory (i.e. are before mpEnd)... { - eastl::uninitialized_move_ptr(mpEnd - n, mpEnd, mpEnd); + eastl::uninitialized_move(mpEnd - n, mpEnd, mpEnd); eastl::move_backward(destPosition, mpEnd - n, mpEnd); // We need move_backward because of potential overlap issues. eastl::copy(first, last, destPosition); } @@ -1608,8 +1614,8 @@ namespace eastl { BidirectionalIterator iTemp = first; eastl::advance(iTemp, nExtra); - eastl::uninitialized_copy_ptr(iTemp, last, mpEnd); - eastl::uninitialized_move_ptr(destPosition, mpEnd, mpEnd + n - nExtra); + eastl::uninitialized_copy(iTemp, last, mpEnd); + eastl::uninitialized_move(destPosition, mpEnd, mpEnd + n - nExtra); eastl::copy_backward(first, iTemp, destPosition + nExtra); } @@ -1626,9 +1632,9 @@ namespace eastl pointer pNewEnd = pNewData; try { - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, destPosition, pNewData); - pNewEnd = eastl::uninitialized_copy_ptr(first, last, pNewEnd); - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(destPosition, mpEnd, pNewEnd); + pNewEnd = eastl::uninitialized_move_if_noexcept(mpBegin, destPosition, pNewData); + pNewEnd = eastl::uninitialized_copy(first, last, pNewEnd); + pNewEnd = eastl::uninitialized_move_if_noexcept(destPosition, mpEnd, pNewEnd); } catch(...) { @@ -1637,9 +1643,9 @@ namespace eastl throw; } #else - pointer pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, destPosition, pNewData); - pNewEnd = eastl::uninitialized_copy_ptr(first, last, pNewEnd); - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(destPosition, mpEnd, pNewEnd); + pointer pNewEnd = eastl::uninitialized_move(mpBegin, destPosition, pNewData); + pNewEnd = eastl::uninitialized_copy(first, last, pNewEnd); + pNewEnd = eastl::uninitialized_move(destPosition, mpEnd, pNewEnd); #endif eastl::destruct(mpBegin, mpEnd); @@ -1674,14 +1680,14 @@ namespace eastl if(n < nExtra) { - eastl::uninitialized_move_ptr(mpEnd - n, mpEnd, mpEnd); + eastl::uninitialized_move(mpEnd - n, mpEnd, mpEnd); eastl::move_backward(destPosition, mpEnd - n, mpEnd); // We need move_backward because of potential overlap issues. eastl::fill(destPosition, destPosition + n, temp); } else { - eastl::uninitialized_fill_n_ptr(mpEnd, n - nExtra, temp); - eastl::uninitialized_move_ptr(destPosition, mpEnd, mpEnd + n - nExtra); + eastl::uninitialized_fill_n(mpEnd, n - nExtra, temp); + eastl::uninitialized_move(destPosition, mpEnd, mpEnd + n - nExtra); eastl::fill(destPosition, mpEnd, temp); } @@ -1699,9 +1705,9 @@ namespace eastl pointer pNewEnd = pNewData; try { - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, destPosition, pNewData); - eastl::uninitialized_fill_n_ptr(pNewEnd, n, value); - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(destPosition, mpEnd, pNewEnd + n); + pNewEnd = eastl::uninitialized_move_if_noexcept(mpBegin, destPosition, pNewData); + eastl::uninitialized_fill_n(pNewEnd, n, value); + pNewEnd = eastl::uninitialized_move_if_noexcept(destPosition, mpEnd, pNewEnd + n); } catch(...) { @@ -1710,9 +1716,9 @@ namespace eastl throw; } #else - pointer pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, destPosition, pNewData); - eastl::uninitialized_fill_n_ptr(pNewEnd, n, value); - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(destPosition, mpEnd, pNewEnd + n); + pointer pNewEnd = eastl::uninitialized_move(mpBegin, destPosition, pNewData); + eastl::uninitialized_fill_n(pNewEnd, n, value); + pNewEnd = eastl::uninitialized_move(destPosition, mpEnd, pNewEnd + n); #endif eastl::destruct(mpBegin, mpEnd); @@ -1739,7 +1745,7 @@ namespace eastl { pointer const pNewData = DoAllocate(n); - pointer pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, mpEnd, pNewData); + pointer pNewEnd = eastl::uninitialized_move_if_noexcept(mpBegin, mpEnd, pNewData); eastl::destruct(mpBegin, mpEnd); DoFree(mpBegin, (size_type)(internalCapacityPtr() - mpBegin)); @@ -1774,7 +1780,7 @@ namespace eastl pointer pNewEnd = pNewData; // Assign pNewEnd a value here in case the copy throws. try { - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, mpEnd, pNewData); + pNewEnd = eastl::uninitialized_move_if_noexcept(mpBegin, mpEnd, pNewData); } catch(...) { @@ -1783,10 +1789,10 @@ namespace eastl throw; } #else - pointer pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, mpEnd, pNewData); + pointer pNewEnd = eastl::uninitialized_move(mpBegin, mpEnd, pNewData); #endif - eastl::uninitialized_fill_n_ptr(pNewEnd, n, value); + eastl::uninitialized_fill_n(pNewEnd, n, value); pNewEnd += n; eastl::destruct(mpBegin, mpEnd); @@ -1798,7 +1804,7 @@ namespace eastl } else { - eastl::uninitialized_fill_n_ptr(mpEnd, n, value); + eastl::uninitialized_fill_n(mpEnd, n, value); mpEnd += n; } } @@ -1815,7 +1821,7 @@ namespace eastl #if EASTL_EXCEPTIONS_ENABLED pointer pNewEnd = pNewData; // Assign pNewEnd a value here in case the copy throws. - try { pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, mpEnd, pNewData); } + try { pNewEnd = eastl::uninitialized_move_if_noexcept(mpBegin, mpEnd, pNewData); } catch (...) { eastl::destruct(pNewData, pNewEnd); @@ -1823,7 +1829,7 @@ namespace eastl throw; } #else - pointer pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, mpEnd, pNewData); + pointer pNewEnd = eastl::uninitialized_move(mpBegin, mpEnd, pNewData); #endif eastl::uninitialized_value_construct_n(pNewEnd, n); @@ -1891,8 +1897,8 @@ namespace eastl // call eastl::destruct on the entire range if only the first part of the range was constructed. ::new((void*)(pNewData + nPosSize)) value_type(eastl::forward(args)...); // Because the old data is potentially being moved rather than copied, we need to move. pNewEnd = NULL; // Set to NULL so that in catch we can tell the exception occurred during the next call. - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, destPosition, pNewData); // the value first, because it might possibly be a reference to the old data being moved. - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(destPosition, mpEnd, ++pNewEnd); + pNewEnd = eastl::uninitialized_move_if_noexcept(mpBegin, destPosition, pNewData); // the value first, because it might possibly be a reference to the old data being moved. + pNewEnd = eastl::uninitialized_move_if_noexcept(destPosition, mpEnd, ++pNewEnd); } catch(...) { @@ -1905,8 +1911,8 @@ namespace eastl } #else ::new((void*)(pNewData + nPosSize)) value_type(eastl::forward(args)...); // Because the old data is potentially being moved rather than copied, we need to move - pointer pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, destPosition, pNewData); // the value first, because it might possibly be a reference to the old data being moved. - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(destPosition, mpEnd, ++pNewEnd); // Question: with exceptions disabled, do we assume all operations are noexcept and thus there's no need for uninitialized_move_ptr_if_noexcept? + pointer pNewEnd = eastl::uninitialized_move(mpBegin, destPosition, pNewData); // the value first, because it might possibly be a reference to the old data being moved. + pNewEnd = eastl::uninitialized_move(destPosition, mpEnd, ++pNewEnd); #endif eastl::destruct(mpBegin, mpEnd); @@ -1931,7 +1937,7 @@ namespace eastl pointer pNewEnd = pNewData; // Assign pNewEnd a value here in case the copy throws. try { - pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, mpEnd, pNewData); + pNewEnd = eastl::uninitialized_move_if_noexcept(mpBegin, mpEnd, pNewData); ::new((void*)pNewEnd) value_type(eastl::forward(args)...); pNewEnd++; } @@ -1942,7 +1948,7 @@ namespace eastl throw; } #else - pointer pNewEnd = eastl::uninitialized_move_ptr_if_noexcept(mpBegin, mpEnd, pNewData); + pointer pNewEnd = eastl::uninitialized_move(mpBegin, mpEnd, pNewData); ::new((void*)pNewEnd) value_type(eastl::forward(args)...); pNewEnd++; #endif @@ -2085,6 +2091,110 @@ namespace eastl return static_cast::size_type>(numRemoved); } + + /////////////////////////////////////////////////////////////////////// + // erase_unsorted + // + // This serves a similar purpose as erase above but with the difference + // that it doesn't preserve the relative order of what is left in the + // vector. + // + // Effects: Removes all elements equal to value from the vector while + // optimizing for speed with the potential reordering of elements as a + // side effect. + // + // Complexity: Linear + // + /////////////////////////////////////////////////////////////////////// + template + typename vector::size_type erase_unsorted(vector& c, const U& value) + { + auto itRemove = c.begin(); + auto ritMove = c.rbegin(); + + while(true) + { + itRemove = eastl::find(itRemove, ritMove.base(), value); + if (itRemove == ritMove.base()) // any elements to remove? + break; + + ritMove = eastl::find_if(ritMove, eastl::make_reverse_iterator(itRemove), [&value](const T& elem) { return elem != value; }); + if (itRemove == ritMove.base()) // any elements that can be moved into place? + break; + + *itRemove = eastl::move(*ritMove); + ++itRemove; + ++ritMove; + } + + // now all elements in the range [itRemove, c.end()) are either to be removed or have already been moved from. + + auto numRemoved = eastl::distance(itRemove, c.end()); + + eastl::destruct(itRemove, c.end()); + c.mpEnd = itRemove; + + // Note: This is technically a lossy conversion when size_type + // is 32bits and ptrdiff_t is 64bits (could happen on 64bit + // systems when EASTL_SIZE_T_32BIT is set). In practice this + // is fine because if EASTL_SIZE_T_32BIT is set then the vector + // should not have more elements than fit in a uint32_t and so + // the distance here should fit in a size_type. + return static_cast::size_type>(numRemoved); + } + + /////////////////////////////////////////////////////////////////////// + // erase_unsorted_if + // + // This serves a similar purpose as erase_if above but with the + // difference that it doesn't preserve the relative order of what is + // left in the vector. + // + // Effects: Removes all elements that return true for the predicate + // while optimizing for speed with the potential reordering of elements + // as a side effect. + // + // Complexity: Linear + // + /////////////////////////////////////////////////////////////////////// + template + typename vector::size_type erase_unsorted_if(vector& c, Predicate predicate) + { + // Erases all elements that satisfy predicate from the container. + auto itRemove = c.begin(); + auto ritMove = c.rbegin(); + + while(true) + { + itRemove = eastl::find_if(itRemove, ritMove.base(), predicate); + if (itRemove == ritMove.base()) // any elements to remove? + break; + + ritMove = eastl::find_if(ritMove, eastl::make_reverse_iterator(itRemove), not_fn(predicate)); + if (itRemove == ritMove.base()) // any elements that can be moved into place? + break; + + *itRemove = eastl::move(*ritMove); + ++itRemove; + ++ritMove; + } + + // now all elements in the range [itRemove, c.end()) are either to be removed or have already been moved from. + + auto numRemoved = eastl::distance(itRemove, c.end()); + + eastl::destruct(itRemove, c.end()); + c.mpEnd = itRemove; + + // Note: This is technically a lossy conversion when size_type + // is 32bits and ptrdiff_t is 64bits (could happen on 64bit + // systems when EASTL_SIZE_T_32BIT is set). In practice this + // is fine because if EASTL_SIZE_T_32BIT is set then the vector + // should not have more elements than fit in a uint32_t and so + // the distance here should fit in a size_type. + return static_cast::size_type>(numRemoved); + } + } // namespace eastl diff --git a/include/EASTL/vector_map.h b/include/EASTL/vector_map.h index cd714a70..26ea471b 100644 --- a/include/EASTL/vector_map.h +++ b/include/EASTL/vector_map.h @@ -40,6 +40,9 @@ #include #include #include +#if EASTL_EXCEPTIONS_ENABLED +#include +#endif #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. @@ -265,6 +268,15 @@ namespace eastl mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); + // non-standard! this was originally inherited from vector with incorrect semantics. + // this is only defined so that we can deprecate it. + // use `*(map.begin() + index)` if you want to get an element by index. + EASTL_REMOVE_AT_2024_SEPT reference at(size_type index); + EASTL_REMOVE_AT_2024_SEPT const_reference at(size_type index) const; + // after the deprecation period the above should be replaced with: + // mapped_type& at(const key_type& k) { return at_key(k); } + // const mapped_type& at(const key_type& k) const { return at_key(k); } + // aka. the standard's at() member function. mapped_type& at_key(const key_type& k); const mapped_type& at_key(const key_type& k) const; @@ -827,6 +839,19 @@ namespace eastl return (*itLB).second; } + template + inline typename vector_map::reference + vector_map::at(size_type index) + { + return *(begin() + index); + } + + template + inline typename vector_map::const_reference + vector_map::at(size_type index) const + { + return *(begin() + index); + } template inline typename vector_map::mapped_type& diff --git a/source/numeric_limits.cpp b/source/numeric_limits.cpp index 90b1d758..ee157a18 100644 --- a/source/numeric_limits.cpp +++ b/source/numeric_limits.cpp @@ -6,593 +6,521 @@ #include -#if EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED - #include // See notes below about usage of this header. - - namespace eastl - { - namespace Internal - { - // For this platformc/compiler combination we fall back to using std::numeric_limits, - // which is available for with most compilers and platforms, though it doesn't necessarily - // support the C++11 functionality that we do. However, we need it just for the four - // floating point types. Note that this code isn't used for most EA platforms, as - // most platforms use GCC, clang, VC++ (yvals), or Dinkumware (yvals). - // To do: Initialize these values via a means that doesn't depend on std::numeric_limits. - - EASTL_API float gFloatInfinity = std::numeric_limits::infinity(); - EASTL_API float gFloatNaN = std::numeric_limits::quiet_NaN(); - EASTL_API float gFloatSNaN = std::numeric_limits::signaling_NaN(); - EASTL_API float gFloatDenorm = std::numeric_limits::denorm_min(); - - EASTL_API double gDoubleInfinity = std::numeric_limits::infinity(); - EASTL_API double gDoubleNaN = std::numeric_limits::quiet_NaN(); - EASTL_API double gDoubleSNaN = std::numeric_limits::signaling_NaN(); - EASTL_API double gDoubleDenorm = std::numeric_limits::denorm_min(); - - EASTL_API long double gLongDoubleInfinity = std::numeric_limits::infinity(); - EASTL_API long double gLongDoubleNaN = std::numeric_limits::quiet_NaN(); - EASTL_API long double gLongDoubleSNaN = std::numeric_limits::signaling_NaN(); - EASTL_API long double gLongDoubleDenorm = std::numeric_limits::denorm_min(); - } - } -#endif - - -#if defined(_MSC_VER) && !defined(EA_COMPILER_CLANG_CL) - // VC++ has a long-standing bug: it fails to allow the definition of static const member variables - // outside the declaration within the class. The C++ Standard actually requires that they be defined - // and some other compilers fail to link if they aren't. So we simply don't define the members for VC++. - // See the C++ Standard Sec. 9.4.2 paragraph 4, which makes this clear. - // http://bytes.com/topic/c/answers/710704-const-static-initialization-visual-studio -#else - - namespace eastl - { - namespace Internal - { - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits_base::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits_base::digits10; - EA_CONSTEXPR_OR_CONST int numeric_limits_base::max_digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits_base::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits_base::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits_base::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits_base::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits_base::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits_base::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits_base::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits_base::is_iec559; - } - - // bool - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // char - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // unsigned char - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // signed char - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // wchar_t - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // char8_t - #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE // If char8_t is a true unique type (as called for by the C++20 Standard) - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - #endif - - // char16_t - #if EA_CHAR16_NATIVE // If char16_t is a true unique type (as called for by the C++11 Standard)... - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - #endif - - // char32_t - #if EA_CHAR32_NATIVE // If char32_t is a true unique type (as called for by the C++11 Standard)... - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - #endif +namespace eastl +{ + // bool + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // char + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // unsigned char + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // signed char + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // wchar_t + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // char8_t + #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE // If char8_t is a true unique type (as called for by the C++20 Standard) + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + #endif + + // char16_t + #if EA_CHAR16_NATIVE // If char16_t is a true unique type (as called for by the C++11 Standard)... + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + #endif + + // char32_t + #if EA_CHAR32_NATIVE // If char32_t is a true unique type (as called for by the C++11 Standard)... + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + #endif - // unsigned short - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // short - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // unsigned int - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // int - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // unsigned long - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // long - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // unsigned long long - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // long long - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // __uint128_t - #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(__clang__)) // If __int128_t/__uint128_t is supported... - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits<__uint128_t>::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits<__uint128_t>::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::is_iec559; - #endif - - // __int128_t - #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(__clang__)) // If __int128_t/__uint128_t is supported... - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits<__int128_t>::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits<__int128_t>::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::is_iec559; - #endif - - // float - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // double - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - // long double - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_specialized; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits; - EA_CONSTEXPR_OR_CONST int numeric_limits::digits10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_signed; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_integer; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_exact; - EA_CONSTEXPR_OR_CONST int numeric_limits::radix; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::min_exponent10; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent; - EA_CONSTEXPR_OR_CONST int numeric_limits::max_exponent10; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_bounded; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_modulo; - EA_CONSTEXPR_OR_CONST bool numeric_limits::traps; - EA_CONSTEXPR_OR_CONST bool numeric_limits::tinyness_before; - EA_CONSTEXPR_OR_CONST float_round_style numeric_limits::round_style; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_infinity; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_quiet_NaN; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_signaling_NaN; - EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits::has_denorm; - EA_CONSTEXPR_OR_CONST bool numeric_limits::has_denorm_loss; - EA_CONSTEXPR_OR_CONST bool numeric_limits::is_iec559; - - } // namespace eastl - -#endif // (VC++ 2010 or earlier) - - + // unsigned short + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // short + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // unsigned int + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // int + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // unsigned long + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // long + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // unsigned long long + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // long long + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // __uint128_t + #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(__clang__)) // If __int128_t/__uint128_t is supported... + constexpr bool numeric_limits<__uint128_t>::is_specialized; + constexpr int numeric_limits<__uint128_t>::digits; + constexpr int numeric_limits<__uint128_t>::digits10; + constexpr bool numeric_limits<__uint128_t>::is_signed; + constexpr bool numeric_limits<__uint128_t>::is_integer; + constexpr bool numeric_limits<__uint128_t>::is_exact; + constexpr int numeric_limits<__uint128_t>::radix; + constexpr int numeric_limits<__uint128_t>::min_exponent; + constexpr int numeric_limits<__uint128_t>::min_exponent10; + constexpr int numeric_limits<__uint128_t>::max_exponent; + constexpr int numeric_limits<__uint128_t>::max_exponent10; + constexpr bool numeric_limits<__uint128_t>::is_bounded; + constexpr bool numeric_limits<__uint128_t>::is_modulo; + constexpr bool numeric_limits<__uint128_t>::traps; + constexpr bool numeric_limits<__uint128_t>::tinyness_before; + constexpr float_round_style numeric_limits<__uint128_t>::round_style; + constexpr bool numeric_limits<__uint128_t>::has_infinity; + constexpr bool numeric_limits<__uint128_t>::has_quiet_NaN; + constexpr bool numeric_limits<__uint128_t>::has_signaling_NaN; + constexpr float_denorm_style numeric_limits<__uint128_t>::has_denorm; + constexpr bool numeric_limits<__uint128_t>::has_denorm_loss; + constexpr bool numeric_limits<__uint128_t>::is_iec559; + #endif + + // __int128_t + #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(__clang__)) // If __int128_t/__uint128_t is supported... + constexpr bool numeric_limits<__int128_t>::is_specialized; + constexpr int numeric_limits<__int128_t>::digits; + constexpr int numeric_limits<__int128_t>::digits10; + constexpr bool numeric_limits<__int128_t>::is_signed; + constexpr bool numeric_limits<__int128_t>::is_integer; + constexpr bool numeric_limits<__int128_t>::is_exact; + constexpr int numeric_limits<__int128_t>::radix; + constexpr int numeric_limits<__int128_t>::min_exponent; + constexpr int numeric_limits<__int128_t>::min_exponent10; + constexpr int numeric_limits<__int128_t>::max_exponent; + constexpr int numeric_limits<__int128_t>::max_exponent10; + constexpr bool numeric_limits<__int128_t>::is_bounded; + constexpr bool numeric_limits<__int128_t>::is_modulo; + constexpr bool numeric_limits<__int128_t>::traps; + constexpr bool numeric_limits<__int128_t>::tinyness_before; + constexpr float_round_style numeric_limits<__int128_t>::round_style; + constexpr bool numeric_limits<__int128_t>::has_infinity; + constexpr bool numeric_limits<__int128_t>::has_quiet_NaN; + constexpr bool numeric_limits<__int128_t>::has_signaling_NaN; + constexpr float_denorm_style numeric_limits<__int128_t>::has_denorm; + constexpr bool numeric_limits<__int128_t>::has_denorm_loss; + constexpr bool numeric_limits<__int128_t>::is_iec559; + #endif + + // float + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // double + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + + // long double + constexpr bool numeric_limits::is_specialized; + constexpr int numeric_limits::digits; + constexpr int numeric_limits::digits10; + constexpr bool numeric_limits::is_signed; + constexpr bool numeric_limits::is_integer; + constexpr bool numeric_limits::is_exact; + constexpr int numeric_limits::radix; + constexpr int numeric_limits::min_exponent; + constexpr int numeric_limits::min_exponent10; + constexpr int numeric_limits::max_exponent; + constexpr int numeric_limits::max_exponent10; + constexpr bool numeric_limits::is_bounded; + constexpr bool numeric_limits::is_modulo; + constexpr bool numeric_limits::traps; + constexpr bool numeric_limits::tinyness_before; + constexpr float_round_style numeric_limits::round_style; + constexpr bool numeric_limits::has_infinity; + constexpr bool numeric_limits::has_quiet_NaN; + constexpr bool numeric_limits::has_signaling_NaN; + constexpr float_denorm_style numeric_limits::has_denorm; + constexpr bool numeric_limits::has_denorm_loss; + constexpr bool numeric_limits::is_iec559; + +} // namespace eastl diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8972a172..78af0b99 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -77,7 +77,7 @@ target_include_directories(EASTLTest PUBLIC include) FetchContent_Declare( EABase GIT_REPOSITORY https://github.com/electronicarts/EABase.git - GIT_TAG 521cb053d9320636f53226ffc616216cf532f0ef + GIT_TAG 123363eb82e132c0181ac53e43226d8ee76dea12 GIT_SUBMODULES "" # This should be temporary until we update the cyclic submodule dependencies in EABase. ) FetchContent_MakeAvailable(EABase) diff --git a/test/source/EASTLTest.h b/test/source/EASTLTest.h index 4daa3512..5ba7776e 100644 --- a/test/source/EASTLTest.h +++ b/test/source/EASTLTest.h @@ -8,6 +8,7 @@ #include +#include #include EA_DISABLE_ALL_VC_WARNINGS() @@ -24,12 +25,17 @@ EA_RESTORE_ALL_VC_WARNINGS(); int TestAlgorithm(); int TestAllocator(); +int TestAllocatorPropagate(); int TestAny(); int TestArray(); +#if defined(EA_COMPILER_CPP20_ENABLED) +int TestBit(); +#endif int TestBitVector(); int TestBitset(); int TestCharTraits(); int TestChrono(); +int TestConcepts(); int TestCppCXTypeTraits(); int TestDeque(); int TestExtra(); @@ -81,8 +87,20 @@ int TestVector(); int TestVectorMap(); int TestVectorSet(); int TestAtomicBasic(); +int TestAtomicMultiThreaded(); int TestAtomicAsm(); int TestBitcast(); +int TestGslAlgorithum(); +int TestGslAssertion(); +int TestGslAt(); +int TestGslByte(); +int TestGslNotNull(); +int TestGslOwner(); +int TestGslSpanCompatibility(); +int TestGslSpanExt(); +int TestGslSpan(); +int TestGslStrictNotNull(); +int TestGslUtils(); // Now enable warnings as desired. @@ -1350,8 +1368,9 @@ class InstanceAllocator kMultiplier = 16 }; // Use 16 because it's the highest currently known platform alignment requirement. - InstanceAllocator(const char* = NULL, uint8_t instanceId = 0) : mInstanceId(instanceId) {} - InstanceAllocator(uint8_t instanceId) : mInstanceId(instanceId) {} + InstanceAllocator() : mInstanceId(0) {} + explicit InstanceAllocator(const char*, uint8_t instanceId = 0) : mInstanceId(instanceId) {} + explicit InstanceAllocator(uint8_t instanceId) : mInstanceId(instanceId) {} InstanceAllocator(const InstanceAllocator& x) : mInstanceId(x.mInstanceId) {} InstanceAllocator(const InstanceAllocator& x, const char*) : mInstanceId(x.mInstanceId) {} @@ -1399,7 +1418,7 @@ class InstanceAllocator const char* get_name() { - sprintf(mName, "InstanceAllocator %u", mInstanceId); + EA::StdC::Snprintf(mName, kNameBufferSize, "InstanceAllocator %u", mInstanceId); return mName; } @@ -1408,8 +1427,9 @@ class InstanceAllocator static void reset_all() { mMismatchCount = 0; } public: + const static int kNameBufferSize = 32; uint8_t mInstanceId; - char mName[32]; + char mName[kNameBufferSize]; static int mMismatchCount; }; @@ -1608,6 +1628,23 @@ struct MoveOnlyTypeDefaultCtor int mVal; }; +struct NonTriviallyCopyable { + // non-trivial special members (that is equivalent to the defaults) + NonTriviallyCopyable(unsigned int v = 0) noexcept : mValue(v) {} + NonTriviallyCopyable(NonTriviallyCopyable&& other) noexcept : mValue(other.mValue) {} + NonTriviallyCopyable& operator=(NonTriviallyCopyable&& other) noexcept { mValue = other.mValue; return *this; } + NonTriviallyCopyable(const NonTriviallyCopyable& other) noexcept : mValue(other.mValue) {} + NonTriviallyCopyable& operator=(const NonTriviallyCopyable& other) noexcept { mValue = other.mValue; return *this; } + + friend bool operator==(const NonTriviallyCopyable& lhs, const NonTriviallyCopyable& rhs) { return lhs.mValue == rhs.mValue; } + +public: + unsigned int mValue; +}; +static_assert(eastl::is_default_constructible::value, "NonTriviallyCopyable"); +static_assert(!eastl::is_trivially_copyable::value, "NonTriviallyCopyable"); +static_assert(eastl::is_standard_layout::value, "NonTriviallyCopyable"); + struct TriviallyCopyableWithCopy { // non-trivial default ctor TriviallyCopyableWithCopy(unsigned int v = 0) noexcept : mValue(v) {} diff --git a/test/source/EASTLTestIterators.h b/test/source/EASTLTestIterators.h index 21bc3de2..f2c2973d 100644 --- a/test/source/EASTLTestIterators.h +++ b/test/source/EASTLTestIterators.h @@ -15,6 +15,9 @@ #ifndef EASTL_TEST_ITERATORS_H #define EASTL_TEST_ITERATORS_H +#include +#include + #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. #endif diff --git a/test/source/TestAllocatorPropagate.cpp b/test/source/TestAllocatorPropagate.cpp new file mode 100644 index 00000000..31d2fad9 --- /dev/null +++ b/test/source/TestAllocatorPropagate.cpp @@ -0,0 +1,181 @@ +// (c) 2023 Electronic Arts Inc. + +#include "EASTLTest.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +int TestPropagateOnContainerCopyAssignment(std::initializer_list ilist1, std::initializer_list ilist2, const char* containerName) +{ + int nErrorCount = 0; + + { + Container c1(ilist1, InstanceAllocator(1)); + Container c2(ilist2, InstanceAllocator(2)); + + EATEST_VERIFY(c1.get_allocator().mInstanceId == 1); + EATEST_VERIFY(c2.get_allocator().mInstanceId == 2); + EATEST_VERIFY(c1.get_allocator() != c2.get_allocator()); + + c1 = c2; + EATEST_VERIFY(c1 == c2); + + // eastl containers have propagate_on_container_copy_assignment behaviour iff EASTL_ALLOCATOR_COPY_ENABLED. + bool allocatorsEqual = (c1.get_allocator() == c2.get_allocator()); + EATEST_VERIFY(allocatorsEqual == (bool)EASTL_ALLOCATOR_COPY_ENABLED); + + // destroying containers to invoke InstanceAllocator::deallocate() checks + } + + EATEST_VERIFY_MSG(InstanceAllocator::mMismatchCount == 0, "Container elements should be deallocated by the allocator that allocated it."); + InstanceAllocator::reset_all(); + + if(nErrorCount > 0) + EASTLTest_Printf("Propagate on %s copy assignment: failure\n", containerName); + + return nErrorCount; +} + +template +int TestPropagateOnContainerMoveAssignment(std::initializer_list ilist1, std::initializer_list ilist2, const char* containerName) +{ + int nErrorCount = 0; + + { + Container c1(ilist1, InstanceAllocator(1)); + Container c2(ilist2, InstanceAllocator(2)); + + EATEST_VERIFY(c1.get_allocator().mInstanceId == 1); + EATEST_VERIFY(c2.get_allocator().mInstanceId == 2); + EATEST_VERIFY(c1.get_allocator() != c2.get_allocator()); + + c1 = eastl::move(c2); + + // eastl containers have propagate_on_container_move_assignment behaviour. + EATEST_VERIFY(c1.get_allocator().mInstanceId == 2); + + // destroying containers to invoke InstanceAllocator::deallocate() checks + } + + EATEST_VERIFY_MSG(InstanceAllocator::mMismatchCount == 0, "Container elements should be deallocated by the allocator that allocated it."); + InstanceAllocator::reset_all(); + + if (nErrorCount > 0) + EASTLTest_Printf("Propagate on %s move assignment: failure\n", containerName); + + return nErrorCount; +} + +template +int TestPropagateOnContainerSwap(std::initializer_list ilist1, std::initializer_list ilist2, const char* containerName) +{ + int nErrorCount = 0; + + { + Container c1(ilist1, InstanceAllocator(1)); + Container c2(ilist2, InstanceAllocator(2)); + + EATEST_VERIFY(c1.get_allocator().mInstanceId == 1); + EATEST_VERIFY(c2.get_allocator().mInstanceId == 2); + EATEST_VERIFY(c1.get_allocator() != c2.get_allocator()); + + eastl::swap(c1, c2); + + // eastl containers have propagate_on_container_swap behaviour. + EATEST_VERIFY(c1.get_allocator().mInstanceId == 2); + EATEST_VERIFY(c2.get_allocator().mInstanceId == 1); + EATEST_VERIFY(c1.get_allocator() != c2.get_allocator()); + + // destroying containers to invoke InstanceAllocator::deallocate() checks + } + + EATEST_VERIFY_MSG(InstanceAllocator::mMismatchCount == 0, "Container elements should be deallocated by the allocator that allocated it."); + InstanceAllocator::reset_all(); + + if (nErrorCount > 0) + EASTLTest_Printf("Propagate on %s swap: failure\n", containerName); + + return nErrorCount; +} + +int TestAllocatorPropagate() +{ + using namespace eastl; + int nErrorCount = 0; + + nErrorCount += TestPropagateOnContainerCopyAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "list"); + nErrorCount += TestPropagateOnContainerCopyAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "slist"); + nErrorCount += TestPropagateOnContainerCopyAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "vector"); + nErrorCount += TestPropagateOnContainerCopyAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "deque"); + nErrorCount += TestPropagateOnContainerCopyAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "segmented_vector"); + nErrorCount += TestPropagateOnContainerCopyAssignment, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "map"); + nErrorCount += TestPropagateOnContainerCopyAssignment, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "multimap"); + nErrorCount += TestPropagateOnContainerCopyAssignment, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "set"); + nErrorCount += TestPropagateOnContainerCopyAssignment, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "multiset"); + nErrorCount += TestPropagateOnContainerCopyAssignment, equal_to, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "hash_map"); + nErrorCount += TestPropagateOnContainerCopyAssignment, equal_to, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "hash_multimap"); + nErrorCount += TestPropagateOnContainerCopyAssignment, equal_to, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "hash_set"); + nErrorCount += TestPropagateOnContainerCopyAssignment, equal_to, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "hash_multiset"); + nErrorCount += TestPropagateOnContainerCopyAssignment>({ 'a', 'b', 'c' }, { 'd', 'e', 'f' }, "basic_string"); + + // commenting out containers with irregular propagation behaviour: + // nErrorCount += TestPropagateOnContainerMoveAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "list"); + // nErrorCount += TestPropagateOnContainerMoveAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "slist"); + nErrorCount += TestPropagateOnContainerMoveAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "vector"); + nErrorCount += TestPropagateOnContainerMoveAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "deque"); + nErrorCount += TestPropagateOnContainerMoveAssignment>({ 1, 2, 3 }, { 4, 5, 6 }, "segmented_vector"); + nErrorCount += TestPropagateOnContainerMoveAssignment, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "map"); + nErrorCount += TestPropagateOnContainerMoveAssignment, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "multimap"); + nErrorCount += TestPropagateOnContainerMoveAssignment, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "set"); + nErrorCount += TestPropagateOnContainerMoveAssignment, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "multiset"); + nErrorCount += TestPropagateOnContainerMoveAssignment, equal_to, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "hash_map"); + nErrorCount += TestPropagateOnContainerMoveAssignment, equal_to, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "hash_multimap"); + nErrorCount += TestPropagateOnContainerMoveAssignment, equal_to, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "hash_set"); + nErrorCount += TestPropagateOnContainerMoveAssignment, equal_to, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "hash_multiset"); + // nErrorCount += TestPropagateOnContainerMoveAssignment>({ 'a', 'b', 'c' }, { 'd', 'e', 'f' }, "basic_string"); + + // nErrorCount += TestPropagateOnContainerSwap>({ 1, 2, 3 }, { 4, 5, 6 }, "list"); + // nErrorCount += TestPropagateOnContainerSwap>({ 1, 2, 3 }, { 4, 5, 6 }, "slist"); + nErrorCount += TestPropagateOnContainerSwap>({ 1, 2, 3 }, { 4, 5, 6 }, "vector"); + nErrorCount += TestPropagateOnContainerSwap>({ 1, 2, 3 }, { 4, 5, 6 }, "deque"); + nErrorCount += TestPropagateOnContainerSwap>({ 1, 2, 3 }, { 4, 5, 6 }, "segmented_vector"); + nErrorCount += TestPropagateOnContainerSwap, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "map"); + nErrorCount += TestPropagateOnContainerSwap, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "multimap"); + nErrorCount += TestPropagateOnContainerSwap, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "set"); + nErrorCount += TestPropagateOnContainerSwap, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "multiset"); + nErrorCount += TestPropagateOnContainerSwap, equal_to, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "hash_map"); + nErrorCount += TestPropagateOnContainerSwap, equal_to, InstanceAllocator>>({ pair(1, 1) }, { pair(2, 2) }, "hash_multimap"); + nErrorCount += TestPropagateOnContainerSwap, equal_to, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "hash_set"); + nErrorCount += TestPropagateOnContainerSwap, equal_to, InstanceAllocator>>({ 1, 2, 3 }, { 4, 5, 6 }, "hash_multiset"); + // nErrorCount += TestPropagateOnContainerSwap>({ 'a', 'b', 'c' }, { 'd', 'e', 'f' }, "basic_string"); + + // fixed containers are not tested because their allocators always compare unequal for different container objects. + + // container adaptors are not tested because their behaviour should be the same as their underlying container: + // queue + // priority_queue + // stack + // vector_map + // vector_multimap + // vector_set + // vector_multiset + // ring_buffer + // lru_cache + + // other containers not tested: + // segmented_vector - copy, move and swap are buggy on this type. + // string_hash_map - doesn't have appropriate constructor. + // string_map - doesn't have appropriate constructor. + // list_map - doesn't have appropriate constructor. + // tuple_vector_alloc - what is this type? + + return nErrorCount; +} diff --git a/test/source/TestAny.cpp b/test/source/TestAny.cpp index fedc85fa..d2fffcc4 100644 --- a/test/source/TestAny.cpp +++ b/test/source/TestAny.cpp @@ -278,11 +278,11 @@ int TestAny() { any a; - a.emplace(42); + VERIFY(a.emplace(42) == 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(a.emplace((short)8) == (short)8); // no way to define a short literal we must cast here. VERIFY(any_cast(a) == 8); VERIFY(a.has_value()); @@ -295,7 +295,7 @@ int TestAny() TestObject::Reset(); { any a; - a.emplace(); + VERIFY(a.emplace() == TestObject()); VERIFY(a.has_value()); } VERIFY(TestObject::IsClear()); @@ -305,13 +305,43 @@ int TestAny() { { any a; - a.emplace(std::initializer_list{1,2,3,4,5,6}); + VERIFY((a.emplace(std::initializer_list{1, 2, 3, 4, 5, 6}).sum == RequiresInitList{ 1, 2, 3, 4, 5, 6 }.sum)); VERIFY(a.has_value()); VERIFY(any_cast(a).sum == 21); } } + // emplace, types that decay + { + // T[] decays to T* + { + any a; + int arr[] = { 1, 2, 3 }; + auto emplaced = a.emplace(arr); + static_assert(eastl::is_same_v, "decay-ed emplace"); + VERIFY(VerifySequence(emplaced, emplaced + 3, { 1, 2, 3 }, "any::emplace()")); + } + + // function decays to pointer to function + { + int(*func) (char, char) = [](char, char) { return 0; }; + + any a; + auto emplaced = a.emplace(func); + static_assert(eastl::is_same_v, "decay-ed emplace"); + VERIFY(emplaced == func); + } + + // const T decays to T + { + any a; + auto emplaced = a.emplace(10); + static_assert(eastl::is_same_v, "decay-ed emplace"); + VERIFY(emplaced == 10); + } + } + // equivalence tests { any a, b; diff --git a/test/source/TestAtomicBasic.cpp b/test/source/TestAtomicBasic.cpp index 166b030f..7ef85f9c 100644 --- a/test/source/TestAtomicBasic.cpp +++ b/test/source/TestAtomicBasic.cpp @@ -1277,7 +1277,7 @@ void AtomicPointerBasicTest::TestAtomicPointerStandalone() { AtomicType atomic; - PtrType ret = atomic_load_cond(&atomic, [](PtrType val) { return true; }); + PtrType ret = atomic_load_cond(&atomic, [](PtrType) { return true; }); VERIFY(ret == (PtrType)0x0); } @@ -1285,7 +1285,7 @@ void AtomicPointerBasicTest::TestAtomicPointerStandalone() { AtomicType atomic; - PtrType ret = atomic_load_cond_explicit(&atomic, [](PtrType val) { return true; }, eastl::memory_order_relaxed); + PtrType ret = atomic_load_cond_explicit(&atomic, [](PtrType) { return true; }, eastl::memory_order_relaxed); VERIFY(ret == (PtrType)0x0); } @@ -3750,7 +3750,7 @@ void AtomicIntegralBasicTest::TestAtomicStandalone() { AtomicType atomic; - IntegralType ret = atomic_load_cond(&atomic, [](IntegralType val) { return true; }); + IntegralType ret = atomic_load_cond(&atomic, [](IntegralType) { return true; }); VERIFY(ret == 0); } @@ -3758,7 +3758,7 @@ void AtomicIntegralBasicTest::TestAtomicStandalone() { AtomicType atomic; - IntegralType ret = atomic_load_cond_explicit(&atomic, [](IntegralType val) { return true; }, eastl::memory_order_relaxed); + IntegralType ret = atomic_load_cond_explicit(&atomic, [](IntegralType) { return true; }, eastl::memory_order_relaxed); VERIFY(ret == 0); } diff --git a/test/source/TestAtomicMultiThreaded.cpp b/test/source/TestAtomicMultiThreaded.cpp new file mode 100644 index 00000000..06919b47 --- /dev/null +++ b/test/source/TestAtomicMultiThreaded.cpp @@ -0,0 +1,252 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +// This one first because it defines EASTL_THREAD_SUPPORT_AVAILABLE +#include + +#if EASTL_THREAD_SUPPORT_AVAILABLE + +#include +#include + +#include "EASTLTest.h" + +/** + * These are some basic tests to test "atomicity" of operations. + */ + +namespace +{ + + struct Test128BitType + { + Test128BitType() = default; + Test128BitType(uint32_t x) : a{x}, b{x}, c{x}, d{x} {}; + Test128BitType(uint32_t x, uint32_t y, uint32_t z, uint32_t w) : a{x}, b{y}, c{z}, d{w} {}; + + bool operator==(const Test128BitType& rhs) const + { + return a == rhs.a && b == rhs.b && c == rhs.c && d == rhs.d; + } + + uint32_t a{}; + uint32_t b{}; + uint32_t c{}; + uint32_t d{}; + }; + + bool AreAllMembersEqual(const Test128BitType& x) { return x.a == x.b && x.b == x.c && x.c == x.d; } + +// We don't support 128bit atomics on 32-bit x86. +#if !defined(EA_PROCESSOR_X86) + struct Test128BitLoadStoreData + { + eastl::atomic mX; + EA::Thread::Semaphore mStartSem{0}; + eastl::atomic mShouldStop{}; + eastl::atomic mFoundTornRead{}; + }; + + static intptr_t RelaxedLoadAndCheck(void* v) + { + Test128BitLoadStoreData* data = static_cast(v); + + // Wait to be signaled that we can start. + data->mStartSem.Wait(); + + bool foundTornRead = false; + // Until we're signaled we should stop. + while (!data->mShouldStop.load(eastl::memory_order_relaxed)) + { + // Do a relaxed load and make sure we don't ever tear. The + // writing thread will do atomic stores with values satisfying + // this condition. + auto loaded = data->mX.load(eastl::memory_order_relaxed); + + foundTornRead |= !AreAllMembersEqual(loaded); + } + + data->mFoundTornRead = foundTornRead; + + return 0; + } + + static intptr_t RelaxedStore(void* v) + { + Test128BitLoadStoreData* data = static_cast(v); + + // Wait to be signaled that we can start. + data->mStartSem.Wait(); + + uint32_t counter{}; + + // Until we're signaled we should stop. + while (!data->mShouldStop.load(eastl::memory_order_relaxed)) + { + // This thread just stores incremental values which have identical entries. + Test128BitType x{counter}; + data->mX.store(x, eastl::memory_order_relaxed); + + // This could wrap, but that's OK, it's not UB on unsigned types. + ++counter; + } + + return 0; + } +#endif + + template + struct TestSequentialConsistencyData + { + eastl::atomic mA1; + eastl::atomic mA2; + eastl::atomic mB1; + eastl::atomic mB2; + EA::Thread::Semaphore mStartSem1{0}; + EA::Thread::Semaphore mStartSem2{0}; + EA::Thread::Semaphore mEndSem{0}; + eastl::atomic mLoopCount{10000}; + eastl::atomic mThreadCounter{}; + }; + + template + static intptr_t TestSequentialConsistencyThreadFn(void* v) + { + TestSequentialConsistencyData& data = *static_cast*>(v); + + // We're doing x = 1, r = y in order, but we want different + // order on different threads, and different r's for the + // different orders. + // + // See the following link for details: + // https://preshing.com/20120515/memory-reordering-caught-in-the-act/ + + const uint32_t threadNumber = ++data.mThreadCounter; + const bool chooseFirst = threadNumber % 2; + + auto& x = chooseFirst ? data.mA1 : data.mA2; + auto& y = chooseFirst ? data.mA2 : data.mA1; + auto& r = chooseFirst ? data.mB1 : data.mB2; + + auto& startSem = chooseFirst ? data.mStartSem1 : data.mStartSem2; + + uint32_t loops = data.mLoopCount; + while (loops--) + { + startSem.Wait(); + + // x = 1; + x.store(T(1), eastl::memory_order_seq_cst); + + // r = y; + r.store(y.load(eastl::memory_order_seq_cst), eastl::memory_order_seq_cst); + + data.mEndSem.Post(); + } + + return 0; + } + + template + static int TestSequentialConsistencyImpl() + { + TestSequentialConsistencyData data; + + int nErrorCount = 0; + EA::Thread::Thread threads[2]; + + threads[0].Begin(TestSequentialConsistencyThreadFn, static_cast(&data)); + threads[1].Begin(TestSequentialConsistencyThreadFn, static_cast(&data)); + + uint32_t loops = data.mLoopCount; + + while (loops--) + { + // Reset the input. + data.mA1.store(T(0), eastl::memory_order_seq_cst); + data.mA2.store(T(0), eastl::memory_order_seq_cst); + + // Signal the threads + data.mStartSem1.Post(); + data.mStartSem2.Post(); + + // Wait for both threads; + data.mEndSem.Wait(); + data.mEndSem.Wait(); + + // Check if there was a CPU reorder. + const auto b1 = data.mB1.load(eastl::memory_order_seq_cst); + const auto b2 = data.mB2.load(eastl::memory_order_seq_cst); + + const bool reorderHappened = (b1 == T{0} && b2 == T{0}); + EATEST_VERIFY(!reorderHappened); + } + + return nErrorCount; + } + + +} // namespace + +int Test128BitLoadStoreMultiThreaded() +{ + int nErrorCount = 0; + +// We don't support 128bit atomics on 32-bit x86. +#if !defined(EA_PROCESSOR_X86) + constexpr int kNumThreads = 4; // 2 readers and 2 writers. + EA::Thread::Thread threads[kNumThreads]; + Test128BitLoadStoreData data; + + for (int i = 0; (i + 1) < kNumThreads; i += 2) + { + threads[i].Begin(RelaxedLoadAndCheck, static_cast(&data)); + threads[i + 1].Begin(RelaxedStore, static_cast(&data)); + } + + data.mStartSem.Post(kNumThreads); + + EA::Thread::ThreadSleep(3000); + + data.mShouldStop = true; + + for (auto& t : threads) + { + t.WaitForEnd(); + } + + nErrorCount += data.mFoundTornRead; +#endif + + return nErrorCount; +} + +int TestSequentialConsistency() +{ + int nErrorCount = 0; + + nErrorCount += TestSequentialConsistencyImpl(); + nErrorCount += TestSequentialConsistencyImpl(); + nErrorCount += TestSequentialConsistencyImpl(); + +// We don't support 128bit atomics on 32-bit x86. +#if !defined(EA_PROCESSOR_X86) + nErrorCount += TestSequentialConsistencyImpl(); +#endif + + return nErrorCount; +} + +int TestAtomicMultiThreaded() +{ + int nErrorCount = 0; + + nErrorCount += Test128BitLoadStoreMultiThreaded(); + nErrorCount += TestSequentialConsistency(); + + return nErrorCount; +} + +#endif diff --git a/test/source/TestBit.cpp b/test/source/TestBit.cpp new file mode 100644 index 00000000..d5561337 --- /dev/null +++ b/test/source/TestBit.cpp @@ -0,0 +1,144 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#include "EASTLTest.h" +#include + +#if defined(EA_COMPILER_CPP20_ENABLED) +template +int TestHasSingleBit() +{ + int nErrorCount = 0; + + VERIFY(eastl::has_single_bit(T(0)) == false); + VERIFY(eastl::has_single_bit(T(1)) == true); + VERIFY(eastl::has_single_bit(T(2)) == true); + VERIFY(eastl::has_single_bit(T(3)) == false); + + VERIFY(eastl::has_single_bit(eastl::numeric_limits::min()) == false); + VERIFY(eastl::has_single_bit(eastl::numeric_limits::max()) == false); + + for (int i = 4; i < eastl::numeric_limits::digits; i++) + { + T power_of_two = static_cast(T(1U) << i); + VERIFY(eastl::has_single_bit(power_of_two)); + VERIFY(eastl::has_single_bit(static_cast(power_of_two - 1)) == false); + } + + return nErrorCount; +} + +template +static int TestBitCeil() +{ + int nErrorCount = 0; + + VERIFY(eastl::bit_ceil(T(0)) == T(1)); + VERIFY(eastl::bit_ceil(T(1)) == T(1)); + VERIFY(eastl::bit_ceil(T(2)) == T(2)); + VERIFY(eastl::bit_ceil(T(3)) == T(4)); + + EA_CONSTEXPR auto DIGITS = eastl::numeric_limits::digits; + EA_CONSTEXPR auto MIN = eastl::numeric_limits::min(); + EA_CONSTEXPR auto MAX = static_cast(T(1) << (DIGITS - 1)); + + VERIFY(eastl::bit_ceil(MAX) == MAX); + VERIFY(eastl::bit_ceil(static_cast(MAX - 1)) == MAX); + VERIFY(eastl::bit_ceil(MIN) == T(1)); + + for (int i = 4; i < eastl::numeric_limits::digits; i++) + { + T power_of_two = static_cast(T(1U) << i); + VERIFY(eastl::bit_ceil(power_of_two) == power_of_two); + VERIFY(eastl::bit_ceil(static_cast(power_of_two - 1)) == power_of_two); + } + + return nErrorCount; +} + +template +static int TestBitFloor() +{ + int nErrorCount = 0; + VERIFY(eastl::bit_floor(T(0)) == T(0)); + VERIFY(eastl::bit_floor(T(1)) == T(1)); + VERIFY(eastl::bit_floor(T(2)) == T(2)); + VERIFY(eastl::bit_floor(T(3)) == T(2)); + + EA_CONSTEXPR auto DIGITS = eastl::numeric_limits::digits; + EA_CONSTEXPR auto MIN = eastl::numeric_limits::min(); + EA_CONSTEXPR auto MAX = eastl::numeric_limits::max(); + + VERIFY(eastl::bit_floor(MAX) == T(1) << (DIGITS - 1)); + VERIFY(eastl::bit_floor(MIN) == T(0)); + + for (int i = 4; i < eastl::numeric_limits::digits; i++) + { + T power_of_two = static_cast(T(1U) << i); + VERIFY(eastl::bit_floor(power_of_two) == power_of_two); + VERIFY(eastl::bit_floor(static_cast(power_of_two + 1)) == power_of_two); + } + return nErrorCount; +} + +template +static int TestBitWidth() +{ + int nErrorCount = 0; + + VERIFY(eastl::bit_width(T(0)) == T(0)); + VERIFY(eastl::bit_width(T(1)) == T(1)); + VERIFY(eastl::bit_width(T(2)) == T(2)); + VERIFY(eastl::bit_width(T(3)) == T(2)); + + EA_CONSTEXPR auto DIGITS = eastl::numeric_limits::digits; + EA_CONSTEXPR auto MIN = eastl::numeric_limits::min(); + EA_CONSTEXPR auto MAX = eastl::numeric_limits::max(); + + VERIFY(eastl::bit_width(MIN) == 0); + VERIFY(eastl::bit_width(MAX) == DIGITS); + + for (int i = 4; i < eastl::numeric_limits::digits; i++) + { + T power_of_two = static_cast(T(1U) << i); + VERIFY(eastl::bit_width(power_of_two) == static_cast(i + 1)); + } + + return nErrorCount; +} + +/////////////////////////////////////////////////////////////////////////////// +// TestBit +// +int TestBit() +{ + int nErrorCount = 0; + + nErrorCount += TestHasSingleBit(); + nErrorCount += TestHasSingleBit(); + nErrorCount += TestHasSingleBit(); + nErrorCount += TestHasSingleBit(); + nErrorCount += TestHasSingleBit(); + + nErrorCount += TestBitCeil(); + nErrorCount += TestBitCeil(); + nErrorCount += TestBitCeil(); + nErrorCount += TestBitCeil(); + nErrorCount += TestBitCeil(); + + nErrorCount += TestBitFloor(); + nErrorCount += TestBitFloor(); + nErrorCount += TestBitFloor(); + nErrorCount += TestBitFloor(); + nErrorCount += TestBitFloor(); + + nErrorCount += TestBitWidth(); + nErrorCount += TestBitWidth(); + nErrorCount += TestBitWidth(); + nErrorCount += TestBitWidth(); + nErrorCount += TestBitWidth(); + + return nErrorCount; +} +#endif diff --git a/test/source/TestBitset.cpp b/test/source/TestBitset.cpp index ef97489b..143de382 100644 --- a/test/source/TestBitset.cpp +++ b/test/source/TestBitset.cpp @@ -6,6 +6,7 @@ #include "EASTLTest.h" #include #include +#include #ifdef _MSC_VER #pragma warning(push, 0) @@ -63,57 +64,164 @@ using namespace eastl; #endif #endif +template +int TestGetFirstBit() +{ + int nErrorCount = 0; -int TestBitset() + EATEST_VERIFY(GetFirstBit((UInt) 0) == sizeof(UInt) * CHAR_BIT); + // EATEST_VERIFY(countr_zero((UInt) 0) == sizeof(UInt) * CHAR_BIT); + + for (uint32_t i = 0; i < sizeof(UInt) * CHAR_BIT; ++i) + { + UInt x = ((UInt) 1 << i) | (UInt(1) << (sizeof(UInt) * CHAR_BIT - 1)); + EATEST_VERIFY(GetFirstBit(x) == i); + //EATEST_VERIFY(GetFirstBit(x) == countr_zero(x)); + } + + return nErrorCount; +} + +template +int TestGetLastBit() { int nErrorCount = 0; + EATEST_VERIFY(GetLastBit((UInt) 0) == sizeof(UInt) * CHAR_BIT); +#if defined(EA_COMPILER_CPP20_ENABLED) + EATEST_VERIFY(bit_width((UInt) 0) == 0); +#endif + + for (uint32_t i = 0; i < sizeof(UInt) * CHAR_BIT; ++i) { - // bitset<0> tests - #if !defined(__GNUC__) || (__GNUC__ >= 3) // GCC before v3.0 can't handle our bitset<0>. - bitset<0> b0(0x10101010); - EATEST_VERIFY(b0.count() == 0); - EATEST_VERIFY(b0.to_ulong() == 0x00000000); - EATEST_VERIFY(b0.to_uint32() == 0x00000000); - EATEST_VERIFY(b0.to_uint64() == 0x00000000); - - b0.flip(); - EATEST_VERIFY(b0.count() == 0); - EATEST_VERIFY(b0.to_ulong() == 0x00000000); - EATEST_VERIFY(b0.to_uint32() == 0x00000000); - EATEST_VERIFY(b0.to_uint64() == 0x00000000); - - b0 <<= 1; - EATEST_VERIFY(b0.count() == 0); - EATEST_VERIFY(b0.to_ulong() == 0x00000000); - EATEST_VERIFY(b0.to_uint32() == 0x00000000); - EATEST_VERIFY(b0.to_uint64() == 0x00000000); - - // Disabled because currently bitset<0> instances can't be modified without triggering asserts. - //b0.from_uint32(0x10101010); - //EATEST_VERIFY(b0.to_uint32() == 0x00000000); - //b0.from_uint64(UINT64_C(0x1010101010101010)); - //EATEST_VERIFY(b0.to_uint64() == UINT64_C(0x0000000000000000)); - #endif - - // bitset<8> tests - bitset<8> b8(0x10101010); + UInt x = ((UInt)1 << i) | UInt(1); + EATEST_VERIFY(GetLastBit(x) == i); +#if defined(EA_COMPILER_CPP20_ENABLED) + EATEST_VERIFY(GetLastBit(x) == ((uint32_t) bit_width(x) - 1)); +#endif + } + + return nErrorCount; +} + +template +int VerifyBitsetConversionThrows(const bitset& bs, UInt(*convert)(const bitset&), UInt truncatedValue) { + int nErrorCount = 0; + +#if EASTL_EXCEPTIONS_ENABLED + try { + convert(bs); + EATEST_VERIFY(false); + } + catch (std::overflow_error&) + { + EATEST_VERIFY(true); // This pathway should be taken. + } + catch (...) + { + EATEST_VERIFY(false); + } +#else + EATEST_VERIFY(convert(bs) == truncatedValue); +#endif + + return nErrorCount; +} + +template +int TestBitsetWithWord() +{ + int nErrorCount = 0; + + auto verifyToUint32Throws = [&nErrorCount](const auto& bs, uint32_t truncatedValue) { + uint32_t(*convert)(const decltype(bs)&) = [](const auto& bs) { return bs.to_uint32_no_assert_convertible(); }; + nErrorCount += VerifyBitsetConversionThrows(bs, convert, truncatedValue); + }; + + auto verifyToUint64Throws = [&nErrorCount](const auto& bs, uint64_t truncatedValue) { + uint64_t(*convert)(const decltype(bs)&) = [](const auto& bs) { return bs.to_uint64_no_assert_convertible(); }; + nErrorCount += VerifyBitsetConversionThrows(bs, convert, truncatedValue); + }; + + auto verifyToUlongThrowsIf32bit = [&nErrorCount](const auto& bs, unsigned long truncatedValue) { + if constexpr (sizeof(unsigned long) < 8) + { + unsigned long(*convert)(const decltype(bs)&) = [](const auto& bs) { return bs.to_ulong_no_assert_convertible(); }; + nErrorCount += VerifyBitsetConversionThrows(bs, convert, truncatedValue); + } + else + { + EATEST_VERIFY(bs.to_ulong_no_assert_convertible() == truncatedValue); + } + }; + + { + // bitset<0, WordType> tests +#if !defined(__GNUC__) || (__GNUC__ >= 3) // GCC before v3.0 can't handle our bitset<0, WordType>. + bitset<0, WordType> b0(0x10101010); + EATEST_VERIFY(b0.count() == 0); + EATEST_VERIFY(b0.to_ulong_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint32_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint64_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint64_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.as_uint64() == 0x00000000); + EATEST_VERIFY(b0.template as_uint() == 0x00000000); + + b0.flip(); + EATEST_VERIFY(b0.count() == 0); + EATEST_VERIFY(b0.to_ulong_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint32_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint64_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint64_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.as_uint64() == 0x00000000); + EATEST_VERIFY(b0.template as_uint() == 0x00000000); + + b0 <<= 1; + EATEST_VERIFY(b0.count() == 0); + EATEST_VERIFY(b0.to_ulong_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint32_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint64_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.to_uint64_assert_convertible() == 0x00000000); + EATEST_VERIFY(b0.as_uint64() == 0x00000000); + EATEST_VERIFY(b0.template as_uint() == 0x00000000); + + b0 = 0x10101010; + EATEST_VERIFY(b0.to_uint32_assert_convertible() == 0x00000000); +#if EA_IS_ENABLED(EASTL_DEPRECATIONS_FOR_2024_SEPT) + b0 = UINT64_C(0x1010101010101010); +#else + b0.from_uint64(UINT64_C(0x1010101010101010)); +#endif + EATEST_VERIFY(b0.to_uint64_assert_convertible() == UINT64_C(0x0000000000000000)); +#endif + +// bitset<8, WordType> tests + bitset<8, WordType> b8(0x10101010); EATEST_VERIFY(b8.count() == 1); - EATEST_VERIFY(b8.to_ulong() == 0x00000010); - EATEST_VERIFY(b8.to_uint32() == 0x00000010); - EATEST_VERIFY(b8.to_uint64() == 0x00000010); + EATEST_VERIFY(b8.to_ulong_assert_convertible() == 0x00000010); + EATEST_VERIFY(b8.to_uint32_assert_convertible() == 0x00000010); + EATEST_VERIFY(b8.to_uint64_assert_convertible() == 0x00000010); + EATEST_VERIFY(b8.to_uint64_assert_convertible() == 0x00000010); + EATEST_VERIFY(b8.as_uint64() == 0x00000010); + EATEST_VERIFY(b8.template as_uint() == 0x00000010); b8.flip(); EATEST_VERIFY(b8.count() == 7); - EATEST_VERIFY(b8.to_ulong() == 0x000000ef); - EATEST_VERIFY(b8.to_uint32() == 0x000000ef); - EATEST_VERIFY(b8.to_uint64() == 0x000000ef); + EATEST_VERIFY(b8.to_ulong_assert_convertible() == 0x000000ef); + EATEST_VERIFY(b8.to_uint32_assert_convertible() == 0x000000ef); + EATEST_VERIFY(b8.to_uint64_assert_convertible() == 0x000000ef); + EATEST_VERIFY(b8.to_uint64_assert_convertible() == 0x000000ef); + EATEST_VERIFY(b8.as_uint64() == 0x000000ef); + EATEST_VERIFY(b8.template as_uint() == 0x000000ef); b8 <<= 1; EATEST_VERIFY(b8.count() == 6); - EATEST_VERIFY(b8.to_ulong() == 0x000000de); - EATEST_VERIFY(b8.to_uint32() == 0x000000de); - EATEST_VERIFY(b8.to_uint64() == 0x000000de); + EATEST_VERIFY(b8.to_ulong_assert_convertible() == 0x000000de); + EATEST_VERIFY(b8.to_uint32_assert_convertible() == 0x000000de); + EATEST_VERIFY(b8.to_uint64_assert_convertible() == 0x000000de); + EATEST_VERIFY(b8.to_uint64_assert_convertible() == 0x000000de); + EATEST_VERIFY(b8.as_uint64() == 0x000000de); + EATEST_VERIFY(b8.template as_uint() == 0x000000de); b8.reset(); b8.flip(); @@ -125,31 +233,40 @@ int TestBitset() b8 >>= 65; EATEST_VERIFY(b8.count() == 0); - b8.from_uint32(0x10101010); - EATEST_VERIFY(b8.to_uint32() == 0x00000010); - b8.from_uint64(UINT64_C(0x0000000000000010)); - EATEST_VERIFY(b8.to_uint64() == UINT64_C(0x0000000000000010)); + b8 = 0x10101010; + EATEST_VERIFY(b8.to_uint32_assert_convertible() == 0x00000010); + b8 = UINT64_C(0x0000000000000010); + EATEST_VERIFY(b8.to_uint64_assert_convertible() == UINT64_C(0x0000000000000010)); - // bitset<16> tests - bitset<16> b16(0x10101010); + // bitset<16, WordType> tests + bitset<16, WordType> b16(0x10101010); EATEST_VERIFY(b16.count() == 2); - EATEST_VERIFY(b16.to_ulong() == 0x00001010); - EATEST_VERIFY(b16.to_uint32() == 0x00001010); - EATEST_VERIFY(b16.to_uint64() == 0x00001010); + EATEST_VERIFY(b16.to_ulong_assert_convertible() == 0x00001010); + EATEST_VERIFY(b16.to_uint32_assert_convertible() == 0x00001010); + EATEST_VERIFY(b16.to_uint64_assert_convertible() == 0x00001010); + EATEST_VERIFY(b16.to_uint64_assert_convertible() == 0x00001010); + EATEST_VERIFY(b16.as_uint64() == 0x00001010); + EATEST_VERIFY(b16.template as_uint() == 0x00001010); b16.flip(); EATEST_VERIFY(b16.count() == 14); - EATEST_VERIFY(b16.to_ulong() == 0x0000efef); - EATEST_VERIFY(b16.to_uint32() == 0x0000efef); - EATEST_VERIFY(b16.to_uint64() == 0x0000efef); + EATEST_VERIFY(b16.to_ulong_assert_convertible() == 0x0000efef); + EATEST_VERIFY(b16.to_uint32_assert_convertible() == 0x0000efef); + EATEST_VERIFY(b16.to_uint64_assert_convertible() == 0x0000efef); + EATEST_VERIFY(b16.to_uint64_assert_convertible() == 0x0000efef); + EATEST_VERIFY(b16.as_uint64() == 0x0000efef); + EATEST_VERIFY(b16.template as_uint() == 0x0000efef); b16 <<= 1; EATEST_VERIFY(b16.count() == 13); - EATEST_VERIFY(b16.to_ulong() == 0x0000dfde); - EATEST_VERIFY(b16.to_uint32() == 0x0000dfde); - EATEST_VERIFY(b16.to_uint64() == 0x0000dfde); + EATEST_VERIFY(b16.to_ulong_assert_convertible() == 0x0000dfde); + EATEST_VERIFY(b16.to_uint32_assert_convertible() == 0x0000dfde); + EATEST_VERIFY(b16.to_uint64_assert_convertible() == 0x0000dfde); + EATEST_VERIFY(b16.to_uint64_assert_convertible() == 0x0000dfde); + EATEST_VERIFY(b16.as_uint64() == 0x0000dfde); + EATEST_VERIFY(b16.template as_uint() == 0x0000dfde); b16.reset(); b16.flip(); @@ -161,31 +278,40 @@ int TestBitset() b16 >>= 65; EATEST_VERIFY(b16.count() == 0); - b16.from_uint32(0x10101010); - EATEST_VERIFY(b16.to_uint32() == 0x00001010); - b16.from_uint64(UINT64_C(0x0000000000001010)); - EATEST_VERIFY(b16.to_uint64() == UINT64_C(0x0000000000001010)); + b16 = 0x10101010; + EATEST_VERIFY(b16.to_uint32_assert_convertible() == 0x00001010); + b16 = UINT64_C(0x0000000000001010); + EATEST_VERIFY(b16.to_uint64_assert_convertible() == UINT64_C(0x0000000000001010)); - // bitset<32> tests - bitset<32> b32(0x10101010); + // bitset<32, WordType> tests + bitset<32, WordType> b32(0x10101010); EATEST_VERIFY(b32.count() == 4); - EATEST_VERIFY(b32.to_ulong() == 0x10101010); - EATEST_VERIFY(b32.to_uint32() == 0x10101010); - EATEST_VERIFY(b32.to_uint64() == 0x10101010); + EATEST_VERIFY(b32.to_ulong_assert_convertible() == 0x10101010); + EATEST_VERIFY(b32.to_uint32_assert_convertible() == 0x10101010); + EATEST_VERIFY(b32.to_uint64_assert_convertible() == 0x10101010); + EATEST_VERIFY(b32.to_uint64_assert_convertible() == 0x10101010); + EATEST_VERIFY(b32.as_uint64() == 0x10101010); + EATEST_VERIFY(b32.template as_uint() == 0x10101010); b32.flip(); EATEST_VERIFY(b32.count() == 28); - EATEST_VERIFY(b32.to_ulong() == 0xefefefef); - EATEST_VERIFY(b32.to_uint32() == 0xefefefef); - EATEST_VERIFY(b32.to_uint64() == 0xefefefef); + EATEST_VERIFY(b32.to_ulong_assert_convertible() == 0xefefefef); + EATEST_VERIFY(b32.to_uint32_assert_convertible() == 0xefefefef); + EATEST_VERIFY(b32.to_uint64_assert_convertible() == 0xefefefef); + EATEST_VERIFY(b32.to_uint64_assert_convertible() == 0xefefefef); + EATEST_VERIFY(b32.as_uint64() == 0xefefefef); + EATEST_VERIFY(b32.template as_uint() == 0xefefefef); b32 <<= 1; EATEST_VERIFY(b32.count() == 27); - EATEST_VERIFY(b32.to_ulong() == 0xdfdfdfde); - EATEST_VERIFY(b32.to_uint32() == 0xdfdfdfde); - EATEST_VERIFY(b32.to_uint64() == 0xdfdfdfde); + EATEST_VERIFY(b32.to_ulong_assert_convertible() == 0xdfdfdfde); + EATEST_VERIFY(b32.to_uint32_assert_convertible() == 0xdfdfdfde); + EATEST_VERIFY(b32.to_uint64_assert_convertible() == 0xdfdfdfde); + EATEST_VERIFY(b32.to_uint64_assert_convertible() == 0xdfdfdfde); + EATEST_VERIFY(b32.as_uint64() == 0xdfdfdfde); + EATEST_VERIFY(b32.template as_uint() == 0xdfdfdfde); b32.reset(); b32.flip(); @@ -197,88 +323,53 @@ int TestBitset() b32 >>= 65; EATEST_VERIFY(b32.count() == 0); - b32.from_uint32(0x10101010); - EATEST_VERIFY(b32.to_uint32() == 0x10101010); - b32.from_uint64(UINT64_C(0x0000000010101010)); - EATEST_VERIFY(b32.to_uint64() == UINT64_C(0x0000000010101010)); - - + b32 = 0x10101010; + EATEST_VERIFY(b32.to_uint32_assert_convertible() == 0x10101010); + b32 = UINT64_C(0x0000000010101010); + EATEST_VERIFY(b32.to_uint64_assert_convertible() == UINT64_C(0x0000000010101010)); - // bitset<64> tests - bitset<64> b64(0x10101010); // b64 => 00000000 00000000 00000000 00000000 00010000 00010000 00010000 00010000 + // bitset<64, WordType> tests + bitset<64, WordType> b64(0x10101010); // b64 => 00000000 00000000 00000000 00000000 00010000 00010000 00010000 00010000 EATEST_VERIFY(b64.count() == 4); - EATEST_VERIFY(b64.to_ulong() == 0x10101010); - EATEST_VERIFY(b64.to_uint32() == 0x10101010); - EATEST_VERIFY(b64.to_uint64() == 0x10101010); + EATEST_VERIFY(b64.to_ulong_assert_convertible() == 0x10101010); + EATEST_VERIFY(b64.to_uint32_assert_convertible() == 0x10101010); + EATEST_VERIFY(b64.to_uint64_assert_convertible() == 0x10101010); + EATEST_VERIFY(b64.to_uint64_assert_convertible() == 0x10101010); + // EATEST_VERIFY(b64.as_uint32() == 0x10101010); // compile error: cannot represent the entire bitset as a uint32_t. + EATEST_VERIFY(b64.as_uint64() == 0x10101010); + EATEST_VERIFY(b64.template as_uint() == 0x10101010); b64.flip(); // b64 => 11111111 11111111 11111111 11111111 11101111 11101111 11101111 11101111 EATEST_VERIFY(b64.count() == 60); - if(sizeof(unsigned long) + nErrorCount - nErrorCount == 4) // We have this no-op math here in order to avoid compiler warnings about constant expressions. - { - #if EASTL_EXCEPTIONS_ENABLED - try { - EATEST_VERIFY(b64.to_ulong() == 0xefefefef); - EATEST_VERIFY(false); - } - catch(std::overflow_error&) - { - EATEST_VERIFY(true); // This pathway should be taken. - } - catch(...) - { - EATEST_VERIFY(false); - } - #else - EATEST_VERIFY(b64.to_ulong() == 0xefefefef); - #endif - } - else - { - EATEST_VERIFY(b64.to_ulong() == (unsigned long)UINT64_C(0xffffffffefefefef)); - } + verifyToUlongThrowsIf32bit(b64, static_cast(0xffffffffefefefeful)); + verifyToUint32Throws(b64, 0xefefefef); + EATEST_VERIFY(b64.to_uint64_assert_convertible() == 0xffffffffefefefefull); + EATEST_VERIFY(b64.as_uint64() == 0xffffffffefefefefull); + EATEST_VERIFY(b64.template as_uint() == 0xffffffffefefefefull); b64 <<= 1; // b64 => 11111111 11111111 11111111 11111111 11011111 11011111 11011111 11011110 EATEST_VERIFY(b64.count() == 59); - if(sizeof(unsigned long) + nErrorCount - nErrorCount == 4) - { - #if !EASTL_EXCEPTIONS_ENABLED - EATEST_VERIFY(b64.to_ulong() == 0xdfdfdfde); - #endif - } - else - { - EATEST_VERIFY(b64.to_ulong() == (unsigned long)UINT64_C(0xffffffffdfdfdfde)); - } + verifyToUlongThrowsIf32bit(b64, static_cast(0xffffffffdfdfdfdeul)); + verifyToUint32Throws(b64, 0xdfdfdfde); + EATEST_VERIFY(b64.to_uint64_assert_convertible() == 0xffffffffdfdfdfdeull); + EATEST_VERIFY(b64.as_uint64() == 0xffffffffdfdfdfdeull); + EATEST_VERIFY(b64.template as_uint() == 0xffffffffdfdfdfdeull); b64.reset(); // b64 => 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 EATEST_VERIFY(b64.count() == 0); - EATEST_VERIFY(b64.to_ulong() == 0); + EATEST_VERIFY(b64.to_ulong_assert_convertible() == 0); b64 <<= 1; // b64 => 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 EATEST_VERIFY(b64.count() == 0); - EATEST_VERIFY(b64.to_ulong() == 0); + EATEST_VERIFY(b64.to_ulong_assert_convertible() == 0); b64.flip(); // b64 => 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 EATEST_VERIFY(b64.count() == 64); - if(sizeof(unsigned long) + nErrorCount - nErrorCount == 4) - { - #if !EASTL_EXCEPTIONS_ENABLED - EATEST_VERIFY(b64.to_ulong() == 0xffffffff); - #endif - } - else - EATEST_VERIFY(b64.to_ulong() == (unsigned long)UINT64_C(0xffffffffffffffff)); + verifyToUlongThrowsIf32bit(b64, static_cast(0xfffffffffffffffful)); b64 <<= 1; // b64 => 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111110 EATEST_VERIFY(b64.count() == 63); - if(sizeof(unsigned long) + nErrorCount - nErrorCount == 4) - { - #if !EASTL_EXCEPTIONS_ENABLED - EATEST_VERIFY(b64.to_ulong() == 0xfffffffe); - #endif - } - else - EATEST_VERIFY(b64.to_ulong() == (unsigned long)UINT64_C(0xfffffffffffffffe)); + verifyToUlongThrowsIf32bit(b64, static_cast(0xfffffffffffffffeul)); b64.reset(); b64.flip(); @@ -290,70 +381,74 @@ int TestBitset() b64 >>= 65; EATEST_VERIFY(b64.count() == 0); - b64.from_uint32(0x10101010); - EATEST_VERIFY(b64.to_uint32() == 0x10101010); + b64 = 0x10101010; + EATEST_VERIFY(b64.to_uint32_assert_convertible() == 0x10101010); +#if EA_IS_ENABLED(EASTL_DEPRECATIONS_FOR_2024_SEPT) + b64 = UINT64_C(0x1010101010101010); +#else b64.from_uint64(UINT64_C(0x1010101010101010)); - EATEST_VERIFY(b64.to_uint64() == UINT64_C(0x1010101010101010)); +#endif + EATEST_VERIFY(b64.to_uint64_assert_convertible() == UINT64_C(0x1010101010101010)); } { - bitset<1> b1; - bitset<1> b1A(1); + bitset<1, WordType> b1; + bitset<1, WordType> b1A(1); EATEST_VERIFY(b1.size() == 1); EATEST_VERIFY(b1.any() == false); EATEST_VERIFY(b1.all() == false); EATEST_VERIFY(b1.none() == true); - EATEST_VERIFY(b1.to_ulong() == 0); + EATEST_VERIFY(b1.to_ulong_assert_convertible() == 0); EATEST_VERIFY(b1A.any() == true); EATEST_VERIFY(b1A.all() == true); EATEST_VERIFY(b1A.none() == false); - EATEST_VERIFY(b1A.to_ulong() == 1); - EATEST_VERIFY(b1A.to_uint32() == 1); - EATEST_VERIFY(b1A.to_uint64() == 1); + EATEST_VERIFY(b1A.to_ulong_assert_convertible() == 1); + EATEST_VERIFY(b1A.to_uint32_assert_convertible() == 1); + EATEST_VERIFY(b1A.to_uint64_assert_convertible() == 1); - bitset<33> b33; - bitset<33> b33A(1); + bitset<33, WordType> b33; + bitset<33, WordType> b33A(1); EATEST_VERIFY(b33.size() == 33); EATEST_VERIFY(b33.any() == false); EATEST_VERIFY(b33.all() == false); EATEST_VERIFY(b33.none() == true); - EATEST_VERIFY(b33.to_ulong() == 0); + EATEST_VERIFY(b33.to_ulong_assert_convertible() == 0); EATEST_VERIFY(b33A.any() == true); EATEST_VERIFY(b33A.all() == false); EATEST_VERIFY(b33A.none() == false); - EATEST_VERIFY(b33A.to_ulong() == 1); + EATEST_VERIFY(b33A.to_ulong_assert_convertible() == 1); - bitset<65> b65; - bitset<65> b65A(1); + bitset<65, WordType> b65; + bitset<65, WordType> b65A(1); EATEST_VERIFY(b65.size() == 65); EATEST_VERIFY(b65.any() == false); EATEST_VERIFY(b65.all() == false); EATEST_VERIFY(b65.none() == true); - EATEST_VERIFY(b65.to_ulong() == 0); + EATEST_VERIFY(b65.to_ulong_assert_convertible() == 0); EATEST_VERIFY(b65A.any() == true); EATEST_VERIFY(b65A.all() == false); EATEST_VERIFY(b65A.none() == false); - EATEST_VERIFY(b65A.to_ulong() == 1); + EATEST_VERIFY(b65A.to_ulong_assert_convertible() == 1); - bitset<129> b129; - bitset<129> b129A(1); + bitset<129, WordType> b129; + bitset<129, WordType> b129A(1); EATEST_VERIFY(b129.size() == 129); EATEST_VERIFY(b129.any() == false); EATEST_VERIFY(b129.all() == false); EATEST_VERIFY(b129.none() == true); - EATEST_VERIFY(b129.to_ulong() == 0); + EATEST_VERIFY(b129.to_ulong_assert_convertible() == 0); EATEST_VERIFY(b129A.any() == true); EATEST_VERIFY(b129A.all() == false); EATEST_VERIFY(b129A.none() == false); - EATEST_VERIFY(b129A.to_ulong() == 1); + EATEST_VERIFY(b129A.to_ulong_assert_convertible() == 1); // operator[], data, test, to_ulong, count @@ -391,7 +486,7 @@ int TestBitset() EATEST_VERIFY(b129.test(128) == true); EATEST_VERIFY(b129.count() == 4); - bitset<1>::word_type* pWordArray; + typename bitset<1, WordType>::word_type* pWordArray; pWordArray = b1.data(); EATEST_VERIFY(pWordArray != NULL); @@ -403,7 +498,7 @@ int TestBitset() EATEST_VERIFY(pWordArray != NULL); - // bitset<1> set, reset, flip, ~ + // bitset<1, WordType> set, reset, flip, ~ b1.reset(); EATEST_VERIFY(b1.count() == 0); @@ -425,12 +520,12 @@ int TestBitset() b1.flip(0); EATEST_VERIFY(b1[0] == true); - bitset<1> b1Not = ~b1; + bitset<1, WordType> b1Not = ~b1; EATEST_VERIFY(b1[0] == true); EATEST_VERIFY(b1Not[0] == false); - // bitset<33> set, reset, flip, ~ + // bitset<33, WordType> set, reset, flip, ~ b33.reset(); EATEST_VERIFY(b33.count() == 0); @@ -459,14 +554,14 @@ int TestBitset() EATEST_VERIFY(b33[0] == true); EATEST_VERIFY(b33[32] == true); - bitset<33> b33Not(~b33); + bitset<33, WordType> b33Not(~b33); EATEST_VERIFY(b33[0] == true); EATEST_VERIFY(b33[32] == true); EATEST_VERIFY(b33Not[0] == false); EATEST_VERIFY(b33Not[32] == false); - // bitset<65> set, reset, flip, ~ + // bitset<65, WordType> set, reset, flip, ~ b65.reset(); EATEST_VERIFY(b65.count() == 0); EATEST_VERIFY(!b65.all()); @@ -506,7 +601,7 @@ int TestBitset() EATEST_VERIFY(b65[32] == true); EATEST_VERIFY(b65[64] == true); - bitset<65> b65Not(~b65); + bitset<65, WordType> b65Not(~b65); EATEST_VERIFY(b65[0] == true); EATEST_VERIFY(b65[32] == true); EATEST_VERIFY(b65[64] == true); @@ -515,7 +610,7 @@ int TestBitset() EATEST_VERIFY(b65Not[64] == false); - // bitset<65> set, reset, flip, ~ + // bitset<65, WordType> set, reset, flip, ~ b129.reset(); EATEST_VERIFY(b129.count() == 0); @@ -558,7 +653,7 @@ int TestBitset() EATEST_VERIFY(b129[64] == true); EATEST_VERIFY(b129[128] == true); - bitset<129> b129Not(~b129); + bitset<129, WordType> b129Not(~b129); EATEST_VERIFY(b129[0] == true); EATEST_VERIFY(b129[32] == true); EATEST_VERIFY(b129[64] == true); @@ -570,24 +665,24 @@ int TestBitset() // operator ==, != - bitset<1> b1Equal(b1); + bitset<1, WordType> b1Equal(b1); EATEST_VERIFY(b1Equal == b1); EATEST_VERIFY(b1Equal != b1Not); - bitset<33> b33Equal(b33); + bitset<33, WordType> b33Equal(b33); EATEST_VERIFY(b33Equal == b33); EATEST_VERIFY(b33Equal != b33Not); - bitset<65> b65Equal(b65); + bitset<65, WordType> b65Equal(b65); EATEST_VERIFY(b65Equal == b65); EATEST_VERIFY(b65Equal != b65Not); - bitset<129> b129Equal(b129); + bitset<129, WordType> b129Equal(b129); EATEST_VERIFY(b129Equal == b129); EATEST_VERIFY(b129Equal != b129Not); - // bitset<1> operator<<=, operator>>=, operator<<, operator>> + // bitset<1, WordType> operator<<=, operator>>=, operator<<, operator>> b1.reset(); b1[0] = true; @@ -643,7 +738,7 @@ int TestBitset() EATEST_VERIFY(b1.none()); - // bitset<33> operator<<=, operator>>=, operator<<, operator>> + // bitset<33, WordType> operator<<=, operator>>=, operator<<, operator>> b33.reset(); b33[0] = true; @@ -706,7 +801,7 @@ int TestBitset() EATEST_VERIFY(b33.none()); - // bitset<65> operator<<=, operator>>=, operator<<, operator>> + // bitset<65, WordType> operator<<=, operator>>=, operator<<, operator>> b65.reset(); b65[0] = true; @@ -771,7 +866,7 @@ int TestBitset() EATEST_VERIFY(b65.count() == 0); - // bitset<129> operator<<=, operator>>=, operator<<, operator>> + // bitset<129, WordType> operator<<=, operator>>=, operator<<, operator>> b129.reset(); b129[0] = true; @@ -909,8 +1004,8 @@ int TestBitset() } { // Test bitset::reference - bitset<65> b65; - bitset<65>::reference r = b65[33]; + bitset<65, WordType> b65; + typename bitset<65, WordType>::reference r = b65[33]; r = true; EATEST_VERIFY(r == true); @@ -919,8 +1014,8 @@ int TestBitset() { // Test find_first, find_next size_t i, j; - // bitset<1> - bitset<1> b1; + // bitset<1, WordType> + bitset<1, WordType> b1; i = b1.find_first(); EATEST_VERIFY(i == b1.kSize); @@ -935,8 +1030,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 1); - // bitset<7> - bitset<7> b7; + // bitset<7, WordType> + bitset<7, WordType> b7; i = b7.find_first(); EATEST_VERIFY(i == b7.kSize); @@ -954,8 +1049,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 7); - // bitset<32> - bitset<32> b32; + // bitset<32, WordType> + bitset<32, WordType> b32; i = b32.find_first(); EATEST_VERIFY(i == b32.kSize); @@ -973,8 +1068,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 32); - // bitset<41> - bitset<41> b41; + // bitset<41, WordType> + bitset<41, WordType> b41; i = b41.find_first(); EATEST_VERIFY(i == b41.kSize); @@ -995,8 +1090,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 41); - // bitset<64> - bitset<64> b64; + // bitset<64, WordType> + bitset<64, WordType> b64; i = b64.find_first(); EATEST_VERIFY(i == b64.kSize); @@ -1017,8 +1112,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 64); - // bitset<79> - bitset<79> b79; + // bitset<79, WordType> + bitset<79, WordType> b79; i = b79.find_first(); EATEST_VERIFY(i == b79.kSize); @@ -1039,8 +1134,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 79); - // bitset<128> - bitset<128> b128; + // bitset<128, WordType> + bitset<128, WordType> b128; i = b128.find_first(); EATEST_VERIFY(i == b128.kSize); @@ -1064,8 +1159,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 128); - // bitset<137> - bitset<137> b137; + // bitset<137, WordType> + bitset<137, WordType> b137; i = b137.find_first(); EATEST_VERIFY(i == b137.kSize); @@ -1099,8 +1194,8 @@ int TestBitset() { // Test find_last, find_prev size_t i, j; - // bitset<1> - bitset<1> b1; + // bitset<1, WordType> + bitset<1, WordType> b1; i = b1.find_last(); EATEST_VERIFY(i == b1.kSize); @@ -1115,8 +1210,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 1); - // bitset<7> - bitset<7> b7; + // bitset<7, WordType> + bitset<7, WordType> b7; i = b7.find_last(); EATEST_VERIFY(i == b7.kSize); @@ -1134,8 +1229,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 7); - // bitset<32> - bitset<32> b32; + // bitset<32, WordType> + bitset<32, WordType> b32; i = b32.find_last(); EATEST_VERIFY(i == b32.kSize); @@ -1153,8 +1248,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 32); - // bitset<41> - bitset<41> b41; + // bitset<41, WordType> + bitset<41, WordType> b41; i = b41.find_last(); EATEST_VERIFY(i == b41.kSize); @@ -1175,8 +1270,8 @@ int TestBitset() ++i; EATEST_VERIFY(i == 41); - // bitset<64> - bitset<64> b64; + // bitset<64, WordType> + bitset<64, WordType> b64; i = b64.find_last(); EATEST_VERIFY(i == b64.kSize); @@ -1192,13 +1287,15 @@ int TestBitset() i = b64.find_prev(i); EATEST_VERIFY(i == b64.kSize); + verifyToUint32Throws(b64, 0x08000001); + b64.set(); for(i = 0, j = b64.find_last(); j != b64.kSize; j = b64.find_prev(j)) ++i; EATEST_VERIFY(i == 64); - // bitset<79> - bitset<79> b79; + // bitset<79, WordType> + bitset<79, WordType> b79; i = b79.find_last(); EATEST_VERIFY(i == b79.kSize); @@ -1214,13 +1311,17 @@ int TestBitset() i = b79.find_prev(i); EATEST_VERIFY(i == b79.kSize); + EATEST_VERIFY(b79.to_uint64_assert_convertible() == 0x0000002008000001); + b79.set(); for(i = 0, j = b79.find_last(); j != b79.kSize; j = b79.find_prev(j)) ++i; EATEST_VERIFY(i == 79); - // bitset<128> - bitset<128> b128; + verifyToUint64Throws(b79, 0xffffffffffffffff); + + // bitset<128, WordType> + bitset<128, WordType> b128; i = b128.find_last(); EATEST_VERIFY(i == b128.kSize); @@ -1239,13 +1340,15 @@ int TestBitset() i = b128.find_prev(i); EATEST_VERIFY(i == b128.kSize); + verifyToUint64Throws(b128, 0x0000002008000001); + b128.set(); for(i = 0, j = b128.find_last(); j != b128.kSize; j = b128.find_prev(j)) ++i; EATEST_VERIFY(i == 128); - // bitset<137> - bitset<137> b137; + // bitset<137, WordType> + bitset<137, WordType> b137; i = b137.find_last(); EATEST_VERIFY(i == b137.kSize); @@ -1274,54 +1377,84 @@ int TestBitset() for(i = 0, j = b137.find_last(); j != b137.kSize; j = b137.find_prev(j)) ++i; EATEST_VERIFY(i == 137); + + // bitset<99, WordType> + bitset<99, WordType> b99; + b99.set(63); + verifyToUint32Throws(b99, 0x0); + EATEST_VERIFY(b99.to_uint64_assert_convertible() == 0x8000000000000000); + verifyToUlongThrowsIf32bit(b99, static_cast(0x8000000000000000ul)); } + // GetFirstBit + { + nErrorCount += TestGetFirstBit(); + nErrorCount += TestGetFirstBit(); + nErrorCount += TestGetFirstBit(); + nErrorCount += TestGetFirstBit(); +#if EASTL_INT128_SUPPORTED + nErrorCount += TestGetFirstBit(); +#endif + } + + // GetLastBit + { + nErrorCount += TestGetLastBit(); + nErrorCount += TestGetLastBit(); + nErrorCount += TestGetLastBit(); + nErrorCount += TestGetLastBit(); +#if EASTL_INT128_SUPPORTED + nErrorCount += TestGetLastBit(); +#endif + } + + return nErrorCount; +} + +int TestBitset() +{ + int nErrorCount = 0; + + nErrorCount += TestBitsetWithWord(); + + nErrorCount += TestBitsetWithWord(); + nErrorCount += TestBitsetWithWord(); + nErrorCount += TestBitsetWithWord(); + nErrorCount += TestBitsetWithWord(); + + nErrorCount += TestBitsetWithWord(); + nErrorCount += TestBitsetWithWord(); + nErrorCount += TestBitsetWithWord(); + nErrorCount += TestBitsetWithWord(); + nErrorCount += TestBitsetWithWord(); + // test BITSET_WORD_COUNT macro { { - typedef eastl::bitset<32, char> bitset_t; + typedef eastl::bitset<32, unsigned char> bitset_t; static_assert(bitset_t::kWordCount == BITSET_WORD_COUNT(bitset_t::kSize, bitset_t::word_type), "bitset failure"); } { - typedef eastl::bitset<32, int> bitset_t; + typedef eastl::bitset<32, unsigned int> bitset_t; static_assert(bitset_t::kWordCount == BITSET_WORD_COUNT(bitset_t::kSize, bitset_t::word_type), "bitset failure"); } { - typedef eastl::bitset<32, int16_t> bitset_t; + typedef eastl::bitset<32, uint16_t> bitset_t; static_assert(bitset_t::kWordCount == BITSET_WORD_COUNT(bitset_t::kSize, bitset_t::word_type), "bitset failure"); } { - typedef eastl::bitset<32, int32_t> bitset_t; + typedef eastl::bitset<32, uint32_t> bitset_t; static_assert(bitset_t::kWordCount == BITSET_WORD_COUNT(bitset_t::kSize, bitset_t::word_type), "bitset failure"); } { - typedef eastl::bitset<128, int64_t> bitset_t; + typedef eastl::bitset<128, uint64_t> bitset_t; static_assert(bitset_t::kWordCount == BITSET_WORD_COUNT(bitset_t::kSize, bitset_t::word_type), "bitset failure"); } { - typedef eastl::bitset<256, int64_t> bitset_t; + typedef eastl::bitset<256, uint64_t> bitset_t; static_assert(bitset_t::kWordCount == BITSET_WORD_COUNT(bitset_t::kSize, bitset_t::word_type), "bitset failure"); } } return nErrorCount; } - - - - - - - - - - - - - - - - - - - diff --git a/test/source/TestConcepts.cpp b/test/source/TestConcepts.cpp new file mode 100644 index 00000000..449eafb1 --- /dev/null +++ b/test/source/TestConcepts.cpp @@ -0,0 +1,126 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "EASTL/unique_ptr.h" +#include "EASTLTest.h" + +struct NoExceptFalseDestructor +{ + ~NoExceptFalseDestructor() noexcept(false) = default; +}; + +// In GCC prior to version 14, the following static_assert fails: +// +// static_assert(!noexcept(std::declval().~NoExceptFalseDestructor()), "bad noexcept!"); +// +// our implementation of some of these concepts depends on that working correctly so we don't +// do these tests in older versions of GCC. Clang handles this properly and on MSVC we use +// __is_nothrow_destructible instead of rolling our own. +#if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION <= 14000) + #define EA_TEST_NoExceptFalseDestructor 0 +#else + #define EA_TEST_NoExceptFalseDestructor 1 +#endif + +struct From; + +struct To +{ + To(const From&) + {} + + To(const int&) // To can be implicitly constructed from int, but int cannot be explicitly converted to To. + {} +}; + +struct From +{ + explicit operator To() const + { + return To{*this}; + } +}; + +struct NotMoveable +{ + NotMoveable() = default; + + NotMoveable(const NotMoveable&) = delete; + NotMoveable& operator=(const NotMoveable&) = delete; + NotMoveable(NotMoveable&&) = delete; + NotMoveable& operator=(NotMoveable&&) = delete; + + ~NotMoveable() = default; +}; + +int TestConcepts() +{ + using namespace eastl; + + int nErrorCount = 0; + + // destructible + { + static_assert(internal::concepts::destructible, "destructible concept failure."); + static_assert(!internal::concepts::destructible, "destructible concept failure."); +#if EA_TEST_NoExceptFalseDestructor + static_assert(!internal::concepts::destructible, "destructible concept failure."); +#endif + } + + // constructible_from + { + static_assert(internal::concepts::constructible_from, "constructible_from concept failure."); + static_assert(internal::concepts::constructible_from, "constructible_from concept failure."); + static_assert(internal::concepts::constructible_from, "constructible_from concept failure."); + static_assert(internal::concepts::constructible_from, "constructible_from concept failure."); + + static_assert(!internal::concepts::constructible_from, "constructible_from concept failure."); + static_assert(!internal::concepts::constructible_from, "constructible_from concept failure."); + static_assert(!internal::concepts::constructible_from, "constructible_from concept failure."); +#if EA_TEST_NoExceptFalseDestructor + static_assert(!internal::concepts::constructible_from, "constructible_from concept failure."); + static_assert(!internal::concepts::constructible_from, "constructible_from concept failure."); +#endif + } + + // constructible_to + { + static_assert(internal::concepts::convertible_to, "convertible_to concept failure."); + static_assert(internal::concepts::convertible_to, "convertible_to concept failure."); + static_assert(internal::concepts::convertible_to, "convertible_to concept failure."); + static_assert(internal::concepts::convertible_to, "convertible_to concept failure."); + + static_assert(!internal::concepts::convertible_to, "convertible_to concept failure."); // No implicit conversion for int. + static_assert(!internal::concepts::convertible_to, "convertible_to concept failure."); + static_assert(!internal::concepts::convertible_to, "convertible_to concept failure."); + } + + // move_constructible + { + static_assert(internal::concepts::move_constructible, "move_constructible concept failure."); + static_assert(internal::concepts::move_constructible>, "move_constructible concept failure."); + +#if EA_TEST_NoExceptFalseDestructor + static_assert(!internal::concepts::move_constructible, "move_constructible concept failure."); +#endif + static_assert(!internal::concepts::move_constructible, "move_constructible concept failure."); + } + + // copy_constructible + { + static_assert(internal::concepts::copy_constructible, "copy_constructible concept failure."); + + static_assert(!internal::concepts::copy_constructible>, "copy_constructible concept failure."); +#if EA_TEST_NoExceptFalseDestructor + static_assert(!internal::concepts::move_constructible, "copy_constructible concept failure."); +#endif + static_assert(!internal::concepts::move_constructible, "copy_constructible concept failure."); + } + + return nErrorCount; +} diff --git a/test/source/TestDeque.cpp b/test/source/TestDeque.cpp index cf324cbf..710633b7 100644 --- a/test/source/TestDeque.cpp +++ b/test/source/TestDeque.cpp @@ -559,14 +559,25 @@ int TestDeque() { { eastl::deque d; - d.emplace_back(MoveAssignable::Create()); - d.emplace_front(MoveAssignable::Create()); + EATEST_VERIFY(d.emplace_back(MoveAssignable::Create()).value == 42); + EATEST_VERIFY(d.emplace_front(MoveAssignable::Create()).value == 42); auto cd = eastl::move(d); EATEST_VERIFY( d.size() == 0); EATEST_VERIFY(cd.size() == 2); } + { + eastl::deque from; + from.emplace_back(MoveAssignable::Create()); + + eastl::deque to; + to = eastl::move(from); + + EATEST_VERIFY(from.empty()); + EATEST_VERIFY(!to.empty()); + } + { // User regression but passing end() to deque::erase is not valid. // Iterator passed to deque::erase but must valid and dereferencable. @@ -581,9 +592,9 @@ int TestDeque() eastl::deque d; // emplace_back - d.emplace_back(MoveAssignable::Create()); - d.emplace_back(MoveAssignable::Create()); - d.emplace_back(MoveAssignable::Create()); + EATEST_VERIFY(d.emplace_back(MoveAssignable::Create()).value == 42); + EATEST_VERIFY(d.emplace_back(MoveAssignable::Create()).value == 42); + EATEST_VERIFY(d.emplace_back(MoveAssignable::Create()).value == 42); // erase d.erase(d.begin()); @@ -614,9 +625,9 @@ int TestDeque() eastl::deque swapped_d; // emplace_front - swapped_d.emplace_front(MoveAssignable::Create()); - swapped_d.emplace_front(MoveAssignable::Create()); - swapped_d.emplace_front(MoveAssignable::Create()); + EATEST_VERIFY(swapped_d.emplace_front(MoveAssignable::Create()).value == 42); + EATEST_VERIFY(swapped_d.emplace_front(MoveAssignable::Create()).value == 42); + EATEST_VERIFY(swapped_d.emplace_front(MoveAssignable::Create()).value == 42); // swap swapped_d.swap(d); @@ -694,13 +705,13 @@ int TestDeque() deque toDequeA; - toDequeA.emplace_back(2, 3, 4); + EATEST_VERIFY(toDequeA.emplace_back(2, 3, 4).mX == (2 + 3 + 4)); EATEST_VERIFY_F((toDequeA.size() == 1) && (toDequeA.back().mX == (2+3+4)) && (TestObject::sTOCtorCount == 1), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.back().mX, (int)TestObject::sTOCtorCount); toDequeA.emplace(toDequeA.begin(), 3, 4, 5); // This is 3 because of how subarray allocation works. EATEST_VERIFY_F((toDequeA.size() == 2) && (toDequeA.front().mX == (3+4+5)) && (TestObject::sTOCtorCount == 3), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.front().mX, (int)TestObject::sTOCtorCount); - toDequeA.emplace_front(6, 7, 8); + EATEST_VERIFY(toDequeA.emplace_front(6, 7, 8).mX == (6 + 7 + 8)); EATEST_VERIFY_F((toDequeA.size() == 3) && (toDequeA.front().mX == (6+7+8)) && (TestObject::sTOCtorCount == 4), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.front().mX, (int)TestObject::sTOCtorCount); @@ -733,26 +744,27 @@ int TestDeque() // because the existing elements of this were allocated by a different allocator and // will be freed in the future with the allocator copied from x. // The test below should work for the case of EASTL_ALLOCATOR_COPY_ENABLED == 0 or 1. - InstanceAllocator::reset_all(); - - InstanceAllocator ia0((uint8_t)0); - InstanceAllocator ia1((uint8_t)1); + { + InstanceAllocator ia0((uint8_t)0); + InstanceAllocator ia1((uint8_t)1); - eastl::deque v0((eastl_size_t)1, (int)0, ia0); - eastl::deque v1((eastl_size_t)1, (int)1, ia1); + eastl::deque v0((eastl_size_t)1, (int)0, ia0); + eastl::deque v1((eastl_size_t)1, (int)1, ia1); - EATEST_VERIFY((v0.front() == 0) && (v1.front() == 1)); - #if EASTL_ALLOCATOR_COPY_ENABLED + EATEST_VERIFY((v0.front() == 0) && (v1.front() == 1)); EATEST_VERIFY(v0.get_allocator() != v1.get_allocator()); - #endif - v0 = v1; - EATEST_VERIFY((v0.front() == 1) && (v1.front() == 1)); - EATEST_VERIFY(InstanceAllocator::mMismatchCount == 0); - EATEST_VERIFY(v0.validate()); - EATEST_VERIFY(v1.validate()); - #if EASTL_ALLOCATOR_COPY_ENABLED - EATEST_VERIFY(v0.get_allocator() == v1.get_allocator()); - #endif + v0 = v1; + EATEST_VERIFY((v0.front() == 1) && (v1.front() == 1)); + EATEST_VERIFY(v0.validate()); + EATEST_VERIFY(v1.validate()); + bool allocatorsEqual = v0.get_allocator() == v1.get_allocator(); + EATEST_VERIFY(allocatorsEqual == (bool)EASTL_ALLOCATOR_COPY_ENABLED); + + // destroying containers to invoke InstanceAllocator::deallocate() checks + } + + EATEST_VERIFY_MSG(InstanceAllocator::mMismatchCount == 0, "Container elements should be deallocated by the allocator that allocated it."); + InstanceAllocator::reset_all(); } @@ -881,9 +893,9 @@ int TestDeque() eastl::deque d; - d.emplace_back(new int(1)); - d.emplace_back(new int(2)); - d.emplace_back(new int(3)); + EATEST_VERIFY(*d.emplace_back(new int(1)).ptr == 1); + EATEST_VERIFY(*d.emplace_back(new int(2)).ptr == 2); + EATEST_VERIFY(*d.emplace_back(new int(3)).ptr == 3); d.erase(d.begin() + 1); } @@ -926,6 +938,56 @@ int TestDeque() } } + // Tests for erase_unordered + { + { + eastl::deque vec = {0, 1, 2, 3}; + auto numErased = eastl::erase_unsorted(vec, 1); + EATEST_VERIFY(numErased == 1); + EATEST_VERIFY(VerifySequence(vec, {0, 3, 2}, "erase_unordered") ); + } + { + eastl::deque vec = {}; + auto numErased = eastl::erase_unsorted(vec, 42); + EATEST_VERIFY(numErased == 0); + EATEST_VERIFY(vec.size() == 0); + } + // The following test checks that the correct implementation is called for deque by checking the order + // of the remaining values. It is not a strict requirement that they have this order but it + // is expected to be the result based on that it minimizes the amount of work. + { + eastl::deque vec = {0, 1, 2, 3, 1, 5, 6, 1, 8, 9}; + auto numErased = eastl::erase_unsorted(vec, 1); + EATEST_VERIFY(numErased == 3); + EATEST_VERIFY(VerifySequence(vec, {0, 9, 2, 3, 8, 5, 6}, "erase_unordered") ); + } + } + + // Tests for erase_unordered_if + { + { + eastl::deque vec = {0, 1, 2, 3}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 2); + EATEST_VERIFY(VerifySequence(vec, {0, 2}, "erase_unordered_if") ); + } + { + eastl::deque vec = {}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 0); + EATEST_VERIFY(vec.size() == 0); + } + // The following test checks that the correct implementation is called for deque by checking the order + // of the remaining values. It is not a strict requirement that they have this order but it + // is expected to be the result based on that it minimizes the amount of work. + { + eastl::deque vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 5); + EATEST_VERIFY(VerifySequence(vec, {0, 8, 2, 6, 4}, "erase_unordered_if") ); + } + } + { eastl::deque d1; eastl::deque d2{d1}; diff --git a/test/source/TestExtra.cpp b/test/source/TestExtra.cpp index 52fbd622..156e4f84 100644 --- a/test/source/TestExtra.cpp +++ b/test/source/TestExtra.cpp @@ -47,6 +47,7 @@ namespace eastl #include "EASTLTest.h" +#include #include #include #include @@ -881,6 +882,7 @@ static int TestStack() stack > toS_C(eastl::move(toVectorM)); EATEST_VERIFY((toS_C.size() == toVector.size()) && toVectorM.empty()); + EASTL_INTERNAL_DISABLE_DEPRECATED() // emplace_back: was declared deprecated { // template // void emplace_back(Args&&... args); @@ -888,6 +890,7 @@ static int TestStack() toS_D.emplace_back(0, 1, 2); EATEST_VERIFY(toS_D.size() == 1) && (toS_D.top() == TestObject(0, 1, 2)); } + EASTL_INTERNAL_RESTORE_DEPRECATED() // emplace_back: was declared deprecated { // template @@ -986,8 +989,9 @@ static int TestCallTraits() CallTraitsContainer ctcVoid(nErrorCount); CallTraitsContainer ctcIntArray; - char buffer[128]; - sprintf(buffer, "%p %p %p %p", &ctcInt, &ctcIntPtr, &ctcVoid, &ctcIntArray); + const int kBufferSize = 128; + char buffer[kBufferSize]; + EA::StdC::Snprintf(buffer, kBufferSize, "%p %p %p %p", &ctcInt, &ctcIntPtr, &ctcVoid, &ctcIntArray); return nErrorCount; } @@ -1054,6 +1058,9 @@ static int TestNumeric() } #if defined(EA_COMPILER_CPP20_ENABLED) + +EA_DISABLE_VC_WARNING(4756) // warning C4756: overflow in constant arithmetic + template static constexpr int SignedIntMidpoint() { @@ -1327,6 +1334,9 @@ static int TestLerp() return nErrorCount; } + +EA_RESTORE_VC_WARNING(); // warning C4756: overflow in constant arithmetic + #endif @@ -1380,143 +1390,6 @@ static int TestAdaptors() return nErrorCount; } -#if defined(EA_COMPILER_CPP20_ENABLED) -template -int TestHasSingleBit() -{ - int nErrorCount = 0; - - VERIFY(eastl::has_single_bit(T(0)) == false); - VERIFY(eastl::has_single_bit(T(1)) == true); - VERIFY(eastl::has_single_bit(T(2)) == true); - VERIFY(eastl::has_single_bit(T(3)) == false); - - VERIFY(eastl::has_single_bit(eastl::numeric_limits::min()) == false); - VERIFY(eastl::has_single_bit(eastl::numeric_limits::max()) == false); - - for (int i = 4; i < eastl::numeric_limits::digits; i++) - { - T power_of_two = static_cast(T(1U) << i); - VERIFY(eastl::has_single_bit(power_of_two)); - VERIFY(eastl::has_single_bit(static_cast(power_of_two - 1)) == false); - } - - return nErrorCount; -} - -template -static int TestBitCeil() -{ - int nErrorCount = 0; - - VERIFY(eastl::bit_ceil(T(0)) == T(1)); - VERIFY(eastl::bit_ceil(T(1)) == T(1)); - VERIFY(eastl::bit_ceil(T(2)) == T(2)); - VERIFY(eastl::bit_ceil(T(3)) == T(4)); - - EA_CONSTEXPR auto DIGITS = eastl::numeric_limits::digits; - EA_CONSTEXPR auto MIN = eastl::numeric_limits::min(); - EA_CONSTEXPR auto MAX = static_cast(T(1) << (DIGITS - 1)); - - VERIFY(eastl::bit_ceil(MAX) == MAX); - VERIFY(eastl::bit_ceil(static_cast(MAX - 1)) == MAX); - VERIFY(eastl::bit_ceil(MIN) == T(1)); - - for (int i = 4; i < eastl::numeric_limits::digits; i++) - { - T power_of_two = static_cast(T(1U) << i); - VERIFY(eastl::bit_ceil(power_of_two) == power_of_two); - VERIFY(eastl::bit_ceil(static_cast(power_of_two - 1)) == power_of_two); - } - - return nErrorCount; -} - -template -static int TestBitFloor() -{ - int nErrorCount = 0; - VERIFY(eastl::bit_floor(T(0)) == T(0)); - VERIFY(eastl::bit_floor(T(1)) == T(1)); - VERIFY(eastl::bit_floor(T(2)) == T(2)); - VERIFY(eastl::bit_floor(T(3)) == T(2)); - - EA_CONSTEXPR auto DIGITS = eastl::numeric_limits::digits; - EA_CONSTEXPR auto MIN = eastl::numeric_limits::min(); - EA_CONSTEXPR auto MAX = eastl::numeric_limits::max(); - - VERIFY(eastl::bit_floor(MAX) == T(1) << (DIGITS - 1)); - VERIFY(eastl::bit_floor(MIN) == T(0)); - - for (int i = 4; i < eastl::numeric_limits::digits; i++) - { - T power_of_two = static_cast(T(1U) << i); - VERIFY(eastl::bit_floor(power_of_two) == power_of_two); - VERIFY(eastl::bit_floor(static_cast(power_of_two + 1)) == power_of_two); - } - return nErrorCount; -} - -template -static int TestBitWidth() -{ - int nErrorCount = 0; - - VERIFY(eastl::bit_width(T(0)) == T(0)); - VERIFY(eastl::bit_width(T(1)) == T(1)); - VERIFY(eastl::bit_width(T(2)) == T(2)); - VERIFY(eastl::bit_width(T(3)) == T(2)); - - EA_CONSTEXPR auto DIGITS = eastl::numeric_limits::digits; - EA_CONSTEXPR auto MIN = eastl::numeric_limits::min(); - EA_CONSTEXPR auto MAX = eastl::numeric_limits::max(); - - VERIFY(eastl::bit_width(MIN) == 0); - VERIFY(eastl::bit_width(MAX) == DIGITS); - - for (int i = 4; i < eastl::numeric_limits::digits; i++) - { - T power_of_two = static_cast(T(1U) << i); - VERIFY(eastl::bit_width(power_of_two) == static_cast(i + 1)); - } - - return nErrorCount; -} - -/////////////////////////////////////////////////////////////////////////////// -// TestPowerofTwo -// -static int TestPowerOfTwo() -{ - int nErrorCount = 0; - nErrorCount += TestHasSingleBit(); - nErrorCount += TestHasSingleBit(); - nErrorCount += TestHasSingleBit(); - nErrorCount += TestHasSingleBit(); - nErrorCount += TestHasSingleBit(); - - nErrorCount += TestBitCeil(); - nErrorCount += TestBitCeil(); - nErrorCount += TestBitCeil(); - nErrorCount += TestBitCeil(); - nErrorCount += TestBitCeil(); - - nErrorCount += TestBitFloor(); - nErrorCount += TestBitFloor(); - nErrorCount += TestBitFloor(); - nErrorCount += TestBitFloor(); - nErrorCount += TestBitFloor(); - - nErrorCount += TestBitWidth(); - nErrorCount += TestBitWidth(); - nErrorCount += TestBitWidth(); - nErrorCount += TestBitWidth(); - nErrorCount += TestBitWidth(); - - return nErrorCount; -} -#endif - /////////////////////////////////////////////////////////////////////////////// // TestExtra // @@ -1535,7 +1408,6 @@ int TestExtra() #if defined(EA_COMPILER_CPP20_ENABLED) nErrorCount += TestMidpoint(); nErrorCount += TestLerp(); - nErrorCount += TestPowerOfTwo(); #endif return nErrorCount; diff --git a/test/source/TestFixedVector.cpp b/test/source/TestFixedVector.cpp index 9ead65bc..8822afaf 100644 --- a/test/source/TestFixedVector.cpp +++ b/test/source/TestFixedVector.cpp @@ -529,13 +529,33 @@ int TestFixedVector() EATEST_VERIFY(fvmv1.validate()); EATEST_VERIFY(fvmv2.validate()); - fixed_vector, FV_SIZE> fv = eastl::move(fvmv1); // Test move copy constructor + fixed_vector, FV_SIZE> fvmv3 = eastl::move(fvmv1); // Test move copy constructor for (unsigned int i = 0; i < FV_SIZE; ++i) { EATEST_VERIFY(!fvmv1[i]); - EATEST_VERIFY(*fv[i] == i); + EATEST_VERIFY(*fvmv3[i] == i); } - EATEST_VERIFY(fv.validate()); + EATEST_VERIFY(fvmv3.validate()); + + fixed_vector, FV_SIZE> fvmv4{eastl::move(fvmv3), fvmv3.get_overflow_allocator()}; + for (unsigned int i = 0; i < FV_SIZE; ++i) + { + EATEST_VERIFY(!fvmv3[i]); + EATEST_VERIFY(*fvmv4[i] == i); + } + EATEST_VERIFY(fvmv4.validate()); + + fvmv4.push_back(make_unique(FV_SIZE)); + EATEST_VERIFY(fvmv4.has_overflowed()); + + fixed_vector, FV_SIZE> fvmv5{eastl::move(fvmv4), fvmv4.get_overflow_allocator()}; + for (unsigned int i = 0; i <= FV_SIZE; ++i) + { + EATEST_VERIFY(!fvmv4[i]); + EATEST_VERIFY(*fvmv5[i] == i); + } + EATEST_VERIFY(fvmv5.validate()); + EATEST_VERIFY(fvmv5.has_overflowed()); } { // Test that ensures that move ctor that triggers realloc (e.g. > capacity) does so via move code path @@ -559,6 +579,7 @@ int TestFixedVector() int64_t copyCtorCount0 = TestObject::sTOCopyCtorCount, moveCtorCount0 = TestObject::sTOMoveCtorCount; decltype(fv1) fv2(eastl::move(fv1), MyAlloc(123)); EATEST_VERIFY(TestObject::sTOCopyCtorCount == copyCtorCount0 && TestObject::sTOMoveCtorCount == (moveCtorCount0 + 2)); + EATEST_VERIFY(fv2.get_overflow_allocator().dummy == 123); } #if defined(EA_COMPILER_CPP17_ENABLED) && __has_include() @@ -568,15 +589,82 @@ int TestFixedVector() eastl::fixed_vector, 4> b = eastl::move(v); } #endif - return nErrorCount; -} - - + // eastl::erase / eastl::erase_if tests + { + { + eastl::fixed_vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto numErased = eastl::erase(v, 5); + VERIFY((v == eastl::fixed_vector {1, 2, 3, 4, 6, 7, 8, 9})); + VERIFY(numErased == 1); + numErased = eastl::erase(v, 2); + VERIFY((v == eastl::fixed_vector {1, 3, 4, 6, 7, 8, 9})); + VERIFY(numErased == 1); + numErased = eastl::erase(v, 9); + VERIFY((v == eastl::fixed_vector {1, 3, 4, 6, 7, 8})); + VERIFY(numErased == 1); + } + { + eastl::fixed_vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto numErased = eastl::erase_if(v, [](auto i) { return i % 2 == 0; }); + VERIFY((v == eastl::fixed_vector{1, 3, 5, 7, 9})); + VERIFY(numErased == 4); + } + } + // Tests for erase_unordered + { + { + eastl::fixed_vector vec = {0, 1, 2, 3}; + auto numErased = eastl::erase_unsorted(vec, 1); + EATEST_VERIFY(numErased == 1); + EATEST_VERIFY(VerifySequence(vec, {0, 3, 2}, "erase_unordered") ); + } + { + eastl::fixed_vector vec = {}; + auto numErased = eastl::erase_unsorted(vec, 42); + EATEST_VERIFY(numErased == 0); + EATEST_VERIFY(vec.size() == 0); + } + // The following test checks that the correct implementation is called for fixed_vector by checking the order + // of the remaining values. It is not a strict requirement that they have this order but it + // is expected to be the result based on that it minimizes the amount of work. + { + eastl::fixed_vector vec = {0, 1, 2, 3, 1, 5, 6, 1, 8, 9}; + auto numErased = eastl::erase_unsorted(vec, 1); + EATEST_VERIFY(numErased == 3); + EATEST_VERIFY(VerifySequence(vec, {0, 9, 2, 3, 8, 5, 6}, "erase_unordered") ); + } + } + // Tests for erase_unordered_if + { + { + eastl::fixed_vector vec = {0, 1, 2, 3}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 2); + EATEST_VERIFY(VerifySequence(vec, {0, 2}, "erase_unordered_if") ); + } + { + eastl::fixed_vector vec = {}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 0); + EATEST_VERIFY(vec.size() == 0); + } + // The following test checks that the correct implementation is called for fixed_vector by checking the order + // of the remaining values. It is not a strict requirement that they have this order but it + // is expected to be the result based on that it minimizes the amount of work. + { + eastl::fixed_vector vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 5); + EATEST_VERIFY(VerifySequence(vec, {0, 8, 2, 6, 4}, "erase_unordered_if") ); + } + } + return nErrorCount; +} diff --git a/test/source/TestFunctional.cpp b/test/source/TestFunctional.cpp index 4c7d834c..24724af1 100644 --- a/test/source/TestFunctional.cpp +++ b/test/source/TestFunctional.cpp @@ -17,6 +17,9 @@ EA_DISABLE_ALL_VC_WARNINGS() #include EA_RESTORE_ALL_VC_WARNINGS() +// 4512/4626 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. +EA_DISABLE_VC_WARNING(4512 4626); + // contains tests for library features that are deprecated EASTL_INTERNAL_DISABLE_DEPRECATED() // *: was declared deprecated @@ -547,7 +550,7 @@ int TestFunctional() void Add(int addAmount) { value += addAmount; } int GetValue() { return value; } int& GetValueReference() { return value; } - void NoThrow(int inValue) EA_NOEXCEPT {} + void NoThrow(int) EA_NOEXCEPT {} int value; }; @@ -682,8 +685,8 @@ int TestFunctional() { struct TestInvokeConstAccess { - void ConstMemberFunc(int i) const {} - void ConstVolatileMemberFunc(int i) const volatile {} + void ConstMemberFunc(int) const {} + void ConstVolatileMemberFunc(int) const volatile {} int mI; }; @@ -1174,6 +1177,35 @@ int TestFunctional() } } #endif // __cpp_deduction_guides + + #if EASTL_RTTI_ENABLED + { + struct Functor { int operator()() { return 42; } }; + struct Functor2 { int operator()() { return 43; } }; + + eastl::function fn = Functor(); + + const std::type_info& type = typeid(Functor); + const std::type_info& targetType = fn.target_type(); + EATEST_VERIFY(targetType == type); + + + Functor* target = fn.target(); + EATEST_VERIFY(target != nullptr); + + Functor2* target2 = fn.target(); + EATEST_VERIFY(target2 == nullptr); + + // This tests the `const` overloads + const auto& constFn = fn; + + const Functor* constTarget = constFn.target(); + EATEST_VERIFY(constTarget != nullptr); + + const Functor2* constTarget2 = constFn.target(); + EATEST_VERIFY(constTarget2 == nullptr); + } + #endif } // Checking _MSC_EXTENSIONS is required because the Microsoft calling convention classifiers are only available when @@ -1460,6 +1492,82 @@ int TestFunctional() result = eastl::logical_not<>{}(false); EATEST_VERIFY(result); } + + // eastl::bit_and + { + EATEST_VERIFY(eastl::bit_and{}(0x00, 0x00) == 0x00); + EATEST_VERIFY(eastl::bit_and{}(0x11, 0x00) == 0x00); + EATEST_VERIFY(eastl::bit_and{}(0x01, 0x10) == 0x00); + EATEST_VERIFY(eastl::bit_and{}(0x11, 0x01) == 0x01); + EATEST_VERIFY(eastl::bit_and{}(0x01, 0x11) == 0x01); + EATEST_VERIFY(eastl::bit_and{}(0x11, 0x11) == 0x11); + } + + // eastl::bit_and + { + EATEST_VERIFY(eastl::bit_and{}(0x00, 0x00) == 0x00); + EATEST_VERIFY(eastl::bit_and{}(0x11, 0x00) == 0x00); + EATEST_VERIFY(eastl::bit_and{}(0x01, 0x10) == 0x00); + EATEST_VERIFY(eastl::bit_and{}(0x11, 0x01) == 0x01); + EATEST_VERIFY(eastl::bit_and{}(0x01, 0x11) == 0x01); + EATEST_VERIFY(eastl::bit_and{}(0x11, 0x11) == 0x11); + } + + // eastl::bit_or + { + EATEST_VERIFY(eastl::bit_or{}(0x00, 0x00) == 0x00); + EATEST_VERIFY(eastl::bit_or{}(0x11, 0x00) == 0x11); + EATEST_VERIFY(eastl::bit_or{}(0x01, 0x10) == 0x11); + EATEST_VERIFY(eastl::bit_or{}(0x11, 0x01) == 0x11); + EATEST_VERIFY(eastl::bit_or{}(0x01, 0x11) == 0x11); + EATEST_VERIFY(eastl::bit_or{}(0x11, 0x11) == 0x11); + } + + // eastl::bit_or + { + EATEST_VERIFY(eastl::bit_or{}(0x00, 0x00) == 0x00); + EATEST_VERIFY(eastl::bit_or{}(0x11, 0x00) == 0x11); + EATEST_VERIFY(eastl::bit_or{}(0x01, 0x10) == 0x11); + EATEST_VERIFY(eastl::bit_or{}(0x11, 0x01) == 0x11); + EATEST_VERIFY(eastl::bit_or{}(0x01, 0x11) == 0x11); + EATEST_VERIFY(eastl::bit_or{}(0x11, 0x11) == 0x11); + } + + // eastl::bit_xor + { + EATEST_VERIFY(eastl::bit_xor{}(0x00, 0x00) == 0x00); + EATEST_VERIFY(eastl::bit_xor{}(0x11, 0x00) == 0x11); + EATEST_VERIFY(eastl::bit_xor{}(0x01, 0x10) == 0x11); + EATEST_VERIFY(eastl::bit_xor{}(0x11, 0x01) == 0x10); + EATEST_VERIFY(eastl::bit_xor{}(0x01, 0x11) == 0x10); + EATEST_VERIFY(eastl::bit_xor{}(0x11, 0x11) == 0x00); + } + + // eastl::bit_xor + { + EATEST_VERIFY(eastl::bit_xor{}(0x00, 0x00) == 0x00); + EATEST_VERIFY(eastl::bit_xor{}(0x11, 0x00) == 0x11); + EATEST_VERIFY(eastl::bit_xor{}(0x01, 0x10) == 0x11); + EATEST_VERIFY(eastl::bit_xor{}(0x11, 0x01) == 0x10); + EATEST_VERIFY(eastl::bit_xor{}(0x01, 0x11) == 0x10); + EATEST_VERIFY(eastl::bit_xor{}(0x11, 0x11) == 0x00); + } + + // eastl::bit_not + { + EATEST_VERIFY(eastl::bit_not{}(0x0) == (unsigned char) 0xFF); + EATEST_VERIFY(eastl::bit_not{}(0x0F) == (unsigned char) 0xF0); + EATEST_VERIFY(eastl::bit_not{}(0xF0) == (unsigned char) 0x0F); + EATEST_VERIFY(eastl::bit_not{}(0xFF) == (unsigned char) 0x0); + } + + // eastl::bit_not + { + EATEST_VERIFY((unsigned char)eastl::bit_not{}(0x0) == (unsigned char)0xFF); + EATEST_VERIFY((unsigned char)eastl::bit_not{}(0x0F) == (unsigned char)0xF0); + EATEST_VERIFY((unsigned char)eastl::bit_not{}(0xF0) == (unsigned char)0x0F); + EATEST_VERIFY((unsigned char)eastl::bit_not{}(0xFF) == (unsigned char)0x0); + } } #endif @@ -1608,3 +1716,5 @@ static_assert(eastl::is_invocable_r::val static_assert(eastl::is_invocable_r::value, "incorrect value for is_invocable_r"); EASTL_INTERNAL_RESTORE_DEPRECATED() + +EA_RESTORE_VC_WARNING(); diff --git a/test/source/TestHash.cpp b/test/source/TestHash.cpp index c94c8f06..ae1ec4f6 100644 --- a/test/source/TestHash.cpp +++ b/test/source/TestHash.cpp @@ -568,26 +568,33 @@ int TestHash() // test hashtable::swap using different allocator instances { - typedef hash_set, eastl::equal_to, InstanceAllocator> HS; - HS hashSet1(InstanceAllocator("hash_set1 name", 111)); - HS hashSet2(InstanceAllocator("hash_set2 name", 222)); - - for(int i = 0; i < 10; i++) { - hashSet1.insert(i); - hashSet2.insert(i+10); - } + typedef hash_set, eastl::equal_to, InstanceAllocator> HS; + HS hashSet1(InstanceAllocator("hash_set1 name", 111)); + HS hashSet2(InstanceAllocator("hash_set2 name", 222)); - hashSet2.swap(hashSet1); + for (int i = 0; i < 10; i++) + { + hashSet1.insert(i); + hashSet2.insert(i + 10); + } - EATEST_VERIFY(hashSet1.validate()); - EATEST_VERIFY(hashSet2.validate()); + hashSet2.swap(hashSet1); - EATEST_VERIFY(hashSet1.get_allocator().mInstanceId == 222); - EATEST_VERIFY(hashSet2.get_allocator().mInstanceId == 111); + EATEST_VERIFY(hashSet1.validate()); + EATEST_VERIFY(hashSet2.validate()); + + EATEST_VERIFY(hashSet1.get_allocator().mInstanceId == 222); + EATEST_VERIFY(hashSet2.get_allocator().mInstanceId == 111); + + EATEST_VERIFY(eastl::all_of(eastl::begin(hashSet2), eastl::end(hashSet2), [](int i) { return i < 10; })); + EATEST_VERIFY(eastl::all_of(eastl::begin(hashSet1), eastl::end(hashSet1), [](int i) { return i >= 10; })); + + // destroying containers to invoke InstanceAllocator::deallocate() checks + } - EATEST_VERIFY(eastl::all_of(eastl::begin(hashSet2), eastl::end(hashSet2), [](int i) { return i < 10; })); - EATEST_VERIFY(eastl::all_of(eastl::begin(hashSet1), eastl::end(hashSet1), [](int i) { return i >= 10; })); + EATEST_VERIFY_MSG(InstanceAllocator::mMismatchCount == 0, "Container elements should be deallocated by the allocator that allocated it."); + InstanceAllocator::reset_all(); } } @@ -984,8 +991,9 @@ int TestHash() for(int i = 0; i < kCount * 2; i++) { - char pString[32]; - sprintf(pString, "%d", i); + const int kBufferSize = 32; + char pString[kBufferSize]; + EA::StdC::Snprintf(pString, kBufferSize, "%d", i); HashSetString::iterator it = hashSet.find_as(pString); if(i < kCount) diff --git a/test/source/TestIntrusiveSDList.cpp b/test/source/TestIntrusiveSDList.cpp index 13a48026..7b5c5f1c 100644 --- a/test/source/TestIntrusiveSDList.cpp +++ b/test/source/TestIntrusiveSDList.cpp @@ -42,12 +42,14 @@ namespace TestSDListLocal eastl::string IntListToString8(const T& cont) { eastl::string s("<"); - char buf[64]; + + const int kBufferSize = 64; + char buf[kBufferSize]; for(typename T::const_iterator it(cont.begin()), itEnd(cont.end()); it != itEnd; ++it) { const int& v = *it; - sprintf(buf, " %d", v); + EA::StdC::Snprintf(buf, kBufferSize, " %d", v); s += buf; } diff --git a/test/source/TestIterator.cpp b/test/source/TestIterator.cpp index da318fdd..10d8c9a2 100644 --- a/test/source/TestIterator.cpp +++ b/test/source/TestIterator.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include EA_DISABLE_ALL_VC_WARNINGS() #include @@ -195,8 +197,8 @@ int TestIterator_moveIterator() FakeProxyIterator& operator++() { return *this; } FakeProxyIterator operator++(int) { return {}; } - bool operator==(const FakeProxyIterator& rhs) { return true; }; - bool operator!=(const FakeProxyIterator& rhs) { return false; }; + bool operator==(const FakeProxyIterator&) { return true; }; + bool operator!=(const FakeProxyIterator&) { return false; }; }; FakeProxyIterator it = {}; @@ -214,6 +216,17 @@ int TestIterator_moveIterator() EATEST_VERIFY(pCopiedX == 42); } + { + // test move_iterator usable in algorithms + + MoveOnlyType moveableArray[] = {MoveOnlyType{1}, MoveOnlyType{2}, MoveOnlyType{3}}; + MoveOnlyType destArray[] = {MoveOnlyType{0}, MoveOnlyType{0}, MoveOnlyType{0}}; + + eastl::copy(eastl::make_move_iterator(moveableArray), eastl::make_move_iterator(moveableArray + 3), destArray); + EATEST_VERIFY(VerifySequence(destArray, destArray + 3, {MoveOnlyType{1}, MoveOnlyType{2}, MoveOnlyType{3}}, + "copy(move_iterator(...))")); + } + return nErrorCount; } @@ -449,7 +462,7 @@ int TestIterator() } } - + EASTL_INTERNAL_DISABLE_DEPRECATED() // '*': was declared deprecated { // is_iterator_wrapper static_assert((eastl::is_iterator_wrapper::value == false), "is_iterator_wrapper failure"); @@ -545,7 +558,8 @@ int TestIterator() eastl::vector::iterator it = unwrap_move_iterator(miVectorInt); EATEST_VERIFY(*it == 1); static_assert((eastl::is_same::iterator>::value == true), "unwrap_iterator failure"); - } + } + EASTL_INTERNAL_RESTORE_DEPRECATED() { // array cbegin - cend diff --git a/test/source/TestList.cpp b/test/source/TestList.cpp index 001b79af..128ab27c 100644 --- a/test/source/TestList.cpp +++ b/test/source/TestList.cpp @@ -413,30 +413,27 @@ int TestList() } } - // void emplace_front(Args&&... args); - // void emplace_front(value_type&& value); - // void emplace_front(const value_type& value); + // template + // reference emplace_front(Args&&... args); { eastl::list ref = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; eastl::list a; for(int i = 0; i < 10; i++) - a.emplace_front(i); + VERIFY(a.emplace_front(i) == i); VERIFY(a == ref); } // template - // void emplace_back(Args&&... args); - // void emplace_back(value_type&& value); - // void emplace_back(const value_type& value); + // reference emplace_back(Args&&... args); { { eastl::list ref = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; eastl::list a; for(int i = 0; i < 10; i++) - a.emplace_back(i); + VERIFY(a.emplace_back(i) == i); VERIFY(a == ref); } @@ -454,9 +451,9 @@ int TestList() eastl::list ref = {{1}, {2}, {3}}; eastl::list a; - a.emplace_back(1); - a.emplace_back(2); - a.emplace_back(3); + VERIFY(a.emplace_back(1) == A{1}); + VERIFY(a.emplace_back(2) == A{2}); + VERIFY(a.emplace_back(3) == A{3}); VERIFY(a == ref); } @@ -465,9 +462,9 @@ int TestList() eastl::list ref = {{1}, {2}, {3}}; eastl::list a; - a.emplace_back(A(1)); - a.emplace_back(A(2)); - a.emplace_back(A(3)); + VERIFY(a.emplace_back(A(1)) == A{1}); + VERIFY(a.emplace_back(A(2)) == A{2}); + VERIFY(a.emplace_back(A(3)) == A{3}); VERIFY(a == ref); } @@ -481,9 +478,9 @@ int TestList() A a2(2); A a3(3); - a.emplace_back(a1); - a.emplace_back(a2); - a.emplace_back(a3); + VERIFY(a.emplace_back(a1) == A{1}); + VERIFY(a.emplace_back(a2) == A{2}); + VERIFY(a.emplace_back(a3) == A{3}); VERIFY(a == ref); } @@ -903,16 +900,16 @@ int TestList() VERIFY(a1 == ref); } - // void unique(); + // size_type unique(); { eastl::list ref = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; eastl::list a = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9}; - a.unique(); + VERIFY(a.unique() == 34); VERIFY(a == ref); } - // void unique(BinaryPredicate); + // size_type unique(BinaryPredicate); { static bool bBreakComparison; struct A @@ -926,10 +923,10 @@ int TestList() {5}, {5}, {5}, {5}, {6}, {7}, {7}, {7}, {7}, {8}, {9}, {9}, {9}}; bBreakComparison = true; - a.unique(); // noop because broken comparison operator + VERIFY(a.unique() == 0); // noop because broken comparison operator VERIFY(a != ref); - a.unique([](const A& lhs, const A& rhs) { return lhs.mValue == rhs.mValue; }); + VERIFY(a.unique([](const A& lhs, const A& rhs) { return lhs.mValue == rhs.mValue; }) == 17); bBreakComparison = false; VERIFY(a == ref); diff --git a/test/source/TestLruCache.cpp b/test/source/TestLruCache.cpp index a5d3ff49..67112513 100644 --- a/test/source/TestLruCache.cpp +++ b/test/source/TestLruCache.cpp @@ -5,6 +5,8 @@ #include "EASTLTest.h" #include #include +#include +// #include namespace TestLruCacheInternal { @@ -52,7 +54,8 @@ namespace TestLruCacheInternal } -int TestLruCache() +template typename LruCache> +int TestLruCacheOfType() { int nErrorCount = 0; @@ -60,7 +63,9 @@ int TestLruCache() { using namespace TestLruCacheInternal; - eastl::lru_cache lruCache(3); + Foo::count = 0; + + LruCache lruCache(3); // Empty state EATEST_VERIFY(lruCache.contains(1) == false); @@ -212,7 +217,7 @@ int TestLruCache() auto createCallback = [&fooCreator](int) { return fooCreator.Create(); }; auto deleteCallback = [&fooCreator](Foo *f) { fooCreator.Destroy(f); }; - eastl::lru_cache lruCache(3, EASTLAllocatorType("eastl lru_cache"), createCallback, deleteCallback); + LruCache lruCache(3, EASTLAllocatorType("eastl lru_cache"), createCallback, deleteCallback); lruCache[1]; EATEST_VERIFY(fooCreator.mFooCreatedCount == 1); @@ -284,7 +289,7 @@ int TestLruCache() // Test iteration { - eastl::lru_cache lc(5); + LruCache lc(5); lc.insert_or_assign(0,10); lc.insert_or_assign(1,11); lc.insert_or_assign(2,12); @@ -325,7 +330,7 @@ int TestLruCache() // test initializer_list { - eastl::lru_cache lc = {{0, 10}, {1, 11}, {2, 12}, {3, 13}, {4, 14}, {5, 15}}; + LruCache lc = {{0, 10}, {1, 11}, {2, 12}, {3, 13}, {4, 14}, {5, 15}}; int i = 0; for(auto& p : lc) @@ -336,5 +341,42 @@ int TestLruCache() } } + // emplace + { + eastl::lru_cache lc = { {0, TestObject{ 1, 2, 3 }}, {1, TestObject{ 4, 5, 6 }} }; + + VERIFY((lc.at(0) == TestObject{1, 2, 3})); + VERIFY((lc.at(1) == TestObject{ 4, 5, 6 })); + + VERIFY((lc.emplace(0, 3, 2, 1).first->second.first == TestObject{ 3, 2, 1 })); + VERIFY((lc.emplace(1, 6, 5, 4).first->second.first == TestObject{ 6, 5, 4 })); + + // fail to emplace: key 0 already exists. + auto [ it, did_emplace ] = lc.emplace(0, 9, 9, 9); + VERIFY(!did_emplace); + VERIFY((it->second.first == TestObject{ 3, 2, 1 })); + } + return nErrorCount; } + +template +using LruCache = eastl::lru_cache, eastl::unordered_map::iterator>>>; + +template +using LruCacheMap = eastl::lru_cache, eastl::map::iterator>>>; + +// template +// using LruCacheDequeMap = eastl::lru_cache, eastl::map::iterator>>>; + +int TestLruCache() +{ + int nErrorCount = 0; + + nErrorCount += TestLruCacheOfType(); + nErrorCount += TestLruCacheOfType(); + // could use a deque instead of a list, except that lru_cache requires the container have a reset_lose_memory() function. + // nErrorCount += TestLruCacheOfType(); + + return nErrorCount; +} \ No newline at end of file diff --git a/test/source/TestMemory.cpp b/test/source/TestMemory.cpp index 607305f4..32d7f30d 100644 --- a/test/source/TestMemory.cpp +++ b/test/source/TestMemory.cpp @@ -439,9 +439,10 @@ int TestMemory() // template // Result uninitialized_copy_ptr(First first, Last last, Result result) - + EASTL_INTERNAL_DISABLE_DEPRECATED() // '*': was declared deprecated pEnd = eastl::uninitialized_copy_ptr((int*)NULL, (int*)NULL, (int*)NULL); EATEST_VERIFY(pEnd == NULL); + EASTL_INTERNAL_RESTORE_DEPRECATED() { @@ -458,8 +459,9 @@ int TestMemory() // template // void uninitialized_fill_ptr(T* first, T* last, const T& value) - + EASTL_INTERNAL_DISABLE_DEPRECATED() // '*': was declared deprecated eastl::uninitialized_fill_ptr((int*)NULL, (int*)NULL, (int)0); + EASTL_INTERNAL_RESTORE_DEPRECATED() { @@ -477,7 +479,9 @@ int TestMemory() // template // void uninitialized_fill_n_ptr(T* first, Count n, const T& value) + EASTL_INTERNAL_DISABLE_DEPRECATED() // '*': was declared deprecated eastl::uninitialized_fill_n_ptr((int*)NULL, (int)0, (int)0); + EASTL_INTERNAL_RESTORE_DEPRECATED() diff --git a/test/source/TestNumericLimits.cpp b/test/source/TestNumericLimits.cpp index 1964442a..bc95a87f 100644 --- a/test/source/TestNumericLimits.cpp +++ b/test/source/TestNumericLimits.cpp @@ -7,14 +7,6 @@ #include -struct NonNumericType -{ - NonNumericType(int value) : mValue(value){} - bool operator==(int value) const { return mValue == value; } - int mValue; // This exists for the purpose of allowing the type to act like a number and allow the test logic below to work. -}; - - /////////////////////////////////////////////////////////////////////////////// // TestNumericLimits // @@ -25,19 +17,6 @@ int TestNumericLimits() // To consider: Some day when we get more time, make a big table-driven set of // expected results to all member variables and function calls. - // Test a type that is not numeric,. - EATEST_VERIFY(!eastl::numeric_limits::is_bounded); - EATEST_VERIFY( eastl::numeric_limits::max() == 0); - - EATEST_VERIFY(!eastl::numeric_limits::is_bounded); - EATEST_VERIFY( eastl::numeric_limits::max() == 0); - - EATEST_VERIFY(!eastl::numeric_limits::is_bounded); - EATEST_VERIFY( eastl::numeric_limits::max() == 0); - - EATEST_VERIFY(!eastl::numeric_limits::is_bounded); - EATEST_VERIFY( eastl::numeric_limits::max() == 0); - // Test bool in all const-volatile variants. EATEST_VERIFY(eastl::numeric_limits::is_bounded); EATEST_VERIFY(eastl::numeric_limits::max() != 0); diff --git a/test/source/TestOptional.cpp b/test/source/TestOptional.cpp index 58cff5ab..7f41983c 100644 --- a/test/source/TestOptional.cpp +++ b/test/source/TestOptional.cpp @@ -71,7 +71,7 @@ bool copy_test::was_copied = false; ///////////////////////////////////////////////////////////////////////////// struct move_test { - move_test() = default; + move_test() = default; move_test(move_test&& mt) { @@ -131,6 +131,387 @@ struct assignment_test int assignment_test::num_objects_inited = 0; +static int TestOptional_MonadicOperations() +{ + using namespace eastl; + + int nErrorCount(0); +#if defined(EASTL_OPTIONAL_ENABLED) && EASTL_OPTIONAL_ENABLED + // and_then l-value ref + { + { + optional o{42}; + auto result = o.and_then( + [](int& x) + { + const int old_x = eastl::exchange(x, 1337); + return make_optional(to_string(old_x)); + }); + VERIFY(result.has_value()); + VERIFY(*result == string_view("42")); + VERIFY(o.has_value()); + VERIFY(*o == 1337); + } + + { + // Ensuring that the callable is not called when optional is empty. + bool called = false; + optional o; + auto result = o.and_then( + [&called](int& x) + { + called = true; + return make_optional(to_string(x)); + }); + VERIFY(!result.has_value()); + VERIFY(!o.has_value()); + VERIFY(!called); + } + + { + optional o{42}; + auto result = o.and_then( + [](int& x) -> optional + { + x = 1337; + return nullopt; + }); + VERIFY(!result.has_value()); + VERIFY(o.has_value()); + VERIFY(*o == 1337); + } + } + + // and_then const l-value ref + { + { + const optional o{42}; + auto result = o.and_then([](const int& x) { return make_optional(to_string(x)); }); + VERIFY(result.has_value()); + VERIFY(*result == string_view("42")); + VERIFY(o.has_value()); + VERIFY(*o == 42); + } + + { + // Ensuring that the callable is not called when optional is empty. + bool called = false; + const optional o; + auto result = o.and_then( + [&called](const int& x) + { + called = true; + return make_optional(to_string(x)); + }); + VERIFY(!result.has_value()); + VERIFY(!o.has_value()); + VERIFY(!called); + } + + { + const optional o{42}; + auto result = o.and_then([](const int&) -> optional { return nullopt; }); + VERIFY(!result.has_value()); + VERIFY(o.has_value()); + VERIFY(*o == 42); + } + } + + // and_then r-value ref + { + { + optional> o = eastl::make_unique(42); + auto result = eastl::move(o).and_then([](auto ptr) { return make_optional(to_string(*ptr)); }); + VERIFY(result.has_value()); + VERIFY(*result == string_view("42")); + VERIFY(o.has_value()); + VERIFY(o.value() == nullptr); // o should be moved-from. + } + + { + // Ensuring that the callable is not called when optional is empty. + bool called = false; + optional> o; + auto result = eastl::move(o).and_then( + [&called](auto ptr) + { + called = true; + return make_optional(to_string(*ptr)); + }); + VERIFY(!result.has_value()); + VERIFY(!o.has_value()); + VERIFY(!called); + } + + { + optional> o = eastl::make_unique(42); + auto result = eastl::move(o).and_then([](auto ptr) -> optional { return nullopt; }); + VERIFY(!result.has_value()); + VERIFY(o.has_value()); + VERIFY(o.value() == nullptr); // o should be moved-from. + } + } + + // and_then const r-value ref + { + { + const optional> o = eastl::make_unique(42); + auto result = + eastl::move(o).and_then([](const unique_ptr&& ptr) { return make_optional(to_string(*ptr)); }); + VERIFY(result.has_value()); + VERIFY(*result == string_view("42")); + VERIFY(o.has_value()); + VERIFY(o.value() != nullptr); + } + + { + // Ensuring that the callable is not called when optional is empty. + bool called = false; + const optional> o; + auto result = eastl::move(o).and_then( + [&called](const unique_ptr&& ptr) + { + called = true; + return make_optional(to_string(*ptr)); + }); + VERIFY(!result.has_value()); + VERIFY(!o.has_value()); + VERIFY(!called); + } + + { + const optional> o = eastl::make_unique(42); + auto result = + eastl::move(o).and_then([](const unique_ptr&&) -> optional { return nullopt; }); + VERIFY(!result.has_value()); + VERIFY(o.has_value()); + VERIFY(o.value() != nullptr); + } + } + + // transform l-value ref + { + { + optional o{42}; + auto result = o.transform( + [](int& x) + { + const int old_x = eastl::exchange(x, 1337); + return to_string(old_x); + }); + VERIFY(result.has_value()); + VERIFY(*result == string_view("42")); + VERIFY(o.has_value()); + VERIFY(*o == 1337); + } + + { + // Ensuring that the callable is not called when optional is empty. + bool called = false; + optional o; + auto result = o.transform( + [&called](int& x) + { + called = true; + return to_string(x); + }); + VERIFY(!result.has_value()); + VERIFY(!o.has_value()); + VERIFY(!called); + } + + { + // Check that the return type of the callable gets remove_cvref_t. + eastl::string externalString = "Jean Guegant was here"; + + optional o{42}; + auto result = o.transform([&externalString](int&) -> const eastl::string& { return externalString; }); + + static_assert(eastl::is_same_v>, + "Wrong return type for transform."); + VERIFY(result.has_value()); + VERIFY(*result == externalString); + } + } + + // transform const l-value ref + { + { + const optional o{42}; + auto result = o.transform([](const int& x) { return to_string(x); }); + VERIFY(result.has_value()); + VERIFY(*result == string_view("42")); + VERIFY(o.has_value()); + } + + { + // Ensuring that the callable is not called when optional is empty. + bool called = false; + const optional o; + auto result = o.transform( + [&called](const int& x) + { + called = true; + return to_string(x); + }); + VERIFY(!result.has_value()); + VERIFY(!o.has_value()); + VERIFY(!called); + } + + { + // Check that the return type of the callable gets remove_cvref_t. + eastl::string externalString = "Jean Guegant was here"; + + const optional o{42}; + auto result = + o.transform([&externalString](const int&) -> const eastl::string& { return externalString; }); + + static_assert(eastl::is_same_v>, + "Wrong return type for transform."); + VERIFY(result.has_value()); + VERIFY(*result == externalString); + } + } + + // transform r-value ref + { + { + optional> o = eastl::make_unique(42); + auto result = eastl::move(o).transform([](auto ptr) { return to_string(*ptr); }); + VERIFY(result.has_value()); + VERIFY(*result == string_view("42")); + VERIFY(o.has_value()); + VERIFY(*o == nullptr); // o should be moved-from. + } + + { + // Ensuring that the callable is not called when optional is empty. + bool called = false; + optional> o; + auto result = eastl::move(o).transform( + [&called](auto ptr) + { + called = true; + return to_string(*ptr); + }); + VERIFY(!result.has_value()); + VERIFY(!o.has_value()); + VERIFY(!called); + } + + { + // Check that the return type of the callable gets remove_cvref_t. + eastl::string externalString = "Jean Guegant was here"; + + optional> o = eastl::make_unique(42); + auto result = eastl::move(o).transform([&externalString](auto ptr) -> const eastl::string& + { return externalString; }); + + static_assert(eastl::is_same_v>, + "Wrong return type for transform."); + VERIFY(result.has_value()); + VERIFY(*result == externalString); + } + } + + // transform const r-value ref + { + { + const optional> o = eastl::make_unique(42); + auto result = eastl::move(o).transform([](const unique_ptr&& ptr) { return to_string(*ptr); }); + VERIFY(result.has_value()); + VERIFY(*result == string_view("42")); + VERIFY(o.has_value()); + VERIFY(*o != nullptr); + } + + { + // Ensuring that the callable is not called when optional is empty. + bool called = false; + const optional> o; + auto result = eastl::move(o).transform( + [&called](const unique_ptr&& ptr) + { + called = true; + return to_string(*ptr); + }); + VERIFY(!result.has_value()); + VERIFY(!o.has_value()); + VERIFY(!called); + } + + { + // Check that the return type of the callable gets remove_cvref_t. + eastl::string externalString = "Jean Guegant was here"; + + const optional> o = eastl::make_unique(42); + auto result = eastl::move(o).transform( + [&externalString](const unique_ptr&&) -> const eastl::string& { return externalString; }); + + static_assert(eastl::is_same_v>, + "Wrong return type for transform."); + VERIFY(result.has_value()); + VERIFY(*result == externalString); + } + } + + // or_else const l-value ref + { + { + const optional o{42}; + auto result = o.or_else([]() { return eastl::make_optional(1337); }); + + VERIFY(result.has_value()); + VERIFY(result.value() == 42); + } + + { + const optional o; + auto result = o.or_else([]() { return eastl::make_optional(1337); }); + + VERIFY(result.has_value()); + VERIFY(result.value() == 1337); + } + + { + // Ensure that we can return refs from the callable that get copied. + eastl::optional externalOptional{1337}; + + const optional o; + auto result = o.or_else([&externalOptional]() -> const eastl::optional& { return externalOptional; }); + + VERIFY(result.has_value()); + VERIFY(result.value() == 1337); + } + } + + // or_else const l-value ref + { + { + optional> o = eastl::make_unique(42); + auto result = eastl::move(o).or_else([]() { return eastl::make_optional(eastl::make_unique(1337)); }); + + VERIFY(o.has_value()); + VERIFY(o.value() == nullptr); // o should be moved-from. + VERIFY(result.has_value()); + VERIFY(*result.value() == 42); + } + + { + optional> o; + auto result = eastl::move(o).or_else([]() { return eastl::make_optional(eastl::make_unique(1337)); }); + + VERIFY(result.has_value()); + VERIFY(*result.value() == 1337); + } + } +#endif + + return nErrorCount; +} + + ///////////////////////////////////////////////////////////////////////////// // TestOptional @@ -223,6 +604,25 @@ int TestOptional() VERIFY(o.value() == 42); } + { + // value_or with this a r-value ref and engaged. + optional> o = eastl::make_unique(42); + auto result = eastl::move(o).value_or(eastl::make_unique(1337)); + VERIFY(result != nullptr); + VERIFY(*result == 42); + VERIFY(o.has_value()); + VERIFY(o.value() == nullptr); // o has been moved-from. + } + + { + // value_or with this a r-value ref and not engaged. + optional> o; + auto result = eastl::move(o).value_or(eastl::make_unique(1337)); + VERIFY(result != nullptr); + VERIFY(*result == 1337); + VERIFY(!o.has_value()); + } + { int a = 42; auto o = make_optional(a); @@ -354,9 +754,9 @@ int TestOptional() } { - forwarding_testft(1.f); - float val = ft.GetValueOrDefault(0.f); - VERIFY(val == 1.f); + forwarding_testft(1.f); + float val = ft.GetValueOrDefault(0.f); + VERIFY(val == 1.f); } { @@ -425,20 +825,29 @@ int TestOptional() // http://en.cppreference.com/w/cpp/utility/optional/emplace { optional o; - o.emplace(42.f, 42.f, 42.f); + vec3& v = o.emplace(42.f, 42.f, 42.f); VERIFY(o->x == 42.f && o->y == 42.f && o->z == 42.f); + VERIFY(v.x == 42.f && v.y == 42.f && v.z == 42.f); + v.x = 10.f; + VERIFY(o->x == 10.f && o->y == 42.f && o->z == 42.f); } { optional o; - o.emplace({42.f, 42.f, 42.f}); + vec3& v = o.emplace({42.f, 42.f, 42.f}); VERIFY(o->x == 42.f && o->y == 42.f && o->z == 42.f); + VERIFY(v.x == 42.f && v.y == 42.f && v.z == 42.f); + v.x = 10.f; + VERIFY(o->x == 10.f && o->y == 42.f && o->z == 42.f); } { optional o; - o.emplace(42); + int& i = o.emplace(42); VERIFY(*o == 42); + VERIFY(i == 42); + i = 10; + VERIFY(*o == 10); } struct nonCopyableNonMovable @@ -647,11 +1056,11 @@ int TestOptional() // optional rvalue tests { VERIFY(*optional(1u) == 1u); - VERIFY(optional(1u).value() == 1u); - VERIFY(optional(1u).value_or(0xdeadf00d) == 1u); - VERIFY(optional().value_or(0xdeadf00d) == 0xdeadf00d); - VERIFY(optional(1u).has_value() == true); - VERIFY(optional().has_value() == false); + VERIFY(optional(1u).value() == 1u); + VERIFY(optional(1u).value_or(0xdeadf00d) == 1u); + VERIFY(optional().value_or(0xdeadf00d) == 0xdeadf00d); + VERIFY(optional(1u).has_value() == true); + VERIFY(optional().has_value() == false); VERIFY( optional(in_place, 10)->data == 10); } @@ -757,7 +1166,10 @@ int TestOptional() VERIFY(!!o == false); } - #endif // EASTL_OPTIONAL_ENABLED + #endif // EASTL_OPTIONAL_ENABLED + + nErrorCount += TestOptional_MonadicOperations(); + return nErrorCount; } diff --git a/test/source/TestRingBuffer.cpp b/test/source/TestRingBuffer.cpp index ff1399a1..d1453896 100644 --- a/test/source/TestRingBuffer.cpp +++ b/test/source/TestRingBuffer.cpp @@ -4,6 +4,7 @@ #include "EASTLTest.h" +#include #include #include #include @@ -82,7 +83,8 @@ int TestRingBuffer() typedef ring_buffer< string, vector > RBVectorString; int counter = 0; - char counterBuffer[32]; + const int kBufferSize = 32; + char counterBuffer[kBufferSize]; // explicit ring_buffer(size_type size = 0); const int kOriginalCapacity = 50; @@ -120,7 +122,7 @@ int TestRingBuffer() } // void push_back(const value_type& value); - sprintf(counterBuffer, "%d", counter++); + EA::StdC::Snprintf(counterBuffer, kBufferSize, "%d", counter++); rbVectorString.push_back(string(counterBuffer)); EATEST_VERIFY(rbVectorString.validate()); EATEST_VERIFY(!rbVectorString.empty()); @@ -149,7 +151,7 @@ int TestRingBuffer() EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference)); EATEST_VERIFY(it->empty()); - sprintf(counterBuffer, "%d", counter++); + EA::StdC::Snprintf(counterBuffer, kBufferSize, "%d", counter++); *it = counterBuffer; EATEST_VERIFY(*it == "1"); @@ -173,21 +175,21 @@ int TestRingBuffer() // Now we start hammering the ring buffer with push_back. for(eastl_size_t i = 0, iEnd = rbVectorString.capacity() * 5; i != iEnd; i++) { - sprintf(counterBuffer, "%d", counter++); + EA::StdC::Snprintf(counterBuffer, kBufferSize, "%d", counter++); rbVectorString.push_back(string(counterBuffer)); EATEST_VERIFY(rbVectorString.validate()); } int counterCheck = counter - 1; - char counterCheckBuffer[32]; - sprintf(counterCheckBuffer, "%d", counterCheck); + char counterCheckBuffer[kBufferSize]; + EA::StdC::Snprintf(counterCheckBuffer, kBufferSize, "%d", counterCheck); EATEST_VERIFY(rbVectorString.back() == counterCheckBuffer); // reverse_iterator rbegin(); // reverse_iterator rend(); for(RBVectorString::reverse_iterator ri = rbVectorString.rbegin(); ri != rbVectorString.rend(); ++ri) { - sprintf(counterCheckBuffer, "%d", counterCheck--); + EA::StdC::Snprintf(counterCheckBuffer, kBufferSize, "%d", counterCheck--); EATEST_VERIFY(*ri == counterCheckBuffer); } @@ -199,7 +201,7 @@ int TestRingBuffer() { EATEST_VERIFY(rbVectorString.validate_iterator(i) == (isf_valid | isf_current | isf_can_dereference)); EATEST_VERIFY(*i == counterCheckBuffer); - sprintf(counterCheckBuffer, "%d", ++counterCheck); + EA::StdC::Snprintf(counterCheckBuffer, kBufferSize, "%d", ++counterCheck); } // void clear(); @@ -213,7 +215,7 @@ int TestRingBuffer() // Not easy to test the expected values without some tedium. for(int j = 0; j < 10000 + (gEASTL_TestLevel * 10000); j++) { - sprintf(counterBuffer, "%d", counter++); + EA::StdC::Snprintf(counterBuffer, kBufferSize, "%d", counter++); const eastl_size_t op = rng.RandLimit(12); const eastl_size_t s = rbVectorString.size(); @@ -606,7 +608,8 @@ int TestRingBuffer() typedef ring_buffer< string, list > RBListString; int counter = 0; - char counterBuffer[32]; + const int kBufferSize = 32; + char counterBuffer[kBufferSize]; // explicit ring_buffer(size_type size = 0); const int kOriginalCapacity = 50; @@ -644,7 +647,7 @@ int TestRingBuffer() } // void push_back(const value_type& value); - sprintf(counterBuffer, "%d", counter++); + EA::StdC::Snprintf(counterBuffer, kBufferSize, "%d", counter++); rbListString.push_back(string(counterBuffer)); EATEST_VERIFY(rbListString.validate()); EATEST_VERIFY(!rbListString.empty()); @@ -673,7 +676,7 @@ int TestRingBuffer() EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference)); EATEST_VERIFY(it->empty()); - sprintf(counterBuffer, "%d", counter++); + EA::StdC::Snprintf(counterBuffer, kBufferSize, "%d", counter++); *it = counterBuffer; EATEST_VERIFY(*it == "1"); @@ -697,21 +700,21 @@ int TestRingBuffer() // Now we start hammering the ring buffer with push_back. for(eastl_size_t i = 0, iEnd = rbListString.capacity() * 5; i != iEnd; i++) { - sprintf(counterBuffer, "%d", counter++); + EA::StdC::Snprintf(counterBuffer, kBufferSize, "%d", counter++); rbListString.push_back(string(counterBuffer)); EATEST_VERIFY(rbListString.validate()); } int counterCheck = counter - 1; - char counterCheckBuffer[32]; - sprintf(counterCheckBuffer, "%d", counterCheck); + char counterCheckBuffer[kBufferSize]; + EA::StdC::Snprintf(counterCheckBuffer, kBufferSize, "%d", counterCheck); EATEST_VERIFY(rbListString.back() == counterCheckBuffer); // reverse_iterator rbegin(); // reverse_iterator rend(); for(RBListString::reverse_iterator ri = rbListString.rbegin(); ri != rbListString.rend(); ++ri) { - sprintf(counterCheckBuffer, "%d", counterCheck--); + EA::StdC::Snprintf(counterCheckBuffer, kBufferSize, "%d", counterCheck--); EATEST_VERIFY(*ri == counterCheckBuffer); } @@ -723,7 +726,7 @@ int TestRingBuffer() { EATEST_VERIFY(rbListString.validate_iterator(i) == (isf_valid | isf_current | isf_can_dereference)); EATEST_VERIFY(*i == counterCheckBuffer); - sprintf(counterCheckBuffer, "%d", ++counterCheck); + EA::StdC::Snprintf(counterCheckBuffer, kBufferSize, "%d", ++counterCheck); } // void clear(); @@ -737,7 +740,7 @@ int TestRingBuffer() // Not easy to test the expected values without some tedium. for(int j = 0; j < 10000 + (gEASTL_TestLevel * 10000); j++) { - sprintf(counterBuffer, "%d", counter++); + EA::StdC::Snprintf(counterBuffer, kBufferSize, "%d", counter++); const eastl_size_t op = rng.RandLimit(12); const eastl_size_t s = rbListString.size(); diff --git a/test/source/TestSList.cpp b/test/source/TestSList.cpp index 94a4d3a6..b64a129d 100644 --- a/test/source/TestSList.cpp +++ b/test/source/TestSList.cpp @@ -286,18 +286,16 @@ int TestSList() // void emplace_front(Args&&... args); - // void emplace_front(value_type&& value); - // void emplace_front(const value_type& value); { slist list1; - list1.emplace_front(42); + VERIFY(list1.emplace_front(42).mI == 42); VERIFY(list1.front().mI == 42); VERIFY(list1.front().mCopyCtor == 0); VERIFY(list1.front().mMoveCtor == 0); VERIFY(list1.size() == 1); VERIFY(list1.validate()); - list1.emplace_front(1,2,3,4); + VERIFY(list1.emplace_front(1,2,3,4).mI == (1 + 2 + 3 + 4)); VERIFY(list1.front().mCopyCtor == 0); VERIFY(list1.front().mMoveCtor == 0); VERIFY(list1.front().mI == (1+2+3+4)); @@ -756,6 +754,38 @@ int TestSList() } } + // size_type unique(); + { + slist ref = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + slist a = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9 }; + VERIFY(a.unique() == 34); + VERIFY(a == ref); + } + + // size_type unique(BinaryPredicate); + { + static bool bBreakComparison; + struct A + { + int mValue; + bool operator==(const A& other) const { return bBreakComparison ? false : mValue == other.mValue; } + }; + + slist ref = { {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9} }; + slist a = { {0}, {0}, {0}, {0}, {0}, {0}, {1}, {2}, {2}, {2}, {2}, {3}, {4}, {5}, + {5}, {5}, {5}, {5}, {6}, {7}, {7}, {7}, {7}, {8}, {9}, {9}, {9} }; + + bBreakComparison = true; + VERIFY(a.unique() == 0); // noop because broken comparison operator + VERIFY(a != ref); + + VERIFY(a.unique([](const A& lhs, const A& rhs) { return lhs.mValue == rhs.mValue; }) == 17); + + bBreakComparison = false; + VERIFY(a == ref); + } + // void sort(); { slist list1 = {0, 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2, 1, 0}; diff --git a/test/source/TestSegmentedVector.cpp b/test/source/TestSegmentedVector.cpp index bb920b7e..235b47f3 100644 --- a/test/source/TestSegmentedVector.cpp +++ b/test/source/TestSegmentedVector.cpp @@ -29,7 +29,7 @@ int TestSegmentedVector() { auto i = sv.begin(); - EATEST_VERIFY(*i == 0); + EATEST_VERIFY(*i == 0); EATEST_VERIFY(*i++ == 0); EATEST_VERIFY(*i++ == 1); EATEST_VERIFY(*i++ == 2); @@ -38,11 +38,245 @@ int TestSegmentedVector() { auto i = sv.begin(); - EATEST_VERIFY(*i == 0); + EATEST_VERIFY(*i == 0); EATEST_VERIFY(*(++i) == 1); EATEST_VERIFY(*(++i) == 2); EATEST_VERIFY(*(++i) == 3); } + + + { + for (int i = 4; i < 100; ++i) + { + sv.push_back(i); + } + + EATEST_VERIFY(sv.size() == 100); + // the capacity is always a multiple of the segment count, + // in this case it should be the smallest multiple of 8 + // which is >= 100, that number is 104. + EATEST_VERIFY(sv.capacity() == 104); + + // multi-segment iteration. + int i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i); + } + } + + { + // pop_back() 25 items. + for (int i = 0; i < 25; ++i) + { + sv.pop_back(); + } + + // started with 100 and then popped 25. + EATEST_VERIFY(sv.size() == 75); + + int i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i); + } + + // capacity is unchanged, this is the same as a normal vector. + EATEST_VERIFY(sv.capacity() == 104); + } + + { + sv.shrink_to_fit(); + // the smallest multiple of 8 which is >= 75 is 80. + EATEST_VERIFY(sv.capacity() == 80); + } + + // Copy/move assignment/construction: + { + // copy construction: + eastl::segmented_vector other = sv; + + // We'll use this down below. + eastl::segmented_vector other2 = sv; + + // check other has the right things in it. + int i = 0; + for (auto it = other.begin(); it != other.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i); + } + EATEST_VERIFY(other.size() == 75); + + // check that sv is unchanged + i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i); + } + EATEST_VERIFY(sv.size() == 75); + + // fill other back to 100 + for (int j = 75; j < 100; ++j) + { + other.push_back(j); + } + + // test copy assignment (growing) + sv = other; + i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i); + } + EATEST_VERIFY(sv.size() == 100); + + // check other is unchanged. + i = 0; + for (auto it = other.begin(); it != other.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i); + } + EATEST_VERIFY(other.size() == 100); + + // make other smaller. + other.clear(); + + // fill it with 10 multiples of 10 + for (int j = 0; j < 10; ++j) + { + other.push_back(10 * j); + } + + // copy assignment (shrinking) + sv = other; + i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i * 10); + } + EATEST_VERIFY(sv.size() == 10); + + + // move assignment (growing) + sv = eastl::move(other2); + i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i); + } + EATEST_VERIFY(sv.size() == 75); + + // this behavior need not be set in stone, we could decide + // the moved from object is in a different state, this is + // just the current behavior. + EATEST_VERIFY(other2.empty()); + + // move assignment (shrinking) + sv = eastl::move(other); + i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i * 10); + } + EATEST_VERIFY(sv.size() == 10); + + // this behavior need not be set in stone, we could decide + // the moved from object is in a different state, this is + // just the current behavior. + EATEST_VERIFY(other.empty()); + + // move construct + eastl::segmented_vector other3 = eastl::move(sv); + i = 0; + for (auto it = other3.begin(); it != other3.end(); ++it, ++i) + { + EATEST_VERIFY(*it == i * 10); + } + EATEST_VERIFY(other3.size() == 10); + + // this behavior need not be set in stone, we could decide + // the moved from object is in a different state, this is + // just the current behavior. + EATEST_VERIFY(sv.empty()); + + // TODO: test move/copy assign with stateful allocators + // which compare different. + } + + { + EATEST_VERIFY(sv.empty()); + // fill up sv with 100 items. + for (int i = 0; i < 100; ++i) + { + sv.push_back(i); + } + EATEST_VERIFY(sv.capacity() == 104); + + // check we can clear() and retain capacity. + sv.clear(); + EATEST_VERIFY(sv.size() == 0); + EATEST_VERIFY(sv.capacity() == 104); + } + } + + { + // resize/reserve + eastl::segmented_vector sv; + sv.reserve(27); + EATEST_VERIFY(sv.capacity() >= 27); + + sv.resize(55); + EATEST_VERIFY(sv.size() == 55); + for (auto i : sv) + { + EATEST_VERIFY(i == 0); + } + + sv.resize(100, 5); + EATEST_VERIFY(sv.size() == 100); + int i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(*it == (i < 55) ? 0 : 5); + } + + sv.resize(10); + EATEST_VERIFY(sv.size() == 10); + } + + { + // emplace_back + TestObject::Reset(); + eastl::segmented_vector sv; + for (int i = 0; i < 25; ++i) + { + sv.emplace_back(i); + } + + { + int i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(it->mX == i); + } + } + + // no copies are made when we emplace_back + EATEST_VERIFY(sv.size() == 25); + EATEST_VERIFY(TestObject::sTOCopyCtorCount == 0); + EATEST_VERIFY(TestObject::sTOCopyAssignCount == 0); + + // move construction should make no copies. + eastl::segmented_vector sv2 = eastl::move(sv); + EATEST_VERIFY(sv2.size() == 25); + EATEST_VERIFY(TestObject::sTOCopyCtorCount == 0); + EATEST_VERIFY(TestObject::sTOCopyAssignCount == 0); + + // no copies on move assignment either + sv = eastl::move(sv2); + EATEST_VERIFY(sv.size() == 25); + EATEST_VERIFY(TestObject::sTOCopyCtorCount == 0); + EATEST_VERIFY(TestObject::sTOCopyAssignCount == 0); } { @@ -56,6 +290,36 @@ int TestSegmentedVector() EATEST_VERIFY(vectorOfListOfTO.empty()); } + { + // Support for non-default constructible types. + struct S + { + int mX; + explicit S(int i) : mX{i} {} + }; + eastl::segmented_vector sv; + + sv.push_back(S{0}); + sv.push_back(S{1}); + sv.push_back(S{2}); + sv.push_back(S{3}); + sv.push_back(S{4}); + sv.emplace_back(S{5}); + sv.emplace_back(S{6}); + sv.emplace_back(S{7}); + sv.emplace_back(S{8}); + sv.emplace_back(S{9}); + + + EATEST_VERIFY(sv.size() == 10); + + int i = 0; + for (auto it = sv.begin(); it != sv.end(); ++it, ++i) + { + EATEST_VERIFY(it->mX == i); + } + } + { // Test basic segmented_vector operations. eastl::segmented_vector vectorOfInt; @@ -85,5 +349,60 @@ int TestSegmentedVector() EATEST_VERIFY(vectorOfInt.segment_count() == 0); } + { + // global operators (==, !=, <, etc.) + eastl::segmented_vector sv1; + eastl::segmented_vector sv2; + + for (auto i = 0; i < 10; ++i) + { + sv1.push_back(i); + sv2.push_back(i); + } + + EATEST_VERIFY((sv1 == sv2)); + EATEST_VERIFY(!(sv1 != sv2)); + EATEST_VERIFY((sv1 <= sv2)); + EATEST_VERIFY((sv1 >= sv2)); + EATEST_VERIFY(!(sv1 < sv2)); + EATEST_VERIFY(!(sv1 > sv2)); + + sv1.push_back(100); // Make sv1 less than sv2. + sv2.push_back(101); + + EATEST_VERIFY(!(sv1 == sv2)); + EATEST_VERIFY((sv1 != sv2)); + EATEST_VERIFY((sv1 <= sv2)); + EATEST_VERIFY(!(sv1 >= sv2)); + EATEST_VERIFY((sv1 < sv2)); + EATEST_VERIFY(!(sv1 > sv2)); + } + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + + { // Test <=> + eastl::segmented_vector sv1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + eastl::segmented_vector sv2 = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + eastl::segmented_vector sv3 = { 1, 2, 3, 4, 5 }; + eastl::segmented_vector sv4 = { 10 }; + + EATEST_VERIFY(sv1 != sv2); + EATEST_VERIFY(sv1 < sv2); + EATEST_VERIFY(sv1 != sv3); + EATEST_VERIFY(sv1 > sv3); + EATEST_VERIFY(sv4 > sv1); + EATEST_VERIFY(sv4 > sv2); + EATEST_VERIFY(sv4 > sv3); + + EATEST_VERIFY((sv1 <=> sv2) != 0); + EATEST_VERIFY((sv1 <=> sv2) < 0); + EATEST_VERIFY((sv1 <=> sv3) != 0); + EATEST_VERIFY((sv1 <=> sv3) > 0); + EATEST_VERIFY((sv4 <=> sv1) > 0); + EATEST_VERIFY((sv4 <=> sv2) > 0); + EATEST_VERIFY((sv4 <=> sv3) > 0); + } +#endif + return nErrorCount; } diff --git a/test/source/TestSmartPtr.cpp b/test/source/TestSmartPtr.cpp index 8052392d..8bdf8895 100644 --- a/test/source/TestSmartPtr.cpp +++ b/test/source/TestSmartPtr.cpp @@ -2188,6 +2188,67 @@ static int Test_safe_ptr() return nErrorCount; } +template +bool equivalent_owner_before(const T& lhs, const U& rhs) { return !lhs.owner_before(rhs) && !rhs.owner_before(lhs); } + +template +bool equivalent(const T& lhs, const U& rhs, const Compare& cmp) { return !cmp(lhs, rhs) && !cmp(rhs, lhs); } + +static int Test_owner_before() +{ + using namespace SmartPtrTest; + using namespace eastl; + + int nErrorCount = 0; + + struct Foo + { + int n1; + int n2; + Foo(int a, int b) : n1(a), n2(b) {} + }; + + auto p1 = make_shared(1, 2); + shared_ptr p2(p1, &p1->n1); + shared_ptr p3(p1, &p1->n2); + + auto unrelated = make_shared(1, 2); + + // owner_before + { + EATEST_VERIFY(equivalent_owner_before(p1, p2)); + EATEST_VERIFY(equivalent_owner_before(p1, p3)); + EATEST_VERIFY(equivalent_owner_before(p2, p3)); + + EATEST_VERIFY(!equivalent_owner_before(p1, unrelated)); + EATEST_VERIFY(!equivalent_owner_before(p2, unrelated)); + EATEST_VERIFY(!equivalent_owner_before(p3, unrelated)); + } + + // owner_less> + { + EATEST_VERIFY(equivalent(p1, p1, owner_less>{})); + EATEST_VERIFY(equivalent(p2, p3, owner_less>{})); + + EATEST_VERIFY(!equivalent(p1, unrelated, owner_less>{})); + } + + // owner_less + { + owner_less cmp; + + EATEST_VERIFY(equivalent(p1, p2, cmp)); + EATEST_VERIFY(equivalent(p1, p3, cmp)); + EATEST_VERIFY(equivalent(p2, p3, cmp)); + + EATEST_VERIFY(!equivalent(p1, unrelated, cmp)); + EATEST_VERIFY(!equivalent(p2, unrelated, cmp)); + EATEST_VERIFY(!equivalent(p3, unrelated, cmp)); + } + + return nErrorCount; +} + int TestSmartPtr() { @@ -2207,6 +2268,7 @@ int TestSmartPtr() nErrorCount += Test_linked_array(); nErrorCount += Test_intrusive_ptr(); nErrorCount += Test_safe_ptr(); + nErrorCount += Test_owner_before(); EATEST_VERIFY(A::mCount == 0); EATEST_VERIFY(RefCountTest::mCount == 0); diff --git a/test/source/TestString.inl b/test/source/TestString.inl index 430cef9e..193a9291 100644 --- a/test/source/TestString.inl +++ b/test/source/TestString.inl @@ -1717,6 +1717,49 @@ int TEST_STRING_NAME() VERIFY(str.rfind(LITERAL('1'), 20) == StringType::npos); } + { + StringType str(LITERAL("abcdabcdabcdabcd")); + + VERIFY(str.rfind(StringType(LITERAL("d"))) == (str.size() - 1)); + VERIFY(str.rfind(StringType(LITERAL("abc"))) == (str.size() - 4)); + VERIFY(str.rfind(StringType(LITERAL("dab"))) == (str.size() - 5)); + VERIFY(str.rfind(StringType(LITERAL("e"))) == StringType::npos); + VERIFY(str.rfind(StringType(LITERAL("abcde"))) == StringType::npos); + + VERIFY(str.rfind(StringType(LITERAL("d")), 8) == 7); + VERIFY(str.rfind(StringType(LITERAL("abc")), 8) == 8); + VERIFY(str.rfind(StringType(LITERAL("abc")), 7) == 4); + VERIFY(str.rfind(StringType(LITERAL("dab")), 8) == 7); + VERIFY(str.rfind(StringType(LITERAL("dab")), 7) == 7); + VERIFY(str.rfind(StringType(LITERAL("e")), 8) == StringType::npos); + VERIFY(str.rfind(StringType(LITERAL("abcde")), 8) == StringType::npos); + + VERIFY(str.rfind(LITERAL("d")) == (str.size() - 1)); + VERIFY(str.rfind(LITERAL("abcd")) == (str.size() - 4)); + VERIFY(str.rfind(LITERAL("abc")) == (str.size() - 4)); + VERIFY(str.rfind(LITERAL("e")) == StringType::npos); + VERIFY(str.rfind(LITERAL("abcde")) == StringType::npos); + + VERIFY(str.rfind(LITERAL("d"), 8) == 7); + VERIFY(str.rfind(LITERAL("abc"), 8) == 8); + VERIFY(str.rfind(LITERAL("abc"), 7) == 4); + VERIFY(str.rfind(LITERAL("dab"), 8) == 7); + VERIFY(str.rfind(LITERAL("dab"), 7) == 7); + VERIFY(str.rfind(LITERAL("e"), 8) == StringType::npos); + VERIFY(str.rfind(LITERAL("abcde"), 8) == StringType::npos); + } + + { + StringType str(LITERAL("abcdeaababcdabcdx")); + + VERIFY(str.rfind(StringType(LITERAL("abcde"))) == 0); + VERIFY(str.rfind(StringType(LITERAL("abcde")), 8) == 0); + + VERIFY(str.rfind(LITERAL("abcde")) == 0); + VERIFY(str.rfind(LITERAL("abcde"), 8) == 0); + } + + // size_type find_first_of(const this_type& x, size_type position = 0) const EA_NOEXCEPT; // size_type find_first_of(const value_type* p, size_type position = 0) const; // size_type find_first_of(const value_type* p, size_type position, size_type n) const; diff --git a/test/source/TestStringHashMap.cpp b/test/source/TestStringHashMap.cpp index be7e1f63..a3624cac 100644 --- a/test/source/TestStringHashMap.cpp +++ b/test/source/TestStringHashMap.cpp @@ -89,6 +89,128 @@ int TestStringHashMap() EATEST_VERIFY(stringHashMap.bucket_count() == 1); } + // emplace/try_emplace + { + 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++) + { + { + // This should construct the object from i. + auto x = stringHashMap.emplace(strings[i], i); + // insertion did happen. + EATEST_VERIFY(x.second); + // We got the correct value + EATEST_VERIFY(x.first->second.mX == i); + } + { + // This should not insert anything. + auto x = stringHashMap.emplace(strings[i], i+1); + // insertion did not happen. + EATEST_VERIFY(!x.second); + // We got the correct (existing) value + EATEST_VERIFY(x.first->second.mX == 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++) + { + { + // This should construct the object from i. + auto x = stringHashMap.try_emplace(strings[i], i); + // insertion did happen. + EATEST_VERIFY(x.second); + // We got the correct value + EATEST_VERIFY(x.first->second.mX == i); + } + { + // This should not insert anything. + auto x = stringHashMap.try_emplace(strings[i], i+1); + // insertion did not happen. + EATEST_VERIFY(!x.second); + // We got the correct (existing) value + EATEST_VERIFY(x.first->second.mX == 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); + } + + // emplace_hint/try_emplace(hint,...) + { + 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); + + auto hint = stringHashMap.begin(); + for (int i = 0; i < (int)kStringCount; i++) + { + // This should construct the object from i. + auto ret = stringHashMap.emplace_hint(hint, strings[i], i); + EATEST_VERIFY(ret->second.mX == i); + // Try again, this should return the already existing value. + auto ret2 = stringHashMap.emplace_hint(hint, strings[i], i+1); + // Should get the same iterator. + EATEST_VERIFY(ret2 == ret); + // Should get old existing value. + EATEST_VERIFY(ret2->second.mX == i); + hint = ret; + } + + 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); + + hint = stringHashMap.begin(); + for (int i = 0; i < (int)kStringCount; i++) + { + // This should construct the object from i. + auto ret = stringHashMap.try_emplace(hint, strings[i], i); + EATEST_VERIFY(ret->second.mX == i); + // Try again, this should return the already existing value. + auto ret2 = stringHashMap.try_emplace(hint, strings[i], i+1); + // Should get the same iterator. + EATEST_VERIFY(ret2 == ret); + // Should get old existing value. + EATEST_VERIFY(ret2->second.mX == i); + hint = ret; + } + 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 diff --git a/test/source/TestStringView.inl b/test/source/TestStringView.inl index cd4214ee..1b5005b0 100644 --- a/test/source/TestStringView.inl +++ b/test/source/TestStringView.inl @@ -390,6 +390,48 @@ int TEST_STRING_NAME() VERIFY(str.rfind(LITERAL('1'), str.length() - 2) == StringViewT::npos); } + { + StringViewT str(LITERAL("abcdabcdabcdabcd")); + + VERIFY(str.rfind(StringViewT(LITERAL("d"))) == (str.size() - 1)); + VERIFY(str.rfind(StringViewT(LITERAL("abcd"))) == (str.size() - 4)); + VERIFY(str.rfind(StringViewT(LITERAL("abc"))) == (str.size() - 4)); + VERIFY(str.rfind(StringViewT(LITERAL("dab"))) == (str.size() - 5)); + VERIFY(str.rfind(StringViewT(LITERAL("e"))) == StringViewT::npos); + VERIFY(str.rfind(StringViewT(LITERAL("abcde"))) == StringViewT::npos); + + VERIFY(str.rfind(StringViewT(LITERAL("d")), 8) == 7); + VERIFY(str.rfind(StringViewT(LITERAL("abc")), 8) == 8); + VERIFY(str.rfind(StringViewT(LITERAL("abc")), 7) == 4); + VERIFY(str.rfind(StringViewT(LITERAL("dab")), 8) == 7); + VERIFY(str.rfind(StringViewT(LITERAL("dab")), 7) == 7); + VERIFY(str.rfind(StringViewT(LITERAL("e")), 8) == StringViewT::npos); + VERIFY(str.rfind(StringViewT(LITERAL("abcde")), 8) == StringViewT::npos); + + VERIFY(str.rfind(LITERAL("d")) == (str.size() - 1)); + VERIFY(str.rfind(LITERAL("abc")) == (str.size() - 4)); + VERIFY(str.rfind(LITERAL("e")) == StringViewT::npos); + VERIFY(str.rfind(LITERAL("abcde")) == StringViewT::npos); + + VERIFY(str.rfind(LITERAL("d"), 8) == 7); + VERIFY(str.rfind(LITERAL("abc"), 8) == 8); + VERIFY(str.rfind(LITERAL("abc"), 7) == 4); + VERIFY(str.rfind(LITERAL("dab"), 8) == 7); + VERIFY(str.rfind(LITERAL("dab"), 7) == 7); + VERIFY(str.rfind(LITERAL("e"), 8) == StringViewT::npos); + VERIFY(str.rfind(LITERAL("abcde"), 8) == StringViewT::npos); + } + + { + StringViewT str(LITERAL("abcdeaababcdabcdx")); + + VERIFY(str.rfind(StringViewT(LITERAL("abcde"))) == 0); + VERIFY(str.rfind(StringViewT(LITERAL("abcde")), 8) == 0); + + VERIFY(str.rfind(LITERAL("abcde")) == 0); + VERIFY(str.rfind(LITERAL("abcde"), 8) == 0); + } + // EA_CONSTEXPR size_type find_first_of(basic_string_view s, size_type pos = 0) const EA_NOEXCEPT; // EA_CONSTEXPR size_type find_first_of(T c, size_type pos = 0) const EA_NOEXCEPT; // EA_CONSTEXPR size_type find_first_of(const T* s, size_type pos, size_type n) const; diff --git a/test/source/TestTypeTraits.cpp b/test/source/TestTypeTraits.cpp index 0c8190e8..580e7a98 100644 --- a/test/source/TestTypeTraits.cpp +++ b/test/source/TestTypeTraits.cpp @@ -1910,10 +1910,11 @@ int TestTypeTraits() eastl::add_const::type i32 = 47; EATEST_VERIFY(i32 == 47); - eastl::add_volatile::type i16 = 47; - EATEST_VERIFY(++i16 == 48); + // C++20 deprecated a lot of volatile operations + eastl::add_volatile::type i16 = 47; + EATEST_VERIFY(i16 + 1 == 48); - eastl::add_cv::type i64 = 47; + eastl::add_cv::type i64 = 47; EATEST_VERIFY(i64 == 47); } diff --git a/test/source/TestUtility.cpp b/test/source/TestUtility.cpp index 1a90e19b..f2601d81 100644 --- a/test/source/TestUtility.cpp +++ b/test/source/TestUtility.cpp @@ -108,23 +108,25 @@ static int TestUtilityPair() pair p1 = make_pair(int(0), float(1)); EATEST_VERIFY((p1.first == 0) && (p1.second == 1.f)); + EASTL_INTERNAL_DISABLE_DEPRECATED() // make_pair_ref: was declared deprecated pair p2 = make_pair_ref(int(0), float(1)); EATEST_VERIFY((p2.first == 0) && (p2.second == 1.f)); + EASTL_INTERNAL_RESTORE_DEPRECATED() // make_pair_ref: was declared deprecated pair p3 = eastl::make_pair("a", 1); EATEST_VERIFY((EA::StdC::Strcmp(p3.first, "a") == 0) && (p3.second == 1)); - pair p4 = eastl::make_pair("a", 1); + pair p4 = eastl::make_pair("a", 1); EATEST_VERIFY((EA::StdC::Strcmp(p4.first, "a") == 0) && (p4.second == 1)); - pair p5 = eastl::make_pair(1, "b"); + pair p5 = eastl::make_pair(1, "b"); EATEST_VERIFY((p5.first == 1) && (EA::StdC::Strcmp(p5.second, "b") == 0)); #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) - pair p6 = eastl::make_pair(1, 2); - pair p7 = eastl::make_pair(2, 1); - pair p8 = eastl::make_pair(7, 8); - pair p9 = eastl::make_pair(10, 1); + pair p6 = eastl::make_pair(1, 2); + pair p7 = eastl::make_pair(2, 1); + pair p8 = eastl::make_pair(7, 8); + pair p9 = eastl::make_pair(10, 1); EATEST_VERIFY( (p6 <=> p7) != 0); EATEST_VERIFY( (p6 <=> p6) == 0); @@ -162,6 +164,102 @@ static int TestUtilityPair() #endif } +#if EASTL_TUPLE_ENABLED + // get(pair&) + // get(pair&) + // get(pair&) + { + pair p{2, 3.3f}; + EATEST_VERIFY(get<0>(p) == 2); + EATEST_VERIFY(get<1>(p) == 3.3f); + + EATEST_VERIFY(get(p) == 2); + EATEST_VERIFY(get(p) == 3.3f); + EATEST_VERIFY((get(p) == 2)); + EATEST_VERIFY((get(p) == 3.3f)); + + get(p) = 3; + EATEST_VERIFY(p.first == 3); + get(p) = 4.0f; + EATEST_VERIFY(p.second == 4.0f); + + // no matching overload, index is not in range. + // get<2>(p); + } + + // get(const pair&) + // get(const pair&) + // get(const pair&) + { + const pair p{ 2, 3.3f }; + EATEST_VERIFY(get<0>(p) == 2); + EATEST_VERIFY(get<1>(p) == 3.3f); + + EATEST_VERIFY(get(p) == 2); + EATEST_VERIFY(get(p) == 3.3f); + EATEST_VERIFY((get(p) == 2)); + EATEST_VERIFY((get(p) == 3.3f)); + + // cannot assign to a const&. + // get(p) = 3; + // get(p) = 4.0f; + } + + // get(pair&&) + // get(pair&&) + // get(pair&&) + { + EATEST_VERIFY(get<0>(pair{ 2, MoveOnlyType(3) }) == 2); + MoveOnlyType mo = get<1>(pair{ 2, MoveOnlyType(3) }); + EATEST_VERIFY(mo == MoveOnlyType(3)); + + EATEST_VERIFY(get(pair{ 2, MoveOnlyType(3) }) == 2); + EATEST_VERIFY(get(pair{ 2, MoveOnlyType(3) }) == MoveOnlyType(3)); + EATEST_VERIFY((get(pair{ 2, MoveOnlyType(3) }) == 2)); + EATEST_VERIFY((get(pair{ 2, MoveOnlyType(3) }) == MoveOnlyType(3))); + } + + // get(const pair&&) + // get(const pair&&) + // get(const pair&&) + { + using pair_type = const pair; + EATEST_VERIFY(get<0>(pair_type{ 2, MoveOnlyType(3) }) == 2); + EATEST_VERIFY(get<1>(pair_type{ 2, MoveOnlyType(3) }) == MoveOnlyType(3)); + + EATEST_VERIFY(get(pair_type{ 2, MoveOnlyType(3) }) == 2); + EATEST_VERIFY(get(pair_type{ 2, MoveOnlyType(3) }) == MoveOnlyType(3)); + EATEST_VERIFY((get(pair_type{ 2, MoveOnlyType(3) }) == 2)); + EATEST_VERIFY((get(pair_type{ 2, MoveOnlyType(3) }) == MoveOnlyType(3))); + } + + // get(pair) + { + pair p{ 1, 2 }; + EATEST_VERIFY(get<0>(p) == 1); + EATEST_VERIFY(get<1>(p) == 2); + + get<0>(p) = 3; + EATEST_VERIFY(p.first == 3); + get<1>(p) = 4; + EATEST_VERIFY(p.second == 4); + + // no matching overload, ambiguous which element. + // get(p) + } + + // get(pair) and get(pair) are constexpr + { + constexpr pair cp{ 1, 2.0f }; + constexpr int ci = get<0>(cp); + constexpr float cf = get(cp); + static_assert(ci == 1, "unexpected return from get(pair)"); + static_assert(cf == 2.0f, "unexpected return from get(pair)"); + } + + +#endif + { // One-off tests and regressions @@ -810,58 +908,102 @@ static int TestUtilityIntegralComp() nErrorCount += TestCmpCommon(); nErrorCount += TestCmpCommon(); + // // Test integral comparison among different types - nErrorCount += TestUtilityCmpEql(int(0), short(0)); - nErrorCount += TestUtilityCmpEql(short(2), long(2)); - nErrorCount += TestUtilityCmpEql(short(3), unsigned long(3)); - nErrorCount += TestUtilityCmpEql(int(-5), long long(-5)); - nErrorCount += TestUtilityCmpEql(short(-100), long long(-100)); - nErrorCount += TestUtilityCmpEql(unsigned int(100), long(100)); - nErrorCount += TestUtilityCmpEql(unsigned long long(100), int(100)); - - nErrorCount += TestUtilityCmpLess(int(0), long long(1)); - nErrorCount += TestUtilityCmpLess(int(-1), unsigned long(1)); - nErrorCount += TestUtilityCmpLess(short(-100), long long(100)); + // + // we're now using integer-suffixes because Clang on + // unix was failing to construct integer types with a + // space in them, like 'unsigned long', e.g. + // + // TestUilityCmpLess(4, unsigned long(4)) + // + // + // Integer literal suffixes: + // + // u/U : unsigned (`unsigned int` by default) + // + // l/L : long + // ll/LL : long long + // z/Z : (c++23) the signed version of size_t + // uz/UZ : (c++23) size_t + // + // unsignedness & the width suffixes can be combined together. + // thus `2ull` is an unsigned long long (of value 2). because + // there's no ordering for u vs ll, we can also write the + // equivalent `2llu`, or `2ULL` + // + // NOTE: integer literals that are too large to fit into the + // requested type will be promoted a little bit to a + // "sensible" (post c++11) higher category. so the + // following: + // + // 235763456234623452345L + // + // is way to large to fit into `L` (long int), and so + // and so will be interpreted as a `long long int` literal. + // + // DOUBLE NOTE: the above promotion rules are expanded a bit for + // binary, hexadecimal, or octal literals. basically + // they can be unsigned sometimes. this is why + // compilers give you a warning when you mess this up. + // + // the more you know!! + // + // + // Further reading + // https://en.cppreference.com/w/cpp/language/integer_literal + // + nErrorCount += TestUtilityCmpEql(0, short(0)); + nErrorCount += TestUtilityCmpEql(short(2), 2l); + nErrorCount += TestUtilityCmpEql(short(3), 3ul); + nErrorCount += TestUtilityCmpEql(-5, -5ll); + nErrorCount += TestUtilityCmpEql(short(-100), -100ll); + nErrorCount += TestUtilityCmpEql(100u, 100l); + nErrorCount += TestUtilityCmpEql(100ull, 100); + + nErrorCount += TestUtilityCmpLess(0, 1ll); + nErrorCount += TestUtilityCmpLess(-1, 1ul); + nErrorCount += TestUtilityCmpLess(short(-100), 100ll); nErrorCount += TestUtilityCmpLess(eastl::numeric_limits::min(), short(0)); nErrorCount += TestUtilityCmpLess(short(0), eastl::numeric_limits::max()); nErrorCount += TestUtilityCmpLess(eastl::numeric_limits::min(), eastl::numeric_limits::max()); nErrorCount += TestUtilityCmpLess(eastl::numeric_limits::max(), eastl::numeric_limits::max()); nErrorCount += TestUtilityCmpLess(eastl::numeric_limits::max(), eastl::numeric_limits::max()); - nErrorCount += TestUtilityCmpLess(int(-100), unsigned int(0)); + nErrorCount += TestUtilityCmpLess(-100, 0u); nErrorCount += TestUtilityCmpLess(eastl::numeric_limits::min(), eastl::numeric_limits::min()); - nErrorCount += TestUtilityCmpGreater(int(1), short(0)); - nErrorCount += TestUtilityCmpGreater(unsigned long(1), int(-1)); - nErrorCount += TestUtilityCmpGreater(unsigned long long(100), short(-100)); + nErrorCount += TestUtilityCmpGreater(1, short(0)); + nErrorCount += TestUtilityCmpGreater(1ul, -1); + nErrorCount += TestUtilityCmpGreater(100ull, short(-100)); nErrorCount += TestUtilityCmpGreater(short(0), eastl::numeric_limits::min()); - nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits::max(), unsigned short(5)); + nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits::max(), (unsigned short)5); nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits::max(), eastl::numeric_limits::min()); nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits::max(), eastl::numeric_limits::max()); nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits::max(), eastl::numeric_limits::max()); - nErrorCount += TestUtilityCmpGreater(unsigned int(0), int(-100)); + nErrorCount += TestUtilityCmpGreater(0u, -100); nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits::min(), eastl::numeric_limits::min()); - nErrorCount += TestUtilityCmpLessEq(int(0), short(1)); - nErrorCount += TestUtilityCmpLessEq(int(-1), long long(-1)); - nErrorCount += TestUtilityCmpLessEq(short(-100), unsigned long long(100)); - nErrorCount += TestUtilityCmpLessEq(short(-100), long long(-100)); + nErrorCount += TestUtilityCmpLessEq(0, short(1)); + nErrorCount += TestUtilityCmpLessEq(-1, -1ll); + nErrorCount += TestUtilityCmpLessEq(short(-100), 100ull); + nErrorCount += TestUtilityCmpLessEq(short(-100), -100ll); nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits::min(), short(0)); nErrorCount += TestUtilityCmpLessEq(short(0), eastl::numeric_limits::max()); nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits::min(), eastl::numeric_limits::min()); nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits::max(), eastl::numeric_limits::max()); nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits::max(), eastl::numeric_limits::max()); - nErrorCount += TestUtilityCmpLessEq(int(50), unsigned int(50)); + nErrorCount += TestUtilityCmpLessEq(50, 50u); nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits::min(), eastl::numeric_limits::min()); - nErrorCount += TestUtilityCmpGreaterEq(int(1), short(1)); - nErrorCount += TestUtilityCmpGreaterEq(long long(-1), int(-1)); - nErrorCount += TestUtilityCmpGreaterEq(long long(-100), short(-100)); - nErrorCount += TestUtilityCmpGreaterEq(short(0), long(0)); + nErrorCount += TestUtilityCmpGreaterEq(1, short(1)); + nErrorCount += TestUtilityCmpGreaterEq(-1ll, -1); + nErrorCount += TestUtilityCmpGreaterEq(-100ll, short(-100)); + nErrorCount += TestUtilityCmpGreaterEq(short(0), 0l); nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits::max(), eastl::numeric_limits::max()); nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits::max(), eastl::numeric_limits::min()); nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits::max(), eastl::numeric_limits::max()); nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits::max(), eastl::numeric_limits::max()); - nErrorCount += TestUtilityCmpGreaterEq(unsigned int(0), int(0)); + nErrorCount += TestUtilityCmpGreaterEq(0u, 0); nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits::min(), eastl::numeric_limits::min()); // Test in_range diff --git a/test/source/TestVariant.cpp b/test/source/TestVariant.cpp index 2a78a89c..111cc6fc 100644 --- a/test/source/TestVariant.cpp +++ b/test/source/TestVariant.cpp @@ -9,6 +9,9 @@ #include #include +// 4512/4626 - 'class' : assignment operator could not be generated. // This disabling would best be put elsewhere. +EA_DISABLE_VC_WARNING(4512 4626); + #ifdef EA_COMPILER_CPP14_ENABLED #include "ConceptImpls.h" #include @@ -280,8 +283,9 @@ int TestVariantHoldsAlternative() using v_t = variant; // default construct first type v_t v; - VERIFY(!holds_alternative(v)); // Verify that a query for a T not in the variant typelist returns false. - VERIFY(!holds_alternative(v)); // Verify that a query for a T not in the variant typelist returns false. + // no matching overload, type is not an alternative. + // holds_alternative(v); + // holds_alternative(v); VERIFY( holds_alternative(v)); // variant does hold an int, because its a default constructible first parameter VERIFY(!holds_alternative(v)); // variant does not hold a short } @@ -290,9 +294,10 @@ int TestVariantHoldsAlternative() using v_t = variant; // default construct monostate v_t v; - VERIFY(!holds_alternative(v)); // Verify that a query for a T not in the variant typelist returns false. - VERIFY(!holds_alternative(v)); // Verify that a query for a T not in the variant typelist returns false. - VERIFY(!holds_alternative(v)); // variant does not hold an int + // no matching overload, type is not an alternative. + // holds_alternative(v); + // holds_alternative(v); + VERIFY(!holds_alternative(v)); // variant does not hold an int VERIFY(!holds_alternative(v)); // variant does not hold a short } @@ -378,7 +383,7 @@ int TestVariantValuelessByException() VERIFY(!v.valueless_by_exception()); } - // TODO(rparolin): review exception safety for variant types + // TODO(rparolin): review exception safety for variant types // // { // #if EASTL_EXCEPTIONS_ENABLED @@ -564,12 +569,12 @@ int TestVariantSwap() v1.swap(v2); - VERIFY(get(v1) == 24); + VERIFY(get(v1) == 24); VERIFY(get(v2) == 42); v1.swap(v2); - VERIFY(get(v1) == 42); + VERIFY(get(v1) == 42); VERIFY(get(v2) == 24); } @@ -577,13 +582,13 @@ int TestVariantSwap() variant v1 = "Hello"; variant v2 = "World"; - VERIFY(get(v1) == "Hello"); + VERIFY(get(v1) == "Hello"); VERIFY(get(v2) == "World"); v1.swap(v2); VERIFY(get(v1) == "World"); - VERIFY(get(v2) == "Hello"); + VERIFY(get(v2) == "Hello"); } return nErrorCount; @@ -787,7 +792,7 @@ int TestVariantVisitorOverloaded() [&](int) { count++; }, [&](string) { count++; }, [&](double) { count++; }, - [&](long) { count++; }), + [&](long) { count++; }), e ); } @@ -1137,7 +1142,7 @@ int TestVariantVisitorReturn() bool operator()(bool) { return false; } }; - eastl::visit(MyVisitor{}, v); + eastl::visit(MyVisitor{}, v); EATEST_VERIFY(bVisited); } @@ -1152,7 +1157,7 @@ int TestVariantVisitorReturn() bool operator()(bool) { return false; } }; - eastl::visit(MyVisitor{}, v); + eastl::visit(MyVisitor{}, v); EATEST_VERIFY(bVisited); } @@ -1821,3 +1826,5 @@ int TestVariant() #else int TestVariant() { return 0; } #endif + +EA_RESTORE_VC_WARNING(); diff --git a/test/source/TestVector.cpp b/test/source/TestVector.cpp index 05f72e89..2e4c2d58 100644 --- a/test/source/TestVector.cpp +++ b/test/source/TestVector.cpp @@ -1543,26 +1543,28 @@ int TestVector() // because the existing elements of this were allocated by a different allocator and // will be freed in the future with the allocator copied from x. // The test below should work for the case of EASTL_ALLOCATOR_COPY_ENABLED == 0 or 1. - InstanceAllocator::reset_all(); - - InstanceAllocator ia0((uint8_t)0); - InstanceAllocator ia1((uint8_t)1); - - eastl::vector v0((eastl_size_t)1, (int)0, ia0); - eastl::vector v1((eastl_size_t)1, (int)1, ia1); + { + InstanceAllocator ia0((uint8_t)0); + InstanceAllocator ia1((uint8_t)1); + + eastl::vector v0((eastl_size_t)1, (int)0, ia0); + eastl::vector v1((eastl_size_t)1, (int)1, ia1); + + EATEST_VERIFY((v0.front() == 0) && (v1.front() == 1)); + EATEST_VERIFY(v0.get_allocator() != v1.get_allocator()); + v0 = v1; + EATEST_VERIFY((v0.front() == 1) && (v1.front() == 1)); + EATEST_VERIFY(InstanceAllocator::mMismatchCount == 0); + EATEST_VERIFY(v0.validate()); + EATEST_VERIFY(v1.validate()); + bool allocatorsEqual = (v0.get_allocator() == v1.get_allocator()); + EATEST_VERIFY(allocatorsEqual == (bool)EASTL_ALLOCATOR_COPY_ENABLED); + + // destroying containers to invoke InstanceAllocator::deallocate() checks + } - EATEST_VERIFY((v0.front() == 0) && (v1.front() == 1)); -#if EASTL_ALLOCATOR_COPY_ENABLED - EATEST_VERIFY(v0.get_allocator() != v1.get_allocator()); -#endif - v0 = v1; - EATEST_VERIFY((v0.front() == 1) && (v1.front() == 1)); - EATEST_VERIFY(InstanceAllocator::mMismatchCount == 0); - EATEST_VERIFY(v0.validate()); - EATEST_VERIFY(v1.validate()); -#if EASTL_ALLOCATOR_COPY_ENABLED - EATEST_VERIFY(v0.get_allocator() == v1.get_allocator()); -#endif + EATEST_VERIFY_MSG(InstanceAllocator::mMismatchCount == 0, "Container elements should be deallocated by the allocator that allocated it."); + InstanceAllocator::reset_all(); } { @@ -1750,7 +1752,6 @@ int TestVector() // 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); @@ -1769,8 +1770,12 @@ int TestVector() VERIFY(v1.empty() && !v2.empty()); v1.swap(v2); VERIFY(!v1.empty() && v2.empty()); + + // destroying containers to invoke InstanceAllocator::deallocate() checks } - VERIFY(InstanceAllocator::mMismatchCount == 0); + + EATEST_VERIFY_MSG(InstanceAllocator::mMismatchCount == 0, "Container elements should be deallocated by the allocator that allocated it."); + InstanceAllocator::reset_all(); } } #endif @@ -1807,5 +1812,54 @@ int TestVector() } } + // Tests for erase_unordered + { + { + eastl::vector vec = {0, 1, 2, 3}; + auto numErased = eastl::erase_unsorted(vec, 1); + EATEST_VERIFY(numErased == 1); + EATEST_VERIFY(VerifySequence(vec, {0, 3, 2}, "erase_unordered") ); + } + { + eastl::vector vec = {}; + auto numErased = eastl::erase_unsorted(vec, 42); + EATEST_VERIFY(numErased == 0); + EATEST_VERIFY(vec.size() == 0); + } + // The following test checks that the correct implementation is called for vector by checking the order + // of the remaining values. It is not a strict requirement that they have this order but it + // is expected to be the result based on that it minimizes the amount of work. + { + eastl::vector vec = {0, 1, 2, 3, 1, 5, 6, 1, 8, 9}; + auto numErased = eastl::erase_unsorted(vec, 1); + EATEST_VERIFY(numErased == 3); + EATEST_VERIFY(VerifySequence(vec, {0, 9, 2, 3, 8, 5, 6}, "erase_unordered") ); + } + } + + // Tests for erase_unordered_if + { + { + eastl::vector vec = {0, 1, 2, 3}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 2); + EATEST_VERIFY(VerifySequence(vec, {0, 2}, "erase_unordered_if") ); + } + { + eastl::vector vec = {}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 0); + EATEST_VERIFY(vec.size() == 0); + } + // The following test checks that the correct implementation is called for vector by checking the order + // of the remaining values. It is not a strict requirement that they have this order but it + // is expected to be the result based on that it minimizes the amount of work. + { + eastl::vector vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto numErased = eastl::erase_unsorted_if(vec, [](const int& v) { return v % 2 == 1; }); + EATEST_VERIFY(numErased == 5); + EATEST_VERIFY(VerifySequence(vec, {0, 8, 2, 6, 4}, "erase_unordered_if") ); + } + } return nErrorCount; } diff --git a/test/source/main.cpp b/test/source/main.cpp index e67b14cb..7e842a9f 100644 --- a/test/source/main.cpp +++ b/test/source/main.cpp @@ -6,6 +6,7 @@ #include "EASTLTest.h" #include #include +#include EA_DISABLE_ALL_VC_WARNINGS() #include @@ -87,12 +88,17 @@ int EAMain(int argc, char* argv[]) testSuite.AddTest("Algorithm", TestAlgorithm); testSuite.AddTest("Allocator", TestAllocator); + testSuite.AddTest("AllocatorPropagate", TestAllocatorPropagate); testSuite.AddTest("Any", TestAny); testSuite.AddTest("Array", TestArray); +#if defined(EA_COMPILER_CPP20_ENABLED) + testSuite.AddTest("Bit", TestBit); +#endif testSuite.AddTest("BitVector", TestBitVector); testSuite.AddTest("Bitset", TestBitset); testSuite.AddTest("CharTraits", TestCharTraits); testSuite.AddTest("Chrono", TestChrono); + testSuite.AddTest("Concepts", TestConcepts); testSuite.AddTest("Deque", TestDeque); testSuite.AddTest("Extra", TestExtra); testSuite.AddTest("Finally", TestFinally); @@ -144,7 +150,10 @@ int EAMain(int argc, char* argv[]) testSuite.AddTest("VectorMap", TestVectorMap); testSuite.AddTest("VectorSet", TestVectorSet); testSuite.AddTest("AtomicBasic", TestAtomicBasic); - testSuite.AddTest("AtomicAsm", TestAtomicAsm); +#if EASTL_THREAD_SUPPORT_AVAILABLE + testSuite.AddTest("AtomicMT", TestAtomicMultiThreaded); +#endif + testSuite.AddTest("AtomicAsm", TestAtomicAsm); testSuite.AddTest("Bitcast", TestBitcast);