diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2025-06-25 12:39:04 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2025-06-25 12:39:04 +0200 |
commit | d9ed004d0928567ab822d2b96862c33454e2e1c7 (patch) | |
tree | 5c4a0afb789249947145e0d1fe3eceae0cea6091 /EASTL | |
parent | 2083c3d97ed2946fd8168b02a2ec3f8e585ecb48 (diff) | |
parent | da5ddb55fd888d6d5ef185acdb054deac335717b (diff) |
Diffstat (limited to 'EASTL')
99 files changed, 5444 insertions, 1165 deletions
diff --git a/EASTL/.github/workflows/c-cpp.yml b/EASTL/.github/workflows/c-cpp.yml index 1537c9c..0be723e 100644 --- a/EASTL/.github/workflows/c-cpp.yml +++ b/EASTL/.github/workflows/c-cpp.yml @@ -30,6 +30,7 @@ jobs: os: [ windows-latest, ubuntu-latest ] compiler: [ clang, gcc, msvc ] configuration: [ Debug, Release ] + std_iter_compatibility: [ std_iter_category_disabled, std_iter_category_enabled ] exclude: - os: windows-latest compiler: gcc @@ -43,13 +44,13 @@ jobs: cxxflags: '/std:c++20 /Zc:char8_t' - os: ubuntu-latest compiler: clang - cc: 'clang-11' - cxx: 'clang++-11' + cc: 'clang-14' + cxx: 'clang++-14' cxxflags: '-std=c++20' - os: ubuntu-latest compiler: gcc - cc: 'gcc-10' - cxx: 'g++-10' + cc: 'gcc-12' + cxx: 'g++-12' cxxflags: '-std=c++2a' name: Build EASTL @@ -63,7 +64,7 @@ jobs: path: Code/ - run: mkdir build - - run: cd build && cmake ../Code -DEASTL_BUILD_BENCHMARK:BOOL=ON -DEASTL_BUILD_TESTS:BOOL=ON + - run: cd build && cmake ../Code -DEASTL_BUILD_BENCHMARK:BOOL=ON -DEASTL_BUILD_TESTS:BOOL=ON -DEASTL_STD_ITERATOR_CATEGORY_ENABLED:BOOL=${{ contains(matrix.std_iter_compatibility, 'enabled') && 'ON' || 'OFF' }} env: CXXFLAGS: ${{ matrix.cxxflags }} CXX: ${{ matrix.cxx }} diff --git a/EASTL/.gitignore b/EASTL/.gitignore index 8d148cd..92749f4 100644 --- a/EASTL/.gitignore +++ b/EASTL/.gitignore @@ -47,3 +47,4 @@ Testing/* /buckaroo/ .buckconfig.local BUCKAROO_DEPS +.vscode/settings.json diff --git a/EASTL/CMakeLists.txt b/EASTL/CMakeLists.txt index e8700dc..25e7373 100644 --- a/EASTL/CMakeLists.txt +++ b/EASTL/CMakeLists.txt @@ -9,6 +9,7 @@ project(EASTL CXX) #------------------------------------------------------------------------------------------- option(EASTL_BUILD_BENCHMARK "Enable generation of build files for benchmark" OFF) 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) #------------------------------------------------------------------------------------------- # Compiler Flags @@ -37,6 +38,9 @@ add_definitions(-D_CHAR16T) add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_SCL_SECURE_NO_WARNINGS) add_definitions(-DEASTL_OPENSOURCE=1) +if (EASTL_STD_ITERATOR_CATEGORY_ENABLED) + add_definitions(-DEASTL_STD_ITERATOR_CATEGORY_ENABLED=1) +endif() #------------------------------------------------------------------------------------------- # Include dirs diff --git a/EASTL/benchmark/CMakeLists.txt b/EASTL/benchmark/CMakeLists.txt index 94bc971..9ef8c66 100644 --- a/EASTL/benchmark/CMakeLists.txt +++ b/EASTL/benchmark/CMakeLists.txt @@ -58,6 +58,9 @@ add_definitions(-D_SCL_SECURE_NO_WARNINGS) add_definitions(-DEASTL_THREAD_SUPPORT_AVAILABLE=0) add_definitions(-DEASTL_OPENSOURCE=1) add_definitions(-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS) # silence std::hash_map deprecation warnings +if (EASTL_STD_ITERATOR_CATEGORY_ENABLED) + add_definitions(-DEASTL_STD_ITERATOR_CATEGORY_ENABLED=1) +endif() if(NOT EASTL_BUILD_TESTS) add_subdirectory(../test/packages/EAStdC ../test/EAStdC) diff --git a/EASTL/doc/EASTL.natvis b/EASTL/doc/EASTL.natvis index 2fb311b..c3e94db 100644 --- a/EASTL/doc/EASTL.natvis +++ b/EASTL/doc/EASTL.natvis @@ -70,7 +70,7 @@ <Item Name="[capacity]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">(mPair.mFirst.heap.mnCapacity & ~kHeapMask)</Item> <Item Name="[value]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">mPair.mFirst.heap.mpBegin,sb</Item> - <Item Name="[length]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize</Item> + <Item Name="[length]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">(SSOLayout::SSO_CAPACITY - mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize)</Item> <Item Name="[capacity]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">SSOLayout::SSO_CAPACITY</Item> <Item Name="[value]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">mPair.mFirst.sso.mData,sb</Item> @@ -87,7 +87,7 @@ <Item Name="[capacity]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">(mPair.mFirst.heap.mnCapacity & ~kHeapMask)</Item> <Item Name="[value]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">mPair.mFirst.heap.mpBegin,su</Item> - <Item Name="[length]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize</Item> + <Item Name="[length]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">(SSOLayout::SSO_CAPACITY - mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize)</Item> <Item Name="[capacity]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">SSOLayout::SSO_CAPACITY</Item> <Item Name="[value]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask)">mPair.mFirst.sso.mData,su</Item> @@ -392,9 +392,9 @@ <Synthetic Name="NOTE!"> <DisplayString>It is possible to expand parents that do not exist.</DisplayString> </Synthetic> - <Item Name="Parent">*(eastl::rbtree_node<$T2>*)(mpNodeParent.value & (~uintptr_t(1)))</Item> - <Item Name="Left">*(eastl::rbtree_node<$T2>*)mpNodeLeft</Item> - <Item Name="Right">*(eastl::rbtree_node<$T2>*)mpNodeRight</Item> + <Item Name="Parent">*(eastl::rbtree_node<$T1>*)mpNodeParent</Item> + <Item Name="Left">*(eastl::rbtree_node<$T1>*)mpNodeLeft</Item> + <Item Name="Right">*(eastl::rbtree_node<$T1>*)mpNodeRight</Item> </Expand> </Type> @@ -407,14 +407,29 @@ <Type Name="eastl::hashtable<*>"> - <DisplayString Condition="mnElementCount == 0">[{mnElementCount}] {{}}</DisplayString> - <DisplayString Condition="mnElementCount != 0">[{mnElementCount}] {{ ... }}</DisplayString> - <Expand> - <ArrayItems> - <Size>mnBucketCount</Size> - <ValuePointer>mpBucketArray</ValuePointer> - </ArrayItems> - </Expand> + <DisplayString Condition="mnElementCount == 0">[{mnElementCount}] {{}}</DisplayString> + <DisplayString Condition="mnElementCount != 0">[{mnElementCount}] {{ ... }}</DisplayString> + <Expand> + <ArrayItems IncludeView="detailed"> + <Size>mnBucketCount</Size> + <ValuePointer>mpBucketArray</ValuePointer> + </ArrayItems> + <CustomListItems ExcludeView="detailed"> + <Variable Name="bucketIndex" InitialValue="0"/> + <Variable Name="entry" InitialValue ="mpBucketArray[bucketIndex]"/> + <Loop> + <Item Condition="entry != nullptr">entry->mValue</Item> + <If Condition="entry != nullptr"> + <Exec>entry = entry->mpNext</Exec> + </If> + <If Condition="entry == nullptr"> + <Exec>bucketIndex++</Exec> + <Break Condition="bucketIndex == mnBucketCount"/> + <Exec>entry = mpBucketArray[bucketIndex]</Exec> + </If> + </Loop> + </CustomListItems> + </Expand> </Type> <Type Name="eastl::hash_node<*>"> @@ -629,7 +644,88 @@ </Type> -<!-- TODO eastl::tuple --> +<Type Name="eastl::tuple<>"> + <DisplayString IncludeView="noparens"></DisplayString> + <DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString> + <Expand/> +</Type> + +<Type Name="eastl::tuple<*>"> + <DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue}</DisplayString> + <DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString> + <Expand> + <Item Name="[0]">(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue</Item> + </Expand> +</Type> + +<Type Name="eastl::tuple<*,*>"> + <DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue}</DisplayString> + <DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString> + <Expand> + <Item Name="[0]">(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue</Item> + <Item Name="[1]">(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue</Item> + </Expand> +</Type> + +<Type Name="eastl::tuple<*,*,*>"> + <DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue}</DisplayString> + <DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString> + <Expand> + <Item Name="[0]">(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue</Item> + <Item Name="[1]">(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue</Item> + <Item Name="[2]">(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue</Item> + </Expand> +</Type> + +<Type Name="eastl::tuple<*,*,*,*>"> + <DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<3,$T4,0>*)&mImpl)).mValue}</DisplayString> + <DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString> + <Expand> + <Item Name="[0]">(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue</Item> + <Item Name="[1]">(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue</Item> + <Item Name="[2]">(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue</Item> + <Item Name="[3]">(*((eastl::Internal::TupleLeaf<3,$T4,0>*)&mImpl)).mValue</Item> + </Expand> +</Type> + +<Type Name="eastl::tuple<*,*,*,*,*>"> + <DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<3,$T4,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<4,$T5,0>*)&mImpl)).mValue}</DisplayString> + <DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString> + <Expand> + <Item Name="[0]">(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue</Item> + <Item Name="[1]">(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue</Item> + <Item Name="[2]">(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue</Item> + <Item Name="[3]">(*((eastl::Internal::TupleLeaf<3,$T4,0>*)&mImpl)).mValue</Item> + <Item Name="[4]">(*((eastl::Internal::TupleLeaf<4,$T5,0>*)&mImpl)).mValue</Item> + </Expand> +</Type> + +<Type Name="eastl::tuple<*,*,*,*,*,*>"> + <DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<3,$T4,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<4,$T5,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<5,$T6,0>*)&mImpl)).mValue}</DisplayString> + <DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString> + <Expand> + <Item Name="[0]">(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue</Item> + <Item Name="[1]">(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue</Item> + <Item Name="[2]">(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue</Item> + <Item Name="[3]">(*((eastl::Internal::TupleLeaf<3,$T4,0>*)&mImpl)).mValue</Item> + <Item Name="[4]">(*((eastl::Internal::TupleLeaf<4,$T5,0>*)&mImpl)).mValue</Item> + <Item Name="[5]">(*((eastl::Internal::TupleLeaf<5,$T6,0>*)&mImpl)).mValue</Item> + </Expand> +</Type> + +<Type Name="eastl::tuple<*,*,*,*,*,*,*>"> + <DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<3,$T4,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<4,$T5,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<5,$T6,0>*)&mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf<6,$T7,0>*)&mImpl)).mValue}</DisplayString> + <DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString> + <Expand> + <Item Name="[0]">(*((eastl::Internal::TupleLeaf<0,$T1,0>*)&mImpl)).mValue</Item> + <Item Name="[1]">(*((eastl::Internal::TupleLeaf<1,$T2,0>*)&mImpl)).mValue</Item> + <Item Name="[2]">(*((eastl::Internal::TupleLeaf<2,$T3,0>*)&mImpl)).mValue</Item> + <Item Name="[3]">(*((eastl::Internal::TupleLeaf<3,$T4,0>*)&mImpl)).mValue</Item> + <Item Name="[4]">(*((eastl::Internal::TupleLeaf<4,$T5,0>*)&mImpl)).mValue</Item> + <Item Name="[5]">(*((eastl::Internal::TupleLeaf<5,$T6,0>*)&mImpl)).mValue</Item> + <Item Name="[6]">(*((eastl::Internal::TupleLeaf<6,$T7,0>*)&mImpl)).mValue</Item> + </Expand> +</Type> </AutoVisualizer> diff --git a/EASTL/include/EASTL/algorithm.h b/EASTL/include/EASTL/algorithm.h index da35c2e..6257514 100644 --- a/EASTL/include/EASTL/algorithm.h +++ b/EASTL/include/EASTL/algorithm.h @@ -128,6 +128,7 @@ // iter_swap // lexicographical_compare // lexicographical_compare<Compare> +// lexicographical_compare_three_way // lower_bound // lower_bound<Compare> // make_heap Found in heap.h @@ -163,6 +164,8 @@ // random_shuffle<Random> // remove // remove_if +// +apply_and_remove +// +apply_and_remove_if // remove_copy // remove_copy_if // +remove_heap Found in heap.h @@ -247,6 +250,7 @@ #include <EASTL/utility.h> #include <EASTL/internal/generic_iterator.h> #include <EASTL/random.h> +#include <EASTL/compare.h> EA_DISABLE_ALL_VC_WARNINGS(); @@ -806,18 +810,18 @@ namespace eastl template <typename T> inline T&& median_impl(T&& a, T&& b, T&& c) { - if(a < b) + if(eastl::less<T>()(a, b)) { - if(b < c) + if(eastl::less<T>()(b, c)) return eastl::forward<T>(b); - else if(a < c) + else if(eastl::less<T>()(a, c)) return eastl::forward<T>(c); else return eastl::forward<T>(a); } - else if(a < c) + else if(eastl::less<T>()(a, c)) return eastl::forward<T>(a); - else if(b < c) + else if(eastl::less<T>()(b, c)) return eastl::forward<T>(c); return eastl::forward<T>(b); } @@ -1259,14 +1263,8 @@ namespace eastl inline BidirectionalIterator2 move_and_copy_backward_chooser(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) { typedef typename eastl::iterator_traits<BidirectionalIterator1>::iterator_category IIC; - typedef typename eastl::iterator_traits<BidirectionalIterator2>::iterator_category OIC; - typedef typename eastl::iterator_traits<BidirectionalIterator1>::value_type value_type_input; - typedef typename eastl::iterator_traits<BidirectionalIterator2>::value_type value_type_output; - const bool canBeMemmoved = eastl::is_trivially_copyable<value_type_output>::value && - eastl::is_same<value_type_input, value_type_output>::value && - (eastl::is_pointer<BidirectionalIterator1>::value || eastl::is_same<IIC, eastl::contiguous_iterator_tag>::value) && - (eastl::is_pointer<BidirectionalIterator2>::value || eastl::is_same<OIC, eastl::contiguous_iterator_tag>::value); + const bool canBeMemmoved = internal::can_be_memmoved_helper<BidirectionalIterator1, BidirectionalIterator2>::value; return eastl::move_and_copy_backward_helper<IIC, isMove, canBeMemmoved>::move_or_copy_backward(first, last, resultEnd); // 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. } @@ -2114,6 +2112,42 @@ namespace eastl } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + + /// lexicographical_compare_three_way + /// + /// Returns: The comparison category ordering between both ranges. For the first non-equivalent pair in the ranges, + /// the comparison will be returned. Else if the first range is a subset (superset) of the second range, then the + /// less (greater) ordering will be returned. + /// + /// Complexity: At most N iterations, where N = min(last1-first1, last2-first2) of the applications + /// of the corresponding comparison. + /// + /// Note: If two sequences have the same number of elements and their + /// corresponding elements are equivalent, then neither sequence is + /// lexicographically less than the other. If one sequence is a prefix + /// of the other, then the shorter sequence is lexicographically less + /// than the longer sequence. Otherwise, the lexicographical comparison + /// of the sequences yields the same result as the comparison of the first + /// corresponding pair of elements that are not equivalent. + /// + template <typename InputIterator1, typename InputIterator2, typename Compare> + constexpr auto lexicographical_compare_three_way(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + Compare compare) -> decltype(compare(*first1, *first2)) + { + for (; (first1 != last1) && (first2 != last2); ++first1, ++first2) + { + if (auto c = compare(*first1, *first2); c != 0) + return c; + } + + return (first1 != last1) ? std::strong_ordering::greater : + (first2 != last2) ? std::strong_ordering::less : + std::strong_ordering::equal; + } +#endif + /// mismatch /// /// Finds the first position where the two ranges [first1, last1) and @@ -2628,6 +2662,94 @@ namespace eastl } + /// apply_and_remove_if + /// + /// Calls the Function function for all elements referred to my iterator i in the range + /// [first, last) for which the following corresponding condition holds: + /// predicate(*i) == true + /// and then left shift moves potential non-matching elements over it. + /// + /// Returns: a past-the-end iterator for the new end of the range. + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate + applies + /// function once for every time the condition holds. + /// + /// Note: Since removing is done by shifting (by means of copy move assignment) the elements + /// in the range in such a way that the elements that are not to be removed appear in the + /// beginning of the range doesn't actually remove it from the given container, the user must call + /// the container erase function if the user wants to erase the element + /// from the container. I.e. in the same they as for remove_if the excess elements + /// are left in a valid but possibly moved from state. + /// + template <typename ForwardIterator, typename Function, typename Predicate> + inline ForwardIterator apply_and_remove_if(ForwardIterator first, + ForwardIterator last, + Function function, + Predicate predicate) + { + first = eastl::find_if(first, last, predicate); + if (first != last) + { + function(*first); + for (auto i = next(first); i != last; ++i) + { + if (predicate(*i)) + { + function(*i); + continue; + } + *first = eastl::move(*i); + ++first; + } + } + return first; + } + + + /// apply_and_remove + /// + /// Calls the Function function for all elements referred to my iterator i in the range + /// [first, last) for which the following corresponding condition holds: + /// value == *i + /// and then left shift moves potential non-matching elements over it. + /// + /// Returns: a past-the-end iterator for the new end of the range. + /// + /// Complexity: Exactly 'last - first' applications of the corresponding equality test + /// + applies function once for every time the condition holds. + /// + /// Note: Since removing is done by shifting (by means of copy move assignment) the elements + /// in the range in such a way that the elements that are not to be removed appear in the + /// beginning of the range doesn't actually remove it from the given container, the user must call + /// the container erase function if the user wants to erase the element + /// from the container. I.e. in the same they as for remove_if the excess elements + /// are left in a valid but possibly moved from state. + /// + template <typename ForwardIterator, typename Function, typename T> + inline ForwardIterator apply_and_remove(ForwardIterator first, + ForwardIterator last, + Function function, + const T& value) + { + first = eastl::find(first, last, value); + if (first != last) + { + function(*first); + for (auto i = next(first); i != last; ++i) + { + if (value == *i) + { + function(*i); + continue; + } + *first = eastl::move(*i); + ++first; + } + } + return first; + } + + /// replace_copy /// /// Effects: Assigns to every iterator i in the range [result, result + (last - first)) @@ -4187,9 +4309,8 @@ namespace eastl template <class T, class Compare> EA_CONSTEXPR const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) { - // code collapsed to a single line due to constexpr requirements - return [&] { EASTL_ASSERT(!comp(hi, lo)); }(), - comp(v, lo) ? lo : comp(hi, v) ? hi : v; + EASTL_ASSERT(!comp(hi, lo)); + return comp(v, lo) ? lo : comp(hi, v) ? hi : v; } template <class T> diff --git a/EASTL/include/EASTL/allocator.h b/EASTL/include/EASTL/allocator.h index ad20e4d..d645466 100644 --- a/EASTL/include/EASTL/allocator.h +++ b/EASTL/include/EASTL/allocator.h @@ -71,8 +71,9 @@ namespace eastl }; bool operator==(const allocator& a, const allocator& b); +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) bool operator!=(const allocator& a, const allocator& b); - +#endif /// dummy_allocator @@ -97,8 +98,9 @@ namespace eastl }; inline bool operator==(const dummy_allocator&, const dummy_allocator&) { return true; } +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) inline bool operator!=(const dummy_allocator&, const dummy_allocator&) { return false; } - +#endif /// Defines a static default allocator which is constant across all types. @@ -299,12 +301,12 @@ namespace eastl return true; // All allocators are considered equal, as they merely use global new/delete. } - +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) inline bool operator!=(const allocator&, const allocator&) { return false; // All allocators are considered equal, as they merely use global new/delete. } - +#endif } // namespace eastl diff --git a/EASTL/include/EASTL/array.h b/EASTL/include/EASTL/array.h index 05d5d32..34ad07d 100644 --- a/EASTL/include/EASTL/array.h +++ b/EASTL/include/EASTL/array.h @@ -401,6 +401,13 @@ namespace eastl return eastl::equal(&a.mValue[0], &a.mValue[N], &b.mValue[0]); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, size_t N> + inline synth_three_way_result<T> operator<=>(const array<T, N>& a, const array<T,N>& b) + { + return eastl::lexicographical_compare_three_way(&a.mValue[0], &a.mValue[N], &b.mValue[0], &b.mValue[N], synth_three_way{}); + } +#else template <typename T, size_t N> EA_CPP14_CONSTEXPR inline bool operator<(const array<T, N>& a, const array<T, N>& b) @@ -435,7 +442,7 @@ namespace eastl { return !eastl::lexicographical_compare(&a.mValue[0], &a.mValue[N], &b.mValue[0], &b.mValue[N]); } - +#endif template <typename T, size_t N> inline void swap(array<T, N>& a, array<T, N>& b) @@ -478,9 +485,96 @@ namespace eastl return internal::to_array(eastl::move(a), eastl::make_index_sequence<N>{}); } +#if EASTL_TUPLE_ENABLED + + template <typename T, size_t N> + class tuple_size<array<T, N>> : public integral_constant<size_t, N> + { + }; + + template <typename T, size_t N> + class tuple_size<const array<T, N>> : public integral_constant<size_t, N> + { + }; + + template <size_t I, typename T, size_t N> + class tuple_element<I, array<T, N>> + { + public: + using type = T; + }; + + template <size_t I, typename T, size_t N> + class tuple_element<I, const array<T, N>> + { + public: + using type = const T; + }; + + template <size_t I> + struct GetArray + { + template <typename T, size_t N> + static EA_CONSTEXPR T& getInternal(array<T, N>& a) + { + return a[I]; + } + + template <typename T, size_t N> + static EA_CONSTEXPR const T& getInternal(const array<T, N>& a) + { + return a[I]; + } + + template <typename T, size_t N> + static EA_CONSTEXPR T&& getInternal(array<T, N>&& a) + { + return eastl::forward<T>(a[I]); + } + }; + + template <size_t I, typename T, size_t N> + EA_CONSTEXPR tuple_element_t<I, array<T, N>>& get(array<T, N>& p) + { + return GetArray<I>::getInternal(p); + } + + template <size_t I, typename T, size_t N> + EA_CONSTEXPR const tuple_element_t<I, array<T, N>>& get(const array<T, N>& p) + { + return GetArray<I>::getInternal(p); + } + + template <size_t I, typename T, size_t N> + EA_CONSTEXPR tuple_element_t<I, array<T, N>>&& get(array<T, N>&& p) + { + return GetArray<I>::getInternal(eastl::move(p)); + } + +#endif // EASTL_TUPLE_ENABLED + } // namespace eastl +/////////////////////////////////////////////////////////////// +// C++17 structured binding support for eastl::array +// +#ifndef EA_COMPILER_NO_STRUCTURED_BINDING + #include <tuple> + + template <typename T, size_t N> + class std::tuple_size<::eastl::array<T, N>> : public ::eastl::integral_constant<size_t, N> + { + }; + + template <size_t I, typename T, size_t N> + struct std::tuple_element<I, ::eastl::array<T, N>> + { + static_assert(I < N, "index is out of bounds"); + using type = T; + }; +#endif // EA_COMPILER_NO_STRUCTURED_BINDING + #endif // Header include guard diff --git a/EASTL/include/EASTL/bit.h b/EASTL/include/EASTL/bit.h index 64efe48..0eeeed0 100644 --- a/EASTL/include/EASTL/bit.h +++ b/EASTL/include/EASTL/bit.h @@ -60,6 +60,113 @@ namespace eastl #endif // EASTL_CONSTEXPR_BIT_CAST_SUPPORTED + #if defined(EA_COMPILER_CPP20_ENABLED) + #ifndef EASTL_COUNT_LEADING_ZEROES + #if defined(__GNUC__) + #if (EA_PLATFORM_PTR_SIZE == 8) + #define EASTL_COUNT_LEADING_ZEROES __builtin_clzll + #else + #define EASTL_COUNT_LEADING_ZEROES __builtin_clz + #endif + #endif + + #ifndef EASTL_COUNT_LEADING_ZEROES + static inline int eastl_count_leading_zeroes(uint64_t x) + { + if(x) + { + int n = 0; + if(x & UINT64_C(0xFFFFFFFF00000000)) { n += 32; x >>= 32; } + if(x & 0xFFFF0000) { n += 16; x >>= 16; } + if(x & 0xFFFFFF00) { n += 8; x >>= 8; } + if(x & 0xFFFFFFF0) { n += 4; x >>= 4; } + if(x & 0xFFFFFFFC) { n += 2; x >>= 2; } + if(x & 0xFFFFFFFE) { n += 1; } + return 63 - n; + } + return 64; + } + + static inline int eastl_count_leading_zeroes(uint32_t x) + { + if(x) + { + int n = 0; + if(x <= 0x0000FFFF) { n += 16; x <<= 16; } + if(x <= 0x00FFFFFF) { n += 8; x <<= 8; } + if(x <= 0x0FFFFFFF) { n += 4; x <<= 4; } + if(x <= 0x3FFFFFFF) { n += 2; x <<= 2; } + if(x <= 0x7FFFFFFF) { n += 1; } + return n; + } + return 32; + } + + #define EASTL_COUNT_LEADING_ZEROES eastl_count_leading_zeroes + #endif + #endif + + template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>> + EA_CONSTEXPR int countl_zero(const T num) EA_NOEXCEPT + { + EA_CONSTEXPR auto DIGITS = eastl::numeric_limits<T>::digits; + EA_CONSTEXPR auto DIGITS_U = eastl::numeric_limits<unsigned>::digits; + EA_CONSTEXPR auto DIGITS_ULL = eastl::numeric_limits<unsigned long long>::digits; + + if (num == 0) + { + return DIGITS; + } + + if constexpr (DIGITS <= DIGITS_U) + { + EA_CONSTEXPR auto DIFF = DIGITS_U - DIGITS; + return EASTL_COUNT_LEADING_ZEROES(static_cast<uint32_t>(num)) - DIFF; + } + else + { + EA_CONSTEXPR auto DIFF = DIGITS_ULL - DIGITS; + return EASTL_COUNT_LEADING_ZEROES(static_cast<uint64_t>(num)) - DIFF; + } + } + + template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>> + EA_CONSTEXPR bool has_single_bit(const T num) EA_NOEXCEPT + { + return num != 0 && (num & (num - 1)) == 0; + } + + template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>> + EA_CONSTEXPR T bit_ceil(const T num) EA_NOEXCEPT + { + if (num <= 1U) + { + return T(1); + } + + const auto shift = eastl::numeric_limits<T>::digits - eastl::countl_zero(static_cast<T>(num - 1)); + return static_cast<T>(T(1) << shift); + } + + template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>> + EA_CONSTEXPR T bit_floor(const T num) EA_NOEXCEPT + { + if (num == 0) + { + return T(0); + } + + const auto shift = eastl::numeric_limits<T>::digits - eastl::countl_zero(num) - 1; + return static_cast<T>(T(1) << shift); + } + + template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>> + EA_CONSTEXPR T bit_width(const T num) EA_NOEXCEPT + { + return static_cast<T>(eastl::numeric_limits<T>::digits - eastl::countl_zero(num)); + } + #endif + } // namespace eastl #endif // EASTL_BIT_H diff --git a/EASTL/include/EASTL/bitset.h b/EASTL/include/EASTL/bitset.h index 8778372..c31831a 100644 --- a/EASTL/include/EASTL/bitset.h +++ b/EASTL/include/EASTL/bitset.h @@ -405,7 +405,9 @@ namespace eastl size_type size() const; bool operator==(const this_type& x) const; +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) bool operator!=(const this_type& x) const; +#endif bool test(size_type i) const; //bool any() const; // We inherit this from the base class. @@ -2078,13 +2080,13 @@ EA_RESTORE_GCC_WARNING() return base_type::operator==(x); } - +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template <size_t N, typename WordType> inline bool bitset<N, WordType>::operator!=(const this_type& x) const { return !base_type::operator==(x); } - +#endif template <size_t N, typename WordType> inline bool bitset<N, WordType>::test(size_type i) const diff --git a/EASTL/include/EASTL/bonus/lru_cache.h b/EASTL/include/EASTL/bonus/lru_cache.h index 46d053d..a8d7c33 100644 --- a/EASTL/include/EASTL/bonus/lru_cache.h +++ b/EASTL/include/EASTL/bonus/lru_cache.h @@ -122,7 +122,7 @@ namespace eastl } lru_cache(std::initializer_list<eastl::pair<Key, Value>> il) - : lru_cache(il.size()) + : lru_cache(static_cast<size_type>(il.size())) { for(auto& p : il) insert_or_assign(p.first, p.second); diff --git a/EASTL/include/EASTL/bonus/overloaded.h b/EASTL/include/EASTL/bonus/overloaded.h new file mode 100644 index 0000000..55ca158 --- /dev/null +++ b/EASTL/include/EASTL/bonus/overloaded.h @@ -0,0 +1,81 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_OVERLOADED_H +#define EASTL_OVERLOADED_H + +#include <EASTL/internal/move_help.h> +#include <EASTL/type_traits.h> + + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) +#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed + // improvements in apps as a result. +#endif + +namespace eastl +{ + /////////////////////////////////////////////////////////////////////////// + /// overloaded + /// + /// A helper class that permits you to combine multiple function objects into one. + /// Typically, this helper is really handy when visiting an eastl::variant with multiple lambdas. + /// Example: + /// + /// eastl::variant<int, string> v{42}; + /// + /// eastl::visit( + /// eastl::overloaded{ + /// [](const int& x) { std::cout << "Visited an integer: " << x << "\n"; }, // Will reach that lambda with x == 42. + /// [](const string& s) { std::cout << "Visited an string: " << s << "\n"; } + /// }, + /// v + /// ); + /////////////////////////////////////////////////////////////////////////// + template <class... T> + struct overloaded; + + template <class T> + struct overloaded<T> : T + { + template <class U> + EA_CPP14_CONSTEXPR overloaded(U&& u) : T(eastl::forward<U>(u)) + { + } + + using T::operator(); + }; + + template <class T, class... R> + struct overloaded<T, R...> : T, overloaded<R...> + { + template <class U, class... V> + EA_CPP14_CONSTEXPR overloaded(U&& u, V&&... v) : T(eastl::forward<U>(u)), overloaded<R...>(eastl::forward<V>(v)...) + { + } + + using T::operator(); + using overloaded<R...>::operator(); + }; + + #ifdef __cpp_deduction_guides + template <class... T> + overloaded(T...) -> overloaded<T...>; + #endif + + /////////////////////////////////////////////////////////////////////////// + /// make_overloaded + /// + /// Helper function to create an overloaded instance when lacking deduction guides. + /// make_overloaded(f1, f2, f3) == overloaded{f1, f2, f3} + /////////////////////////////////////////////////////////////////////////// + template <class... T> + EA_CPP14_CONSTEXPR overloaded<typename eastl::remove_cvref<T>::type...> make_overloaded(T&&... t) + { + return overloaded<typename eastl::remove_cvref<T>::type...>{eastl::forward<T>(t)...}; + } + +} // namespace eastl + +#endif // EASTL_OVERLOADED_H
\ No newline at end of file diff --git a/EASTL/include/EASTL/bonus/tuple_vector.h b/EASTL/include/EASTL/bonus/tuple_vector.h index 7123c57..6ade75a 100644 --- a/EASTL/include/EASTL/bonus/tuple_vector.h +++ b/EASTL/include/EASTL/bonus/tuple_vector.h @@ -340,13 +340,13 @@ struct TupleVecIterCompatible<TupleTypes<Us...>, TupleTypes<Ts...>> : // storing - and harmoniously updating on each modification - a full tuple of pointers to the tupleVec's data template <eastl_size_t... Indices, typename... Ts> struct TupleVecIter<index_sequence<Indices...>, Ts...> - : public iterator<random_access_iterator_tag, tuple<Ts...>, eastl_size_t, tuple<Ts*...>, tuple<Ts&...>> + : public iterator<EASTL_ITC_NS::random_access_iterator_tag, tuple<Ts...>, eastl_size_t, tuple<Ts*...>, tuple<Ts&...>> { private: typedef TupleVecIter<index_sequence<Indices...>, Ts...> this_type; typedef eastl_size_t size_type; - typedef iterator<random_access_iterator_tag, tuple<Ts...>, eastl_size_t, tuple<Ts*...>, tuple<Ts&...>> iter_type; + typedef iterator<EASTL_ITC_NS::random_access_iterator_tag, tuple<Ts...>, eastl_size_t, tuple<Ts*...>, tuple<Ts&...>> iter_type; template<typename U, typename... Us> friend struct TupleVecIter; @@ -1411,7 +1411,6 @@ class move_iterator<TupleVecInternal::TupleVecIter<index_sequence<Indices...>, T { public: typedef TupleVecInternal::TupleVecIter<index_sequence<Indices...>, Ts...> iterator_type; - typedef iterator_type wrapped_iterator_type; // This is not in the C++ Standard; it's used by use to identify it as // a wrapping iterator type. typedef iterator_traits<iterator_type> traits_type; typedef typename traits_type::iterator_category iterator_category; @@ -1477,6 +1476,13 @@ private: { return reference(eastl::move(((Ts*)mIterator.mpData[Indices])[mIterator.mIndex])...); } + + // Unwrapping interface, not part of the public API. + iterator_type unwrap() const { return mIterator; } + + // The unwrapper helpers need access to unwrap(). + friend is_iterator_wrapper_helper<this_type, true>; + friend is_iterator_wrapper<this_type>; }; template <typename AllocatorA, typename AllocatorB, typename Indices, typename... Ts> diff --git a/EASTL/include/EASTL/chrono.h b/EASTL/include/EASTL/chrono.h index 5d8ca42..4b94fe4 100644 --- a/EASTL/include/EASTL/chrono.h +++ b/EASTL/include/EASTL/chrono.h @@ -16,7 +16,7 @@ #define EASTL_CHRONO_H #if defined(EA_PRAGMA_ONCE_SUPPORTED) - #pragma once + #pragma once #endif #include <EASTL/internal/config.h> @@ -50,12 +50,9 @@ #if defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_MINGW) // Nothing to do -#elif defined(EA_PLATFORM_SONY) - #include <Dinkum/threads/xtimec.h> - #include <kernel.h> #elif defined(EA_PLATFORM_APPLE) #include <mach/mach_time.h> -#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 <pthread_time.h> @@ -104,7 +101,7 @@ namespace chrono namespace Internal { /////////////////////////////////////////////////////////////////////////////// - // IsRatio + // IsRatio /////////////////////////////////////////////////////////////////////////////// template <typename> struct IsRatio : eastl::false_type {}; template <intmax_t N, intmax_t D> struct IsRatio<ratio<N, D>> : eastl::true_type {}; @@ -114,7 +111,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // IsDuration + // IsDuration /////////////////////////////////////////////////////////////////////////////// template<typename> struct IsDuration : eastl::false_type{}; template<typename Rep, typename Period> struct IsDuration<duration<Rep, Period>> : eastl::true_type{}; @@ -124,7 +121,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // RatioGCD + // RatioGCD /////////////////////////////////////////////////////////////////////////////// template <class Period1, class Period2> struct RatioGCD @@ -197,10 +194,10 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // duration_cast + // duration_cast /////////////////////////////////////////////////////////////////////////////// template <typename ToDuration, typename Rep, typename Period> - inline typename eastl::enable_if<Internal::IsDuration<ToDuration>::value, ToDuration>::type + inline typename eastl::enable_if<Internal::IsDuration<ToDuration>::value, ToDuration>::type duration_cast(const duration<Rep, Period>& d) { typedef typename duration<Rep, Period>::this_type FromDuration; @@ -209,12 +206,12 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // duration + // duration /////////////////////////////////////////////////////////////////////////////// template <class Rep, class Period> class duration { - Rep mRep; + Rep mRep; public: typedef Rep rep; @@ -222,7 +219,7 @@ namespace chrono typedef duration<Rep, Period> this_type; #if defined(EA_COMPILER_NO_DEFAULTED_FUNCTIONS) - EA_CONSTEXPR duration() + EA_CONSTEXPR duration() : mRep() {} duration(const duration& other) @@ -238,7 +235,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // conversion constructors + // conversion constructors /////////////////////////////////////////////////////////////////////////////// template <class Rep2> inline EA_CONSTEXPR explicit duration( @@ -258,12 +255,12 @@ namespace chrono : mRep(duration_cast<duration>(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<Rep>::zero()); } EA_CONSTEXPR inline static duration min() { return duration(duration_values<Rep>::min()); } @@ -314,7 +311,7 @@ namespace chrono duration<typename eastl::common_type<Rep1, Rep2>::type, Period1> EASTL_FORCE_INLINE operator*(const duration<Rep1, Period1>& lhs, const Rep2& rhs) { - typedef typename duration<eastl::common_type<Rep1, Rep2>, Period1>::type common_duration_t; + typedef duration<typename eastl::common_type<Rep1, Rep2>::type, Period1> common_duration_t; return common_duration_t(common_duration_t(lhs).count() * rhs); } @@ -421,7 +418,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// // 20.12.6, time_point /////////////////////////////////////////////////////////////////////////////// - template <typename Clock, typename Duration = typename Clock::duration> + template <typename Clock, typename Duration = typename Clock::duration> class time_point { Duration mDuration; @@ -443,7 +440,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()); } @@ -546,26 +543,26 @@ 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 _XTIME_NSECS_PER_TICK + #define EASTL_NS_PER_TICK 1 #elif defined EA_PLATFORM_POSIX #define EASTL_NS_PER_TICK _XTIME_NSECS_PER_TICK #else - #define EASTL_NS_PER_TICK 100 + #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<eastl::ratio<EASTL_NS_PER_TICK, 1>, nano>::type SystemClock_Period; - typedef eastl::ratio_multiply<eastl::ratio<EASTL_NS_PER_TICK, 1>, nano>::type SteadyClock_Period; + typedef eastl::ratio_multiply<eastl::ratio<EASTL_NS_PER_TICK, 1>, nano>::type SystemClock_Period; + typedef eastl::ratio_multiply<eastl::ratio<EASTL_NS_PER_TICK, 1>, nano>::type SteadyClock_Period; #endif /////////////////////////////////////////////////////////////////////////////// - // Internal::GetTicks + // Internal::GetTicks /////////////////////////////////////////////////////////////////////////////// inline uint64_t GetTicks() { @@ -574,7 +571,7 @@ namespace chrono { LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); - return double(1000000000.0L / frequency.QuadPart); // nanoseconds per tick + return double(1000000000.0L / (long double)frequency.QuadPart); // nanoseconds per tick }; auto queryCounter = [] @@ -587,11 +584,23 @@ namespace chrono 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 * queryCounter()); - #elif defined EA_PLATFORM_SONY - return sceKernelGetProcessTimeCounter(); + return uint64_t(frequency * (double)queryCounter()); + #elif defined EA_PLATFORM_SONY + static_assert(false, "Implementing GetTicks() requires first party support"); + return 0; #elif defined(EA_PLATFORM_APPLE) - return mach_absolute_time(); + auto queryTimeInfo = [] + { + mach_timebase_info_data_t info; + mach_timebase_info(&info); + return info; + }; + + static auto timeInfo = queryTimeInfo(); + uint64_t t = mach_absolute_time(); + t *= timeInfo.numer; + t /= timeInfo.denom; + return t; #elif defined(EA_PLATFORM_POSIX) // Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms). #if (defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC)) timespec ts; @@ -616,7 +625,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // system_clock + // system_clock /////////////////////////////////////////////////////////////////////////////// class system_clock { @@ -630,15 +639,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 { @@ -652,24 +661,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 <typename Rep1, typename Period1, typename Rep2, typename Period2> struct common_type<chrono::duration<Rep1, Period1>, chrono::duration<Rep2, Period2>> @@ -680,7 +689,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // time_point common_type specialization + // time_point common_type specialization /////////////////////////////////////////////////////////////////////////////// template <typename Clock, typename Duration1, typename Duration2> struct common_type<chrono::time_point<Clock, Duration1>, chrono::time_point<Clock, Duration2>> @@ -690,10 +699,15 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // chrono_literals + // chrono_literals /////////////////////////////////////////////////////////////////////////////// #if EASTL_USER_LITERALS_ENABLED && EASTL_INLINE_NAMESPACES_ENABLED - EA_DISABLE_VC_WARNING(4455) // disable warning C4455: literal suffix identifiers that do not start with an underscore are reserved + // Disabling the Clang/GCC/MSVC warning about using user + // defined literals without a leading '_' as they are reserved + // for standard libary usage. + EA_DISABLE_VC_WARNING(4455) + EA_DISABLE_CLANG_WARNING(-Wuser-defined-literals) + EA_DISABLE_GCC_WARNING(-Wliteral-suffix) inline namespace literals { inline namespace chrono_literals @@ -726,7 +740,9 @@ namespace chrono } // namespace chrono_literals }// namespace literals - EA_RESTORE_VC_WARNING() // warning: 4455 + EA_RESTORE_GCC_WARNING() // -Wliteral-suffix + EA_RESTORE_CLANG_WARNING() // -Wuser-defined-literals + EA_RESTORE_VC_WARNING() // warning: 4455 #endif } // namespace eastl @@ -740,4 +756,4 @@ namespace chrono #endif -#endif +#endif diff --git a/EASTL/include/EASTL/compare.h b/EASTL/include/EASTL/compare.h new file mode 100644 index 0000000..9bc3bd6 --- /dev/null +++ b/EASTL/include/EASTL/compare.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_COMPARE_H +#define EASTL_COMPARE_H + + +#include <EABase/eabase.h> + +namespace eastl +{ + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + struct synth_three_way + { + template <typename T, typename U> + constexpr auto operator()(const T& t, const U& u) const requires requires + { + {t < u}->std::convertible_to<bool>; + {u < t}->std::convertible_to<bool>; + } + { + if constexpr (std::three_way_comparable_with<T, U>) + { + return t <=> u; + } + else + { + return (t < u) ? std::weak_ordering::less : + (u < t) ? std::weak_ordering::greater : + std::weak_ordering::equivalent; + } + } + }; + + template <typename T, typename U=T> + using synth_three_way_result = decltype(synth_three_way{}(declval<T&>(), declval<U&>())); +#endif + +} // namespace eastl + + +#endif // Header include guard
\ No newline at end of file diff --git a/EASTL/include/EASTL/deque.h b/EASTL/include/EASTL/deque.h index c2d55b1..9a812c9 100644 --- a/EASTL/include/EASTL/deque.h +++ b/EASTL/include/EASTL/deque.h @@ -2619,6 +2619,14 @@ namespace eastl return ((a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin())); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename Allocator, unsigned kDequeSubarraySize> + inline synth_three_way_result<T> operator<=>(const deque<T, Allocator, kDequeSubarraySize>& a, const deque<T, Allocator, kDequeSubarraySize>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } + +#else template <typename T, typename Allocator, unsigned kDequeSubarraySize> inline bool operator!=(const deque<T, Allocator, kDequeSubarraySize>& a, const deque<T, Allocator, kDequeSubarraySize>& b) { @@ -2648,6 +2656,7 @@ namespace eastl { return !(a < b); } +#endif template <typename T, typename Allocator, unsigned kDequeSubarraySize> inline void swap(deque<T, Allocator, kDequeSubarraySize>& a, deque<T, Allocator, kDequeSubarraySize>& b) @@ -2661,17 +2670,39 @@ namespace eastl // https://en.cppreference.com/w/cpp/container/deque/erase2 /////////////////////////////////////////////////////////////////////// template <class T, class Allocator, class U> - void erase(deque<T, Allocator>& c, const U& value) + typename deque<T, Allocator>::size_type erase(deque<T, Allocator>& c, const U& value) { - // Erases all elements that compare equal to value from the container. - c.erase(eastl::remove(c.begin(), c.end(), value), c.end()); + // Erases all elements that compare equal to value from the container. + auto origEnd = c.end(); + auto newEnd = eastl::remove(c.begin(), origEnd, value); + auto numRemoved = eastl::distance(newEnd, origEnd); + c.erase(newEnd, 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<typename deque<T, Allocator>::size_type>(numRemoved); } template <class T, class Allocator, class Predicate> - void erase_if(deque<T, Allocator>& c, Predicate predicate) - { - // Erases all elements that satisfy the predicate pred from the container. - c.erase(eastl::remove_if(c.begin(), c.end(), predicate), c.end()); + typename deque<T, Allocator>::size_type erase_if(deque<T, Allocator>& c, Predicate predicate) + { + // Erases all elements that satisfy the predicate pred from the container. + auto origEnd = c.end(); + auto newEnd = eastl::remove_if(c.begin(), origEnd, predicate); + auto numRemoved = eastl::distance(newEnd, origEnd); + c.erase(newEnd, 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<typename deque<T, Allocator>::size_type>(numRemoved); } diff --git a/EASTL/include/EASTL/fixed_substring.h b/EASTL/include/EASTL/fixed_substring.h index 033052f..e186cfc 100644 --- a/EASTL/include/EASTL/fixed_substring.h +++ b/EASTL/include/EASTL/fixed_substring.h @@ -108,6 +108,10 @@ namespace eastl { } + fixed_substring(const fixed_substring& x) + : fixed_substring(static_cast<const base_type&>(x)) + {} + fixed_substring(const base_type& x) : base_type() { @@ -156,6 +160,12 @@ namespace eastl AllocateSelf(); } + this_type& operator=(const this_type& x) + { + assign(x); + return *this; + } + this_type& operator=(const base_type& x) { assign(x); diff --git a/EASTL/include/EASTL/hash_map.h b/EASTL/include/EASTL/hash_map.h index c363597..e7cad7b 100644 --- a/EASTL/include/EASTL/hash_map.h +++ b/EASTL/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. /////////////////////////////////////////////////////////////////////////////// @@ -64,7 +64,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 +74,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 +96,16 @@ namespace eastl /// hash_map<string, int> hashMap; /// i = hashMap.find_as("hello", hash<char*>(), equal_to_2<string, char*>()); /// - template <typename Key, typename T, typename Hash = eastl::hash<Key>, typename Predicate = eastl::equal_to<Key>, + template <typename Key, typename T, typename Hash = eastl::hash<Key>, typename Predicate = eastl::equal_to<Key>, typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false> class hash_map : public hashtable<Key, eastl::pair<const Key, T>, Allocator, eastl::use_first<eastl::pair<const Key, T> >, Predicate, Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, true> { public: - typedef hashtable<Key, eastl::pair<const Key, T>, Allocator, - eastl::use_first<eastl::pair<const Key, T> >, - Predicate, Hash, mod_range_hashing, default_ranged_hash, + typedef hashtable<Key, eastl::pair<const Key, T>, Allocator, + eastl::use_first<eastl::pair<const Key, T> >, + Predicate, Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, true> base_type; typedef hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode> this_type; typedef typename base_type::size_type size_type; @@ -125,8 +125,19 @@ namespace eastl /// /// Default constructor. /// - explicit hash_map(const allocator_type& allocator = EASTL_HASH_MAP_DEFAULT_ALLOCATOR) - : base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), + hash_map() + : this_type(EASTL_HASH_MAP_DEFAULT_ALLOCATOR) + { + // Empty + } + + + /// hash_map + /// + /// Constructor which creates an empty container with allocator. + /// + explicit hash_map(const allocator_type& allocator) + : base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_first<eastl::pair<const Key, T> >(), allocator) { // Empty @@ -136,12 +147,12 @@ 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(), + 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<eastl::pair<const Key, T> >(), allocator) { // Empty @@ -168,12 +179,12 @@ namespace eastl /// hash_map /// - /// initializer_list-based constructor. + /// initializer_list-based constructor. /// Allows for initializing with brace values (e.g. hash_map<int, char*> hm = { {3,"c"}, {4,"d"}, {5,"e"} }; ) - /// - hash_map(std::initializer_list<value_type> ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), + /// + hash_map(std::initializer_list<value_type> 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<eastl::pair<const Key, T> >(), allocator) { // Empty @@ -182,13 +193,13 @@ namespace eastl /// 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 <typename ForwardIterator> - 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<eastl::pair<const Key, T> >(), allocator) { // Empty @@ -215,8 +226,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) @@ -285,15 +296,61 @@ namespace eastl return (*base_type::DoInsertKey(true_type(), eastl::move(key)).first).second; } + // try_emplace API added in C++17 + template <class... Args> + inline insert_return_type try_emplace(const key_type& k, Args&&... args) + { + return try_emplace_forwarding(k, eastl::forward<Args>(args)...); + } + + template <class... Args> + inline insert_return_type try_emplace(key_type&& k, Args&&... args) { + return try_emplace_forwarding(eastl::move(k), eastl::forward<Args>(args)...); + } + + template <class... Args> + inline iterator try_emplace(const_iterator, const key_type& k, Args&&... args) { + // Currently, the first parameter is ignored. + insert_return_type result = try_emplace(k, eastl::forward<Args>(args)...); + return base_type::DoGetResultIterator(true_type(), result); + } + + template <class... Args> + inline iterator try_emplace(const_iterator, key_type&& k, Args&&... args) { + // Currently, the first parameter is ignored. + insert_return_type result = try_emplace(eastl::move(k), eastl::forward<Args>(args)...); + return base_type::DoGetResultIterator(true_type(), result); + } + private: + template <class K, class... Args> + insert_return_type try_emplace_forwarding(K&& k, Args&&... args) + { + const auto key_data = base_type::DoFindKeyData(k); + if (key_data.node) + { // Node exists, no insertion needed. + return eastl::pair<iterator, bool>( + iterator(key_data.node, base_type::mpBucketArray + key_data.bucket_index), false); + } + else + { + node_type* const pNodeNew = + base_type::DoAllocateNode(piecewise_construct, eastl::forward_as_tuple(eastl::forward<K>(k)), + forward_as_tuple(eastl::forward<Args>(args)...)); + // the key might have been moved from above, so we can't use `k` anymore. + const auto& key = base_type::mExtractKey(pNodeNew->mValue); + return base_type::template DoInsertUniqueNode<true>(key, key_data.code, key_data.bucket_index, pNodeNew); + } + } }; // hash_map /// hash_map erase_if /// /// https://en.cppreference.com/w/cpp/container/unordered_map/erase_if template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode, typename UserPredicate> - void erase_if(eastl::hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate) + typename eastl::hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>::size_type erase_if(eastl::hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate) { + auto oldSize = c.size(); // Erases all elements that satisfy the predicate from the container. for (auto i = c.begin(), last = c.end(); i != last;) { @@ -306,13 +363,14 @@ namespace eastl ++i; } } + return oldSize - c.size(); } /// 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 Key, typename T, typename Hash = eastl::hash<Key>, typename Predicate = eastl::equal_to<Key>, @@ -322,9 +380,9 @@ namespace eastl Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, false> { public: - typedef hashtable<Key, eastl::pair<const Key, T>, Allocator, - eastl::use_first<eastl::pair<const Key, T> >, - Predicate, Hash, mod_range_hashing, default_ranged_hash, + typedef hashtable<Key, eastl::pair<const Key, T>, Allocator, + eastl::use_first<eastl::pair<const Key, T> >, + Predicate, Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, false> base_type; typedef hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode> this_type; typedef typename base_type::size_type size_type; @@ -339,7 +397,6 @@ namespace eastl using base_type::insert; private: - using base_type::try_emplace; using base_type::insert_or_assign; public: @@ -348,7 +405,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<eastl::pair<const Key, T> >(), allocator) { // Empty @@ -358,12 +415,12 @@ 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(), + 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<eastl::pair<const Key, T> >(), allocator) { // Empty @@ -390,12 +447,12 @@ namespace eastl /// hash_multimap /// - /// initializer_list-based constructor. + /// initializer_list-based constructor. /// Allows for initializing with brace values (e.g. hash_multimap<int, char*> hm = { {3,"c"}, {3,"C"}, {4,"d"} }; ) - /// - hash_multimap(std::initializer_list<value_type> ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(), + /// + hash_multimap(std::initializer_list<value_type> 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<eastl::pair<const Key, T> >(), allocator) { // Empty @@ -404,13 +461,13 @@ namespace eastl /// 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 <typename ForwardIterator> - 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<eastl::pair<const Key, T> >(), allocator) { // Empty @@ -437,8 +494,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) @@ -458,8 +515,9 @@ namespace eastl /// /// https://en.cppreference.com/w/cpp/container/unordered_multimap/erase_if template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode, typename UserPredicate> - void erase_if(eastl::hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate) + typename eastl::hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>::size_type erase_if(eastl::hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate) { + auto oldSize = c.size(); // Erases all elements that satisfy the predicate from the container. for (auto i = c.begin(), last = c.end(); i != last;) { @@ -472,6 +530,7 @@ namespace eastl ++i; } } + return oldSize - c.size(); } @@ -481,7 +540,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////// template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode> - inline bool operator==(const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a, + inline bool operator==(const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a, const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& b) { typedef typename hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>::const_iterator const_iterator; @@ -496,23 +555,24 @@ 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. } return true; } +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode> - inline bool operator!=(const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a, + inline bool operator!=(const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a, const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& b) { return !(a == b); } - +#endif template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode> - inline bool operator==(const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a, + inline bool operator==(const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a, const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& b) { typedef typename hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>::const_iterator const_iterator; @@ -522,9 +582,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<const_iterator, const_iterator> aRange; eastl::pair<const_iterator, const_iterator> bRange; @@ -545,12 +605,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; @@ -560,21 +620,17 @@ namespace eastl return true; } +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode> - inline bool operator!=(const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a, + inline bool operator!=(const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a, const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& b) { return !(a == b); } +#endif } // namespace eastl #endif // Header include guard - - - - - - diff --git a/EASTL/include/EASTL/hash_set.h b/EASTL/include/EASTL/hash_set.h index c075975..3215d36 100644 --- a/EASTL/include/EASTL/hash_set.h +++ b/EASTL/include/EASTL/hash_set.h @@ -117,8 +117,19 @@ namespace eastl /// hash_set /// /// Default constructor. - /// - explicit hash_set(const allocator_type& allocator = EASTL_HASH_SET_DEFAULT_ALLOCATOR) + /// + hash_set() + : this_type(EASTL_HASH_SET_DEFAULT_ALLOCATOR) + { + // Empty + } + + + /// hash_set + /// + /// Constructor which creates an empty container with allocator. + /// + explicit hash_set(const allocator_type& allocator) : base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_self<Value>(), allocator) { // Empty @@ -207,8 +218,9 @@ namespace eastl /// /// https://en.cppreference.com/w/cpp/container/unordered_set/erase_if template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode, typename UserPredicate> - void erase_if(eastl::hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate) + typename eastl::hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>::size_type erase_if(eastl::hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate) { + auto oldSize = c.size(); // Erases all elements that satisfy the predicate pred from the container. for (auto i = c.begin(), last = c.end(); i != last;) { @@ -221,6 +233,7 @@ namespace eastl ++i; } } + return oldSize - c.size(); } @@ -341,8 +354,9 @@ namespace eastl /// /// https://en.cppreference.com/w/cpp/container/unordered_multiset/erase_if template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode, typename UserPredicate> - void erase_if(eastl::hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate) + typename eastl::hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>::size_type erase_if(eastl::hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate) { + auto oldSize = c.size(); // Erases all elements that satisfy the predicate pred from the container. for (auto i = c.begin(), last = c.end(); i != last;) { @@ -355,6 +369,7 @@ namespace eastl ++i; } } + return oldSize - c.size(); } @@ -386,13 +401,14 @@ namespace eastl return true; } +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode> inline bool operator!=(const hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& a, const hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& b) { return !(a == b); } - +#endif template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode> inline bool operator==(const hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& a, @@ -443,12 +459,14 @@ namespace eastl return true; } +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode> inline bool operator!=(const hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& a, const hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& b) { return !(a == b); } +#endif } // namespace eastl diff --git a/EASTL/include/EASTL/heap.h b/EASTL/include/EASTL/heap.h index f0e770b..a8e4260 100644 --- a/EASTL/include/EASTL/heap.h +++ b/EASTL/include/EASTL/heap.h @@ -55,7 +55,7 @@ namespace eastl inline void promote_heap_impl(RandomAccessIterator first, Distance topPosition, Distance position, T value) { for(Distance parentPosition = (position - 1) >> 1; // This formula assumes that (position > 0). // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>. - (position > topPosition) && (*(first + parentPosition) < value); + (position > topPosition) && eastl::less<ValueType>()(*(first + parentPosition), value); parentPosition = (position - 1) >> 1) { *(first + position) = eastl::forward<ValueType>(*(first + parentPosition)); // Swap the node with its parent. @@ -170,7 +170,7 @@ namespace eastl for(; childPosition < heapSize; childPosition = (2 * childPosition) + 2) { - if(*(first + childPosition) < *(first + (childPosition - 1))) // Choose the larger of the two children. + if(eastl::less<ValueType>()(*(first + childPosition), *(first + (childPosition - 1)))) // Choose the larger of the two children. --childPosition; *(first + position) = eastl::forward<ValueType>(*(first + childPosition)); // Swap positions with this child. position = childPosition; diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h index 142a514..77c383a 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h @@ -68,7 +68,7 @@ #define EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, MemoryOrder, PRE_COMPUTE_DESIRED, POST_COMPUTE_RET) \ { \ - bool cmpxchgRet; \ + EASTL_ATOMIC_DEFAULT_INIT(bool, cmpxchgRet); \ EASTL_ATOMIC_LOAD_RELAXED_64(type, ret, ptr); \ do \ { \ @@ -106,7 +106,7 @@ #define EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, MemoryOrder, PRE_COMPUTE_DESIRED, POST_COMPUTE_RET) \ { \ - bool cmpxchgRet; \ + EASTL_ATOMIC_DEFAULT_INIT(bool, cmpxchgRet); \ /* This is intentionally a non-atomic 128-bit load which may observe shearing. */ \ /* Either we do not observe *(ptr) but then the cmpxchg will fail and the observed */ \ /* atomic load will be returned. Or the non-atomic load got lucky and the cmpxchg succeeds */ \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_exchange.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_exchange.h index 624d2f5..b1de7d8 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_exchange.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_exchange.h @@ -56,7 +56,7 @@ #define EASTL_ARCH_ATOMIC_X86_EXCHANGE_128(type, ret, ptr, val, MemoryOrder) \ { \ - bool cmpxchgRet; \ + EASTL_ATOMIC_DEFAULT_INIT(bool, cmpxchgRet); \ /* This is intentionally a non-atomic 128-bit load which may observe shearing. */ \ /* Either we do not observe *(ptr) but then the cmpxchg will fail and the observed */ \ /* atomic load will be returned. Or the non-atomic load got lucky and the cmpxchg succeeds */ \ diff --git a/EASTL/include/EASTL/internal/atomic/atomic_base_width.h b/EASTL/include/EASTL/internal/atomic/atomic_base_width.h index ca47618..ac76097 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_base_width.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_base_width.h @@ -64,7 +64,7 @@ namespace internal #define EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(op, bits) \ - bool retVal; \ + EASTL_ATOMIC_DEFAULT_INIT(bool, retVal); \ EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits) fixedWidthDesired = EASTL_ATOMIC_TYPE_PUN_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), desired); \ EA_PREPROCESSOR_JOIN(op, bits)(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), \ retVal, \ diff --git a/EASTL/include/EASTL/internal/atomic/atomic_integral.h b/EASTL/include/EASTL/internal/atomic/atomic_integral.h index bcf7c17..a9c96c7 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_integral.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_integral.h @@ -156,7 +156,7 @@ namespace internal struct atomic_integral_width; #define EASTL_ATOMIC_INTEGRAL_FUNC_IMPL(op, bits) \ - T retVal; \ + EASTL_ATOMIC_DEFAULT_INIT(T, retVal); \ EA_PREPROCESSOR_JOIN(op, bits)(T, retVal, this->GetAtomicAddress(), arg); \ return retVal; diff --git a/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros.h b/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros.h index 941ac51..437b221 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros.h @@ -10,6 +10,7 @@ #pragma once #endif +#include <EABase/eabase.h> #include "atomic_macros_base.h" @@ -141,5 +142,15 @@ #endif +// We write some of our variables in inline assembly, which MSAN +// doesn't understand. This macro forces initialization of those +// variables when MSAN is enabled and doesn't pay the initialization +// cost when it's not enabled. +#if EA_MSAN_ENABLED + #define EASTL_ATOMIC_DEFAULT_INIT(type, var) type var{} +#else + #define EASTL_ATOMIC_DEFAULT_INIT(type, var) type var +#endif // EA_MSAN_ENABLED + #endif /* EASTL_ATOMIC_INTERNAL_ATOMIC_MACROS_H */ diff --git a/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_base.h b/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_base.h index f03720d..486e137 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_base.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_base.h @@ -17,8 +17,13 @@ #define EASTL_ATOMIC_INTERNAL_ARCH_AVAILABLE(op) \ EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ARCH_, op), _AVAILABLE) + +// We can't just use static_assert(false, ...) here, since on MSVC 17.10 +// the /Zc:static_assert flag makes non-dependent static_asserts in the body of a template +// be evaluated at template-parse time, rather than at template instantion time. +// So instead we just make the assert dependent on the type. #define EASTL_ATOMIC_INTERNAL_NOT_IMPLEMENTED_ERROR(...) \ - static_assert(false, "eastl::atomic<T> atomic macro not implemented!") + static_assert(!eastl::is_same_v<T,T>, "eastl::atomic<T> atomic macro not implemented!") /* Compiler && Arch Not Implemented */ diff --git a/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc.h b/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc.h index 6df8c05..90901ee 100644 --- a/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc.h +++ b/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc.h @@ -12,7 +12,6 @@ EA_DISABLE_ALL_VC_WARNINGS(); -#include <Windows.h> #include <intrin.h> EA_RESTORE_ALL_VC_WARNINGS(); diff --git a/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cpu_pause.h b/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cpu_pause.h index 720701a..5f436b8 100644 --- a/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cpu_pause.h +++ b/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cpu_pause.h @@ -10,18 +10,13 @@ #pragma once #endif - -///////////////////////////////////////////////////////////////////////////////// -// -// void EASTL_COMPILER_ATOMIC_CPU_PAUSE() -// -// NOTE: -// Rather obscure macro in Windows.h that expands to pause or rep; nop on -// compatible x86 cpus or the arm yield on compatible arm processors. -// This is nicer than switching on platform specific intrinsics. -// -#define EASTL_COMPILER_ATOMIC_CPU_PAUSE() \ - YieldProcessor() +#if defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64) + #define EASTL_COMPILER_ATOMIC_CPU_PAUSE() _mm_pause() +#elif defined(EA_PROCESSOR_ARM32) || defined(EA_PROCESSOR_ARM64) + #define EASTL_COMPILER_ATOMIC_CPU_PAUSE() __yield() +#else + #error Unsupported CPU architecture for EASTL_COMPILER_ATOMIC_CPU_PAUSE +#endif #endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CPU_PAUSE_H */ diff --git a/EASTL/include/EASTL/internal/config.h b/EASTL/include/EASTL/internal/config.h index 8dc1420..0564e18 100644 --- a/EASTL/include/EASTL/internal/config.h +++ b/EASTL/include/EASTL/internal/config.h @@ -89,8 +89,8 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef EASTL_VERSION - #define EASTL_VERSION "3.18.00" - #define EASTL_VERSION_N 31800 + #define EASTL_VERSION "3.20.02" + #define EASTL_VERSION_N 32002 #endif @@ -670,6 +670,17 @@ namespace eastl /////////////////////////////////////////////////////////////////////////////// +// EASTL_CRASH +// +// Executes an invalid memory write, which should result in an exception +// on most platforms. +// +/////////////////////////////////////////////////////////////////////////////// + +#define EASTL_CRASH() *((volatile int*)0) = 0xDEADC0DE; + + +/////////////////////////////////////////////////////////////////////////////// // EASTL_ALLOCATOR_COPY_ENABLED // // Defined as 0 or 1. Default is 0 (disabled) until some future date. @@ -825,7 +836,7 @@ namespace eastl // Defined as 0 or 1. // #ifndef EASTL_INT128_SUPPORTED - #if defined(__SIZEOF_INT128__) || (defined(EA_COMPILER_INTMAX_SIZE) && (EA_COMPILER_INTMAX_SIZE >= 16)) + #if defined(EA_COMPILER_INTMAX_SIZE) && (EA_COMPILER_INTMAX_SIZE >= 16) #define EASTL_INT128_SUPPORTED 1 #else #define EASTL_INT128_SUPPORTED 0 @@ -833,6 +844,21 @@ namespace eastl #endif +/////////////////////////////////////////////////////////////////////////////// +// EASTL_GCC_STYLE_INT128_SUPPORTED +// +// Defined as 0 or 1. +// Specifies whether __int128_t/__uint128_t are defined. +// +#ifndef EASTL_GCC_STYLE_INT128_SUPPORTED +#if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) +#define EASTL_GCC_STYLE_INT128_SUPPORTED 1 +#else +#define EASTL_GCC_STYLE_INT128_SUPPORTED 0 +#endif +#endif + + /////////////////////////////////////////////////////////////////////////////// // EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED @@ -859,12 +885,15 @@ namespace eastl // // Defined as 0 or 1. // Specifies whether eastl_int128_t/eastl_uint128_t have been typedef'd yet. +// NB: these types are not considered fundamental, arithmetic or integral when using the EAStdC implementation. +// this changes the compiler type traits defined in type_traits.h. +// eg. is_signed<eastl_int128_t>::value may be false, because it is not arithmetic. // #ifndef EASTL_INT128_DEFINED #if EASTL_INT128_SUPPORTED #define EASTL_INT128_DEFINED 1 - #if defined(__SIZEOF_INT128__) || defined(EA_COMPILER_GNUC) || defined(__clang__) + #if EASTL_GCC_STYLE_INT128_SUPPORTED typedef __int128_t eastl_int128_t; typedef __uint128_t eastl_uint128_t; #else @@ -875,7 +904,6 @@ namespace eastl #endif - /////////////////////////////////////////////////////////////////////////////// // EASTL_BITSET_WORD_TYPE_DEFAULT / EASTL_BITSET_WORD_SIZE_DEFAULT // @@ -1779,16 +1807,6 @@ typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept #ifndef EASTL_USER_LITERALS_ENABLED #if defined(EA_COMPILER_CPP14_ENABLED) #define EASTL_USER_LITERALS_ENABLED 1 - - // Disabling the Clang/GCC/MSVC warning about using user defined literals without a leading '_' as they are - // reserved for standard libary usage. - EA_DISABLE_CLANG_WARNING(-Wuser-defined-literals) - EA_DISABLE_CLANG_WARNING(-Wreserved-user-defined-literal) - EA_DISABLE_GCC_WARNING(-Wliteral-suffix) - #ifdef _MSC_VER - #pragma warning(disable: 4455) // disable warning C4455: literal suffix identifiers that do not start with an underscore are reserved - #endif - #else #define EASTL_USER_LITERALS_ENABLED 0 #endif @@ -1837,17 +1855,48 @@ typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept /// EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE #if defined(__clang__) + // NB: !__is_identifier() is correct: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970#c11 #if !__is_identifier(__has_unique_object_representations) #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 1 #else #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 0 #endif -#elif defined(_MSC_VER) && (_MSC_VER >= 1913) // VS2017+ +#elif defined(_MSC_VER) && (_MSC_VER >= 1913) // VS2017 15.6+ #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 1 #else #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 0 #endif +#if defined(__clang__) + // NB: !__is_identifier() is correct: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970#c11 + #if !__is_identifier(__is_final) + #define EASTL_IS_FINAL_AVAILABLE 1 + #else + #define EASTL_IS_FINAL_AVAILABLE 0 + #endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1914) // VS2017 15.7+ + #define EASTL_IS_FINAL_AVAILABLE 1 +#elif defined(EA_COMPILER_GNUC) + #define EASTL_IS_FINAL_AVAILABLE 1 +#else + #define EASTL_IS_FINAL_AVAILABLE 0 +#endif + +#if defined(__clang__) + // NB: !__is_identifier() is correct: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970#c11 + #if !__is_identifier(__is_aggregate) + #define EASTL_IS_AGGREGATE_AVAILABLE 1 + #else + #define EASTL_IS_AGGREGATE_AVAILABLE 0 + #endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1915) // VS2017 15.8+ + #define EASTL_IS_AGGREGATE_AVAILABLE 1 +#elif defined(EA_COMPILER_GNUC) + #define EASTL_IS_AGGREGATE_AVAILABLE 1 +#else + #define EASTL_IS_AGGREGATE_AVAILABLE 0 +#endif + /// EASTL_ENABLE_PAIR_FIRST_ELEMENT_CONSTRUCTOR /// This feature define allows users to toggle the problematic eastl::pair implicit diff --git a/EASTL/include/EASTL/internal/copy_help.h b/EASTL/include/EASTL/internal/copy_help.h index 67b5d87..0b2c1b8 100644 --- a/EASTL/include/EASTL/internal/copy_help.h +++ b/EASTL/include/EASTL/internal/copy_help.h @@ -121,19 +121,40 @@ namespace eastl }; + namespace internal { + // This exists to handle the case when EASTL_ITC_NS is `std` + // and the C++ version is older than C++20, in this case + // std::contiguous_iterator_tag does not exist so we can't use + // is_same<> directly. + #if !EASTL_STD_ITERATOR_CATEGORY_ENABLED || defined(EA_COMPILER_CPP20_ENABLED) + template <typename IC> + using is_contiguous_iterator_helper = eastl::is_same<IC, EASTL_ITC_NS::contiguous_iterator_tag>; + #else + template <typename IC> + using is_contiguous_iterator_helper = eastl::false_type; + #endif + + template <typename InputIterator, typename OutputIterator> + struct can_be_memmoved_helper { + using IIC = typename eastl::iterator_traits<InputIterator>::iterator_category; + using OIC = typename eastl::iterator_traits<OutputIterator>::iterator_category; + using value_type_input = typename eastl::iterator_traits<InputIterator>::value_type; + using value_type_output = typename eastl::iterator_traits<OutputIterator>::value_type; + + static constexpr bool value = eastl::is_trivially_copyable<value_type_output>::value && + eastl::is_same<value_type_input, value_type_output>::value && + (eastl::is_pointer<InputIterator>::value || is_contiguous_iterator_helper<IIC>::value) && + (eastl::is_pointer<OutputIterator>::value || is_contiguous_iterator_helper<OIC>::value); + + }; + } template <bool isMove, typename InputIterator, typename OutputIterator> inline OutputIterator move_and_copy_chooser(InputIterator first, InputIterator last, OutputIterator result) { typedef typename eastl::iterator_traits<InputIterator>::iterator_category IIC; - typedef typename eastl::iterator_traits<OutputIterator>::iterator_category OIC; - typedef typename eastl::iterator_traits<InputIterator>::value_type value_type_input; - typedef typename eastl::iterator_traits<OutputIterator>::value_type value_type_output; - const bool canBeMemmoved = eastl::is_trivially_copyable<value_type_output>::value && - eastl::is_same<value_type_input, value_type_output>::value && - (eastl::is_pointer<InputIterator>::value || eastl::is_same<IIC, eastl::contiguous_iterator_tag>::value) && - (eastl::is_pointer<OutputIterator>::value || eastl::is_same<OIC, eastl::contiguous_iterator_tag>::value); + const bool canBeMemmoved = internal::can_be_memmoved_helper<InputIterator, OutputIterator>::value; return eastl::move_and_copy_helper<IIC, isMove, canBeMemmoved>::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. } @@ -143,7 +164,7 @@ namespace eastl template <bool isMove, typename InputIterator, typename OutputIterator> inline OutputIterator move_and_copy_unwrapper(InputIterator first, InputIterator last, OutputIterator result) { - return OutputIterator(eastl::move_and_copy_chooser<isMove>(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), eastl::unwrap_iterator(result))); // Have to convert to OutputIterator because result.base() could be a T* + return OutputIterator(eastl::move_and_copy_chooser<isMove>(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* } diff --git a/EASTL/include/EASTL/internal/fixed_pool.h b/EASTL/include/EASTL/internal/fixed_pool.h index 4d71035..61c0557 100644 --- a/EASTL/include/EASTL/internal/fixed_pool.h +++ b/EASTL/include/EASTL/internal/fixed_pool.h @@ -1363,9 +1363,8 @@ namespace eastl } fixed_vector_allocator(const fixed_vector_allocator& x) + : mOverflowAllocator(x.mOverflowAllocator), mpPoolBegin(x.mpPoolBegin) { - mpPoolBegin = x.mpPoolBegin; - mOverflowAllocator = x.mOverflowAllocator; } fixed_vector_allocator& operator=(const fixed_vector_allocator& x) @@ -1480,12 +1479,14 @@ namespace eastl void* allocate(size_t /*n*/, int /*flags*/ = 0) { EASTL_ASSERT(false); // A fixed_vector should not reallocate, else the user has exhausted its space. + EASTL_CRASH(); // We choose to crash here since the owning vector can't handle an allocator returning null. Better to crash earlier. return NULL; } void* allocate(size_t /*n*/, size_t /*alignment*/, size_t /*offset*/, int /*flags*/ = 0) { - EASTL_ASSERT(false); + EASTL_ASSERT(false); // A fixed_vector should not reallocate, else the user has exhausted its space. + EASTL_CRASH(); // We choose to crash here since the owning vector can't handle an allocator returning null. Better to crash earlier. return NULL; } diff --git a/EASTL/include/EASTL/internal/function.h b/EASTL/include/EASTL/internal/function.h index 785969d..ace71d8 100644 --- a/EASTL/include/EASTL/internal/function.h +++ b/EASTL/include/EASTL/internal/function.h @@ -133,7 +133,7 @@ namespace eastl { return !f; } - +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template <typename R, typename... Args> bool operator==(std::nullptr_t, const function<R(Args...)>& f) EA_NOEXCEPT { @@ -151,7 +151,7 @@ namespace eastl { return !!f; } - +#endif template <typename R, typename... Args> void swap(function<R(Args...)>& lhs, function<R(Args...)>& rhs) { diff --git a/EASTL/include/EASTL/internal/functional_base.h b/EASTL/include/EASTL/internal/functional_base.h index ef27800..de446db 100644 --- a/EASTL/include/EASTL/internal/functional_base.h +++ b/EASTL/include/EASTL/internal/functional_base.h @@ -118,7 +118,8 @@ namespace eastl template <typename R, typename F, typename... Args> struct is_invocable_r_impl<R, F, void_t<typename invoke_result<F, Args...>::type>, Args...> - : public is_convertible<typename invoke_result<F, Args...>::type, R> {}; + : public disjunction<is_convertible<typename invoke_result<F, Args...>::type, R>, + is_same<typename remove_cv<R>::type, void>> {}; template <typename R, typename F, typename... Args> struct is_invocable_r : public is_invocable_r_impl<R, F, void, Args...> {}; @@ -232,7 +233,7 @@ namespace eastl T& get() const EA_NOEXCEPT; template <typename... ArgTypes> - typename eastl::result_of<T&(ArgTypes&&...)>::type operator() (ArgTypes&&...) const; + typename eastl::invoke_result<T&, ArgTypes...>::type operator() (ArgTypes&&...) const; private: T* val; @@ -269,7 +270,7 @@ namespace eastl template <typename T> template <typename... ArgTypes> - typename eastl::result_of<T&(ArgTypes&&...)>::type reference_wrapper<T>::operator() (ArgTypes&&... args) const + typename eastl::invoke_result<T&, ArgTypes...>::type reference_wrapper<T>::operator() (ArgTypes&&... args) const { return eastl::invoke(*val, eastl::forward<ArgTypes>(args)...); } diff --git a/EASTL/include/EASTL/internal/generic_iterator.h b/EASTL/include/EASTL/internal/generic_iterator.h index b32998a..0f1e28b 100644 --- a/EASTL/include/EASTL/internal/generic_iterator.h +++ b/EASTL/include/EASTL/internal/generic_iterator.h @@ -56,7 +56,6 @@ namespace eastl typedef typename eastl::iterator_traits<Iterator>::reference reference; typedef typename eastl::iterator_traits<Iterator>::pointer pointer; typedef Iterator iterator_type; - typedef iterator_type wrapped_iterator_type; // This is not in the C++ Standard; it's used by use to identify it as a wrapping iterator type. typedef Container container_type; typedef generic_iterator<Iterator, Container> this_type; @@ -109,6 +108,15 @@ namespace eastl const iterator_type& base() const { return mIterator; } + private: + // Unwrapping interface, not part of the public API. + const iterator_type& unwrap() const + { return mIterator; } + + // The unwrapper helpers need access to unwrap(). + friend is_iterator_wrapper_helper<this_type, true>; + friend is_iterator_wrapper<this_type>; + }; // class generic_iterator @@ -187,7 +195,7 @@ namespace eastl /// unwrap_generic_iterator /// - /// Returns Iterator::get_base() if it's a generic_iterator, else returns Iterator as-is. + /// Returns `it.base()` if it's a generic_iterator, else returns `it` as-is. /// /// Example usage: /// vector<int> intVector; @@ -196,7 +204,10 @@ namespace eastl /// template <typename Iterator> inline typename eastl::is_iterator_wrapper_helper<Iterator, eastl::is_generic_iterator<Iterator>::value>::iterator_type unwrap_generic_iterator(Iterator it) - { return eastl::is_iterator_wrapper_helper<Iterator, eastl::is_generic_iterator<Iterator>::value>::get_base(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<Iterator, eastl::is_generic_iterator<Iterator>::value>::get_unwrapped(it); + } } // namespace eastl diff --git a/EASTL/include/EASTL/internal/hashtable.h b/EASTL/include/EASTL/internal/hashtable.h index a9347b1..077f5b4 100644 --- a/EASTL/include/EASTL/internal/hashtable.h +++ b/EASTL/include/EASTL/internal/hashtable.h @@ -879,6 +879,12 @@ namespace eastl RehashPolicy mRehashPolicy; // To do: Use base class optimization to make this go away. allocator_type mAllocator; // To do: Use base class optimization to make this go away. + struct NodeFindKeyData { + node_type* node; + hash_code_t code; + size_type bucket_index; + }; + public: hashtable(size_type nBucketCount, const H1&, const H2&, const H&, const Equal&, const ExtractKey&, const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); @@ -1003,11 +1009,6 @@ namespace eastl template <class... Args> iterator emplace_hint(const_iterator position, Args&&... args); - template <class... Args> insert_return_type try_emplace(const key_type& k, Args&&... args); - template <class... Args> insert_return_type try_emplace(key_type&& k, Args&&... args); - template <class... Args> iterator try_emplace(const_iterator position, const key_type& k, Args&&... args); - template <class... Args> iterator try_emplace(const_iterator position, key_type&& k, Args&&... args); - insert_return_type insert(const value_type& value); insert_return_type insert(value_type&& otherValue); iterator insert(const_iterator hint, const value_type& value); @@ -1200,6 +1201,9 @@ namespace eastl node_type** DoAllocateBuckets(size_type n); void DoFreeBuckets(node_type** pBucketArray, size_type n); + template <bool bDeleteOnException, typename Enabled = bool_constant<bUniqueKeys>, ENABLE_IF_TRUETYPE(Enabled) = nullptr> // only enabled when keys are unique + eastl::pair<iterator, bool> DoInsertUniqueNode(const key_type& k, hash_code_t c, size_type n, node_type* pNodeNew); + template <typename BoolConstantT, class... Args, ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr> eastl::pair<iterator, bool> DoInsertValue(BoolConstantT, Args&&... args); @@ -1278,6 +1282,7 @@ namespace eastl void DoRehash(size_type nBucketCount); node_type* DoFindNode(node_type* pNode, const key_type& k, hash_code_t c) const; + NodeFindKeyData DoFindKeyData(const key_type& k) const; template <typename T> ENABLE_IF_HAS_HASHCODE(T, node_type) DoFindNode(T* pNode, hash_code_t c) const @@ -1293,6 +1298,14 @@ namespace eastl template <typename U, typename BinaryPredicate> node_type* DoFindNodeT(node_type* pNode, const U& u, BinaryPredicate predicate) const; + private: + template <typename V, typename Enabled = bool_constant<bUniqueKeys>, ENABLE_IF_TRUETYPE(Enabled) = nullptr> + eastl::pair<iterator, bool> DoInsertValueExtraForwarding(const key_type& k, + hash_code_t c, + node_type* pNodeNew, + V&& value); + + }; // class hashtable @@ -1953,6 +1966,16 @@ namespace eastl } + template <typename K, typename V, typename A, typename EK, typename Eq, + typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> + inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::NodeFindKeyData + hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoFindKeyData(const key_type& k) const { + NodeFindKeyData d; + d.code = get_hash_code(k); + d.bucket_index = (size_type)bucket_index(k, d.code, (uint32_t)mnBucketCount); + d.node = DoFindNode(mpBucketArray[d.bucket_index], k, d.code); + return d; + } template <typename K, typename V, typename A, typename EK, typename Eq, typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> @@ -1984,6 +2007,41 @@ namespace eastl } + template <typename K, typename V, typename A, typename EK, typename Eq, + typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> + template <bool bDeleteOnException, typename Enabled, ENABLE_IF_TRUETYPE(Enabled)> // only enabled when keys are unique + eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool> + hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertUniqueNode(const key_type& k, hash_code_t c, size_type n, node_type* pNodeNew) + { + const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + if(bRehash.first) + { + n = (size_type)bucket_index(k, c, (uint32_t)bRehash.second); + DoRehash(bRehash.second); + } + + EASTL_ASSERT((uintptr_t)mpBucketArray != (uintptr_t)&gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + ++mnElementCount; + + return eastl::pair<iterator, bool>(iterator(pNodeNew, mpBucketArray + n), true); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + EA_CONSTEXPR_IF(bDeleteOnException) { DoFreeNode(pNodeNew); } + throw; + } + #endif + } template <typename K, typename V, typename A, typename EK, typename Eq, typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> @@ -2010,34 +2068,7 @@ namespace eastl if(pNode == NULL) // If value is not present... add it. { - const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); - - set_code(pNodeNew, c); // This is a no-op for most hashtables. - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - if(bRehash.first) - { - n = (size_type)bucket_index(k, c, (uint32_t)bRehash.second); - DoRehash(bRehash.second); - } - - EASTL_ASSERT((uintptr_t)mpBucketArray != (uintptr_t)&gpEmptyBucketArray[0]); - pNodeNew->mpNext = mpBucketArray[n]; - mpBucketArray[n] = pNodeNew; - ++mnElementCount; - - return eastl::pair<iterator, bool>(iterator(pNodeNew, mpBucketArray + n), true); - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - DoFreeNode(pNodeNew); - throw; - } - #endif + return DoInsertUniqueNode<true>(k, c, n, pNodeNew); } else { @@ -2133,14 +2164,33 @@ namespace eastl // The reason is because the specializations below are slightly more efficient because they can delay // the creation of a node until it's known that it will be needed. //////////////////////////////////////////////////////////////////////////////////////////////////// - template <typename K, typename V, typename A, typename EK, typename Eq, typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> template <typename BoolConstantT> - eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool> + inline eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool> hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertValueExtra(BoolConstantT, const key_type& k, hash_code_t c, node_type* pNodeNew, value_type&& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. { + return DoInsertValueExtraForwarding(k, c, pNodeNew, eastl::move(value)); + } + + template <typename K, typename V, typename A, typename EK, typename Eq, + typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> + template <typename BoolConstantT> + inline eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool> + hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertValueExtra(BoolConstantT, const key_type& k, + hash_code_t c, node_type* pNodeNew, const value_type& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. + { + return DoInsertValueExtraForwarding(k, c, pNodeNew, value); + } + + template <typename K, typename V, typename A, typename EK, typename Eq, + typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> + template <typename VFwd, typename Enabled, ENABLE_IF_TRUETYPE(Enabled)> // true_type means bUniqueKeys is true. + eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool> + hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertValueExtraForwarding(const key_type& k, + hash_code_t c, node_type* pNodeNew, VFwd&& value) + { // Adds the value to the hash table if not already present. // If already present then the existing value is returned via an iterator/bool pair. size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); @@ -2148,56 +2198,18 @@ namespace eastl if(pNode == NULL) // If value is not present... add it. { - const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); - - // Allocate the new node before doing the rehash so that we don't + // Allocate the new node before doing the rehash so that we don't // do a rehash if the allocation throws. - #if EASTL_EXCEPTIONS_ENABLED - bool nodeAllocated; // If exceptions are enabled then we we need to track if we allocated the node so we can free it in the catch block. - #endif - if(pNodeNew) { - ::new(eastl::addressof(pNodeNew->mValue)) value_type(eastl::move(value)); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. - #if EASTL_EXCEPTIONS_ENABLED - nodeAllocated = false; - #endif + ::new(eastl::addressof(pNodeNew->mValue)) value_type(eastl::forward<VFwd>(value)); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. + return DoInsertUniqueNode<false>(k, c, n, pNodeNew); } else { pNodeNew = DoAllocateNode(eastl::move(value)); - #if EASTL_EXCEPTIONS_ENABLED - nodeAllocated = true; - #endif + return DoInsertUniqueNode<true>(k, c, n, pNodeNew); } - - set_code(pNodeNew, c); // This is a no-op for most hashtables. - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - if(bRehash.first) - { - n = (size_type)bucket_index(k, c, (uint32_t)bRehash.second); - DoRehash(bRehash.second); - } - - EASTL_ASSERT((uintptr_t)mpBucketArray != (uintptr_t)&gpEmptyBucketArray[0]); - pNodeNew->mpNext = mpBucketArray[n]; - mpBucketArray[n] = pNodeNew; - ++mnElementCount; - - return eastl::pair<iterator, bool>(iterator(pNodeNew, mpBucketArray + n), true); - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - if(nodeAllocated) // If we allocated the node within this function, free it. Else let the caller retain ownership of it. - DoFreeNode(pNodeNew); - throw; - } - #endif } // Else the value is already present, so don't add a new node. And don't free pNodeNew. @@ -2303,78 +2315,6 @@ namespace eastl #endif } - - template <typename K, typename V, typename A, typename EK, typename Eq, - typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> - template<typename BoolConstantT> - eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool> - hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertValueExtra(BoolConstantT, const key_type& k, hash_code_t c, node_type* pNodeNew, const value_type& value, - ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. - { - // Adds the value to the hash table if not already present. - // If already present then the existing value is returned via an iterator/bool pair. - size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); - node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); - - if(pNode == NULL) // If value is not present... add it. - { - const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); - - // Allocate the new node before doing the rehash so that we don't - // do a rehash if the allocation throws. - #if EASTL_EXCEPTIONS_ENABLED - bool nodeAllocated; // If exceptions are enabled then we we need to track if we allocated the node so we can free it in the catch block. - #endif - - if(pNodeNew) - { - ::new(eastl::addressof(pNodeNew->mValue)) value_type(value); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. - #if EASTL_EXCEPTIONS_ENABLED - nodeAllocated = false; - #endif - } - else - { - pNodeNew = DoAllocateNode(value); - #if EASTL_EXCEPTIONS_ENABLED - nodeAllocated = true; - #endif - } - - set_code(pNodeNew, c); // This is a no-op for most hashtables. - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - if(bRehash.first) - { - n = (size_type)bucket_index(k, c, (uint32_t)bRehash.second); - DoRehash(bRehash.second); - } - - EASTL_ASSERT((uintptr_t)mpBucketArray != (uintptr_t)&gpEmptyBucketArray[0]); - pNodeNew->mpNext = mpBucketArray[n]; - mpBucketArray[n] = pNodeNew; - ++mnElementCount; - - return eastl::pair<iterator, bool>(iterator(pNodeNew, mpBucketArray + n), true); - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - if(nodeAllocated) // If we allocated the node within this function, free it. Else let the caller retain ownership of it. - DoFreeNode(pNodeNew); - throw; - } - #endif - } - // Else the value is already present, so don't add a new node. And don't free pNodeNew. - - return eastl::pair<iterator, bool>(iterator(pNode, mpBucketArray + n), false); - } - - template <typename K, typename V, typename A, typename EK, typename Eq, typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> template<typename BoolConstantT> @@ -2695,54 +2635,6 @@ namespace eastl } template <typename K, typename V, typename A, typename EK, typename Eq, - typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> - template <class... Args> - // inline eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool> - inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert_return_type - hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::try_emplace(const key_type& key, Args&&... args) - { - return DoInsertValue(has_unique_keys_type(), piecewise_construct, eastl::forward_as_tuple(key), - eastl::forward_as_tuple(eastl::forward<Args>(args)...)); - } - - template <typename K, typename V, typename A, typename EK, typename Eq, - typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> - template <class... Args> - // inline eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool> - inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert_return_type - hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::try_emplace(key_type&& key, Args&&... args) - { - return DoInsertValue(has_unique_keys_type(), piecewise_construct, eastl::forward_as_tuple(eastl::move(key)), - eastl::forward_as_tuple(eastl::forward<Args>(args)...)); - } - - template <typename K, typename V, typename A, typename EK, typename Eq, - typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> - template <class... Args> - inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator - hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::try_emplace(const_iterator, const key_type& key, Args&&... args) - { - insert_return_type result = DoInsertValue( - has_unique_keys_type(), - value_type(piecewise_construct, eastl::forward_as_tuple(key), eastl::forward_as_tuple(eastl::forward<Args>(args)...))); - - return DoGetResultIterator(has_unique_keys_type(), result); - } - - template <typename K, typename V, typename A, typename EK, typename Eq, - typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> - template <class... Args> - inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator - hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::try_emplace(const_iterator, key_type&& key, Args&&... args) - { - insert_return_type result = - DoInsertValue(has_unique_keys_type(), value_type(piecewise_construct, eastl::forward_as_tuple(eastl::move(key)), - eastl::forward_as_tuple(eastl::forward<Args>(args)...))); - - return DoGetResultIterator(has_unique_keys_type(), result); - } - - template <typename K, typename V, typename A, typename EK, typename Eq, typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU> typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert_return_type hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert(value_type&& otherValue) @@ -2962,14 +2854,25 @@ namespace eastl while(*pBucketArray && !compare(k, c, *pBucketArray)) pBucketArray = &(*pBucketArray)->mpNext; + node_type* pDeleteList = nullptr; while(*pBucketArray && compare(k, c, *pBucketArray)) { node_type* const pNode = *pBucketArray; *pBucketArray = pNode->mpNext; - DoFreeNode(pNode); + // Don't free the node here, k might be a reference to the key inside this node, + // and we're re-using it when we compare to the following nodes. + // Instead, add it to the list of things to be deleted. + pNode->mpNext = pDeleteList; + pDeleteList = pNode; --mnElementCount; } + while (pDeleteList) { + node_type* const pToDelete = pDeleteList; + pDeleteList = pDeleteList->mpNext; + DoFreeNode(pToDelete); + } + return nElementCountSaved - mnElementCount; } diff --git a/EASTL/include/EASTL/internal/integer_sequence.h b/EASTL/include/EASTL/internal/integer_sequence.h index 2a5539d..ba5dd4e 100644 --- a/EASTL/include/EASTL/internal/integer_sequence.h +++ b/EASTL/include/EASTL/internal/integer_sequence.h @@ -24,6 +24,21 @@ public: static EA_CONSTEXPR size_t size() EA_NOEXCEPT { return sizeof...(Ints); } }; +template <size_t... Is> +using index_sequence = integer_sequence<size_t, Is...>; + +#if (defined(EA_COMPILER_GNUC) && EA_COMPILER_VERSION >= 8001) + +template <typename T, T N> +using make_integer_sequence = integer_sequence<T, __integer_pack(N)...>; + +#elif (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_BUILTIN(__make_integer_seq)) || (defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1910)) + +template <class T, T N> +using make_integer_sequence = __make_integer_seq<integer_sequence, T, N>; + +#else + template <size_t N, typename IndexSeq> struct make_index_sequence_impl; @@ -39,12 +54,6 @@ struct make_index_sequence_impl<0, integer_sequence<size_t, Is...>> typedef integer_sequence<size_t, Is...> type; }; -template <size_t... Is> -using index_sequence = integer_sequence<size_t, Is...>; - -template <size_t N> -using make_index_sequence = typename make_index_sequence_impl<N, integer_sequence<size_t>>::type; - template <typename Target, typename Seq> struct integer_sequence_convert_impl; @@ -54,15 +63,20 @@ struct integer_sequence_convert_impl<Target, integer_sequence<size_t, Is...>> typedef integer_sequence<Target, Is...> type; }; -template <typename T, size_t N> +template <typename T, T N> struct make_integer_sequence_impl { - typedef typename integer_sequence_convert_impl<T, make_index_sequence<N>>::type type; + typedef typename integer_sequence_convert_impl<T, typename make_index_sequence_impl<N, integer_sequence<size_t>>::type>::type type; }; -template <typename T, size_t N> +template <typename T, T N> using make_integer_sequence = typename make_integer_sequence_impl<T, N>::type; +#endif + +template <size_t N> +using make_index_sequence = make_integer_sequence<size_t, N>; + // Helper alias template that converts any type parameter pack into an index sequence of the same length template<typename... T> using index_sequence_for = make_index_sequence<sizeof...(T)>; diff --git a/EASTL/include/EASTL/internal/red_black_tree.h b/EASTL/include/EASTL/internal/red_black_tree.h index 111fbb4..5b29b7c 100644 --- a/EASTL/include/EASTL/internal/red_black_tree.h +++ b/EASTL/include/EASTL/internal/red_black_tree.h @@ -478,11 +478,6 @@ namespace eastl template <class... Args> iterator emplace_hint(const_iterator position, Args&&... args); - template <class... Args> eastl::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); - template <class... Args> eastl::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); - template <class... Args> iterator try_emplace(const_iterator position, const key_type& k, Args&&... args); - template <class... Args> iterator try_emplace(const_iterator position, key_type&& k, Args&&... args); - // Standard conversion overload to avoid the overhead of mismatched 'pair<const Key, Value>' types. template <class P, class = typename eastl::enable_if<eastl::is_constructible<value_type, P&&>::value>::type> insert_return_type insert(P&& otherValue); @@ -1109,43 +1104,6 @@ namespace eastl } template <typename K, typename V, typename C, typename A, typename E, bool bM, bool bU> - template <class... Args> - inline eastl::pair<typename rbtree<K, V, C, A, E, bM, bU>::iterator, bool> - rbtree<K, V, C, A, E, bM, bU>::try_emplace(const key_type& key, Args&&... args) - { - return DoInsertValue(has_unique_keys_type(), piecewise_construct, eastl::forward_as_tuple(key), eastl::forward_as_tuple(eastl::forward<Args>(args)...)); - } - - template <typename K, typename V, typename C, typename A, typename E, bool bM, bool bU> - template <class... Args> - inline eastl::pair<typename rbtree<K, V, C, A, E, bM, bU>::iterator, bool> - rbtree<K, V, C, A, E, bM, bU>::try_emplace(key_type&& key, Args&&... args) - { - return DoInsertValue(has_unique_keys_type(), piecewise_construct, eastl::forward_as_tuple(eastl::move(key)), eastl::forward_as_tuple(eastl::forward<Args>(args)...)); - } - - template <typename K, typename V, typename C, typename A, typename E, bool bM, bool bU> - template <class... Args> - inline typename rbtree<K, V, C, A, E, bM, bU>::iterator - rbtree<K, V, C, A, E, bM, bU>::try_emplace(const_iterator position, const key_type& key, Args&&... args) - { - return DoInsertValueHint( - has_unique_keys_type(), position, - piecewise_construct, eastl::forward_as_tuple(key), eastl::forward_as_tuple(eastl::forward<Args>(args)...)); - } - - template <typename K, typename V, typename C, typename A, typename E, bool bM, bool bU> - template <class... Args> - inline typename rbtree<K, V, C, A, E, bM, bU>::iterator - rbtree<K, V, C, A, E, bM, bU>::try_emplace(const_iterator position, key_type&& key, Args&&... args) - { - return DoInsertValueHint( - has_unique_keys_type(), position, - piecewise_construct, eastl::forward_as_tuple(eastl::move(key)), eastl::forward_as_tuple(eastl::forward<Args>(args)...)); - } - - - template <typename K, typename V, typename C, typename A, typename E, bool bM, bool bU> template <class P, class> inline typename rbtree<K, V, C, A, E, bM, bU>::insert_return_type // map/set::insert return a pair, multimap/multiset::iterator return an iterator. rbtree<K, V, C, A, E, bM, bU>::insert(P&& otherValue) diff --git a/EASTL/include/EASTL/internal/thread_support.h b/EASTL/include/EASTL/internal/thread_support.h index 60272a9..49856c0 100644 --- a/EASTL/include/EASTL/internal/thread_support.h +++ b/EASTL/include/EASTL/internal/thread_support.h @@ -93,92 +93,6 @@ namespace eastl { namespace Internal { - /// atomic_increment - /// Returns the new value. - inline int32_t atomic_increment(int32_t* p32) EA_NOEXCEPT - { - #if defined(__clang__) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) - return __sync_add_and_fetch(p32, 1); - #elif defined(EA_COMPILER_MSVC) - static_assert(sizeof(long) == sizeof(int32_t), "unexpected size"); - return _InterlockedIncrement((volatile long*)p32); - #elif defined(EA_COMPILER_GNUC) - int32_t result; - __asm__ __volatile__ ("lock; xaddl %0, %1" - : "=r" (result), "=m" (*p32) - : "0" (1), "m" (*p32) - : "memory" - ); - return result + 1; - #else - EASTL_FAIL_MSG("EASTL thread safety is not implemented yet. See EAThread for how to do this for the given platform."); - return ++*p32; - #endif - } - - /// atomic_decrement - /// Returns the new value. - inline int32_t atomic_decrement(int32_t* p32) EA_NOEXCEPT - { - #if defined(__clang__) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) - return __sync_add_and_fetch(p32, -1); - #elif defined(EA_COMPILER_MSVC) - return _InterlockedDecrement((volatile long*)p32); // volatile long cast is OK because int32_t == long on Microsoft platforms. - #elif defined(EA_COMPILER_GNUC) - int32_t result; - __asm__ __volatile__ ("lock; xaddl %0, %1" - : "=r" (result), "=m" (*p32) - : "0" (-1), "m" (*p32) - : "memory" - ); - return result - 1; - #else - EASTL_FAIL_MSG("EASTL thread safety is not implemented yet. See EAThread for how to do this for the given platform."); - return --*p32; - #endif - } - - - /// atomic_compare_and_swap - /// Safely sets the value to a new value if the original value is equal to - /// a condition value. Returns true if the condition was met and the - /// assignment occurred. The comparison and value setting are done as - /// an atomic operation and thus another thread cannot intervene between - /// the two as would be the case with simple C code. - inline bool atomic_compare_and_swap(int32_t* p32, int32_t newValue, int32_t condition) - { - #if defined(__clang__) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) - return __sync_bool_compare_and_swap(p32, condition, newValue); - #elif defined(EA_COMPILER_MSVC) - return ((int32_t)_InterlockedCompareExchange((volatile long*)p32, (long)newValue, (long)condition) == condition); - #elif defined(EA_COMPILER_GNUC) - // GCC Inline ASM Constraints - // r <--> Any general purpose register - // a <--> The a register. - // 1 <--> The constraint '1' for operand 2 says that it must occupy the same location as operand 1. - // =a <--> output registers - // =r <--> output registers - - int32_t result; - __asm__ __volatile__( - "lock; cmpxchgl %3, (%1) \n" // Test *p32 against EAX, if same, then *p32 = newValue - : "=a" (result), "=r" (p32) // outputs - : "a" (condition), "r" (newValue), "1" (p32) // inputs - : "memory" // clobbered - ); - return result == condition; - #else - EASTL_FAIL_MSG("EASTL thread safety is not implemented yet. See EAThread for how to do this for the given platform."); - if(*p32 == condition) - { - *p32 = newValue; - return true; - } - return false; - #endif - } - - // mutex #if EASTL_CPP11_MUTEX_ENABLED using std::mutex; diff --git a/EASTL/include/EASTL/internal/type_compound.h b/EASTL/include/EASTL/internal/type_compound.h index 1f85250..339dc8e 100644 --- a/EASTL/include/EASTL/internal/type_compound.h +++ b/EASTL/include/EASTL/internal/type_compound.h @@ -128,58 +128,18 @@ namespace eastl #define EASTL_TYPE_TRAIT_is_member_function_pointer_CONFORMANCE 1 // is_member_function_pointer is conforming; doesn't make mistakes. - // To do: Revise this to support C++11 variadic templates when possible. - // To do: We can probably also use remove_cv to simply the multitude of types below. - - template <typename T> struct is_mem_fun_pointer_value : public false_type{}; - - template <typename R, typename T> struct is_mem_fun_pointer_value<R (T::*)()> : public true_type{}; - template <typename R, typename T> struct is_mem_fun_pointer_value<R (T::*)() const> : public true_type{}; - template <typename R, typename T> struct is_mem_fun_pointer_value<R (T::*)() volatile> : public true_type{}; - template <typename R, typename T> struct is_mem_fun_pointer_value<R (T::*)() const volatile> : public true_type{}; - - template <typename R, typename T, typename Arg0> struct is_mem_fun_pointer_value<R (T::*)(Arg0)> : public true_type{}; - template <typename R, typename T, typename Arg0> struct is_mem_fun_pointer_value<R (T::*)(Arg0) const> : public true_type{}; - template <typename R, typename T, typename Arg0> struct is_mem_fun_pointer_value<R (T::*)(Arg0) volatile> : public true_type{}; - template <typename R, typename T, typename Arg0> struct is_mem_fun_pointer_value<R (T::*)(Arg0) const volatile> : public true_type{}; - - template <typename R, typename T, typename Arg0, typename Arg1> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1)> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1) const> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1) volatile> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1) const volatile> : public true_type{}; - - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2)> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2) const> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2) volatile> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2) const volatile> : public true_type{}; - - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3)> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3) const> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3) volatile> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3) const volatile> : public true_type{}; - - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4)> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4) const> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4) volatile> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4) const volatile> : public true_type{}; - - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5)> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5) const> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5) volatile> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5) const volatile> : public true_type{}; - - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6)> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) const> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) volatile> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) const volatile> : public true_type{}; - - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6, typename Arg7> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7)> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6, typename Arg7> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) const> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6, typename Arg7> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) volatile> : public true_type{}; - template <typename R, typename T, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6, typename Arg7> struct is_mem_fun_pointer_value<R (T::*)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) const volatile> : public true_type{}; + namespace internal + { + template<typename T> + struct is_member_function_pointer_helper : false_type {}; - template <typename T> - struct is_member_function_pointer : public integral_constant<bool, is_mem_fun_pointer_value<T>::value>{}; + template<typename T, typename U> + struct is_member_function_pointer_helper<T U::*> : is_function<T> {}; + } + + template<typename T> + struct is_member_function_pointer + : internal::is_member_function_pointer_helper<typename remove_cv<T>::type> {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template<typename T> @@ -198,13 +158,19 @@ namespace eastl #define EASTL_TYPE_TRAIT_is_member_pointer_CONFORMANCE 1 // is_member_pointer is conforming; doesn't make mistakes. - template <typename T> - struct is_member_pointer - : public eastl::integral_constant<bool, eastl::is_member_function_pointer<T>::value>{}; + namespace internal { + template <typename T> + struct is_member_pointer_helper + : public eastl::false_type {}; - template <typename T, typename U> - struct is_member_pointer<U T::*> - : public eastl::true_type{}; + template <typename T, typename U> + struct is_member_pointer_helper<U T::*> + : public eastl::true_type {}; + } + + template<typename T> + struct is_member_pointer + : public internal::is_member_pointer_helper<typename remove_cv<T>::type>::type {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template<typename T> @@ -690,7 +656,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////// // is_final /////////////////////////////////////////////////////////////////////// - #if EA_COMPILER_HAS_FEATURE(is_final) + #if EASTL_IS_FINAL_AVAILABLE == 1 template <typename T> struct is_final : public integral_constant<bool, __is_final(T)> {}; #else @@ -722,7 +688,7 @@ namespace eastl // * no default member initializers // /////////////////////////////////////////////////////////////////////// - #if EA_COMPILER_HAS_FEATURE(is_aggregate) || defined(_MSC_VER) && (_MSC_VER >= 1916) // VS2017 15.9+ + #if EASTL_IS_AGGREGATE_AVAILABLE == 1 #define EASTL_TYPE_TRAIT_is_aggregate_CONFORMANCE 1 template <typename T> diff --git a/EASTL/include/EASTL/internal/type_detected.h b/EASTL/include/EASTL/internal/type_detected.h new file mode 100644 index 0000000..e368a6f --- /dev/null +++ b/EASTL/include/EASTL/internal/type_detected.h @@ -0,0 +1,180 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_TYPE_DETECTED_H +#define EASTL_INTERNAL_TYPE_DETECTED_H + + +#include <EABase/eabase.h> +#if defined(EA_PRAGMA_ONCE_SUPPORTED) +#pragma once +#endif + +#include <EASTL/type_traits.h> + +namespace eastl +{ + /////////////////////////////////////////////////////////////////////// + // nonesuch + // + // Type given as a result from detected_t if the supplied arguments does not respect the constraint. + // + // https://en.cppreference.com/w/cpp/experimental/nonesuch + // + /////////////////////////////////////////////////////////////////////// + struct nonesuch + { + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + void operator=(nonesuch const&) = delete; + }; + + namespace internal + { + template <class Default, class AlwaysVoid, template <class...> class Op, class... Args> + struct detector + { + using type = Default; + using value_t = false_type; + }; + + template <class Default, template <class...> class Op, class... Args> + struct detector<Default, void_t<Op<Args...>>, Op, Args...> + { + using type = Op<Args...>; + using value_t = true_type; + }; + } // namespace internal + + /////////////////////////////////////////////////////////////////////// + // is_detected + // + // Checks if some supplied arguments (Args) respect a constraint (Op). + // is_detected expands to true_type if the arguments respect the constraint, false_type otherwise. + // This helper is convenient to use for compile time introspection. + // + // https://en.cppreference.com/w/cpp/experimental/is_detected + // + // Example: + // template <class T, class U> + // using detect_can_use_addition_operator = decltype(declval<T>() + declval<U>()); + // + // template <class T, class U> + // void sum(const T& t, const U& u) + // { + // static_assert(is_detected<detect_can_use_addition_operator, T, U>::value, "Supplied types cannot be summedtogether."); + // // or... + // static_assert(is_detected_v<detect_can_use_addition_operator, T, U>, "Supplied types cannot be summedtogether."); + // return t + u; + // } + // + /////////////////////////////////////////////////////////////////////// + template <template <class...> class Op, class... Args> + using is_detected = typename internal::detector<nonesuch, void, Op, Args...>::value_t; + +#if EASTL_VARIABLE_TEMPLATES_ENABLED + template <template <class...> class Op, class... Args> + EA_CONSTEXPR bool is_detected_v = is_detected<Op, Args...>::value; +#endif + + /////////////////////////////////////////////////////////////////////// + // detected_t + // + // Check which type we obtain after expanding some arguments (Args) over a constraint (Op). + // If the constraint cannot be applied, the result type will be nonesuch. + // + // https://en.cppreference.com/w/cpp/experimental/is_detected + // + // Example: + // template <class T, class U> + // using detect_can_use_addition_operator = decltype(declval<T>() + declval<U>()); + // + // using result_type = detected_t<detect_can_use_addition_operator, int, int>; + // // result_type == int + // using failed_result_type = detected_t<detect_can_use_addition_operator, int, string>; + // // failed_result_type == nonesuch + // + /////////////////////////////////////////////////////////////////////// + template <template <class...> class Op, class... Args> + using detected_t = typename internal::detector<nonesuch, void, Op, Args...>::type; + + /////////////////////////////////////////////////////////////////////// + // detected_or + // + // Checks if some supplied arguments (Args) respect a constraint (Op). + // Expand to a struct that contains two type aliases: + // - type: the type we obtain after expanding some arguments (Args) over a constraint (Op). + // If the constraint cannot be applied, the result type will be the suplied Default type. + // - value_t: true_type if the arguments respect the constraint, false_type otherwise. + // + // https://en.cppreference.com/w/cpp/experimental/is_detected + // + // Example: + // template <class T, class U> + // using detected_calling_foo = decltype(declval<T>().foo()); + // + // using result = detected_or<bool, detected_calling_foo, std::string>; // std::string doesn't have foo member. + // function. + // // result::type == bool + // // result::value_t == false_type + // + /////////////////////////////////////////////////////////////////////// + template <class Default, template <class...> class Op, class... Args> + using detected_or = internal::detector<Default, void, Op, Args...>; + + /////////////////////////////////////////////////////////////////////// + // detected_or_t + // + // Equivalent to detected_or<Default, Op, Args...>::type. + // + /////////////////////////////////////////////////////////////////////// + template <class Default, template <class...> class Op, class... Args> + using detected_or_t = typename detected_or<Default, Op, Args...>::type; + + /////////////////////////////////////////////////////////////////////// + // is_detected_exact + // + // Check that the type we obtain after expanding some arguments (Args) over a constraint (Op) is equivalent to + // Expected. + // + // template <class T, class U> + // using detected_calling_size = decltype(declval<T>().size()); + // + // using result = is_detected_exact<int, detected_calling_size, std::string>; + // result == false_type // std::string::size returns eastl_size_t which is not the same as int. + // + /////////////////////////////////////////////////////////////////////// + template <class Expected, template <class...> class Op, class... Args> + using is_detected_exact = is_same<Expected, detected_t<Op, Args...>>; + +#if EASTL_VARIABLE_TEMPLATES_ENABLED + template <class Expected, template <class...> class Op, class... Args> + EA_CONSTEXPR bool is_detected_exact_v = is_detected_exact<Expected, Op, Args...>::value; +#endif + + /////////////////////////////////////////////////////////////////////// + // is_detected_convertible + // + // Check that the type we obtain after expanding some arguments (Args) over a constraint (Op) is convertible to + // Expected. + // + // template <class T, class U> + // using detected_calling_size = decltype(declval<T>().size()); + // + // using result = is_detected_convertible<int, detected_calling_size, std::string>; + // result == true_type // std::string::size returns eastl_size_t which is convertible to int. + // + /////////////////////////////////////////////////////////////////////// + template <class To, template <class...> class Op, class... Args> + using is_detected_convertible = is_convertible<detected_t<Op, Args...>, To>; + +#if EASTL_VARIABLE_TEMPLATES_ENABLED + template <class To, template <class...> class Op, class... Args> + EA_CONSTEXPR bool is_detected_convertible_v = is_detected_convertible<To, Op, Args...>::value; +#endif + +} // namespace eastl + +#endif // EASTL_INTERNAL_TYPE_DETECTED_H
\ No newline at end of file diff --git a/EASTL/include/EASTL/internal/type_fundamental.h b/EASTL/include/EASTL/internal/type_fundamental.h index 5ff9259..c99b70c 100644 --- a/EASTL/include/EASTL/internal/type_fundamental.h +++ b/EASTL/include/EASTL/internal/type_fundamental.h @@ -130,6 +130,10 @@ namespace eastl template <> struct is_integral_helper<bool> : public true_type{}; template <> struct is_integral_helper<char> : public true_type{}; + + #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE + template <> struct is_integral_helper<char8_t> : public true_type{}; + #endif #if defined(EA_CHAR16_NATIVE) && EA_CHAR16_NATIVE template <> struct is_integral_helper<char16_t> : public true_type{}; #endif @@ -139,7 +143,7 @@ namespace eastl #ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type which is already handled above... template <> struct is_integral_helper<wchar_t> : public true_type{}; #endif - #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #if EASTL_GCC_STYLE_INT128_SUPPORTED template <> struct is_integral_helper<__int128_t> : public true_type{}; template <> struct is_integral_helper<__uint128_t> : public true_type{}; #endif diff --git a/EASTL/include/EASTL/internal/type_pod.h b/EASTL/include/EASTL/internal/type_pod.h index 998e957..fef5511 100644 --- a/EASTL/include/EASTL/internal/type_pod.h +++ b/EASTL/include/EASTL/internal/type_pod.h @@ -693,7 +693,7 @@ namespace eastl // template <typename T> - struct is_trivially_copyable { static const bool value = __is_trivially_copyable(T); }; + struct is_trivially_copyable : public bool_constant<__is_trivially_copyable(T)> {}; #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_MSVC) || defined(EA_COMPILER_GNUC)) #define EASTL_TYPE_TRAIT_is_trivially_copyable_CONFORMANCE 1 @@ -850,7 +850,7 @@ namespace eastl // whether the __is_trivially_constructible compiler intrinsic is available. // 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)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_trivially_constructible)) || defined(EA_COMPILER_MSVC)) template <typename T, typename Arg0 = eastl::unused> struct is_trivially_constructible @@ -915,7 +915,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)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(__clang__) && EA_COMPILER_HAS_FEATURE(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. @@ -1691,7 +1691,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if 0 // defined(_MSC_VER) && (_MSC_VER >= 1800) // VS2013+ -- Disabled due to __is_destructible being broken in VC++ versions up to at least VS2013. A ticket will be submitted for this + #if defined(_MSC_VER) && (_MSC_VER >= 1920) #define EASTL_TYPE_TRAIT_is_destructible_CONFORMANCE 1 template <typename T> @@ -1710,24 +1710,19 @@ namespace eastl struct is_destructible : public eastl::integral_constant<bool, !eastl::is_array_of_unknown_bounds<T>::value && !eastl::is_void<T>::value && - !eastl::is_function<T>::value && - !eastl::is_abstract<T>::value> {}; + !eastl::is_function<T>::value> {}; #else #define EASTL_TYPE_TRAIT_is_destructible_CONFORMANCE 1 - template <typename U> - struct destructible_test_helper{ U u; }; - template <typename> eastl::false_type destructible_test_function(...); - template <typename T, typename U = decltype(eastl::declval<eastl::destructible_test_helper<T> >().~destructible_test_helper<T>())> + template <typename T, typename U = typename eastl::remove_all_extents<T>::type, typename V = decltype(eastl::declval<U&>().~U())> eastl::true_type destructible_test_function(int); template <typename T, bool = eastl::is_array_of_unknown_bounds<T>::value || // Exclude these types from being considered destructible. eastl::is_void<T>::value || - eastl::is_function<T>::value || - eastl::is_abstract<T>::value> + eastl::is_function<T>::value> struct is_destructible_helper : public eastl::identity<decltype(eastl::destructible_test_function<T>(0))>::type {}; // Need to wrap decltype with identity because some compilers otherwise don't like the bare decltype usage. @@ -1735,6 +1730,14 @@ namespace eastl struct is_destructible_helper<T, true> : public eastl::false_type {}; + template <typename T, bool Whatever> + struct is_destructible_helper<T&, Whatever> // Reference are trivially destructible. + : public eastl::true_type {}; + + template <typename T, bool Whatever> + struct is_destructible_helper<T&&, Whatever> // Reference are trivially destructible. + : public eastl::true_type {}; + template <typename T> struct is_destructible : public is_destructible_helper<T> {}; @@ -1771,7 +1774,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if 0 // defined(_MSC_VER) && (_MSC_VER >= 1800) // VS2013+ -- Disabled due to __is_trivially_destructible being broken in VC++ versions up to at least VS2013. A ticket will be submitted for this + #if defined(_MSC_VER) && (_MSC_VER >= 1920) #define EASTL_TYPE_TRAIT_is_trivially_destructible_CONFORMANCE 1 template <typename T> @@ -1822,7 +1825,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if 0 // defined(_MSC_VER) && (_MSC_VER >= 1800) // VS2013+ -- Disabled due to __is_nothrow_destructible being broken in VC++ versions up to at least VS2013. A ticket will be submitted for this + #if defined(_MSC_VER) && (_MSC_VER >= 1920) #define EASTL_TYPE_TRAIT_is_nothrow_destructible_CONFORMANCE ((_MSC_VER >= 1900) ? 1 : 0) // VS2013 (1800) doesn't support noexcept and so can't support all usage of this properly (in particular default exception specifications defined in [C++11 Standard, 15.4 paragraph 14]. template <typename T> diff --git a/EASTL/include/EASTL/internal/type_properties.h b/EASTL/include/EASTL/internal/type_properties.h index df90fcb..78bdfca 100644 --- a/EASTL/include/EASTL/internal/type_properties.h +++ b/EASTL/include/EASTL/internal/type_properties.h @@ -104,47 +104,31 @@ namespace eastl /////////////////////////////////////////////////////////////////////// // is_signed // - // is_signed<T>::value == true if and only if T is one of the following types: - // [const] [volatile] char (maybe) - // [const] [volatile] signed char - // [const] [volatile] short - // [const] [volatile] int - // [const] [volatile] long - // [const] [volatile] long long - // [const] [volatile] float - // [const] [volatile] double - // [const] [volatile] long double + // is_signed<T>::value == true if T is a (possibly cv-qualified) floating-point or signed integer type. // - // Used to determine if a integral type is signed or unsigned. + // Used to determine if a type is signed. // Given that there are some user-made classes which emulate integral // types, we provide the EASTL_DECLARE_SIGNED macro to allow you to // set a given class to be identified as a signed type. /////////////////////////////////////////////////////////////////////// #define EASTL_TYPE_TRAIT_is_signed_CONFORMANCE 1 // is_signed is conforming. + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4296) // '<': expression is always false +#endif + template<typename T, bool = is_arithmetic<T>::value> + struct is_signed_helper : bool_constant<T(-1) < T(0)> {}; +#ifdef _MSC_VER + #pragma warning(pop) +#endif - template <typename T> struct is_signed_helper : public false_type{}; - - template <> struct is_signed_helper<signed char> : public true_type{}; - template <> struct is_signed_helper<signed short> : public true_type{}; - template <> struct is_signed_helper<signed int> : public true_type{}; - template <> struct is_signed_helper<signed long> : public true_type{}; - template <> struct is_signed_helper<signed long long> : public true_type{}; - template <> struct is_signed_helper<float> : public true_type{}; - template <> struct is_signed_helper<double> : public true_type{}; - template <> struct is_signed_helper<long double> : public true_type{}; - - #if (CHAR_MAX == SCHAR_MAX) - template <> struct is_signed_helper<char> : public true_type{}; - #endif - #ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type... - #if defined(__WCHAR_MAX__) && ((__WCHAR_MAX__ == 2147483647) || (__WCHAR_MAX__ == 32767)) // GCC defines __WCHAR_MAX__ for most platforms. - template <> struct is_signed_helper<wchar_t> : public true_type{}; - #endif - #endif + template<typename T> + struct is_signed_helper<T, false> : false_type {}; template <typename T> - struct is_signed : public eastl::is_signed_helper<typename eastl::remove_cv<T>::type>{}; + struct is_signed : public eastl::is_signed_helper<T>::type {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template <class T> @@ -164,41 +148,31 @@ namespace eastl /////////////////////////////////////////////////////////////////////// // is_unsigned // - // is_unsigned<T>::value == true if and only if T is one of the following types: - // [const] [volatile] char (maybe) - // [const] [volatile] unsigned char - // [const] [volatile] unsigned short - // [const] [volatile] unsigned int - // [const] [volatile] unsigned long - // [const] [volatile] unsigned long long + // is_unsigned<T>::value == true if T is a (possibly cv-qualified) bool or unsigned integer type. // - // Used to determine if a integral type is signed or unsigned. + // Used to determine if a type is unsigned. // Given that there are some user-made classes which emulate integral // types, we provide the EASTL_DECLARE_UNSIGNED macro to allow you to // set a given class to be identified as an unsigned type. /////////////////////////////////////////////////////////////////////// #define EASTL_TYPE_TRAIT_is_unsigned_CONFORMANCE 1 // is_unsigned is conforming. + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4296) // '<': expression is always false +#endif + template<typename T, bool = is_arithmetic<T>::value> + struct is_unsigned_helper : integral_constant<bool, T(0) < T(-1)> {}; +#ifdef _MSC_VER + #pragma warning(pop) +#endif - template <typename T> struct is_unsigned_helper : public false_type{}; - - template <> struct is_unsigned_helper<unsigned char> : public true_type{}; - template <> struct is_unsigned_helper<unsigned short> : public true_type{}; - template <> struct is_unsigned_helper<unsigned int> : public true_type{}; - template <> struct is_unsigned_helper<unsigned long> : public true_type{}; - template <> struct is_unsigned_helper<unsigned long long> : public true_type{}; - - #if (CHAR_MAX == UCHAR_MAX) - template <> struct is_unsigned_helper<char> : public true_type{}; - #endif - #ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type... - #if defined(_MSC_VER) || (defined(__WCHAR_MAX__) && ((__WCHAR_MAX__ == 4294967295U) || (__WCHAR_MAX__ == 65535))) // GCC defines __WCHAR_MAX__ for most platforms. - template <> struct is_unsigned_helper<wchar_t> : public true_type{}; - #endif - #endif + template<typename T> + struct is_unsigned_helper<T, false> : false_type {}; template <typename T> - struct is_unsigned : public eastl::is_unsigned_helper<typename eastl::remove_cv<T>::type>{}; + struct is_unsigned : public eastl::is_unsigned_helper<T>::type {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template <class T> @@ -213,7 +187,53 @@ namespace eastl template <> struct is_unsigned<const volatile T> : public true_type{}; \ } + /////////////////////////////////////////////////////////////////////// + // is_bounded_array + // + // is_bounded_array<T>::value == true if T is an array type of known bound. + // + // is_bounded_array<int>::value is false. + // is_bounded_array<int[5]>::value is true. + // is_bounded_array<int[]>::value is false. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_bounded_array_CONFORMANCE 1 // is_bounded_array is conforming. + + template<class T> + struct is_bounded_array: eastl::false_type {}; + + template<class T, size_t N> + struct is_bounded_array<T[N]> : eastl::true_type {}; + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template <class T> + EA_CONSTEXPR bool is_bounded_array_v = is_bounded_array<T>::value; + #endif + + /////////////////////////////////////////////////////////////////////// + // is_unbounded_array + // + // is_unbounded_array<T>::value == true if T is an array type of known bound. + // + // is_unbounded_array<int>::value is false. + // is_unbounded_array<int[5]>::value is false. + // is_unbounded_array<int[]>::value is true. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_unbounded_array_CONFORMANCE 1 // is_unbounded_array is conforming. + + template<class T> + struct is_unbounded_array: eastl::false_type {}; + + template<class T> + struct is_unbounded_array<T[]> : eastl::true_type {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template <class T> + EA_CONSTEXPR bool is_unbounded_array_v = is_unbounded_array<T>::value; + #endif /////////////////////////////////////////////////////////////////////// // alignment_of @@ -241,7 +261,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////// // is_aligned - // + // // Defined as true if the type has alignment requirements greater // than default alignment, which is taken to be 8. This allows for // doing specialized object allocation and placement for such types. diff --git a/EASTL/include/EASTL/internal/type_transformations.h b/EASTL/include/EASTL/internal/type_transformations.h index 2d77a55..5454cfa 100644 --- a/EASTL/include/EASTL/internal/type_transformations.h +++ b/EASTL/include/EASTL/internal/type_transformations.h @@ -153,7 +153,7 @@ namespace eastl struct int128_helper { - #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #if EASTL_GCC_STYLE_INT128_SUPPORTED typedef __int128_t type; #endif }; @@ -168,7 +168,7 @@ namespace eastl eastl::conditional_t<sizeof(T) <= sizeof(signed int), int_helper, eastl::conditional_t<sizeof(T) <= sizeof(signed long), long_helper, eastl::conditional_t<sizeof(T) <= sizeof(signed long long), longlong_helper, - #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) && defined(__clang__)) + #if EASTL_GCC_STYLE_INT128_SUPPORTED eastl::conditional_t<sizeof(T) <= sizeof(__int128_t), int128_helper, no_type_helper > @@ -223,7 +223,7 @@ namespace eastl template <> struct make_signed<unsigned long> { typedef signed long type; }; template <> struct make_signed<signed long long> { typedef signed long long type; }; template <> struct make_signed<unsigned long long> { typedef signed long long type; }; - #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #if EASTL_GCC_STYLE_INT128_SUPPORTED template <> struct make_signed<__int128_t> { typedef __int128_t type; }; template <> struct make_signed<__uint128_t> { typedef __int128_t type; }; #endif @@ -318,7 +318,7 @@ namespace eastl struct int128_helper { - #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #if EASTL_GCC_STYLE_INT128_SUPPORTED typedef __uint128_t type; #endif }; @@ -334,7 +334,7 @@ namespace eastl eastl::conditional_t<sizeof(T) <= sizeof(unsigned int), int_helper, eastl::conditional_t<sizeof(T) <= sizeof(unsigned long), long_helper, eastl::conditional_t<sizeof(T) <= sizeof(unsigned long long), longlong_helper, - #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) && defined(__clang__)) + #if EASTL_GCC_STYLE_INT128_SUPPORTED eastl::conditional_t<sizeof(T) <= sizeof(__uint128_t), int128_helper, no_type_helper > @@ -390,7 +390,7 @@ namespace eastl template <> struct make_unsigned<unsigned long> { typedef unsigned long type; }; template <> struct make_unsigned<signed long long> { typedef unsigned long long type; }; template <> struct make_unsigned<unsigned long long> { typedef unsigned long long type; }; - #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #if EASTL_GCC_STYLE_INT128_SUPPORTED template <> struct make_unsigned<__int128_t> { typedef __uint128_t type; }; template <> struct make_unsigned<__uint128_t> { typedef __uint128_t type; }; #endif @@ -477,15 +477,33 @@ namespace eastl // add_pointer // // Add pointer to a type. - // Provides the member typedef type which is the type T*. If T is a - // reference type, then type is a pointer to the referred type. - // + // Provides the member typedef type which is the type T*. + // + // If T is a reference type, + // type member is a pointer to the referred type. + // If T is an object type, a function type that is not cv- or ref-qualified, + // or a (possibly cv-qualified) void type, + // type member is T*. + // Otherwise (T is a cv- or ref-qualified function type), + // type member is T (ie. not a pointer). + // + // cv- and ref-qualified function types are invalid, which is why there is a specific clause for it. + // See https://cplusplus.github.io/LWG/issue2101 for more. + // /////////////////////////////////////////////////////////////////////// #define EASTL_TYPE_TRAIT_add_pointer_CONFORMANCE 1 - template<class T> - struct add_pointer { typedef typename eastl::remove_reference<T>::type* type; }; + namespace internal + { + template <typename T> + auto try_add_pointer(int) -> type_identity<typename std::remove_reference<T>::type*>; + template <typename T> + auto try_add_pointer(...) -> type_identity<T>; + } + + template <typename T> + struct add_pointer : decltype(internal::try_add_pointer<T>(0)) {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template <class T> @@ -747,32 +765,6 @@ namespace eastl return u.destValue; } - - - /////////////////////////////////////////////////////////////////////// - // void_t - // - // Maps a sequence of any types to void. This utility class is used in - // template meta programming to simplify compile time reflection mechanisms - // required by the standard library. - // - // http://en.cppreference.com/w/cpp/types/void_t - // - // Example: - // template <typename T, typename = void> - // struct is_iterable : false_type {}; - // - // template <typename T> - // struct is_iterable<T, void_t<decltype(declval<T>().begin()), - // decltype(declval<T>().end())>> : true_type {}; - // - /////////////////////////////////////////////////////////////////////// - #if EASTL_VARIABLE_TEMPLATES_ENABLED - template <class...> - using void_t = void; - #endif - - } // namespace eastl diff --git a/EASTL/include/EASTL/internal/type_void_t.h b/EASTL/include/EASTL/internal/type_void_t.h new file mode 100644 index 0000000..40c6818 --- /dev/null +++ b/EASTL/include/EASTL/internal/type_void_t.h @@ -0,0 +1,43 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_TYPE_VOID_T_H +#define EASTL_INTERNAL_TYPE_VOID_T_H + + +#include <EABase/eabase.h> +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +namespace eastl +{ + + /////////////////////////////////////////////////////////////////////// + // void_t + // + // Maps a sequence of any types to void. This utility class is used in + // template meta programming to simplify compile time reflection mechanisms + // required by the standard library. + // + // http://en.cppreference.com/w/cpp/types/void_t + // + // Example: + // template <typename T, typename = void> + // struct is_iterable : false_type {}; + // + // template <typename T> + // struct is_iterable<T, void_t<decltype(declval<T>().begin()), + // decltype(declval<T>().end())>> : true_type {}; + // + /////////////////////////////////////////////////////////////////////// + template <class...> + using void_t = void; + + +} // namespace eastl + + +#endif // Header include guard diff --git a/EASTL/include/EASTL/iterator.h b/EASTL/include/EASTL/iterator.h index 6760348..6c268aa 100644 --- a/EASTL/include/EASTL/iterator.h +++ b/EASTL/include/EASTL/iterator.h @@ -9,6 +9,8 @@ #include <EASTL/internal/config.h> #include <EASTL/internal/move_help.h> +#include <EASTL/internal/type_detected.h> +#include <EASTL/internal/type_void_t.h> #include <EASTL/initializer_list.h> EA_DISABLE_ALL_VC_WARNINGS(); @@ -93,16 +95,35 @@ namespace eastl // struct iterator_traits - template <typename Iterator> - struct iterator_traits + namespace internal { - typedef typename Iterator::iterator_category iterator_category; - typedef typename Iterator::value_type value_type; - typedef typename Iterator::difference_type difference_type; - typedef typename Iterator::pointer pointer; - typedef typename Iterator::reference reference; - }; + // Helper to make iterator_traits SFINAE friendly as N3844 requires. + template <typename Iterator, class = void> + struct default_iterator_traits {}; + template <typename Iterator> + struct default_iterator_traits< + Iterator, + void_t< + typename Iterator::iterator_category, + typename Iterator::value_type, + typename Iterator::difference_type, + typename Iterator::pointer, + typename Iterator::reference + > + > + { + typedef typename Iterator::iterator_category iterator_category; + typedef typename Iterator::value_type value_type; + typedef typename Iterator::difference_type difference_type; + typedef typename Iterator::pointer pointer; + typedef typename Iterator::reference reference; + }; + } + + template <typename Iterator> + struct iterator_traits : internal::default_iterator_traits<Iterator> {}; + template <typename T> struct iterator_traits<T*> { @@ -129,37 +150,46 @@ namespace eastl /// is_iterator_wrapper /// /// Tells if an Iterator type is a wrapper type as opposed to a regular type. - /// Relies on the class declaring a typedef called wrapped_iterator_type. + /// Relies on the class declaring a member function called unwrap. /// /// Examples of wrapping iterators: - /// reverse_iterator /// generic_iterator /// move_iterator + /// reverse_iterator<T> (if T is a wrapped iterator) /// Examples of non-wrapping iterators: /// iterator /// list::iterator /// char* /// /// Example behavior: - /// is_iterator_wrapper(int*)::value => false - /// is_iterator_wrapper(eastl::array<char>*)::value => false - /// is_iterator_wrapper(eastl::vector<int>::iterator)::value => false - /// is_iterator_wrapper(eastl::generic_iterator<int*>)::value => true - /// is_iterator_wrapper(eastl::move_iterator<eastl::array<int>::iterator>)::value => true + /// is_iterator_wrapper(int*)::value => false + /// is_iterator_wrapper(eastl::array<char>*)::value => false + /// is_iterator_wrapper(eastl::vector<int>::iterator)::value => false + /// is_iterator_wrapper(eastl::generic_iterator<int*>)::value => true + /// is_iterator_wrapper(eastl::move_iterator<eastl::array<int>::iterator>)::value => true + /// is_iterator_wrapper(eastl::reverse_iterator<int*>)::value => false + /// is_iterator_wrapper(eastl::reverse_iterator<eastl::move_iterator<int*>>)::value => true /// template<typename Iterator> class is_iterator_wrapper { - template<typename> - static eastl::no_type test(...); - - template<typename U> - static eastl::yes_type test(typename U::wrapped_iterator_type*, typename eastl::enable_if<eastl::is_class<U>::value>::type* = 0); - +#if defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_CLANG_CL) + // Using a default template type parameter trick here because + // of a bug in clang that makes the other implementation not + // work when unwrap() is private and this is class is a + // friend. + // See: https://bugs.llvm.org/show_bug.cgi?id=25334 + template<typename T, typename U = decltype(eastl::declval<T>().unwrap())> + using detect_has_unwrap = U; +#else + // Note: the above implementation does not work on GCC when + // unwrap() is private and this class is a friend. So we're + // forced to diverge here to support both GCC and clang. + template<typename T> + using detect_has_unwrap = decltype(eastl::declval<T>().unwrap()); +#endif public: - EA_DISABLE_VC_WARNING(6334) - static const bool value = (sizeof(test<Iterator>(NULL)) == sizeof(eastl::yes_type)); - EA_RESTORE_VC_WARNING() + static const bool value = eastl::is_detected<detect_has_unwrap, Iterator>::value; }; @@ -180,25 +210,28 @@ namespace eastl template <typename Iterator, bool isWrapper> struct is_iterator_wrapper_helper { - typedef Iterator iterator_type; + using iterator_type = Iterator; - static iterator_type get_base(Iterator it) - { return it; } + static iterator_type get_unwrapped(Iterator it) { return it; } }; template <typename Iterator> struct is_iterator_wrapper_helper<Iterator, true> { - typedef typename Iterator::iterator_type iterator_type; + // get_unwrapped must return by value since we're returning + // it.unwrap(), and `it` will be out of scope as soon as + // get_unwrapped returns. + using iterator_type = + typename eastl::remove_cvref<decltype(eastl::declval<Iterator>().unwrap())>::type; - static iterator_type get_base(Iterator it) - { return it.base(); } + static iterator_type get_unwrapped(Iterator it) { return it.unwrap(); } }; + template <typename Iterator> inline typename is_iterator_wrapper_helper<Iterator, eastl::is_iterator_wrapper<Iterator>::value>::iterator_type unwrap_iterator(Iterator it) - { return eastl::is_iterator_wrapper_helper<Iterator, eastl::is_iterator_wrapper<Iterator>::value>::get_base(it); } + { return eastl::is_iterator_wrapper_helper<Iterator, eastl::is_iterator_wrapper<Iterator>::value>::get_unwrapped(it); } @@ -222,9 +255,13 @@ namespace eastl typename eastl::iterator_traits<Iterator>::pointer, typename eastl::iterator_traits<Iterator>::reference> { + private: + using base_wrapped_iterator_type = + typename eastl::is_iterator_wrapper_helper<Iterator, + eastl::is_iterator_wrapper<Iterator>::value>::iterator_type; + public: typedef Iterator iterator_type; - typedef iterator_type wrapped_iterator_type; // This is not in the C++ Standard; it's used by use to identify it as a wrapping iterator type. typedef typename eastl::iterator_traits<Iterator>::pointer pointer; typedef typename eastl::iterator_traits<Iterator>::reference reference; typedef typename eastl::iterator_traits<Iterator>::difference_type difference_type; @@ -304,6 +341,18 @@ namespace eastl // operator[] may return something other than reference. EA_CPP14_CONSTEXPR reference operator[](difference_type n) const { return mIterator[-n - 1]; } + + + private: + // Unwrapping interface, not part of the public API. + template <typename U = iterator_type> + EA_CPP14_CONSTEXPR typename eastl::enable_if<eastl::is_iterator_wrapper<U>::value, reverse_iterator<base_wrapped_iterator_type>>::type unwrap() const + { return reverse_iterator<base_wrapped_iterator_type>(unwrap_iterator(mIterator)); } + + // The unwrapper helpers need access to unwrap() (when it exists). + using this_type = reverse_iterator<Iterator>; + friend is_iterator_wrapper_helper<this_type, is_iterator_wrapper<iterator_type>::value>; + friend is_iterator_wrapper<this_type>; }; @@ -380,21 +429,15 @@ namespace eastl struct is_reverse_iterator< eastl::reverse_iterator<Iterator> > : public eastl::true_type {}; - - - /// unwrap_reverse_iterator - /// - /// Returns Iterator::get_base() if it's a reverse_iterator, else returns Iterator as-is. - /// - /// Example usage: - /// vector<int> intVector; - /// eastl::reverse_iterator<vector<int>::iterator> reverseIterator(intVector.begin()); - /// vector<int>::iterator it = unwrap_reverse_iterator(reverseIterator); - /// - /// Disabled until there is considered a good use for it. - /// template <typename Iterator> - /// inline typename eastl::is_iterator_wrapper_helper<Iterator, eastl::is_reverse_iterator<Iterator>::value>::iterator_type unwrap_reverse_iterator(Iterator it) - /// { return eastl::is_iterator_wrapper_helper<Iterator, eastl::is_reverse_iterator<Iterator>::value>::get_base(it); } + /// unwrap_reverse_iterator is not implemented since there's no + /// good use case and there's some abiguitiy. Note that + /// unwrap_iterator(reverse_iterator<T>) returns + /// reverse_iterator<unwrap(T)>. However, given what + /// unwrap_generic_iterator and unwrap_move_iterator do, one might + /// expect unwrap_reverse_iterator(reverse_iterator<T>) to return + /// T, which is not the same. To avoid that confusion, and because + /// there's no current use case for this, we don't provide + /// unwrap_reverse_iterator. @@ -414,7 +457,6 @@ namespace eastl public: typedef Iterator iterator_type; - typedef iterator_type wrapped_iterator_type; // This is not in the C++ Standard; it's used by use to identify it as a wrapping iterator type. typedef iterator_traits<Iterator> traits_type; typedef typename traits_type::iterator_category iterator_category; typedef typename traits_type::value_type value_type; @@ -496,6 +538,16 @@ namespace eastl reference operator[](difference_type n) const { return eastl::move(mIterator[n]); } + + private: + // Unwrapping interface, not part of the public API. + iterator_type unwrap() const + { return mIterator; } + + // The unwrapper helpers need access to unwrap(). + using this_type = move_iterator<Iterator>; + friend is_iterator_wrapper_helper<this_type, true>; + friend is_iterator_wrapper<this_type>; }; template<typename Iterator1, typename Iterator2> @@ -589,7 +641,7 @@ namespace eastl /// unwrap_move_iterator /// - /// Returns Iterator::get_base() if it's a move_iterator, else returns Iterator as-is. + /// Returns `it.base()` if it's a move_iterator, else returns `it` as-is. /// /// Example usage: /// vector<int> intVector; @@ -598,9 +650,10 @@ namespace eastl /// template <typename Iterator> inline typename eastl::is_iterator_wrapper_helper<Iterator, eastl::is_move_iterator<Iterator>::value>::iterator_type unwrap_move_iterator(Iterator it) - { return eastl::is_iterator_wrapper_helper<Iterator, eastl::is_move_iterator<Iterator>::value>::get_base(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<Iterator, eastl::is_move_iterator<Iterator>::value>::get_unwrapped(it); + } /// back_insert_iterator diff --git a/EASTL/include/EASTL/list.h b/EASTL/include/EASTL/list.h index 5e79437..be99c01 100644 --- a/EASTL/include/EASTL/list.h +++ b/EASTL/include/EASTL/list.h @@ -403,10 +403,10 @@ namespace eastl void clear() EA_NOEXCEPT; void reset_lose_memory() EA_NOEXCEPT; // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. - void remove(const T& x); + size_type remove(const T& x); template <typename Predicate> - void remove_if(Predicate); + size_type remove_if(Predicate); void reverse() EA_NOEXCEPT; @@ -1457,9 +1457,10 @@ namespace eastl template <typename T, typename Allocator> - void list<T, Allocator>::remove(const value_type& value) + typename list<T, Allocator>::size_type list<T, Allocator>::remove(const value_type& value) { iterator current((ListNodeBase*)internalNode().mpNext); + size_type numRemoved = 0; while(current.mpNode != &internalNode()) { @@ -1469,23 +1470,30 @@ namespace eastl { ++current; DoErase((ListNodeBase*)current.mpNode->mpPrev); + ++numRemoved; } } + return numRemoved; } template <typename T, typename Allocator> template <typename Predicate> - inline void list<T, Allocator>::remove_if(Predicate predicate) + inline typename list<T, Allocator>::size_type list<T, Allocator>::remove_if(Predicate predicate) { + size_type numRemoved = 0; for(iterator first((ListNodeBase*)internalNode().mpNext), last((ListNodeBase*)&internalNode()); first != last; ) { iterator temp(first); ++temp; if(predicate(first.mpNode->mValue)) + { DoErase((ListNodeBase*)first.mpNode); + ++numRemoved; + } first = temp; } + return numRemoved; } @@ -2100,6 +2108,13 @@ namespace eastl #endif } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename Allocator> + inline synth_three_way_result<T> operator<=>(const list<T, Allocator>& a, const list<T, Allocator>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } +#else template <typename T, typename Allocator> bool operator<(const list<T, Allocator>& a, const list<T, Allocator>& b) { @@ -2129,7 +2144,7 @@ namespace eastl { return !(a < b); } - +#endif template <typename T, typename Allocator> void swap(list<T, Allocator>& a, list<T, Allocator>& b) { @@ -2143,17 +2158,17 @@ namespace eastl // https://en.cppreference.com/w/cpp/container/list/erase2 /////////////////////////////////////////////////////////////////////// template <class T, class Allocator, class U> - void erase(list<T, Allocator>& c, const U& value) + typename list<T, Allocator>::size_type erase(list<T, Allocator>& c, const U& value) { // Erases all elements that compare equal to value from the container. - c.remove_if([&](auto& elem) { return elem == value; }); + return c.remove(value); } template <class T, class Allocator, class Predicate> - void erase_if(list<T, Allocator>& c, Predicate predicate) + typename list<T, Allocator>::size_type erase_if(list<T, Allocator>& c, Predicate predicate) { // Erases all elements that satisfy the predicate pred from the container. - c.remove_if(predicate); + return c.remove_if(predicate); } diff --git a/EASTL/include/EASTL/map.h b/EASTL/include/EASTL/map.h index 0e6c1d0..7824250 100644 --- a/EASTL/include/EASTL/map.h +++ b/EASTL/include/EASTL/map.h @@ -156,6 +156,17 @@ namespace eastl T& at(const Key& key); const T& at(const Key& key) const; + template <class... Args> eastl::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); + template <class... Args> eastl::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); + template <class... Args> iterator try_emplace(const_iterator position, const key_type& k, Args&&... args); + template <class... Args> iterator try_emplace(const_iterator position, key_type&& k, Args&&... args); + + private: + template <class KFwd, class... Args> + eastl::pair<iterator, bool> try_emplace_forward(KFwd&& k, Args&&... args); + + template <class KFwd, class... Args> + iterator try_emplace_forward(const_iterator hint, KFwd&& key, Args&&... args); }; // map @@ -268,7 +279,6 @@ namespace eastl private: // these base member functions are not included in multimaps - using base_type::try_emplace; using base_type::insert_or_assign; }; // multimap @@ -439,31 +449,28 @@ namespace eastl //return it->second; } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename Key, typename T, typename Compare, typename Allocator> + inline synth_three_way_result<eastl::pair<const Key, T>> operator<=>(const map<Key, T, Compare, Allocator>& a, + const map<Key, T, Compare, Allocator>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } +#endif template <typename Key, typename T, typename Compare, typename Allocator> inline T& map<Key, T, Compare, Allocator>::at(const Key& key) { - iterator itLower(lower_bound(key)); // itLower->first is >= key. - - if(itLower == end()) - { - #if EASTL_EXCEPTIONS_ENABLED - throw std::out_of_range("map::at key does not exist"); - #else - EASTL_FAIL_MSG("map::at key does not exist"); - #endif - } - - return (*itLower).second; + // use the use const version of ::at to remove duplication + return const_cast<T&>(const_cast<map<Key, T, Compare, Allocator> const*>(this)->at(key)); } - template <typename Key, typename T, typename Compare, typename Allocator> inline const T& map<Key, T, Compare, Allocator>::at(const Key& key) const { - const_iterator itLower(lower_bound(key)); // itLower->first is >= key. + const_iterator candidate = this->find(key); - if(itLower == end()) + if (candidate == end()) { #if EASTL_EXCEPTIONS_ENABLED throw std::out_of_range("map::at key does not exist"); @@ -472,7 +479,7 @@ namespace eastl #endif } - return (*itLower).second; + return candidate->second; } @@ -482,8 +489,9 @@ namespace eastl // https://en.cppreference.com/w/cpp/container/map/erase_if /////////////////////////////////////////////////////////////////////// template <class Key, class T, class Compare, class Allocator, class Predicate> - void erase_if(map<Key, T, Compare, Allocator>& c, Predicate predicate) + typename map<Key, T, Compare, Allocator>::size_type erase_if(map<Key, T, Compare, Allocator>& c, Predicate predicate) { + auto oldSize = c.size(); for (auto i = c.begin(), last = c.end(); i != last;) { if (predicate(*i)) @@ -495,8 +503,84 @@ namespace eastl ++i; } } + return oldSize - c.size(); + } + + + template <class Key, class T, class Compare, class Allocator> + template <class... Args> + inline eastl::pair<typename map<Key, T, Compare, Allocator>::iterator, bool> + map<Key, T, Compare, Allocator>::try_emplace(const key_type& key, Args&&... args) + { + return try_emplace_forward(key, eastl::forward<Args>(args)...); + } + + template <class Key, class T, class Compare, class Allocator> + template <class... Args> + inline eastl::pair<typename map<Key, T, Compare, Allocator>::iterator, bool> + map<Key, T, Compare, Allocator>::try_emplace(key_type&& key, Args&&... args) + { + return try_emplace_forward(eastl::move(key), eastl::forward<Args>(args)...); + } + + template <class Key, class T, class Compare, class Allocator> + template <class KFwd, class... Args> + inline eastl::pair<typename map<Key, T, Compare, Allocator>::iterator, bool> + map<Key, T, Compare, Allocator>::try_emplace_forward(KFwd&& key, Args&&... args) + { + bool canInsert; + node_type* const pPosition = base_type::DoGetKeyInsertionPositionUniqueKeys(canInsert, key); + if (!canInsert) + { + return pair<iterator, bool>(iterator(pPosition), false); + } + node_type* const pNodeNew = + base_type::DoCreateNode(piecewise_construct, eastl::forward_as_tuple(eastl::forward<KFwd>(key)), + eastl::forward_as_tuple(eastl::forward<Args>(args)...)); + // the key might be moved above, so we can't re-use it, + // we need to get it back from the node's value. + const auto& k = extract_key{}(pNodeNew->mValue); + const iterator itResult(base_type::DoInsertValueImpl(pPosition, false, k, pNodeNew)); + return pair<iterator, bool>(itResult, true); + } + + template <class Key, class T, class Compare, class Allocator> + template <class... Args> + inline typename map<Key, T, Compare, Allocator>::iterator + map<Key, T, Compare, Allocator>::try_emplace(const_iterator hint, const key_type& key, Args&&... args) + { + return try_emplace_forward(hint, key, eastl::forward<Args>(args)...); } + template <class Key, class T, class Compare, class Allocator> + template <class... Args> + inline typename map<Key, T, Compare, Allocator>::iterator + map<Key, T, Compare, Allocator>::try_emplace(const_iterator hint, key_type&& key, Args&&... args) + { + return try_emplace_forward(hint, eastl::move(key), eastl::forward<Args>(args)...); + } + + template <class Key, class T, class Compare, class Allocator> + template <class KFwd, class... Args> + inline typename map<Key, T, Compare, Allocator>::iterator + map<Key, T, Compare, Allocator>::try_emplace_forward(const_iterator hint, KFwd&& key, Args&&... args) + { + bool bForceToLeft; + node_type* const pPosition = base_type::DoGetKeyInsertionPositionUniqueKeysHint(hint, bForceToLeft, key); + + if (!pPosition) + { + // the hint didn't help, we need to do a normal insert. + return try_emplace_forward(eastl::forward<KFwd>(key), eastl::forward<Args>(args)...).first; + } + + node_type* const pNodeNew = + base_type::DoCreateNode(piecewise_construct, eastl::forward_as_tuple(eastl::forward<KFwd>(key)), + eastl::forward_as_tuple(eastl::forward<Args>(args)...)); + // the key might be moved above, so we can't re-use it, + // we need to get it back from the node's value. + return base_type::DoInsertValueImpl(pPosition, bForceToLeft, extract_key{}(pNodeNew->mValue), pNodeNew); + } /////////////////////////////////////////////////////////////////////// // multimap @@ -658,8 +742,9 @@ namespace eastl // https://en.cppreference.com/w/cpp/container/multimap/erase_if /////////////////////////////////////////////////////////////////////// template <class Key, class T, class Compare, class Allocator, class Predicate> - void erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate predicate) + typename multimap<Key, T, Compare, Allocator>::size_type erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate predicate) { + auto oldSize = c.size(); // Erases all elements that satisfy the predicate pred from the container. for (auto i = c.begin(), last = c.end(); i != last;) { @@ -672,7 +757,26 @@ namespace eastl ++i; } } + return oldSize - c.size(); + } + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename Key, typename T, typename Compare, typename Allocator> + inline synth_three_way_result<eastl::pair<const Key, T>> operator<=>(const multimap<Key, T, Compare, Allocator>& a, + const multimap<Key, T, Compare, Allocator>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); } +#endif + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename Key, typename T, typename Compare, typename Allocator> + inline synth_three_way_result<eastl::pair<const Key, T>> operator<=>(const multimap<Key, T, Compare, Allocator>& a, + const multimap<Key, T, Compare, Allocator>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } +#endif } // namespace eastl diff --git a/EASTL/include/EASTL/memory.h b/EASTL/include/EASTL/memory.h index 1993b8e..ab2798f 100644 --- a/EASTL/include/EASTL/memory.h +++ b/EASTL/include/EASTL/memory.h @@ -1682,6 +1682,41 @@ namespace eastl { return eastl::addressof(r); } // 20.6.3.2: if element_type is (possibly cv-qualified) void, the type of r is unspecified; otherwise, it is T&. }; + /////////////////////////////////////////////////////////////////////// + // to_address + // + // Helper that call the customization point in pointer_traits<T>::to_address for retrieving the address of a pointer. + // This is useful if you are using fancy-pointers. + /////////////////////////////////////////////////////////////////////// + + namespace Internal + { + template <class T> + using detect_pointer_traits_to_address = decltype(eastl::pointer_traits<T>::to_address(eastl::declval<const T&>())); + + template <class T> + using result_detect_pointer_traits_to_address = eastl::is_detected<detect_pointer_traits_to_address, T>; + } + + template<class T> + EA_CPP14_CONSTEXPR T* to_address(T* p) noexcept + { + static_assert(!eastl::is_function<T>::value, "Cannot call to_address with a function pointer. C++20 20.2.4.1 - Pointer conversion."); + return p; + } + + template <class Ptr, typename eastl::enable_if<Internal::result_detect_pointer_traits_to_address<Ptr>::value, int>::type = 0> + EA_CPP14_CONSTEXPR auto to_address(const Ptr& ptr) noexcept -> decltype(eastl::pointer_traits<Ptr>::to_address(ptr)) + { + return eastl::pointer_traits<Ptr>::to_address(ptr); + } + + template <class Ptr, typename eastl::enable_if<!Internal::result_detect_pointer_traits_to_address<Ptr>::value, int>::type = 0> + EA_CPP14_CONSTEXPR auto to_address(const Ptr& ptr) noexcept -> decltype(to_address(ptr.operator->())) + { + return to_address(ptr.operator->()); + } + } // namespace eastl diff --git a/EASTL/include/EASTL/numeric.h b/EASTL/include/EASTL/numeric.h index 4b83c94..200be6c 100644 --- a/EASTL/include/EASTL/numeric.h +++ b/EASTL/include/EASTL/numeric.h @@ -233,6 +233,103 @@ namespace eastl } + #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 <typename T> + constexpr eastl::enable_if_t<eastl::is_arithmetic_v<T> && !eastl::is_same_v<eastl::remove_cv_t<T>, bool>, T> midpoint(const T lhs, const T rhs) EA_NOEXCEPT + { + // If T is an integral type... + if constexpr(eastl::is_integral_v<T>) + { + using U = eastl::make_unsigned_t<T>; + + int sign = 1; + U m = lhs; + U M = rhs; + + if (lhs > rhs) + { + sign = -1; + m = rhs; + M = lhs; + } + + return lhs + static_cast<T>(sign * static_cast<T>((U(M - m)) / 2 )); + } + + // otherwise if T is a floating point + else + { + const T LO = eastl::numeric_limits<T>::min() * 2; + const T HI = eastl::numeric_limits<T>::max() / 2; + + const T lhs_abs = (lhs < 0) ? -lhs : lhs; + const T rhs_abs = (rhs < 0) ? -rhs : rhs; + + if (lhs_abs <= HI && rhs_abs <= HI) + 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); + } + } + + + /// midpoint + /// + /// Computes the midpoint address between pointers LHS and RHS. + /// The midpoint address closer to the LHS is chosen. + /// + template <typename T> + constexpr eastl::enable_if_t<eastl::is_object_v<T>, T*> midpoint(T* lhs, T* rhs) + { + return lhs + ((rhs - lhs) / 2); + } + + + template <class T> + constexpr T shared_lerp(const T a, const T b, const T t) EA_NOEXCEPT + { + if ((a <= 0 && b >= 0) || (a >= 0 && b <= 0)) + { + return t * b + (1 - t) * a; + } + + if (t == 1) + { + return b; + } + + const T X = a + t * (b - a); + + if ((t > 1) == (b > a)) + { + return (b > X) ? b : X; + } + return (b < X) ? b : X; + } + + /// lerp + /// + /// Calculates the linear interpolation of two points A and B expressed A + T * (B - A) + /// where T is some value in range [0, 1]. If T is outside this range, the linear extrapolation will be computed. + /// + /// https://en.cppreference.com/w/cpp/numeric/lerp + /// + /// C++ proposal paper: + /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0811r3.html + /// + constexpr float lerp(float a, float b, float t) EA_NOEXCEPT { return shared_lerp(a, b, t); } + 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 diff --git a/EASTL/include/EASTL/numeric_limits.h b/EASTL/include/EASTL/numeric_limits.h index e991e7e..0d7dc97 100644 --- a/EASTL/include/EASTL/numeric_limits.h +++ b/EASTL/include/EASTL/numeric_limits.h @@ -614,6 +614,65 @@ namespace eastl }; + #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE + template<> + struct numeric_limits<char8_t> + { + 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() + { return EASTL_LIMITS_MIN(value_type); } + + static EA_CONSTEXPR value_type max() + { return EASTL_LIMITS_MAX(value_type); } + + static EA_CONSTEXPR value_type lowest() + { return EASTL_LIMITS_MIN(value_type); } + + static EA_CONSTEXPR value_type epsilon() + { return 0; } + + static EA_CONSTEXPR value_type round_error() + { return 0; } + + static EA_CONSTEXPR value_type infinity() + { return 0; } + + static EA_CONSTEXPR value_type quiet_NaN() + { return 0; } + + static EA_CONSTEXPR value_type signaling_NaN() + { return 0; } + + static EA_CONSTEXPR value_type denorm_min() + { return (value_type)0; } + }; + #endif + #if EA_CHAR16_NATIVE // If char16_t is a true unique type (as called for by the C++11 Standard)... // numeric_limits<char16_t> @@ -1435,6 +1494,19 @@ namespace eastl static 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 value_type quiet_NaN() + { return __builtin_nanf("0"); } + + static value_type signaling_NaN() + { return __builtin_nansf("1"); } + + static value_type denorm_min() + { return FLT_TRUE_MIN; } + #else static value_type infinity() { return _CSTD _FInf._Float; } @@ -1446,6 +1518,7 @@ namespace eastl static value_type denorm_min() { return _CSTD _FDenorm._Float; } + #endif #endif }; @@ -1553,6 +1626,19 @@ namespace eastl static 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 value_type quiet_NaN() + { return __builtin_nan("0"); } + + static value_type signaling_NaN() + { return __builtin_nans("1"); } + + static value_type denorm_min() + { return DBL_TRUE_MIN; } + #else static value_type infinity() { return _CSTD _Inf._Double; } @@ -1564,6 +1650,7 @@ namespace eastl static value_type denorm_min() { return _CSTD _Denorm._Double; } + #endif #endif }; @@ -1671,6 +1758,19 @@ namespace eastl static 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 value_type quiet_NaN() + { return __builtin_nan("0"); } + + static value_type signaling_NaN() + { return __builtin_nans("1"); } + + static value_type denorm_min() + { return LDBL_TRUE_MIN; } + #else static value_type infinity() { return _CSTD _LInf._Long_double; } @@ -1682,6 +1782,7 @@ namespace eastl static value_type denorm_min() { return _CSTD _LDenorm._Long_double; } + #endif #endif }; diff --git a/EASTL/include/EASTL/optional.h b/EASTL/include/EASTL/optional.h index 763bfd8..15cacd0 100644 --- a/EASTL/include/EASTL/optional.h +++ b/EASTL/include/EASTL/optional.h @@ -552,6 +552,17 @@ namespace eastl inline EA_CONSTEXPR bool operator>=(const optional<T>& lhs, const optional<T>& rhs) { return !(lhs < rhs); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <class T, class U=T> requires std::three_way_comparable_with<T, U> + inline EA_CONSTEXPR std::compare_three_way_result_t<T, U> operator<=>(const optional<T>& lhs, const optional<U>& rhs) + { + if (lhs && rhs) + { + return *lhs <=> *rhs; + } + return lhs.has_value() <=> rhs.has_value(); + } +#endif /////////////////////////////////////////////////////////////////////////////// // Compare an optional object with a nullopt @@ -559,7 +570,11 @@ namespace eastl template <class T> inline EA_CONSTEXPR bool operator==(const optional<T>& opt, eastl::nullopt_t) EA_NOEXCEPT { return !opt; } - +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <class T> + inline EA_CONSTEXPR std::strong_ordering operator<=>(const optional<T>& opt, eastl::nullopt_t) EA_NOEXCEPT + { return opt.has_value() <=> false; } +#else template <class T> inline EA_CONSTEXPR bool operator==(eastl::nullopt_t, const optional<T>& opt) EA_NOEXCEPT { return !opt; } @@ -603,7 +618,7 @@ namespace eastl template <class T> inline EA_CONSTEXPR bool operator>=(eastl::nullopt_t, const optional<T>& opt) EA_NOEXCEPT { return !opt; } - +#endif /////////////////////////////////////////////////////////////////////////////// // Compare an optional object with a T @@ -656,6 +671,11 @@ namespace eastl inline EA_CONSTEXPR bool operator>=(const T& value, const optional<T>& opt) { return !(value < opt); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <class T, class U=T> requires std::three_way_comparable_with<T, U> + inline EA_CONSTEXPR std::compare_three_way_result_t<T, U> operator<=>(const optional<T>& opt, const U& value) + { return (opt.has_value()) ? *opt <=> value : std::strong_ordering::less; } +#endif /////////////////////////////////////////////////////////////////////////////// /// hash diff --git a/EASTL/include/EASTL/queue.h b/EASTL/include/EASTL/queue.h index 9e06e20..8b29555 100644 --- a/EASTL/include/EASTL/queue.h +++ b/EASTL/include/EASTL/queue.h @@ -308,6 +308,14 @@ namespace eastl { return (a.c == b.c); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename Container> requires std::three_way_comparable<Container> + + inline synth_three_way_result<T> operator<=>(const queue<T, Container>& a, const queue<T, Container>& b) + { + return a.c <=> b.c; + } +#endif template <typename T, typename Container> inline bool operator!=(const queue<T, Container>& a, const queue<T, Container>& b) @@ -339,7 +347,6 @@ namespace eastl return !(a.c < b.c); } - template <typename T, typename Container> inline void swap(queue<T, Container>& a, queue<T, Container>& b) EA_NOEXCEPT_IF((eastl::is_nothrow_swappable<typename queue<T, Container>::container_type>::value)) // EDG has a bug and won't let us use Container in this noexcept statement { diff --git a/EASTL/include/EASTL/set.h b/EASTL/include/EASTL/set.h index a66a885..8256162 100644 --- a/EASTL/include/EASTL/set.h +++ b/EASTL/include/EASTL/set.h @@ -401,8 +401,9 @@ namespace eastl // https://en.cppreference.com/w/cpp/container/set/erase_if /////////////////////////////////////////////////////////////////////// template <class Key, class Compare, class Allocator, class Predicate> - void erase_if(set<Key, Compare, Allocator>& c, Predicate predicate) + typename set<Key, Compare, Allocator>::size_type erase_if(set<Key, Compare, Allocator>& c, Predicate predicate) { + auto oldSize = c.size(); for (auto i = c.begin(), last = c.end(); i != last;) { if (predicate(*i)) @@ -414,8 +415,17 @@ namespace eastl ++i; } } + return oldSize - c.size(); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <class Key, class Compare, class Allocator> + synth_three_way_result<Key> operator<=>(const set<Key, Compare, Allocator>& a, const set<Key, Compare, Allocator>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } +#endif + /////////////////////////////////////////////////////////////////////// // multiset @@ -611,8 +621,9 @@ namespace eastl // https://en.cppreference.com/w/cpp/container/multiset/erase_if /////////////////////////////////////////////////////////////////////// template <class Key, class Compare, class Allocator, class Predicate> - void erase_if(multiset<Key, Compare, Allocator>& c, Predicate predicate) + typename multiset<Key, Compare, Allocator>::size_type erase_if(multiset<Key, Compare, Allocator>& c, Predicate predicate) { + auto oldSize = c.size(); // Erases all elements that satisfy the predicate pred from the container. for (auto i = c.begin(), last = c.end(); i != last;) { @@ -625,8 +636,17 @@ namespace eastl ++i; } } + return oldSize - c.size(); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <class Key, class Compare, class Allocator> + synth_three_way_result<Key> operator<=>(const multiset<Key, Compare, Allocator>& a, const multiset<Key, Compare, Allocator>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } +#endif + } // namespace eastl diff --git a/EASTL/include/EASTL/shared_ptr.h b/EASTL/include/EASTL/shared_ptr.h index 5535adf..e7eb778 100644 --- a/EASTL/include/EASTL/shared_ptr.h +++ b/EASTL/include/EASTL/shared_ptr.h @@ -48,6 +48,7 @@ #include <EASTL/unique_ptr.h> #include <EASTL/functional.h> #include <EASTL/allocator.h> +#include <EASTL/atomic.h> #if EASTL_RTTI_ENABLED #include <typeinfo> #endif @@ -117,8 +118,8 @@ namespace eastl /// This is a small utility class used by shared_ptr and weak_ptr. struct ref_count_sp { - int32_t mRefCount; /// Reference count on the contained pointer. Starts as 1 by default. - int32_t mWeakRefCount; /// Reference count on contained pointer plus this ref_count_sp object itself. Starts as 1 by default. + atomic<int32_t> mRefCount; /// Reference count on the contained pointer. Starts as 1 by default. + atomic<int32_t> mWeakRefCount; /// Reference count on contained pointer plus this ref_count_sp object itself. Starts as 1 by default. public: ref_count_sp(int32_t refCount = 1, int32_t weakRefCount = 1) EA_NOEXCEPT; @@ -147,44 +148,49 @@ namespace eastl inline int32_t ref_count_sp::use_count() const EA_NOEXCEPT { - return mRefCount; // To figure out: is this right? + return mRefCount.load(memory_order_relaxed); // To figure out: is this right? } inline void ref_count_sp::addref() EA_NOEXCEPT { - Internal::atomic_increment(&mRefCount); - Internal::atomic_increment(&mWeakRefCount); + mRefCount.fetch_add(1, memory_order_relaxed); + mWeakRefCount.fetch_add(1, memory_order_relaxed); } inline void ref_count_sp::release() { - EASTL_ASSERT((mRefCount > 0) && (mWeakRefCount > 0)); - if(Internal::atomic_decrement(&mRefCount) == 0) + EASTL_ASSERT((mRefCount.load(memory_order_relaxed) > 0)); + if(mRefCount.fetch_sub(1, memory_order_release) == 1) + { + atomic_thread_fence(memory_order_acquire); free_value(); + } - if(Internal::atomic_decrement(&mWeakRefCount) == 0) - free_ref_count_sp(); + weak_release(); } inline void ref_count_sp::weak_addref() EA_NOEXCEPT { - Internal::atomic_increment(&mWeakRefCount); + mWeakRefCount.fetch_add(1, memory_order_relaxed); } inline void ref_count_sp::weak_release() { - EASTL_ASSERT(mWeakRefCount > 0); - if(Internal::atomic_decrement(&mWeakRefCount) == 0) + EASTL_ASSERT(mWeakRefCount.load(memory_order_relaxed) > 0); + if(mWeakRefCount.fetch_sub(1, memory_order_release) == 1) + { + atomic_thread_fence(memory_order_acquire); free_ref_count_sp(); + } } inline ref_count_sp* ref_count_sp::lock() EA_NOEXCEPT { - for(int32_t refCountTemp = mRefCount; refCountTemp != 0; refCountTemp = mRefCount) + for(int32_t refCountTemp = mRefCount.load(memory_order_relaxed); refCountTemp != 0; ) { - if(Internal::atomic_compare_and_swap(&mRefCount, refCountTemp + 1, refCountTemp)) + if(mRefCount.compare_exchange_weak(refCountTemp, refCountTemp + 1, memory_order_relaxed)) { - Internal::atomic_increment(&mWeakRefCount); + mWeakRefCount.fetch_add(1, memory_order_relaxed); return this; } } @@ -810,14 +816,14 @@ namespace eastl /// Returns: the number of shared_ptr objects, *this included, that share ownership with *this, or 0 when *this is empty. int use_count() const EA_NOEXCEPT { - return mpRefCount ? mpRefCount->mRefCount : 0; + return mpRefCount ? mpRefCount->use_count() : 0; } /// unique /// Returns: use_count() == 1. bool unique() const EA_NOEXCEPT { - return (mpRefCount && (mpRefCount->mRefCount == 1)); + return (mpRefCount && (mpRefCount->use_count() == 1)); } @@ -970,6 +976,13 @@ namespace eastl return (a.get() == b.get()); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename U> + std::strong_ordering operator<=>(const shared_ptr<T>& a, const shared_ptr<U>& b) EA_NOEXCEPT + { + return a.get() <=> b.get(); + } +#else template <typename T, typename U> inline bool operator!=(const shared_ptr<T>& a, const shared_ptr<U>& b) EA_NOEXCEPT { @@ -1006,6 +1019,7 @@ namespace eastl { return !(a < b); } +#endif template <typename T> inline bool operator==(const shared_ptr<T>& a, std::nullptr_t) EA_NOEXCEPT @@ -1013,6 +1027,13 @@ namespace eastl return !a; } + #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T> + inline std::strong_ordering operator<=>(const shared_ptr<T>& a, std::nullptr_t) EA_NOEXCEPT + { + return a.get() <=> nullptr; + } + #else template <typename T> inline bool operator==(std::nullptr_t, const shared_ptr<T>& b) EA_NOEXCEPT { @@ -1078,7 +1099,7 @@ namespace eastl { return !(nullptr < b); } - +#endif @@ -1508,13 +1529,13 @@ namespace eastl // Returns: 0 if *this is empty ; otherwise, the number of shared_ptr instances that share ownership with *this. int use_count() const EA_NOEXCEPT { - return mpRefCount ? mpRefCount->mRefCount : 0; + return mpRefCount ? mpRefCount->use_count() : 0; } // Returns: use_count() == 0 bool expired() const EA_NOEXCEPT { - return (!mpRefCount || (mpRefCount->mRefCount == 0)); + return (!mpRefCount || (mpRefCount->use_count() == 0)); } void reset() diff --git a/EASTL/include/EASTL/slist.h b/EASTL/include/EASTL/slist.h index 1dbb44f..dc3c447 100644 --- a/EASTL/include/EASTL/slist.h +++ b/EASTL/include/EASTL/slist.h @@ -353,10 +353,10 @@ namespace eastl void clear() EA_NOEXCEPT; void reset_lose_memory() EA_NOEXCEPT; // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. - void remove(const value_type& value); + size_type remove(const value_type& value); template <typename Predicate> - void remove_if(Predicate predicate); + size_type remove_if(Predicate predicate); void reverse() EA_NOEXCEPT; @@ -1249,32 +1249,42 @@ namespace eastl template <typename T, typename Allocator> - void slist<T, Allocator>::remove(const value_type& value) + typename slist<T, Allocator>::size_type slist<T, Allocator>::remove(const value_type& value) { base_node_type* pNode = &internalNode(); + size_type numErased = 0; while(pNode && pNode->mpNext) { - if(static_cast<node_type*>(pNode->mpNext)->mValue == value) + if (static_cast<node_type*>(pNode->mpNext)->mValue == value) + { DoEraseAfter((SListNodeBase*)pNode); // This will take care of modifying pNode->mpNext. + ++numErased; + } else pNode = pNode->mpNext; } + return numErased; } template <typename T, typename Allocator> template <typename Predicate> - void slist<T, Allocator>::remove_if(Predicate predicate) + inline typename slist<T, Allocator>::size_type slist<T, Allocator>::remove_if(Predicate predicate) { base_node_type* pNode = &internalNode(); + size_type numErased = 0; while(pNode && pNode->mpNext) { - if(predicate(static_cast<node_type*>(pNode->mpNext)->mValue)) + if (predicate(static_cast<node_type*>(pNode->mpNext)->mValue)) + { DoEraseAfter((SListNodeBase*)pNode); // This will take care of modifying pNode->mpNext. + ++numErased; + } else pNode = pNode->mpNext; } + return numErased; } @@ -1811,7 +1821,13 @@ namespace eastl #endif } - +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename Allocator> + inline synth_three_way_result<T> operator<=>(const slist<T, Allocator>& a, const slist<T, Allocator>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } +#else template <typename T, typename Allocator> inline bool operator<(const slist<T, Allocator>& a, const slist<T, Allocator>& b) { @@ -1845,7 +1861,7 @@ namespace eastl { return !(a < b); } - +#endif template <typename T, typename Allocator> inline void swap(slist<T, Allocator>& a, slist<T, Allocator>& b) @@ -1858,17 +1874,17 @@ namespace eastl /// /// https://en.cppreference.com/w/cpp/container/forward_list/erase2 template <class T, class Allocator, class U> - void erase(slist<T, Allocator>& c, const U& value) + typename slist<T, Allocator>::size_type erase(slist<T, Allocator>& c, const U& value) { // Erases all elements that compare equal to value from the container. - c.remove_if([&](auto& elem) { return elem == value; }); + return c.remove(value); } template <class T, class Allocator, class Predicate> - void erase_if(slist<T, Allocator>& c, Predicate predicate) + typename slist<T, Allocator>::size_type erase_if(slist<T, Allocator>& c, Predicate predicate) { // Erases all elements that satisfy the predicate pred from the container. - c.remove_if(predicate); + return c.remove_if(predicate); } diff --git a/EASTL/include/EASTL/sort.h b/EASTL/include/EASTL/sort.h index 60cfcd8..fb1c6e5 100644 --- a/EASTL/include/EASTL/sort.h +++ b/EASTL/include/EASTL/sort.h @@ -715,18 +715,20 @@ namespace eastl template <typename RandomAccessIterator, typename T> inline RandomAccessIterator get_partition_impl(RandomAccessIterator first, RandomAccessIterator last, T&& pivotValue) { + using PureT = decay_t<T>; + for(; ; ++first) { - while(*first < pivotValue) + while(eastl::less<PureT>()(*first, pivotValue)) { - EASTL_VALIDATE_COMPARE(!(pivotValue < *first)); // Validate that the compare function is sane. + EASTL_VALIDATE_COMPARE(!eastl::less<PureT>()(pivotValue, *first)); // Validate that the compare function is sane. ++first; } --last; - while(pivotValue < *last) + while(eastl::less<PureT>()(pivotValue, *last)) { - EASTL_VALIDATE_COMPARE(!(*last < pivotValue)); // Validate that the compare function is sane. + EASTL_VALIDATE_COMPARE(!eastl::less<PureT>()(*last, pivotValue)); // Validate that the compare function is sane. --last; } @@ -813,9 +815,9 @@ namespace eastl RandomAccessIterator end(current), prev(current); value_type value(eastl::forward<value_type>(*current)); - for(--prev; value < *prev; --end, --prev) // We skip checking for (prev >= first) because quick_sort (our caller) makes this unnecessary. + for(--prev; eastl::less<value_type>()(value, *prev); --end, --prev) // We skip checking for (prev >= first) because quick_sort (our caller) makes this unnecessary. { - EASTL_VALIDATE_COMPARE(!(*prev < value)); // Validate that the compare function is sane. + EASTL_VALIDATE_COMPARE(!eastl::less<value_type>()(*prev, value)); // Validate that the compare function is sane. *end = eastl::forward<value_type>(*prev); } @@ -860,9 +862,9 @@ namespace eastl for(RandomAccessIterator i = middle; i < last; ++i) { - if(*i < *first) + if(eastl::less<value_type>()(*i, *first)) { - EASTL_VALIDATE_COMPARE(!(*first < *i)); // Validate that the compare function is sane. + EASTL_VALIDATE_COMPARE(!eastl::less<value_type>()(*first, *i)); // Validate that the compare function is sane. value_type temp(eastl::forward<value_type>(*i)); *i = eastl::forward<value_type>(*first); eastl::adjust_heap<RandomAccessIterator, difference_type, value_type> @@ -1712,6 +1714,7 @@ namespace eastl bucketPosition[i + 1] = bucketPosition[i] + bucketSize[i]; bucketSize[i] = 0; // Clear the bucket for the next pass } + bucketSize[numBuckets - 1] = 0; uint32_t jNext = j + DigitBits; for (temp = srcFirst; temp != last; ++temp) diff --git a/EASTL/include/EASTL/stack.h b/EASTL/include/EASTL/stack.h index 3edd5f5..f060b60 100644 --- a/EASTL/include/EASTL/stack.h +++ b/EASTL/include/EASTL/stack.h @@ -284,6 +284,13 @@ namespace eastl return (a.c == b.c); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename Container> requires std::three_way_comparable<Container> + inline synth_three_way_result<T> operator<=>(const stack<T, Container>& a, const stack<T, Container>& b) + { + return a.c <=> b.c; + } +#endif template <typename T, typename Container> inline bool operator!=(const stack<T, Container>& a, const stack<T, Container>& b) @@ -319,7 +326,6 @@ namespace eastl return !(a.c < b.c); } - template <typename T, typename Container> inline void swap(stack<T, Container>& a, stack<T, Container>& b) EA_NOEXCEPT_IF((eastl::is_nothrow_swappable<typename stack<T, Container>::container_type>::value)) { diff --git a/EASTL/include/EASTL/string.h b/EASTL/include/EASTL/string.h index 2352a61..3a70b79 100644 --- a/EASTL/include/EASTL/string.h +++ b/EASTL/include/EASTL/string.h @@ -294,7 +294,7 @@ namespace eastl typedef ptrdiff_t difference_type; typedef Allocator allocator_type; - static const size_type npos = (size_type)-1; /// 'npos' means non-valid position or simply non-position. + static const EA_CONSTEXPR size_type npos = (size_type)-1; /// 'npos' means non-valid position or simply non-position. public: // CtorDoNotInitialize exists so that we can create a constructor that allocates but doesn't @@ -1798,7 +1798,9 @@ namespace eastl } if (nReturnValue >= 0) + { internalLayout().SetSize(nInitialSize + nReturnValue); + } #if EASTL_VA_COPY_ENABLED // va_end for arguments will be called by the caller. @@ -1854,7 +1856,9 @@ namespace eastl } if(nReturnValue >= 0) + { internalLayout().SetSize(nInitialSize + nReturnValue); + } #if EASTL_VA_COPY_ENABLED // va_end for arguments will be called by the caller. @@ -3764,7 +3768,7 @@ namespace eastl return ((a.size() == b.size()) && (memcmp(a.data(), b.data(), (size_t)a.size() * sizeof(typename basic_string<T, Allocator>::value_type)) == 0)); } - +#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template <typename T, typename Allocator> inline bool operator==(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) { @@ -3772,7 +3776,7 @@ namespace eastl const size_type n = (size_type)CharStrlen(p); return ((n == b.size()) && (memcmp(p, b.data(), (size_t)n * sizeof(*p)) == 0)); } - +#endif template <typename T, typename Allocator> inline bool operator==(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) @@ -3781,6 +3785,52 @@ namespace eastl const size_type n = (size_type)CharStrlen(p); return ((a.size() == n) && (memcmp(a.data(), p, (size_t)n * sizeof(*p)) == 0)); } + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename Allocator> + inline auto operator<=>(const basic_string<T, Allocator>& a, const basic_string<T, Allocator>& b) + { + return basic_string<T, Allocator>::compare(a.begin(), a.end(), b.begin(), b.end()) <=> 0; + } + + template <typename T, typename Allocator> + inline auto operator<=>(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) + { + typedef typename basic_string<T, Allocator>::size_type size_type; + const size_type n = (size_type)CharStrlen(p); + return basic_string<T, Allocator>::compare(a.begin(), a.end(), p, p + n) <=> 0; + } + + template <typename T, typename Allocator> + inline auto operator<=>(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::view_type v) + { + typedef typename basic_string<T, Allocator>::view_type view_type; + return static_cast<view_type>(a) <=> v; + } + +#else + + template <typename T, typename Allocator> + inline bool operator==(const typename basic_string<T, Allocator>::view_type v, const basic_string<T, Allocator>& b) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + typedef typename basic_string<T, Allocator>::view_type view_type; + return v == static_cast<view_type>(b); + } + + template <typename T, typename Allocator> + inline bool operator==(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::view_type v) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + typedef typename basic_string<T, Allocator>::view_type view_type; + return static_cast<view_type>(a) == v; + } template <typename T, typename Allocator> @@ -3789,7 +3839,6 @@ namespace eastl return !(a == b); } - template <typename T, typename Allocator> inline bool operator!=(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) { @@ -3802,6 +3851,28 @@ namespace eastl { return !(a == p); } + + + template <typename T, typename Allocator> + inline bool operator!=(const typename basic_string<T, Allocator>::view_type v, const basic_string<T, Allocator>& b) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return !(v == b); + } + + + template <typename T, typename Allocator> + inline bool operator!=(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::view_type v) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return !(a == v); + } // Operator< (and also >, <=, and >=). @@ -3827,6 +3898,30 @@ namespace eastl const size_type n = (size_type)CharStrlen(p); return basic_string<T, Allocator>::compare(a.begin(), a.end(), p, p + n) < 0; } + + + template <typename T, typename Allocator> + inline bool operator<(const typename basic_string<T, Allocator>::view_type v, const basic_string<T, Allocator>& b) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + typedef typename basic_string<T, Allocator>::view_type view_type; + return v < static_cast<view_type>(b); + } + + + template <typename T, typename Allocator> + inline bool operator<(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::view_type v) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + typedef typename basic_string<T, Allocator>::view_type view_type; + return static_cast<view_type>(a) < v; + } template <typename T, typename Allocator> @@ -3848,6 +3943,28 @@ namespace eastl { return p < a; } + + + template <typename T, typename Allocator> + inline bool operator>(const typename basic_string<T, Allocator>::view_type v, const basic_string<T, Allocator>& b) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return b < v; + } + + + template <typename T, typename Allocator> + inline bool operator>(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::view_type v) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return v < a; + } template <typename T, typename Allocator> @@ -3869,6 +3986,28 @@ namespace eastl { return !(p < a); } + + + template <typename T, typename Allocator> + inline bool operator<=(const typename basic_string<T, Allocator>::view_type v, const basic_string<T, Allocator>& b) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return !(b < v); + } + + + template <typename T, typename Allocator> + inline bool operator<=(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::view_type v) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return !(v < a); + } template <typename T, typename Allocator> @@ -3890,7 +4029,29 @@ namespace eastl { return !(a < p); } + + + template <typename T, typename Allocator> + inline bool operator>=(const typename basic_string<T, Allocator>::view_type v, const basic_string<T, Allocator>& b) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return !(v < b); + } + + + template <typename T, typename Allocator> + inline bool operator>=(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::view_type v) + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + return !(a < v); + } +#endif template <typename T, typename Allocator> inline void swap(basic_string<T, Allocator>& a, basic_string<T, Allocator>& b) @@ -4060,7 +4221,12 @@ namespace eastl /// http://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s /// #if EASTL_USER_LITERALS_ENABLED && EASTL_INLINE_NAMESPACES_ENABLED - EA_DISABLE_VC_WARNING(4455) // disable warning C4455: literal suffix identifiers that do not start with an underscore are reserved + // Disabling the Clang/GCC/MSVC warning about using user + // defined literals without a leading '_' as they are reserved + // for standard libary usage. + EA_DISABLE_VC_WARNING(4455) + EA_DISABLE_CLANG_WARNING(-Wuser-defined-literals) + EA_DISABLE_GCC_WARNING(-Wliteral-suffix) inline namespace literals { inline namespace string_literals @@ -4076,7 +4242,9 @@ namespace eastl #endif } } - EA_RESTORE_VC_WARNING() // warning: 4455 + EA_RESTORE_GCC_WARNING() // -Wliteral-suffix + EA_RESTORE_CLANG_WARNING() // -Wuser-defined-literals + EA_RESTORE_VC_WARNING() // warning: 4455 #endif @@ -4084,17 +4252,40 @@ namespace eastl /// /// https://en.cppreference.com/w/cpp/string/basic_string/erase2 template <class CharT, class Allocator, class U> - void erase(basic_string<CharT, Allocator>& c, const U& value) + typename basic_string<CharT, Allocator>::size_type erase(basic_string<CharT, Allocator>& c, const U& value) { // Erases all elements that compare equal to value from the container. - c.erase(eastl::remove(c.begin(), c.end(), value), c.end()); + auto origEnd = c.end(); + auto newEnd = eastl::remove(c.begin(), origEnd, value); + auto numRemoved = eastl::distance(newEnd, origEnd); + c.erase(newEnd, 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 + // string should not have more characters than fit in a + // uint32_t and so the distance here should fit in a + // size_type. + return static_cast<typename basic_string<CharT, Allocator>::size_type>(numRemoved); } template <class CharT, class Allocator, class Predicate> - void erase_if(basic_string<CharT, Allocator>& c, Predicate predicate) + typename basic_string<CharT, Allocator>::size_type erase_if(basic_string<CharT, Allocator>& c, Predicate predicate) { // Erases all elements that satisfy the predicate pred from the container. - c.erase(eastl::remove_if(c.begin(), c.end(), predicate), c.end()); + auto origEnd = c.end(); + auto newEnd = eastl::remove_if(c.begin(), origEnd, predicate); + auto numRemoved = eastl::distance(newEnd, origEnd); + c.erase(newEnd, 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 + // string should not have more characters than fit in a + // uint32_t and so the distance here should fit in a + // size_type. + return static_cast<typename basic_string<CharT, Allocator>::size_type>(numRemoved); } } // namespace eastl diff --git a/EASTL/include/EASTL/string_view.h b/EASTL/include/EASTL/string_view.h index 54452a3..f600e50 100644 --- a/EASTL/include/EASTL/string_view.h +++ b/EASTL/include/EASTL/string_view.h @@ -451,64 +451,268 @@ namespace eastl // global operators + // Disabling symmetric comparisons that require conversions, since they are causing an internal compiler error + // when compiled using MSVC when certain flags are enabled (/Zi /O2 /Zc:inline) + // template <class CharT> + // inline EA_CONSTEXPR bool operator==(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return (lhs.size() == rhs.size()) && (lhs.compare(rhs) == 0); + // } + // + // // type_identity_t is used in this context to forcefully trigger conversion operators towards basic_string_view. + // // Mostly we want basic_string::operator basic_string_view() to kick-in to be able to compare strings and string_views. + // template <class CharT> + // inline EA_CONSTEXPR bool operator==(type_identity_t<basic_string_view<CharT>> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return (lhs.size() == rhs.size()) && (lhs.compare(rhs) == 0); + // } + template <class CharT> - inline EA_CONSTEXPR bool operator==(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR bool operator==(basic_string_view<CharT> lhs, type_identity_t<basic_string_view<CharT>> rhs) EA_NOEXCEPT { return (lhs.size() == rhs.size()) && (lhs.compare(rhs) == 0); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) template <class CharT> - inline EA_CONSTEXPR bool operator==(decay_t<basic_string_view<CharT>> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR auto operator<=>(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT { - return (lhs.size() == rhs.size()) && (lhs.compare(rhs) == 0); + return static_cast<std::weak_ordering>(lhs.compare(rhs) <=> 0); } template <class CharT> - inline EA_CONSTEXPR bool operator==(basic_string_view<CharT> lhs, decay_t<basic_string_view<CharT>> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR auto operator<=>(basic_string_view<CharT> lhs, typename basic_string_view<CharT>::const_pointer rhs) EA_NOEXCEPT { - return (lhs.size() == rhs.size()) && (lhs.compare(rhs) == 0); + typedef basic_string_view<CharT> view_type; + return static_cast<std::weak_ordering>(lhs <=> static_cast<view_type>(rhs)); } +#else template <class CharT> - inline EA_CONSTEXPR bool operator==(decay_t<basic_string_view<CharT>> lhs, decay_t<basic_string_view<CharT>> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR bool operator==(typename basic_string_view<CharT>::const_pointer lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT { - return (lhs.size() == rhs.size()) && (lhs.compare(rhs) == 0); + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + typedef basic_string_view<CharT> view_type; + return static_cast<view_type>(lhs) == rhs; + } + + template <class CharT> + inline EA_CONSTEXPR bool operator==(basic_string_view<CharT> lhs, typename basic_string_view<CharT>::const_pointer rhs) EA_NOEXCEPT + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + typedef basic_string_view<CharT> view_type; + return lhs == static_cast<view_type>(rhs); } + // Disabling symmetric comparisons that require conversions, since they are causing an internal compiler error + // when compiled using MSVC when certain flags are enabled (/Zi /O2 /Zc:inline) + // template <class CharT> + // inline EA_CONSTEXPR bool operator!=(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return !(lhs == rhs); + // } + // + // template <class CharT> + // inline EA_CONSTEXPR bool operator!=(type_identity_t<basic_string_view<CharT>> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return !(lhs == rhs); + // } + template <class CharT> + inline EA_CONSTEXPR bool operator!=(basic_string_view<CharT> lhs, type_identity_t<basic_string_view<CharT>> rhs) EA_NOEXCEPT + { + return !(lhs == rhs); + } + template <class CharT> + inline EA_CONSTEXPR bool operator!=(typename basic_string_view<CharT>::const_pointer lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return !(lhs == rhs); + } template <class CharT> - inline EA_CONSTEXPR bool operator!=(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR bool operator!=(basic_string_view<CharT> lhs, typename basic_string_view<CharT>::const_pointer rhs) EA_NOEXCEPT { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + return !(lhs == rhs); } + // Disabling symmetric comparisons that require conversions, since they are causing an internal compiler error + // when compiled using MSVC when certain flags are enabled (/Zi /O2 /Zc:inline) + // template <class CharT> + // inline EA_CONSTEXPR bool operator<(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return lhs.compare(rhs) < 0; + // } + // + // template <class CharT> + // inline EA_CONSTEXPR bool operator<(type_identity_t<basic_string_view<CharT>> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return lhs.compare(rhs) < 0; + // } + template <class CharT> - inline EA_CONSTEXPR bool operator<(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR bool operator<(basic_string_view<CharT> lhs, type_identity_t<basic_string_view<CharT>> rhs) EA_NOEXCEPT { return lhs.compare(rhs) < 0; } template <class CharT> - inline EA_CONSTEXPR bool operator<=(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR bool operator<(typename basic_string_view<CharT>::const_pointer lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + typedef basic_string_view<CharT> view_type; + return static_cast<view_type>(lhs) < rhs; + } + + template <class CharT> + inline EA_CONSTEXPR bool operator<(basic_string_view<CharT> lhs, typename basic_string_view<CharT>::const_pointer rhs) EA_NOEXCEPT + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + typedef basic_string_view<CharT> view_type; + return lhs < static_cast<view_type>(rhs); + } + + // Disabling symmetric comparisons that require conversions, since they are causing an internal compiler error + // when compiled using MSVC when certain flags are enabled (/Zi /O2 /Zc:inline) + // template <class CharT> + // inline EA_CONSTEXPR bool operator<=(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return !(rhs < lhs); + // } + // + // template <class CharT> + // inline EA_CONSTEXPR bool operator<=(type_identity_t<basic_string_view<CharT>> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return !(rhs < lhs); + // } + + template <class CharT> + inline EA_CONSTEXPR bool operator<=(basic_string_view<CharT> lhs, type_identity_t<basic_string_view<CharT>> rhs) EA_NOEXCEPT + { + return !(rhs < lhs); + } + + template <class CharT> + inline EA_CONSTEXPR bool operator<=(typename basic_string_view<CharT>::const_pointer lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + return !(rhs < lhs); } template <class CharT> - inline EA_CONSTEXPR bool operator>(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR bool operator<=(basic_string_view<CharT> lhs, typename basic_string_view<CharT>::const_pointer rhs) EA_NOEXCEPT + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return !(rhs < lhs); + } + + // Disabling symmetric comparisons that require conversions, since they are causing an internal compiler error + // when compiled using MSVC when certain flags are enabled (/Zi /O2 /Zc:inline) + // template <class CharT> + // inline EA_CONSTEXPR bool operator>(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return rhs < lhs; + // } + // + // template <class CharT> + // inline EA_CONSTEXPR bool operator>(type_identity_t<basic_string_view<CharT>> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return rhs < lhs; + // } + + template <class CharT> + inline EA_CONSTEXPR bool operator>(basic_string_view<CharT> lhs, type_identity_t<basic_string_view<CharT>> rhs) EA_NOEXCEPT + { + return rhs < lhs; + } + + template <class CharT> + inline EA_CONSTEXPR bool operator>(typename basic_string_view<CharT>::const_pointer lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return rhs < lhs; + } + + template <class CharT> + inline EA_CONSTEXPR bool operator>(basic_string_view<CharT> lhs, typename basic_string_view<CharT>::const_pointer rhs) EA_NOEXCEPT { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + return rhs < lhs; } + // Disabling symmetric comparisons that require conversions, since they are causing an internal compiler error + // when compiled using MSVC when certain flags are enabled (/Zi /O2 /Zc:inline) + // template <class CharT> + // inline EA_CONSTEXPR bool operator>=(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return !(lhs < rhs); + // } + // + // template <class CharT> + // inline EA_CONSTEXPR bool operator>=(type_identity_t<basic_string_view<CharT>> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + // { + // return !(lhs < rhs); + // } + + template <class CharT> + inline EA_CONSTEXPR bool operator>=(basic_string_view<CharT> lhs, type_identity_t<basic_string_view<CharT>> rhs) EA_NOEXCEPT + { + return !(lhs < rhs); + } + template <class CharT> - inline EA_CONSTEXPR bool operator>=(basic_string_view<CharT> lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT + inline EA_CONSTEXPR bool operator>=(typename basic_string_view<CharT>::const_pointer lhs, basic_string_view<CharT> rhs) EA_NOEXCEPT { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + return !(lhs < rhs); } - // string_view / wstring_view + template <class CharT> + inline EA_CONSTEXPR bool operator>=(basic_string_view<CharT> lhs, typename basic_string_view<CharT>::const_pointer rhs) EA_NOEXCEPT + { + // Workaround for basic_string_view comparisons that require conversions, + // since they are causing an internal compiler error when compiled using + // MSVC when certain flags are enabled (/Zi /O2 /Zc:inline). + + return !(lhs < rhs); + } +#endif + // string_view / wstring_view typedef basic_string_view<char> string_view; typedef basic_string_view<wchar_t> wstring_view; @@ -599,7 +803,13 @@ namespace eastl #if EASTL_USER_LITERALS_ENABLED && EASTL_INLINE_NAMESPACES_ENABLED - EA_DISABLE_VC_WARNING(4455) // disable warning C4455: literal suffix identifiers that do not start with an underscore are reserved + // Disabling the Clang/GCC/MSVC warning about using user + // defined literals without a leading '_' as they are reserved + // for standard libary usage. + EA_DISABLE_VC_WARNING(4455) + EA_DISABLE_CLANG_WARNING(-Wuser-defined-literals) + EA_DISABLE_GCC_WARNING(-Wliteral-suffix) + inline namespace literals { inline namespace string_view_literals @@ -609,11 +819,16 @@ namespace eastl EA_CONSTEXPR inline u32string_view operator "" sv(const char32_t* str, size_t len) EA_NOEXCEPT { return {str, len}; } EA_CONSTEXPR inline wstring_view operator "" sv(const wchar_t* str, size_t len) EA_NOEXCEPT { return {str, len}; } + // We've seen _sv trigger the following warning on clang: + // identifier '_sv' is reserved because it starts with '_' at global scope [-Wreserved-identifier] + // Temporarily disable the warning until we figure out why it thinks _sv is "at global scope". + EA_DISABLE_CLANG_WARNING(-Wreserved-identifier) // Backwards compatibility. EA_CONSTEXPR inline string_view operator "" _sv(const char* str, size_t len) EA_NOEXCEPT { return {str, len}; } EA_CONSTEXPR inline u16string_view operator "" _sv(const char16_t* str, size_t len) EA_NOEXCEPT { return {str, len}; } EA_CONSTEXPR inline u32string_view operator "" _sv(const char32_t* str, size_t len) EA_NOEXCEPT { return {str, len}; } EA_CONSTEXPR inline wstring_view operator "" _sv(const wchar_t* str, size_t len) EA_NOEXCEPT { return {str, len}; } + EA_RESTORE_CLANG_WARNING() // -Wreserved-identifier // C++20 char8_t support. #if EA_CHAR8_UNIQUE @@ -622,7 +837,10 @@ namespace eastl #endif } } - EA_RESTORE_VC_WARNING() // warning: 4455 + + EA_RESTORE_GCC_WARNING() // -Wliteral-suffix + EA_RESTORE_CLANG_WARNING() // -Wuser-defined-literals + EA_RESTORE_VC_WARNING() // warning: 4455 #endif } // namespace eastl diff --git a/EASTL/include/EASTL/tuple.h b/EASTL/include/EASTL/tuple.h index cec5115..12460c6 100644 --- a/EASTL/include/EASTL/tuple.h +++ b/EASTL/include/EASTL/tuple.h @@ -6,6 +6,7 @@ #define EASTL_TUPLE_H #include <EASTL/internal/config.h> +#include <EASTL/compare.h> #include <EASTL/functional.h> #include <EASTL/type_traits.h> #include <EASTL/utility.h> @@ -199,7 +200,7 @@ namespace Internal // We shouldn't need this explicit constructor as it should be handled by the template below but OSX clang // is_constructible type trait incorrectly gives false for is_constructible<T&&, T&&>::value - explicit TupleLeaf(ValueType&& v) : mValue(eastl::move(v)) {} + explicit TupleLeaf(ValueType&& v) : mValue(eastl::forward<ValueType>(v)) {} template <typename T, typename = typename enable_if<is_constructible<ValueType, T&&>::value>::type> explicit TupleLeaf(T&& t) @@ -233,50 +234,6 @@ namespace Internal ValueType mValue; }; - // TupleLeaf: Specialize for when ValueType is a reference - template <size_t I, typename ValueType, bool IsEmpty> - class TupleLeaf<I, ValueType&, IsEmpty> - { - public: - TupleLeaf(const TupleLeaf&) = default; - TupleLeaf& operator=(const TupleLeaf&) = delete; - - template <typename T, typename = typename enable_if<is_constructible<ValueType, T&&>::value>::type> - explicit TupleLeaf(T&& t) - : mValue(eastl::forward<T>(t)) - { - } - - explicit TupleLeaf(ValueType& t) : mValue(t) - { - } - - template <typename T> - explicit TupleLeaf(const TupleLeaf<I, T>& t) - : mValue(t.getInternal()) - { - } - - template <typename T> - TupleLeaf& operator=(T&& t) - { - mValue = eastl::forward<T>(t); - return *this; - } - - int swap(TupleLeaf& t) - { - eastl::Internal::swap(*this, t); - return 0; - } - - ValueType& getInternal() { return mValue; } - const ValueType& getInternal() const { return mValue; } - - private: - ValueType& mValue; - }; - // TupleLeaf: partial specialization for when we can use the Empty Base Class Optimization template <size_t I, typename ValueType> class TupleLeaf<I, ValueType, true> : private ValueType @@ -609,7 +566,6 @@ namespace Internal } }; - // TupleLess // // @@ -718,6 +674,16 @@ namespace Internal return TC2::DoCat2(eastl::forward<TupleArg1>(t1), eastl::forward<TupleArg2>(t2)); } }; + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename... T1s, typename... T2s, size_t... Is> + constexpr auto TupleThreeWay(const tuple<T1s...>& t1, const tuple<T2s...>& t2, index_sequence<Is...> is) + { + std::common_comparison_category_t<synth_three_way_result<T1s, T2s>...> result = std::strong_ordering::equal; + ((result = synth_three_way{}(get<Is>(t1), get<Is>(t2)), result != 0) || ...); + return result; + } +#endif } // namespace Internal @@ -868,6 +834,13 @@ inline bool operator==(const tuple<T1s...>& t1, const tuple<T2s...>& t2) return Internal::TupleEqual<sizeof...(T1s)>()(t1, t2); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) +template <typename... T1s, typename... T2s> +inline constexpr std::common_comparison_category_t<synth_three_way_result<T1s, T2s>...> operator<=>(const tuple<T1s...>& t1, const tuple<T2s...>& t2) +{ + return Internal::TupleThreeWay(t1, t2, make_index_sequence<sizeof...(T1s)>{}); +} +#else template <typename... T1s, typename... T2s> inline bool operator<(const tuple<T1s...>& t1, const tuple<T2s...>& t2) { @@ -878,7 +851,7 @@ template <typename... T1s, typename... T2s> inline bool operator!=(const tuple<T template <typename... T1s, typename... T2s> inline bool operator> (const tuple<T1s...>& t1, const tuple<T2s...>& t2) { return t2 < t1; } template <typename... T1s, typename... T2s> inline bool operator<=(const tuple<T1s...>& t1, const tuple<T2s...>& t2) { return !(t2 < t1); } template <typename... T1s, typename... T2s> inline bool operator>=(const tuple<T1s...>& t1, const tuple<T2s...>& t2) { return !(t1 < t2); } - +#endif // tuple_cat // diff --git a/EASTL/include/EASTL/type_traits.h b/EASTL/include/EASTL/type_traits.h index 2cf5d7a..73d2216 100644 --- a/EASTL/include/EASTL/type_traits.h +++ b/EASTL/include/EASTL/type_traits.h @@ -93,6 +93,8 @@ // is_abstract T is an abstract class. // is_signed T is a signed integral type. // is_unsigned T is an unsigned integral type. +// is_bounded_array T is a type is an array type of known bound +// is_unbounded_array T is a type is an array type of unknown bound // // is_constructible // is_trivially_constructible @@ -131,6 +133,7 @@ // remove_cv // remove_const The member typedef type shall be the same as T except that any top level const-qualifier has been removed. remove_const<const volatile int>::type evaluates to volatile int, whereas remove_const<const int*> is const int*. // remove_volatile +// remove_cvref // add_cv // add_const // add_volatile @@ -177,6 +180,12 @@ // is_nothrow_swappable " // is_reference_wrapper Found in <EASTL/functional.h> // remove_reference_wrapper " +// is_detected Checks if some supplied arguments (Args) respect a constraint (Op). +// detected_t Check which type we obtain after expanding some arguments (Args) over a constraint (Op). +// detected_or Checks if some supplied arguments (Args) respect a constraint (Op) and allow to overwrite return type. +// detected_or_t Equivalent to detected_or<Default, Op, Args...>::type. +// is_detected_exact Check that the type we obtain after expanding some arguments (Args) over a constraint (Op) is equivalent to Expected. +// is_detected_convertible Check that the type we obtain after expanding some arguments (Args) over a constraint (Op) is convertible to Expected. // // Deprecated pre-C++11 type traits // has_trivial_constructor The default constructor for T is trivial. @@ -621,12 +630,8 @@ namespace eastl #define EASTL_TYPE_TRAIT_is_const_CONFORMANCE 1 // is_const is conforming. - template <typename T> struct is_const_value : public eastl::false_type{}; - template <typename T> struct is_const_value<const T*> : public eastl::true_type{}; - template <typename T> struct is_const_value<const volatile T*> : public eastl::true_type{}; - - template <typename T> struct is_const : public eastl::is_const_value<T*>{}; - template <typename T> struct is_const<T&> : public eastl::false_type{}; // Note here that T is const, not the reference to T. So is_const is false. See section 8.3.2p1 of the C++ standard. + template <typename T> struct is_const : public eastl::false_type {}; + template <typename T> struct is_const<const T> : public eastl::true_type {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template <class T> @@ -643,12 +648,8 @@ namespace eastl #define EASTL_TYPE_TRAIT_is_volatile_CONFORMANCE 1 // is_volatile is conforming. - template <typename T> struct is_volatile_value : public eastl::false_type{}; - template <typename T> struct is_volatile_value<volatile T*> : public eastl::true_type{}; - template <typename T> struct is_volatile_value<const volatile T*> : public eastl::true_type{}; - - template <typename T> struct is_volatile : public eastl::is_volatile_value<T*>{}; - template <typename T> struct is_volatile<T&> : public eastl::false_type{}; // Note here that T is volatile, not the reference to T. So is_const is false. See section 8.3.2p1 of the C++ standard. + template <typename T> struct is_volatile : public eastl::false_type {}; + template <typename T> struct is_volatile<volatile T> : public eastl::true_type {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template <class T> @@ -686,39 +687,21 @@ namespace eastl #define EASTL_TYPE_TRAIT_is_function_CONFORMANCE 1 // is_function is conforming. - template <typename> + // afaik, original credit is to Walter Brown who described this implementation at CppCon 2019. + // libc++, libstdc++ and MS STL all use similar implementations. + // This relies on the fact that only function and reference types can't be const qualified. + // Rather than listing an obscene number of specializations for const, volatile, l- and r-value reference, + // noexcept and all relevant combinations we take advantage of this fact. +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored +#endif + template <typename T> struct is_function - : public eastl::false_type {}; - - #if EA_PLATFORM_PTR_SIZE == 4 && defined(EA_PLATFORM_MICROSOFT) && defined(_MSC_EXTENSIONS) - // __cdecl specialization - template <typename ReturnValue, typename... ArgPack> - struct is_function<ReturnValue __cdecl (ArgPack...)> - : public eastl::true_type {}; - - template <typename ReturnValue, typename... ArgPack> - struct is_function<ReturnValue __cdecl (ArgPack..., ...)> // The second ellipsis handles the case of a function that takes ellipsis, like printf. - : public eastl::true_type {}; - - // __stdcall specialization - template <typename ReturnValue, typename... ArgPack> - struct is_function<ReturnValue __stdcall (ArgPack...)> - : public eastl::true_type {}; - - // When functions use a variable number of arguments, it is the caller that cleans the stack (cf. cdecl). - // - // template <typename ReturnValue, typename... ArgPack> - // struct is_function<ReturnValue __stdcall (ArgPack..., ...)> // The second ellipsis handles the case of a function that takes ellipsis, like printf. - // : public eastl::true_type {}; - #else - template <typename ReturnValue, typename... ArgPack> - struct is_function<ReturnValue (ArgPack...)> - : public eastl::true_type {}; - - template <typename ReturnValue, typename... ArgPack> - struct is_function<ReturnValue (ArgPack..., ...)> // The second ellipsis handles the case of a function that takes ellipsis, like printf. - : public eastl::true_type {}; - #endif + : public eastl::bool_constant<!eastl::is_reference<T>::value && !eastl::is_const<const T>::value>::type {}; +#ifdef _MSC_VER + #pragma warning(pop) +#endif #if EASTL_VARIABLE_TEMPLATES_ENABLED template<typename T> @@ -900,12 +883,16 @@ namespace eastl #define EASTL_TYPE_TRAIT_add_lvalue_reference_CONFORMANCE 1 // add_lvalue_reference is conforming. - template <typename T> struct add_lvalue_reference { typedef T& type; }; // If T is an && type then T&& & will be equivalent to T&. - template <typename T> struct add_lvalue_reference<T&> { typedef T& type; }; // This shouldn't be required for modern compilers, as they recognize that a reference to a reference is still a reference. - template <> struct add_lvalue_reference<void> { typedef void type; }; - template <> struct add_lvalue_reference<const void> { typedef const void type; }; - template <> struct add_lvalue_reference<volatile void> { typedef volatile void type; }; - template <> struct add_lvalue_reference<const volatile void> { typedef const volatile void type; }; + namespace internal + { + template <typename T> + auto try_add_lvalue_reference(int)->type_identity<T&>; + + template <typename T> + auto try_add_lvalue_reference(...)->type_identity<T>; + } + + template <typename T> struct add_lvalue_reference : decltype(internal::try_add_lvalue_reference<T>(0)) {}; #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) // To do: define macro. @@ -935,12 +922,16 @@ namespace eastl #define EASTL_TYPE_TRAIT_add_rvalue_reference_CONFORMANCE 1 - template <typename T> struct add_rvalue_reference { typedef T&& type; }; // Dinkumware has this as { typedef typename eastl::remove_reference<T>::type&& type; }, but that doesn't seem right to me. - template <typename T> struct add_rvalue_reference<T&> { typedef T& type; }; // The Standard section 20.7.9.2 specifies that we do this, though it seems like the compiler ought to not require this, as C++11 stipulates that & + && -> &. - template <> struct add_rvalue_reference<void> { typedef void type; }; - template <> struct add_rvalue_reference<const void> { typedef const void type; }; - template <> struct add_rvalue_reference<volatile void> { typedef volatile void type; }; - template <> struct add_rvalue_reference<const volatile void> { typedef const volatile void type; }; + namespace internal + { + template <typename T> + auto try_add_rvalue_reference(int)->type_identity<T&&>; + + template <typename T> + auto try_add_rvalue_reference(...)->type_identity<T>; + } + + template <typename T> struct add_rvalue_reference : decltype(internal::try_add_rvalue_reference<T>(0)) {}; #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) // To do: define macro. @@ -1040,9 +1031,11 @@ namespace eastl // The following files implement the type traits themselves. #include <EASTL/internal/type_fundamental.h> #include <EASTL/internal/type_transformations.h> +#include <EASTL/internal/type_void_t.h> #include <EASTL/internal/type_properties.h> #include <EASTL/internal/type_compound.h> #include <EASTL/internal/type_pod.h> +#include <EASTL/internal/type_detected.h> #endif // Header include guard diff --git a/EASTL/include/EASTL/unique_ptr.h b/EASTL/include/EASTL/unique_ptr.h index c5d2480..195cc42 100644 --- a/EASTL/include/EASTL/unique_ptr.h +++ b/EASTL/include/EASTL/unique_ptr.h @@ -536,27 +536,12 @@ namespace eastl /// /// auto pArray = make_unique<Test[]>(4); /// - namespace Internal - { - template <typename T> - struct unique_type - { typedef unique_ptr<T> unique_type_single; }; - - template <typename T> - struct unique_type<T[]> - { typedef unique_ptr<T[]> unique_type_unbounded_array; }; - - template <typename T, size_t N> - struct unique_type<T[N]> - { typedef void unique_type_bounded_array; }; - } - template <typename T, typename... Args> - inline typename Internal::unique_type<T>::unique_type_single make_unique(Args&&... args) + inline typename eastl::enable_if<!eastl::is_array<T>::value, eastl::unique_ptr<T>>::type make_unique(Args&&... args) { return unique_ptr<T>(new T(eastl::forward<Args>(args)...)); } template <typename T> - inline typename Internal::unique_type<T>::unique_type_unbounded_array make_unique(size_t n) + inline typename eastl::enable_if<eastl::is_unbounded_array<T>::value, eastl::unique_ptr<T>>::type make_unique(size_t n) { typedef typename eastl::remove_extent<T>::type TBase; return unique_ptr<T>(new TBase[n]); @@ -564,7 +549,7 @@ namespace eastl // It's not possible to create a unique_ptr for arrays of a known bound (e.g. int[4] as opposed to int[]). template <typename T, typename... Args> - typename Internal::unique_type<T>::unique_type_bounded_array + typename eastl::enable_if<eastl::is_bounded_array<T>::value>::type make_unique(Args&&...) = delete; @@ -596,12 +581,20 @@ namespace eastl { return (a.get() == b.get()); } - + #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T1, typename D1, typename T2, typename D2> + requires std::three_way_comparable_with<typename unique_ptr<T1, D1>::pointer, typename unique_ptr<T2, D2>::pointer> + inline std::compare_three_way_result_t<typename unique_ptr<T1, D1>::pointer, typename unique_ptr<T2, D2>::pointer> operator<=>(const unique_ptr<T1, D1>& a, const unique_ptr<T2, D2>& b) + { + return a.get() <=> b.get(); + } + #else template <typename T1, typename D1, typename T2, typename D2> inline bool operator!=(const unique_ptr<T1, D1>& a, const unique_ptr<T2, D2>& b) { return !(a.get() == b.get()); } + #endif /// Returns which unique_ptr is 'less' than the other. Useful when storing /// sorted containers of unique_ptr objects. @@ -646,6 +639,14 @@ namespace eastl return !a; } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename D> + requires std::three_way_comparable_with<typename unique_ptr<T, D>::pointer, std::nullptr_t> + inline std::compare_three_way_result_t<typename unique_ptr<T, D>::pointer, std::nullptr_t> operator<=>(const unique_ptr<T, D>& a, std::nullptr_t) + { + return a.get() <=> nullptr; + } +#else template <typename T, typename D> inline bool operator==(std::nullptr_t, const unique_ptr<T, D>& a) EA_NOEXCEPT { @@ -663,6 +664,7 @@ namespace eastl { return static_cast<bool>(a); } +#endif template <typename T, typename D> inline bool operator<(const unique_ptr<T, D>& a, std::nullptr_t) diff --git a/EASTL/include/EASTL/utility.h b/EASTL/include/EASTL/utility.h index a91ce8c..1e6b922 100644 --- a/EASTL/include/EASTL/utility.h +++ b/EASTL/include/EASTL/utility.h @@ -10,6 +10,8 @@ #include <EASTL/internal/config.h> #include <EASTL/type_traits.h> #include <EASTL/iterator.h> +#include <EASTL/numeric_limits.h> +#include <EASTL/compare.h> #include <EASTL/internal/functional_base.h> #include <EASTL/internal/move_help.h> #include <EABase/eahave.h> @@ -352,6 +354,90 @@ namespace eastl } + #if defined(EA_COMPILER_CPP20_ENABLED) + /////////////////////////////////////////////////////////////////////// + /// Safe Integral Comparisons + /// + template <typename T, typename U> + EA_CONSTEXPR bool cmp_equal(const T x, const U y) EA_NOEXCEPT + { + // Assert types are not chars, bools, etc. + static_assert(eastl::is_integral_v<T> && !eastl::is_same_v<eastl::remove_cv_t<T>, bool> && !eastl::is_same_v<eastl::remove_cv_t<T>, char>); + static_assert(eastl::is_integral_v<U> && !eastl::is_same_v<eastl::remove_cv_t<U>, bool> && !eastl::is_same_v<eastl::remove_cv_t<U>, char>); + + using UT = eastl::make_unsigned_t<T>; + using UU = eastl::make_unsigned_t<U>; + + if constexpr (eastl::is_signed_v<T> == eastl::is_signed_v<U>) + { + return x == y; + } + else if (eastl::is_signed_v<T>) + { + return (x < 0) ? false : UT(x) == y; + } + else + { + return (y < 0) ? false : x == UU(y); + } + } + + + template <typename T, typename U> + EA_CONSTEXPR bool cmp_not_equal(const T x, const U y) EA_NOEXCEPT + { return !eastl::cmp_equal(x, y); } + + + template <typename T, typename U> + EA_CONSTEXPR bool cmp_less(const T x, const U y) EA_NOEXCEPT + { + static_assert(eastl::is_integral_v<T> && !eastl::is_same_v<eastl::remove_cv_t<T>, bool> && !eastl::is_same_v<eastl::remove_cv_t<T>, char>); + static_assert(eastl::is_integral_v<U> && !eastl::is_same_v<eastl::remove_cv_t<U>, bool> && !eastl::is_same_v<eastl::remove_cv_t<U>, char>); + + using UT = eastl::make_unsigned_t<T>; + using UU = eastl::make_unsigned_t<U>; + + if constexpr (eastl::is_signed_v<T> == eastl::is_signed_v<U>) + { + return x < y; + } + else if (eastl::is_signed_v<T>) + { + return (x < 0) ? true : UT(x) < y; + } + else + { + return (y < 0) ? false : x < UU(y); + } + } + + + template <typename T, typename U> + EA_CONSTEXPR bool cmp_greater(const T x, const U y) EA_NOEXCEPT + { return eastl::cmp_less(y, x); } + + + template <typename T, typename U> + EA_CONSTEXPR bool cmp_less_equal(const T x, const U y) EA_NOEXCEPT + { return !eastl::cmp_greater(x, y); } + + + template <typename T, typename U> + EA_CONSTEXPR bool cmp_greater_equal(const T x, const U y) EA_NOEXCEPT + { return !eastl::cmp_less(x, y); } + + + template <typename T, typename U> + EA_CONSTEXPR bool in_range(const U x) EA_NOEXCEPT + { + static_assert(eastl::is_integral_v<T> && !eastl::is_same_v<eastl::remove_cv_t<T>, bool> && !eastl::is_same_v<eastl::remove_cv_t<T>, char>); + static_assert(eastl::is_integral_v<U> && !eastl::is_same_v<eastl::remove_cv_t<U>, bool> && !eastl::is_same_v<eastl::remove_cv_t<U>, char>); + + return eastl::cmp_greater_equal(x, eastl::numeric_limits<T>::min()) && eastl::cmp_less_equal(x, eastl::numeric_limits<T>::max()); + } + #endif + + /////////////////////////////////////////////////////////////////////// /// pair_first_construct /// @@ -360,7 +446,7 @@ namespace eastl struct pair_first_construct_t {}; EA_CONSTEXPR pair_first_construct_t pair_first_construct = pair_first_construct_t(); - + /////////////////////////////////////////////////////////////////////// /// pair /// @@ -630,7 +716,17 @@ namespace eastl return ((a.first == b.first) && (a.second == b.second)); } - + #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T1, typename T2> + EA_CONSTEXPR inline std::common_comparison_category_t<synth_three_way_result<T1>, synth_three_way_result<T2>> operator<=>(const pair<T1, T2>& a, const pair<T1, T2>& b) + { + if (auto result = synth_three_way{}(a.first, b.first); result != 0) + { + return result; + } + return synth_three_way{}(a.second, b.second); + } + #else template <typename T1, typename T2> EA_CPP14_CONSTEXPR inline bool operator<(const pair<T1, T2>& a, const pair<T1, T2>& b) { @@ -668,7 +764,7 @@ namespace eastl { return !(b < a); } - + #endif diff --git a/EASTL/include/EASTL/variant.h b/EASTL/include/EASTL/variant.h index 6e77928..a7af97b 100644 --- a/EASTL/include/EASTL/variant.h +++ b/EASTL/include/EASTL/variant.h @@ -224,11 +224,15 @@ namespace eastl struct monostate {}; // 20.7.8, monostate relational operators +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + EA_CONSTEXPR std::strong_ordering operator<=>(monostate, monostate) EA_NOEXCEPT { return std::strong_ordering::equal; } +#else EA_CONSTEXPR bool operator> (monostate, monostate) EA_NOEXCEPT { return false; } EA_CONSTEXPR bool operator< (monostate, monostate) EA_NOEXCEPT { return false; } EA_CONSTEXPR bool operator!=(monostate, monostate) EA_NOEXCEPT { return false; } EA_CONSTEXPR bool operator<=(monostate, monostate) EA_NOEXCEPT { return true; } EA_CONSTEXPR bool operator>=(monostate, monostate) EA_NOEXCEPT { return true; } +#endif EA_CONSTEXPR bool operator==(monostate, monostate) EA_NOEXCEPT { return true; } // 20.7.11, hash support @@ -1434,7 +1438,6 @@ namespace eastl // struct variant_relational_comparison { - template <typename Compare, size_t I, typename Variant> static EA_CONSTEXPR bool invoke_relational_visitor(const Variant& lhs, const Variant& rhs) { @@ -1457,6 +1460,29 @@ namespace eastl return call_index<Compare>(lhs, rhs, eastl::make_index_sequence<eastl::variant_size_v<eastl::decay_t<Variant>>>()); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename Compare, size_t I, typename Variant> + static EA_CONSTEXPR std::compare_three_way_result_t<Variant> invoke_relational_visitor_three_way(const Variant& lhs, const Variant& rhs) + { + return eastl::invoke(Compare{}, eastl::get<I>(lhs), eastl::get<I>(rhs)); + } + + template <typename Compare, typename Variant, size_t... VariantArgIndices> + static EA_CONSTEXPR std::compare_three_way_result_t<Variant> call_index_three_way(const Variant& lhs, const Variant& rhs, eastl::index_sequence<VariantArgIndices...>) + { + using invoke_relational_visitor_func_ptr = std::compare_three_way_result_t<Variant> (*)(const Variant&, const Variant&); + + EA_CONSTEXPR invoke_relational_visitor_func_ptr visitors[] = {static_cast<invoke_relational_visitor_func_ptr>(&invoke_relational_visitor_three_way<Compare, VariantArgIndices, Variant>)...}; + + return visitors[lhs.index()](lhs, rhs); + } + + template <typename Compare, typename Variant> + static EA_CONSTEXPR std::compare_three_way_result_t<Variant> call_three_way(const Variant& lhs, const Variant& rhs) + { + return call_index_three_way<Compare>(lhs, rhs, eastl::make_index_sequence<eastl::variant_size_v<eastl::decay_t<Variant>>>()); + } +#endif }; template <typename Compare, typename Variant> @@ -1465,6 +1491,14 @@ namespace eastl return variant_relational_comparison::call<Compare>(lhs, rhs); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename Compare, typename Variant> + static EA_CONSTEXPR std::compare_three_way_result_t<Variant> CompareVariantRelationalThreeWay(const Variant& lhs, const Variant& rhs) + { + return variant_relational_comparison::call_three_way<Compare>(lhs, rhs); + } +#endif + } // namespace internal @@ -1533,6 +1567,20 @@ namespace eastl return internal::CompareVariantRelational<eastl::greater_equal<>>(lhs, rhs); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <class... Types> requires (std::three_way_comparable<Types> && ...) + EA_CONSTEXPR std::common_comparison_category_t<std::compare_three_way_result_t<Types>...> operator<=>(const variant<Types...>& lhs, const variant<Types...>& rhs) + { + if (lhs.valueless_by_exception() && rhs.valueless_by_exception()) return std::strong_ordering::equal; + if (lhs.valueless_by_exception()) return std::strong_ordering::less; + if (rhs.valueless_by_exception()) return std::strong_ordering::greater; + if (auto result = (lhs.index() <=> rhs.index()); result != 0) return result; + + return internal::CompareVariantRelationalThreeWay<std::compare_three_way>(lhs, rhs); + + } +#endif + } // namespace eastl EA_RESTORE_VC_WARNING() diff --git a/EASTL/include/EASTL/vector.h b/EASTL/include/EASTL/vector.h index 1736a78..b6ca8dc 100644 --- a/EASTL/include/EASTL/vector.h +++ b/EASTL/include/EASTL/vector.h @@ -1058,8 +1058,9 @@ namespace eastl { if(mpEnd == internalCapacityPtr()) { - const size_type newSize = (size_type)(mpEnd - mpBegin) + 1; - reserve(newSize); + const size_type nPrevSize = size_type(mpEnd - mpBegin); + const size_type nNewSize = GetNewCapacity(nPrevSize); + DoGrow(nNewSize); } return mpEnd++; @@ -1982,7 +1983,13 @@ namespace eastl return ((a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin())); } - +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + template <typename T, typename Allocator> + inline synth_three_way_result<T> operator<=>(const vector<T, Allocator>& a, const vector<T, Allocator>& b) + { + return eastl::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(), synth_three_way{}); + } +#else template <typename T, typename Allocator> inline bool operator!=(const vector<T, Allocator>& a, const vector<T, Allocator>& b) { @@ -2016,7 +2023,7 @@ namespace eastl { return !(a < b); } - +#endif template <typename T, typename Allocator> inline void swap(vector<T, Allocator>& a, vector<T, Allocator>& b) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(a.swap(b))) @@ -2032,17 +2039,39 @@ namespace eastl // https://en.cppreference.com/w/cpp/container/vector/erase2 /////////////////////////////////////////////////////////////////////// template <class T, class Allocator, class U> - void erase(vector<T, Allocator>& c, const U& value) + typename vector<T, Allocator>::size_type erase(vector<T, Allocator>& c, const U& value) { // Erases all elements that compare equal to value from the container. - c.erase(eastl::remove(c.begin(), c.end(), value), c.end()); + auto origEnd = c.end(); + auto newEnd = eastl::remove(c.begin(), origEnd, value); + auto numRemoved = eastl::distance(newEnd, origEnd); + c.erase(newEnd, 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 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<typename vector<T, Allocator>::size_type>(numRemoved); } template <class T, class Allocator, class Predicate> - void erase_if(vector<T, Allocator>& c, Predicate predicate) + typename vector<T, Allocator>::size_type erase_if(vector<T, Allocator>& c, Predicate predicate) { // Erases all elements that satisfy the predicate pred from the container. - c.erase(eastl::remove_if(c.begin(), c.end(), predicate), c.end()); + auto origEnd = c.end(); + auto newEnd = eastl::remove_if(c.begin(), origEnd, predicate); + auto numRemoved = eastl::distance(newEnd, origEnd); + c.erase(newEnd, 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 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<typename vector<T, Allocator>::size_type>(numRemoved); } } // namespace eastl diff --git a/EASTL/source/assert.cpp b/EASTL/source/assert.cpp index a4734af..63b444a 100644 --- a/EASTL/source/assert.cpp +++ b/EASTL/source/assert.cpp @@ -7,7 +7,9 @@ #include <EASTL/string.h> #include <EABase/eabase.h> -#if defined(EA_PLATFORM_MICROSOFT) +#if defined(EA_PLATFORM_WINDOWS_KERNEL) + #include <Wdm.h> +#elif defined(EA_PLATFORM_MICROSOFT) EA_DISABLE_ALL_VC_WARNINGS(); #if defined(EA_COMPILER_MSVC) #include <crtdbg.h> @@ -66,7 +68,9 @@ namespace eastl EASTL_API void AssertionFailureFunctionDefault(const char* pExpression, void* /*pContext*/) { #if EASTL_ASSERT_ENABLED - #if defined(EA_PLATFORM_MICROSOFT) + #if defined(EA_PLATFORM_WINDOWS_KERNEL) + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "%s", pExpression); + #elif defined(EA_PLATFORM_MICROSOFT) printf("%s\n", pExpression); // Write the message to stdout if( ::IsDebuggerPresent()) { diff --git a/EASTL/source/numeric_limits.cpp b/EASTL/source/numeric_limits.cpp index 4eed54e..90b1d75 100644 --- a/EASTL/source/numeric_limits.cpp +++ b/EASTL/source/numeric_limits.cpp @@ -197,6 +197,32 @@ EA_CONSTEXPR_OR_CONST bool numeric_limits<wchar_t>::has_denorm_loss; EA_CONSTEXPR_OR_CONST bool numeric_limits<wchar_t>::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<char8_t>::is_specialized; + EA_CONSTEXPR_OR_CONST int numeric_limits<char8_t>::digits; + EA_CONSTEXPR_OR_CONST int numeric_limits<char8_t>::digits10; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::is_signed; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::is_integer; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::is_exact; + EA_CONSTEXPR_OR_CONST int numeric_limits<char8_t>::radix; + EA_CONSTEXPR_OR_CONST int numeric_limits<char8_t>::min_exponent; + EA_CONSTEXPR_OR_CONST int numeric_limits<char8_t>::min_exponent10; + EA_CONSTEXPR_OR_CONST int numeric_limits<char8_t>::max_exponent; + EA_CONSTEXPR_OR_CONST int numeric_limits<char8_t>::max_exponent10; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::is_bounded; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::is_modulo; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::traps; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::tinyness_before; + EA_CONSTEXPR_OR_CONST float_round_style numeric_limits<char8_t>::round_style; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::has_infinity; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::has_quiet_NaN; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::has_signaling_NaN; + EA_CONSTEXPR_OR_CONST float_denorm_style numeric_limits<char8_t>::has_denorm; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::has_denorm_loss; + EA_CONSTEXPR_OR_CONST bool numeric_limits<char8_t>::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<char16_t>::is_specialized; diff --git a/EASTL/test/CMakeLists.txt b/EASTL/test/CMakeLists.txt index 3fac51f..ff16189 100644 --- a/EASTL/test/CMakeLists.txt +++ b/EASTL/test/CMakeLists.txt @@ -17,6 +17,9 @@ add_definitions(-D_SCL_SECURE_NO_WARNINGS) add_definitions(-DEASTL_OPENSOURCE=1) add_definitions(-D_CHAR16T) add_definitions(-DEASTL_THREAD_SUPPORT_AVAILABLE=0) +if (EASTL_STD_ITERATOR_CATEGORY_ENABLED) + add_definitions(-DEASTL_STD_ITERATOR_CATEGORY_ENABLED=1) +endif() #------------------------------------------------------------------------------------------- # Compiler Flags diff --git a/EASTL/test/source/EASTLTest.h b/EASTL/test/source/EASTLTest.h index 1908f5f..fca6b2c 100644 --- a/EASTL/test/source/EASTLTest.h +++ b/EASTL/test/source/EASTLTest.h @@ -1261,6 +1261,8 @@ public: activeAllocatedMemory = 0; } + virtual ~CountingAllocator() = default; + static uint64_t activeAllocCount; static uint64_t totalAllocCount; static uint64_t totalDeallocCount; diff --git a/EASTL/test/source/TestAlgorithm.cpp b/EASTL/test/source/TestAlgorithm.cpp index 142d45e..a0f64da 100644 --- a/EASTL/test/source/TestAlgorithm.cpp +++ b/EASTL/test/source/TestAlgorithm.cpp @@ -1509,6 +1509,82 @@ int TestAlgorithm() EATEST_VERIFY( b); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + // <compairison_category> lexicographical_compare_three_way(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, Compare compare) + + int intArray1[6] = {0, 1, 2, 3, 4, 5}; + int intArray2[6] = {0, 1, 2, 3, 4, 6}; + int intArray3[5] = {0, 1, 2, 3, 4}; + int intArray4[5] = {4, 3, 2, 1, 0}; + + // strong ordering + auto compare_strong = [](int first, int second) + { + return (first < second) ? std::strong_ordering::less : + (first > second) ? std::strong_ordering::greater : + std::strong_ordering::equal; + }; + + auto b = lexicographical_compare_three_way(intArray1, intArray1 + 6, intArray2, intArray2 + 6, compare_strong); + EATEST_VERIFY(b == std::strong_ordering::less); + b = lexicographical_compare_three_way(intArray3, intArray3 + 5, intArray2, intArray2 + 6, compare_strong); + EATEST_VERIFY(b == std::strong_ordering::less); + b = lexicographical_compare_three_way(intArray3, intArray3 + 5, intArray2, intArray2 + 6, synth_three_way{}); + EATEST_VERIFY(b == std::strong_ordering::less); + + b = lexicographical_compare_three_way(intArray2, intArray2 + 6, intArray1, intArray1 + 6, compare_strong); + EATEST_VERIFY(b == std::strong_ordering::greater); + b = lexicographical_compare_three_way(intArray2, intArray2 + 6, intArray1, intArray1 + 6, synth_three_way{}); + EATEST_VERIFY(b == std::strong_ordering::greater); + + b = lexicographical_compare_three_way(intArray1, intArray1 + 6, intArray3, intArray3 + 5, compare_strong); + EATEST_VERIFY(b == std::strong_ordering::greater); + b = lexicographical_compare_three_way(intArray1, intArray1 + 6, intArray3, intArray3 + 5, synth_three_way{}); + EATEST_VERIFY(b == std::strong_ordering::greater); + + b = lexicographical_compare_three_way(intArray1, intArray1, intArray2, intArray2, compare_strong); // Test empty range. + EATEST_VERIFY(b == std::strong_ordering::equal); + b = lexicographical_compare_three_way(intArray1, intArray1, intArray2, intArray2, synth_three_way{}); // Test empty range. + EATEST_VERIFY(b == std::strong_ordering::equal); + + // weak ordering + auto compare_weak = [](int first, int second) + { + return (first < second) ? std::weak_ordering::less : + (first > second) ? std::weak_ordering::greater : + std::weak_ordering::equivalent; + }; + + auto c = lexicographical_compare_three_way(intArray3, intArray3 + 5, intArray4, intArray4 + 5, compare_weak); + EATEST_VERIFY(c == std::weak_ordering::less); + c = lexicographical_compare_three_way(intArray4, intArray4 + 5, intArray3, intArray3 + 5, compare_weak); + EATEST_VERIFY(c == std::weak_ordering::greater); + c = lexicographical_compare_three_way(intArray3, intArray3 + 5, intArray4, intArray4 + 5, synth_three_way{}); + EATEST_VERIFY(c == std::weak_ordering::less); + c = lexicographical_compare_three_way(intArray4, intArray4 + 5, intArray3, intArray3 + 5, synth_three_way{}); + EATEST_VERIFY(c == std::weak_ordering::greater); + } + + { + EATEST_VERIFY(synth_three_way{}(1, 1) == std::strong_ordering::equal); + EATEST_VERIFY(synth_three_way{}(2, 1) == std::strong_ordering::greater); + EATEST_VERIFY(synth_three_way{}(1, 2) == std::strong_ordering::less); + + struct weak_struct + { + int val; + inline std::weak_ordering operator<=>(const weak_struct& b) const + { + return val <=> b.val; + } + }; + + EATEST_VERIFY(synth_three_way{}(weak_struct{1}, weak_struct{2}) == std::weak_ordering::less); + EATEST_VERIFY(synth_three_way{}(weak_struct{2}, weak_struct{1}) == std::weak_ordering::greater); + EATEST_VERIFY(synth_three_way{}(weak_struct{1}, weak_struct{1}) == std::weak_ordering::equivalent); + } +#endif { // ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& value) @@ -1815,7 +1891,164 @@ int TestAlgorithm() } - { + { + // ForwardIterator apply_and_remove(ForwardIterator first, ForwardIterator last, Function function, const T& + // value) ForwardIterator apply_and_remove_if(ForwardIterator first, ForwardIterator last, Function function, + // Predicate predicate) + + // Test for empty range and full container range + { + int intArray[12] = {0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + int* pInt = apply_and_remove(intArray, intArray, func, 1); + EATEST_VERIFY(pInt == intArray); + EATEST_VERIFY(VerifySequence(intArray, intArray + 12, int(), "apply_and_remove", 0, 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove", -1)); + pInt = apply_and_remove(intArray, intArray + 12, func, 1); + EATEST_VERIFY(pInt == intArray + 6); + EATEST_VERIFY(VerifySequence(intArray, intArray + 6, int(), "apply_and_remove", 0, 0, 0, 0, 0, 0, -1)); + EATEST_VERIFY( + VerifySequence(output.begin(), output.end(), int(), "apply_and_remove", 1, 1, 1, 1, 1, 1, -1)); + } + + // Test for no match on empty range and full container range + { + int intArray[12] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + int* pInt = apply_and_remove(intArray, intArray, func, 1); + EATEST_VERIFY(pInt == intArray); + EATEST_VERIFY(VerifySequence(intArray, intArray + 12, int(), "apply_and_remove", 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove", -1)); + pInt = apply_and_remove(intArray, intArray + 12, func, 1); + EATEST_VERIFY(pInt == intArray + 12); + EATEST_VERIFY(VerifySequence(intArray, intArray + 12, int(), "apply_and_remove", 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove", -1)); + } + + // Test for empty range and full container range + { + int intArray[12] = {0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + int* pInt = apply_and_remove_if(intArray, intArray, func, bind2nd(equal_to<int>(), (int)1)); + EATEST_VERIFY(pInt == intArray); + EATEST_VERIFY(VerifySequence(intArray, intArray + 12, int(), "apply_and_remove_if", 0, 0, 1, 1, 0, 0, 1, 1, + 0, 0, 1, 1, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove_if", -1)); + pInt = apply_and_remove_if(intArray, intArray + 12, func, bind2nd(equal_to<int>(), (int)1)); + EATEST_VERIFY(pInt == intArray + 6); + EATEST_VERIFY(VerifySequence(intArray, intArray + 6, int(), "apply_and_remove_if", 0, 0, 0, 0, 0, 0, -1)); + EATEST_VERIFY( + VerifySequence(output.begin(), output.end(), int(), "apply_and_remove_if", 1, 1, 1, 1, 1, 1, -1)); + } + + // Test for no match on empty range and full container range + { + int intArray[12] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + int* pInt = apply_and_remove_if(intArray, intArray, func, bind2nd(equal_to<int>(), (int)1)); + EATEST_VERIFY(pInt == intArray); + EATEST_VERIFY(VerifySequence(intArray, intArray + 12, int(), "apply_and_remove_if", 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove_if", -1)); + pInt = apply_and_remove_if(intArray, intArray + 12, func, bind2nd(equal_to<int>(), (int)1)); + EATEST_VERIFY(pInt == intArray + 12); + EATEST_VERIFY(VerifySequence(intArray, intArray + 12, int(), "apply_and_remove_if", 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove_if", -1)); + } + + auto even = [](int a) { return (a % 2) == 0; }; + // Test to verify that the remaining element have stable ordering + { + int intArray[12] = {7, 8, 2, 3, 4, 5, 6, 0, 1, 9, 10, 11}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + int* pInt = apply_and_remove_if(intArray, intArray + 12, func, even); + EATEST_VERIFY(pInt == intArray + 6); + EATEST_VERIFY(VerifySequence(intArray, intArray + 6, int(), "apply_and_remove_if", 7, 3, 5, 1, 9, 11, -1)); + EATEST_VERIFY( + VerifySequence(output.begin(), output.end(), int(), "apply_and_remove_if", 8, 2, 4, 6, 0, 10, -1)); + } + { + int intArray[12] = {7, 8, 0, 0, 4, 5, 6, 0, 1, 9, 0, 11}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + int* pInt = apply_and_remove(intArray, intArray + 12, func, 0); + EATEST_VERIFY(pInt == intArray + 8); + EATEST_VERIFY( + VerifySequence(intArray, intArray + 8, int(), "apply_and_remove", 7, 8, 4, 5, 6, 1, 9, 11, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove", 0, 0, 0, 0, -1)); + } + + // Tests on a list (i.e. non-contiguous memory container) + { + list<int> intList = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + auto listIter = apply_and_remove_if(intList.begin(), intList.begin(), func, even); + EATEST_VERIFY(listIter == intList.begin()); + EATEST_VERIFY(VerifySequence(intList.begin(), intList.end(), int(), "apply_and_remove_if", 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove_if", -1)); + listIter = apply_and_remove_if(intList.begin(), intList.end(), func, even); + EATEST_VERIFY(listIter == next(intList.begin(), 6)); + EATEST_VERIFY( + VerifySequence(intList.begin(), listIter, int(), "apply_and_remove_if", 1, 3, 5, 7, 9, 11, -1)); + EATEST_VERIFY( + VerifySequence(output.begin(), output.end(), int(), "apply_and_remove_if", 0, 2, 4, 6, 8, 10, -1)); + } + { + list<int> intList = {0, 4, 2, 3, 4, 5, 6, 4, 4, 4, 10, 11}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + auto listIter = apply_and_remove(intList.begin(), intList.begin(), func, 4); + EATEST_VERIFY(listIter == intList.begin()); + EATEST_VERIFY(VerifySequence(intList.begin(), intList.end(), int(), "apply_and_remove", 0, 4, 2, 3, 4, 5, 6, + 4, 4, 4, 10, 11, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove", -1)); + listIter = apply_and_remove(intList.begin(), intList.end(), func, 4); + EATEST_VERIFY(listIter == next(intList.begin(), 7)); + EATEST_VERIFY( + VerifySequence(intList.begin(), listIter, int(), "apply_and_remove", 0, 2, 3, 5, 6, 10, 11, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove", 4, 4, 4, 4, 4, -1)); + } + + // Tests on a part of a container + { + vector<int> intVector = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + auto vectorIter = apply_and_remove_if(next(intVector.begin(), 3), prev(intVector.end(), 2), func, even); + EATEST_VERIFY(vectorIter == next(intVector.begin(), 7)); + EATEST_VERIFY( + VerifySequence(intVector.begin(), vectorIter, int(), "apply_and_remove_if", 0, 1, 2, 3, 5, 7, 9, -1)); + EATEST_VERIFY( + VerifySequence(prev(intVector.end(), 2), intVector.end(), int(), "apply_and_remove_if", 10, 11, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove_if", 4, 6, 8, -1)); + } + { + vector<int> intVector = {5, 1, 5, 3, 4, 5, 5, 7, 8, 5, 10, 5}; + vector<int> output; + auto func = [&output](int a) { output.push_back(a); }; + auto vectorIter = apply_and_remove(next(intVector.begin(), 2), prev(intVector.end(), 3), func, 5); + EATEST_VERIFY(vectorIter == next(intVector.begin(), 6)); + EATEST_VERIFY( + VerifySequence(intVector.begin(), vectorIter, int(), "apply_and_remove", 5, 1, 3, 4, 7, 8, -1)); + EATEST_VERIFY( + VerifySequence(prev(intVector.end(), 3), intVector.end(), int(), "apply_and_remove", 5, 10, 5, -1)); + EATEST_VERIFY(VerifySequence(output.begin(), output.end(), int(), "apply_and_remove", 5, 5, 5, -1)); + } + } + + + { // OutputIterator replace_copy(InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T& new_value) // OutputIterator replace_copy_if(InputIterator first, InputIterator last, OutputIterator result, Predicate predicate, const T& new_value) diff --git a/EASTL/test/source/TestAllocator.cpp b/EASTL/test/source/TestAllocator.cpp index 85f5adf..2a28c07 100644 --- a/EASTL/test/source/TestAllocator.cpp +++ b/EASTL/test/source/TestAllocator.cpp @@ -151,6 +151,8 @@ static int TestFixedAllocator() { EATEST_VERIFY(buffer1[i].mValue == TEST_VALUE); } + + intList1.clear(); } { // fixed_allocator_with_overflow diff --git a/EASTL/test/source/TestArray.cpp b/EASTL/test/source/TestArray.cpp index 3db95b9..ca05b67 100644 --- a/EASTL/test/source/TestArray.cpp +++ b/EASTL/test/source/TestArray.cpp @@ -125,6 +125,22 @@ int TestArray() VERIFY(!(a >= c)); VERIFY(!(a > c)); +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + VERIFY( (a <=> b) == 0); + VERIFY(!((a <=> b) != 0)); + VERIFY(!((a <=> b) < 0)); + VERIFY( (a <=> b) <= 0); + VERIFY( (a <=> b) >= 0); + VERIFY(!((a <=> b) > 0)); + + VERIFY(!((a <=> c) == 0)); + VERIFY( (a <=> c) != 0); + VERIFY( (a <=> c) < 0); + VERIFY( (a <=> c) <= 0); + VERIFY(!((a <=> c) >= 0)); + VERIFY(!((a <=> c) > 0)); +#endif + // deduction guides #ifdef __cpp_deduction_guides array deduced {1,2,3,4,5}; @@ -132,6 +148,37 @@ int TestArray() static_assert(eastl::is_same_v<decltype(deduced)::value_type, int>, "deduced array value_type mismatch"); VERIFY(deduced.size() == 5); #endif + + // structured binding + + { + eastl::array<int, 5> aCopy = a; + auto&& [a0, a1, a2, a3, a4] = aCopy; + + VERIFY(a0 == aCopy[0]); + VERIFY(a1 == aCopy[1]); + VERIFY(a2 == aCopy[2]); + VERIFY(a3 == aCopy[3]); + VERIFY(a4 == aCopy[4]); + + a0 = 100; + VERIFY(aCopy[0] == 100); + + a4 = 0; + VERIFY(aCopy[4] == 0); + + // The deduced type may or may not be a reference type; it is an aliased type, + // as per https://en.cppreference.com/w/cpp/language/structured_binding: + // > Like a reference, a structured binding is an alias to an existing object. Unlike a reference, + // the type of a structured binding does not have to be a reference type. + // Any reference specifier is thus removed to check only the type & its const qualifier + static_assert(eastl::is_same_v<eastl::remove_reference_t<decltype(a0)>, int>); + + const eastl::array<int, 5> aConstCopy = a; + auto&& [aConst0, aConst1, aConst2, aConst3, aConst4] = aConstCopy; + + static_assert(eastl::is_same_v<eastl::remove_reference_t<decltype(aConst0)>, const int>); + } } // constexpr tests diff --git a/EASTL/test/source/TestChrono.cpp b/EASTL/test/source/TestChrono.cpp index 6a698e9..a56b934 100644 --- a/EASTL/test/source/TestChrono.cpp +++ b/EASTL/test/source/TestChrono.cpp @@ -89,12 +89,15 @@ int TestDuration() microseconds us = 2 * ms; // 6000 microseconds constructed from 3 milliseconds VERIFY(us.count() == 6000); + + microseconds us2 = ms * 2; // 6000 microseconds constructed from 3 milliseconds + VERIFY(us2.count() == 6000); - microseconds us2 = us / 2; - VERIFY(us2.count() == 3000); + microseconds us3 = us / 2; + VERIFY(us3.count() == 3000); - microseconds us3 = us % 2; - VERIFY(us3.count() == 0); + microseconds us4 = us % 2; + VERIFY(us4.count() == 0); } } diff --git a/EASTL/test/source/TestDeque.cpp b/EASTL/test/source/TestDeque.cpp index 99076ff..e3f4ab6 100644 --- a/EASTL/test/source/TestDeque.cpp +++ b/EASTL/test/source/TestDeque.cpp @@ -1063,29 +1063,61 @@ int TestDeque() { eastl::deque<int> d = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - eastl::erase(d, 2); + auto numErased = eastl::erase(d, 2); VERIFY((d == eastl::deque<int>{1, 3, 4, 5, 6, 7, 8, 9})); + VERIFY(numErased == 1); - eastl::erase(d, 7); + numErased = eastl::erase(d, 7); VERIFY((d == eastl::deque<int>{1, 3, 4, 5, 6, 8, 9})); + VERIFY(numErased == 1); - eastl::erase(d, 9); + numErased = eastl::erase(d, 9); VERIFY((d == eastl::deque<int>{1, 3, 4, 5, 6, 8})); + VERIFY(numErased == 1); - eastl::erase(d, 5); + numErased = eastl::erase(d, 5); VERIFY((d == eastl::deque<int>{1, 3, 4, 6, 8})); + VERIFY(numErased == 1); - eastl::erase(d, 3); + numErased = eastl::erase(d, 3); VERIFY((d == eastl::deque<int>{1, 4, 6, 8})); + VERIFY(numErased == 1); } { eastl::deque<int> d = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - eastl::erase_if(d, [](auto i) { return i % 2 == 0; }); + auto numErased = eastl::erase_if(d, [](auto i) { return i % 2 == 0; }); VERIFY((d == eastl::deque<int>{1, 3, 5, 7, 9})); + VERIFY(numErased == 4); } } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + + { // Test <=> + eastl::deque<int> d1 = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + eastl::deque<int> d2 = {9, 8, 7, 6, 5, 4, 3, 2, 1}; + eastl::deque<int> d3 = {1, 2, 3, 4, 5}; + eastl::deque<int> d4 = {10}; + + VERIFY(d1 != d2); + VERIFY(d1 < d2); + VERIFY(d1 != d3); + VERIFY(d1 > d3); + VERIFY(d4 > d1); + VERIFY(d4 > d2); + VERIFY(d4 > d3); + + VERIFY((d1 <=> d2) != 0); + VERIFY((d1 <=> d2) < 0); + VERIFY((d1 <=> d3) != 0); + VERIFY((d1 <=> d3) > 0); + VERIFY((d4 <=> d1) > 0); + VERIFY((d4 <=> d2) > 0); + VERIFY((d4 <=> d3) > 0); + } +#endif + return nErrorCount; } diff --git a/EASTL/test/source/TestExtra.cpp b/EASTL/test/source/TestExtra.cpp index 03f7b41..52fbd62 100644 --- a/EASTL/test/source/TestExtra.cpp +++ b/EASTL/test/source/TestExtra.cpp @@ -66,6 +66,7 @@ namespace eastl #include <EASTL/string.h> #include <EASTL/hash_set.h> #include <EASTL/random.h> +#include <EASTL/bit.h> #include <EASTL/core_allocator_adapter.h> #include <EASTL/bonus/call_traits.h> #include <EASTL/bonus/compressed_pair.h> @@ -292,7 +293,6 @@ static int TestQueue() EATEST_VERIFY(!(toListQueue < toListQueue2)); EATEST_VERIFY(!(toListQueue > toListQueue2)); - // bool empty() const; // size_type size() const; EATEST_VERIFY(toListQueue.empty()); @@ -356,6 +356,103 @@ static int TestQueue() EATEST_VERIFY(intQueue.front() == 5); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + // queue(const Sequence& x = Sequence()); + queue<TestObject, list<TestObject>> toListQueue; + queue<TestObject, list<TestObject>> toListQueue2; + + + // global operators + EATEST_VERIFY( ((toListQueue <=> toListQueue2) == 0)); + EATEST_VERIFY(!((toListQueue <=> toListQueue2) != 0)); + EATEST_VERIFY( ((toListQueue <=> toListQueue2) <= 0)); + EATEST_VERIFY( ((toListQueue <=> toListQueue2) >= 0)); + EATEST_VERIFY(!((toListQueue <=> toListQueue2) < 0)); + EATEST_VERIFY(!((toListQueue <=> toListQueue2) > 0)); + + // bool empty() const; + // size_type size() const; + EATEST_VERIFY(toListQueue.empty()); + EATEST_VERIFY(toListQueue.size() == 0); + + // Verify toListQueue > toListQueue2 + toListQueue.push(TestObject(0)); + toListQueue.push(TestObject(1)); + toListQueue2.push(TestObject(0)); + + EATEST_VERIFY(!((toListQueue <=> toListQueue2) == 0)); + EATEST_VERIFY( ((toListQueue <=> toListQueue2) != 0)); + EATEST_VERIFY( ((toListQueue <=> toListQueue2) >= 0)); + EATEST_VERIFY(!((toListQueue <=> toListQueue2) <= 0)); + EATEST_VERIFY( ((toListQueue <=> toListQueue2) > 0)); + EATEST_VERIFY(!((toListQueue <=> toListQueue2) < 0)); + + // Verify toListQueue2 > toListQueue by element size + toListQueue2.push(TestObject(3)); + EATEST_VERIFY(!((toListQueue <=> toListQueue2) == 0)); + EATEST_VERIFY( ((toListQueue <=> toListQueue2) != 0)); + EATEST_VERIFY( ((toListQueue <=> toListQueue2) <= 0)); + EATEST_VERIFY(!((toListQueue <=> toListQueue2) >= 0)); + EATEST_VERIFY( ((toListQueue <=> toListQueue2) < 0)); + EATEST_VERIFY(!((toListQueue <=> toListQueue2) > 0)); + + queue<TestObject, list<TestObject>> toListQueue3; + queue<TestObject, list<TestObject>> toListQueue4; + + for (int i = 0; i < 10; i++) + { + toListQueue3.push(TestObject(i)); + if (i < 5) + toListQueue4.push(TestObject(i)); + } + + // Verify toListQueue4 is a strict subset of toListQueue3 + EATEST_VERIFY(!((toListQueue3 <=> toListQueue4) == 0)); + EATEST_VERIFY( ((toListQueue3 <=> toListQueue4) != 0)); + EATEST_VERIFY( ((toListQueue3 <=> toListQueue4) >= 0)); + EATEST_VERIFY(!((toListQueue3 <=> toListQueue4) <= 0)); + EATEST_VERIFY( ((toListQueue3 <=> toListQueue4) > 0)); + EATEST_VERIFY(!((toListQueue3 <=> toListQueue4) < 0)); + + // Verify that even thoughn toListQueue4 has a smaller size, it's lexicographically larger + toListQueue4.push(TestObject(11)); + EATEST_VERIFY(!((toListQueue3 <=> toListQueue4) == 0)); + EATEST_VERIFY( ((toListQueue3 <=> toListQueue4) != 0)); + EATEST_VERIFY( ((toListQueue3 <=> toListQueue4) <= 0)); + EATEST_VERIFY(!((toListQueue3 <=> toListQueue4) >= 0)); + EATEST_VERIFY( ((toListQueue3 <=> toListQueue4) < 0)); + EATEST_VERIFY(!((toListQueue3 <=> toListQueue4) > 0)); + + } + + { + queue<TestObject, list<TestObject>> toListQueue1; + queue<TestObject, list<TestObject>> toListQueue2; + queue<TestObject, list<TestObject>> toListQueue3; + + for (int i = 0; i < 10; i++) + { + toListQueue1.push(TestObject(i)); + toListQueue2.push(TestObject(9-i)); + if (i < 5) + toListQueue3.push(TestObject(i)); + } + + struct weak_ordering_queue + { + queue<TestObject, list<TestObject>> queue; + inline std::weak_ordering operator<=>(const weak_ordering_queue& b) const { return queue <=> b.queue; } + }; + + EATEST_VERIFY(synth_three_way{}(weak_ordering_queue{toListQueue1}, weak_ordering_queue{toListQueue2}) == std::weak_ordering::less); + EATEST_VERIFY(synth_three_way{}(weak_ordering_queue{toListQueue3}, weak_ordering_queue{toListQueue1}) == std::weak_ordering::less); + EATEST_VERIFY(synth_three_way{}(weak_ordering_queue{toListQueue2}, weak_ordering_queue{toListQueue1}) == std::weak_ordering::greater); + EATEST_VERIFY(synth_three_way{}(weak_ordering_queue{toListQueue2}, weak_ordering_queue{toListQueue3}) == std::weak_ordering::greater); + EATEST_VERIFY(synth_three_way{}(weak_ordering_queue{toListQueue1}, weak_ordering_queue{toListQueue1}) == std::weak_ordering::equivalent); + } + #endif + { vector<TestObject> toVector; for(int i = 0; i < 100; i++) @@ -620,7 +717,6 @@ static int TestStack() EATEST_VERIFY(!(toListStack < toListStack2)); EATEST_VERIFY(!(toListStack > toListStack2)); - // void push(const value_type& value); // reference top(); // const_reference top() const; @@ -665,6 +761,101 @@ static int TestStack() #endif } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + // stack(const Sequence& x = Sequence()); + stack<TestObject, list<TestObject> > toListStack; + stack<TestObject, list<TestObject> > toListStack2; + + // bool empty() const; + // size_type size() const; + EATEST_VERIFY(toListStack.empty()); + EATEST_VERIFY(toListStack.size() == 0); + + + // global operators + EATEST_VERIFY( ((toListStack <=> toListStack2) == 0)); + EATEST_VERIFY(!((toListStack <=> toListStack2) != 0)); + EATEST_VERIFY( ((toListStack <=> toListStack2) <= 0)); + EATEST_VERIFY( ((toListStack <=> toListStack2) >= 0)); + EATEST_VERIFY(!((toListStack <=> toListStack2) < 0)); + EATEST_VERIFY(!((toListStack <=> toListStack2) > 0)); + + toListStack.push(TestObject(0)); + toListStack.push(TestObject(1)); + toListStack2.push(TestObject(0)); + + EATEST_VERIFY(!((toListStack <=> toListStack2) == 0)); + EATEST_VERIFY( ((toListStack <=> toListStack2) != 0)); + EATEST_VERIFY( ((toListStack <=> toListStack2) >= 0)); + EATEST_VERIFY(!((toListStack <=> toListStack2) <= 0)); + EATEST_VERIFY( ((toListStack <=> toListStack2) > 0)); + EATEST_VERIFY(!((toListStack <=> toListStack2) < 0)); + + // Verify toListStack2 > toListStack by element size + toListStack2.push(TestObject(3)); + EATEST_VERIFY(!((toListStack <=> toListStack2) == 0)); + EATEST_VERIFY( ((toListStack <=> toListStack2) != 0)); + EATEST_VERIFY( ((toListStack <=> toListStack2) <= 0)); + EATEST_VERIFY(!((toListStack <=> toListStack2) >= 0)); + EATEST_VERIFY( ((toListStack <=> toListStack2) < 0)); + EATEST_VERIFY(!((toListStack <=> toListStack2) > 0)); + + stack<TestObject, list<TestObject> > toListStack3; + stack<TestObject, list<TestObject> > toListStack4; + + for (int i = 0; i < 10; i++) + { + toListStack3.push(TestObject(i)); + if (i < 5) + toListStack4.push(TestObject(i)); + } + + // Verify toListStack4 is a strict subset of toListStack3 + EATEST_VERIFY(!((toListStack3 <=> toListStack4) == 0)); + EATEST_VERIFY( ((toListStack3 <=> toListStack4) != 0)); + EATEST_VERIFY( ((toListStack3 <=> toListStack4) >= 0)); + EATEST_VERIFY(!((toListStack3 <=> toListStack4) <= 0)); + EATEST_VERIFY( ((toListStack3 <=> toListStack4) > 0)); + EATEST_VERIFY(!((toListStack3 <=> toListStack4) < 0)); + + // Verify that even thoughn toListQueue4 has a smaller size, it's lexicographically larger + toListStack4.push(TestObject(11)); + EATEST_VERIFY(!((toListStack3 <=> toListStack4) == 0)); + EATEST_VERIFY( ((toListStack3 <=> toListStack4) != 0)); + EATEST_VERIFY( ((toListStack3 <=> toListStack4) <= 0)); + EATEST_VERIFY(!((toListStack3 <=> toListStack4) >= 0)); + EATEST_VERIFY( ((toListStack3 <=> toListStack4) < 0)); + EATEST_VERIFY(!((toListStack3 <=> toListStack4) > 0)); + } + + { + stack<TestObject, list<TestObject> > toListStack1; + stack<TestObject, list<TestObject> > toListStack2; + stack<TestObject, list<TestObject> > toListStack3; + + for (int i = 0; i < 10; i++) + { + toListStack1.push(TestObject(i)); + toListStack2.push(TestObject(9-i)); + if (i < 5) + toListStack3.push(TestObject(i)); + } + + struct weak_ordering_stack + { + stack<TestObject, list<TestObject> > stack; + inline std::weak_ordering operator<=>(const weak_ordering_stack& b) const { return stack <=> b.stack; } + }; + + EATEST_VERIFY(synth_three_way{}(weak_ordering_stack{toListStack1}, weak_ordering_stack{toListStack2}) == std::weak_ordering::less); + EATEST_VERIFY(synth_three_way{}(weak_ordering_stack{toListStack3}, weak_ordering_stack{toListStack1}) == std::weak_ordering::less); + EATEST_VERIFY(synth_three_way{}(weak_ordering_stack{toListStack2}, weak_ordering_stack{toListStack1}) == std::weak_ordering::greater); + EATEST_VERIFY(synth_three_way{}(weak_ordering_stack{toListStack2}, weak_ordering_stack{toListStack3}) == std::weak_ordering::greater); + EATEST_VERIFY(synth_three_way{}(weak_ordering_stack{toListStack1}, weak_ordering_stack{toListStack1}) == std::weak_ordering::equivalent); + } +#endif + { vector<TestObject> toVector; @@ -862,6 +1053,281 @@ static int TestNumeric() return nErrorCount; } +#if defined(EA_COMPILER_CPP20_ENABLED) +template <typename T> +static constexpr int SignedIntMidpoint() +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::midpoint(T(0), T(0)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(0), T(2)) == T(1)); + EATEST_VERIFY(eastl::midpoint(T(0), T(4)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(0), T(8)) == T(4)); + EATEST_VERIFY(eastl::midpoint(T(2), T(0)) == T(1)); + EATEST_VERIFY(eastl::midpoint(T(4), T(0)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(8), T(0)) == T(4)); + + EATEST_VERIFY(eastl::midpoint(T(1), T(1)) == T(1)); + EATEST_VERIFY(eastl::midpoint(T(1), T(3)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(3), T(1)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(2), T(6)) == T(4)); + EATEST_VERIFY(eastl::midpoint(T(6), T(2)) == T(4)); + + EATEST_VERIFY(eastl::midpoint(T(-1), T(-1)) == T(-1)); + EATEST_VERIFY(eastl::midpoint(T(-1), T(-3)) == T(-2)); + EATEST_VERIFY(eastl::midpoint(T(-3), T(-1)) == T(-2)); + EATEST_VERIFY(eastl::midpoint(T(-2), T(-6)) == T(-4)); + EATEST_VERIFY(eastl::midpoint(T(-6), T(-2)) == T(-4)); + + EATEST_VERIFY(eastl::midpoint(T(-0), T(0)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(0), T(-0)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(-0), T(-0)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(-1), T(1)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(-10), T(10)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(-3), T(7)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(-7), T(3)) == T(-2)); + EATEST_VERIFY(eastl::midpoint(T(-2), T(6)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(-6), T(2)) == T(-2)); + EATEST_VERIFY(eastl::midpoint(T(2), T(-6)) == T(-2)); + EATEST_VERIFY(eastl::midpoint(T(6), T(-2)) == T(2)); + + // If an odd sum, midpoint should round towards the LHS operand. + EATEST_VERIFY(eastl::midpoint(T(0), T(5)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(5), T(0)) == T(3)); + EATEST_VERIFY(eastl::midpoint(T(1), T(4)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(4), T(1)) == T(3)); + EATEST_VERIFY(eastl::midpoint(T(7), T(10)) == T(8)); + EATEST_VERIFY(eastl::midpoint(T(10), T(7)) == T(9)); + EATEST_VERIFY(eastl::midpoint(T(-1), T(2)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(2), T(-1)) == T(1)); + EATEST_VERIFY(eastl::midpoint(T(-5), T(4)) == T(-1)); + EATEST_VERIFY(eastl::midpoint(T(4), T(-5)) == T(0)); + + // Test absolute limits + constexpr T MIN = eastl::numeric_limits<T>::min(); + constexpr T MAX = eastl::numeric_limits<T>::max(); + + EATEST_VERIFY(eastl::midpoint(MIN, MIN) == MIN); + EATEST_VERIFY(eastl::midpoint(MAX, MAX) == MAX); + EATEST_VERIFY(eastl::midpoint(MIN, MAX) == T(-1)); + EATEST_VERIFY(eastl::midpoint(MAX, MIN) == T(0)); + EATEST_VERIFY(eastl::midpoint(MIN, T(0)) == MIN / 2); + EATEST_VERIFY(eastl::midpoint(T(0), MIN) == MIN / 2); + EATEST_VERIFY(eastl::midpoint(MAX, T(0)) == (MAX / 2) + 1); + EATEST_VERIFY(eastl::midpoint(T(0), MAX) == (MAX / 2)); + + EATEST_VERIFY(eastl::midpoint(MIN, T(10)) == (MIN / 2) + 5); + EATEST_VERIFY(eastl::midpoint(T(10), MIN) == (MIN / 2) + 5); + EATEST_VERIFY(eastl::midpoint(MAX, T(10)) == (MAX / 2) + 5 + 1); + EATEST_VERIFY(eastl::midpoint(T(10), MAX) == (MAX / 2) + 5); + EATEST_VERIFY(eastl::midpoint(MIN, T(-10)) == (MIN / 2) - 5); + EATEST_VERIFY(eastl::midpoint(T(-10), MIN) == (MIN / 2) - 5); + EATEST_VERIFY(eastl::midpoint(MAX, T(-10)) == (MAX / 2) - 5 + 1); + EATEST_VERIFY(eastl::midpoint(T(-10), MAX) == (MAX / 2) - 5); + + return nErrorCount; +} + +template <typename T> +static constexpr int UnsignedIntMidpoint() +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::midpoint(T(0), T(0)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(0), T(2)) == T(1)); + EATEST_VERIFY(eastl::midpoint(T(0), T(4)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(0), T(8)) == T(4)); + EATEST_VERIFY(eastl::midpoint(T(2), T(0)) == T(1)); + EATEST_VERIFY(eastl::midpoint(T(4), T(0)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(8), T(0)) == T(4)); + + EATEST_VERIFY(eastl::midpoint(T(1), T(1)) == T(1)); + EATEST_VERIFY(eastl::midpoint(T(1), T(3)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(3), T(1)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(2), T(6)) == T(4)); + EATEST_VERIFY(eastl::midpoint(T(6), T(2)) == T(4)); + + // If an odd sum, midpoint should round towards the LHS operand. + EATEST_VERIFY(eastl::midpoint(T(0), T(5)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(5), T(0)) == T(3)); + EATEST_VERIFY(eastl::midpoint(T(1), T(4)) == T(2)); + EATEST_VERIFY(eastl::midpoint(T(4), T(1)) == T(3)); + EATEST_VERIFY(eastl::midpoint(T(7), T(10)) == T(8)); + EATEST_VERIFY(eastl::midpoint(T(10), T(7)) == T(9)); + + // Test absolute limits + constexpr T MIN = eastl::numeric_limits<T>::min(); + constexpr T MAX = eastl::numeric_limits<T>::max(); + + EATEST_VERIFY(eastl::midpoint(MIN, MIN) == MIN); + EATEST_VERIFY(eastl::midpoint(MAX, MAX) == MAX); + EATEST_VERIFY(eastl::midpoint(MIN, MAX) == MAX / 2); + EATEST_VERIFY(eastl::midpoint(MAX, MIN) == (MAX / 2) + 1); + EATEST_VERIFY(eastl::midpoint(MIN, T(0)) == T(0)); + EATEST_VERIFY(eastl::midpoint(T(0), MIN) == T(0)); + + EATEST_VERIFY(eastl::midpoint(MIN, T(10)) == (MIN / 2) + 5); + EATEST_VERIFY(eastl::midpoint(T(10), MIN) == (MIN / 2) + 5); + EATEST_VERIFY(eastl::midpoint(MAX, T(10)) == (MAX / 2) + 5 + 1); + EATEST_VERIFY(eastl::midpoint(T(10), MAX) == (MAX / 2) + 5); + + return nErrorCount; +} + +template <typename T> +static constexpr int FloatMidpoint() +{ + // for use with floats, double, long doubles. + int nErrorCount = 0; + EATEST_VERIFY(eastl::midpoint(T(0.0), T(0.0)) == T(0.0)); + EATEST_VERIFY(eastl::midpoint(T(0.0), T(2.0)) == T(1.0)); + EATEST_VERIFY(eastl::midpoint(T(0.0), T(4.0)) == T(2.0)); + EATEST_VERIFY(eastl::midpoint(T(2.0), T(0.0)) == T(1.0)); + EATEST_VERIFY(eastl::midpoint(T(4.0), T(0.0)) == T(2.0)); + + EATEST_VERIFY(eastl::midpoint(T(0.5), T(0.5)) == T(0.5)); + EATEST_VERIFY(eastl::midpoint(T(0.0), T(0.5)) == T(0.25)); + EATEST_VERIFY(eastl::midpoint(T(0.5), T(0.0)) == T(0.25)); + EATEST_VERIFY(eastl::midpoint(T(0.5), T(1.0)) == T(0.75)); + EATEST_VERIFY(eastl::midpoint(T(1.0), T(0.5)) == T(0.75)); + + EATEST_VERIFY(eastl::midpoint(T(-0.0), T(0.0)) == T(0.0)); + EATEST_VERIFY(eastl::midpoint(T(0.0), T(-0.0)) == T(0.0)); + EATEST_VERIFY(eastl::midpoint(T(-0.0), T(-0.0)) == T(0.0)); + EATEST_VERIFY(eastl::midpoint(T(-1.0), T(2.0)) == T(0.5)); + EATEST_VERIFY(eastl::midpoint(T(-2.0), T(1)) == T(-0.5)); + EATEST_VERIFY(eastl::midpoint(T(-3.0), T(6.0)) == T(1.5)); + EATEST_VERIFY(eastl::midpoint(T(-6.0), T(3.0)) == T(-1.5)); + + // Test absolute limits + const T MIN = eastl::numeric_limits<T>::min(); + const T MAX = eastl::numeric_limits<T>::max(); + + EATEST_VERIFY(eastl::midpoint(MIN, MIN) == MIN); + EATEST_VERIFY(eastl::midpoint(MAX, MAX) == MAX); + EATEST_VERIFY(eastl::midpoint(MIN, MAX) == MAX / 2); + EATEST_VERIFY(eastl::midpoint(MAX, MIN) == MAX / 2); + EATEST_VERIFY(eastl::midpoint(-MAX, MIN) == -MAX / 2); + + EATEST_VERIFY(eastl::midpoint(MIN, T(9.0)) == T(4.5)); + EATEST_VERIFY(eastl::midpoint(MIN, T(-9.0)) == T(-4.5)); + EATEST_VERIFY(eastl::midpoint(T(9.0), MIN) == T(4.5)); + EATEST_VERIFY(eastl::midpoint(T(-9.0), MIN) == T(-4.5)); + EATEST_VERIFY(eastl::midpoint(MAX, T(9.0)) == MAX / 2 + T(4.5)); + EATEST_VERIFY(eastl::midpoint(MAX, T(-9.0)) == MAX / 2 - T(4.5)); + EATEST_VERIFY(eastl::midpoint(T(9.0), MAX) == MAX / 2 + T(4.5)); + EATEST_VERIFY(eastl::midpoint(T(-9.0), MAX) == MAX / 2 - T(4.5)); + + return nErrorCount; +} + +template <typename T> +static constexpr int PointerMidpoint() +{ + int nErrorCount = 0; + + const T ARR[100] = {}; + + EATEST_VERIFY(eastl::midpoint(ARR, ARR) == ARR); + EATEST_VERIFY(eastl::midpoint(ARR, ARR + 100) == ARR + 50); + EATEST_VERIFY(eastl::midpoint(ARR + 100, ARR) == ARR + 50); + EATEST_VERIFY(eastl::midpoint(ARR, ARR + 25) == ARR + 12); + EATEST_VERIFY(eastl::midpoint(ARR + 25, ARR) == ARR + 13); + EATEST_VERIFY(eastl::midpoint(ARR, ARR + 13) == ARR + 6); + EATEST_VERIFY(eastl::midpoint(ARR + 13, ARR) == ARR + 7); + EATEST_VERIFY(eastl::midpoint(ARR + 50, ARR + 100) == ARR + 75); + EATEST_VERIFY(eastl::midpoint(ARR + 100, ARR + 50) == ARR + 75); + + return nErrorCount; +} + + +/////////////////////////////////////////////////////////////////////////////// +// TestMidpoint +// +static int TestMidpoint() +{ + int nErrorCount = 0; + + // template <typename T> + // constexpr eastl::enable_if_t<eastl::is_arithmetic_v<T> && !eastl::is_same_v<eastl::remove_cv_t<T>, bool>, T> + // midpoint(const T lhs, const T rhs) EA_NOEXCEPT + nErrorCount += SignedIntMidpoint<int>(); + nErrorCount += SignedIntMidpoint<char>(); + nErrorCount += SignedIntMidpoint<short>(); + nErrorCount += SignedIntMidpoint<long>(); + nErrorCount += SignedIntMidpoint<long long>(); + + nErrorCount += UnsignedIntMidpoint<unsigned int>(); + nErrorCount += UnsignedIntMidpoint<unsigned char>(); + nErrorCount += UnsignedIntMidpoint<unsigned short>(); + nErrorCount += UnsignedIntMidpoint<unsigned long>(); + nErrorCount += UnsignedIntMidpoint<unsigned long long>(); + + nErrorCount += FloatMidpoint<float>(); + nErrorCount += FloatMidpoint<double>(); + nErrorCount += FloatMidpoint<long double>(); + + // template <typename T> + // constexpr eastl::enable_if_t<eastl::is_object_v<T>, const T*> midpoint(const T* lhs, const T* rhs) + nErrorCount += PointerMidpoint<int>(); + nErrorCount += PointerMidpoint<char>(); + nErrorCount += PointerMidpoint<short>(); + nErrorCount += PointerMidpoint<float>(); + nErrorCount += PointerMidpoint<double>(); + nErrorCount += PointerMidpoint<long double>(); + + return nErrorCount; +} + + +template <typename T> +static constexpr int FloatLerp() +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::lerp(T(0.0), T(0.0), T(0.0)) == T(0.0)); + EATEST_VERIFY(eastl::lerp(T(1.0), T(0.0), T(0.0)) == T(1.0)); + EATEST_VERIFY(eastl::lerp(T(-1.0), T(0.0), T(0.0)) == T(-1.0)); + EATEST_VERIFY(eastl::lerp(T(0.0), T(1.0), T(0.0)) == T(0.0)); + EATEST_VERIFY(eastl::lerp(T(0.0), T(-1.0), T(0.0)) == T(0.0)); + EATEST_VERIFY(eastl::lerp(T(-1.0), T(1.0), T(1.0)) == T(1.0)); + EATEST_VERIFY(eastl::lerp(T(1.0), T(-1.0), T(1.0)) == T(-1.0)); + EATEST_VERIFY(eastl::lerp(T(-1.0), T(1.0), T(0.5)) == T(0.0)); + EATEST_VERIFY(eastl::lerp(T(1.0), T(-1.0), T(0.5)) == T(0.0)); + EATEST_VERIFY(eastl::lerp(T(5.0), T(5.0), T(0.5)) == T(5.0)); + EATEST_VERIFY(eastl::lerp(T(-5.0), T(-5.0), T(0.5)) == T(-5.0)); + EATEST_VERIFY(eastl::lerp(T(1.0), T(2.0), T(1.0)) == T(2.0)); + EATEST_VERIFY(eastl::lerp(T(2.0), T(1.0), T(1.0)) == T(1.0)); + EATEST_VERIFY(eastl::lerp(T(1.0), T(2.0), T(1.0)) == T(2.0)); + EATEST_VERIFY(eastl::lerp(T(1.0), T(2.0), T(2.0)) == T(3.0)); + EATEST_VERIFY(eastl::lerp(T(2.0), T(1.0), T(2.0)) == T(0.0)); + EATEST_VERIFY(eastl::lerp(T(1.0), T(-2.0), T(2.0)) == T(-5.0)); + EATEST_VERIFY(eastl::lerp(T(-1.0), T(2.0), T(2.0)) == T(5.0)); + EATEST_VERIFY(eastl::lerp(T(-1.5), T(1.5), T(0.75)) == T(0.75)); + EATEST_VERIFY(eastl::lerp(T(0.125), T(1.75), T(0.25)) == T(0.53125)); + EATEST_VERIFY(eastl::lerp(T(-0.125), T(-1.75), T(0.5)) == T(-0.9375)); + EATEST_VERIFY(eastl::lerp(T(-0.125), T(1.5), T(2.5)) == T(3.9375)); + + return nErrorCount; +} + +/////////////////////////////////////////////////////////////////////////////// +// TestLerp +// +static int TestLerp() +{ + int nErrorCount = 0; + + // template <class T> + // constexpr T lerp(const T a, const T b, const T t) EA_NOEXCEPT + nErrorCount += FloatLerp<float>(); + nErrorCount += FloatLerp<double>(); + nErrorCount += FloatLerp<long double>(); + + return nErrorCount; +} +#endif /////////////////////////////////////////////////////////////////////////////// @@ -914,7 +1380,142 @@ static int TestAdaptors() return nErrorCount; } +#if defined(EA_COMPILER_CPP20_ENABLED) +template <typename T> +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<T>::min()) == false); + VERIFY(eastl::has_single_bit(eastl::numeric_limits<T>::max()) == false); + for (int i = 4; i < eastl::numeric_limits<T>::digits; i++) + { + T power_of_two = static_cast<T>(T(1U) << i); + VERIFY(eastl::has_single_bit(power_of_two)); + VERIFY(eastl::has_single_bit(static_cast<T>(power_of_two - 1)) == false); + } + + return nErrorCount; +} + +template <typename T> +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<T>::digits; + EA_CONSTEXPR auto MIN = eastl::numeric_limits<T>::min(); + EA_CONSTEXPR auto MAX = static_cast<T>(T(1) << (DIGITS - 1)); + + VERIFY(eastl::bit_ceil(MAX) == MAX); + VERIFY(eastl::bit_ceil(static_cast<T>(MAX - 1)) == MAX); + VERIFY(eastl::bit_ceil(MIN) == T(1)); + + for (int i = 4; i < eastl::numeric_limits<T>::digits; i++) + { + T power_of_two = static_cast<T>(T(1U) << i); + VERIFY(eastl::bit_ceil(power_of_two) == power_of_two); + VERIFY(eastl::bit_ceil(static_cast<T>(power_of_two - 1)) == power_of_two); + } + + return nErrorCount; +} + +template <typename T> +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<T>::digits; + EA_CONSTEXPR auto MIN = eastl::numeric_limits<T>::min(); + EA_CONSTEXPR auto MAX = eastl::numeric_limits<T>::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<T>::digits; i++) + { + T power_of_two = static_cast<T>(T(1U) << i); + VERIFY(eastl::bit_floor(power_of_two) == power_of_two); + VERIFY(eastl::bit_floor(static_cast<T>(power_of_two + 1)) == power_of_two); + } + return nErrorCount; +} + +template <typename T> +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<T>::digits; + EA_CONSTEXPR auto MIN = eastl::numeric_limits<T>::min(); + EA_CONSTEXPR auto MAX = eastl::numeric_limits<T>::max(); + + VERIFY(eastl::bit_width(MIN) == 0); + VERIFY(eastl::bit_width(MAX) == DIGITS); + + for (int i = 4; i < eastl::numeric_limits<T>::digits; i++) + { + T power_of_two = static_cast<T>(T(1U) << i); + VERIFY(eastl::bit_width(power_of_two) == static_cast<T>(i + 1)); + } + + return nErrorCount; +} + +/////////////////////////////////////////////////////////////////////////////// +// TestPowerofTwo +// +static int TestPowerOfTwo() +{ + int nErrorCount = 0; + nErrorCount += TestHasSingleBit<unsigned int>(); + nErrorCount += TestHasSingleBit<unsigned char>(); + nErrorCount += TestHasSingleBit<unsigned short>(); + nErrorCount += TestHasSingleBit<unsigned long>(); + nErrorCount += TestHasSingleBit<unsigned long long>(); + + nErrorCount += TestBitCeil<unsigned int>(); + nErrorCount += TestBitCeil<unsigned char>(); + nErrorCount += TestBitCeil<unsigned short>(); + nErrorCount += TestBitCeil<unsigned long>(); + nErrorCount += TestBitCeil<unsigned long long>(); + + nErrorCount += TestBitFloor<unsigned int>(); + nErrorCount += TestBitFloor<unsigned char>(); + nErrorCount += TestBitFloor<unsigned short>(); + nErrorCount += TestBitFloor<unsigned long>(); + nErrorCount += TestBitFloor<unsigned long long>(); + + nErrorCount += TestBitWidth<unsigned int>(); + nErrorCount += TestBitWidth<unsigned char>(); + nErrorCount += TestBitWidth<unsigned short>(); + nErrorCount += TestBitWidth<unsigned long>(); + nErrorCount += TestBitWidth<unsigned long long>(); + + return nErrorCount; +} +#endif /////////////////////////////////////////////////////////////////////////////// // TestExtra @@ -931,6 +1532,11 @@ int TestExtra() nErrorCount += TestCallTraits(); nErrorCount += TestNumeric(); nErrorCount += TestAdaptors(); +#if defined(EA_COMPILER_CPP20_ENABLED) + nErrorCount += TestMidpoint(); + nErrorCount += TestLerp(); + nErrorCount += TestPowerOfTwo(); +#endif return nErrorCount; } diff --git a/EASTL/test/source/TestFixedString.cpp b/EASTL/test/source/TestFixedString.cpp index a7f7bd9..8528dc7 100644 --- a/EASTL/test/source/TestFixedString.cpp +++ b/EASTL/test/source/TestFixedString.cpp @@ -131,6 +131,58 @@ int TestFixedSubstring() EATEST_VERIFY(str == ""); } + + { + // Check that copies/moves don't become independent strings. + // They should all point to the same sub-string. + string str = "hello world"; + fixed_substring<char> sub(str, 2, 5); + + EATEST_VERIFY(sub.size() == 5); + EATEST_VERIFY(sub[0] == 'l'); + EATEST_VERIFY(sub == "llo w"); + + vector<fixed_substring<char>> v; + for (eastl_size_t i = 0; i < 1000; ++i) { + v.push_back(sub); + } + + sub[0] = 'g'; + EATEST_VERIFY(str == "heglo world"); + EATEST_VERIFY(sub == "glo w"); + + for (const auto& s : v){ + EATEST_VERIFY(s == "glo w"); + } + + // copy construct + fixed_substring<char> sub2 = sub; + + // copy assign + fixed_substring<char> sub3; + sub3 = sub; + + // move construct + fixed_substring<char> sub4 = eastl::move(sub); + + // move assign + fixed_substring<char> sub_again(str, 2, 5); + fixed_substring<char> sub5; + sub5 = eastl::move(sub_again); + + EATEST_VERIFY(sub2 == "glo w"); + EATEST_VERIFY(sub3 == "glo w"); + EATEST_VERIFY(sub4 == "glo w"); + EATEST_VERIFY(sub5 == "glo w"); + + str[5] = 'g'; + EATEST_VERIFY(sub2 == "glogw"); + EATEST_VERIFY(sub3 == "glogw"); + EATEST_VERIFY(sub4 == "glogw"); + EATEST_VERIFY(sub5 == "glogw"); + + } + return nErrorCount; } diff --git a/EASTL/test/source/TestFunctional.cpp b/EASTL/test/source/TestFunctional.cpp index 63b3516..1e25200 100644 --- a/EASTL/test/source/TestFunctional.cpp +++ b/EASTL/test/source/TestFunctional.cpp @@ -1491,5 +1491,39 @@ struct TestInvokeResult }; template struct eastl::invoke_result<decltype(&TestInvokeResult::f), TestInvokeResult, void>; + static_assert(!eastl::is_invocable<decltype(&TestInvokeResult::f), TestInvokeResult, void>::value, "incorrect value for is_invocable"); +static_assert(!eastl::is_invocable<decltype(&TestInvokeResult::f), TestInvokeResult, int, int>::value, "incorrect value for is_invocable"); static_assert(eastl::is_invocable<decltype(&TestInvokeResult::f), TestInvokeResult, int>::value, "incorrect value for is_invocable"); + +static_assert(!eastl::is_invocable_r<int, decltype(&TestInvokeResult::f), TestInvokeResult, void>::value, "incorrect value for is_invocable_r"); +static_assert(!eastl::is_invocable_r<void, decltype(&TestInvokeResult::f), TestInvokeResult, int, int>::value, "incorrect value for is_invocable_r"); +static_assert(eastl::is_invocable_r<void, decltype(&TestInvokeResult::f), TestInvokeResult, int>::value, "incorrect value for is_invocable_r"); +static_assert(eastl::is_invocable_r<int, decltype(&TestInvokeResult::f), TestInvokeResult, int>::value, "incorrect value for is_invocable_r"); + +struct TestCallableInvokeResult +{ + int operator()(int i) {return i;} +}; + +template struct eastl::invoke_result<TestCallableInvokeResult, void>; + +static_assert(!eastl::is_invocable<TestCallableInvokeResult, void>::value, "incorrect value for is_invocable"); +static_assert(!eastl::is_invocable<TestCallableInvokeResult, int, int>::value, "incorrect value for is_invocable"); +static_assert(eastl::is_invocable<TestCallableInvokeResult, int>::value, "incorrect value for is_invocable"); + +static_assert(!eastl::is_invocable_r<int, TestCallableInvokeResult, void>::value, "incorrect value for is_invocable_r"); +static_assert(!eastl::is_invocable_r<void, TestCallableInvokeResult, int, int>::value, "incorrect value for is_invocable_r"); +static_assert(eastl::is_invocable_r<void, TestCallableInvokeResult, int>::value, "incorrect value for is_invocable_r"); +static_assert(eastl::is_invocable_r<int, TestCallableInvokeResult, int>::value, "incorrect value for is_invocable_r"); + +typedef decltype(eastl::ref(eastl::declval<TestCallableInvokeResult&>())) TestCallableRefInvokeResult; + +static_assert(!eastl::is_invocable<TestCallableRefInvokeResult, void>::value, "incorrect value for is_invocable"); +static_assert(!eastl::is_invocable<TestCallableRefInvokeResult, int, int>::value, "incorrect value for is_invocable"); +static_assert(eastl::is_invocable<TestCallableRefInvokeResult, int>::value, "incorrect value for is_invocable"); + +static_assert(!eastl::is_invocable_r<int, TestCallableRefInvokeResult, void>::value, "incorrect value for is_invocable_r"); +static_assert(!eastl::is_invocable_r<void, TestCallableRefInvokeResult, int, int>::value, "incorrect value for is_invocable_r"); +static_assert(eastl::is_invocable_r<void, TestCallableRefInvokeResult, int>::value, "incorrect value for is_invocable_r"); +static_assert(eastl::is_invocable_r<int, TestCallableRefInvokeResult, int>::value, "incorrect value for is_invocable_r"); diff --git a/EASTL/test/source/TestHash.cpp b/EASTL/test/source/TestHash.cpp index 9c9bf9d..1bcf996 100644 --- a/EASTL/test/source/TestHash.cpp +++ b/EASTL/test/source/TestHash.cpp @@ -746,14 +746,16 @@ int TestHash() { // hash_set erase_if hash_set<int> m = {0, 1, 2, 3, 4}; - eastl::erase_if(m, [](auto i) { return i % 2 == 0; }); + auto numErased = eastl::erase_if(m, [](auto i) { return i % 2 == 0; }); VERIFY((m == hash_set<int>{1, 3})); + VERIFY(numErased == 3); } { // hash_multiset erase_if hash_multiset<int> m = {0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4}; - eastl::erase_if(m, [](auto i) { return i % 2 == 0; }); + auto numErased = eastl::erase_if(m, [](auto i) { return i % 2 == 0; }); VERIFY((m == hash_multiset<int>{1, 1, 1, 3})); + VERIFY(numErased == 12); } @@ -943,15 +945,17 @@ int TestHash() { // hash_map erase_if hash_map<int, int> m = {{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}; - eastl::erase_if(m, [](auto p) { return p.first % 2 == 0; }); + auto numErased = eastl::erase_if(m, [](auto p) { return p.first % 2 == 0; }); VERIFY((m == hash_map<int, int>{{1, 1}, {3, 3}})); + VERIFY(numErased == 3); } { // hash_multimap erase_if hash_multimap<int, int> m = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {1, 1}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {3, 3}, {3, 3}, {4, 4}}; - eastl::erase_if(m, [](auto p) { return p.first % 2 == 0; }); + auto numErased = eastl::erase_if(m, [](auto p) { return p.first % 2 == 0; }); VERIFY((m == hash_multimap<int, int>{{1, 1}, {3, 3}, {3, 3}})); + VERIFY(numErased == 9); } diff --git a/EASTL/test/source/TestIterator.cpp b/EASTL/test/source/TestIterator.cpp index 02aa1d4..b6c6f76 100644 --- a/EASTL/test/source/TestIterator.cpp +++ b/EASTL/test/source/TestIterator.cpp @@ -4,6 +4,7 @@ #include "EASTLTest.h" +#include <EASTL/deque.h> #include <EASTL/iterator.h> #include <EASTL/vector.h> #include <EASTL/set.h> @@ -21,6 +22,8 @@ EA_DISABLE_ALL_VC_WARNINGS() #include <string.h> EA_RESTORE_ALL_VC_WARNINGS() +template <class T> +using detect_iterator_traits_reference = typename eastl::iterator_traits<T>::reference; // This is used below, though is currently disabled as documented below. struct IListNode : public eastl::intrusive_list_node{}; @@ -181,7 +184,7 @@ int TestIterator_moveIterator() // Check that we support iterators yielding plain value (typically a proxy-iterator). struct FakeProxyIterator { - using iterator_category = eastl::forward_iterator_tag; + using iterator_category = EASTL_ITC_NS::forward_iterator_tag; using difference_type = ptrdiff_t; using value_type = int; using pointer = int; // Note that we are yielding by value. @@ -250,6 +253,34 @@ int TestIterator() } { + // Regression bug with assign/insert combined with reverse iterator. + eastl::vector<int> a; + for (int i = 0; i < 10; ++i) { + a.push_back(i); + } + + eastl::deque<int> d; + d.assign(a.rbegin(), a.rend()); + for (int i = 0; i < 10; ++i) { + EATEST_VERIFY(a[i] == d[a.size() - i - 1]); + } + d.insert(d.end(), a.rbegin(), a.rend()); + for (int i = 0; i < 10; ++i) { + EATEST_VERIFY(a[i] == d[d.size() - i - 1]); + } + + eastl::vector<int> b; + b.assign(a.rbegin(), a.rend()); + for (int i = 0; i < 10; ++i) { + EATEST_VERIFY(a[i] == b[a.size() - i - 1]); + } + b.insert(b.end(), a.rbegin(), a.rend()); + for (int i = 0; i < 10; ++i) { + EATEST_VERIFY(a[i] == b[b.size() - i - 1]); + } + } + + { // move_iterator // move_iterator<Iterator> make_move_iterator(Iterator mi) typedef eastl::vector<eastl::string> StringArray; @@ -421,13 +452,17 @@ int TestIterator() { // is_iterator_wrapper - static_assert((eastl::is_iterator_wrapper<void>::value == false), "is_iterator_wrapper failure"); - static_assert((eastl::is_iterator_wrapper<int>::value == false), "is_iterator_wrapper failure"); - static_assert((eastl::is_iterator_wrapper<int*>::value == false), "is_iterator_wrapper failure"); - static_assert((eastl::is_iterator_wrapper<eastl::array<char>*>::value == false), "is_iterator_wrapper failure"); - static_assert((eastl::is_iterator_wrapper<eastl::vector<char> >::value == false), "is_iterator_wrapper failure"); - static_assert((eastl::is_iterator_wrapper<eastl::generic_iterator<int*> >::value == true), "is_iterator_wrapper failure"); - static_assert((eastl::is_iterator_wrapper<eastl::move_iterator<eastl::array<int>::iterator> >::value == true), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<void>::value == false), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<int>::value == false), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<int*>::value == false), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<eastl::array<int>::iterator>::value == false), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<eastl::array<char>*>::value == false), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<eastl::vector<char> >::value == false), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<eastl::generic_iterator<int*> >::value == true), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<eastl::move_iterator<eastl::array<int>::iterator> >::value == true), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<eastl::reverse_iterator<eastl::array<int>::iterator> >::value == false), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<eastl::reverse_iterator<int*> >::value == false), "is_iterator_wrapper failure"); + static_assert((eastl::is_iterator_wrapper<eastl::reverse_iterator<eastl::move_iterator<int*>> >::value == true), "is_iterator_wrapper failure"); } @@ -455,6 +490,61 @@ int TestIterator() intVector[0] = 20; EATEST_VERIFY(*itVector == 20); static_assert((eastl::is_same<decltype(eastl::unwrap_iterator(miIntVector)), eastl::vector<int>::iterator>::value == true), "unwrap_iterator failure"); + + eastl::reverse_iterator<eastl::vector<int>::iterator> riIntVector = intVector.rbegin(); + eastl::reverse_iterator<eastl::vector<int>::iterator> riUnwrapped = eastl::unwrap_iterator(riIntVector); + EATEST_VERIFY(*riUnwrapped == 19); + static_assert((eastl::is_same<decltype(eastl::unwrap_iterator(riIntVector)), eastl::reverse_iterator<eastl::vector<int>::iterator>>::value == true), "unwrap_iterator failure"); + + eastl::reverse_iterator<eastl::move_iterator<eastl::vector<int>::iterator>> rimiIntVec(miIntVector); + static_assert((eastl::is_same<decltype(eastl::unwrap_iterator(rimiIntVec)), eastl::reverse_iterator<eastl::vector<int>::iterator>>::value == true), "unwrap_iterator failure"); + + eastl::reverse_iterator<eastl::generic_iterator<int*>> rigiIntArray(giIntArray); + static_assert((eastl::is_same<decltype(eastl::unwrap_iterator(rigiIntArray)), eastl::reverse_iterator<int*>>::value == true), "unwrap_iterator failure"); + + eastl::deque<int> intDeque(3); + eastl::deque<int>::iterator begin = intDeque.begin(); + eastl::generic_iterator<eastl::deque<int>::iterator> giWrappedBegin(begin); + static_assert((eastl::is_same<decltype(eastl::unwrap_iterator(giWrappedBegin)), eastl::deque<int>::iterator>::value == true), "unwrap_iterator failure"); + + eastl::deque<int>::iterator unwrappedBegin = eastl::unwrap_iterator(giWrappedBegin); + EATEST_VERIFY(begin == unwrappedBegin); + } + + { + // unwrap_generic_iterator + int intArray[2] = {0, 1}; + eastl::generic_iterator<int*> giIntArray(intArray); + int* pInt = eastl::unwrap_generic_iterator(giIntArray); + EATEST_VERIFY(*pInt == 0); + static_assert((eastl::is_same<decltype(eastl::unwrap_generic_iterator(giIntArray)), int*>::value == true), "unwrap_iterator failure"); + + eastl::move_iterator<int*> miIntArray(intArray); + static_assert((eastl::is_same<decltype(eastl::unwrap_generic_iterator(miIntArray)), eastl::move_iterator<int*>>::value == true), "unwrap_iterator failure"); + + eastl::vector<int> intVector(1, 1); + eastl::generic_iterator<eastl::vector<int>::iterator> giVectorInt(intVector.begin()); + eastl::vector<int>::iterator it = unwrap_generic_iterator(giVectorInt); + EATEST_VERIFY(*it == 1); + static_assert((eastl::is_same<decltype(eastl::unwrap_generic_iterator(giVectorInt)), eastl::vector<int>::iterator>::value == true), "unwrap_iterator failure"); + } + + { + // unwrap_move_iterator + int intArray[2] = {0, 1}; + eastl::move_iterator<int*> miIntArray(intArray); + int* pInt = eastl::unwrap_move_iterator(miIntArray); + EATEST_VERIFY(*pInt == 0); + static_assert((eastl::is_same<decltype(eastl::unwrap_move_iterator(miIntArray)), int*>::value == true), "unwrap_iterator failure"); + + eastl::generic_iterator<int*> giIntArray(intArray); + static_assert((eastl::is_same<decltype(eastl::unwrap_move_iterator(giIntArray)), eastl::generic_iterator<int*>>::value == true), "unwrap_iterator failure"); + + eastl::vector<int> intVector(1, 1); + eastl::move_iterator<eastl::vector<int>::iterator> miVectorInt(intVector.begin()); + eastl::vector<int>::iterator it = unwrap_move_iterator(miVectorInt); + EATEST_VERIFY(*it == 1); + static_assert((eastl::is_same<decltype(eastl::unwrap_move_iterator(miVectorInt)), eastl::vector<int>::iterator>::value == true), "unwrap_iterator failure"); } { @@ -468,6 +558,11 @@ int TestIterator() EATEST_VERIFY(dist == 3); } + { + // Regression test that ensure N3844 is working correctly. + static_assert(!eastl::is_detected<detect_iterator_traits_reference, int>::value, "detecting iterator_traits<int> should SFINAE gracefully."); + } + return nErrorCount; } diff --git a/EASTL/test/source/TestList.cpp b/EASTL/test/source/TestList.cpp index 8a5e057..001b79a 100644 --- a/EASTL/test/source/TestList.cpp +++ b/EASTL/test/source/TestList.cpp @@ -975,20 +975,115 @@ int TestList() { eastl::list<int> l = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - eastl::erase(l, 3); - eastl::erase(l, 5); - eastl::erase(l, 7); + auto numErased = eastl::erase(l, 3); + VERIFY(numErased == 1); + numErased = eastl::erase(l, 5); + VERIFY(numErased == 1); + numErased = eastl::erase(l, 7); + VERIFY(numErased == 1); VERIFY((l == eastl::list<int>{1, 2, 4, 6, 8, 9})); } { eastl::list<int> l = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - eastl::erase_if(l, [](auto i) { return i % 2 == 0; }); + auto numErased = eastl::erase_if(l, [](auto i) { return i % 2 == 0; }); VERIFY((l == eastl::list<int>{1, 3, 5, 7, 9})); + VERIFY(numErased == 4); } } + { // Test global operators + { + eastl::list<int> list1 = {0, 1, 2, 3, 4, 5}; + eastl::list<int> list2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + eastl::list<int> list3 = {5, 6, 7, 8}; + + VERIFY(list1 == list1); + VERIFY(!(list1 != list1)); + + VERIFY(list1 != list2); + VERIFY(list2 != list3); + VERIFY(list1 != list3); + + VERIFY(list1 < list2); + VERIFY(list1 <= list2); + + VERIFY(list2 > list1); + VERIFY(list2 >= list1); + + VERIFY(list3 > list1); + VERIFY(list3 > list2); + } + + // three way comparison operator +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + eastl::list<int> list1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + eastl::list<int> list2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // Verify equality between list1 and list2 + VERIFY((list1 <=> list2) == 0); + VERIFY(!((list1 <=> list2) != 0)); + VERIFY((list1 <=> list2) <= 0); + VERIFY((list1 <=> list2) >= 0); + VERIFY(!((list1 <=> list2) < 0)); + VERIFY(!((list1 <=> list2) > 0)); + + list1.push_back(100); // Make list1 less than list2. + list2.push_back(101); + + // Verify list1 < list2 + VERIFY(!((list1 <=> list2) == 0)); + VERIFY((list1 <=> list2) != 0); + VERIFY((list1 <=> list2) <= 0); + VERIFY(!((list1 <=> list2) >= 0)); + VERIFY(((list1 <=> list2) < 0)); + VERIFY(!((list1 <=> list2) > 0)); + + for (int i = 0; i < 3; i++) // Make the length of list2 less than list1 + list2.pop_back(); + + // Verify list2.size() < list1.size() and list2 is a subset of list1 + VERIFY(!((list1 <=> list2) == 0)); + VERIFY((list1 <=> list2) != 0); + VERIFY((list1 <=> list2) >= 0); + VERIFY(!((list1 <=> list2) <= 0)); + VERIFY(((list1 <=> list2) > 0)); + VERIFY(!((list1 <=> list2) < 0)); + } + + { + eastl::list<int> list1 = {1, 2, 3, 4, 5, 6, 7}; + eastl::list<int> list2 = {7, 6, 5, 4, 3, 2, 1}; + eastl::list<int> list3 = {1, 2, 3, 4}; + + struct weak_ordering_list + { + eastl::list<int> list; + inline std::weak_ordering operator<=>(const weak_ordering_list& b) const { return list <=> b.list; } + }; + + VERIFY(synth_three_way{}(weak_ordering_list{list1}, weak_ordering_list{list2}) == std::weak_ordering::less); + VERIFY(synth_three_way{}(weak_ordering_list{list3}, weak_ordering_list{list1}) == std::weak_ordering::less); + VERIFY(synth_three_way{}(weak_ordering_list{list2}, weak_ordering_list{list1}) == std::weak_ordering::greater); + VERIFY(synth_three_way{}(weak_ordering_list{list2}, weak_ordering_list{list3}) == std::weak_ordering::greater); + VERIFY(synth_three_way{}(weak_ordering_list{list1}, weak_ordering_list{list1}) == std::weak_ordering::equivalent); + + struct strong_ordering_list + { + eastl::list<int> list; + inline std::strong_ordering operator<=>(const strong_ordering_list& b) const { return list <=> b.list; } + }; + + VERIFY(synth_three_way{}(strong_ordering_list{list1}, strong_ordering_list{list2}) == std::strong_ordering::less); + VERIFY(synth_three_way{}(strong_ordering_list{list3}, strong_ordering_list{list1}) == std::strong_ordering::less); + VERIFY(synth_three_way{}(strong_ordering_list{list2}, strong_ordering_list{list1}) == std::strong_ordering::greater); + VERIFY(synth_three_way{}(strong_ordering_list{list2}, strong_ordering_list{list3}) == std::strong_ordering::greater); + VERIFY(synth_three_way{}(strong_ordering_list{list1}, strong_ordering_list{list1}) == std::strong_ordering::equal); + } +#endif + } return nErrorCount; } diff --git a/EASTL/test/source/TestMap.cpp b/EASTL/test/source/TestMap.cpp index e2eef2f..0df8c88 100644 --- a/EASTL/test/source/TestMap.cpp +++ b/EASTL/test/source/TestMap.cpp @@ -147,15 +147,23 @@ int TestMap() { typedef eastl::map<int, int> IntIntMap; IntIntMap map1; + map1[1] = 1; + map1[3] = 3; #if EASTL_EXCEPTIONS_ENABLED EATEST_VERIFY_THROW(map1.at(0)); + EATEST_VERIFY_THROW(map1.at(2)); + EATEST_VERIFY_THROW(map1.at(4)); #endif - map1[0]=1; + map1[0] = 1; #if EASTL_EXCEPTIONS_ENABLED EATEST_VERIFY_NOTHROW(map1.at(0)); + EATEST_VERIFY_NOTHROW(map1.at(1)); + EATEST_VERIFY_NOTHROW(map1.at(3)); #endif EATEST_VERIFY(map1.at(0) == 1); + EATEST_VERIFY(map1.at(1) == 1); + EATEST_VERIFY(map1.at(3) == 3); const IntIntMap map2; const IntIntMap map3(map1); @@ -223,16 +231,65 @@ int TestMap() { // Test erase_if eastl::map<int, int> m = {{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}; - eastl::erase_if(m, [](auto p) { return p.first % 2 == 0; }); + auto numErased = eastl::erase_if(m, [](auto p) { return p.first % 2 == 0; }); VERIFY((m == eastl::map<int, int>{{1, 1},{3, 3}})); + VERIFY(numErased == 3); } { // Test erase_if eastl::multimap<int, int> m = {{0, 0}, {0, 0}, {0, 0}, {1, 1}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {4, 4}, {4, 4}}; - eastl::erase_if(m, [](auto p) { return p.first % 2 == 0; }); - VERIFY((m == eastl::multimap<int, int>{{1, 1}, {1, 1}, {3, 3}})); + auto numErased = eastl::erase_if(m, [](auto p) { return p.first % 2 == 0; }); + VERIFY((m == eastl::multimap<int, int>{{1, 1}, {1, 1}, {3, 3}}));; + VERIFY(numErased == 7); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { // Test map <=> + eastl::map<int, int> m1 = {{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}; + eastl::map<int, int> m2 = {{4, 4}, {3, 3}, {2, 2}, {1, 1}, {0, 0}}; + eastl::map<int, int> m3 = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}}; + eastl::map<int, int> m4 = {{1, 0}, {3, 2}, {5, 4}, {7, 6}, {9, 8}}; + eastl::map<int, int> m5 = {{0, 1}, {2, 3}, {4, 5}}; + + VERIFY(m1 == m2); + VERIFY(m1 != m3); + VERIFY(m3 != m4); + VERIFY(m3 < m4); + VERIFY(m5 < m4); + VERIFY(m5 < m3); + + + VERIFY((m1 <=> m2) == 0); + VERIFY((m1 <=> m3) != 0); + VERIFY((m3 <=> m4) != 0); + VERIFY((m3 <=> m4) < 0); + VERIFY((m5 <=> m4) < 0); + VERIFY((m5 <=> m3) < 0); + } + + { // Test multimap <=> + eastl::multimap<int, int> m1 = {{0, 0}, {0, 0}, {1, 1}, {1, 1}, {2, 2}, {2, 2}, {3, 3}, {3, 3}, {4, 4}, {4, 4}}; + eastl::multimap<int, int> m2 = {{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {4, 4}, {3, 3}, {2, 2}, {1, 1}, {0, 0}}; + eastl::multimap<int, int> m3 = {{0, 1}, {2, 3}, {4, 5}, {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}}; + eastl::multimap<int, int> m4 = {{1, 0}, {3, 2}, {5, 4}, {1, 0}, {3, 2}, {5, 4}, {7, 6}, {9, 8}}; + eastl::multimap<int, int> m5 = {{10, 11}, {10, 11}}; + + VERIFY(m1 == m2); + VERIFY(m1 != m3); + VERIFY(m3 != m4); + VERIFY(m3 < m4); + VERIFY(m5 > m4); + VERIFY(m5 > m3); + + VERIFY((m1 <=> m2) == 0); + VERIFY((m1 <=> m3) != 0); + VERIFY((m3 <=> m4) != 0); + VERIFY((m3 <=> m4) < 0); + VERIFY((m5 <=> m4) > 0); + VERIFY((m5 <=> m3) > 0); + } +#endif + return nErrorCount; } diff --git a/EASTL/test/source/TestMap.h b/EASTL/test/source/TestMap.h index 09353cd..8d480cf 100644 --- a/EASTL/test/source/TestMap.h +++ b/EASTL/test/source/TestMap.h @@ -1248,11 +1248,18 @@ int TestMapCpp17() VERIFY(toMap.size() == 1); } + auto ctorCount = TestObject::sTOCtorCount; + { // verify duplicate not inserted auto result = toMap.try_emplace(7, mapped_type(7)); // test fwding to copy-ctor VERIFY(!result.second); VERIFY(result.first->second == mapped_type(7)); VERIFY(toMap.size() == 1); + + // we explicitly constructed an element for the parameter + // and one for the VERIFY check + ctorCount += 2; + VERIFY(ctorCount == TestObject::sTOCtorCount); } { // verify duplicate not inserted @@ -1261,6 +1268,9 @@ int TestMapCpp17() VERIFY(result->first == 7); VERIFY(result->second == mapped_type(7)); VERIFY(toMap.size() == 1); + // we explicitly constructed an element for the VERIFY check + ++ctorCount; + VERIFY(ctorCount == TestObject::sTOCtorCount); } { // verify duplicate not inserted @@ -1269,20 +1279,36 @@ int TestMapCpp17() VERIFY(result->first == 7); VERIFY(result->second == mapped_type(7)); VERIFY(toMap.size() == 1); + + // we explicitly constructed an element for the parameter + // and one for the VERIFY check + ctorCount += 2; + VERIFY(ctorCount == TestObject::sTOCtorCount); } { { - auto result = toMap.try_emplace(8, 8); + auto result = toMap.try_emplace(8, 8); + // emplacing a new value should call exactly one constructor, + // when the value is constructed in place inside the container. + ++ctorCount; VERIFY(result.second); VERIFY(result.first->second == mapped_type(8)); + // One more constructor for the temporary in the VERIFY + ++ctorCount; VERIFY(toMap.size() == 2); + VERIFY(ctorCount == TestObject::sTOCtorCount); } { - auto result = toMap.try_emplace(9, mapped_type(9)); + auto result = toMap.try_emplace(9, mapped_type(9)); VERIFY(result.second); VERIFY(result.first->second == mapped_type(9)); VERIFY(toMap.size() == 3); + // one more constructor for the temporary argument, + // one for moving it to the container, and one for the VERIFY + ctorCount += 3; + VERIFY(ctorCount == TestObject::sTOCtorCount); + } } } diff --git a/EASTL/test/source/TestMemory.cpp b/EASTL/test/source/TestMemory.cpp index 4e25738..77caf9f 100644 --- a/EASTL/test/source/TestMemory.cpp +++ b/EASTL/test/source/TestMemory.cpp @@ -133,6 +133,23 @@ eastl::late_constructed<LCTestObject, false, true> gLCTestObjectFalseTrue; eastl::late_constructed<LCTestObject, false, false> gLCTestObjectFalseFalse; eastl::late_constructed<LCTestObject, true, false> gLCTestObjectTrueFalse; +struct TypeWithPointerTraits {}; + +namespace eastl +{ + template <> + struct pointer_traits<TypeWithPointerTraits> + { + // Note: only parts of the traits we are interested to test are defined here. + static const int* to_address(TypeWithPointerTraits) + { + return &a; + } + + inline static constexpr int a = 42; + }; +} + /////////////////////////////////////////////////////////////////////////////// // TestMemory @@ -684,6 +701,33 @@ int TestMemory() } } + // to_address + { + // Normal pointers. + int a; + int* ptrA = &a; + EATEST_VERIFY(ptrA == to_address(ptrA)); + + // Smart pointer. + struct MockSmartPointer + { + const int* operator->() const + { + return &a; + } + + int a = 42; + }; + + MockSmartPointer sp; + EATEST_VERIFY(&sp.a == to_address(sp)); + + // Type with specialized pointer_traits. + TypeWithPointerTraits t; + const int* result = to_address(t); + EATEST_VERIFY(result != nullptr && *result == 42); + } + { // Test that align handles integral overflow correctly and returns NULL. void* ptr; diff --git a/EASTL/test/source/TestNumericLimits.cpp b/EASTL/test/source/TestNumericLimits.cpp index 440715b..1964442 100644 --- a/EASTL/test/source/TestNumericLimits.cpp +++ b/EASTL/test/source/TestNumericLimits.cpp @@ -64,6 +64,11 @@ int TestNumericLimits() EATEST_VERIFY(eastl::numeric_limits<wchar_t>::is_bounded); EATEST_VERIFY(eastl::numeric_limits<wchar_t>::max() != 0); + #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE + EATEST_VERIFY(eastl::numeric_limits<char8_t>::is_bounded); + EATEST_VERIFY(eastl::numeric_limits<char8_t>::max() != 0); + #endif + EATEST_VERIFY(eastl::numeric_limits<char16_t>::is_bounded); EATEST_VERIFY(eastl::numeric_limits<char16_t>::max() != 0); diff --git a/EASTL/test/source/TestOptional.cpp b/EASTL/test/source/TestOptional.cpp index b4934a7..36307ad 100644 --- a/EASTL/test/source/TestOptional.cpp +++ b/EASTL/test/source/TestOptional.cpp @@ -18,12 +18,17 @@ struct IntStruct int data; }; +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) +auto operator<=>(const IntStruct& lhs, const IntStruct& rhs) { return lhs.data <=> rhs.data; } +#else bool operator<(const IntStruct& lhs, const IntStruct& rhs) { return lhs.data < rhs.data; } +#endif bool operator==(const IntStruct& lhs, const IntStruct& rhs) { return lhs.data == rhs.data; } + ///////////////////////////////////////////////////////////////////////////// struct destructor_test { @@ -476,6 +481,43 @@ int TestOptional() VERIFY(o >= nullopt); } + #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + optional<IntStruct> o(in_place, 10); + optional<IntStruct> e; + + VERIFY((o <=> IntStruct(42)) < 0); + VERIFY((o <=> IntStruct(2)) >= 0); + VERIFY((o <=> IntStruct(10)) >= 0); + VERIFY((e <=> o) < 0); + VERIFY((e <=> IntStruct(10)) < 0); + + VERIFY((o <=> IntStruct(4)) > 0); + VERIFY(o <=> IntStruct(42) <= 0); + + VERIFY((o <=> IntStruct(4)) >= 0); + VERIFY((o <=> IntStruct(10)) >= 0); + VERIFY((IntStruct(4) <=> o) <= 0); + VERIFY((IntStruct(10) <=> o) <= 0); + + VERIFY((o <=> IntStruct(10)) == 0); + VERIFY((o->data <=> IntStruct(10).data) == 0); + + VERIFY((o <=> IntStruct(11)) != 0); + VERIFY((o->data <=> IntStruct(11).data) != 0); + + VERIFY((e <=> nullopt) == 0); + VERIFY((nullopt <=> e) == 0); + + VERIFY((o <=> nullopt) != 0); + VERIFY((nullopt <=> o) != 0); + VERIFY((nullopt <=> o) < 0); + VERIFY((o <=> nullopt) > 0); + VERIFY((nullopt <=> o) <= 0); + VERIFY((o <=> nullopt) >= 0); + } + #endif + // hash { { diff --git a/EASTL/test/source/TestSList.cpp b/EASTL/test/source/TestSList.cpp index d73f2bc..94a4d3a 100644 --- a/EASTL/test/source/TestSList.cpp +++ b/EASTL/test/source/TestSList.cpp @@ -795,36 +795,134 @@ int TestSList() { slist<int> l = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - eastl::erase(l, 5); + auto numErased = eastl::erase(l, 5); VERIFY((l == slist<int>{0, 1, 2, 3, 4, 6, 7, 8, 9})); + VERIFY(numErased == 1); - eastl::erase(l, 7); + numErased = eastl::erase(l, 7); VERIFY((l == slist<int>{0, 1, 2, 3, 4, 6, 8, 9})); + VERIFY(numErased == 1); - eastl::erase(l, 2); + numErased = eastl::erase(l, 2); VERIFY((l == slist<int>{0, 1, 3, 4, 6, 8, 9})); + VERIFY(numErased == 1); - eastl::erase(l, 0); + numErased = eastl::erase(l, 0); VERIFY((l == slist<int>{1, 3, 4, 6, 8, 9})); + VERIFY(numErased == 1); - eastl::erase(l, 4); + numErased = eastl::erase(l, 4); VERIFY((l == slist<int>{1, 3, 6, 8, 9})); + VERIFY(numErased == 1); } { slist<int> l = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - eastl::erase_if(l, [](auto e) { return e % 2 == 0; }); + auto numErased = eastl::erase_if(l, [](auto e) { return e % 2 == 0; }); VERIFY((l == slist<int>{1, 3, 5, 7, 9})); + VERIFY(numErased == 5); - eastl::erase_if(l, [](auto e) { return e == 5; }); + numErased = eastl::erase_if(l, [](auto e) { return e == 5; }); VERIFY((l == slist<int>{1, 3, 7, 9})); + VERIFY(numErased == 1); - eastl::erase_if(l, [](auto e) { return e % 3 == 0; }); + numErased = eastl::erase_if(l, [](auto e) { return e % 3 == 0; }); VERIFY((l == slist<int>{1, 7})); + VERIFY(numErased == 2); } } + { // Test global operators + { + slist<int> list1 = {0, 1, 2, 3, 4, 5}; + slist<int> list2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + slist<int> list3 = {5, 6, 7, 8}; + + VERIFY(list1 == list1); + VERIFY(!(list1 != list1)); + + VERIFY(list1 != list2); + VERIFY(list2 != list3); + VERIFY(list1 != list3); + + VERIFY(list1 < list2); + VERIFY(list1 <= list2); + + VERIFY(list2 > list1); + VERIFY(list2 >= list1); + + VERIFY(list3 > list1); + VERIFY(list3 > list2); + } + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + slist<int> list1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + slist<int> list2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + slist<int> list3 = {-1, 0, 1, 2, 3, 4, 5}; + + // Verify equality between list1 and list2 + VERIFY((list1 <=> list2) == 0); + VERIFY(!((list1 <=> list2) != 0)); + VERIFY((list1 <=> list2) <= 0); + VERIFY((list1 <=> list2) >= 0); + VERIFY(!((list1 <=> list2) < 0)); + VERIFY(!((list1 <=> list2) > 0)); + + list1.push_front(-2); // Make list1 less than list2. + list2.push_front(-1); + + // Verify list1 < list2 + VERIFY(!((list1 <=> list2) == 0)); + VERIFY((list1 <=> list2) != 0); + VERIFY((list1 <=> list2) <= 0); + VERIFY(!((list1 <=> list2) >= 0)); + VERIFY(((list1 <=> list2) < 0)); + VERIFY(!((list1 <=> list2) > 0)); + + + // Verify list3.size() < list2.size() and list3 is a subset of list2 + VERIFY(!((list3 <=> list2) == 0)); + VERIFY((list3 <=> list2) != 0); + VERIFY((list3 <=> list2) <= 0); + VERIFY(!((list3 <=> list2) >= 0)); + VERIFY(((list3 <=> list2) < 0)); + VERIFY(!((list3 <=> list2) > 0)); + } + + { + slist<int> list1 = {1, 2, 3, 4, 5, 6, 7}; + slist<int> list2 = {7, 6, 5, 4, 3, 2, 1}; + slist<int> list3 = {1, 2, 3, 4}; + + struct weak_ordering_slist + { + slist<int> slist; + inline std::weak_ordering operator<=>(const weak_ordering_slist& b) const { return slist <=> b.slist; } + }; + + VERIFY(synth_three_way{}(weak_ordering_slist{list1}, weak_ordering_slist{list2}) == std::weak_ordering::less); + VERIFY(synth_three_way{}(weak_ordering_slist{list3}, weak_ordering_slist{list1}) == std::weak_ordering::less); + VERIFY(synth_three_way{}(weak_ordering_slist{list2}, weak_ordering_slist{list1}) == std::weak_ordering::greater); + VERIFY(synth_three_way{}(weak_ordering_slist{list2}, weak_ordering_slist{list3}) == std::weak_ordering::greater); + VERIFY(synth_three_way{}(weak_ordering_slist{list1}, weak_ordering_slist{list1}) == std::weak_ordering::equivalent); + + struct strong_ordering_slist + { + slist<int> slist; + inline std::strong_ordering operator<=>(const strong_ordering_slist& b) const { return slist <=> b.slist; } + }; + + VERIFY(synth_three_way{}(strong_ordering_slist{list1}, strong_ordering_slist{list2}) == std::strong_ordering::less); + VERIFY(synth_three_way{}(strong_ordering_slist{list3}, strong_ordering_slist{list1}) == std::strong_ordering::less); + VERIFY(synth_three_way{}(strong_ordering_slist{list2}, strong_ordering_slist{list1}) == std::strong_ordering::greater); + VERIFY(synth_three_way{}(strong_ordering_slist{list2}, strong_ordering_slist{list3}) == std::strong_ordering::greater); + VERIFY(synth_three_way{}(strong_ordering_slist{list1}, strong_ordering_slist{list1}) == std::strong_ordering::equal); + } +#endif + } + return nErrorCount; } diff --git a/EASTL/test/source/TestSet.cpp b/EASTL/test/source/TestSet.cpp index 1adc12f..9a590c2 100644 --- a/EASTL/test/source/TestSet.cpp +++ b/EASTL/test/source/TestSet.cpp @@ -162,16 +162,60 @@ int TestSet() { // set erase_if tests set<int> s = {0, 1, 2, 3, 4}; - eastl::erase_if(s, [](auto i) { return i % 2 == 0;}); + auto numErased = eastl::erase_if(s, [](auto i) { return i % 2 == 0;}); VERIFY((s == set<int>{1,3})); + VERIFY(numErased == 3); } { // multiset erase_if tests multiset<int> s = {0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3, 3, 4}; - eastl::erase_if(s, [](auto i) { return i % 2 == 0;}); + auto numErased = eastl::erase_if(s, [](auto i) { return i % 2 == 0;}); VERIFY((s == multiset<int>{1, 1, 1, 3, 3, 3})); + VERIFY(numErased == 7); } +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { // Test set <=> + set<int> s1 = {0, 1, 2, 3, 4}; + set<int> s2 = {4, 3, 2, 1, 0}; + set<int> s3 = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + set<int> s4 = {1, 2, 3, 4, 5, 6}; + set<int> s5 = {9}; + + VERIFY(s1 == s2); + VERIFY(s1 != s3); + VERIFY(s3 > s4); + VERIFY(s5 > s4); + VERIFY(s5 > s3); + + VERIFY((s1 <=> s2) == 0); + VERIFY((s1 <=> s3) != 0); + VERIFY((s3 <=> s4) > 0); + VERIFY((s5 <=> s4) > 0); + VERIFY((s5 <=> s3) > 0); + } + + { // Test multiset <=> + multiset<int> s1 = {0, 0, 0, 1, 1, 2, 3, 3, 4}; + multiset<int> s2 = {4, 3, 3, 2, 1, 1, 0, 0, 0}; + multiset<int> s3 = {1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9}; + multiset<int> s4 = {1, 1, 2, 2, 3, 4, 5, 5, 6}; + multiset<int> s5 = {9}; + + VERIFY(s1 == s2); + VERIFY(s1 != s3); + VERIFY(s3 > s4); + VERIFY(s5 > s4); + VERIFY(s5 > s3); + + VERIFY((s1 <=> s2) == 0); + VERIFY((s1 <=> s3) != 0); + VERIFY((s3 <=> s4) > 0); + VERIFY((s5 <=> s4) > 0); + VERIFY((s5 <=> s3) > 0); + } +#endif + { // user reported regression: ensure container elements are NOT // moved from during the eastl::set construction process. diff --git a/EASTL/test/source/TestSmartPtr.cpp b/EASTL/test/source/TestSmartPtr.cpp index dc94b96..8052392 100644 --- a/EASTL/test/source/TestSmartPtr.cpp +++ b/EASTL/test/source/TestSmartPtr.cpp @@ -8,6 +8,7 @@ #include "GetTypeName.h" #include <EAStdC/EAString.h> #include <EAStdC/EAStopwatch.h> +#include <EASTL/atomic.h> #include <EASTL/core_allocator_adapter.h> #include <EASTL/core_allocator.h> #include <EASTL/intrusive_ptr.h> @@ -800,6 +801,38 @@ static int Test_unique_ptr() } #endif + #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + unique_ptr<int> pT1(new int(5)); + unique_ptr<int> pT2(new int(10)); + unique_ptr<int> pT3(new int(0)); + + EATEST_VERIFY((pT1 <=> pT2) != 0); + EATEST_VERIFY((pT2 <=> pT1) != 0); + + EATEST_VERIFY((pT1 <=> pT2) < 0); + EATEST_VERIFY((pT1 <=> pT2) <= 0); + EATEST_VERIFY((pT2 <=> pT1) > 0); + EATEST_VERIFY((pT2 <=> pT1) >= 0); + + EATEST_VERIFY((pT3 <=> pT1) < 0); + EATEST_VERIFY((pT3 <=> pT2) < 0); + EATEST_VERIFY((pT1 <=> pT3) > 0); + EATEST_VERIFY((pT2 <=> pT3) > 0); + + unique_ptr<A> pT4(new A(5)); + unique_ptr<A> pT5(new A(10)); + + EATEST_VERIFY((pT4 <=> pT5) != 0); + EATEST_VERIFY((pT5 <=> pT4) != 0); + + EATEST_VERIFY((pT4 <=> pT5) < 0); + EATEST_VERIFY((pT4 <=> pT5) <= 0); + EATEST_VERIFY((pT5 <=> pT4) > 0); + EATEST_VERIFY((pT5 <=> pT4) >= 0); + } + #endif + // ToDo: Test move assignment between two convertible types with an is_assignable deleter_type //{ // struct Base {}; @@ -1351,7 +1384,7 @@ static int Test_shared_ptr() { EA::Thread::ThreadParameters mThreadParams; EA::Thread::Thread mThread; - volatile bool mbShouldContinue; + eastl::atomic<bool> mbShouldContinue; int mnErrorCount; eastl::shared_ptr<TestObject>* mpSPTO; eastl::weak_ptr<TestObject>* mpWPTO; @@ -1364,7 +1397,7 @@ static int Test_shared_ptr() { int& nErrorCount = mnErrorCount; // declare nErrorCount so that EATEST_VERIFY can work, as it depends on it being declared. - while(mbShouldContinue) + while(mbShouldContinue.load(eastl::memory_order_relaxed)) { EA::UnitTest::ThreadSleepRandom(1, 10); @@ -1419,7 +1452,7 @@ static int Test_shared_ptr_thread() EA::UnitTest::ThreadSleep(2000); for(size_t i = 0; i < EAArrayCount(thread); i++) - thread[i].mbShouldContinue = false; + thread[i].mbShouldContinue.store(false, eastl::memory_order_relaxed); for(size_t i = 0; i < EAArrayCount(thread); i++) { diff --git a/EASTL/test/source/TestSort.cpp b/EASTL/test/source/TestSort.cpp index 2d0116f..114a73b 100644 --- a/EASTL/test/source/TestSort.cpp +++ b/EASTL/test/source/TestSort.cpp @@ -177,8 +177,22 @@ namespace eastl return x; } }; + + struct TestNoLessOperator + { + int i {}; + }; } // namespace Internal + template <> + struct less<Internal::TestNoLessOperator> + { + bool operator()(const Internal::TestNoLessOperator& lhs, const Internal::TestNoLessOperator& rhs) const noexcept + { + return lhs.i < rhs.i; + } + }; + } // namespace eastl int TestSort() @@ -630,6 +644,13 @@ int TestSort() radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>>(begin(input), end(input), buffer); EATEST_VERIFY(is_sorted(begin(input), end(input))); } + { + // Test case for bug where the last histogram bucket was not being cleared to zero + uint32_t input[] = { 0xff00, 0xff }; + uint32_t buffer[EAArrayCount(input)]; + radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>>(begin(input), end(input), buffer); + EATEST_VERIFY(is_sorted(begin(input), end(input))); + } } { @@ -793,7 +814,7 @@ int TestSort() } { - // EATEST_VERIFY deque sorting can compile. + // Test checking that deque sorting can compile. deque<int> intDeque; vector<int> intVector; @@ -801,6 +822,25 @@ int TestSort() stable_sort(intVector.begin(), intVector.end()); } + { + // Test checking that sorting containers having elements of a type without an operator< compiles correctly + + vector<TestNoLessOperator> noLessVector; + + stable_sort(noLessVector.begin(), noLessVector.end()); + bubble_sort(noLessVector.begin(), noLessVector.end()); + shaker_sort(noLessVector.begin(), noLessVector.end()); + insertion_sort(noLessVector.begin(), noLessVector.end()); + selection_sort(noLessVector.begin(), noLessVector.end()); + shell_sort(noLessVector.begin(), noLessVector.end()); + comb_sort(noLessVector.begin(), noLessVector.end()); + heap_sort(noLessVector.begin(), noLessVector.end()); + merge_sort(noLessVector.begin(), noLessVector.end(), *get_default_allocator(nullptr)); + quick_sort(noLessVector.begin(), noLessVector.end()); + + vector<TestNoLessOperator> buffer; + tim_sort_buffer(noLessVector.begin(), noLessVector.end(), buffer.data()); +} { // Test sorting of a container of pointers to objects as opposed to a container of objects themselves. diff --git a/EASTL/test/source/TestString.inl b/EASTL/test/source/TestString.inl index 08fb924..3a59e68 100644 --- a/EASTL/test/source/TestString.inl +++ b/EASTL/test/source/TestString.inl @@ -2024,19 +2024,25 @@ int TEST_STRING_NAME() // test eastl::erase { StringType str(LITERAL("abcdefghijklmnopqrstuvwxyz")); - eastl::erase(str, LITERAL('a')); - eastl::erase(str, LITERAL('f')); - eastl::erase(str, LITERAL('l')); - eastl::erase(str, LITERAL('w')); - eastl::erase(str, LITERAL('y')); + auto numErased = eastl::erase(str, LITERAL('a')); + VERIFY(numErased == 1); + numErased = eastl::erase(str, LITERAL('f')); + VERIFY(numErased == 1); + numErased = eastl::erase(str, LITERAL('l')); + VERIFY(numErased == 1); + numErased = eastl::erase(str, LITERAL('w')); + VERIFY(numErased == 1); + numErased = eastl::erase(str, LITERAL('y')); + VERIFY(numErased == 1); VERIFY(str == LITERAL("bcdeghijkmnopqrstuvxz")); } // test eastl::erase_if { StringType str(LITERAL("abcdefghijklmnopqrstuvwxyz")); - eastl::erase_if(str, [](auto c) { return c == LITERAL('a') || c == LITERAL('v'); }); + auto numErased = eastl::erase_if(str, [](auto c) { return c == LITERAL('a') || c == LITERAL('v'); }); VERIFY(str == LITERAL("bcdefghijklmnopqrstuwxyz")); + VERIFY(numErased == 2); } // template<> struct hash<eastl::string>; @@ -2064,6 +2070,28 @@ int TEST_STRING_NAME() VERIFY(LocalHash(sw2) == LocalHash(sw3)); } + // test <=> operator + #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + StringType sw1(LITERAL("Test String ")); + StringType sw2(LITERAL("Test String 1")); + StringType sw3(LITERAL("Test String 2")); + StringType sw4(LITERAL("abcdef")); + + VERIFY((sw1 <=> sw2) != 0); + VERIFY((sw1 <=> sw3) != 0); + VERIFY((sw2 <=> sw3) != 0); + VERIFY((sw1 <=> sw2) < 0); + VERIFY((sw1 <=> sw3) < 0); + VERIFY((sw2 <=> sw2) == 0); + VERIFY((sw2 <=> sw3) < 0); + VERIFY((sw2 <=> sw4) < 0); + VERIFY((sw4 <=> sw2) > 0); + VERIFY((sw4 <=> sw3) > 0); + VERIFY((sw3 <=> sw2) > 0); + } + #endif + return nErrorCount; } diff --git a/EASTL/test/source/TestStringView.cpp b/EASTL/test/source/TestStringView.cpp index 835488c..23e6e51 100644 --- a/EASTL/test/source/TestStringView.cpp +++ b/EASTL/test/source/TestStringView.cpp @@ -5,6 +5,7 @@ #include "EASTLTest.h" #include <EABase/eabase.h> #include <EASTL/numeric_limits.h> +#include <EASTL/string.h> #include <EASTL/string_view.h> // Verify char8_t support is present if the test build requested it. @@ -78,12 +79,18 @@ int TestStringView() static_assert(eastl::is_same_v<decltype(U"abcdef"_sv), eastl::u32string_view>, "string_view literal type mismatch"); static_assert(eastl::is_same_v<decltype(L"abcdef"_sv), eastl::wstring_view>, "string_view literal type mismatch"); - // TODO: Need to resolve this. Not sure why on Clang the user literal 'operator ""sv' can't be found. - // VERIFY("cplusplus"sv.compare("cplusplus") == 0); - // VERIFY(L"cplusplus"sv.compare(L"cplusplus") == 0); - // VERIFY(u"cplusplus"sv.compare(u"cplusplus") == 0); - // VERIFY(U"cplusplus"sv.compare(U"cplusplus") == 0); - // VERIFY(u8"cplusplus"sv.compare(u8"cplusplus") == 0); + + VERIFY("cplusplus"sv.compare("cplusplus") == 0); + VERIFY(L"cplusplus"sv.compare(L"cplusplus") == 0); + VERIFY(u"cplusplus"sv.compare(u"cplusplus") == 0); + VERIFY(U"cplusplus"sv.compare(U"cplusplus") == 0); + VERIFY(u8"cplusplus"sv.compare(u8"cplusplus") == 0); + + static_assert(eastl::is_same_v<decltype("abcdef"sv), eastl::string_view>, "string_view literal type mismatch"); + static_assert(eastl::is_same_v<decltype(u8"abcdef"sv), eastl::u8string_view>, "string_view literal type mismatch"); + static_assert(eastl::is_same_v<decltype(u"abcdef"sv), eastl::u16string_view>, "string_view literal type mismatch"); + static_assert(eastl::is_same_v<decltype(U"abcdef"sv), eastl::u32string_view>, "string_view literal type mismatch"); + static_assert(eastl::is_same_v<decltype(L"abcdef"sv), eastl::wstring_view>, "string_view literal type mismatch"); } #endif diff --git a/EASTL/test/source/TestStringView.inl b/EASTL/test/source/TestStringView.inl index 14472fb..cd4214e 100644 --- a/EASTL/test/source/TestStringView.inl +++ b/EASTL/test/source/TestStringView.inl @@ -5,6 +5,8 @@ template<typename StringViewT> int TEST_STRING_NAME() { + using StringT = eastl::basic_string<typename StringViewT::value_type>; + int nErrorCount = 0; { // EA_CONSTEXPR basic_string_view() @@ -476,6 +478,84 @@ int TEST_STRING_NAME() VERIFY(sw1 <= sw2); VERIFY(sw2 > sw1); VERIFY(sw2 >= sw1); + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + VERIFY((sw1 <=> StringViewT(LITERAL("AAAAABBBBBCCCDDDDDEEEEEFFFGGH"))) == 0); + VERIFY((sw1 <=> StringViewT(LITERAL("abcdefghijklmnopqrstuvwxyz"))) != 0); + VERIFY((sw1 <=> sw2) < 0); + VERIFY((sw1 <=> sw2) <= 0); + VERIFY((sw2 <=> sw1) > 0); + VERIFY((sw2 <=> sw1) >= 0); +#endif + } + + { + auto s = LITERAL("Hello, World"); + StringViewT sv(s); + + VERIFY(s == sv); + VERIFY(sv == s); + + VERIFY(s <= sv); + VERIFY(sv <= s); + VERIFY(s >= sv); + VERIFY(sv >= s); + VERIFY(!(s != sv)); + VERIFY(!(sv != s)); + VERIFY(!(s < sv)); + VERIFY(!(sv < s)); + VERIFY(!(s > sv)); + VERIFY(!(sv > s)); + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + VERIFY((s <=> sv) == 0); + VERIFY((sv <=> s) == 0); + + VERIFY((s <=> sv) <= 0); + VERIFY((sv <=> s) <= 0); + VERIFY((s <=> sv) >= 0); + VERIFY((sv <=> s) >= 0); + VERIFY(!((s <=> sv) != 0)); + VERIFY(!((sv <=> s) != 0)); + VERIFY(!((s <=> sv) > 0)); + VERIFY(!((sv <=> s) < 0)); +#endif + } + + // Regression comparison operators should work between basic_string_view and basic_string. + // The idea is that type_identity_t on some overloads will force basic_string::operator basic_string_view() to kick in. + { + StringT s(LITERAL("Hello, Stockholm")); + StringViewT sv(s); + + VERIFY(s == sv); + VERIFY(sv == s); + + // All the operators bellow used to not work. + VERIFY(s <= sv); + VERIFY(sv <= s); + VERIFY(s >= sv); + VERIFY(sv >= s); + VERIFY(!(s != sv)); + VERIFY(!(sv != s)); + VERIFY(!(s < sv)); + VERIFY(!(sv < s)); + VERIFY(!(s > sv)); + VERIFY(!(sv > s)); + +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + VERIFY((s <=> sv) == 0); + VERIFY((sv <=> s) == 0); + + VERIFY((s <=> sv) <= 0); + VERIFY((sv <=> s) <= 0); + VERIFY((s <=> sv) >= 0); + VERIFY((sv <=> s) >= 0); + VERIFY(!((s <=> sv) != 0)); + VERIFY(!((sv <=> s) != 0)); + VERIFY(!((s <=> sv) > 0)); + VERIFY(!((sv <=> s) < 0)); +#endif } // template<> struct hash<std::string_view>; diff --git a/EASTL/test/source/TestTuple.cpp b/EASTL/test/source/TestTuple.cpp index 8c1b48a..6a7647e 100644 --- a/EASTL/test/source/TestTuple.cpp +++ b/EASTL/test/source/TestTuple.cpp @@ -227,11 +227,27 @@ int TestTuple() EATEST_VERIFY(aTuple != aDefaultInitTuple); EATEST_VERIFY(aDefaultInitTuple < aTuple); + #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + EATEST_VERIFY((aTuple <=> anotherTuple) == 0); + EATEST_VERIFY((aTuple <=> anotherTuple) >= 0); + EATEST_VERIFY((anotherTuple <=> aTuple) >= 0); + EATEST_VERIFY((aTuple <=> aDefaultInitTuple) != 0); + EATEST_VERIFY((aDefaultInitTuple <=> aTuple) < 0); + #endif + tuple<int, int, int> lesserTuple(1, 2, 3); tuple<int, int, int> greaterTuple(1, 2, 4); EATEST_VERIFY(lesserTuple < greaterTuple && !(greaterTuple < lesserTuple) && greaterTuple > lesserTuple && !(lesserTuple > greaterTuple)); + #if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + EATEST_VERIFY((lesserTuple <=> greaterTuple) != 0); + EATEST_VERIFY((lesserTuple <=> greaterTuple) < 0); + EATEST_VERIFY((lesserTuple <=> greaterTuple) <= 0); + EATEST_VERIFY((greaterTuple <=> lesserTuple) > 0); + EATEST_VERIFY((greaterTuple <=> lesserTuple) >= 0); + #endif + tuple<int, float, TestObject> valTup(2, 2.0f, TestObject(2)); tuple<int&, float&, TestObject&> refTup(valTup); tuple<const int&, const float&, const TestObject&> constRefTup(valTup); @@ -272,6 +288,20 @@ int TestTuple() } { + // Test construction of tuple containing r-value references + int x = 42; + TestObject object{1337}; + + tuple<int&&, TestObject&&> aTupleWithRValueReference(eastl::move(x), eastl::move(object)); + static_assert(is_same<decltype(get<0>(aTupleWithRValueReference)), int&>::value, "wrong return type for get when using r-value reference."); + static_assert(is_same<decltype(get<1>(aTupleWithRValueReference)), TestObject&>::value, "wrong return type for get when using r-value reference."); + EATEST_VERIFY(get<0>(aTupleWithRValueReference) == 42); + EATEST_VERIFY(get<1>(aTupleWithRValueReference).mX == 1337); + + static_assert(!is_constructible<decltype(aTupleWithRValueReference), int&, TestObject&>::value, "it shouldn't be possible to assign r-value references with l-values."); + } + + { // Tuple helpers // make_tuple @@ -481,6 +511,27 @@ int TestTuple() #endif } + // Compilation test to make sure that we can handle reference to forward-declared types + { + struct ForwardDeclared; + + auto fill_tuple = [](ForwardDeclared& f) { + eastl::tuple<ForwardDeclared&, const ForwardDeclared&> t{f, f}; + return t; + }; + + struct ForwardDeclared + { + int x; + }; + + ForwardDeclared f{666}; + auto t = fill_tuple(f); + + EATEST_VERIFY(get<0>(t).x == 666); + EATEST_VERIFY(get<1>(t).x == 666); + } + #ifndef EA_COMPILER_NO_STRUCTURED_BINDING // tuple structured bindings test { diff --git a/EASTL/test/source/TestTypeTraits.cpp b/EASTL/test/source/TestTypeTraits.cpp index 0353be9..2670e24 100644 --- a/EASTL/test/source/TestTypeTraits.cpp +++ b/EASTL/test/source/TestTypeTraits.cpp @@ -173,15 +173,16 @@ struct NonPod2 virtual void Function(){} }; -#if EASTL_VARIABLE_TEMPLATES_ENABLED - struct HasIncrementOperator { HasIncrementOperator& operator++() { return *this; } }; +struct HasIncrementOperator { HasIncrementOperator& operator++() { return *this; } }; - template<typename, typename = eastl::void_t<>> - struct has_increment_operator : eastl::false_type {}; +template <class T> +using has_increment_operator_detection = decltype(++eastl::declval<T>()); - template <typename T> - struct has_increment_operator<T, eastl::void_t<decltype(++eastl::declval<T>())>> : eastl::true_type {}; -#endif +template<typename, typename = eastl::void_t<>> +struct has_increment_operator_using_void_t : eastl::false_type {}; + +template <typename T> +struct has_increment_operator_using_void_t<T, eastl::void_t<has_increment_operator_detection<T>>> : eastl::true_type {}; // We use this for the is_copy_constructible test in order to verify that @@ -359,13 +360,19 @@ struct NonPolymorphic1 void Function(){} }; +// Disable the following warning: +// warning: ‘struct Abstract’ has virtual functions and accessible non-virtual destructor [-Wnon-virtual-dtor] +// We explicitly want this class not to have a virtual destructor to test our type traits. +EA_DISABLE_VC_WARNING(4265) +EA_DISABLE_CLANG_WARNING(-Wnon-virtual-dtor) +EA_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) struct Abstract { - #if defined(EA_COMPILER_GNUC) // GCC warns about this, so we include it for this class, even though for this compiler it partly defeats the purpose of its usage. - virtual ~Abstract(){} - #endif virtual void Function() = 0; }; +EA_RESTORE_GCC_WARNING() +EA_RESTORE_CLANG_WARNING() +EA_RESTORE_VC_WARNING() struct AbstractWithDtor { @@ -542,6 +549,7 @@ int TestTypeTraits() EATEST_VERIFY(GetType(is_integral<float>()) == false); static_assert(is_integral<bool>::value, "is_integral failure"); + static_assert(is_integral<char8_t>::value, "is_integral failure"); static_assert(is_integral<char16_t>::value, "is_integral failure"); static_assert(is_integral<char32_t>::value, "is_integral failure"); static_assert(is_integral<char>::value, "is_integral failure"); @@ -616,6 +624,42 @@ int TestTypeTraits() EATEST_VERIFY(GetType(is_array<uint32_t*>()) == false); + //is_bounded_array + static_assert(is_bounded_array<Array>::value == true, "is_bounded_array failure"); + EATEST_VERIFY(GetType(is_bounded_array<Array>()) == true); + + static_assert(is_bounded_array<ArrayConst>::value == true, "is_bounded_array failure"); + EATEST_VERIFY(GetType(is_bounded_array<ArrayConst>()) == true); + + static_assert(is_bounded_array<int>::value == false, "is_bounded_array failure"); + static_assert(is_bounded_array<int[32]>::value == true, "is_bounded_array failure"); + static_assert(is_bounded_array<int[]>::value == false, "is_bounded_array failure"); + + static_assert(is_bounded_array<uint32_t>::value == false, "is_bounded_array failure"); + EATEST_VERIFY(GetType(is_bounded_array<uint32_t>()) == false); + + static_assert(is_bounded_array<uint32_t*>::value == false, "is_bounded_array failure"); + EATEST_VERIFY(GetType(is_bounded_array<uint32_t*>()) == false); + + + //is_unbounded_array + static_assert(is_unbounded_array<Array>::value == false, "is_unbounded_array failure"); + EATEST_VERIFY(GetType(is_unbounded_array<Array>()) == false); + + static_assert(is_unbounded_array<ArrayConst>::value == false, "is_unbounded_array failure"); + EATEST_VERIFY(GetType(is_unbounded_array<ArrayConst>()) == false); + + static_assert(is_unbounded_array<int>::value == false, "is_unbounded_array failure"); + static_assert(is_unbounded_array<int[32]>::value == false, "is_unbounded_array failure"); + static_assert(is_unbounded_array<int[]>::value == true, "is_unbounded_array failure"); + + static_assert(is_unbounded_array<uint32_t>::value == false, "is_unbounded_array failure"); + EATEST_VERIFY(GetType(is_unbounded_array<uint32_t>()) == false); + + static_assert(is_unbounded_array<uint32_t*>::value == false, "is_unbounded_array failure"); + EATEST_VERIFY(GetType(is_unbounded_array<uint32_t*>()) == false); + + // is_reference static_assert(is_reference<Class&>::value == true, "is_reference failure"); EATEST_VERIFY(GetType(is_reference<Class&>()) == true); @@ -640,6 +684,10 @@ int TestTypeTraits() static_assert(is_member_function_pointer<int>::value == false, "is_member_function_pointer failure"); static_assert(is_member_function_pointer<int(Class::*)>::value == false, "is_member_function_pointer failure"); static_assert(is_member_function_pointer<int(Class::*)()>::value == true, "is_member_function_pointer failure"); + static_assert(is_member_function_pointer<int(Class::*)(...)>::value == true, "is_member_function_pointer failure"); + static_assert(is_member_function_pointer<int(Class::*)() noexcept>::value == true, "is_member_function_pointer failure"); + static_assert(is_member_function_pointer<int(Class::*)() &>::value == true, "is_member_function_pointer failure"); + static_assert(is_member_function_pointer<int(Class::*)() &&>::value == true, "is_member_function_pointer failure"); // is_member_object_pointer @@ -652,6 +700,9 @@ int TestTypeTraits() static_assert(is_member_pointer<int>::value == false, "is_member_pointer failure"); static_assert(is_member_pointer<int(Class::*)>::value == true, "is_member_pointer failure"); static_assert(is_member_pointer<int(Class::*)()>::value == true, "is_member_pointer failure"); + static_assert(is_member_pointer<int(Class::* const)>::value == true, "is_member_pointer failure"); + static_assert(is_member_pointer<int(Class::* volatile)>::value == true, "is_member_pointer failure"); + static_assert(is_member_pointer<int(Class::* const volatile)>::value == true, "is_member_pointer failure"); // is_pointer @@ -719,7 +770,7 @@ int TestTypeTraits() // is_function static_assert(is_function<void>::value == false, "is_function failure"); static_assert(is_function<FunctionVoidVoid>::value == true, "is_function failure"); - static_assert(is_function<FunctionVoidVoid&>::value == false, "is_function failure"); + static_assert(is_function<FunctionVoidVoid&>::value == false, "is_function failure"); static_assert(is_function<FunctionIntVoid>::value == true, "is_function failure"); static_assert(is_function<FunctionIntFloat>::value == true, "is_function failure"); static_assert(is_function<FunctionVoidVoidPtr>::value == false, "is_function failure"); @@ -731,6 +782,16 @@ int TestTypeTraits() // typedef int PrintfConst(const char*, ...) const; static_assert(is_function<int (const char*, ...)>::value == true, "is_function failure"); // This is the signature of printf. #endif + + static_assert(is_function<int (float)>::value == true, "is_function failure"); + static_assert(is_function<int (float) const>::value == true, "is_function failure"); + static_assert(is_function<int(float) volatile>::value == true, "is_function failure"); + static_assert(is_function<int(float) const volatile>::value == true, "is_function failure"); + static_assert(is_function<int(float)&>::value == true, "is_function failure"); + static_assert(is_function<int(float)&&>::value == true, "is_function failure"); + static_assert(is_function<int(float) noexcept>::value == true, "is_function failure"); + static_assert(is_function<FunctionIntFloat &>::value == false, "is_function failure"); // reference to function, not a l-value reference qualified function + static_assert(is_function<FunctionIntFloat &&>::value == false, "is_function failure"); static_assert(is_function_v<void> == false, "is_function failure"); static_assert(is_function_v<FunctionVoidVoid> == true, "is_function failure"); @@ -820,6 +881,8 @@ int TestTypeTraits() static_assert(is_const<ConstVolatileIntReference>::value == false, "is_const failure"); // Note here that the int is const, not the reference to the int. EATEST_VERIFY(GetType(is_const<ConstVolatileIntReference>()) == false); + static_assert(is_const<void() const>::value == false, "is_const failure"); + EATEST_VERIFY(GetType(is_const<void() const>()) == false); // is_volatile static_assert(is_volatile<Int>::value == false, "is_volatile failure"); @@ -843,6 +906,9 @@ int TestTypeTraits() static_assert(is_volatile<ConstVolatileIntReference>::value == false, "is_volatile failure"); // Note here that the int is volatile, not the reference to the int. EATEST_VERIFY(GetType(is_volatile<ConstVolatileIntReference>()) == false); + static_assert(is_volatile<void() const>::value == false, "is_volatile failure"); + EATEST_VERIFY(GetType(is_volatile<void() const>()) == false); + // underlying_type and to_underlying #if EASTL_TYPE_TRAIT_underlying_type_CONFORMANCE && !defined(EA_COMPILER_NO_STRONGLY_TYPED_ENUMS) // If we can execute this test... @@ -1067,7 +1133,24 @@ int TestTypeTraits() static_assert(is_signed<double>::value == true, "is_signed failure "); static_assert(is_signed_v<double> == true, "is_signed failure "); EATEST_VERIFY(GetType(is_signed<double>()) == true); - + + static_assert(is_signed<char16_t>::value == false, "is_signed failure "); + static_assert(is_signed_v<char16_t> == false, "is_signed failure "); + EATEST_VERIFY(GetType(is_signed<char16_t>()) == false); + + static_assert(is_signed<char32_t>::value == false, "is_signed failure "); + static_assert(is_signed_v<char32_t> == false, "is_signed failure "); + EATEST_VERIFY(GetType(is_signed<char32_t>()) == false); + +#if EASTL_GCC_STYLE_INT128_SUPPORTED + static_assert(is_signed<__int128_t>::value == true, "is_signed failure "); + static_assert(is_signed_v<__int128_t> == true, "is_signed failure "); + EATEST_VERIFY(GetType(is_signed<__int128_t>()) == true); + + static_assert(is_signed<__uint128_t>::value == false, "is_signed failure "); + static_assert(is_signed_v<__uint128_t> == false, "is_signed failure "); + EATEST_VERIFY(GetType(is_signed<__uint128_t>()) == false); +#endif // is_unsigned static_assert(is_unsigned<unsigned int>::value == true, "is_unsigned failure "); @@ -1082,9 +1165,9 @@ int TestTypeTraits() static_assert(is_unsigned_v<int32_t> == false, "is_unsigned failure "); EATEST_VERIFY(GetType(is_unsigned<int32_t>()) == false); - static_assert(is_unsigned<bool>::value == false, "is_unsigned failure "); - static_assert(is_unsigned_v<bool> == false, "is_unsigned failure "); - EATEST_VERIFY(GetType(is_unsigned<bool>()) == false); + static_assert(is_unsigned<bool>::value == true, "is_unsigned failure "); + static_assert(is_unsigned_v<bool> == true, "is_unsigned failure "); + EATEST_VERIFY(GetType(is_unsigned<bool>()) == true); static_assert(is_unsigned<float>::value == false, "is_unsigned failure "); static_assert(is_unsigned_v<float> == false, "is_unsigned failure "); @@ -1093,6 +1176,24 @@ int TestTypeTraits() static_assert(is_unsigned<double>::value == false, "is_unsigned failure "); static_assert(is_unsigned_v<double> == false, "is_unsigned failure "); EATEST_VERIFY(GetType(is_unsigned<double>()) == false); + + static_assert(is_unsigned<char16_t>::value == true, "is_unsigned failure "); + static_assert(is_unsigned_v<char16_t> == true, "is_unsigned failure "); + EATEST_VERIFY(GetType(is_unsigned<char16_t>()) == true); + + static_assert(is_unsigned<char32_t>::value == true, "is_unsigned failure "); + static_assert(is_unsigned_v<char32_t> == true, "is_unsigned failure "); + EATEST_VERIFY(GetType(is_unsigned<char32_t>()) == true); + +#if EASTL_GCC_STYLE_INT128_SUPPORTED + static_assert(is_unsigned<__int128_t>::value == false, "is_unsigned failure "); + static_assert(is_unsigned_v<__int128_t> == false, "is_unsigned failure "); + EATEST_VERIFY(GetType(is_unsigned<__int128_t>()) == false); + + static_assert(is_unsigned<__uint128_t>::value == true, "is_unsigned failure "); + static_assert(is_unsigned_v<__uint128_t> == true, "is_unsigned failure "); + EATEST_VERIFY(GetType(is_unsigned<__uint128_t>()) == true); +#endif // is_lvalue_reference @@ -1252,6 +1353,7 @@ int TestTypeTraits() // is_trivially_copyable static_assert(is_trivially_copyable<void>::value == false, "is_trivially_copyable failure"); + EATEST_VERIFY(GetType(is_trivially_copyable<void>()) == false); static_assert(is_trivially_copyable<int>::value == true, "is_trivially_copyable failure"); static_assert(is_trivially_copyable<int*>::value == true, "is_trivially_copyable failure"); static_assert(is_trivially_copyable<int[]>::value == true, "is_trivially_copyable failure"); @@ -1395,14 +1497,16 @@ int TestTypeTraits() // is_destructible static_assert(is_destructible<int>::value == true, "is_destructible failure"); + static_assert(is_destructible<int&>::value == true, "is_destructible failure"); + static_assert(is_destructible<int&&>::value == true, "is_destructible failure"); static_assert(is_destructible<char>::value == true, "is_destructible failure"); static_assert(is_destructible<char*>::value == true, "is_destructible failure"); static_assert(is_destructible<PodA>::value == true, "is_destructible failure"); static_assert(is_destructible<void>::value == false, "is_destructible failure"); static_assert(is_destructible<int[3]>::value == true, "is_destructible failure"); static_assert(is_destructible<int[]>::value == false, "is_destructible failure"); // You can't call operator delete on this class. - static_assert(is_destructible<Abstract>::value == false, "is_destructible failure"); // You can't call operator delete on this class. - static_assert(is_destructible<AbstractWithDtor>::value == false, "is_destructible failure"); // You can't call operator delete on this class. + static_assert(is_destructible<Abstract>::value == true, "is_destructible failure"); + static_assert(is_destructible<AbstractWithDtor>::value == true, "is_destructible failure"); #if !defined(EA_COMPILER_NO_DELETED_FUNCTIONS) static_assert(is_destructible<DeletedDtor>::value == false, "is_destructible failure"); // You can't call operator delete on this class. #endif @@ -1420,18 +1524,25 @@ int TestTypeTraits() static_assert(is_trivially_destructible<PodA>::value == true, "is_trivially_destructible failure"); static_assert(is_trivially_destructible<int[3]>::value == true, "is_trivially_destructible failure"); static_assert(is_trivially_destructible<int[]>::value == false, "is_trivially_destructible failure"); - static_assert(is_trivially_destructible<Abstract>::value == false, "is_trivially_destructible failure"); - static_assert(is_trivially_destructible<AbstractWithDtor>::value == false, "is_trivially_destructible failure"); + static_assert(is_trivially_destructible<Abstract>::value == true, "is_trivially_destructible failure"); + static_assert(is_trivially_destructible<AbstractWithDtor>::value == false, "is_trivially_destructible failure"); // Having a user-defined destructor make it non-trivial. + #if !defined(EA_COMPILER_NO_DELETED_FUNCTIONS) static_assert(is_trivially_destructible<DeletedDtor>::value == false, "is_trivially_destructible failure"); + #endif static_assert(is_trivially_destructible<NonPod2>::value == false, "is_trivially_destructible failure"); // This case differs from is_destructible, because we have a declared destructor. #endif // is_nothrow_destructible static_assert(is_nothrow_destructible<int>::value == true, "is_nothrow_destructible failure"); - static_assert(is_nothrow_destructible<int&>::value == true, "is_nothrow_destructible failure"); - static_assert(is_nothrow_destructible<int&&>::value == true, "is_nothrow_destructible failure"); + static_assert(is_nothrow_destructible<int&>::value == true, "is_nothrow_destructible failure"); + static_assert(is_nothrow_destructible<int&&>::value == true, "is_nothrow_destructible failure"); static_assert(is_nothrow_destructible<void>::value == false, "is_nothrow_destructible failure"); + static_assert(is_nothrow_destructible<Abstract>::value == true, "is_nothrow_destructible failure"); + static_assert(is_nothrow_destructible<AbstractWithDtor>::value == true, "is_nothrow_destructible failure"); + #if !defined(EA_COMPILER_NO_DELETED_FUNCTIONS) + static_assert(is_nothrow_destructible<DeletedDtor>::value == false, "is_nothrow_destructible failure"); // You can't call operator delete on this class. + #endif #if EASTL_TYPE_TRAIT_is_nothrow_destructible_CONFORMANCE static_assert(is_nothrow_destructible<NonPod2>::value == true, "is_nothrow_destructible failure"); // NonPod2 is nothrow destructible because it has an empty destructor (makes no calls) which has no exception specification. Thus its exception specification defaults to noexcept(true) [C++11 Standard, 15.4 paragraph 14] static_assert(is_nothrow_destructible<NoThrowDestructible>::value == true, "is_nothrow_destructible failure"); @@ -1669,7 +1780,7 @@ int TestTypeTraits() static_assert(eastl::is_same_v<unsigned long, eastl::make_unsigned<unsigned long>::type>); static_assert(eastl::is_same_v<unsigned long long, eastl::make_unsigned<unsigned long long>::type>); - #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #if EASTL_GCC_STYLE_INT128_SUPPORTED static_assert(eastl::is_same_v<__uint128_t, eastl::make_unsigned<__int128_t>::type>); static_assert(eastl::is_same_v<__uint128_t, eastl::make_unsigned<__uint128_t>::type>); @@ -1680,13 +1791,18 @@ int TestTypeTraits() // Char tests static_assert(sizeof(char) == sizeof(eastl::make_signed<char>::type)); static_assert(sizeof(wchar_t) == sizeof(eastl::make_signed<wchar_t>::type)); + static_assert(sizeof(char8_t) == sizeof(eastl::make_signed<char8_t>::type)); static_assert(sizeof(char16_t) == sizeof(eastl::make_signed<char16_t>::type)); static_assert(sizeof(char32_t) == sizeof(eastl::make_signed<char32_t>::type)); static_assert(sizeof(char) == sizeof(eastl::make_unsigned<char>::type)); static_assert(sizeof(wchar_t) == sizeof(eastl::make_unsigned<wchar_t>::type)); + static_assert(sizeof(char8_t) == sizeof(eastl::make_unsigned<char8_t>::type)); static_assert(sizeof(char16_t) == sizeof(eastl::make_unsigned<char16_t>::type)); static_assert(sizeof(char32_t) == sizeof(eastl::make_unsigned<char32_t>::type)); + static_assert(eastl::is_same_v<signed char, eastl::make_signed<char8_t>::type>); + static_assert(eastl::is_same_v<unsigned char, eastl::make_unsigned<char8_t>::type>); + // Enum tests enum EnumUCharSize : unsigned char {}; enum EnumUShortSize : unsigned short {}; @@ -1833,6 +1949,28 @@ int TestTypeTraits() yValue = 3; EATEST_VERIFY(yValue == 3); + // ref to T + // -> T* + static_assert(is_same_v<add_pointer_t<int&>, int*>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<int(&)()>, int(*)()>, "add_pointer failure"); + + // object type (a (possibly cv-qualified) type other than function type, reference type or void), or + // a function type that is not cv- or ref-qualified, or a (possibly cv-qualified) void type + // -> T* + static_assert(is_same_v<add_pointer_t<int>, int*>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<int*>, int**>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<int()>, int(*)()>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<void>, void*>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<const void>, const void*>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<volatile void>, volatile void*>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<const volatile void>, const volatile void*>, "add_pointer failure"); + + // otherwise (cv- or ref-qualified function type) + // -> T + static_assert(is_same_v<add_pointer_t<int() const>, int() const>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<int() volatile>, int() volatile>, "add_pointer failure"); + static_assert(is_same_v<add_pointer_t<int() const volatile>, int() const volatile>, "add_pointer failure"); + // remove_extent // If T is an array of some type X, provides the member typedef type equal to X, otherwise // type is T. Note that if T is a multidimensional array, only the first dimension is removed. @@ -1846,6 +1984,55 @@ int TestTypeTraits() static_assert((eastl::is_same<Int2, int>::value == true), "remove_all_extents/is_same failure"); } + // add_lvalue_reference + { + // function type with no cv- or ref-qualifier + // -> T& + static_assert(is_same_v<add_lvalue_reference_t<void()>, void(&)()>, "add_lvalue_reference failure"); + + // object type (a (possibly cv-qualified) type other than function type, reference type or void) + // -> T& + static_assert(is_same_v<add_lvalue_reference_t<int>, int&>, "add_lvalue_reference failure"); + static_assert(is_same_v<add_lvalue_reference_t<const int>, const int&>, "add_lvalue_reference failure"); + + // if T is an rvalue reference (to some type U) + // -> U& + static_assert(is_same_v<add_lvalue_reference_t<int&&>, int&>, "add_lvalue_reference failure"); + + // otherwise (cv- or ref-qualified function type, or reference type, or (possibly cv-qualified) void) + // -> T + static_assert(is_same_v<add_lvalue_reference_t<void() const>, void() const>, "add_lvalue_reference failure"); + static_assert(is_same_v<add_lvalue_reference_t<void()&>, void()&>, "add_lvalue_reference failure"); + static_assert(is_same_v<add_lvalue_reference_t<void()&&>, void()&&>, "add_lvalue_reference failure"); + static_assert(is_same_v<add_lvalue_reference_t<int&>, int&>, "add_lvalue_reference failure"); + static_assert(is_same_v<add_lvalue_reference_t<const int&>, const int&>, "add_lvalue_reference failure"); + static_assert(is_same_v<add_lvalue_reference_t<void>, void>, "add_lvalue_reference failure"); + static_assert(is_same_v<add_lvalue_reference_t<const void>, const void>, "add_lvalue_reference failure"); + } + + // add_rvalue_reference + { + // function type with no cv- or ref-qualifier + // -> T&& + static_assert(is_same_v<add_rvalue_reference_t<void()>, void(&&)()>, "add_rvalue_reference failure"); + + // object type (a (possibly cv-qualified) type other than function type, reference type or void) + // -> T&& + static_assert(is_same_v<add_rvalue_reference_t<int>, int&&>, "add_rvalue_reference failure"); + static_assert(is_same_v<add_rvalue_reference_t<const int>, const int&&>, "add_rvalue_reference failure"); + + // otherwise (cv- or ref-qualified function type, or reference type, or (possibly cv-qualified) void) + // -> T + static_assert(is_same_v<add_rvalue_reference_t<void() const>, void() const>, "add_rvalue_reference failure"); + static_assert(is_same_v<add_rvalue_reference_t<void()&>, void()&>, "add_rvalue_reference failure"); + static_assert(is_same_v<add_rvalue_reference_t<void()&&>, void()&&>, "add_rvalue_reference failure"); + static_assert(is_same_v<add_rvalue_reference_t<int&>, int&>, "add_rvalue_reference failure"); + static_assert(is_same_v<add_rvalue_reference_t<int&&>, int&&>, "add_rvalue_reference failure"); + static_assert(is_same_v<add_rvalue_reference_t<const int&>, const int&>, "add_rvalue_reference failure"); + static_assert(is_same_v<add_rvalue_reference_t<void>, void>, "add_rvalue_reference failure"); + static_assert(is_same_v<add_rvalue_reference_t<const void>, const void>, "add_rvalue_reference failure"); + } + // decay { @@ -1979,26 +2166,71 @@ int TestTypeTraits() } // void_t - #if EASTL_VARIABLE_TEMPLATES_ENABLED { { - static_assert(is_same_v<void_t<void>, void>, "void_t failure"); - static_assert(is_same_v<void_t<int>, void>, "void_t failure"); - static_assert(is_same_v<void_t<short>, void>, "void_t failure"); - static_assert(is_same_v<void_t<long>, void>, "void_t failure"); - static_assert(is_same_v<void_t<long long>, void>, "void_t failure"); - static_assert(is_same_v<void_t<ClassEmpty>, void>, "void_t failure"); - static_assert(is_same_v<void_t<ClassNonEmpty>, void>, "void_t failure"); - static_assert(is_same_v<void_t<vector<int>>, void>, "void_t failure"); + static_assert(is_same<void_t<void>, void>::value, "void_t failure"); + static_assert(is_same<void_t<int>, void>::value, "void_t failure"); + static_assert(is_same<void_t<short>, void>::value, "void_t failure"); + static_assert(is_same<void_t<long>, void>::value, "void_t failure"); + static_assert(is_same<void_t<long long>, void>::value, "void_t failure"); + static_assert(is_same<void_t<ClassEmpty>, void>::value, "void_t failure"); + static_assert(is_same<void_t<ClassNonEmpty>, void>::value, "void_t failure"); + static_assert(is_same<void_t<vector<int>>, void>::value, "void_t failure"); } // new sfinae mechansim test { - static_assert(has_increment_operator<HasIncrementOperator>::value, "void_t sfinae failure"); - static_assert(!has_increment_operator<ClassEmpty>::value, "void_t sfinae failure"); + static_assert(has_increment_operator_using_void_t<HasIncrementOperator>::value, "void_t sfinae failure"); + static_assert(!has_increment_operator_using_void_t<ClassEmpty>::value, "void_t sfinae failure"); } } + + // detected idiom + { + static_assert(is_detected<has_increment_operator_detection, HasIncrementOperator>::value, "is_detected failure."); + static_assert(!is_detected<has_increment_operator_detection, ClassEmpty>::value, "is_detected failure."); + + static_assert(is_same<detected_t<has_increment_operator_detection, HasIncrementOperator>, HasIncrementOperator&>::value, "is_detected_t failure."); + static_assert(is_same<detected_t<has_increment_operator_detection, ClassEmpty>, nonesuch>::value, "is_detected_t failure."); + + using detected_or_positive_result = detected_or<float, has_increment_operator_detection, HasIncrementOperator>; + using detected_or_negative_result = detected_or<float, has_increment_operator_detection, ClassEmpty>; + static_assert(detected_or_positive_result::value_t::value, "detected_or failure."); + static_assert(!detected_or_negative_result::value_t::value, "detected_or failure."); + static_assert(is_same<detected_or_positive_result::type, HasIncrementOperator&>::value, "detected_or failure."); + static_assert(is_same<detected_or_negative_result::type, float>::value, "detected_or failure."); + + static_assert(is_same<detected_or_t<float, has_increment_operator_detection, HasIncrementOperator>, HasIncrementOperator&>::value, "detected_or_t failure."); + static_assert(is_same<detected_or_t<float, has_increment_operator_detection, ClassEmpty>, float>::value, "detected_or_t failure."); + + static_assert(is_detected_exact<HasIncrementOperator&, has_increment_operator_detection, HasIncrementOperator>::value, "is_detected_exact failure."); + static_assert(!is_detected_exact<float, has_increment_operator_detection, HasIncrementOperator>::value, "is_detected_exact failure."); + static_assert(is_detected_exact<nonesuch, has_increment_operator_detection, ClassEmpty>::value, "is_detected_exact failure."); + static_assert(!is_detected_exact<float, has_increment_operator_detection, ClassEmpty>::value, "is_detected_exact failure."); + + static_assert(is_detected_convertible<HasIncrementOperator&, has_increment_operator_detection, HasIncrementOperator>::value, "is_detected_convertible failure."); + static_assert(is_detected_convertible<HasIncrementOperator, has_increment_operator_detection, HasIncrementOperator>::value, "is_detected_convertible failure."); + static_assert(!is_detected_convertible<float, has_increment_operator_detection, HasIncrementOperator>::value, "is_detected_convertible failure."); + static_assert(!is_detected_convertible<nonesuch, has_increment_operator_detection, ClassEmpty>::value, "is_detected_convertible failure."); + static_assert(!is_detected_convertible<float, has_increment_operator_detection, ClassEmpty>::value, "is_detected_convertible failure."); + + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + static_assert(is_detected_v<has_increment_operator_detection, HasIncrementOperator>, "is_detected_v failure."); + static_assert(!is_detected_v<has_increment_operator_detection, ClassEmpty>, "is_detected_v failure."); + + static_assert(is_detected_exact_v<HasIncrementOperator&, has_increment_operator_detection, HasIncrementOperator>, "is_detected_exact_v failure."); + static_assert(!is_detected_exact_v<float, has_increment_operator_detection, HasIncrementOperator>, "is_detected_exact_v failure."); + static_assert(is_detected_exact_v<nonesuch, has_increment_operator_detection, ClassEmpty>, "is_detected_exact_v failure."); + static_assert(!is_detected_exact_v<float, has_increment_operator_detection, ClassEmpty>, "is_detected_exact_v failure."); + + static_assert(is_detected_convertible_v<HasIncrementOperator&, has_increment_operator_detection, HasIncrementOperator>, "is_detected_convertible_v failure."); + static_assert(is_detected_convertible_v<HasIncrementOperator, has_increment_operator_detection, HasIncrementOperator>, "is_detected_convertible_v failure."); + static_assert(!is_detected_convertible_v<float, has_increment_operator_detection, HasIncrementOperator>, "is_detected_convertible_v failure."); + static_assert(!is_detected_convertible_v<nonesuch, has_increment_operator_detection, ClassEmpty>, "is_detected_convertible_v failure."); + static_assert(!is_detected_convertible_v<float, has_increment_operator_detection, ClassEmpty>, "is_detected_convertible_v failure."); #endif + } // conjunction { @@ -2160,8 +2392,10 @@ int TestTypeTraits() static_assert(!is_aggregate_v<NotAggregrate>, "is_aggregate failure"); } - #ifndef EA_COMPILER_MSVC - // NOTE(rparolin): MSVC is incorrectly categorizing the aggregate type in this test-case. + #if defined(EA_COMPILER_CPP11_ENABLED) && !defined(EA_COMPILER_CPP14_ENABLED) + // See https://en.cppreference.com/w/cpp/language/aggregate_initialization + // In C++11 the requirement was added to aggregate types that no default member initializers exist, + // however this requirement was removed in C++14. { struct NotAggregrate { int data = 42; }; // default member initializer static_assert(!is_aggregate_v<NotAggregrate>, "is_aggregate failure"); diff --git a/EASTL/test/source/TestUtility.cpp b/EASTL/test/source/TestUtility.cpp index 363f409..e9027e5 100644 --- a/EASTL/test/source/TestUtility.cpp +++ b/EASTL/test/source/TestUtility.cpp @@ -108,7 +108,7 @@ static int TestUtilityPair() EATEST_VERIFY((p2.first == 0) && (p2.second == 1.f)); pair<const char*, int> p3 = eastl::make_pair("a", 1); - EATEST_VERIFY((EA::StdC::Strcmp(p3.first, "a") == 0) && (p2.second == 1)); + EATEST_VERIFY((EA::StdC::Strcmp(p3.first, "a") == 0) && (p3.second == 1)); pair<const char*, int> p4 = eastl::make_pair<const char*, int>("a", 1); EATEST_VERIFY((EA::StdC::Strcmp(p4.first, "a") == 0) && (p4.second == 1)); @@ -116,6 +116,20 @@ static int TestUtilityPair() pair<int, const char*> p5 = eastl::make_pair<int, const char*>(1, "b"); EATEST_VERIFY((p5.first == 1) && (EA::StdC::Strcmp(p5.second, "b") == 0)); +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + pair<int, int> p6 = eastl::make_pair<int, int>(1, 2); + pair<int, int> p7 = eastl::make_pair<int, int>(2, 1); + pair<int, int> p8 = eastl::make_pair<int, int>(7, 8); + pair<int, int> p9 = eastl::make_pair<int, int>(10, 1); + + EATEST_VERIFY( (p6 <=> p7) != 0); + EATEST_VERIFY( (p6 <=> p6) == 0); + EATEST_VERIFY( (p7 <=> p8) < 0); + EATEST_VERIFY( (p7 <=> p8) <= 0); + EATEST_VERIFY( (p9 <=> p8) > 0); + EATEST_VERIFY( (p9 <=> p8) >= 0); +#endif + #if !defined(EA_COMPILER_NO_AUTO) auto p60 = eastl::make_pair("a", "b"); // Different strings of same length of 1. EATEST_VERIFY((EA::StdC::Strcmp(p60.first, "a") == 0) && (EA::StdC::Strcmp(p60.second, "b") == 0)); @@ -495,13 +509,15 @@ static int TestUtilityIntegerSequence() using namespace eastl; int nErrorCount = 0; #if EASTL_VARIADIC_TEMPLATES_ENABLED -// Android clang chokes with an internal compiler error on make_integer_sequence -#if !defined(EA_PLATFORM_ANDROID) + EATEST_VERIFY((integer_sequence<int, 0, 1, 2, 3, 4>::size() == 5)); EATEST_VERIFY((make_integer_sequence<int, 5>::size() == 5)); -#endif + static_assert(is_same<make_integer_sequence<int, 5>, integer_sequence<int, 0, 1, 2, 3, 4>>::value); + EATEST_VERIFY((index_sequence<0, 1, 2, 3, 4>::size() == 5)); EATEST_VERIFY((make_index_sequence<5>::size() == 5)); + static_assert(is_same<make_index_sequence<5>, index_sequence<0, 1, 2, 3, 4>>::value); + static_assert(is_same<make_index_sequence<5>, integer_sequence<size_t, 0, 1, 2, 3, 4>>::value); #endif // EASTL_VARIADIC_TEMPLATES_ENABLED return nErrorCount; @@ -614,6 +630,271 @@ static int TestUtilityExchange() return nErrorCount; } +#if defined(EA_COMPILER_CPP20_ENABLED) +template <typename T> +static int TestCmpCommon() +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::cmp_equal(T(0), T(0))); + EATEST_VERIFY(eastl::cmp_equal(T(1), T(1))); + EATEST_VERIFY(eastl::cmp_equal(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::min())); + EATEST_VERIFY(eastl::cmp_equal(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::max())); + EATEST_VERIFY(!eastl::cmp_equal(T(0), T(1))); + EATEST_VERIFY(!eastl::cmp_equal(T(1), T(0))); + if (eastl::is_signed_v<T>) + { + EATEST_VERIFY(eastl::cmp_equal(T(-1), T(-1))); + EATEST_VERIFY(!eastl::cmp_equal(T(-1), T(-2))); + EATEST_VERIFY(!eastl::cmp_equal(T(-2), T(-1))); + } + + EATEST_VERIFY(eastl::cmp_not_equal(T(1), T(0))); + EATEST_VERIFY(eastl::cmp_not_equal(T(0), T(1))); + EATEST_VERIFY(eastl::cmp_not_equal(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::max())); + EATEST_VERIFY(eastl::cmp_not_equal(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::min())); + if (eastl::is_signed_v<T>) + { + EATEST_VERIFY(!eastl::cmp_not_equal(T(-1), T(-1))); + EATEST_VERIFY(eastl::cmp_not_equal(T(-1), T(-2))); + EATEST_VERIFY(eastl::cmp_not_equal(T(-2), T(-1))); + } + + EATEST_VERIFY(eastl::cmp_less(T(0), T(1))); + EATEST_VERIFY(eastl::cmp_less(T(5), T(10))); + EATEST_VERIFY(!eastl::cmp_less(T(0), T(0))); + EATEST_VERIFY(!eastl::cmp_less(T(1), T(0))); + EATEST_VERIFY(eastl::cmp_less(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::max())); + EATEST_VERIFY(!eastl::cmp_less(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::min())); + EATEST_VERIFY(!eastl::cmp_less(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::max())); + EATEST_VERIFY(!eastl::cmp_less(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::min())); + if (eastl::is_signed_v<T>) + { + EATEST_VERIFY(!eastl::cmp_less(T(-1), T(-1))); + EATEST_VERIFY(!eastl::cmp_less(T(-1), T(-2))); + EATEST_VERIFY(eastl::cmp_less(T(-2), T(-1))); + } + + EATEST_VERIFY(eastl::cmp_less_equal(T(0), T(1))); + EATEST_VERIFY(eastl::cmp_less_equal(T(5), T(10))); + EATEST_VERIFY(eastl::cmp_less_equal(T(0), T(0))); + EATEST_VERIFY(eastl::cmp_less_equal(T(1), T(1))); + EATEST_VERIFY(!eastl::cmp_less_equal(T(1), T(0))); + EATEST_VERIFY(eastl::cmp_less_equal(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::max())); + EATEST_VERIFY(eastl::cmp_less_equal(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::min())); + EATEST_VERIFY(eastl::cmp_less_equal(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::max())); + EATEST_VERIFY(!eastl::cmp_less_equal(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::min())); + if (eastl::is_signed_v<T>) + { + EATEST_VERIFY(eastl::cmp_less_equal(T(-1), T(-1))); + EATEST_VERIFY(!eastl::cmp_less_equal(T(-1), T(-2))); + EATEST_VERIFY(eastl::cmp_less_equal(T(-2), T(-1))); + } + + EATEST_VERIFY(eastl::cmp_greater(T(1), T(0))); + EATEST_VERIFY(eastl::cmp_greater(T(10), T(5))); + EATEST_VERIFY(!eastl::cmp_greater(T(0), T(0))); + EATEST_VERIFY(!eastl::cmp_greater(T(0), T(1))); + EATEST_VERIFY(eastl::cmp_greater(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::min())); + EATEST_VERIFY(!eastl::cmp_greater(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::min())); + EATEST_VERIFY(!eastl::cmp_greater(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::max())); + EATEST_VERIFY(!eastl::cmp_greater(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::max())); + if (eastl::is_signed_v<T>) + { + EATEST_VERIFY(!eastl::cmp_greater(T(-1), T(-1))); + EATEST_VERIFY(eastl::cmp_greater(T(-1), T(-2))); + EATEST_VERIFY(!eastl::cmp_greater(T(-2), T(-1))); + } + + EATEST_VERIFY(eastl::cmp_greater_equal(T(1), T(0))); + EATEST_VERIFY(eastl::cmp_greater_equal(T(10), T(5))); + EATEST_VERIFY(eastl::cmp_greater_equal(T(0), T(0))); + EATEST_VERIFY(!eastl::cmp_greater_equal(T(0), T(1))); + EATEST_VERIFY(eastl::cmp_greater_equal(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::min())); + EATEST_VERIFY(eastl::cmp_greater_equal(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::min())); + EATEST_VERIFY(eastl::cmp_greater_equal(eastl::numeric_limits<T>::max(), eastl::numeric_limits<T>::max())); + EATEST_VERIFY(!eastl::cmp_greater_equal(eastl::numeric_limits<T>::min(), eastl::numeric_limits<T>::max())); + if (eastl::is_signed_v<T>) + { + EATEST_VERIFY(eastl::cmp_greater_equal(T(-1), T(-1))); + EATEST_VERIFY(eastl::cmp_greater_equal(T(-1), T(-2))); + EATEST_VERIFY(!eastl::cmp_greater_equal(T(-2), T(-1))); + } + + return nErrorCount; +} + +template <typename T, typename U> +static int TestUtilityCmpEql(const T x, const U y) +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::cmp_equal(T(x), U(y))); + EATEST_VERIFY(eastl::cmp_equal(U(y), T(x))); + EATEST_VERIFY(!eastl::cmp_not_equal(T(x), U(y))); + EATEST_VERIFY(!eastl::cmp_not_equal(U(y), T(x))); + + return nErrorCount; +} + +template <typename T, typename U> +static int TestUtilityCmpLess(const T x, const U y) +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::cmp_less(T(x), U(y))); + EATEST_VERIFY(!eastl::cmp_less(U(y), T(x))); + + EATEST_VERIFY(!eastl::cmp_greater_equal(T(x), U(y))); + EATEST_VERIFY(eastl::cmp_greater_equal(U(y), T(x))); + + return nErrorCount; +} + +template <typename T, typename U> +static int TestUtilityCmpGreater(const T x, const U y) +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::cmp_greater(T(x), U(y))); + EATEST_VERIFY(!eastl::cmp_greater(U(y), T(x))); + + EATEST_VERIFY(!eastl::cmp_less_equal(T(x), U(y))); + EATEST_VERIFY(eastl::cmp_less_equal(U(y), T(x))); + + return nErrorCount; +} + +template <typename T, typename U> +static int TestUtilityCmpLessEq(const T x, const U y) +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::cmp_less_equal(T(x), U(y))); + EATEST_VERIFY(eastl::cmp_less(T(x), U(y)) || eastl::cmp_equal(T(x), U(y))); + + EATEST_VERIFY(eastl::cmp_greater_equal(U(y), T(x))); + + return nErrorCount; +} + +template <typename T, typename U> +static int TestUtilityCmpGreaterEq(const T x, const U y) +{ + int nErrorCount = 0; + + EATEST_VERIFY(eastl::cmp_greater_equal(T(x), U(y))); + EATEST_VERIFY(eastl::cmp_greater(T(x), U(y)) || eastl::cmp_equal(T(x), U(y))); + + EATEST_VERIFY(eastl::cmp_less_equal(U(y), T(x))); + + return nErrorCount; +} + +static int TestUtilityIntegralComp() +{ + int nErrorCount = 0; + + // Test integral comparisons among same types + nErrorCount += TestCmpCommon<int>(); + nErrorCount += TestCmpCommon<short>(); + nErrorCount += TestCmpCommon<long>(); + nErrorCount += TestCmpCommon<long long>(); + + nErrorCount += TestCmpCommon<unsigned int>(); + nErrorCount += TestCmpCommon<unsigned short>(); + nErrorCount += TestCmpCommon<unsigned long>(); + nErrorCount += TestCmpCommon<unsigned long long>(); + + // 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)); + nErrorCount += TestUtilityCmpLess(eastl::numeric_limits<long>::min(), short(0)); + nErrorCount += TestUtilityCmpLess(short(0), eastl::numeric_limits<int>::max()); + nErrorCount += TestUtilityCmpLess(eastl::numeric_limits<unsigned short>::min(), eastl::numeric_limits<int>::max()); + nErrorCount += TestUtilityCmpLess(eastl::numeric_limits<short>::max(), eastl::numeric_limits<long>::max()); + nErrorCount += TestUtilityCmpLess(eastl::numeric_limits<int>::max(), eastl::numeric_limits<long long>::max()); + nErrorCount += TestUtilityCmpLess(int(-100), unsigned int(0)); + nErrorCount += TestUtilityCmpLess(eastl::numeric_limits<int>::min(), eastl::numeric_limits<unsigned int>::min()); + + nErrorCount += TestUtilityCmpGreater(int(1), short(0)); + nErrorCount += TestUtilityCmpGreater(unsigned long(1), int(-1)); + nErrorCount += TestUtilityCmpGreater(unsigned long long(100), short(-100)); + nErrorCount += TestUtilityCmpGreater(short(0), eastl::numeric_limits<short>::min()); + nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits<long>::max(), unsigned short(5)); + nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits<long>::max(), eastl::numeric_limits<int>::min()); + nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits<int>::max(), eastl::numeric_limits<short>::max()); + nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits<long long>::max(), eastl::numeric_limits<int>::max()); + nErrorCount += TestUtilityCmpGreater(unsigned int(0), int(-100)); + nErrorCount += TestUtilityCmpGreater(eastl::numeric_limits<unsigned int>::min(), eastl::numeric_limits<int>::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(eastl::numeric_limits<int>::min(), short(0)); + nErrorCount += TestUtilityCmpLessEq(short(0), eastl::numeric_limits<int>::max()); + nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits<short>::min(), eastl::numeric_limits<short>::min()); + nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits<int>::max(), eastl::numeric_limits<int>::max()); + nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits<int>::max(), eastl::numeric_limits<long long>::max()); + nErrorCount += TestUtilityCmpLessEq(int(50), unsigned int(50)); + nErrorCount += TestUtilityCmpLessEq(eastl::numeric_limits<int>::min(), eastl::numeric_limits<unsigned int>::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(eastl::numeric_limits<long>::max(), eastl::numeric_limits<long>::max()); + nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits<int>::max(), eastl::numeric_limits<short>::min()); + nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits<int>::max(), eastl::numeric_limits<short>::max()); + nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits<long long>::max(), eastl::numeric_limits<int>::max()); + nErrorCount += TestUtilityCmpGreaterEq(unsigned int(0), int(0)); + nErrorCount += TestUtilityCmpGreaterEq(eastl::numeric_limits<unsigned int>::min(), eastl::numeric_limits<int>::min()); + + // Test in_range + EATEST_VERIFY(eastl::in_range<int>(0)); + EATEST_VERIFY(eastl::in_range<int>(eastl::numeric_limits<int>::min())); + EATEST_VERIFY(eastl::in_range<int>(eastl::numeric_limits<int>::max())); + EATEST_VERIFY(eastl::in_range<unsigned int>(0)); + EATEST_VERIFY(eastl::in_range<unsigned int>(eastl::numeric_limits<unsigned int>::min())); + EATEST_VERIFY(eastl::in_range<unsigned int>(eastl::numeric_limits<unsigned int>::max())); + EATEST_VERIFY(!eastl::in_range<unsigned int>(-1)); + EATEST_VERIFY(!eastl::in_range<int>(eastl::numeric_limits<unsigned int>::max())); + EATEST_VERIFY(!eastl::in_range<unsigned int>(eastl::numeric_limits<int>::min())); + + EATEST_VERIFY(eastl::in_range<short>(100)); + EATEST_VERIFY(eastl::in_range<short>(eastl::numeric_limits<short>::min())); + EATEST_VERIFY(eastl::in_range<short>(eastl::numeric_limits<short>::max())); + EATEST_VERIFY(eastl::in_range<unsigned short>(100)); + EATEST_VERIFY(eastl::in_range<unsigned short>(eastl::numeric_limits<unsigned short>::min())); + EATEST_VERIFY(eastl::in_range<unsigned short>(eastl::numeric_limits<unsigned short>::max())); + EATEST_VERIFY(!eastl::in_range<unsigned short>(-1)); + EATEST_VERIFY(!eastl::in_range<short>(eastl::numeric_limits<unsigned int>::max())); + EATEST_VERIFY(!eastl::in_range<unsigned short>(eastl::numeric_limits<int>::min())); + + EATEST_VERIFY(eastl::in_range<long>(50)); + EATEST_VERIFY(eastl::in_range<long>(eastl::numeric_limits<long>::min())); + EATEST_VERIFY(eastl::in_range<long>(eastl::numeric_limits<long>::max())); + EATEST_VERIFY(eastl::in_range<unsigned long>(50)); + EATEST_VERIFY(eastl::in_range<unsigned long>(eastl::numeric_limits<unsigned long>::min())); + EATEST_VERIFY(eastl::in_range<unsigned long>(eastl::numeric_limits<unsigned long>::max())); + EATEST_VERIFY(!eastl::in_range<unsigned long>(-1)); + EATEST_VERIFY(!eastl::in_range<long>(eastl::numeric_limits<unsigned int>::max())); + EATEST_VERIFY(!eastl::in_range<unsigned long>(eastl::numeric_limits<int>::min())); + + return nErrorCount; +} +#endif + /////////////////////////////////////////////////////////////////////////////// // TestUtility // @@ -627,6 +908,8 @@ int TestUtility() nErrorCount += TestUtilityMove(); nErrorCount += TestUtilityIntegerSequence(); nErrorCount += TestUtilityExchange(); - +#if defined(EA_COMPILER_CPP20_ENABLED) + nErrorCount += TestUtilityIntegralComp(); +#endif return nErrorCount; } diff --git a/EASTL/test/source/TestVariant.cpp b/EASTL/test/source/TestVariant.cpp index a8197e5..2a78a89 100644 --- a/EASTL/test/source/TestVariant.cpp +++ b/EASTL/test/source/TestVariant.cpp @@ -7,6 +7,7 @@ #include <EASTL/string.h> #include <EASTL/algorithm.h> #include <EASTL/sort.h> +#include <EASTL/bonus/overloaded.h> #ifdef EA_COMPILER_CPP14_ENABLED #include "ConceptImpls.h" @@ -291,7 +292,7 @@ int TestVariantHoldsAlternative() VERIFY(!holds_alternative<long>(v)); // Verify that a query for a T not in the variant typelist returns false. VERIFY(!holds_alternative<string>(v)); // Verify that a query for a T not in the variant typelist returns false. - VERIFY(!holds_alternative<int>(v)); // variant does not hold an int + VERIFY(!holds_alternative<int>(v)); // variant does not hold an int VERIFY(!holds_alternative<short>(v)); // variant does not hold a short } @@ -377,7 +378,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 @@ -563,12 +564,12 @@ int TestVariantSwap() v1.swap(v2); - VERIFY(get<int>(v1) == 24); + VERIFY(get<int>(v1) == 24); VERIFY(get<int>(v2) == 42); v1.swap(v2); - VERIFY(get<int>(v1) == 42); + VERIFY(get<int>(v1) == 42); VERIFY(get<int>(v2) == 24); } @@ -576,13 +577,13 @@ int TestVariantSwap() variant<string> v1 = "Hello"; variant<string> v2 = "World"; - VERIFY(get<string>(v1) == "Hello"); + VERIFY(get<string>(v1) == "Hello"); VERIFY(get<string>(v2) == "World"); v1.swap(v2); VERIFY(get<string>(v1) == "World"); - VERIFY(get<string>(v2) == "Hello"); + VERIFY(get<string>(v2) == "Hello"); } return nErrorCount; @@ -657,7 +658,7 @@ EA_NO_INLINE int TestVariantVisitNoInline(const eastl::variant<int, bool, unsign struct MyVisitor { MyVisitor() = delete; - MyVisitor(bool& b) : mVisited(b) {} + MyVisitor(bool& visited) : mVisited(visited) {}; void operator()(int) { mVisited = true; } void operator()(bool) { mVisited = true; } @@ -666,7 +667,7 @@ EA_NO_INLINE int TestVariantVisitNoInline(const eastl::variant<int, bool, unsign bool& mVisited; }; - eastl::visit(MyVisitor{bVisited}, v); + eastl::visit(MyVisitor(bVisited), v); EATEST_VERIFY(bVisited); @@ -682,7 +683,7 @@ EA_NO_INLINE int TestVariantVisit2NoInline(const eastl::variant<int, bool>& v0, struct MyVisitor { MyVisitor() = delete; - MyVisitor(bool& b) : mVisited(b) {} + MyVisitor(bool& visited) : mVisited(visited) {}; void operator()(int, int) { mVisited = true; } void operator()(bool, int) { mVisited = true; } @@ -692,7 +693,7 @@ EA_NO_INLINE int TestVariantVisit2NoInline(const eastl::variant<int, bool>& v0, bool& mVisited; }; - eastl::visit(MyVisitor{bVisited}, v0, v1); + eastl::visit(MyVisitor(bVisited), v0, v1); EATEST_VERIFY(bVisited); @@ -708,7 +709,7 @@ EA_NO_INLINE int TestVariantVisit3tNoInline(const eastl::variant<int, bool>& v0, struct MyVisitor { MyVisitor() = delete; - MyVisitor(bool& b) : mVisited(b) {} + MyVisitor(bool& visited) : mVisited(visited) {}; void operator()(int, int, int) { mVisited = true; } void operator()(bool, int, int) { mVisited = true; } @@ -723,38 +724,101 @@ EA_NO_INLINE int TestVariantVisit3tNoInline(const eastl::variant<int, bool>& v0, bool& mVisited; }; - eastl::visit(MyVisitor{bVisited}, v0, v1, v2); + eastl::visit(MyVisitor(bVisited), v0, v1, v2); EATEST_VERIFY(bVisited); return nErrorCount; } -int TestVariantVisitor() +int TestVariantVisitorOverloaded() { using namespace eastl; int nErrorCount = 0; using v_t = variant<int, string, double, long>; + v_t arr[] = {42, "jean", 42.0, 42L}; + v_t v{42.0}; + + + #ifdef __cpp_deduction_guides + { + int count = 0; + + for (auto& e : arr) + { + eastl::visit( + overloaded{ + [&](int) { count++; }, + [&](string) { count++; }, + [&](double) { count++; }, + [&](long) { count++; }}, + e + ); + } + + VERIFY(count == EAArrayCount(arr)); + } + + { + double visitedValue = 0.0f; + + eastl::visit( + overloaded{ + [](int) { }, + [](string) { }, + [&](double d) { visitedValue = d; }, + [](long) { }}, + v + ); + + VERIFY(visitedValue == 42.0f); + } + + #endif + + { + int count = 0; + + for (auto& e : arr) + { + eastl::visit( + eastl::make_overloaded( + [&](int) { count++; }, + [&](string) { count++; }, + [&](double) { count++; }, + [&](long) { count++; }), + e + ); + } - // TODO(rparolin): When we have a C++17 compiler - // - // template deduction guides test - // template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; - // template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; + VERIFY(count == EAArrayCount(arr)); + } + + { + double visitedValue = 0.0f; - // { - // v_t arr[] = {42, "rob", 42.0, 42L}; + eastl::visit( + eastl::make_overloaded( + [](int) { }, + [](string) { }, + [&](double d) { visitedValue = d; }, + [](long) { }), + v + ); - // int count = 0; - // for (auto& e : arr) - // { - // eastl::visit(overloaded{[&](int) { count++; }, - // [&](string) { count++; }, - // [&](double) { count++; }, - // [&](long) { count++; }}, e); - // } - // } + VERIFY(visitedValue == 42.0f); + } + + return nErrorCount; +} + +int TestVariantVisitor() +{ + using namespace eastl; + int nErrorCount = 0; + + using v_t = variant<int, string, double, long>; { v_t arr[] = {42, "hello", 42.0, 42L}; @@ -982,7 +1046,7 @@ int TestVariantVisitor() struct MultipleVisitor { MultipleVisitor() = delete; - MultipleVisitor(bool& b) : mVisited(b) {} + MultipleVisitor(bool& visited) : mVisited(visited) {}; void operator()(int, int) { mVisited = true; } void operator()(int, bool) {} @@ -992,11 +1056,11 @@ int TestVariantVisitor() bool& mVisited; }; - visit(MultipleVisitor{bVisited}, v0, v1); + visit(MultipleVisitor(bVisited), v0, v1); EATEST_VERIFY(bVisited); bVisited = false; - visit<void>(MultipleVisitor{bVisited}, v0, v1); + visit<void>(MultipleVisitor(bVisited), v0, v1); EATEST_VERIFY(bVisited); } @@ -1742,6 +1806,7 @@ int TestVariant() nErrorCount += TestVariantEmplace(); nErrorCount += TestVariantRelOps(); nErrorCount += TestVariantInplaceCtors(); + nErrorCount += TestVariantVisitorOverloaded(); nErrorCount += TestVariantVisitor(); nErrorCount += TestVariantAssignment(); nErrorCount += TestVariantMoveOnly(); @@ -1756,13 +1821,3 @@ int TestVariant() #else int TestVariant() { return 0; } #endif - - - - - - - - - - diff --git a/EASTL/test/source/TestVector.cpp b/EASTL/test/source/TestVector.cpp index 0fe719d..69cdb52 100644 --- a/EASTL/test/source/TestVector.cpp +++ b/EASTL/test/source/TestVector.cpp @@ -1258,6 +1258,84 @@ int TestVector() EATEST_VERIFY(!(intArray1 > intArray2)); } + // three way comparison operator +#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) + { + using namespace eastl; + + vector<int> intArray1(10); + vector<int> intArray2(10); + + for (i = 0; i < intArray1.size(); i++) + { + intArray1[i] = (int)i; // Make intArray1 equal to intArray2. + intArray2[i] = (int)i; + } + + // Verify equality between intArray1 and intArray2 + EATEST_VERIFY((intArray1 <=> intArray2) == 0); + EATEST_VERIFY(!((intArray1 <=> intArray2) != 0)); + EATEST_VERIFY((intArray1 <=> intArray2) <= 0); + EATEST_VERIFY((intArray1 <=> intArray2) >= 0); + EATEST_VERIFY(!((intArray1 <=> intArray2) < 0)); + EATEST_VERIFY(!((intArray1 <=> intArray2) > 0)); + + intArray1.push_back(100); // Make intArray1 less than intArray2. + intArray2.push_back(101); + + // Verify intArray1 < intArray2 + EATEST_VERIFY(!((intArray1 <=> intArray2) == 0)); + EATEST_VERIFY((intArray1 <=> intArray2) != 0); + EATEST_VERIFY((intArray1 <=> intArray2) <= 0); + EATEST_VERIFY(!((intArray1 <=> intArray2) >= 0)); + EATEST_VERIFY(((intArray1 <=> intArray2) < 0)); + EATEST_VERIFY(!((intArray1 <=> intArray2) > 0)); + + for (i = 0; i < 3; i++) // Make the length of intArray2 less than intArray1 + intArray2.pop_back(); + + // Verify intArray2.size() < intArray1.size() and intArray2 is a subset of intArray1 + EATEST_VERIFY(!((intArray1 <=> intArray2) == 0)); + EATEST_VERIFY((intArray1 <=> intArray2) != 0); + EATEST_VERIFY((intArray1 <=> intArray2) >= 0); + EATEST_VERIFY(!((intArray1 <=> intArray2) <= 0)); + EATEST_VERIFY(((intArray1 <=> intArray2) > 0)); + EATEST_VERIFY(!((intArray1 <=> intArray2) < 0)); + } + + { + using namespace eastl; + + vector<int> intArray1 = {1, 2, 3, 4, 5, 6, 7}; + vector<int> intArray2 = {7, 6, 5, 4, 3, 2, 1}; + vector<int> intArray3 = {1, 2, 3, 4}; + + struct weak_ordering_vector + { + vector<int> vec; + inline std::weak_ordering operator<=>(const weak_ordering_vector& b) const { return vec <=> b.vec; } + }; + + EATEST_VERIFY(synth_three_way{}(weak_ordering_vector{intArray1}, weak_ordering_vector{intArray2}) == std::weak_ordering::less); + EATEST_VERIFY(synth_three_way{}(weak_ordering_vector{intArray3}, weak_ordering_vector{intArray1}) == std::weak_ordering::less); + EATEST_VERIFY(synth_three_way{}(weak_ordering_vector{intArray2}, weak_ordering_vector{intArray1}) == std::weak_ordering::greater); + EATEST_VERIFY(synth_three_way{}(weak_ordering_vector{intArray2}, weak_ordering_vector{intArray3}) == std::weak_ordering::greater); + EATEST_VERIFY(synth_three_way{}(weak_ordering_vector{intArray1}, weak_ordering_vector{intArray1}) == std::weak_ordering::equivalent); + + struct strong_ordering_vector + { + vector<int> vec; + inline std::strong_ordering operator<=>(const strong_ordering_vector& b) const { return vec <=> b.vec; } + }; + + EATEST_VERIFY(synth_three_way{}(strong_ordering_vector{intArray1}, strong_ordering_vector{intArray2}) == std::strong_ordering::less); + EATEST_VERIFY(synth_three_way{}(strong_ordering_vector{intArray3}, strong_ordering_vector{intArray1}) == std::strong_ordering::less); + EATEST_VERIFY(synth_three_way{}(strong_ordering_vector{intArray2}, strong_ordering_vector{intArray1}) == std::strong_ordering::greater); + EATEST_VERIFY(synth_three_way{}(strong_ordering_vector{intArray2}, strong_ordering_vector{intArray3}) == std::strong_ordering::greater); + EATEST_VERIFY(synth_three_way{}(strong_ordering_vector{intArray1}, strong_ordering_vector{intArray1}) == std::strong_ordering::equal); + } +#endif + { using namespace eastl; @@ -1317,7 +1395,7 @@ int TestVector() eastl::vector<TestObject> toTest; // InputIterator - demoted_iterator<TestObject*, eastl::forward_iterator_tag> toInput(&to); + demoted_iterator<TestObject*, EASTL_ITC_NS::forward_iterator_tag> toInput(&to); toTest.assign(toInput, toInput); // ForwardIterator @@ -1637,7 +1715,7 @@ int TestVector() { struct iterator { - typedef eastl::input_iterator_tag iterator_category; + typedef EASTL_ITC_NS::input_iterator_tag iterator_category; typedef int value_type; typedef ptrdiff_t difference_type; typedef int* pointer; @@ -1718,20 +1796,24 @@ int TestVector() { eastl::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - eastl::erase(v, 5); + auto numErased = eastl::erase(v, 5); VERIFY((v == eastl::vector<int> {1, 2, 3, 4, 6, 7, 8, 9})); + VERIFY(numErased == 1); - eastl::erase(v, 2); + numErased = eastl::erase(v, 2); VERIFY((v == eastl::vector<int> {1, 3, 4, 6, 7, 8, 9})); + VERIFY(numErased == 1); - eastl::erase(v, 9); + numErased = eastl::erase(v, 9); VERIFY((v == eastl::vector<int> {1, 3, 4, 6, 7, 8})); + VERIFY(numErased == 1); } { eastl::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - eastl::erase_if(v, [](auto i) { return i % 2 == 0; }); + auto numErased = eastl::erase_if(v, [](auto i) { return i % 2 == 0; }); VERIFY((v == eastl::vector<int>{1, 3, 5, 7, 9})); + VERIFY(numErased == 4); } } diff --git a/EASTL/test/source/main.cpp b/EASTL/test/source/main.cpp index 8ce7c1b..132bab1 100644 --- a/EASTL/test/source/main.cpp +++ b/EASTL/test/source/main.cpp @@ -145,7 +145,7 @@ int EAMain(int argc, char* argv[]) testSuite.AddTest("VectorSet", TestVectorSet); testSuite.AddTest("AtomicBasic", TestAtomicBasic); testSuite.AddTest("AtomicAsm", TestAtomicAsm); - testSuite.AddTest("TestBitcast", TestBitcast); + testSuite.AddTest("Bitcast", TestBitcast); nErrorCount += testSuite.Run(); |