diff options
149 files changed, 8739 insertions, 1973 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd8addb..36d4c89 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,12 +25,12 @@ jobs: strategy: fail-fast: true matrix: - os: ["ubuntu-latest", "ubuntu-20.04"] + os: ["ubuntu-latest", "ubuntu-22.04"] include: - compiler: "gcc" os: "ubuntu-latest" - - compiler: "gcc-7" - os: "ubuntu-20.04" + - compiler: "gcc-9" + os: "ubuntu-22.04" steps: - uses: actions/checkout@v2 @@ -68,7 +68,7 @@ jobs: make deps-distclean - name: Upload Package if: startsWith(matrix.os, 'ubuntu-latest') - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: mingw-w64-dpp-package path: mingw-w64-dpp.tar diff --git a/CRT/gen_wrapper.sh b/CRT/gen_wrapper.sh index 4e26162..35e0d43 100755 --- a/CRT/gen_wrapper.sh +++ b/CRT/gen_wrapper.sh @@ -9,6 +9,14 @@ CURLINE=0 while read -r line; do CURLINE=$(expr ${CURLINE} + 1) VALID=1 + SYMBOL_EXISTS=0 + + if [ -z "${line}" ]; then + continue + fi + if [ $(printf '%s\n' "${line}" | grep -oE '^#*') ]; then + continue + fi rtype=$(printf '%s\n' "${line}" | grep -oE '(NTSTATUS NTAPI|VOID NTAPI|PVOID NTAPI)') if [ -z "${rtype}" ]; then @@ -16,11 +24,18 @@ while read -r line; do VALID=0 fi - fnname=$(printf '%s\n' "${line}" | grep -oE '(Zw|Rtl|Ob|Mm|Io)[^ (]*') + fnname=$(printf '%s\n' "${line}" | grep -oE '(_|)(Zw|Rtl|Ob[^j]|Mm|Io)[^ (]*') if [ -z "${fnname}" ]; then printf '%s\n' "Line ${CURLINE}: Missing function name." >&2 VALID=0 fi + if [ $(printf '%s\n' "${fnname}" | wc -l) -ne 1 ]; then + printf '%s\n' "Invalid function name '${fnname}'." >&2 + VALID=0 + fi + if [ $(printf '%s\n' "${fnname}" | grep -oE '^_*') ]; then + SYMBOL_EXISTS=1 + fi fnsig=$(printf '%s\n' "${line}" | grep -oE '\([^;]*') if [ -z "${fnsig}" ]; then @@ -53,22 +68,29 @@ while read -r line; do fi if [ ${VALID} -eq 1 ]; then - TYPEDEFS="${TYPEDEFS}\ntypedef ${rtype} (*${fnname}_t) ${fnsig};" - STATICS="${STATICS}\nstatic ${fnname}_t _${fnname} = NULL;" + TYPE="${fnname}_t" + VAR="_${fnname}" + TYPEDEFS="${TYPEDEFS}\ntypedef ${rtype} (*${TYPE}) ${fnsig};" + STATICS="${STATICS}\nstatic ${TYPE} ${VAR} = NULL;" + if [ ${SYMBOL_EXISTS} -eq 1 ]; then + fnname_str=$(printf '%s\n' "${fnname}" | sed 's/^\(.\)\{1\}//g') + else + fnname_str="${fnname}" + fi INITS=$(cat <<EOF ${INITS} #ifdef __cplusplus - RtlInitUnicodeString(&fnName, skCrypt(L"${fnname}")); + RtlInitUnicodeString(&fnName, skCrypt(L"${fnname_str}")); #else - RtlInitUnicodeString(&fnName, L"${fnname}"); + RtlInitUnicodeString(&fnName, L"${fnname_str}"); #endif - _${fnname} = (${fnname}_t)MmGetSystemRoutineAddress(&fnName); - if (_${fnname} == NULL) + ${VAR} = (${TYPE})MmGetSystemRoutineAddress(&fnName); + if (${VAR} == NULL) { #ifdef __cplusplus - DbgPrint(skCrypt("%s\\\n"), skCrypt("System routine ${fnname} not found.")); + DbgPrint(skCrypt("%s\\\n"), skCrypt("System routine ${fnname_str} not found.")); #else - DbgPrint("%s\\\n", "System routine ${fnname} not found."); + DbgPrint("%s\\\n", "System routine ${fnname_str} not found."); #endif retval++; } @@ -85,15 +107,15 @@ EOF NTSTATUS*) WRAPPERS=$(cat <<EOF ${WRAPPERS} - if (_${fnname} == NULL) + if (${VAR} == NULL) return STATUS_PROCEDURE_NOT_FOUND; - return _${fnname} (${params}); + return ${VAR} (${params}); } -${rtype} Wrapper${fnname} ${fnsig} +${rtype} Wrapper${fnname_str} ${fnsig} { - return _${fnname} (${params}); + return ${VAR} (${params}); } EOF ) @@ -101,12 +123,12 @@ EOF PVOID*) WRAPPERS=$(cat <<EOF ${WRAPPERS} - return _${fnname} (${params}); + return ${VAR} (${params}); } ${rtype} Wrapper${fnname} ${fnsig} { - return _${fnname} (${params}); + return ${VAR} (${params}); } EOF ) @@ -148,3 +170,5 @@ cat <<EOF }; #endif EOF + +printf '%s lines parsed\n' "${CURLINE}" >&2 diff --git a/CRT/ntdll_zw_functions.c b/CRT/ntdll_zw_functions.c index 7fac930..312440c 100644 --- a/CRT/ntdll_zw_functions.c +++ b/CRT/ntdll_zw_functions.c @@ -18,6 +18,9 @@ typedef NTSTATUS NTAPI (*ZwTraceEvent_t) (_In_ HANDLE TraceHandle, _In_ ULONG Fl typedef NTSTATUS NTAPI (*ZwQueryVirtualMemory_t) (_In_ HANDLE ProcessHandle, _In_ PVOID BaseAddress, _In_ int MemoryInformationClass, _Out_ PVOID MemoryInformation, _In_ SIZE_T MemoryInformationLength, _Out_ PSIZE_T ReturnLength); typedef NTSTATUS NTAPI (*ZwProtectVirtualMemory_t) (_In_ HANDLE ProcessHandle, _In_ _Out_ PVOID* BaseAddress, _In_ _Out_ PSIZE_T NumberOfBytesToProtect, _In_ ULONG NewAccessProtection, _Out_ PULONG OldAccessProtection); typedef NTSTATUS NTAPI (*ZwQuerySystemInformation_t) (_In_ int SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength); +typedef NTSTATUS NTAPI (*_ZwCreateFile_t) (_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_ PVOID EaBuffer, _In_ ULONG EaLength); +typedef NTSTATUS NTAPI (*_ZwClose_t) (_In_ HANDLE Handle); +typedef NTSTATUS NTAPI (*_ZwWriteFile_t) (_In_ HANDLE FileHandle, _In_ HANDLE Event, _In_ PIO_APC_ROUTINE ApcRoutine, _In_ PVOID ApcContext, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PVOID Buffer, _In_ ULONG Length, _In_ PLARGE_INTEGER ByteOffset, _In_ PULONG Key); static MmMapIoSpaceEx_t _MmMapIoSpaceEx = NULL; static ObOpenObjectByPointer_t _ObOpenObjectByPointer = NULL; @@ -29,6 +32,9 @@ static ZwTraceEvent_t _ZwTraceEvent = NULL; static ZwQueryVirtualMemory_t _ZwQueryVirtualMemory = NULL; static ZwProtectVirtualMemory_t _ZwProtectVirtualMemory = NULL; static ZwQuerySystemInformation_t _ZwQuerySystemInformation = NULL; +static _ZwCreateFile_t __ZwCreateFile = NULL; +static _ZwClose_t __ZwClose = NULL; +static _ZwWriteFile_t __ZwWriteFile = NULL; int __cdecl ntdll_zw_functions (void) { @@ -185,6 +191,51 @@ int __cdecl ntdll_zw_functions (void) #endif retval++; } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwCreateFile")); +#else + RtlInitUnicodeString(&fnName, L"ZwCreateFile"); +#endif + __ZwCreateFile = (_ZwCreateFile_t)MmGetSystemRoutineAddress(&fnName); + if (__ZwCreateFile == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwCreateFile not found.")); +#else + DbgPrint("%s\n", "System routine ZwCreateFile not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwClose")); +#else + RtlInitUnicodeString(&fnName, L"ZwClose"); +#endif + __ZwClose = (_ZwClose_t)MmGetSystemRoutineAddress(&fnName); + if (__ZwClose == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwClose not found.")); +#else + DbgPrint("%s\n", "System routine ZwClose not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwWriteFile")); +#else + RtlInitUnicodeString(&fnName, L"ZwWriteFile"); +#endif + __ZwWriteFile = (_ZwWriteFile_t)MmGetSystemRoutineAddress(&fnName); + if (__ZwWriteFile == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwWriteFile not found.")); +#else + DbgPrint("%s\n", "System routine ZwWriteFile not found."); +#endif + retval++; + } return retval; } @@ -314,6 +365,45 @@ NTSTATUS NTAPI WrapperZwQuerySystemInformation (_In_ int SystemInformationClass, return _ZwQuerySystemInformation (SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); } +NTSTATUS NTAPI _ZwCreateFile (_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_ PVOID EaBuffer, _In_ ULONG EaLength) +{ + if (__ZwCreateFile == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return __ZwCreateFile (FileHandle, DesiredAccess, ObjectAttributes, StatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength); +} + +NTSTATUS NTAPI WrapperZwCreateFile (_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_ PVOID EaBuffer, _In_ ULONG EaLength) +{ + return __ZwCreateFile (FileHandle, DesiredAccess, ObjectAttributes, StatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength); +} + +NTSTATUS NTAPI _ZwClose (_In_ HANDLE Handle) +{ + if (__ZwClose == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return __ZwClose (Handle); +} + +NTSTATUS NTAPI WrapperZwClose (_In_ HANDLE Handle) +{ + return __ZwClose (Handle); +} + +NTSTATUS NTAPI _ZwWriteFile (_In_ HANDLE FileHandle, _In_ HANDLE Event, _In_ PIO_APC_ROUTINE ApcRoutine, _In_ PVOID ApcContext, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PVOID Buffer, _In_ ULONG Length, _In_ PLARGE_INTEGER ByteOffset, _In_ PULONG Key) +{ + if (__ZwWriteFile == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return __ZwWriteFile (FileHandle, Event, ApcRoutine, ApcContext, StatusBlock, Buffer, Length, ByteOffset, Key); +} + +NTSTATUS NTAPI WrapperZwWriteFile (_In_ HANDLE FileHandle, _In_ HANDLE Event, _In_ PIO_APC_ROUTINE ApcRoutine, _In_ PVOID ApcContext, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PVOID Buffer, _In_ ULONG Length, _In_ PLARGE_INTEGER ByteOffset, _In_ PULONG Key) +{ + return __ZwWriteFile (FileHandle, Event, ApcRoutine, ApcContext, StatusBlock, Buffer, Length, ByteOffset, Key); +} + #ifdef __cplusplus }; #endif diff --git a/CRT/ntdll_zw_functions.txt b/CRT/ntdll_zw_functions.txt index 76a9106..2cb1964 100644 --- a/CRT/ntdll_zw_functions.txt +++ b/CRT/ntdll_zw_functions.txt @@ -1,3 +1,7 @@ +# Some functions that get resolved at runtime +# They can always be called prefixed with "Wrapper" i.e. MmCopyMemory(...) becomes WrapperMmCopyMemory(...) +# If not prefixed with '_', MmCopyMemory(...) should also work + PVOID NTAPI MmMapIoSpaceEx(_In_ PHYSICAL_ADDRESS PhysicalAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Protect); NTSTATUS NTAPI ObOpenObjectByPointer (_In_ PVOID obj, _In_ ULONG HandleAttributes, _In_ PACCESS_STATE PassedAccessState, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_TYPE objType, _In_ KPROCESSOR_MODE AccessMode, _Out_ PHANDLE Handle); NTSTATUS NTAPI MmCopyMemory (_In_ PVOID TargetAddress, _In_ PVOID SourceAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Flags, _Out_ PSIZE_T NumberOfBytesTransferred); @@ -8,3 +12,8 @@ NTSYSCALLAPI NTSTATUS NTAPI ZwTraceEvent (_In_ HANDLE TraceHandle, _In_ ULONG Fl NTSYSCALLAPI NTSTATUS NTAPI ZwQueryVirtualMemory(_In_ HANDLE ProcessHandle, _In_ PVOID BaseAddress, _In_ int MemoryInformationClass, _Out_ PVOID MemoryInformation, _In_ SIZE_T MemoryInformationLength, _Out_ PSIZE_T ReturnLength); NTSYSAPI NTSTATUS NTAPI ZwProtectVirtualMemory(_In_ HANDLE ProcessHandle, _In_ _Out_ PVOID* BaseAddress, _In_ _Out_ PSIZE_T NumberOfBytesToProtect, _In_ ULONG NewAccessProtection, _Out_ PULONG OldAccessProtection); NTSYSCALLAPI NTSTATUS NTAPI ZwQuerySystemInformation(_In_ int SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength); + +# Prefixed with a '_', the resulting function should only get called as "Wrapper" i.e. _ZwClose(...) will become WrapperZwClose(...) +NTSYSAPI NTSTATUS NTAPI _ZwCreateFile(_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_ PVOID EaBuffer, _In_ ULONG EaLength); +NTSYSAPI NTSTATUS NTAPI _ZwClose(_In_ HANDLE Handle); +NTSYSAPI NTSTATUS NTAPI _ZwWriteFile(_In_ HANDLE FileHandle, _In_ HANDLE Event, _In_ PIO_APC_ROUTINE ApcRoutine, _In_ PVOID ApcContext, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PVOID Buffer, _In_ ULONG Length, _In_ PLARGE_INTEGER ByteOffset, _In_ PULONG Key); diff --git a/EASTL/.github/workflows/c-cpp.yml b/EASTL/.github/workflows/c-cpp.yml new file mode 100644 index 0000000..0be723e --- /dev/null +++ b/EASTL/.github/workflows/c-cpp.yml @@ -0,0 +1,133 @@ +name: EASTL Build & Test Pipeline + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + checkout: + name: Checkout EASTL and submodules + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + path: EASTL/ + - run: cd EASTL/ && git submodule update --init + - name: Upload checked out code + uses: actions/upload-artifact@v2.3.1 + with: + name: Code + path: EASTL/ + + build: + needs: checkout + + strategy: + fail-fast: false + matrix: + 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 + - os: windows-latest + compiler: clang + - os: ubuntu-latest + compiler: msvc + include: + - os: windows-latest + compiler: msvc + cxxflags: '/std:c++20 /Zc:char8_t' + - os: ubuntu-latest + compiler: clang + cc: 'clang-14' + cxx: 'clang++-14' + cxxflags: '-std=c++20' + - os: ubuntu-latest + compiler: gcc + cc: 'gcc-12' + cxx: 'g++-12' + cxxflags: '-std=c++2a' + + name: Build EASTL + runs-on: ${{ matrix.os }} + + steps: + - name: Download a Build Artifact + uses: actions/download-artifact@v2.1.0 + with: + name: Code + path: Code/ + + - run: mkdir build + - 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 }} + CC: ${{ matrix.cc }} + - run: cd build && cmake --build . --config ${{ matrix.configuration }} + - name: Upload binaries + uses: actions/upload-artifact@v2.3.1 + with: + name: Binaries-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.configuration }} + path: build/ + + test: + needs: build + name: Run EASTL tests + strategy: + fail-fast: false + matrix: + os: [ windows-latest, ubuntu-latest ] + compiler: [ clang, msvc, gcc ] + configuration: [ Debug, Release ] + exclude: + - os: windows-latest + compiler: gcc + - os: windows-latest + compiler: clang + - os: ubuntu-latest + compiler: msvc + runs-on: ${{ matrix.os }} + + steps: + - name: Download a Build Artifact + uses: actions/download-artifact@v2.1.0 + with: + name: Binaries-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.configuration }} + path: Binaries/ + - if: matrix.os == 'ubuntu-latest' + run: chmod 755 ./Binaries/test/EASTLTest + - run: cd Binaries/test && ctest -C ${{ matrix.configuration }} -V + + benchmark: + needs: build + name: Run EASTL benchmarks + strategy: + fail-fast: false + matrix: + os: [ windows-latest, ubuntu-latest ] + compiler: [ clang, msvc, gcc ] + configuration: [ Release ] + exclude: + - os: windows-latest + compiler: gcc + - os: windows-latest + compiler: clang + - os: ubuntu-latest + compiler: msvc + runs-on: ${{ matrix.os }} + + steps: + - name: Download a Build Artifact + uses: actions/download-artifact@v2.1.0 + with: + name: Binaries-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.configuration }} + path: Binaries/ + - if: matrix.os == 'ubuntu-latest' + run: chmod 755 ./Binaries/benchmark/EASTLBenchmarks + - run: cd Binaries/benchmark && ctest -C ${{ matrix.configuration }} -V 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/README.md b/EASTL/README.md index 07b8ea3..8548d9e 100644 --- a/EASTL/README.md +++ b/EASTL/README.md @@ -42,13 +42,13 @@ Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on compiling and testi ## Credits And Maintainers -EASTL was created by Paul Pedriana and he maintained the project for roughly 10 years. +EASTL was created by Paul Pedriana and he maintained the project for roughly 10 years. EASTL was subsequently maintained by Roberto Parolin for more than 8 years. He was the driver and proponent for getting EASTL opensourced. Rob was a mentor to all members of the team and taught us everything we ever wanted to know about C++ spookyness. -Max Winkler is the current EASTL owner and primary maintainer within EA and is responsible for the open source repository. +After Rob, maintenance of EASTL passed to Max Winkler for roughly a year, until landing with its current maintainer Liam Mitchell. Significant EASTL contributions were made by (in alphabetical order): 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/Design.md b/EASTL/doc/Design.md index bda7378..5877bb7 100644 --- a/EASTL/doc/Design.md +++ b/EASTL/doc/Design.md @@ -306,7 +306,7 @@ EASTL algorithms very much follow the philosophy of standard C++ algorithms, as EASTL algorithms are optimized at least as well as the best STL algorithms found in commercial libraries and are significantly optimized over the algorithms that come with the first-party STLs that come with compilers. Most significantly, EASTL algorithms take advantage of type traits of contained classes and take advantage of iterator types to optimize code generation. For example, if you resize an array of integers (or other "pod" type), EASTL will detect that this can be done with a memcpy instead of a slow object-by-object move as would Micrsoft STL. -The optimizations found in EASTL algorithms and the supporting code in EASTL type traits consistts of some fairly tricky advanced C++ and while it is fairly easy to read, it requires a C++ expert (language lawyer, really) to implement confidently. The result of this is that it takes more effort to develop and maintain EASTL than it would to maintain a simpler library. However, the performance advantages have been deemed worth the tradeoff. +The optimizations found in EASTL algorithms and the supporting code in EASTL type traits consists of some fairly tricky advanced C++ and while it is fairly easy to read, it requires a C++ expert (language lawyer, really) to implement confidently. The result of this is that it takes more effort to develop and maintain EASTL than it would to maintain a simpler library. However, the performance advantages have been deemed worth the tradeoff. ## Smart Pointer Design diff --git a/EASTL/doc/EASTL.natvis b/EASTL/doc/EASTL.natvis index 30986d5..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<*>"> @@ -558,8 +573,159 @@ <DisplayString>{mFlag.mAtomic}</DisplayString> </Type> +<Type Name="eastl::variant<*>"> + <Intrinsic Name="index" Expression="(int)mIndex"/> + <DisplayString Condition="index() == size_t(-1)">[valueless_by_exception]</DisplayString> + <DisplayString Condition="index() == 0" Optional="true">{{ index=0, value={($T1*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 1" Optional="true">{{ index=1, value={($T2*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 2" Optional="true">{{ index=2, value={($T3*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 3" Optional="true">{{ index=3, value={($T4*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 4" Optional="true">{{ index=4, value={($T5*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 5" Optional="true">{{ index=5, value={($T6*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 6" Optional="true">{{ index=6, value={($T7*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 7" Optional="true">{{ index=7, value={($T8*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 8" Optional="true">{{ index=8, value={($T9*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 9" Optional="true">{{ index=9, value={($T10*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 10" Optional="true">{{ index=10, value={($T11*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 11" Optional="true">{{ index=11, value={($T12*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 12" Optional="true">{{ index=12, value={($T13*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 13" Optional="true">{{ index=13, value={($T14*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 14" Optional="true">{{ index=14, value={($T15*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 15" Optional="true">{{ index=15, value={($T16*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 16" Optional="true">{{ index=16, value={($T17*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 17" Optional="true">{{ index=17, value={($T18*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 18" Optional="true">{{ index=18, value={($T19*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 19" Optional="true">{{ index=19, value={($T20*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 20" Optional="true">{{ index=20, value={($T21*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 21" Optional="true">{{ index=21, value={($T22*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 22" Optional="true">{{ index=22, value={($T23*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 23" Optional="true">{{ index=23, value={($T24*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 24" Optional="true">{{ index=24, value={($T25*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 25" Optional="true">{{ index=25, value={($T26*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 26" Optional="true">{{ index=26, value={($T27*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 27" Optional="true">{{ index=27, value={($T28*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 28" Optional="true">{{ index=28, value={($T29*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 29" Optional="true">{{ index=29, value={($T30*)mStorage.mBuffer.mCharData}}</DisplayString> + <DisplayString Condition="index() == 30" Optional="true">{{ index=30, value={($T31*)mStorage.mBuffer.mCharData}}</DisplayString> + <Expand> + <Item Name="index">index()</Item> + <Item Name="[value]" Condition="index() == 0" Optional="true">($T1*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 1" Optional="true">($T2*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 2" Optional="true">($T3*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 3" Optional="true">($T4*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 4" Optional="true">($T5*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 5" Optional="true">($T6*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 6" Optional="true">($T7*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 7" Optional="true">($T8*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 8" Optional="true">($T9*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 9" Optional="true">($T10*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 10" Optional="true">($T11*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 11" Optional="true">($T12*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 12" Optional="true">($T13*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 13" Optional="true">($T14*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 14" Optional="true">($T15*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 15" Optional="true">($T16*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 16" Optional="true">($T17*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 17" Optional="true">($T18*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 18" Optional="true">($T19*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 19" Optional="true">($T20*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 20" Optional="true">($T21*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 21" Optional="true">($T22*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 22" Optional="true">($T23*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 23" Optional="true">($T24*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 24" Optional="true">($T25*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 25" Optional="true">($T26*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 26" Optional="true">($T27*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 27" Optional="true">($T28*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 28" Optional="true">($T29*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 29" Optional="true">($T30*)mStorage.mBuffer.mCharData</Item> + <Item Name="[value]" Condition="index() == 30" Optional="true">($T31*)mStorage.mBuffer.mCharData</Item> + </Expand> +</Type> + + +<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> -<!-- TODO eastl::tuple --> +<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/doc/FAQ.md b/EASTL/doc/FAQ.md index e80172b..1444c48 100644 --- a/EASTL/doc/FAQ.md +++ b/EASTL/doc/FAQ.md @@ -211,7 +211,7 @@ EASTL additions/amendments to std STL * vector and string have set_capacity(). * string has sprintf(), append_sprintf(), trim(), compare_i(), make_lower(), make_upper(). * deque allows you to specify the subarray size. -* list has a push_back(void) and push_back(void) function. +* list has a push_front(void) and push_back(void) function. * hash_map, hash_set, etc. have find_as(). EASTL coverage of TR1 (tr1 refers to proposed additions for the next C++ standard library, ~2008) diff --git a/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/allocator_malloc.h b/EASTL/include/EASTL/allocator_malloc.h index 31f8dec..78f4f69 100644 --- a/EASTL/include/EASTL/allocator_malloc.h +++ b/EASTL/include/EASTL/allocator_malloc.h @@ -40,7 +40,7 @@ #endif #elif defined(EA_PLATFORM_BSD) #include <malloc/malloc.h> - #elif defined(EA_COMPILER_CLANG) + #elif defined(__clang__) #if __has_include(<malloc/malloc.h>) #include <malloc/malloc.h> #elif __has_include(<malloc.h>) diff --git a/EASTL/include/EASTL/array.h b/EASTL/include/EASTL/array.h index 590aa94..34ad07d 100644 --- a/EASTL/include/EASTL/array.h +++ b/EASTL/include/EASTL/array.h @@ -279,12 +279,6 @@ namespace eastl EA_CPP14_CONSTEXPR inline typename array<T, N>::reference array<T, N>::operator[](size_type i) { - #if EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(i >= N)) - EASTL_FAIL_MSG("array::operator[] -- out of range"); - #endif - - EA_ANALYSIS_ASSUME(i < N); return mValue[i]; } @@ -293,13 +287,6 @@ namespace eastl EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reference array<T, N>::operator[](size_type i) const { - #if EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(i >= N)) - EASTL_FAIL_MSG("array::operator[] -- out of range"); - - #endif - - EA_ANALYSIS_ASSUME(i < N); return mValue[i]; } @@ -308,11 +295,6 @@ namespace eastl EA_CPP14_CONSTEXPR inline typename array<T, N>::reference array<T, N>::front() { - #if EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(empty())) // We don't allow the user to reference an empty container. - EASTL_FAIL_MSG("array::front -- empty array"); - #endif - return mValue[0]; } @@ -321,11 +303,6 @@ namespace eastl EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reference array<T, N>::front() const { - #if EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(empty())) // We don't allow the user to reference an empty container. - EASTL_FAIL_MSG("array::front -- empty array"); - #endif - return mValue[0]; } @@ -334,11 +311,6 @@ namespace eastl EA_CPP14_CONSTEXPR inline typename array<T, N>::reference array<T, N>::back() { - #if EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(empty())) // We don't allow the user to reference an empty container. - EASTL_FAIL_MSG("array::back -- empty array"); - #endif - return mValue[N - 1]; } @@ -347,11 +319,6 @@ namespace eastl EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reference array<T, N>::back() const { - #if EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(empty())) // We don't allow the user to reference an empty container. - EASTL_FAIL_MSG("array::back -- empty array"); - #endif - return mValue[N - 1]; } @@ -381,7 +348,6 @@ namespace eastl EASTL_FAIL_MSG("array::at -- out of range"); #endif - EA_ANALYSIS_ASSUME(i < N); return static_cast<const_reference>(mValue[i]); } @@ -397,7 +363,6 @@ namespace eastl EASTL_FAIL_MSG("array::at -- out of range"); #endif - EA_ANALYSIS_ASSUME(i < N); return static_cast<reference>(mValue[i]); } @@ -436,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) @@ -470,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) @@ -513,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 new file mode 100644 index 0000000..0eeeed0 --- /dev/null +++ b/EASTL/include/EASTL/bit.h @@ -0,0 +1,172 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_BIT_H +#define EASTL_BIT_H + +#include <EASTL/internal/config.h> + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include <EASTL/internal/memory_base.h> +#include <EASTL/type_traits.h> +#include <string.h> // memcpy + +namespace eastl +{ + // eastl::bit_cast + // Obtains a value of type To by reinterpreting the object representation of 'from'. + // Every bit in the value representation of the returned To object is equal to the + // corresponding bit in the object representation of 'from'. + // + // In order for bit_cast to be constexpr, the compiler needs to explicitly support + // it by providing the __builtin_bit_cast builtin. If that builtin is not available, + // then we memcpy into aligned storage at runtime and return that instead. + // + // Both types To and From must be equal in size, and must be trivially copyable. + + #if defined(EASTL_CONSTEXPR_BIT_CAST_SUPPORTED) && EASTL_CONSTEXPR_BIT_CAST_SUPPORTED + + template<typename To, typename From, + typename = eastl::enable_if_t< + sizeof(To) == sizeof(From) + && eastl::is_trivially_copyable<To>::value + && eastl::is_trivially_copyable<From>::value + > + > + EA_CONSTEXPR To bit_cast(const From& from) EA_NOEXCEPT + { + return __builtin_bit_cast(To, from); + } + + #else + + template<typename To, typename From, + typename = eastl::enable_if_t< + sizeof(To) == sizeof(From) + && eastl::is_trivially_copyable<To>::value + && eastl::is_trivially_copyable<From>::value + > + > + inline To bit_cast(const From& from) EA_NOEXCEPT + { + typename eastl::aligned_storage<sizeof(To), alignof(To)>::type to; + ::memcpy(eastl::addressof(to), eastl::addressof(from), sizeof(To)); + return reinterpret_cast<To&>(to); + } + + #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 d926105..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. @@ -1505,7 +1507,7 @@ EA_RESTORE_GCC_WARNING() inline typename BitsetBase<2, WordType>::size_type BitsetBase<2, WordType>::count() const { - #if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 304) // GCC 3.4 or later + #if (defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 304)) || defined(__clang__) // GCC 3.4 or later #if(EA_PLATFORM_WORD_SIZE == 4) return (size_type)__builtin_popcountl(mWord[0]) + (size_type)__builtin_popcountl(mWord[1]); #else @@ -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 453ab0f..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,18 +584,29 @@ 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; int result = clock_gettime(CLOCK_MONOTONIC, &ts); - if(result == EINVAL - ) + if (result == -1 && errno == EINVAL) result = clock_gettime(CLOCK_REALTIME, &ts); const uint64_t nNanoseconds = (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000)); @@ -617,7 +625,7 @@ namespace chrono /////////////////////////////////////////////////////////////////////////////// - // system_clock + // system_clock /////////////////////////////////////////////////////////////////////////////// class system_clock { @@ -631,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 { @@ -653,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>> @@ -681,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>> @@ -691,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 @@ -727,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 @@ -741,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_hash_map.h b/EASTL/include/EASTL/fixed_hash_map.h index af6663d..b94ea54 100644 --- a/EASTL/include/EASTL/fixed_hash_map.h +++ b/EASTL/include/EASTL/fixed_hash_map.h @@ -251,7 +251,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -267,11 +267,13 @@ namespace eastl { EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) + if (!bEnableOverflow) + { base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + } #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -288,11 +290,13 @@ namespace eastl { EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) + if (!bEnableOverflow) + { base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + } #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -314,7 +318,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -377,7 +381,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -402,7 +406,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -532,8 +536,10 @@ namespace eastl { EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) + if (!bEnableOverflow) + { base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + } #if EASTL_NAME_ENABLED mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); @@ -556,7 +562,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -577,7 +583,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -599,7 +605,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -616,7 +622,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -662,7 +668,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -687,7 +693,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); diff --git a/EASTL/include/EASTL/fixed_hash_set.h b/EASTL/include/EASTL/fixed_hash_set.h index 0db9f49..fa2783a 100644 --- a/EASTL/include/EASTL/fixed_hash_set.h +++ b/EASTL/include/EASTL/fixed_hash_set.h @@ -75,7 +75,7 @@ namespace eastl bucketCount + 1, sizeof(typename hash_set<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type), nodeCount, - EASTL_ALIGN_OF(Value), + EASTL_ALIGN_OF(typename hash_set<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type), 0, bEnableOverflow, OverflowAllocator>, @@ -83,8 +83,9 @@ namespace eastl { public: typedef fixed_hashtable_allocator<bucketCount + 1, sizeof(typename hash_set<Value, Hash, Predicate, - OverflowAllocator, bCacheHashCode>::node_type), nodeCount, EASTL_ALIGN_OF(Value), 0, - bEnableOverflow, OverflowAllocator> fixed_allocator_type; + OverflowAllocator, bCacheHashCode>::node_type), nodeCount, + EASTL_ALIGN_OF(typename hash_set<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type), + 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; typedef fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator> this_type; typedef hash_set<Value, Hash, Predicate, fixed_allocator_type, bCacheHashCode> base_type; @@ -162,7 +163,7 @@ namespace eastl bucketCount + 1, sizeof(typename hash_multiset<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type), nodeCount, - EASTL_ALIGN_OF(Value), + EASTL_ALIGN_OF(typename hash_multiset<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type), 0, bEnableOverflow, OverflowAllocator>, @@ -170,7 +171,8 @@ namespace eastl { public: typedef fixed_hashtable_allocator<bucketCount + 1, sizeof(typename hash_multiset<Value, Hash, Predicate, - OverflowAllocator, bCacheHashCode>::node_type), nodeCount, EASTL_ALIGN_OF(Value), 0, + OverflowAllocator, bCacheHashCode>::node_type), nodeCount, EASTL_ALIGN_OF(typename hash_multiset<Value, Hash, Predicate, + OverflowAllocator, bCacheHashCode>::node_type), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; typedef hash_multiset<Value, Hash, Predicate, fixed_allocator_type, bCacheHashCode> base_type; @@ -238,11 +240,13 @@ namespace eastl { EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) + if (!bEnableOverflow) + { base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + } #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -262,7 +266,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -279,11 +283,13 @@ namespace eastl { EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) + if (!bEnableOverflow) + { base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + } #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -302,10 +308,12 @@ namespace eastl EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); if(!bEnableOverflow) + { base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + } #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -322,7 +330,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -344,7 +352,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -366,7 +374,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -391,7 +399,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -515,7 +523,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -535,7 +543,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -556,7 +564,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -578,7 +586,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); @@ -595,7 +603,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -617,7 +625,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -639,7 +647,7 @@ namespace eastl mAllocator.copy_overflow_allocator(x.mAllocator); #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); + mAllocator.set_name(x.mAllocator.get_name()); #endif EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); @@ -664,7 +672,7 @@ namespace eastl base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. #if EASTL_NAME_ENABLED - mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); + mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME); #endif mAllocator.reset(mNodeBuffer); diff --git a/EASTL/include/EASTL/fixed_list.h b/EASTL/include/EASTL/fixed_list.h index 9e48089..e57c08b 100644 --- a/EASTL/include/EASTL/fixed_list.h +++ b/EASTL/include/EASTL/fixed_list.h @@ -63,12 +63,12 @@ namespace eastl /// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap. /// template <typename T, size_t nodeCount, bool bEnableOverflow = true, typename OverflowAllocator = EASTLAllocatorType> - class fixed_list : public list<T, fixed_node_allocator<sizeof(typename list<T>::node_type), - nodeCount, EASTL_ALIGN_OF(T), 0, bEnableOverflow, OverflowAllocator> > + class fixed_list : public list<T, fixed_node_allocator<sizeof(typename list<T>::node_type), + nodeCount, EASTL_ALIGN_OF(typename list<T>::node_type), 0, bEnableOverflow, OverflowAllocator> > { public: - typedef fixed_node_allocator<sizeof(typename list<T>::node_type), nodeCount, - EASTL_ALIGN_OF(T), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef fixed_node_allocator<sizeof(typename list<T>::node_type), nodeCount, + EASTL_ALIGN_OF(typename list<T>::node_type), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; typedef OverflowAllocator overflow_allocator_type; typedef list<T, fixed_allocator_type> base_type; typedef fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type; diff --git a/EASTL/include/EASTL/fixed_slist.h b/EASTL/include/EASTL/fixed_slist.h index 85a7a7b..abad7ad 100644 --- a/EASTL/include/EASTL/fixed_slist.h +++ b/EASTL/include/EASTL/fixed_slist.h @@ -63,12 +63,12 @@ namespace eastl /// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap. /// template <typename T, size_t nodeCount, bool bEnableOverflow = true, typename OverflowAllocator = EASTLAllocatorType> - class fixed_slist : public slist<T, fixed_node_allocator<sizeof(typename slist<T>::node_type), - nodeCount, EASTL_ALIGN_OF(T), 0, bEnableOverflow, OverflowAllocator> > + class fixed_slist : public slist<T, fixed_node_allocator<sizeof(typename slist<T>::node_type), + nodeCount, EASTL_ALIGN_OF(typename slist<T>::node_type), 0, bEnableOverflow, OverflowAllocator> > { public: - typedef fixed_node_allocator<sizeof(typename slist<T>::node_type), nodeCount, - EASTL_ALIGN_OF(T), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef fixed_node_allocator<sizeof(typename slist<T>::node_type), nodeCount, + EASTL_ALIGN_OF(typename slist<T>::node_type), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; typedef OverflowAllocator overflow_allocator_type; typedef slist<T, fixed_allocator_type> base_type; typedef fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type; 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/functional.h b/EASTL/include/EASTL/functional.h index 556bf02..6fa3489 100644 --- a/EASTL/include/EASTL/functional.h +++ b/EASTL/include/EASTL/functional.h @@ -389,52 +389,41 @@ namespace eastl // Dual type functions /////////////////////////////////////////////////////////////////////// + template <typename T, typename U> struct equal_to_2 : public binary_function<T, U, bool> { EA_CPP14_CONSTEXPR bool operator()(const T& a, const U& b) const { return a == b; } - EA_CPP14_CONSTEXPR bool operator()(const U& b, const T& a) const // If you are getting a 'operator() already defined' error related to on this line while compiling a - { return b == a; } // hashtable class (e.g. hash_map), it's likely that you are using hashtable::find_as when you should - }; // be using hashtable::find instead. The problem is that (const T, U) collide. To do: make this work. - template <typename T> - struct equal_to_2<T, T> : public equal_to<T> - { + template <typename T_ = T, typename U_ = U, typename = eastl::enable_if_t<!eastl::is_same_v<eastl::remove_const_t<T_>, eastl::remove_const_t<U_>>>> + EA_CPP14_CONSTEXPR bool operator()(const U& b, const T& a) const + { return b == a; } }; - template <typename T, typename U> struct not_equal_to_2 : public binary_function<T, U, bool> { EA_CPP14_CONSTEXPR bool operator()(const T& a, const U& b) const { return a != b; } + + template <typename T_ = T, typename U_ = U, typename = eastl::enable_if_t<!eastl::is_same_v<eastl::remove_const_t<T_>, eastl::remove_const_t<U_>>>> EA_CPP14_CONSTEXPR bool operator()(const U& b, const T& a) const { return b != a; } }; - template <typename T> - struct not_equal_to_2<T, T> : public not_equal_to<T> - { - }; - template <typename T, typename U> struct less_2 : public binary_function<T, U, bool> { EA_CPP14_CONSTEXPR bool operator()(const T& a, const U& b) const { return a < b; } + + template <typename T_ = T, typename U_ = U, typename = eastl::enable_if_t<!eastl::is_same_v<eastl::remove_const_t<T_>, eastl::remove_const_t<U_>>>> EA_CPP14_CONSTEXPR bool operator()(const U& b, const T& a) const { return b < a; } }; - template <typename T> - struct less_2<T, T> : public less<T> - { - }; - - - /// unary_negate /// 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/arm/arch_arm_memory_barrier.h b/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_memory_barrier.h index c52962e..44dc991 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_memory_barrier.h +++ b/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_memory_barrier.h @@ -11,7 +11,7 @@ #endif -#if defined(EA_COMPILER_MSVC) +#if defined(EA_COMPILER_MSVC) && !defined(EA_COMPILER_CLANG_CL) #if defined(EA_PROCESSOR_ARM32) @@ -46,7 +46,7 @@ EASTL_ATOMIC_COMPILER_BARRIER() -#elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) +#elif defined(EA_COMPILER_GNUC) || defined(__clang__) #define EASTL_ARM_DMB_ISH ish 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 5087c13..77c383a 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h @@ -32,23 +32,14 @@ ///////////////////////////////////////////////////////////////////////////////// - -#if defined(EA_COMPILER_MSVC) - +#if (defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64) + #define EASTL_ARCH_ATOMIC_HAS_128BIT +#elif defined(EA_COMPILER_MSVC) #if EA_PLATFORM_PTR_SIZE == 8 #define EASTL_ARCH_ATOMIC_HAS_128BIT #endif - #endif - -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) - - #define EASTL_ARCH_ATOMIC_HAS_128BIT - -#endif - - ///////////////////////////////////////////////////////////////////////////////// @@ -77,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 \ { \ @@ -104,7 +95,7 @@ * SSE 128-bit loads are not guaranteed to be atomic even though some CPUs * make them atomic such as AMD Ryzen or Intel SandyBridge. */ -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_NOP_PRE_COMPUTE_DESIRED(ret, observed, val) \ @@ -115,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_add_fetch.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_add_fetch.h index 4534806..7b77528 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_add_fetch.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_add_fetch.h @@ -54,7 +54,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_and_fetch.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_and_fetch.h index c38ba41..0583163 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_and_fetch.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_and_fetch.h @@ -54,7 +54,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_strong.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_strong.h index e028398..1968e9a 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_strong.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_strong.h @@ -15,7 +15,7 @@ // // void EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_*_*_N(type, bool ret, type * ptr, type * expected, type desired) // -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_weak.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_weak.h index f8b956a..61a126c 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_weak.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_weak.h @@ -15,7 +15,7 @@ // // void EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_*_*_N(type, bool ret, type * ptr, type * expected, type desired) // -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ 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 0f05800..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 @@ -51,12 +51,12 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #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/arch/x86/arch_x86_fetch_add.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_add.h index d78b333..e816af9 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_add.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_add.h @@ -51,7 +51,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_and.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_and.h index fd7dbb9..ff27b1a 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_and.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_and.h @@ -51,7 +51,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_or.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_or.h index 50da6db..8627d3a 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_or.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_or.h @@ -51,7 +51,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_sub.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_sub.h index 77bee83..14b43f9 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_sub.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_sub.h @@ -51,7 +51,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_xor.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_xor.h index 2e76b0c..666df8b 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_xor.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_xor.h @@ -51,7 +51,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_load.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_load.h index b044190..644a2a1 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_load.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_load.h @@ -15,7 +15,46 @@ // // void EASTL_ARCH_ATOMIC_LOAD_*_N(type, type ret, type * ptr) // -#if defined(EA_COMPILER_MSVC) + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + /** + * NOTE: + * + * Since the cmpxchg 128-bit inline assembly does a sete in the asm to set the return boolean, + * it doesn't get dead-store removed even though we don't care about the success of the + * cmpxchg since the compiler cannot reason about what is inside asm blocks. + * Thus this variant just does the minimum required to do an atomic load. + */ +#define EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, MemoryOrder) \ + { \ + EASTL_ATOMIC_FIXED_WIDTH_TYPE_128 expected = 0; \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, expected); \ + \ + /* Compare RDX:RAX with m128. If equal, set ZF and load RCX:RBX into m128. Else, clear ZF and load m128 into RDX:RAX. */ \ + __asm__ __volatile__ ("lock; cmpxchg16b %2" /* cmpxchg16b sets/clears ZF */ \ + /* Output Operands */ \ + : "=a"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "=d"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]), \ + "+m"(*(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(__uint128_t, (ptr)))) \ + /* Input Operands */ \ + : "b"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "c"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]), \ + "a"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "d"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]) \ + /* Clobbers */ \ + : "memory", "cc"); \ + } + + +#define EASTL_ARCH_ATOMIC_LOAD_RELAXED_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, RELAXED) + +#define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, ACQUIRE) + +#define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, SEQ_CST) + +#elif defined(EA_COMPILER_MSVC) #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1920) // >= VS2019 @@ -119,49 +158,6 @@ #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128(type, ret, ptr) \ EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, SEQ_CST) - -#endif - - -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) - - - /** - * NOTE: - * - * Since the cmpxchg 128-bit inline assembly does a sete in the asm to set the return boolean, - * it doesn't get dead-store removed even though we don't care about the success of the - * cmpxchg since the compiler cannot reason about what is inside asm blocks. - * Thus this variant just does the minimum required to do an atomic load. - */ - #define EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, MemoryOrder) \ - { \ - EASTL_ATOMIC_FIXED_WIDTH_TYPE_128 expected = 0; \ - ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, expected); \ - \ - /* Compare RDX:RAX with m128. If equal, set ZF and load RCX:RBX into m128. Else, clear ZF and load m128 into RDX:RAX. */ \ - __asm__ __volatile__ ("lock; cmpxchg16b %2" /* cmpxchg16b sets/clears ZF */ \ - /* Output Operands */ \ - : "=a"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "=d"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]), \ - "+m"(*(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(__uint128_t, (ptr)))) \ - /* Input Operands */ \ - : "b"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "c"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]), \ - "a"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "d"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]) \ - /* Clobbers */ \ - : "memory", "cc"); \ - } - - - #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_128(type, ret, ptr) \ - EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, RELAXED) - - #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_128(type, ret, ptr) \ - EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, ACQUIRE) - - #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128(type, ret, ptr) \ - EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, SEQ_CST) - - #endif diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_memory_barrier.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_memory_barrier.h index 1d1c8fc..7bad141 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_memory_barrier.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_memory_barrier.h @@ -46,7 +46,7 @@ #endif -#elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) +#elif defined(__clang__) || defined(EA_COMPILER_GNUC) /** * NOTE: diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_or_fetch.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_or_fetch.h index 751cc2a..42f7d61 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_or_fetch.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_or_fetch.h @@ -54,7 +54,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_store.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_store.h index 397ff5f..31655c3 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_store.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_store.h @@ -145,7 +145,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, MemoryOrder) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_sub_fetch.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_sub_fetch.h index 124b586..a1d0932 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_sub_fetch.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_sub_fetch.h @@ -54,7 +54,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_thread_fence.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_thread_fence.h index fe3bd58..183c7f3 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_thread_fence.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_thread_fence.h @@ -31,7 +31,7 @@ #endif -#if defined(EA_COMPILER_MSVC) || defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC) +#if defined(EA_COMPILER_MSVC) || defined(__clang__) || defined(EA_COMPILER_GNUC) #define EASTL_ARCH_ATOMIC_THREAD_FENCE_SEQ_CST() \ EASTL_ATOMIC_CPU_MB() diff --git a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_xor_fetch.h b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_xor_fetch.h index 28cb958..a5b62c3 100644 --- a/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_xor_fetch.h +++ b/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_xor_fetch.h @@ -54,7 +54,7 @@ #endif -#if ((defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) #define EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ diff --git a/EASTL/include/EASTL/internal/atomic/atomic.h b/EASTL/include/EASTL/internal/atomic/atomic.h index e1c5286..eb27d2d 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic.h +++ b/EASTL/include/EASTL/internal/atomic/atomic.h @@ -62,7 +62,7 @@ namespace internal template <typename T> struct is_atomic_lockfree_size { - static EASTL_CPP17_INLINE_VARIABLE constexpr bool value = false || + static EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR_OR_CONST bool value = false || #if defined(EASTL_ATOMIC_HAS_8BIT) sizeof(T) == 1 || #endif @@ -85,7 +85,7 @@ namespace internal template <typename T> struct is_user_type_suitable_for_primary_template { - static EASTL_CPP17_INLINE_VARIABLE constexpr bool value = eastl::internal::is_atomic_lockfree_size<T>::value; + static EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR_OR_CONST bool value = eastl::internal::is_atomic_lockfree_size<T>::value; }; @@ -116,7 +116,7 @@ namespace internal \ public: \ \ - static EASTL_CPP17_INLINE_VARIABLE constexpr bool is_always_lock_free = eastl::internal::is_atomic_lockfree_size<type>::value; \ + static EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR_OR_CONST bool is_always_lock_free = eastl::internal::is_atomic_lockfree_size<type>::value; \ \ public: /* deleted ctors && assignment operators */ \ \ 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_flag.h b/EASTL/include/EASTL/internal/atomic/atomic_flag.h index e135d61..eed448a 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_flag.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_flag.h @@ -42,13 +42,13 @@ public: /* deleted ctors && assignment operators */ public: /* clear */ template <typename Order> - void clear(Order order) volatile EA_NOEXCEPT + void clear(Order /*order*/) volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(Order); } template <typename Order> - void clear(Order order) EA_NOEXCEPT + void clear(Order /*order*/) EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(Order); } @@ -76,14 +76,14 @@ public: /* clear */ public: /* test_and_set */ template <typename Order> - bool test_and_set(Order order) volatile EA_NOEXCEPT + bool test_and_set(Order /*order*/) volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(Order); return false; } template <typename Order> - bool test_and_set(Order order) EA_NOEXCEPT + bool test_and_set(Order /*order*/) EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(Order); return false; @@ -122,14 +122,14 @@ public: /* test_and_set */ public: /* test */ template <typename Order> - bool test(Order order) const volatile EA_NOEXCEPT + bool test(Order /*order*/) const volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(Order); return false; } template <typename Order> - bool test(Order order) const EA_NOEXCEPT + bool test(Order /*order*/) const EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(Order); return false; diff --git a/EASTL/include/EASTL/internal/atomic/atomic_integral.h b/EASTL/include/EASTL/internal/atomic/atomic_integral.h index 7c94db3..a9c96c7 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_integral.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_integral.h @@ -24,18 +24,18 @@ namespace internal #define EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(funcName) \ template <typename Order> \ - T funcName(T arg, Order order) EA_NOEXCEPT \ + T funcName(T /*arg*/, Order /*order*/) EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); \ } \ \ template <typename Order> \ - T funcName(T arg, Order order) volatile EA_NOEXCEPT \ + T funcName(T /*arg*/, Order /*order*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ } \ \ - T funcName(T arg) volatile EA_NOEXCEPT \ + T funcName(T /*arg*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ } @@ -54,7 +54,7 @@ namespace internal #define EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(operatorOp) \ - T operator operatorOp(T arg) volatile EA_NOEXCEPT \ + T operator operatorOp(T /*arg*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ } @@ -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/atomic_memory_order.h b/EASTL/include/EASTL/internal/atomic/atomic_memory_order.h index b1c1403..1564d87 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_memory_order.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_memory_order.h @@ -30,12 +30,12 @@ struct memory_order_seq_cst_s {}; } // namespace internal -EASTL_CPP17_INLINE_VARIABLE constexpr auto memory_order_relaxed = internal::memory_order_relaxed_s{}; -EASTL_CPP17_INLINE_VARIABLE constexpr auto memory_order_read_depends = internal::memory_order_read_depends_s{}; -EASTL_CPP17_INLINE_VARIABLE constexpr auto memory_order_acquire = internal::memory_order_acquire_s{}; -EASTL_CPP17_INLINE_VARIABLE constexpr auto memory_order_release = internal::memory_order_release_s{}; -EASTL_CPP17_INLINE_VARIABLE constexpr auto memory_order_acq_rel = internal::memory_order_acq_rel_s{}; -EASTL_CPP17_INLINE_VARIABLE constexpr auto memory_order_seq_cst = internal::memory_order_seq_cst_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_relaxed = internal::memory_order_relaxed_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_read_depends = internal::memory_order_read_depends_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_acquire = internal::memory_order_acquire_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_release = internal::memory_order_release_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_acq_rel = internal::memory_order_acq_rel_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_seq_cst = internal::memory_order_seq_cst_s{}; } // namespace eastl diff --git a/EASTL/include/EASTL/internal/atomic/atomic_pointer.h b/EASTL/include/EASTL/internal/atomic/atomic_pointer.h index 18f6691..c0b19e6 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_pointer.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_pointer.h @@ -27,18 +27,18 @@ namespace internal #define EASTL_ATOMIC_POINTER_STATIC_ASSERT_FUNCS_IMPL(funcName) \ template <typename Order> \ - T* funcName(ptrdiff_t arg, Order order) EA_NOEXCEPT \ + T* funcName(ptrdiff_t /*arg*/, Order /*order*/) EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); \ } \ \ template <typename Order> \ - T* funcName(ptrdiff_t arg, Order order) volatile EA_NOEXCEPT \ + T* funcName(ptrdiff_t /*arg*/, Order /*order*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ } \ \ - T* funcName(ptrdiff_t arg) volatile EA_NOEXCEPT \ + T* funcName(ptrdiff_t /*arg*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ } @@ -55,7 +55,7 @@ namespace internal } #define EASTL_ATOMIC_POINTER_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(operatorOp) \ - T* operator operatorOp(ptrdiff_t arg) volatile EA_NOEXCEPT \ + T* operator operatorOp(ptrdiff_t /*arg*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ } diff --git a/EASTL/include/EASTL/internal/atomic/atomic_size_aligned.h b/EASTL/include/EASTL/internal/atomic/atomic_size_aligned.h index db23e47..f503375 100644 --- a/EASTL/include/EASTL/internal/atomic/atomic_size_aligned.h +++ b/EASTL/include/EASTL/internal/atomic/atomic_size_aligned.h @@ -24,40 +24,40 @@ namespace internal #define EASTL_ATOMIC_SIZE_ALIGNED_STATIC_ASSERT_CMPXCHG_IMPL(funcName) \ template <typename OrderSuccess, typename OrderFailure> \ - bool funcName(T& expected, T desired, \ - OrderSuccess orderSuccess, \ - OrderFailure orderFailure) EA_NOEXCEPT \ + bool funcName(T& /*expected*/, T /*desired*/, \ + OrderSuccess /*orderSuccess*/, \ + OrderFailure /*orderFailure*/) EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); \ return false; \ } \ \ template <typename OrderSuccess, typename OrderFailure> \ - bool funcName(T& expected, T desired, \ - OrderSuccess orderSuccess, \ - OrderFailure orderFailure) volatile EA_NOEXCEPT \ + bool funcName(T& /*expected*/, T /*desired*/, \ + OrderSuccess /*orderSuccess*/, \ + OrderFailure /*orderFailure*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ return false; \ } \ \ template <typename Order> \ - bool funcName(T& expected, T desired, \ - Order order) EA_NOEXCEPT \ + bool funcName(T& /*expected*/, T /*desired*/, \ + Order /*order*/) EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); \ return false; \ } \ \ template <typename Order> \ - bool funcName(T& expected, T desired, \ - Order order) volatile EA_NOEXCEPT \ + bool funcName(T& /*expected*/, T /*desired*/, \ + Order /*order*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ return false; \ } \ \ - bool funcName(T& expected, T desired) volatile EA_NOEXCEPT \ + bool funcName(T& /*expected*/, T /*desired*/) volatile EA_NOEXCEPT \ { \ EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ return false; \ @@ -90,18 +90,18 @@ namespace internal public: /* store */ template <typename Order> - void store(T desired, Order order) EA_NOEXCEPT + void store(T /*desired*/, Order /*order*/) EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); } template <typename Order> - void store(T desired, Order order) volatile EA_NOEXCEPT + void store(T /*desired*/, Order /*order*/) volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); } - void store(T desired) volatile EA_NOEXCEPT + void store(T /*desired*/) volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); } @@ -109,13 +109,13 @@ namespace internal public: /* load */ template <typename Order> - T load(Order order) const EA_NOEXCEPT + T load(Order /*order*/) const EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); } template <typename Order> - T load(Order order) const volatile EA_NOEXCEPT + T load(Order /*order*/) const volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); } @@ -128,18 +128,18 @@ namespace internal public: /* exchange */ template <typename Order> - T exchange(T desired, Order order) EA_NOEXCEPT + T exchange(T /*desired*/, Order /*order*/) EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); } template <typename Order> - T exchange(T desired, Order order) volatile EA_NOEXCEPT + T exchange(T /*desired*/, Order /*order*/) volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); } - T exchange(T desired) volatile EA_NOEXCEPT + T exchange(T /*desired*/) volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); } @@ -154,7 +154,7 @@ namespace internal public: /* assignment operator */ - T operator=(T desired) volatile EA_NOEXCEPT + T operator=(T /*desired*/) volatile EA_NOEXCEPT { EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); } diff --git a/EASTL/include/EASTL/internal/atomic/compiler/compiler.h b/EASTL/include/EASTL/internal/atomic/compiler/compiler.h index 65a4cd0..fc12879 100644 --- a/EASTL/include/EASTL/internal/atomic/compiler/compiler.h +++ b/EASTL/include/EASTL/internal/atomic/compiler/compiler.h @@ -15,7 +15,7 @@ // // Include the compiler specific implementations // -#if defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) +#if defined(EA_COMPILER_GNUC) || defined(__clang__) #include "gcc/compiler_gcc.h" 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_barrier.h b/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_barrier.h index 02e2d03..90b78a6 100644 --- a/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_barrier.h +++ b/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_barrier.h @@ -16,7 +16,9 @@ // void EASTL_COMPILER_ATOMIC_COMPILER_BARRIER() // #define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER() \ - _ReadWriteBarrier() + EA_DISABLE_CLANG_WARNING(-Wdeprecated-declarations) \ + _ReadWriteBarrier() \ + EA_RESTORE_CLANG_WARNING() ///////////////////////////////////////////////////////////////////////////////// diff --git a/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_strong.h b/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_strong.h index 42117a1..8217f23 100644 --- a/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_strong.h +++ b/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_strong.h @@ -10,7 +10,6 @@ #pragma once #endif - #if defined(EA_PROCESSOR_X86_64) #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_8 _InterlockedCompareExchange8 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 530bbc8..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.17.06" - #define EASTL_VERSION_N 31706 + #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(EA_COMPILER_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 // @@ -1274,7 +1302,7 @@ namespace eastl // useful macro identifier for our type traits implementation. // #ifndef EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE - #if defined(_MSC_VER) && (_MSC_VER >= 1500) // VS2008 or later + #if defined(_MSC_VER) && (_MSC_VER >= 1500) && !defined(EA_COMPILER_CLANG_CL) // VS2008 or later #pragma warning(push, 0) #include <yvals.h> #pragma warning(pop) @@ -1283,9 +1311,9 @@ namespace eastl #else #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 0 #endif - #elif defined(EA_COMPILER_CLANG) && defined(__APPLE__) && defined(_CXXCONFIG) // Apple clang but with GCC's libstdc++. + #elif defined(__clang__) && defined(__APPLE__) && defined(_CXXCONFIG) // Apple clang but with GCC's libstdc++. #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 0 - #elif defined(EA_COMPILER_CLANG) + #elif defined(__clang__) #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 1 #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003) && !defined(__GCCXML__) #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 1 @@ -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 @@ -1836,18 +1854,49 @@ typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept /// EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE -#if defined(_MSC_VER) && (_MSC_VER >= 1913) // VS2017+ - #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 1 -#elif defined(EA_COMPILER_CLANG) +#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 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 @@ -1873,5 +1922,17 @@ typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept #define EASTL_SYSTEM_LITTLE_ENDIAN_STATEMENT(...) #endif +/// EASTL_CONSTEXPR_BIT_CAST_SUPPORTED +/// eastl::bit_cast, in order to be implemented as constexpr, requires explicit compiler support. +/// This macro defines whether it's possible for bit_cast to be constexpr. +/// +#if (defined(EA_COMPILER_MSVC) && defined(EA_COMPILER_MSVC_VERSION_14_26) && EA_COMPILER_VERSION >= EA_COMPILER_MSVC_VERSION_14_26) \ + || EA_COMPILER_HAS_BUILTIN(__builtin_bit_cast) + #define EASTL_CONSTEXPR_BIT_CAST_SUPPORTED 1 +#else + #define EASTL_CONSTEXPR_BIT_CAST_SUPPORTED 0 +#endif + + #endif // Header include guard diff --git a/EASTL/include/EASTL/internal/copy_help.h b/EASTL/include/EASTL/internal/copy_help.h index e5fb2ab..0b2c1b8 100644 --- a/EASTL/include/EASTL/internal/copy_help.h +++ b/EASTL/include/EASTL/internal/copy_help.h @@ -6,12 +6,12 @@ #ifndef EASTL_INTERNAL_COPY_HELP_H #define EASTL_INTERNAL_COPY_HELP_H +#include <EASTL/internal/config.h> #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once #endif -#include <EASTL/internal/config.h> #include <EASTL/type_traits.h> #include <EASTL/iterator.h> #include <string.h> // memcpy, memcmp, memmove @@ -19,15 +19,15 @@ namespace eastl { - /// move / move_n / move_backward + /// move / move_n / move_backward /// copy / copy_n / copy_backward /// /// We want to optimize move, move_n, move_backward, copy, copy_backward, copy_n to do memmove operations - /// when possible. + /// when possible. /// - /// We could possibly use memcpy, though it has stricter overlap requirements than the move and copy - /// algorithms and would require a runtime if/else to choose it over memmove. In particular, memcpy - /// allows no range overlap at all, whereas move/copy allow output end overlap and move_backward/copy_backward + /// We could possibly use memcpy, though it has stricter overlap requirements than the move and copy + /// algorithms and would require a runtime if/else to choose it over memmove. In particular, memcpy + /// allows no range overlap at all, whereas move/copy allow output end overlap and move_backward/copy_backward /// allow output begin overlap. Despite this it might be useful to use memcpy for any platforms where /// memcpy is significantly faster than memmove, and since in most cases the copy/move operation in fact /// doesn't target overlapping memory and so memcpy would be usable. @@ -36,13 +36,13 @@ namespace eastl /// InputIterator and OutputIterator are of the same type. /// InputIterator and OutputIterator are of type contiguous_iterator_tag or simply are pointers (the two are virtually synonymous). /// is_trivially_copyable<T>::value is true. i.e. the constructor T(const T& t) (or T(T&& t) if present) can be replaced by memmove(this, &t, sizeof(T)) - /// - /// copy normally differs from move, but there is a case where copy is the same as move: when copy is - /// used with a move_iterator. We handle that case here by detecting that copy is being done with a + /// + /// copy normally differs from move, but there is a case where copy is the same as move: when copy is + /// used with a move_iterator. We handle that case here by detecting that copy is being done with a /// move_iterator and redirect it to move (which can take advantage of memmove/memcpy). /// - /// The generic_iterator class is typically used for wrapping raw memory pointers so they can act like - /// formal iterators. Since pointers provide an opportunity for memmove/memcpy operations, we can + /// The generic_iterator class is typically used for wrapping raw memory pointers so they can act like + /// formal iterators. Since pointers provide an opportunity for memmove/memcpy operations, we can /// detect a generic iterator and use it's wrapped type as a pointer if it happens to be one. // Implementation moving copying both trivial and non-trivial data via a lesser iterator than random-access. @@ -61,7 +61,7 @@ namespace eastl // Specialization for copying non-trivial data via a random-access iterator. It's theoretically faster because the compiler can see the count when its a compile-time const. // This specialization converts the random access InputIterator last-first to an integral type. There's simple way for us to take advantage of a random access output iterator, // as the range is specified by the input instead of the output, and distance(first, last) for a non-random-access iterator is potentially slow. - template <> + template <> struct move_and_copy_helper<EASTL_ITC_NS::random_access_iterator_tag, false, false> { template <typename InputIterator, typename OutputIterator> @@ -88,7 +88,7 @@ namespace eastl return result; } }; - + // Specialization for moving non-trivial data via a random-access iterator. It's theoretically faster because the compiler can see the count when its a compile-time const. template <> struct move_and_copy_helper<EASTL_ITC_NS::random_access_iterator_tag, true, false> @@ -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,17 +164,17 @@ 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* } /// move /// - /// After this operation the elements in the moved-from range will still contain valid values of the - /// appropriate type, but not necessarily the same values as before the move. + /// After this operation the elements in the moved-from range will still contain valid values of the + /// appropriate type, but not necessarily the same values as before the move. /// Returns the end of the result range. /// Note: When moving between containers, the dest range must be valid; this function doesn't resize containers. - /// Note: if result is within [first, last), move_backward must be used instead of move. + /// Note: if result is within [first, last), move_backward must be used instead of move. /// /// Example usage: /// eastl::move(myArray.begin(), myArray.end(), myDestArray.begin()); @@ -180,7 +201,7 @@ namespace eastl /// starting from first and proceeding to last. For each nonnegative integer n < (last - first), /// performs *(result + n) = *(first + n). /// - /// Returns: result + (last - first). That is, returns the end of the result. Note that this + /// Returns: result + (last - first). That is, returns the end of the result. Note that this /// is different from how memmove/memcpy work, as they return the beginning of the result. /// /// Requires: result shall not be in the range [first, last). But the end of the result range @@ -197,19 +218,4 @@ namespace eastl } } // namespace eastl -#endif // Header include guard - - - - - - - - - - - - - - - +#endif // EASTL_INTERNAL_COPY_HELP_H diff --git a/EASTL/include/EASTL/internal/fill_help.h b/EASTL/include/EASTL/internal/fill_help.h index 235a24e..07e3b62 100644 --- a/EASTL/include/EASTL/internal/fill_help.h +++ b/EASTL/include/EASTL/internal/fill_help.h @@ -85,7 +85,7 @@ namespace eastl } - #if(defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #if (defined(EA_COMPILER_GNUC) || defined(__clang__)) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) #if defined(EA_PROCESSOR_X86_64) template <typename Value> inline void fill(uint64_t* first, uint64_t* last, Value c) @@ -327,7 +327,7 @@ namespace eastl } #endif - #if(defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #if (defined(EA_COMPILER_GNUC) || defined(__clang__)) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) #if defined(EA_PROCESSOR_X86_64) template <typename Size, typename Value> inline uint64_t* fill_n(uint64_t* first, Size n, Value c) diff --git a/EASTL/include/EASTL/internal/fixed_pool.h b/EASTL/include/EASTL/internal/fixed_pool.h index 5a38004..61c0557 100644 --- a/EASTL/include/EASTL/internal/fixed_pool.h +++ b/EASTL/include/EASTL/internal/fixed_pool.h @@ -1362,12 +1362,10 @@ namespace eastl { } - // Disabled because the default is sufficient. - //fixed_vector_allocator(const fixed_vector_allocator& x) - //{ - // mpPoolBegin = x.mpPoolBegin; - // mOverflowAllocator = x.mOverflowAllocator; - //} + fixed_vector_allocator(const fixed_vector_allocator& x) + : mOverflowAllocator(x.mOverflowAllocator), mpPoolBegin(x.mpPoolBegin) + { + } fixed_vector_allocator& operator=(const fixed_vector_allocator& x) { @@ -1481,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 6e857f0..ace71d8 100644 --- a/EASTL/include/EASTL/internal/function.h +++ b/EASTL/include/EASTL/internal/function.h @@ -5,6 +5,8 @@ #ifndef EASTL_FUNCTION_H #define EASTL_FUNCTION_H +#include <EASTL/internal/config.h> + #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once #endif @@ -131,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 { @@ -149,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/function_detail.h b/EASTL/include/EASTL/internal/function_detail.h index dc18b63..3ee3667 100644 --- a/EASTL/include/EASTL/internal/function_detail.h +++ b/EASTL/include/EASTL/internal/function_detail.h @@ -95,7 +95,7 @@ namespace eastl template <typename Functor, int SIZE_IN_BYTES> struct is_functor_inplace_allocatable { - static constexpr bool value = + static EA_CONSTEXPR bool value = sizeof(Functor) <= sizeof(functor_storage<SIZE_IN_BYTES>) && (eastl::alignment_of_v<functor_storage<SIZE_IN_BYTES>> % eastl::alignment_of_v<Functor>) == 0; }; diff --git a/EASTL/include/EASTL/internal/functional_base.h b/EASTL/include/EASTL/internal/functional_base.h index a7d2dc9..de446db 100644 --- a/EASTL/include/EASTL/internal/functional_base.h +++ b/EASTL/include/EASTL/internal/functional_base.h @@ -6,21 +6,23 @@ #ifndef EASTL_INTERNAL_FUNCTIONAL_BASE_H #define EASTL_INTERNAL_FUNCTIONAL_BASE_H +#include <EASTL/internal/config.h> + #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once #endif -#include <EASTL/internal/config.h> #include <EASTL/internal/memory_base.h> #include <EASTL/internal/move_help.h> #include <EASTL/type_traits.h> + namespace eastl { // foward declaration for swap template <typename T> inline void swap(T& a, T& b) - EA_NOEXCEPT_IF(eastl::is_nothrow_move_constructible<T>::value&& eastl::is_nothrow_move_assignable<T>::value); + EA_NOEXCEPT_IF(eastl::is_nothrow_move_constructible<T>::value && eastl::is_nothrow_move_assignable<T>::value); /// invoke @@ -39,44 +41,47 @@ namespace eastl /// http://en.cppreference.com/w/cpp/utility/functional/invoke /// template <typename R, typename C, typename T, typename... Args> - auto invoke_impl(R C::*func, T&& obj, Args&&... args) -> - typename enable_if<is_base_of<C, decay_t<decltype(obj)>>::value, + EA_CONSTEXPR auto invoke_impl(R C::*func, T&& obj, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR((eastl::forward<T>(obj).*func)(eastl::forward<Args>(args)...))) + -> typename enable_if<is_base_of<C, decay_t<T>>::value, decltype((eastl::forward<T>(obj).*func)(eastl::forward<Args>(args)...))>::type { return (eastl::forward<T>(obj).*func)(eastl::forward<Args>(args)...); } template <typename F, typename... Args> - auto invoke_impl(F&& func, Args&&... args) -> decltype(eastl::forward<F>(func)(eastl::forward<Args>(args)...)) + EA_CONSTEXPR auto invoke_impl(F&& func, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(eastl::forward<F>(func)(eastl::forward<Args>(args)...))) + -> decltype(eastl::forward<F>(func)(eastl::forward<Args>(args)...)) { return eastl::forward<F>(func)(eastl::forward<Args>(args)...); } template <typename R, typename C, typename T, typename... Args> - auto invoke_impl(R C::*func, T&& obj, Args&&... args) -> decltype(((*eastl::forward<T>(obj)).*func)(eastl::forward<Args>(args)...)) + EA_CONSTEXPR auto invoke_impl(R C::*func, T&& obj, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(((*eastl::forward<T>(obj)).*func)(eastl::forward<Args>(args)...))) + -> decltype(((*eastl::forward<T>(obj)).*func)(eastl::forward<Args>(args)...)) { return ((*eastl::forward<T>(obj)).*func)(eastl::forward<Args>(args)...); } template <typename M, typename C, typename T> - auto invoke_impl(M C::*member, T&& obj) -> - typename enable_if< - is_base_of<C, decay_t<decltype(obj)>>::value, - decltype(obj.*member) + EA_CONSTEXPR auto invoke_impl(M C::*member, T&& obj) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(eastl::forward<T>(obj).*member)) + -> typename enable_if< + is_base_of<C, decay_t<T>>::value, + decltype(eastl::forward<T>(obj).*member) >::type { - return obj.*member; + return eastl::forward<T>(obj).*member; } template <typename M, typename C, typename T> - auto invoke_impl(M C::*member, T&& obj) -> decltype((*eastl::forward<T>(obj)).*member) + EA_CONSTEXPR auto invoke_impl(M C::*member, T&& obj) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR((*eastl::forward<T>(obj)).*member)) + -> decltype((*eastl::forward<T>(obj)).*member) { return (*eastl::forward<T>(obj)).*member; } template <typename F, typename... Args> - inline decltype(auto) invoke(F&& func, Args&&... args) + EA_CONSTEXPR decltype(auto) invoke(F&& func, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(invoke_impl(eastl::forward<F>(func), eastl::forward<Args>(args)...))) { return invoke_impl(eastl::forward<F>(func), eastl::forward<Args>(args)...); } @@ -86,9 +91,9 @@ namespace eastl }; template <typename F, typename... Args> - struct invoke_result_impl<F, void_t<decltype(invoke_impl(eastl::declval<decay_t<F>>(), eastl::declval<Args>()...))>, Args...> + struct invoke_result_impl<F, void_t<decltype(invoke_impl(eastl::declval<F>(), eastl::declval<Args>()...))>, Args...> { - typedef decltype(invoke_impl(eastl::declval<decay_t<F>>(), eastl::declval<Args>()...)) type; + typedef decltype(invoke_impl(eastl::declval<F>(), eastl::declval<Args>()...)) type; }; template <typename F, typename... Args> @@ -113,18 +118,46 @@ 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...> {}; - #if EASTL_VARIABLE_TEMPLATES_ENABLED - template <typename F, typename... Args> - EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_invocable_v = is_invocable<F, Args...>::value; + template <typename F, typename... Args> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_invocable_v = is_invocable<F, Args...>::value; - template <typename R, typename F, typename... Args> - EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_invocable_r_v = is_invocable_r<R, F, Args...>::value; - #endif + template <typename R, typename F, typename... Args> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_invocable_r_v = is_invocable_r<R, F, Args...>::value; + + template <typename F, typename = void, typename... Args> + struct is_nothrow_invocable_impl : public eastl::false_type {}; + + template <typename F, typename... Args> + struct is_nothrow_invocable_impl<F, void_t<typename eastl::invoke_result<F, Args...>::type>, Args...> + : public eastl::bool_constant<EA_NOEXCEPT_EXPR(eastl::invoke(eastl::declval<F>(), eastl::declval<Args>()...))> {}; + + template <typename F, typename... Args> + struct is_nothrow_invocable : public is_nothrow_invocable_impl<F, void, Args...> {}; + + template <typename R, typename F, typename = void, typename... Args> + struct is_nothrow_invocable_r_impl : public eastl::false_type {}; + + template <typename R, typename F, typename... Args> + struct is_nothrow_invocable_r_impl<R, F, void_t<typename eastl::invoke_result<F, Args...>::type>, Args...> + { + static EA_CONSTEXPR_OR_CONST bool value = eastl::is_convertible<typename eastl::invoke_result<F, Args...>::type, R>::value + && eastl::is_nothrow_invocable<F, Args...>::value; + }; + + template <typename R, typename F, typename... Args> + struct is_nothrow_invocable_r : public is_nothrow_invocable_r_impl<R, F, void, Args...> {}; + + template <typename F, typename... Args> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_no_throw_invocable_v = is_nothrow_invocable<F, Args...>::value; + + template <typename R, typename F, typename... Args> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_nothrow_invocable_r_v = is_nothrow_invocable_r<R, F, Args...>::value; /// allocator_arg_t /// @@ -144,9 +177,7 @@ namespace eastl /// such as tuple, function, promise, and packaged_task. /// http://en.cppreference.com/w/cpp/memory/allocator_arg /// - #if !defined(EA_COMPILER_NO_CONSTEXPR) - EA_CONSTEXPR allocator_arg_t allocator_arg = allocator_arg_t(); - #endif + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR allocator_arg_t allocator_arg = allocator_arg_t(); template <typename Argument, typename Result> @@ -202,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; @@ -239,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)...); } @@ -248,7 +279,7 @@ namespace eastl template <typename T> reference_wrapper<T> ref(T& t) EA_NOEXCEPT { - return eastl::reference_wrapper<T>(t); + return eastl::reference_wrapper<T>(t); } template <typename T> @@ -307,16 +338,16 @@ namespace eastl // These have to come after reference_wrapper is defined, but reference_wrapper needs to have a // definition of invoke, so these specializations need to come after everything else has been defined. template <typename R, typename C, typename T, typename... Args> - auto invoke_impl(R (C::*func)(Args...), T&& obj, Args&&... args) -> - typename enable_if<is_reference_wrapper<typename remove_reference<T>::type>::value, + EA_CONSTEXPR auto invoke_impl(R C::*func, T&& obj, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR((obj.get().*func)(eastl::forward<Args>(args)...))) + -> typename enable_if<is_reference_wrapper<eastl::decay_t<T>>::value, decltype((obj.get().*func)(eastl::forward<Args>(args)...))>::type { return (obj.get().*func)(eastl::forward<Args>(args)...); } template <typename M, typename C, typename T> - auto invoke_impl(M(C::*member), T&& obj) -> - typename enable_if<is_reference_wrapper<typename remove_reference<T>::type>::value, + EA_CONSTEXPR auto invoke_impl(M C::*member, T&& obj) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(obj.get().*member)) + -> typename enable_if<is_reference_wrapper<eastl::decay_t<T>>::value, decltype(obj.get().*member)>::type { return obj.get().*member; @@ -386,4 +417,4 @@ namespace eastl } // namespace eastl -#endif // Header include guard +#endif // EASTL_INTERNAL_FUNCTIONAL_BASE_H 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 bb6d27e..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 @@ -1572,7 +1585,7 @@ namespace eastl typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type* hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoAllocateNodeFromKey(const key_type& key) { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(value_type), 0); + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); #if EASTL_EXCEPTIONS_ENABLED @@ -1598,7 +1611,7 @@ namespace eastl typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type* hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoAllocateNodeFromKey(key_type&& key) { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(value_type), 0); + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); #if EASTL_EXCEPTIONS_ENABLED @@ -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 { @@ -2105,7 +2136,7 @@ namespace eastl typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type* hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoAllocateNode(Args&&... args) { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(value_type), 0); + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); #if EASTL_EXCEPTIONS_ENABLED @@ -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. @@ -2283,7 +2295,7 @@ namespace eastl typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type* hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoAllocateNode(value_type&& value) { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(value_type), 0); + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); #if EASTL_EXCEPTIONS_ENABLED @@ -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> @@ -2453,7 +2393,7 @@ namespace eastl typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type* hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoAllocateNode(const value_type& value) { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(value_type), 0); + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); #if EASTL_EXCEPTIONS_ENABLED @@ -2480,7 +2420,7 @@ namespace eastl hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::allocate_uninitialized_node() { // We don't wrap this in try/catch because users of this function are expected to do that themselves as needed. - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(value_type), 0); + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); // Leave pNode->mValue uninitialized. pNode->mpNext = NULL; @@ -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 88cf1b1..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,19 +63,54 @@ 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)>; +namespace internal +{ + +template <typename T> +struct integer_sequence_size_helper; + +template <typename T, T... Ints> +struct integer_sequence_size_helper<eastl::integer_sequence<T, Ints...>> : public integral_constant<size_t, sizeof...(Ints)> +{ +}; + +template <typename T> +struct integer_sequence_size : public integer_sequence_size_helper<eastl::remove_cv_t<T>> +{ +}; + +template <typename T> +struct index_sequence_size : public integer_sequence_size_helper<eastl::remove_cv_t<T>> +{ +}; + +template <typename T> +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t integer_sequence_size_v = integer_sequence_size<T>::value; + +template <typename T> +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t index_sequence_size_v = index_sequence_size<T>::value; + + +} // namespace internal + #endif // EASTL_VARIADIC_TEMPLATES_ENABLED } // namespace eastl diff --git a/EASTL/include/EASTL/internal/red_black_tree.h b/EASTL/include/EASTL/internal/red_black_tree.h index 7448bd4..5b29b7c 100644 --- a/EASTL/include/EASTL/internal/red_black_tree.h +++ b/EASTL/include/EASTL/internal/red_black_tree.h @@ -169,6 +169,7 @@ namespace eastl rbtree_iterator(); explicit rbtree_iterator(const node_type* pNode); rbtree_iterator(const iterator& x); + rbtree_iterator& operator=(const iterator& x); reference operator*() const; pointer operator->() const; @@ -477,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); @@ -662,6 +658,13 @@ namespace eastl rbtree_iterator<T, Pointer, Reference>::rbtree_iterator(const iterator& x) : mpNode(x.mpNode) { } + template <typename T, typename Pointer, typename Reference> + typename rbtree_iterator<T, Pointer, Reference>::this_type& + rbtree_iterator<T, Pointer, Reference>::operator=(const iterator& x) + { + mpNode = x.mpNode; + return *this; + } template <typename T, typename Pointer, typename Reference> typename rbtree_iterator<T, Pointer, Reference>::reference @@ -1101,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/smart_ptr.h b/EASTL/include/EASTL/internal/smart_ptr.h index f1d52e1..8a37950 100644 --- a/EASTL/include/EASTL/internal/smart_ptr.h +++ b/EASTL/include/EASTL/internal/smart_ptr.h @@ -162,7 +162,10 @@ namespace eastl default_delete(const default_delete<U>&, typename eastl::enable_if<is_convertible<U*, T*>::value>::type* = 0) EA_NOEXCEPT {} void operator()(T* p) const EA_NOEXCEPT - { delete p; } + { + static_assert(eastl::internal::is_complete_type_v<T>, "Attempting to call the destructor of an incomplete type"); + delete p; + } }; diff --git a/EASTL/include/EASTL/internal/thread_support.h b/EASTL/include/EASTL/internal/thread_support.h index 80386d2..49856c0 100644 --- a/EASTL/include/EASTL/internal/thread_support.h +++ b/EASTL/include/EASTL/internal/thread_support.h @@ -19,10 +19,12 @@ // // fatal error C1189: <mutex> is not supported when compiling with /clr or /clr:pure ///////////////////////////////////////////////////////////////////////////////////////////////////// -#if defined(EA_HAVE_CPP11_MUTEX) && !defined(EA_COMPILER_MANAGED_CPP) - #define EASTL_CPP11_MUTEX_ENABLED 1 -#else - #define EASTL_CPP11_MUTEX_ENABLED 0 +#if !defined(EASTL_CPP11_MUTEX_ENABLED) + #if defined(EA_HAVE_CPP11_MUTEX) && !defined(EA_COMPILER_MANAGED_CPP) + #define EASTL_CPP11_MUTEX_ENABLED 1 + #else + #define EASTL_CPP11_MUTEX_ENABLED 0 + #endif #endif #if EASTL_CPP11_MUTEX_ENABLED @@ -77,7 +79,7 @@ EA_DISABLE_VC_WARNING(4625 4626 4275); /////////////////////////////////////////////////////////////////////////////// #if !defined(EASTL_THREAD_SUPPORT_AVAILABLE) - #if defined(EA_COMPILER_CLANG) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) + #if defined(__clang__) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) #define EASTL_THREAD_SUPPORT_AVAILABLE 1 #elif defined(EA_COMPILER_MSVC) #define EASTL_THREAD_SUPPORT_AVAILABLE 1 @@ -91,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(EA_COMPILER_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(EA_COMPILER_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(EA_COMPILER_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 178a734..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> @@ -290,7 +256,7 @@ namespace eastl // is_convertible<D*, A*>::value; // Generates compiler error. /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_convertible_to))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_convertible_to))) #define EASTL_TYPE_TRAIT_is_convertible_CONFORMANCE 1 // is_convertible is conforming. // Problem: VC++ reports that int is convertible to short, yet if you construct a short from an int then VC++ generates a warning: @@ -371,7 +337,7 @@ namespace eastl // via 'msl::is_union<T>::value'. The user can force something to be // evaluated as a union via EASTL_DECLARE_UNION. /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_union))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_union))) #define EASTL_TYPE_TRAIT_is_union_CONFORMANCE 1 // is_union is conforming. template <typename T> @@ -401,7 +367,7 @@ namespace eastl // distinguish between unions and classes. As a result, is_class // will erroneously evaluate to true for union types. /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_class))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_class))) #define EASTL_TYPE_TRAIT_is_class_CONFORMANCE 1 // is_class is conforming. template <typename T> @@ -442,57 +408,6 @@ namespace eastl #endif - /////////////////////////////////////////////////////////////////////// - // is_enum - // - // is_enum<T>::value == true if and only if T is an enumeration type. - // - /////////////////////////////////////////////////////////////////////// - - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_enum))) - #define EASTL_TYPE_TRAIT_is_enum_CONFORMANCE 1 // is_enum is conforming. - - template <typename T> - struct is_enum : public integral_constant<bool, __is_enum(T)>{}; - #else - #define EASTL_TYPE_TRAIT_is_enum_CONFORMANCE 1 // is_enum is conforming. - - struct int_convertible{ int_convertible(int); }; - - template <bool is_arithmetic_or_reference> - struct is_enum_helper { template <typename T> struct nest : public is_convertible<T, int_convertible>{}; }; - - template <> - struct is_enum_helper<true> { template <typename T> struct nest : public false_type {}; }; - - template <typename T> - struct is_enum_helper2 - { - typedef type_or<is_arithmetic<T>::value, is_reference<T>::value, is_class<T>::value> selector; - typedef is_enum_helper<selector::value> helper_t; - typedef typename add_reference<T>::type ref_t; - typedef typename helper_t::template nest<ref_t> result; - }; - - template <typename T> - struct is_enum : public integral_constant<bool, is_enum_helper2<T>::result::value>{}; - - template <> struct is_enum<void> : public false_type {}; - template <> struct is_enum<void const> : public false_type {}; - template <> struct is_enum<void volatile> : public false_type {}; - template <> struct is_enum<void const volatile> : public false_type {}; - #endif - - #if EASTL_VARIABLE_TEMPLATES_ENABLED - template<typename T> - EA_CONSTEXPR bool is_enum_v = is_enum<T>::value; - #endif - - #define EASTL_DECLARE_ENUM(T) namespace eastl{ template <> struct is_enum<T> : public true_type{}; template <> struct is_enum<const T> : public true_type{}; } - - - - /////////////////////////////////////////////////////////////////////// // is_polymorphic @@ -503,7 +418,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_polymorphic))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_polymorphic))) #define EASTL_TYPE_TRAIT_is_polymorphic_CONFORMANCE 1 // is_polymorphic is conforming. template <typename T> @@ -741,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 @@ -773,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 950d15e..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(EA_COMPILER_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 @@ -262,6 +266,59 @@ namespace eastl EA_CONSTEXPR bool is_hat_type_v = is_hat_type<T>::value; #endif + + + /////////////////////////////////////////////////////////////////////// + // is_enum + // + // is_enum<T>::value == true if and only if T is an enumeration type. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_enum))) + #define EASTL_TYPE_TRAIT_is_enum_CONFORMANCE 1 // is_enum is conforming. + + template <typename T> + struct is_enum : public integral_constant<bool, __is_enum(T)>{}; + #else + #define EASTL_TYPE_TRAIT_is_enum_CONFORMANCE 1 // is_enum is conforming. + + struct int_convertible{ int_convertible(int); }; + + template <bool is_arithmetic_or_reference> + struct is_enum_helper { template <typename T> struct nest : public is_convertible<T, int_convertible>{}; }; + + template <> + struct is_enum_helper<true> { template <typename T> struct nest : public false_type {}; }; + + template <typename T> + struct is_enum_helper2 + { + typedef type_or<is_arithmetic<T>::value, is_reference<T>::value, is_class<T>::value> selector; + typedef is_enum_helper<selector::value> helper_t; + typedef typename add_reference<T>::type ref_t; + typedef typename helper_t::template nest<ref_t> result; + }; + + template <typename T> + struct is_enum : public integral_constant<bool, is_enum_helper2<T>::result::value>{}; + + template <> struct is_enum<void> : public false_type {}; + template <> struct is_enum<void const> : public false_type {}; + template <> struct is_enum<void volatile> : public false_type {}; + template <> struct is_enum<void const volatile> : public false_type {}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template<typename T> + EA_CONSTEXPR bool is_enum_v = is_enum<T>::value; + #endif + + #define EASTL_DECLARE_ENUM(T) namespace eastl{ template <> struct is_enum<T> : public true_type{}; template <> struct is_enum<const T> : public true_type{}; } + + + + } // namespace eastl diff --git a/EASTL/include/EASTL/internal/type_pod.h b/EASTL/include/EASTL/internal/type_pod.h index 8726a7e..fef5511 100644 --- a/EASTL/include/EASTL/internal/type_pod.h +++ b/EASTL/include/EASTL/internal/type_pod.h @@ -25,7 +25,7 @@ namespace eastl // // is_empty cannot be used with union types until is_union can be made to work. /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_empty))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_empty))) #define EASTL_TYPE_TRAIT_is_empty_CONFORMANCE 1 // is_empty is conforming. template <typename T> @@ -90,7 +90,7 @@ namespace eastl struct is_pod : public eastl::integral_constant<bool, (__has_trivial_constructor(T) && __is_pod(T) && !eastl::is_hat_type<T>::value) || eastl::is_void<T>::value || eastl::is_scalar<T>::value>{}; EA_RESTORE_VC_WARNING() - #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_pod))) + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_pod))) #define EASTL_TYPE_TRAIT_is_pod_CONFORMANCE 1 // is_pod is conforming. template <typename T> @@ -128,7 +128,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////// // is_standard_layout // - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(EA_COMPILER_MSVC) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006)) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_standard_layout))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(EA_COMPILER_MSVC) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006)) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_standard_layout))) #define EASTL_TYPE_TRAIT_is_standard_layout_CONFORMANCE 1 // is_standard_layout is conforming. template <typename T> @@ -182,12 +182,12 @@ namespace eastl // can be called without an argument. /////////////////////////////////////////////////////////////////////// - #if defined(_MSC_VER) && (_MSC_VER >= 1600) // VS2010+ + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && !defined(EA_COMPILER_CLANG_CL) // VS2010+ #define EASTL_TYPE_TRAIT_has_trivial_constructor_CONFORMANCE 1 // has_trivial_constructor is conforming. template <typename T> struct has_trivial_constructor : public eastl::integral_constant<bool, (__has_trivial_constructor(T) || eastl::is_pod<T>::value) && !eastl::is_hat_type<T>::value>{}; - #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_has_trivial_constructor_CONFORMANCE 1 // has_trivial_constructor is conforming. template <typename T> @@ -242,12 +242,12 @@ namespace eastl // use EASTL_DECLARE_TRIVIAL_COPY to help the compiler. /////////////////////////////////////////////////////////////////////// - #if defined(_MSC_VER) + #if defined(_MSC_VER) && !defined(EA_COMPILER_CLANG_CL) #define EASTL_TYPE_TRAIT_has_trivial_copy_CONFORMANCE 1 // has_trivial_copy is conforming. template <typename T> struct has_trivial_copy : public eastl::integral_constant<bool, (__has_trivial_copy(T) || eastl::is_pod<T>::value) && !eastl::is_volatile<T>::value && !eastl::is_hat_type<T>::value>{}; - #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_has_trivial_copy_CONFORMANCE 1 // has_trivial_copy is conforming. template <typename T> @@ -292,12 +292,12 @@ namespace eastl // can use EASTL_DECLARE_TRIVIAL_ASSIGN to help the compiler. /////////////////////////////////////////////////////////////////////// - #if defined(_MSC_VER) && (_MSC_VER >= 1600) + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && !defined(EA_COMPILER_CLANG_CL) #define EASTL_TYPE_TRAIT_has_trivial_assign_CONFORMANCE 1 // has_trivial_assign is conforming. template <typename T> struct has_trivial_assign : public integral_constant<bool, (__has_trivial_assign(T) || eastl::is_pod<T>::value) && !eastl::is_const<T>::value && !eastl::is_volatile<T>::value && !eastl::is_hat_type<T>::value>{}; - #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_has_trivial_assign_CONFORMANCE 1 // has_trivial_assign is conforming. template <typename T> @@ -341,12 +341,12 @@ namespace eastl // The user can use EASTL_DECLARE_TRIVIAL_DESTRUCTOR to help the compiler. /////////////////////////////////////////////////////////////////////// - #if defined(_MSC_VER) && (_MSC_VER >= 1600) + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && !defined(EA_COMPILER_CLANG_CL) #define EASTL_TYPE_TRAIT_has_trivial_destructor_CONFORMANCE 1 // has_trivial_destructor is conforming. template <typename T> struct has_trivial_destructor : public eastl::integral_constant<bool, (__has_trivial_destructor(T) || eastl::is_pod<T>::value) && !eastl::is_hat_type<T>::value>{}; - #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_has_trivial_destructor_CONFORMANCE 1 // has_trivial_destructor is conforming. template <typename T> @@ -412,7 +412,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_has_nothrow_constructor_CONFORMANCE 1 template <typename T> @@ -452,7 +452,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_has_nothrow_copy_CONFORMANCE 1 template <typename T> @@ -491,7 +491,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_has_nothrow_assign_CONFORMANCE 1 template <typename T> @@ -529,7 +529,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_has_virtual_destructor_CONFORMANCE 1 template <typename T> @@ -571,7 +571,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_literal)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_literal)) #define EASTL_TYPE_TRAIT_is_literal_type_CONFORMANCE 1 template <typename T> @@ -615,7 +615,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_abstract))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_abstract))) #define EASTL_TYPE_TRAIT_is_abstract_CONFORMANCE 1 // is_abstract is conforming. template <typename T> @@ -672,7 +672,7 @@ namespace eastl // up obj1 are copied into obj2, obj2 shall subsequently hold the // same value as obj1. In other words, you can memcpy/memmove it. /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(_MSC_VER) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 5003)) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_trivially_copyable))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(_MSC_VER) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 5003)) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_trivially_copyable))) #define EASTL_TYPE_TRAIT_is_trivially_copyable_CONFORMANCE 1 // https://connect.microsoft.com/VisualStudio/feedback/details/808827/c-std-is-trivially-copyable-produces-wrong-result-for-arrays @@ -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 @@ -731,7 +731,7 @@ namespace eastl #define EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE 1 - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_constructible))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_constructible))) template<typename T, typename... Args> struct is_constructible : public bool_constant<__is_constructible(T, Args...) > {}; #else @@ -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(EA_COMPILER_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(EA_COMPILER_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. @@ -1380,7 +1380,7 @@ namespace eastl // arrays of unknown bound /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_trivially_assignable)) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_trivially_assignable)) #define EASTL_TYPE_TRAIT_is_trivially_assignable_CONFORMANCE 1 template <typename T, typename U> @@ -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,14 +1774,14 @@ 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> struct is_trivially_destructible : integral_constant<bool, __is_trivially_destructible(T)> {}; - #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) #define EASTL_TYPE_TRAIT_is_trivially_destructible_CONFORMANCE EASTL_TYPE_TRAIT_is_destructible_CONFORMANCE 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 5276f87..78bdfca 100644 --- a/EASTL/include/EASTL/internal/type_properties.h +++ b/EASTL/include/EASTL/internal/type_properties.h @@ -28,7 +28,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(_MSC_VER) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007)) || defined(EA_COMPILER_CLANG)) // VS2012+ + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(_MSC_VER) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007)) || defined(__clang__)) // VS2012+ #define EASTL_TYPE_TRAIT_underlying_type_CONFORMANCE 1 // underlying_type is conforming. template <typename T> @@ -46,6 +46,25 @@ namespace eastl using underlying_type_t = typename underlying_type<T>::type; #endif + /////////////////////////////////////////////////////////////////////// + // to_underlying + // + // Cast a enum value to its underlying type. + // For example: + // + // enum class MyEnum : uint8_t { Value = 0; } + // auto x = MyEnum::Value; + // std::cout << to_underlying(x); // equivalent to sts::cout << static_cast<uint8_t>(x); + /////////////////////////////////////////////////////////////////////// + + #if EASTL_VARIABLE_TEMPLATES_ENABLED && !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template<class T> + constexpr underlying_type_t<T> to_underlying(T value) noexcept + { + return static_cast<underlying_type_t<T>>(value); + } + #endif + /////////////////////////////////////////////////////////////////////// // has_unique_object_representations @@ -85,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> @@ -145,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> @@ -194,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 @@ -222,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. @@ -281,7 +320,7 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_base_of))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || ((defined(__clang__)) && EA_COMPILER_HAS_FEATURE(is_base_of))) #define EASTL_TYPE_TRAIT_is_base_of_CONFORMANCE 1 // is_base_of is conforming. template <typename Base, typename Derived> @@ -374,6 +413,44 @@ namespace eastl EA_CONSTEXPR auto has_equality_v = has_equality<T>::value; #endif + namespace internal + { + /////////////////////////////////////////////////////////////////////// + // is_complete_type + // + // Determines if the specified type is complete + // + // Warning: Be careful when using is_complete_type since the value is fixed at first instantiation. + // Consider the following: + // + // struct Foo; + // is_complete_type_v<Foo> // false + // struct Foo {}; + // is_complete_type_v<Foo> // still false + /////////////////////////////////////////////////////////////////////// + + template<typename T, typename = void> + struct is_complete_type : public false_type {}; + + template<typename T> + struct is_complete_type<T, eastl::void_t<decltype(sizeof(T) != 0)>> : public true_type {}; + + template<> + struct is_complete_type<const volatile void> : public false_type {}; + template<> + struct is_complete_type<const void> : public false_type {}; + template<> + struct is_complete_type<volatile void> : public false_type {}; + template<> + struct is_complete_type<void> : public false_type {}; + + template<typename T> + struct is_complete_type<T, eastl::enable_if_t<eastl::is_function_v<T>>> : public true_type {}; + + template <typename T> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_complete_type_v = is_complete_type<T, void>::value; + } + } // namespace eastl diff --git a/EASTL/include/EASTL/internal/type_transformations.h b/EASTL/include/EASTL/internal/type_transformations.h index cffa65e..5454cfa 100644 --- a/EASTL/include/EASTL/internal/type_transformations.h +++ b/EASTL/include/EASTL/internal/type_transformations.h @@ -111,54 +111,148 @@ namespace eastl // make_signed // // Used to convert an integral type to its signed equivalent, if not already. - // T shall be a (possibly const and/or volatile-qualified) integral type + // T shall be a (possibly const and/or volatile-qualified) integral type // or enumeration but not a bool type.; // - // The user can define their own make_signed overrides for their own + // The user can define their own make_signed overrides for their own // types by making a template specialization like done below and adding // it to the user's code. /////////////////////////////////////////////////////////////////////// - // To do: This implementation needs to be updated to support C++11 conformance (recognition of enums) and - // to support volatile-qualified types. It will probably be useful to have it fail for unsupported types. - #define EASTL_TYPE_TRAIT_make_signed_CONFORMANCE 0 // make_signed is only partially conforming. + #define EASTL_TYPE_TRAIT_make_signed_CONFORMANCE 1 - template <typename T> struct make_signed { typedef T type; }; + namespace internal + { + template <typename T, bool = eastl::is_enum_v<T> || eastl::is_integral_v<T>> + struct make_signed_helper_0 + { + struct char_helper + { + typedef signed char type; + }; + + struct short_helper + { + typedef signed short type; + }; + + struct int_helper + { + typedef signed int type; + }; + + struct long_helper + { + typedef signed long type; + }; + + struct longlong_helper + { + typedef signed long long type; + }; + + struct int128_helper + { + #if EASTL_GCC_STYLE_INT128_SUPPORTED + typedef __int128_t type; + #endif + }; + + struct no_type_helper + { + }; + + typedef typename + eastl::conditional<sizeof(T) <= sizeof(signed char), char_helper, + eastl::conditional_t<sizeof(T) <= sizeof(signed short), short_helper, + 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_GCC_STYLE_INT128_SUPPORTED + eastl::conditional_t<sizeof(T) <= sizeof(__int128_t), int128_helper, + no_type_helper + > + #else + no_type_helper + #endif + > + > + > + > + >::type type; + }; + + template <typename T> + struct make_signed_helper_0<T, false> + { + struct no_type_helper + { + }; + + typedef no_type_helper type; + }; + + template <typename T> + struct make_signed_helper_1 + { + typedef typename T::type type; + }; + + template <typename T> + struct make_signed_helper + { + typedef typename eastl::internal::make_signed_helper_1<typename eastl::internal::make_signed_helper_0<T>::type>::type type; + }; + + } // namespace internal + + template <typename T> + struct make_signed + { + typedef typename eastl::internal::make_signed_helper<T>::type type; + }; + template <> struct make_signed<bool> {}; + template <> struct make_signed<signed char> { typedef signed char type; }; template <> struct make_signed<unsigned char> { typedef signed char type; }; - template <> struct make_signed<const unsigned char> { typedef const signed char type; }; + template <> struct make_signed<signed short> { typedef signed short type; }; template <> struct make_signed<unsigned short> { typedef signed short type; }; - template <> struct make_signed<const unsigned short> { typedef const signed short type; }; + template <> struct make_signed<signed int> { typedef signed int type; }; template <> struct make_signed<unsigned int> { typedef signed int type; }; - template <> struct make_signed<const unsigned int> { typedef const signed int type; }; + template <> struct make_signed<signed long> { typedef signed long type; }; template <> struct make_signed<unsigned long> { typedef signed long type; }; - template <> struct make_signed<const unsigned long> { typedef const 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; }; - template <> struct make_signed<const unsigned long long> { typedef const signed long long type; }; + #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 + #if (defined(CHAR_MAX) && defined(UCHAR_MAX) && (CHAR_MAX == UCHAR_MAX)) // If char is unsigned, we convert char to signed char. However, if char is signed then make_signed returns char itself and not signed char. template <> struct make_signed<char> { typedef signed char type; }; - template <> struct make_signed<const char> { typedef signed char 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__ == 4294967295U)) // If wchar_t is a 32 bit unsigned value... - template<> - struct make_signed<wchar_t> - { typedef int32_t type; }; - #elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ == 65535)) // If wchar_t is a 16 bit unsigned value... - template<> - struct make_signed<wchar_t> - { typedef int16_t type; }; - #elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ == 255)) // If wchar_t is an 8 bit unsigned value... - template<> - struct make_signed<wchar_t> - { typedef int8_t type; }; - #endif - #endif + template <typename T> + struct make_signed<const T> + { + typedef eastl::add_const_t<typename eastl::make_signed<T>::type> type; + }; + + template <typename T> + struct make_signed<volatile T> + { + typedef eastl::add_volatile_t<typename eastl::make_signed<T>::type> type; + }; + + template <typename T> + struct make_signed<const volatile T> + { + typedef eastl::add_cv_t<typename eastl::make_signed<T>::type> type; + }; #if EASTL_VARIABLE_TEMPLATES_ENABLED - template <class T> + template <typename T> using make_signed_t = typename make_signed<T>::type; #endif @@ -180,55 +274,155 @@ namespace eastl /////////////////////////////////////////////////////////////////////// // make_unsigned // - // Used to convert an integral type to its signed equivalent, if not already. - // T shall be a (possibly const and/or volatile-qualified) integral type + // Used to convert an integral type to its unsigned equivalent, if not already. + // T shall be a (possibly const and/or volatile-qualified) integral type // or enumeration but not a bool type.; // - // The user can define their own make_signed overrides for their own + // The user can define their own make_unsigned overrides for their own // types by making a template specialization like done below and adding // it to the user's code. /////////////////////////////////////////////////////////////////////// - // To do: This implementation needs to be updated to support C++11 conformance (recognition of enums) and - // to support volatile-qualified types. It will probably be useful to have it fail for unsupported types. - #define EASTL_TYPE_TRAIT_make_unsigned_CONFORMANCE 0 // make_unsigned is only partially conforming. + #define EASTL_TYPE_TRAIT_make_unsigned_CONFORMANCE 1 + + namespace internal + { + + template <typename T, bool = eastl::is_enum<T>::value || eastl::is_integral<T>::value> + struct make_unsigned_helper_0 + { + struct char_helper + { + typedef unsigned char type; + }; - template <typename T> struct make_unsigned { typedef T type; }; + struct short_helper + { + typedef unsigned short type; + }; + struct int_helper + { + typedef unsigned int type; + }; + + struct long_helper + { + typedef unsigned long type; + }; + + struct longlong_helper + { + typedef unsigned long long type; + }; + + struct int128_helper + { + #if EASTL_GCC_STYLE_INT128_SUPPORTED + typedef __uint128_t type; + #endif + }; + + struct no_type_helper + { + }; + + + typedef typename + eastl::conditional<sizeof(T) <= sizeof(unsigned char), char_helper, + eastl::conditional_t<sizeof(T) <= sizeof(unsigned short), short_helper, + 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_GCC_STYLE_INT128_SUPPORTED + eastl::conditional_t<sizeof(T) <= sizeof(__uint128_t), int128_helper, + no_type_helper + > + #else + no_type_helper + #endif + > + > + > + > + >::type type; + }; + + + template <typename T> + struct make_unsigned_helper_0<T, false> + { + struct no_type_helper + { + }; + + typedef no_type_helper type; + }; + + template <typename T> + struct make_unsigned_helper_1 + { + typedef typename T::type type; + }; + + template <typename T> + struct make_unsigned_helper + { + typedef typename eastl::internal::make_unsigned_helper_1<typename eastl::internal::make_unsigned_helper_0<T>::type>::type type; + }; + + } // namespace internal + + template <typename T> + struct make_unsigned + { + typedef typename eastl::internal::make_unsigned_helper<T>::type type; + }; + + template <> struct make_unsigned<bool> {}; template <> struct make_unsigned<signed char> { typedef unsigned char type; }; - template <> struct make_unsigned<const signed char> { typedef const unsigned char type; }; + template <> struct make_unsigned<unsigned char> { typedef unsigned char type; }; template <> struct make_unsigned<signed short> { typedef unsigned short type; }; - template <> struct make_unsigned<const signed short> { typedef const unsigned short type; }; + template <> struct make_unsigned<unsigned short> { typedef unsigned short type; }; template <> struct make_unsigned<signed int> { typedef unsigned int type; }; - template <> struct make_unsigned<const signed int> { typedef const unsigned int type; }; + template <> struct make_unsigned<unsigned int> { typedef unsigned int type; }; template <> struct make_unsigned<signed long> { typedef unsigned long type; }; - template <> struct make_unsigned<const signed long> { typedef const unsigned long type; }; + 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<const signed long long> { typedef const unsigned long long type; }; + template <> struct make_unsigned<unsigned long long> { typedef unsigned long long type; }; + #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 #if (CHAR_MIN < 0) // If char is signed, we convert char to unsigned char. However, if char is unsigned then make_unsigned returns char itself and not unsigned char. template <> struct make_unsigned<char> { typedef unsigned char type; }; - template <> struct make_unsigned<const char> { typedef unsigned char 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__ != 4294967295U)) // If wchar_t is a 32 bit signed value... - template<> - struct make_unsigned<wchar_t> - { typedef uint32_t type; }; - #elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ != 65535)) // If wchar_t is a 16 bit signed value... - template<> - struct make_unsigned<wchar_t> - { typedef uint16_t type; }; - #elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ != 255)) // If wchar_t is an 8 bit signed value... - template<> - struct make_unsigned<wchar_t> - { typedef uint8_t type; }; - #endif + #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE + template <> struct make_unsigned<char8_t> { typedef unsigned char type; }; #endif + template <typename T> + struct make_unsigned<const T> + { + typedef eastl::add_const_t<typename eastl::make_unsigned<T>::type> type; + }; + + template <typename T> + struct make_unsigned<volatile T> + { + typedef eastl::add_volatile_t<typename eastl::make_unsigned<T>::type> type; + }; + + template <typename T> + struct make_unsigned<const volatile T> + { + typedef eastl::add_cv_t<typename eastl::make_unsigned<T>::type> type; + }; + #if EASTL_VARIABLE_TEMPLATES_ENABLED - template <class T> + template <typename T> using make_unsigned_t = typename make_unsigned<T>::type; #endif @@ -283,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> @@ -553,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/intrusive_list.h b/EASTL/include/EASTL/intrusive_list.h index 18d7e93..dc0129f 100644 --- a/EASTL/include/EASTL/intrusive_list.h +++ b/EASTL/include/EASTL/intrusive_list.h @@ -146,6 +146,7 @@ namespace eastl intrusive_list_iterator(); explicit intrusive_list_iterator(pointer pNode); // Note that you can also construct an iterator from T via this, since value_type == node_type. intrusive_list_iterator(const iterator& x); + intrusive_list_iterator& operator=(const iterator& x); reference operator*() const; pointer operator->() const; @@ -368,6 +369,13 @@ namespace eastl // Empty } + template <typename T, typename Pointer, typename Reference> + inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type& + intrusive_list_iterator<T, Pointer, Reference>::operator=(const iterator& x) + { + mpNode = x.mpNode; + return *this; + } template <typename T, typename Pointer, typename Reference> inline typename intrusive_list_iterator<T, Pointer, Reference>::reference diff --git a/EASTL/include/EASTL/iterator.h b/EASTL/include/EASTL/iterator.h index d2dc899..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. @@ -409,15 +452,19 @@ namespace eastl template<typename Iterator> class move_iterator // Don't inherit from iterator. { + private: + using WrappedIteratorReference = typename iterator_traits<Iterator>::reference; + 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; typedef typename traits_type::difference_type difference_type; typedef Iterator pointer; - typedef value_type&& reference; + using reference = conditional_t<is_reference<WrappedIteratorReference>::value, + remove_reference_t<WrappedIteratorReference>&&, + WrappedIteratorReference>; protected: iterator_type mIterator; @@ -440,8 +487,7 @@ namespace eastl iterator_type base() const { return mIterator; } - reference operator*() const - { return eastl::move(*mIterator); } + reference operator*() const { return static_cast<reference>(*mIterator); } pointer operator->() const { return mIterator; } @@ -492,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> @@ -585,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; @@ -594,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 @@ -1070,10 +1127,16 @@ namespace eastl return container.begin(); } + template<typename T, size_t arraySize> + EA_CPP14_CONSTEXPR inline T* begin(T (&arrayObject)[arraySize]) EA_NOEXCEPT + { + return arrayObject; + } + template <typename Container> - EA_CPP14_CONSTEXPR inline auto cbegin(const Container& container) -> decltype(container.begin()) + EA_CPP14_CONSTEXPR inline auto cbegin(const Container& container) -> decltype(eastl::begin(container)) { - return container.begin(); + return eastl::begin(container); } template <typename Container> @@ -1088,10 +1151,16 @@ namespace eastl return container.end(); } + template<typename T, size_t arraySize> + EA_CPP14_CONSTEXPR inline T* end(T (&arrayObject)[arraySize]) EA_NOEXCEPT + { + return (arrayObject + arraySize); + } + template <typename Container> - EA_CPP14_CONSTEXPR inline auto cend(const Container& container) -> decltype(container.end()) + EA_CPP14_CONSTEXPR inline auto cend(const Container& container) -> decltype(eastl::end(container)) { - return container.end(); + return eastl::end(container); } template <typename Container> @@ -1130,17 +1199,6 @@ namespace eastl return container.rend(); } - template<typename T, size_t arraySize> - EA_CPP14_CONSTEXPR inline T* begin(T (&arrayObject)[arraySize]) - { - return arrayObject; - } - - template<typename T, size_t arraySize> - EA_CPP14_CONSTEXPR inline T* end(T (&arrayObject)[arraySize]) - { - return (arrayObject + arraySize); - } template <typename T, size_t arraySize> EA_CPP14_CONSTEXPR inline reverse_iterator<T*> rbegin(T (&arrayObject)[arraySize]) @@ -1179,7 +1237,7 @@ namespace eastl // Some compilers (e.g. GCC 4.6) support range-based for loops, but have a bug with // respect to argument-dependent lookup which results on them unilaterally using std::begin/end // with range-based for loops. To work around this we #include <iterator> for this case in -// order to make std::begin/end visible to users of <eastl/iterator.h>, for portability. +// order to make std::begin/end visible to users of <EASTL/iterator.h>, for portability. #if !EASTL_BEGIN_END_ENABLED && !defined(EA_COMPILER_NO_RANGE_BASED_FOR_LOOP) #include <iterator> #endif diff --git a/EASTL/include/EASTL/list.h b/EASTL/include/EASTL/list.h index 680dcad..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; @@ -771,7 +771,7 @@ namespace eastl inline typename ListBase<T, Allocator>::node_type* ListBase<T, Allocator>::DoAllocateNode() { - node_type* pNode = (node_type*)allocate_memory(internalAllocator(), sizeof(node_type), EASTL_ALIGN_OF(T), 0); + node_type* pNode = (node_type*)allocate_memory(internalAllocator(), sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); EASTL_ASSERT(pNode != nullptr); return pNode; } @@ -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 cf24b41..ab2798f 100644 --- a/EASTL/include/EASTL/memory.h +++ b/EASTL/include/EASTL/memory.h @@ -439,6 +439,9 @@ namespace eastl template <typename T> static T* do_move_start(T* first, T* last, T* dest) { + if (EASTL_UNLIKELY(first == last)) + return dest; + return (T*)memcpy(dest, first, (size_t)((uintptr_t)last - (uintptr_t)first)) + (last - first); } @@ -882,6 +885,9 @@ namespace eastl template <typename ForwardIterator, typename Count> inline void uninitialized_default_fill_n_impl(ForwardIterator first, Count n, true_type) { + if (EASTL_UNLIKELY(n == 0)) + return; + typedef typename eastl::iterator_traits<ForwardIterator>::value_type value_type; memset(first, 0, sizeof(value_type) * n); } @@ -1676,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/meta.h b/EASTL/include/EASTL/meta.h index 09880b7..545354d 100644 --- a/EASTL/include/EASTL/meta.h +++ b/EASTL/include/EASTL/meta.h @@ -36,24 +36,24 @@ namespace eastl template <int I, typename T, typename Head, typename... Types> struct get_type_index<I, T, Head, Types...> { - static const int value = is_same_v<T, Head> ? I : get_type_index<I + 1, T, Types...>::value; + static EA_CONSTEXPR_OR_CONST int value = is_same_v<T, Head> ? I : get_type_index<I + 1, T, Types...>::value; }; template <int I, typename T> struct get_type_index<I, T> { - static const int value = -1; + static EA_CONSTEXPR_OR_CONST int value = -1; }; } template<typename T, typename... Types> struct get_type_index { - static const int value = Internal::get_type_index<0, T, Types...>::value; + static EA_CONSTEXPR_OR_CONST int value = Internal::get_type_index<0, T, Types...>::value; }; template <typename T, typename... Ts> - constexpr int get_type_index_v = get_type_index<T, Ts...>::value; + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR int get_type_index_v = get_type_index<T, Ts...>::value; //////////////////////////////////////////////////////////////////////////////////////////// @@ -77,7 +77,7 @@ namespace eastl //////////////////////////////////////////////////////////////////////////////////////////// - // type_count_v + // type_count_v // // Returns the number of occurrences of type T in a typelist. // @@ -87,33 +87,37 @@ namespace eastl template <typename T, typename H, typename... Types> struct type_count<T, H, Types...> { - static const int value = (is_same_v<T, H> ? 1 : 0) + type_count<T, Types...>::value; + static EA_CONSTEXPR_OR_CONST int value = (is_same_v<T, H> ? 1 : 0) + type_count<T, Types...>::value; }; - template <typename T> struct type_count<T> { static const int value = 0; }; + template <typename T> + struct type_count<T> + { + static EA_CONSTEXPR_OR_CONST int value = 0; + }; template <typename T, typename... Ts> - constexpr int type_count_v = type_count<T, Ts...>::value; + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR int type_count_v = type_count<T, Ts...>::value; //////////////////////////////////////////////////////////////////////////////////////////// // duplicate_type_check_v - // + // // Checks if a type T occurs in a typelist more than once. // template <typename T, typename... Types> struct duplicate_type_check { - static const bool value = (type_count<T, Types...>::value == 1); + static EA_CONSTEXPR_OR_CONST bool value = (type_count<T, Types...>::value == 1); }; template <typename... Ts> - constexpr bool duplicate_type_check_v = duplicate_type_check<Ts...>::value; + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool duplicate_type_check_v = duplicate_type_check<Ts...>::value; ////////////////////////////////////////////////////////////////////////////////// - // type_list + // type_list // // type_list is a simple struct that allows us to pass template parameter packs // around in a single struct, and deduce parameter packs from function arguments @@ -170,8 +174,8 @@ namespace eastl //////////////////////////////////////////////////////////////////////////////////////////// - // overload_resolution_t - // + // overload_resolution_t + // // Given an input type and a typelist (which is a stand-in for alternative // function overloads) this traits will return the same type chosen as if // overload_resolution has selected a function to run. @@ -215,8 +219,29 @@ namespace eastl template <typename T, typename OverloadSet> using overload_resolution_t = typename overload_resolution<decay_t<T>, OverloadSet>::type; - } // namespace meta + + //////////////////////////////////////////////////////////////////////////////////////////// + // double_pack_expansion + // + // MSVC 2017 has a hard time expanding two packs of different lengths. + // This is a helper meant to coerce MSVC 2017 into doing the expansion by adding another level + // of indirection. + // + + template <typename T, size_t I> + struct double_pack_expansion; + + template <size_t... Is, size_t I> + struct double_pack_expansion<index_sequence<Is...>, I> + { + using type = index_sequence<Is..., I>; + }; + + template <typename IndexSequence, size_t I> + using double_pack_expansion_t = typename double_pack_expansion<IndexSequence, I>::type; + + + } // namespace meta } // namespace eastl #endif // EASTL_META_H - 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 c2770c9..0d7dc97 100644 --- a/EASTL/include/EASTL/numeric_limits.h +++ b/EASTL/include/EASTL/numeric_limits.h @@ -57,7 +57,7 @@ EA_DISABLE_VC_WARNING(4310 4296) // Indicates whether we need to define our own implementations of inf, nan, snan, denorm floating point constants. // #if !defined(EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED) - #if (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) && defined(__FLT_MIN__)) || defined(_CPPLIB_VER) // __FLT_MIN__ detects if it's really GCC/clang and not a mimic. _CPPLIB_VER (Dinkumware) covers VC++, and Microsoft platforms. + #if (defined(EA_COMPILER_GNUC) || defined(__clang__) && defined(__FLT_MIN__)) || defined(_CPPLIB_VER) // __FLT_MIN__ detects if it's really GCC/clang and not a mimic. _CPPLIB_VER (Dinkumware) covers VC++, and Microsoft platforms. #define EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED 0 #else #define EASTL_CUSTOM_FLOAT_CONSTANTS_REQUIRED 1 @@ -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> @@ -1213,7 +1272,7 @@ namespace eastl }; - #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) // If __int128_t/__uint128_t is supported... + #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(__clang__)) // If __int128_t/__uint128_t is supported... // numeric_limits<__uint128_t> template<> struct numeric_limits<__uint128_t> @@ -1391,7 +1450,7 @@ namespace eastl static value_type denorm_min() { return Internal::gFloatDenorm; } - #elif (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && defined(__FLT_MIN__) + #elif (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__FLT_MIN__) static EA_CONSTEXPR value_type min() { return __FLT_MIN__; } @@ -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 }; @@ -1509,7 +1582,7 @@ namespace eastl static value_type denorm_min() { return Internal::gDoubleDenorm; } - #elif (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && defined(__DBL_MIN__) + #elif (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__DBL_MIN__) static EA_CONSTEXPR value_type min() { return __DBL_MIN__; } @@ -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 }; @@ -1627,7 +1714,7 @@ namespace eastl static value_type denorm_min() { return Internal::gLongDoubleDenorm; } - #elif (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && defined(__LDBL_MIN__) + #elif (defined(EA_COMPILER_GNUC) || defined(__clang__)) && defined(__LDBL_MIN__) static EA_CONSTEXPR value_type min() { return __LDBL_MIN__; } @@ -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 2796692..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; @@ -679,7 +679,7 @@ namespace eastl template <typename T, typename Allocator> inline SListNode<T>* SListBase<T, Allocator>::DoAllocateNode() { - return (node_type*)allocate_memory(internalAllocator(), sizeof(node_type), EASTL_ALIGN_OF(T), 0); + return (node_type*)allocate_memory(internalAllocator(), sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); } @@ -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 d089431..fb1c6e5 100644 --- a/EASTL/include/EASTL/sort.h +++ b/EASTL/include/EASTL/sort.h @@ -305,7 +305,7 @@ namespace eastl { iBack = iCurrent = iSorted; - for(iBack -= nSpace; (iCurrent != iInsertFirst) && compare(*iCurrent, *iBack); iCurrent = iBack, iBack -= nSpace) + for(; (iCurrent != iInsertFirst) && compare(*iCurrent, *(iBack -= nSpace)); iCurrent = iBack) { EASTL_VALIDATE_COMPARE(!compare(*iBack, *iCurrent)); // Validate that the compare function is sane. eastl::iter_swap(iCurrent, iBack); @@ -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> @@ -1653,8 +1655,8 @@ namespace eastl IntegerType) { RandomAccessIterator srcFirst = first; - constexpr size_t numBuckets = 1 << DigitBits; - constexpr IntegerType bucketMask = numBuckets - 1; + EA_CONSTEXPR_OR_CONST size_t numBuckets = 1 << DigitBits; + EA_CONSTEXPR_OR_CONST IntegerType bucketMask = numBuckets - 1; // The alignment of this variable isn't required; it merely allows the code below to be faster on some platforms. uint32_t EA_PREFIX_ALIGN(EASTL_PLATFORM_PREFERRED_ALIGNMENT) bucketSize[numBuckets]; @@ -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/span.h b/EASTL/include/EASTL/span.h index 1f3b9b4..9c47f5b 100644 --- a/EASTL/include/EASTL/span.h +++ b/EASTL/include/EASTL/span.h @@ -76,7 +76,7 @@ namespace eastl static EA_CONSTEXPR size_t extent = Extent; // constructors / destructor - EA_CONSTEXPR span() EA_NOEXCEPT = default; + EA_CONSTEXPR span() EA_NOEXCEPT; EA_CONSTEXPR span(const span& other) EA_NOEXCEPT = default; EA_CONSTEXPR span(pointer ptr, index_type count); EA_CONSTEXPR span(pointer pBegin, pointer pEnd); @@ -86,9 +86,14 @@ namespace eastl EA_CPP14_CONSTEXPR span& operator=(const span& other) EA_NOEXCEPT = default; // conversion constructors for c-array and eastl::array - template <size_t N> EA_CONSTEXPR span(element_type (&arr)[N]) EA_NOEXCEPT; - template <size_t N> EA_CONSTEXPR span(eastl::array<value_type, N>& arr) EA_NOEXCEPT; - template <size_t N> EA_CONSTEXPR span(const eastl::array<value_type, N>& arr) EA_NOEXCEPT; + template <size_t N, typename = enable_if_t<(Extent == eastl::dynamic_extent || N == Extent)>> + EA_CONSTEXPR span(element_type (&arr)[N]) EA_NOEXCEPT; + + template <size_t N, typename = enable_if_t<(Extent == eastl::dynamic_extent || N == Extent)>> + EA_CONSTEXPR span(eastl::array<value_type, N>& arr) EA_NOEXCEPT; + + template <size_t N, typename = enable_if_t<(Extent == eastl::dynamic_extent || N == Extent)>> + EA_CONSTEXPR span(const eastl::array<value_type, N>& arr) EA_NOEXCEPT; // SfinaeForGenericContainers // @@ -198,34 +203,43 @@ namespace eastl // ctor implementations /////////////////////////////////////////////////////////////////////////// + + template <typename T, size_t Extent> + EA_CONSTEXPR span<T, Extent>::span() EA_NOEXCEPT + { + static_assert(Extent == dynamic_extent || Extent == 0, "impossible to default construct a span with a fixed Extent different than 0"); + } + template <typename T, size_t Extent> EA_CONSTEXPR span<T, Extent>::span(pointer ptr, index_type size) : mpData(ptr), mnSize(size) { + EASTL_ASSERT_MSG(Extent == dynamic_extent || Extent == mnSize, "impossible to create a span with a fixed Extent different than the size of the supplied buffer"); } template <typename T, size_t Extent> EA_CONSTEXPR span<T, Extent>::span(pointer pBegin, pointer pEnd) : mpData(pBegin), mnSize(static_cast<index_type>(pEnd - pBegin)) { + EASTL_ASSERT_MSG(Extent == dynamic_extent || Extent == mnSize, "impossible to create a span with a fixed Extent different than the size of the supplied buffer"); } template <typename T, size_t Extent> - template <size_t N> + template <size_t N, typename> EA_CONSTEXPR span<T, Extent>::span(element_type(&arr)[N]) EA_NOEXCEPT : span(arr, static_cast<index_type>(N)) { } template <typename T, size_t Extent> - template <size_t N> + template <size_t N, typename> EA_CONSTEXPR span<T, Extent>::span(eastl::array<value_type, N> &arr) EA_NOEXCEPT : span(arr.data(), arr.size()) { } template <typename T, size_t Extent> - template <size_t N> + template <size_t N, typename> EA_CONSTEXPR span<T, Extent>::span(const eastl::array<value_type, N>& arr) EA_NOEXCEPT : span(arr.data(), arr.size()) { @@ -368,7 +382,7 @@ namespace eastl template <size_t Count> EA_CPP14_CONSTEXPR span<typename span<T, Extent>::element_type, Count> span<T, Extent>::first() const { - EASTL_ASSERT_MSG(bounds_check(Count), "undefined behavior accessing out of bounds"); + EASTL_ASSERT_MSG(Count <= size(), "undefined behavior accessing out of bounds"); return {data(), static_cast<index_type>(Count)}; } @@ -376,7 +390,7 @@ namespace eastl EA_CPP14_CONSTEXPR span<typename span<T, Extent>::element_type, dynamic_extent> span<T, Extent>::first(size_t sz) const { - EASTL_ASSERT_MSG(bounds_check(sz), "undefined behavior accessing out of bounds"); + EASTL_ASSERT_MSG(sz <= size(), "undefined behavior accessing out of bounds"); return {data(), static_cast<index_type>(sz)}; } @@ -384,7 +398,7 @@ namespace eastl template <size_t Count> EA_CPP14_CONSTEXPR span<typename span<T, Extent>::element_type, Count> span<T, Extent>::last() const { - EASTL_ASSERT_MSG(bounds_check(Count), "undefined behavior accessing out of bounds"); + EASTL_ASSERT_MSG(Count <= size(), "undefined behavior accessing out of bounds"); return {data() + size() - Count, static_cast<index_type>(Count)}; } @@ -392,7 +406,7 @@ namespace eastl EA_CPP14_CONSTEXPR span<typename span<T, Extent>::element_type, dynamic_extent> span<T, Extent>::last(size_t sz) const { - EASTL_ASSERT_MSG(bounds_check(sz), "undefined behavior accessing out of bounds"); + EASTL_ASSERT_MSG(sz <= size(), "undefined behavior accessing out of bounds"); return {data() + size() - sz, static_cast<index_type>(sz)}; } @@ -401,7 +415,7 @@ namespace eastl EA_CONSTEXPR span<typename span<T, Extent>::element_type, Internal::SubspanExtent<Extent, Offset, Count>::value> span<T, Extent>::subspan() const { - EASTL_ASSERT_MSG(bounds_check(Offset), "undefined behaviour accessing out of bounds"); + EASTL_ASSERT_MSG(Offset <= size(), "undefined behaviour accessing out of bounds"); EASTL_ASSERT_MSG(Count == dynamic_extent || Count <= (size() - Offset), "undefined behaviour exceeding size of span"); return {data() + Offset, eastl_size_t(Count == dynamic_extent ? size() - Offset : Count)}; @@ -411,16 +425,16 @@ namespace eastl EA_CONSTEXPR span<typename span<T, Extent>::element_type, dynamic_extent> span<T, Extent>::subspan(size_t offset, size_t count) const { - EASTL_ASSERT_MSG(bounds_check(offset), "undefined behaviour accessing out of bounds"); + EASTL_ASSERT_MSG(offset <= size(), "undefined behaviour accessing out of bounds"); EASTL_ASSERT_MSG(count == dynamic_extent || count <= (size() - offset), "undefined behaviour exceeding size of span"); return {data() + offset, eastl_size_t(count == dynamic_extent ? size() - offset : count)}; } template <typename T, size_t Extent> - EA_CONSTEXPR bool span<T, Extent>::bounds_check(size_t sz) const + EA_CONSTEXPR bool span<T, Extent>::bounds_check(size_t offset) const { - return (sz >= 0 && sz < size()); + return offset < size(); } } 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 82816a4..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 @@ -303,7 +303,12 @@ namespace eastl // CtorSprintf exists so that we can create a constructor that accepts printf-style // arguments but also doesn't collide with any other constructor declaration. - struct CtorSprintf{}; + #ifdef EA_PLATFORM_MINGW + // Workaround for MinGW compiler bug: variadic arguments are corrupted if empty object is passed before it + struct CtorSprintf{ int dummy; }; + #else + struct CtorSprintf{}; + #endif // CtorConvert exists so that we can have a constructor that implements string encoding // conversion, such as between UCS2 char16_t and UTF8 char8_t. @@ -313,19 +318,19 @@ namespace eastl // Masks used to determine if we are in SSO or Heap #ifdef EA_SYSTEM_BIG_ENDIAN // Big Endian use LSB, unless we want to reorder struct layouts on endianness, Bit is set when we are in Heap - static constexpr size_type kHeapMask = 0x1; - static constexpr size_type kSSOMask = 0x1; + static EA_CONSTEXPR_OR_CONST size_type kHeapMask = 0x1; + static EA_CONSTEXPR_OR_CONST size_type kSSOMask = 0x1; #else // Little Endian use MSB - static constexpr size_type kHeapMask = ~(size_type(~size_type(0)) >> 1); - static constexpr size_type kSSOMask = 0x80; + static EA_CONSTEXPR_OR_CONST size_type kHeapMask = ~(size_type(~size_type(0)) >> 1); + static EA_CONSTEXPR_OR_CONST size_type kSSOMask = 0x80; #endif public: #ifdef EA_SYSTEM_BIG_ENDIAN - static constexpr size_type kMaxSize = (~kHeapMask) >> 1; + static EA_CONSTEXPR_OR_CONST size_type kMaxSize = (~kHeapMask) >> 1; #else - static constexpr size_type kMaxSize = ~kHeapMask; + static EA_CONSTEXPR_OR_CONST size_type kMaxSize = ~kHeapMask; #endif protected: @@ -353,7 +358,7 @@ namespace eastl // The view of memory when the string data is able to store the string data locally (without a heap allocation). struct SSOLayout { - static constexpr size_type SSO_CAPACITY = (sizeof(HeapLayout) - sizeof(char)) / sizeof(value_type); + static EA_CONSTEXPR_OR_CONST size_type SSO_CAPACITY = (sizeof(HeapLayout) - sizeof(char)) / sizeof(value_type); // mnSize must correspond to the last byte of HeapLayout.mnCapacity, so we don't want the compiler to insert // padding after mnSize if sizeof(value_type) != 1; Also ensures both layouts are the same size. @@ -1793,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. @@ -1849,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. @@ -3759,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) { @@ -3767,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) @@ -3776,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> @@ -3784,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) { @@ -3797,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 >=). @@ -3822,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> @@ -3843,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> @@ -3864,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> @@ -3885,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) @@ -4055,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 @@ -4071,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 @@ -4079,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 68a388d..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. @@ -500,13 +509,8 @@ namespace eastl struct conjunction<B, Bn...> : conditional<bool(B::value), conjunction<Bn...>, B>::type {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED - #if EASTL_INLINE_VARIABLE_ENABLED - template<class... Bn> - inline constexpr bool conjunction_v = conjunction<Bn...>::value; - #else - template<class... Bn> - static const constexpr bool conjunction_v = conjunction<Bn...>::value; - #endif + template <typename... Bn> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool conjunction_v = conjunction<Bn...>::value; #endif @@ -529,13 +533,8 @@ namespace eastl struct disjunction<B, Bn...> : conditional<bool(B::value), B, disjunction<Bn...>>::type {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED - #if EASTL_INLINE_VARIABLE_ENABLED - template<class... B> - inline constexpr bool disjunction_v = disjunction<B...>::value; - #else - template<class... B> - static const constexpr bool disjunction_v = disjunction<B...>::value; - #endif + template <typename... B> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool disjunction_v = disjunction<B...>::value; #endif @@ -552,13 +551,8 @@ namespace eastl struct negation : eastl::bool_constant<!bool(B::value)> {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED - #if EASTL_INLINE_VARIABLE_ENABLED - template<class B> - inline constexpr bool negation_v = negation<B>::value; - #else - template<class B> - static const constexpr bool negation_v = negation<B>::value; - #endif + template <typename B> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool negation_v = negation<B>::value; #endif @@ -636,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> @@ -658,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> @@ -674,15 +660,16 @@ namespace eastl /////////////////////////////////////////////////////////////////////// // is_reference // - // is_reference<T>::value == true if and only if T is a reference type. + // is_reference<T>::value == true if and only if T is a reference type (l-value reference or r-value reference). // This category includes reference to function types. // /////////////////////////////////////////////////////////////////////// #define EASTL_TYPE_TRAIT_is_reference_CONFORMANCE 1 // is_reference is conforming; doesn't make mistakes. - template <typename T> struct is_reference : public eastl::false_type{}; - template <typename T> struct is_reference<T&> : public eastl::true_type{}; + template <typename T> struct is_reference : public eastl::false_type{}; + template <typename T> struct is_reference<T&> : public eastl::true_type{}; + template <typename T> struct is_reference<T&&> : public eastl::true_type{}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template<typename T> @@ -700,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> @@ -831,9 +800,11 @@ namespace eastl // // The add_reference transformation trait adds a level of indirection // by reference to the type to which it is applied. For a given type T, - // add_reference<T>::type is equivalent to T& if is_reference<T>::value == false, + // add_reference<T>::type is equivalent to T& if is_lvalue_reference<T>::value == false, // and T otherwise. // + // Note: due to the reference collapsing rules, if you supply an r-value reference such as T&&, it will collapse to T&. + // /////////////////////////////////////////////////////////////////////// #define EASTL_TYPE_TRAIT_add_reference_CONFORMANCE 1 // add_reference is conforming. @@ -912,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. @@ -947,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. @@ -1052,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 562e2c4..195cc42 100644 --- a/EASTL/include/EASTL/unique_ptr.h +++ b/EASTL/include/EASTL/unique_ptr.h @@ -88,6 +88,7 @@ namespace eastl template <typename T, typename Deleter = eastl::default_delete<T> > class unique_ptr { + static_assert(!is_rvalue_reference<Deleter>::value, "The supplied Deleter cannot be a r-value reference."); public: typedef Deleter deleter_type; typedef T element_type; @@ -130,7 +131,7 @@ namespace eastl /// Example usage: /// eastl::smart_ptr_deleter<int> del; /// unique_ptr<int> ptr(new int(3), del); - unique_ptr(pointer pValue, typename eastl::conditional<eastl::is_reference<deleter_type>::value, deleter_type, typename eastl::add_lvalue_reference<const deleter_type>::type>::type deleter) EA_NOEXCEPT + unique_ptr(pointer pValue, typename eastl::conditional<eastl::is_lvalue_reference<deleter_type>::value, deleter_type, typename eastl::add_lvalue_reference<const deleter_type>::type>::type deleter) EA_NOEXCEPT : mPair(pValue, deleter) {} /// unique_ptr @@ -140,7 +141,7 @@ namespace eastl unique_ptr(pointer pValue, typename eastl::remove_reference<deleter_type>::type&& deleter) EA_NOEXCEPT : mPair(pValue, eastl::move(deleter)) { - static_assert(!eastl::is_reference<deleter_type>::value, "deleter_type reference refers to an rvalue deleter. The reference will probably become invalid before used. Change the deleter_type to not be a reference or construct with permanent deleter."); + static_assert(!eastl::is_lvalue_reference<deleter_type>::value, "deleter_type reference refers to an rvalue deleter. The reference will probably become invalid before used. Change the deleter_type to not be a reference or construct with permanent deleter."); } /// unique_ptr @@ -157,7 +158,7 @@ namespace eastl /// unique_ptr<int> ptr(new int(3)); /// unique_ptr<int> newPtr = eastl::move(ptr); template <typename U, typename E> - unique_ptr(unique_ptr<U, E>&& u, typename enable_if<!is_array<U>::value && is_convertible<typename unique_ptr<U, E>::pointer, pointer>::value && is_convertible<E, deleter_type>::value && (is_same<deleter_type, E>::value || !is_reference<deleter_type>::value)>::type* = 0) EA_NOEXCEPT + unique_ptr(unique_ptr<U, E>&& u, typename enable_if<!is_array<U>::value && is_convertible<typename unique_ptr<U, E>::pointer, pointer>::value && is_convertible<E, deleter_type>::value && (is_same<deleter_type, E>::value || !is_lvalue_reference<deleter_type>::value)>::type* = 0) EA_NOEXCEPT : mPair(u.release(), eastl::forward<E>(u.get_deleter())) {} /// unique_ptr @@ -383,7 +384,7 @@ namespace eastl } template <typename P> - unique_ptr(P pArray, typename eastl::conditional<eastl::is_reference<deleter_type>::value, deleter_type, + unique_ptr(P pArray, typename eastl::conditional<eastl::is_lvalue_reference<deleter_type>::value, deleter_type, typename eastl::add_lvalue_reference<const deleter_type>::type>::type deleter, typename eastl::enable_if<Internal::is_array_cv_convertible<P, pointer>::value>::type* = 0) EA_NOEXCEPT : mPair(pArray, deleter) {} @@ -392,7 +393,7 @@ namespace eastl unique_ptr(P pArray, typename eastl::remove_reference<deleter_type>::type&& deleter, eastl::enable_if_t<Internal::is_array_cv_convertible<P, pointer>::value>* = 0) EA_NOEXCEPT : mPair(pArray, eastl::move(deleter)) { - static_assert(!eastl::is_reference<deleter_type>::value, "deleter_type reference refers to an rvalue deleter. The reference will probably become invalid before used. Change the deleter_type to not be a reference or construct with permanent deleter."); + static_assert(!eastl::is_lvalue_reference<deleter_type>::value, "deleter_type reference refers to an rvalue deleter. The reference will probably become invalid before used. Change the deleter_type to not be a reference or construct with permanent deleter."); } unique_ptr(this_type&& x) EA_NOEXCEPT @@ -401,7 +402,7 @@ namespace eastl template <typename U, typename E> unique_ptr(unique_ptr<U, E>&& u, typename enable_if<Internal::is_safe_array_conversion<T, pointer, U, typename unique_ptr<U, E>::pointer>::value && eastl::is_convertible<E, deleter_type>::value && - (!eastl::is_reference<deleter_type>::value || eastl::is_same<E, deleter_type>::value)>::type* = 0) EA_NOEXCEPT + (!eastl::is_lvalue_reference<deleter_type>::value || eastl::is_same<E, deleter_type>::value)>::type* = 0) EA_NOEXCEPT : mPair(u.release(), eastl::forward<E>(u.get_deleter())) {} this_type& operator=(this_type&& x) EA_NOEXCEPT @@ -535,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]); @@ -563,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; @@ -595,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. @@ -645,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 { @@ -662,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 cc546fb..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 /// @@ -413,7 +499,7 @@ namespace eastl // // See bug submitted to LLVM for more details. // https://bugs.llvm.org/show_bug.cgi?id=38374 - #if !defined(EA_COMPILER_CLANG) + #if !defined(__clang__) template<typename T> using single_pair_ctor_sfinae = eastl::enable_if_t<eastl::is_default_constructible_v<T>>; #else @@ -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 e59c300..a7af97b 100644 --- a/EASTL/include/EASTL/variant.h +++ b/EASTL/include/EASTL/variant.h @@ -10,12 +10,12 @@ // // As with unions, if a variant holds a value of some object type T, the object // representation of T is allocated directly within the object representation of -// the variant itself. +// the variant itself. // // Variant is not allowed to allocate additional (dynamic) memory. // // A variant is not permitted to hold references, arrays, or the type void. -// Empty variants are also ill-formed (variant<monostate> can be used instead). +// Empty variants are also ill-formed (variant<monostate> can be used instead). // // A variant is permitted to hold the same type more than once, and to hold // differently cv-qualified versions of the same type. As with unions, the @@ -48,7 +48,7 @@ // * strong exception guarantees as specified (we punted on the assignment problem). // if an exception is thrown during assignment its undefined behaviour in our implementation. // -// Reference: +// Reference: // * http://en.cppreference.com/w/cpp/utility/variant // * https://thenewcpp.wordpress.com/2012/02/15/variadic-templates-part-3-or-how-i-wrote-a-variant-class/ /////////////////////////////////////////////////////////////////////////// @@ -65,6 +65,13 @@ #include <EASTL/initializer_list.h> #include <EASTL/tuple.h> #include <EASTL/type_traits.h> +#include <EASTL/array.h> + +#if EASTL_EXCEPTIONS_ENABLED + #include <stdexcept> + #include <exception> +#endif + #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. @@ -83,7 +90,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // default_construct_if_supported<T> // - // Utility class to remove default constructor calls for types that + // Utility class to remove default constructor calls for types that // do not support default construction. // // We can remove these utilities when C++17 'constexpr if' is available. @@ -106,7 +113,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // destroy_if_supported<T> // - // Utility class to remove default constructor calls for types that + // Utility class to remove default constructor calls for types that // do not support default construction. // // We can remove these utilities when C++17 'constexpr if' is available. @@ -129,7 +136,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // copy_if_supported<T> // - // Utility class to remove copy constructor calls for types that + // Utility class to remove copy constructor calls for types that // do not support copying. // // We can remove these utilities when C++17 'constexpr if' is available. @@ -152,7 +159,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // move_if_supported<T> // - // Utility class to remove move constructor calls for types that + // Utility class to remove move constructor calls for types that // do not support moves. // // We can remove these utilities when C++17 'constexpr if' is available. @@ -171,7 +178,7 @@ namespace eastl { static void call(T* pThis, T* pOther) {} // intentionally blank }; - } // namespace internal + } // namespace internal /////////////////////////////////////////////////////////////////////////// @@ -217,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 @@ -229,10 +240,10 @@ namespace eastl template <> struct hash<monostate> { size_t operator()(monostate) const { return static_cast<size_t>(-0x42); } }; - + /////////////////////////////////////////////////////////////////////////// // variant_storage - // + // // This is a utility class to simplify the implementation of a storage type // for a distriminted union. This utility handles the alignment, size // requirements, and data access required by the variant type. @@ -318,33 +329,33 @@ namespace eastl public: variant_storage() { - DoOp(StorageOp::DEFAULT_CONSTRUCT); + DoOp(StorageOp::DEFAULT_CONSTRUCT); } ~variant_storage() { - DoOp(StorageOp::DESTROY); + DoOp(StorageOp::DESTROY); } variant_storage(const variant_storage& other) { - DoOp(StorageOp::COPY, other); + DoOp(StorageOp::COPY, other); } variant_storage(variant_storage&& other) { - DoOp(StorageOp::MOVE, other); + DoOp(StorageOp::MOVE, other); } variant_storage& operator=(const variant_storage& other) { - DoOp(StorageOp::COPY, other); + DoOp(StorageOp::COPY, other); return *this; } variant_storage& operator=(variant_storage&& other) { - DoOp(StorageOp::MOVE, eastl::move(other)); + DoOp(StorageOp::MOVE, eastl::move(other)); return *this; } @@ -353,7 +364,7 @@ namespace eastl { // NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which // variant_storage used to store types. The size selected should be large enough to hold the largest type in - // the user provided variant type-list. + // the user provided variant type-list. static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size"); using RT = remove_reference_t<T>; @@ -368,7 +379,7 @@ namespace eastl { // NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which // variant_storage used to store types. The size selected should be large enough to hold the largest type in - // the user provided variant type-list. + // the user provided variant type-list. static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size"); using RT = remove_reference_t<T>; @@ -394,7 +405,7 @@ namespace eastl void destroy() { - DoOp(StorageOp::DESTROY); + DoOp(StorageOp::DESTROY); } }; @@ -426,7 +437,7 @@ namespace eastl { // NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which // variant_storage used to store types. The size selected should be large enough to hold the largest type in - // the user provided variant type-list. + // the user provided variant type-list. static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size"); new (&mBuffer) remove_reference_t<T>(eastl::forward<Args>(args)...); @@ -438,7 +449,7 @@ namespace eastl { // NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which // variant_storage used to store types. The size selected should be large enough to hold the largest type in - // the user provided variant type-list. + // the user provided variant type-list. static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size"); new (&mBuffer) remove_reference_t<T>(il, eastl::forward<Args>(args)...); @@ -464,7 +475,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// - // 20.7.2, forward-declaration for types that depend on the variant + // 20.7.2, forward-declaration for types that depend on the variant // template <class... Types> class variant; @@ -473,18 +484,19 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // 20.7.3, variant_size, variant_size_v helper classes // - template <class T> struct variant_size; + template <class T> struct variant_size; template <class T> struct variant_size<const T> : integral_constant<size_t, variant_size<T>::value> {}; template <class T> struct variant_size<volatile T> : integral_constant<size_t, variant_size<T>::value> {}; template <class T> struct variant_size<const volatile T> : integral_constant<size_t, variant_size<T>::value> {}; template <class... Types> struct variant_size<variant<Types...>> : integral_constant<size_t, sizeof...(Types)> {}; // variant_size_v template alias - template <class T> EA_CONSTEXPR size_t variant_size_v = variant_size<T>::value; + template <typename T> + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t variant_size_v = variant_size<T>::value; /////////////////////////////////////////////////////////////////////////// - // variant_alternative_helper + // variant_alternative_helper // // This helper does the heavy lifting of traversing the variadic type list // and retrieving the type at the user provided index. @@ -508,9 +520,9 @@ namespace eastl template <size_t I, class... Types> struct variant_alternative<I, variant<Types...>> : variant_alternative_helper<I, Types...> {}; // ISO required cv-qualifer specializations - template <size_t I, class T> struct variant_alternative<I, const T> : add_cv_t<variant_alternative<I, T>> {}; - template <size_t I, class T> struct variant_alternative<I, volatile T> : add_volatile_t<variant_alternative<I, T>> {}; - template <size_t I, class T> struct variant_alternative<I, const volatile T> : add_cv_t<variant_alternative<I, T>> {}; + template <size_t I, class T> struct variant_alternative<I, const T> : add_const<typename variant_alternative<I, T>::type> {}; + template <size_t I, class T> struct variant_alternative<I, volatile T> : add_volatile<typename variant_alternative<I, T>::type> {}; + template <size_t I, class T> struct variant_alternative<I, const volatile T> : add_cv<typename variant_alternative<I, T>::type> {}; // variant_alternative_t template alias template <size_t I, class T> using variant_alternative_t = typename variant_alternative<I, T>::type; @@ -525,7 +537,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// - // get_if + // get_if // template <size_t I, class... Types> EA_CONSTEXPR add_pointer_t<variant_alternative_t<I, variant<Types...>>> get_if(variant<Types...>* pv) EA_NOEXCEPT @@ -559,15 +571,14 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// - // get + // get // template <size_t I, class... Types> EA_CONSTEXPR variant_alternative_t<I, variant<Types...>>& get(variant<Types...>& v) { static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - EASTL_ASSERT(v.index() == I); + return *v.mStorage.template get_as<return_type>(); } @@ -576,8 +587,7 @@ namespace eastl { static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - EASTL_ASSERT(v.index() == I); + return eastl::move(*v.mStorage.template get_as<return_type>()); } @@ -586,8 +596,7 @@ namespace eastl { static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - EASTL_ASSERT(v.index() == I); + return *v.mStorage.template get_as<return_type>(); } @@ -596,8 +605,7 @@ namespace eastl { static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - EASTL_ASSERT(v.index() == I); + return eastl::move(*v.mStorage.template get_as<return_type>()); } @@ -645,7 +653,7 @@ namespace eastl // 20.7.2, variant // template <class... Types> - class variant + class variant { static_assert(sizeof...(Types) > 0, "variant must have at least 1 type (empty variants are ill-formed)"); static_assert(disjunction_v<is_void<Types>...> == false, "variant does not allow void as an alternative type"); @@ -661,7 +669,7 @@ namespace eastl // variant_index_t mIndex; variant_storage_t mStorage; - + public: /////////////////////////////////////////////////////////////////////////// // 20.7.2.1, constructors @@ -808,10 +816,16 @@ namespace eastl variant_alternative_t<I, variant>& emplace(Args&&... args) { if (!valueless_by_exception()) + { mStorage.destroy(); - mIndex = static_cast<variant_index_t>(I); + #if EASTL_EXCEPTIONS_ENABLED + mIndex = static_cast<variant_index_t>(variant_npos); + #endif + } + mStorage.template set_as<T>(eastl::forward<Args>(args)...); + mIndex = static_cast<variant_index_t>(I); return *reinterpret_cast<T*>(&mStorage.mBuffer); } @@ -829,10 +843,16 @@ namespace eastl variant_alternative_t<I, variant>& emplace(std::initializer_list<U> il, Args&&... args) { if (!valueless_by_exception()) + { mStorage.destroy(); - mIndex = static_cast<variant_index_t>(I); + #if EASTL_EXCEPTIONS_ENABLED + mIndex = static_cast<variant_index_t>(variant_npos); + #endif + } + mStorage.template set_as<T>(il, eastl::forward<Args>(args)...); + mIndex = static_cast<variant_index_t>(I); return *reinterpret_cast<T*>(&mStorage.mBuffer); } @@ -896,14 +916,29 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // 20.7.2.5, value status // - EA_CONSTEXPR size_t index() const EA_NOEXCEPT { return valueless_by_exception() ? variant_npos : mIndex; } - EA_CONSTEXPR bool valueless_by_exception() const EA_NOEXCEPT { return mIndex == variant_npos; } + EA_CONSTEXPR size_t index() const EA_NOEXCEPT + { + #if EASTL_EXCEPTIONS_ENABLED + return valueless_by_exception() ? variant_npos : mIndex; + #else + return mIndex; + #endif + } + + EA_CONSTEXPR bool valueless_by_exception() const EA_NOEXCEPT + { + #if EASTL_EXCEPTIONS_ENABLED + return mIndex == variant_npos; + #else + return false; + #endif + } /////////////////////////////////////////////////////////////////////////// // 20.7.2.6, swap // - void swap(variant& other) + void swap(variant& other) EA_NOEXCEPT(conjunction_v<is_nothrow_move_constructible<Types>..., is_nothrow_swappable<Types>...>) { eastl::swap(mIndex, other.mIndex); @@ -926,12 +961,13 @@ namespace eastl // 20.7.9, swap // template <class... Types> - void swap(variant<Types...>& lhs, variant<Types...>& rhs) + void swap(variant<Types...>& lhs, variant<Types...>& rhs) EA_NOEXCEPT(EA_NOEXCEPT(lhs.swap(rhs))) { lhs.swap(rhs); } + // visit is a bit convoluted, in order to fulfill a few requirements: // - It must support visiting multiple variants using a single visitor and a single function call. The // visitor in this case should have one function for each possible combination of types: @@ -949,149 +985,355 @@ namespace eastl // // - It must be declared constexpr // - It must be constant-time for the case of visiting a single variant - // - It must allow different return types in the visitor, as long as they are all convertible // - // visitor_caller is responsible for the mechanics of visit. Each visitor_caller creates an array of - // functions which call get<I>() on the variant (where I is the array index), then add the returned reference - // to a tuple of arguments. The final visitor_caller calls invoke() with the visitor and the unpacked - // arguments. + // - 20.7.7 states that variant visitation requires all combinations of visitors to return the same type. // - // This allows us to look up each appropriate get() function in constant time using the variant's index. - template <typename Visitor, typename Variant, typename... Variants> - struct visitor_caller + // NOTE(mwinkler): + // Visit creates an N-Dimensional matrix whereby each dimension is M wide. + // Where N == sizeof...(Variants) and M == variant_size_v<Variant> + // + // variant<int, bool, float> v; + // visit(Visitor{}, v, v); + // + // This creates a 3x3 matrix of potential visitors. + // The argument indices into the variants are as follows. + // [0, 0], [0, 1], [0, 2] + // [1, 0], [1, 1], [1, 2] + // [2, 0], [2, 1], [2, 2] + // + // These indices are compile-time constants but the variants have a runtime index. + // Therefore we must instantiate an NxNxN... matrix of function pointers who are + // templated on the indices based on their position in the matrix and then + // at runtime index into the array to call the correct function pointer that can + // get the correct alternatives in the variants. + // + // There are a couple of ways to do this. We can construct the matrix bottom up or top down. + // + // Constructing a matrix bottom up would look something as follows. + // + // make_visitor_matrix_recurse(eastl::index_sequence<>{}, eastl::make_index_sequence<eastl::variant_size_v<eastl::decay_t<Variants>>>{}...); + // + // make_visitor_matrix_recurse(eastl::index_sequence<Is...>) { return templated function pointer on Is... } + // + // make_visitor_matrix_recurse(eastl::index_sequence<Is...>, eastl::index_sequence<Js...>, RestIndex... rest) + // return make_array(make_visitor_matrix_recurse(eastl::index_sequence<Is..., Js>{}, rest...)...); + // + // Essentially we construct the matrix bottom up, row by row of indices and return an array of function pointers. + // The end result is a NxNxN... array on the stack which can be indexed by each variant in order as follows, + // array[v0.index()][v1.index()][vn.index()](); + // + // The downside with this approach is the massive NxNxN... array that is created on the stack. + // + // The other approach is to build the matrix top down and use tail recursion to ensure there is only one + // N sized array on the stack. The downside here is the extra function calls, but we feel this approach provides + // a good balance between performance and memory usage. + // + // We construct the matrix top down by first creating an N sized array that is indexed by the first variant. + // This calls a function that recursively creates another N sized array that is indexed by the second variant. + // The recursion continues until we reach the base case which is the last variant. At this point we know + // the compile-time value of the N indices needed to get each alternative from each variant to invoke the visitor upon. + // Essentially we create a tree of function pointers like so. + // + // + // +------------------------------------------------------------------+ + // | | + // | 0 1 N | + // | | + // | | + // +----+---------------------------+---------------------------------+ + // | | + // | | + // | | + // | | + // | | + // +--------------------------+-----------------+ +----+------------------------------------+ + // | | | | + // |0,0 0,1 0,N| |1,0 1,1 1,N| + // | | | | + // | | | | + // +--------------------------------------------+ +-----------------------------------------+ + // + // Essentially each call creates a N sized array of function pointers that is the concatention of the indices known so far + // and the index of itself in the array whereby the leaf function pointer does the final invoke of the visitor. + // + + // Since decltype() is not one of the contexts where an overloaded function can be used without arguments; + // We use this function to deduce the function pointer types. + // We also return an eastl::array<> since we cannot return C-style arrays as value types. + template <typename T> + static EA_CONSTEXPR array<decay_t<T>, 1> make_visitor_array(T&& t) { - // @visitor, @variant and @variants are all the arguments to the initial visit() function. - // @args is the tuple of arguments which have been retrieved by any previous visitor_callers. - // - // The two unnamed index_sequence parameters let us deduce two different sets of indices - // as parameter packs - one for the arguments and one for the array of call_next functions. - // This is necessary so we can create the constexpr array of functions which call - // get<I>(variant) based on the array index, and so we can unpack the final of arguments by - // calling get<I>(args) for each index in args. - template <size_t I, typename ArgsTuple, size_t... ArgsIndices, size_t... ArrayIndices> - static decltype(auto) EA_CONSTEXPR call_next(Visitor&& visitor, - index_sequence<ArgsIndices...>, - index_sequence<ArrayIndices...>, - ArgsTuple&& args, - Variant&& variant, - Variants&&... variants) - { - // Call the appropriate get() function on the variant, and pack the result into a new tuple along with - // all of the previous arguments. Then call the next visitor_caller with the new argument added, - // and the current variant removed. - return visitor_caller<Visitor, Variants...>::call( - eastl::forward<Visitor>(visitor), - index_sequence<ArgsIndices..., sizeof...(ArgsIndices)>(), - index_sequence<ArrayIndices...>(), - eastl::make_tuple(get<ArgsIndices>(eastl::forward<ArgsTuple>(args))..., get<I>(eastl::forward<Variant>(variant))), - eastl::forward<Variants>(variants)... - ); + return { { eastl::forward<T>(t) } }; + } + + template <typename T, typename... Ts> + static EA_CONSTEXPR array<decay_t<T>, sizeof...(Ts) + 1> make_visitor_array(T&& t, Ts&&... ts) + { + static_assert(conjunction_v<is_same<decay_t<T>, decay_t<Ts>>...>, "`visit` variant visitation requires that all visitors have the same return type!"); + + return { { eastl::forward<T>(t), eastl::forward<Ts>(ts)... } }; + } + + + template <size_t N, typename Variant, typename... Variants, eastl::enable_if_t<N == 0, int> = 0> + static EA_CONSTEXPR decltype(auto) get_variant_n(Variant&& variant, Variants&&... variants) + { + return eastl::forward<Variant>(variant); + } + + template <size_t N, typename Variant, typename... Variants, eastl::enable_if_t<N != 0, int> = 0> + static EA_CONSTEXPR decltype(auto) get_variant_n(Variant&& variant, Variants&&... variants) + { + return get_variant_n<N - 1>(eastl::forward<Variants>(variants)...); + } + + + template <typename Visitor, typename Index, typename Array, typename... Variants> + static EA_CONSTEXPR decltype(auto) call_visitor_at_index(Array&& array, Index index, Visitor&& visitor, Variants&&... variants) + { + return array[static_cast<typename Array::size_type>(index)](eastl::forward<Visitor>(visitor), eastl::forward<Variants>(variants)...); + } + + template <size_t VariantsIndex, typename Visitor, typename Array, typename... Variants> + static EA_CONSTEXPR decltype(auto) call_visitor_at(Array&& array, Visitor&& visitor, Variants&&... variants) + { + return call_visitor_at_index(eastl::forward<Array>(array), + get_variant_n<VariantsIndex>(eastl::forward<Variants>(variants)...).index(), + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); + } + + + // abstracts calling visit on 2 or more variants + template <typename VariantIndexSequence, typename Visitor, typename... Variants> + struct visitor_caller_n; + + template <typename Visitor, typename... Variants, size_t... VariantIndices> + struct visitor_caller_n<index_sequence<VariantIndices...>, Visitor, Variants...> + { + using return_type = invoke_result_t<Visitor, variant_alternative_t<0, remove_reference_t<Variants>>...>; + + template <size_t... VariantArgIndices> + static EA_CONSTEXPR return_type invoke_visitor_leaf(Visitor&& visitor, Variants&&... variants) + { + return eastl::invoke(eastl::forward<Visitor>(visitor), + eastl::get<VariantArgIndices>(eastl::forward<Variants>(variants))...); + } + + template <size_t... VariantArgIndices> + static EA_CONSTEXPR auto make_invoke_visitor_leaf(index_sequence<VariantArgIndices...>) + { + return &invoke_visitor_leaf<VariantArgIndices...>; } - // Arguments are the same as for call_next (see above). - template <typename ArgsTuple, size_t... ArgsIndices, size_t... ArrayIndices> - static decltype(auto) EA_CPP14_CONSTEXPR call(Visitor&& visitor, - index_sequence<ArgsIndices...>, - index_sequence<ArrayIndices...>, - ArgsTuple&& args, - Variant&& variant, - Variants&&... variants) - { - // Deduce the type of the inner array of call_next functions - using return_type = decltype(call_next<0>( - eastl::forward<Visitor>(visitor), - index_sequence<ArgsIndices...>(), - index_sequence<ArrayIndices...>(), - eastl::forward<ArgsTuple>(args), - eastl::forward<Variant>(variant), - eastl::forward<Variants>(variants)...) - ); - - using next_type = return_type (*)( - Visitor&&, - index_sequence<ArgsIndices...>, - index_sequence<ArrayIndices...>, - ArgsTuple&&, - Variant&&, - Variants&&... - ); - - // Create an array of call_next<0>, call_next<1>, ... , call_next<N - 1> - // where N = variant_size<Variant>. - EA_CPP14_CONSTEXPR next_type next[] = { static_cast<next_type>(call_next<ArrayIndices>)... }; - - // call_next() with the correct index for the variant. - return next[variant.index()]( - eastl::forward<Visitor>(visitor), - index_sequence<ArgsIndices...>(), - index_sequence<ArrayIndices...>(), - eastl::forward<ArgsTuple>(args), - eastl::forward<Variant>(variant), - eastl::forward<Variants>(variants)... - ); + + template <size_t... VariantArgIndices> + static EA_CONSTEXPR return_type invoke_visitor_recurse(Visitor&& visitor, Variants&&... variants) + { + return call(index_sequence<VariantArgIndices...>{}, + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); } + + template <size_t... VariantArgIndices> + static EA_CONSTEXPR auto make_invoke_visitor_recurse(index_sequence<VariantArgIndices...>) + { + return &invoke_visitor_recurse<VariantArgIndices...>; + } + + + template <typename VariantArgIndexSequence, enable_if_t<internal::index_sequence_size_v<VariantArgIndexSequence> + 1 == sizeof...(Variants), int> = 0> + static EA_CPP14_CONSTEXPR decltype(auto) call(VariantArgIndexSequence, Visitor&& visitor, Variants&&... variants) + { + EA_CPP14_CONSTEXPR auto callers = make_visitor_array(make_invoke_visitor_leaf(meta::double_pack_expansion_t<VariantArgIndexSequence, VariantIndices>{})...); + + return call_visitor_at<internal::index_sequence_size_v<VariantArgIndexSequence>>(eastl::move(callers), + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); + } + + template <typename VariantArgIndexSequence, enable_if_t<internal::index_sequence_size_v<VariantArgIndexSequence> + 1 != sizeof...(Variants), int> = 0> + static EA_CPP14_CONSTEXPR decltype(auto) call(VariantArgIndexSequence, Visitor&& visitor, Variants&&... variants) + { + EA_CPP14_CONSTEXPR auto callers = make_visitor_array(make_invoke_visitor_recurse(meta::double_pack_expansion_t<VariantArgIndexSequence, VariantIndices>{})...); + + return call_visitor_at<internal::index_sequence_size_v<VariantArgIndexSequence>>(eastl::move(callers), + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); + } + }; - template <typename Visitor, typename Variant> - struct visitor_caller<Visitor, Variant> + template <typename VariantIndexSequence, typename Visitor, typename... Variants> + static EA_CONSTEXPR decltype(auto) call_initial_n(VariantIndexSequence, Visitor&& visitor, Variants&&... variants) { - // Invoke the correct visitor for a given variant index, and call the correct get() function to retrieve - // the argument. Unpack any additional arguments from earlier visitor_callers (see above). - template <typename R, size_t I, typename ArgsTuple, size_t... ArgsIndices> - static decltype(auto) EA_CONSTEXPR invoke_visitor(Visitor&& visitor, index_sequence<ArgsIndices...>, ArgsTuple&& args, Variant&& variant) - { - return static_cast<R>(invoke( - eastl::forward<Visitor>(visitor), - get<ArgsIndices>(eastl::forward<ArgsTuple>(args))..., - get<I>(eastl::forward<Variant>(variant)) - )); + return visitor_caller_n<VariantIndexSequence, Visitor, Variants...>::call(index_sequence<>{}, eastl::forward<Visitor>(visitor), eastl::forward<Variants>(variants)...); + } + + + // abstracts calling visit on 2 or more variants with return types convertible to R + template <typename R, typename VariantIndexSequence, typename Visitor, typename... Variants> + struct visitor_caller_n_r; + + template <typename R, size_t... VariantIndices, typename Visitor, typename... Variants> + struct visitor_caller_n_r<R, index_sequence<VariantIndices...>, Visitor, Variants...> + { + template <typename R_, size_t... VariantArgIndices> + struct visitor_leaf_r + { + static EA_CONSTEXPR R_ invoke_visitor_leaf_r(Visitor&& visitor, Variants&&... variants) + { + return eastl::invoke(eastl::forward<Visitor>(visitor), + eastl::get<VariantArgIndices>(eastl::forward<Variants>(variants))...); + } + }; + + // void return type must discard the return values of the visitor even if the visitor returns a value. + template <size_t... VariantArgIndices> + struct visitor_leaf_r<void, VariantArgIndices...> + { + static EA_CONSTEXPR void invoke_visitor_leaf_r(Visitor&& visitor, Variants&&... variants) + { + eastl::invoke(eastl::forward<Visitor>(visitor), + eastl::get<VariantArgIndices>(eastl::forward<Variants>(variants))...); + } + }; + template <size_t... VariantArgIndices> struct visitor_leaf_r<const void, VariantArgIndices...> : public visitor_leaf_r<void, VariantArgIndices...> {}; + template <size_t... VariantArgIndices> struct visitor_leaf_r<volatile void, VariantArgIndices...> : public visitor_leaf_r<void, VariantArgIndices...> {}; + template <size_t... VariantArgIndices> struct visitor_leaf_r<const volatile void, VariantArgIndices...> : public visitor_leaf_r<void, VariantArgIndices...> {}; + + template <typename R_, size_t... VariantArgIndices> + static EA_CONSTEXPR auto make_invoke_visitor_leaf_r(index_sequence<VariantArgIndices...>) + { + return &visitor_leaf_r<R_, VariantArgIndices...>::invoke_visitor_leaf_r; } - // The final call() in the recursion. - // - // By this point, <ArgsIndices...> expands to <0 .. N - 2> where N is the number of arguments to the - // final invoke() call. This corresponds to each element in @args, so `get<ArgsIndices>(args)...` - // expands to `get<0>(args), get<1>(args), ... , get<N - 2>(args)`. The final argument is selected - // based on the final array index, leaving us with a sequence of arguments from 0 .. N - 1. - // - // <ArrayIndices...> is the same as in earlier calls - it expands to <0 .. I - 1> where I is the - // number of alternatives in the variant. This lets us call the correct `get<I>` based on the - // final variant index, as we did for all earlier calls. - template <typename ArgsTuple, size_t... ArgsIndices, size_t... ArrayIndices> - static decltype(auto) EA_CPP14_CONSTEXPR call(Visitor&& visitor, index_sequence<ArgsIndices...>, index_sequence<ArrayIndices...>, ArgsTuple&& args, Variant&& variant) - { - // MSVC isn't able to handle the nested pack expansion required here, so we have to just use the - // return type of the first visitor function instead of the common_type of all possible visitor - // functions. This means we can't handle the case where visitor functions return different (but - // compatible) types. This is unlikely to be a common case, but we might be able to get around it - // if it's a big issue. - // - // TODO: we should reevaluate this on future compiler releases - #if defined(EA_COMPILER_MSVC) - using return_type = invoke_result_t<Visitor, decltype(get<ArgsIndices>(args))..., decltype(get<0>(variant))>; - #else - // If we're on a compiler that can take it, determine the common_type between all possible visitor - // invocations. - using return_type = common_type_t< - invoke_result_t<Visitor, decltype(get<ArgsIndices>(args))..., decltype(get<ArrayIndices>(variant))>... - >; - #endif - using caller_type = return_type (*)(Visitor&&, index_sequence<ArgsIndices...>, ArgsTuple&&, Variant&&); + template <typename R_, size_t... VariantArgIndices> + struct visitor_recurse_r + { + static EA_CONSTEXPR R_ invoke_visitor_recurse_r(Visitor&& visitor, Variants&&... variants) + { + return call_r(index_sequence<VariantArgIndices...>{}, + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); + } + }; + + template <typename R_, size_t... VariantArgIndices> + static EA_CONSTEXPR auto make_invoke_visitor_recurse_r(index_sequence<VariantArgIndices...>) + { + return &visitor_recurse_r<R_, VariantArgIndices...>::invoke_visitor_recurse_r; + } + + + template <typename VariantArgIndexSequence, enable_if_t<internal::index_sequence_size_v<VariantArgIndexSequence> + 1 == sizeof...(Variants), int> = 0> + static EA_CPP14_CONSTEXPR decltype(auto) call_r(VariantArgIndexSequence, Visitor&& visitor, Variants&&... variants) + { + EA_CPP14_CONSTEXPR auto callers = make_visitor_array(make_invoke_visitor_leaf_r<R>(meta::double_pack_expansion_t<VariantArgIndexSequence, VariantIndices>{})...); + + return call_visitor_at<internal::index_sequence_size_v<VariantArgIndexSequence>>(eastl::move(callers), + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); + } + + template <typename VariantArgIndexSequence, enable_if_t<internal::index_sequence_size_v<VariantArgIndexSequence> + 1 != sizeof...(Variants), int> = 0> + static EA_CPP14_CONSTEXPR decltype(auto) call_r(VariantArgIndexSequence, Visitor&& visitor, Variants&&... variants) + { + EA_CPP14_CONSTEXPR auto callers = make_visitor_array(make_invoke_visitor_recurse_r<R>(meta::double_pack_expansion_t<VariantArgIndexSequence, VariantIndices>{})...); + + return call_visitor_at<internal::index_sequence_size_v<VariantArgIndexSequence>>(eastl::move(callers), + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); + } + + }; + + template <typename R, typename VariantIndexSequence, typename Visitor, typename... Variants> + static EA_CONSTEXPR decltype(auto) call_initial_n_r(VariantIndexSequence, Visitor&& visitor, Variants&&... variants) + { + return visitor_caller_n_r<R, VariantIndexSequence, Visitor, Variants...>::call_r(index_sequence<>{}, eastl::forward<Visitor>(visitor), eastl::forward<Variants>(variants)...); + } + + + // abstracts calling visit on a single variant + struct visitor_caller_one + { + + template <typename Visitor, typename Variant, size_t I> + static EA_CONSTEXPR decltype(auto) invoke_visitor(Visitor&& visitor, Variant&& variant) + { + return eastl::invoke(eastl::forward<Visitor>(visitor), + eastl::get<I>(eastl::forward<Variant>(variant))); + } + + template <typename Visitor, typename Variant, size_t... VariantArgIndices> + static EA_CPP14_CONSTEXPR decltype(auto) call_index(Visitor&& visitor, Variant&& variant, index_sequence<VariantArgIndices...>) + { + EA_CPP14_CONSTEXPR auto callers = make_visitor_array((&invoke_visitor<Visitor, Variant, VariantArgIndices>)...); + + return call_visitor_at_index(eastl::move(callers), eastl::forward<Variant>(variant).index(), + eastl::forward<Visitor>(visitor), eastl::forward<Variant>(variant)); + } + + template <typename Visitor, typename Variant> + static EA_CONSTEXPR decltype(auto) call(Visitor&& visitor, Variant&& variant) + { + return call_index(eastl::forward<Visitor>(visitor), + eastl::forward<Variant>(variant), + make_index_sequence<variant_size_v<decay_t<Variant>>>{}); + } + + }; + + template <typename R> + struct visitor_r + { + template <typename Visitor, typename Variant, size_t I> + static EA_CONSTEXPR R invoke_visitor_r(Visitor&& visitor, Variant&& variant) + { + return eastl::invoke(eastl::forward<Visitor>(visitor), + eastl::get<I>(eastl::forward<Variant>(variant))); + } + }; + + // void return type must discard the return values of the visitor even if the visitor returns a value. + template <> + struct visitor_r<void> + { + template <typename Visitor, typename Variant, size_t I> + static EA_CONSTEXPR void invoke_visitor_r(Visitor&& visitor, Variant&& variant) + { + eastl::invoke(eastl::forward<Visitor>(visitor), + eastl::get<I>(eastl::forward<Variant>(variant))); + } + }; + + template<> struct visitor_r<const void> : public visitor_r<void> {}; + template<> struct visitor_r<volatile void> : public visitor_r<void> {}; + template<> struct visitor_r<const volatile void> : public visitor_r<void> {}; + + // abstracts calling visit on a single variant with return types convertible to R + struct visitor_caller_one_r + { + template <typename R, typename Visitor, typename Variant, size_t... VariantArgIndices> + static EA_CPP14_CONSTEXPR decltype(auto) call_index_r(Visitor&& visitor, Variant&& variant, eastl::index_sequence<VariantArgIndices...>) + { + EA_CPP14_CONSTEXPR auto callers = make_visitor_array(&visitor_r<R>::template invoke_visitor_r<Visitor, Variant, VariantArgIndices>...); - // Create the final array of invoke_visitor<0>, invoke_visitor<1>, ... , invoke_visitor<N - 1> - // where N = variant_size<Variant> - EA_CPP14_CONSTEXPR caller_type callers[] = { invoke_visitor<return_type, ArrayIndices>... }; + return callers[static_cast<typename decltype(callers)::size_type>(eastl::forward<Variant>(variant).index())](eastl::forward<Visitor>(visitor), + eastl::forward<Variant>(variant)); + } - return callers[eastl::forward<Variant>(variant).index()]( - eastl::forward<Visitor>(visitor), - index_sequence<ArgsIndices...>(), - eastl::forward<ArgsTuple>(args), - eastl::forward<Variant>(variant) - ); + template <typename R, typename Visitor, typename Variant> + static EA_CONSTEXPR decltype(auto) call_r(Visitor&& visitor, Variant&& variant) + { + return call_index_r<R>(eastl::forward<Visitor>(visitor), eastl::forward<Variant>(variant), eastl::make_index_sequence<eastl::variant_size_v<eastl::decay_t<Variant>>>()); } + }; + /////////////////////////////////////////////////////////////////////////// // 20.7.6, visitation // @@ -1107,24 +1349,80 @@ namespace eastl // visit(MyVisitor{}, v); // calls MyVisitor::operator()(string) {} // + template <typename... Variants> + static EA_CPP14_CONSTEXPR void visit_throw_bad_variant_access(Variants&&... variants) + { + #if EASTL_EXCEPTIONS_ENABLED + using bool_array_type = bool[]; + bool badAccess = false; + + (void)bool_array_type{ (badAccess |= eastl::forward<Variants>(variants).valueless_by_exception(), false)... }; + + if (badAccess) + { + throw bad_variant_access(); + } + #endif + } + + template <typename... Variants> + static EA_CONSTEXPR void visit_static_assert_check(Variants&&... variants) + { + static_assert(sizeof...(Variants) > 0, "`visit` at least one variant instance must be passed as an argument to the visit function"); + + using variant_type = decay_t<meta::get_type_at_t<0, Variants...>>; + static_assert(conjunction_v<is_same<variant_type, decay_t<Variants>>...>, + "`visit` all variants passed to eastl::visit() must have the same type"); + } + + // visit // - template <class Visitor, class... Variants> - EA_CONSTEXPR decltype(auto) visit(Visitor&& visitor, Variants&&... variants) + template <typename Visitor, typename Variant> + EA_CPP14_CONSTEXPR decltype(auto) visit(Visitor&& visitor, Variant&& variant) + { + visit_static_assert_check(eastl::forward<Variant>(variant)); + + visit_throw_bad_variant_access(eastl::forward<Variant>(variant)); + + return visitor_caller_one::call(eastl::forward<Visitor>(visitor), + eastl::forward<Variant>(variant)); + } + + template <typename Visitor, typename... Variants> + EA_CPP14_CONSTEXPR decltype(auto) visit(Visitor&& visitor, Variants&&... variants) { - static_assert(sizeof...(Variants) > 0, "at least one variant instance must be passed as an argument to the visit function"); - - using variant_type = remove_reference_t<meta::get_type_at_t<0, Variants...>>; - static_assert(conjunction_v<is_same<variant_type, remove_reference_t<Variants>>...>, - "all variants passed to eastl::visit() must have the same type"); - - return visitor_caller<Visitor, Variants...>::call( - eastl::forward<Visitor>(visitor), - index_sequence<>(), - make_index_sequence<variant_size_v<variant_type>>(), - tuple<>(), - eastl::forward<Variants>(variants)... - ); + visit_static_assert_check(eastl::forward<Variants>(variants)...); + + visit_throw_bad_variant_access(eastl::forward<Variants>(variants)...); + + return call_initial_n(make_index_sequence<variant_size_v<decay_t<meta::get_type_at_t<0, Variants...>>>>{}, + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); + + } + + template <typename R, typename Visitor, typename Variant, eastl::enable_if_t<!eastl::is_same_v<R, Visitor>, int> = 0> + EA_CPP14_CONSTEXPR R visit(Visitor&& visitor, Variant&& variant) + { + visit_static_assert_check(eastl::forward<Variant>(variant)); + + visit_throw_bad_variant_access(eastl::forward<Variant>(variant)); + + return visitor_caller_one_r::call_r<R>(eastl::forward<Visitor>(visitor), + eastl::forward<Variant>(variant)); + } + + template <typename R, typename Visitor, typename... Variants, eastl::enable_if_t<!eastl::is_same_v<R, Visitor>, int> = 0> + EA_CPP14_CONSTEXPR R visit(Visitor&& visitor, Variants&&... variants) + { + visit_static_assert_check(eastl::forward<Variants>(variants)...); + + visit_throw_bad_variant_access(eastl::forward<Variants>(variants)...); + + return call_initial_n_r<R>(make_index_sequence<variant_size_v<decay_t<meta::get_type_at_t<0, Variants...>>>>{}, + eastl::forward<Visitor>(visitor), + eastl::forward<Variants>(variants)...); } @@ -1134,42 +1432,76 @@ namespace eastl namespace internal { - template <class... Types, class Predicate> - EA_CPP14_CONSTEXPR bool Compare(const variant<Types...>& lhs, const variant<Types...>& rhs, Predicate predicate) + // For relational operators we do not need to create the NxN matrix of comparisons since we know already + // that both the lhs and rhs variants have the same index. We just need to compare the value of the types at that + // index for equality. Therefore the visitation is simpler than visit() for relational operators. + // + struct variant_relational_comparison { - return visit(predicate, lhs, rhs); - } + template <typename Compare, size_t I, typename Variant> + static EA_CONSTEXPR bool invoke_relational_visitor(const Variant& lhs, const Variant& rhs) + { + return eastl::invoke(Compare{}, eastl::get<I>(lhs), eastl::get<I>(rhs)); + } - // For variant visitation, we need to have a comparison function for all possible combinations of types, - // eg. for variant<int, string>, our comparator needs: - // - // bool operator()(int, int); - // bool operator()(int, string); - // bool operator()(string, int); - // bool operator()(string, string); - // - // Even though we never call the mixed-type versions of these functions when comparing variants, we - // need them in order to compile visit(). So this struct forwards the good comparisons to the appropriate - // comparison, and asserts that we never call the bad comparisons. - template <typename C> - struct variant_comparison : public C - { - template <typename A, typename B, typename = eastl::enable_if_t<eastl::is_same_v<eastl::decay_t<A>, eastl::decay_t<B>>>> - auto operator()(const A& a, const B& b) + template <typename Compare, typename Variant, size_t... VariantArgIndices> + static EA_CPP14_CONSTEXPR bool call_index(const Variant& lhs, const Variant& rhs, eastl::index_sequence<VariantArgIndices...>) { - return C::operator()(a, b); + using invoke_relational_visitor_func_ptr = bool (*)(const Variant&, const Variant&); + + EA_CPP14_CONSTEXPR invoke_relational_visitor_func_ptr visitors[] = { static_cast<invoke_relational_visitor_func_ptr>(&invoke_relational_visitor<Compare, VariantArgIndices, Variant>)... }; + + return visitors[lhs.index()](lhs, rhs); } - template <typename A, typename B, typename = eastl::enable_if_t<!eastl::is_same_v<eastl::decay_t<A>, eastl::decay_t<B>>>> - bool operator()(const A&, const B&) + template <typename Compare, typename Variant> + static EA_CONSTEXPR bool call(const Variant& lhs, const Variant& rhs) { - EASTL_ASSERT_MSG(false, "eastl::variant<> comparison function called on two different types at different indices! This is a library bug! Please file bug report."); - return false; + 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> + static EA_CONSTEXPR bool CompareVariantRelational(const Variant& lhs, const Variant& rhs) + { + 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 + /////////////////////////////////////////////////////////////////////////// // 20.7.5, relational operators // @@ -1178,7 +1510,17 @@ namespace eastl { if (lhs.index() != rhs.index()) return false; if (lhs.valueless_by_exception()) return true; - return internal::Compare(lhs, rhs, internal::variant_comparison<equal_to<>>{}); + + return internal::CompareVariantRelational<eastl::equal_to<>>(lhs, rhs); + } + + template <class... Types> + EA_CPP14_CONSTEXPR bool operator!=(const variant<Types...>& lhs, const variant<Types...>& rhs) + { + if (lhs.index() != rhs.index()) return true; + if (lhs.valueless_by_exception()) return false; + + return internal::CompareVariantRelational<eastl::not_equal_to<>>(lhs, rhs); } template <class... Types> @@ -1188,15 +1530,8 @@ namespace eastl if (lhs.valueless_by_exception()) return true; if (lhs.index() < rhs.index()) return true; if (lhs.index() > rhs.index()) return false; - return internal::Compare(lhs, rhs, internal::variant_comparison<less<>>{}); - } - template <class... Types> - EA_CPP14_CONSTEXPR bool operator!=(const variant<Types...>& lhs, const variant<Types...>& rhs) - { - if (lhs.index() != rhs.index()) return true; - if (lhs.valueless_by_exception()) return false; - return internal::Compare(lhs, rhs, internal::variant_comparison<not_equal_to<>>{}); + return internal::CompareVariantRelational<eastl::less<>>(lhs, rhs); } template <class... Types> @@ -1206,17 +1541,19 @@ namespace eastl if (rhs.valueless_by_exception()) return true; if (lhs.index() > rhs.index()) return true; if (lhs.index() < rhs.index()) return false; - return internal::Compare(lhs, rhs, internal::variant_comparison<greater<>>{}); + + return internal::CompareVariantRelational<eastl::greater<>>(lhs, rhs); } template <class... Types> EA_CPP14_CONSTEXPR bool operator<=(const variant<Types...>& lhs, const variant<Types...>& rhs) { - if (rhs.valueless_by_exception()) return true; - if (lhs.valueless_by_exception()) return false; + if (lhs.valueless_by_exception()) return true; + if (rhs.valueless_by_exception()) return false; if (lhs.index() < rhs.index()) return true; if (lhs.index() > rhs.index()) return false; - return internal::Compare(lhs, rhs, internal::variant_comparison<less_equal<>>{}); + + return internal::CompareVariantRelational<eastl::less_equal<>>(lhs, rhs); } template <class... Types> @@ -1226,11 +1563,26 @@ namespace eastl if (lhs.valueless_by_exception()) return false; if (lhs.index() > rhs.index()) return true; if (lhs.index() < rhs.index()) return false; - return internal::Compare(lhs, rhs, internal::variant_comparison<greater_equal<>>{}); + + 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() #endif // EASTL_VARIANT_H - 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 7b7bf2f..90b1d75 100644 --- a/EASTL/source/numeric_limits.cpp +++ b/EASTL/source/numeric_limits.cpp @@ -39,7 +39,7 @@ #endif -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(EA_COMPILER_CLANG_CL) // VC++ has a long-standing bug: it fails to allow the definition of static const member variables // outside the declaration within the class. The C++ Standard actually requires that they be defined // and some other compilers fail to link if they aren't. So we simply don't define the members for VC++. @@ -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; @@ -442,7 +468,7 @@ EA_CONSTEXPR_OR_CONST bool numeric_limits<long long>::is_iec559; // __uint128_t - #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) // If __int128_t/__uint128_t is supported... + #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(__clang__)) // If __int128_t/__uint128_t is supported... EA_CONSTEXPR_OR_CONST bool numeric_limits<__uint128_t>::is_specialized; EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::digits; EA_CONSTEXPR_OR_CONST int numeric_limits<__uint128_t>::digits10; @@ -468,7 +494,7 @@ #endif // __int128_t - #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) // If __int128_t/__uint128_t is supported... + #if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(__clang__)) // If __int128_t/__uint128_t is supported... EA_CONSTEXPR_OR_CONST bool numeric_limits<__int128_t>::is_specialized; EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::digits; EA_CONSTEXPR_OR_CONST int numeric_limits<__int128_t>::digits10; diff --git a/EASTL/source/thread_support.cpp b/EASTL/source/thread_support.cpp index 3b8550e..693dd24 100644 --- a/EASTL/source/thread_support.cpp +++ b/EASTL/source/thread_support.cpp @@ -53,7 +53,11 @@ namespace eastl pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); - pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); + + #if defined(EA_HAVE_pthread_mutexattr_setpshared_DECL) + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); + #endif + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&mMutex, &attr); pthread_mutexattr_destroy(&attr); 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 1cb298b..fca6b2c 100644 --- a/EASTL/test/source/EASTLTest.h +++ b/EASTL/test/source/EASTLTest.h @@ -82,6 +82,7 @@ int TestVectorMap(); int TestVectorSet(); int TestAtomicBasic(); int TestAtomicAsm(); +int TestBitcast(); // Now enable warnings as desired. @@ -1260,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 8cfaadd..2a28c07 100644 --- a/EASTL/test/source/TestAllocator.cpp +++ b/EASTL/test/source/TestAllocator.cpp @@ -129,7 +129,6 @@ static int TestFixedAllocator() typedef eastl::list<int, fixed_allocator_with_overflow> IntList; typedef IntList::node_type IntListNode; - IntList intList1; const size_t kAlignOfIntListNode = EA_ALIGN_OF(IntListNode); // ensure the fixed buffer contains the default value that will be replaced @@ -140,6 +139,8 @@ static int TestFixedAllocator() EATEST_VERIFY(buffer1[i].mValue == DEFAULT_VALUE); } + IntList intList1; + // replace all the values in the local buffer with the test value intList1.get_allocator().init(buffer1, sizeof(buffer1), sizeof(IntListNode), kAlignOfIntListNode); for (size_t i = 0; i < kBufferCount; i++) @@ -150,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/TestBitcast.cpp b/EASTL/test/source/TestBitcast.cpp new file mode 100644 index 0000000..d6f0840 --- /dev/null +++ b/EASTL/test/source/TestBitcast.cpp @@ -0,0 +1,52 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLTest.h" +#include <EASTL/bit.h> + +using namespace eastl; + + +int TestBitcast() +{ + int nErrorCount = 0; + + { + uint32_t int32Value = 0x12345678; + float floatValue = eastl::bit_cast<float>(int32Value); + VERIFY(memcmp(&int32Value, &floatValue, sizeof(float)) == 0); + } + + { + struct IntFloatStruct + { + uint32_t i = 0x87654321; + float f = 10.f; + }; + struct CharIntStruct + { + char c1; + char c2; + char c3; + char c4; + uint32_t i; + }; + + IntFloatStruct ifStruct; + CharIntStruct ciStruct = eastl::bit_cast<CharIntStruct>(ifStruct); + VERIFY(memcmp(&ifStruct, &ciStruct, sizeof(IntFloatStruct)) == 0); + } + +#if EASTL_CONSTEXPR_BIT_CAST_SUPPORTED + { + constexpr uint32_t int32Value = 40; + constexpr float floatValue = eastl::bit_cast<float>(int32Value); + VERIFY(memcmp(&int32Value, &floatValue, sizeof(float)) == 0); + } +#endif + + + return nErrorCount; +} 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 88f4c2c..1e25200 100644 --- a/EASTL/test/source/TestFunctional.cpp +++ b/EASTL/test/source/TestFunctional.cpp @@ -68,14 +68,17 @@ namespace bool operator==(const N1& n1, const N1& n1a){ return (n1.mX == n1a.mX); } bool operator==(const N1& n1, const N2& n2) { return (n1.mX == n2.mX); } bool operator==(const N2& n2, const N1& n1) { return (n2.mX == n1.mX); } + bool operator==(const volatile N1& n1, const volatile N1& n1a) { return (n1.mX == n1a.mX); } bool operator!=(const N1& n1, const N1& n1a){ return (n1.mX != n1a.mX); } bool operator!=(const N1& n1, const N2& n2) { return (n1.mX != n2.mX); } bool operator!=(const N2& n2, const N1& n1) { return (n2.mX != n1.mX); } + bool operator!=(const volatile N1& n1, const volatile N1& n1a) { return (n1.mX != n1a.mX); } bool operator< (const N1& n1, const N1& n1a){ return (n1.mX < n1a.mX); } bool operator< (const N1& n1, const N2& n2) { return (n1.mX < n2.mX); } bool operator< (const N2& n2, const N1& n1) { return (n2.mX < n1.mX); } + bool operator< (const volatile N1& n1, const volatile N1& n1a) { return (n1.mX < n1a.mX); } // Used for mem_fun tests below. @@ -260,8 +263,12 @@ int TestFunctional() N1 n13(3); N2 n21(1); N2 n22(2); - //const N1 cn11(1); - //const N1 cn13(3); + const N1 cn11(1); + const N1 cn13(3); + volatile N1 vn11(1); + volatile N1 vn13(3); + const volatile N1 cvn11(1); + const volatile N1 cvn13(3); equal_to_2<N1, N2> e; EATEST_VERIFY(e(n11, n21)); @@ -269,9 +276,40 @@ int TestFunctional() equal_to_2<N1, N1> es; EATEST_VERIFY(es(n11, n11)); + EATEST_VERIFY(!es(n11, n13)); - //equal_to_2<const N1, N1> ec; // To do: Make this case work. - //EATEST_VERIFY(e(cn11, n11)); + equal_to_2<const N1, N1> ec; + EATEST_VERIFY(ec(cn11, n11)); + EATEST_VERIFY(ec(n11, cn11)); + + equal_to_2<N1, const N1> ec2; + EATEST_VERIFY(ec2(n11, cn11)); + EATEST_VERIFY(ec2(cn11, n11)); + + equal_to_2<const N1, const N1> ecc; + EATEST_VERIFY(ecc(cn11, cn11)); + + equal_to_2<volatile N1, N1> ev; + EATEST_VERIFY(ev(vn11, n11)); + EATEST_VERIFY(ev(n11, vn11)); + + equal_to_2<N1, volatile N1> ev2; + EATEST_VERIFY(ev2(n11, vn11)); + EATEST_VERIFY(ev2(vn11, n11)); + + equal_to_2<volatile N1, volatile N1> evv; + EATEST_VERIFY(evv(vn11, vn11)); + + equal_to_2<const volatile N1, N1> ecv; + EATEST_VERIFY(ecv(cvn11, n11)); + EATEST_VERIFY(ecv(n11, cvn11)); + + equal_to_2<N1, const volatile N1> ecv2; + EATEST_VERIFY(ecv2(n11, cvn11)); + EATEST_VERIFY(ecv2(cvn11, n11)); + + equal_to_2<const volatile N1, const volatile N1> ecvcv; + EATEST_VERIFY(ecvcv(cvn11, cvn11)); // not_equal_to_2 not_equal_to_2<N1, N2> n; @@ -280,6 +318,40 @@ int TestFunctional() not_equal_to_2<N1, N1> ns; EATEST_VERIFY(ns(n11, n13)); + EATEST_VERIFY(!ns(n11, n11)); + + not_equal_to_2<const N1, N1> nc; + EATEST_VERIFY(nc(cn11, n13)); + EATEST_VERIFY(nc(n13, cn11)); + + not_equal_to_2<N1, const N1> nc2; + EATEST_VERIFY(nc2(n13, cn11)); + EATEST_VERIFY(nc2(cn11, n13)); + + not_equal_to_2<const N1, const N1> ncc; + EATEST_VERIFY(ncc(cn11, cn13)); + + not_equal_to_2<volatile N1, N1> nv; + EATEST_VERIFY(nv(vn11, n13)); + EATEST_VERIFY(nv(n11, vn13)); + + not_equal_to_2<N1, volatile N1> nv2; + EATEST_VERIFY(nv2(n11, vn13)); + EATEST_VERIFY(nv2(vn11, n13)); + + not_equal_to_2<volatile N1, volatile N1> nvv; + EATEST_VERIFY(nvv(vn11, vn13)); + + not_equal_to_2<const volatile N1, N1> ncv; + EATEST_VERIFY(ncv(cvn11, n13)); + EATEST_VERIFY(ncv(n11, cvn13)); + + not_equal_to_2<N1, const volatile N1> ncv2; + EATEST_VERIFY(ncv2(n11, cvn13)); + EATEST_VERIFY(ncv2(cvn11, n13)); + + not_equal_to_2<const volatile N1, const volatile N1> ncvcv; + EATEST_VERIFY(ncvcv(cvn11, cvn13)); // less_2 less_2<N1, N2> le; @@ -288,6 +360,39 @@ int TestFunctional() less_2<N1, N1> les; EATEST_VERIFY(les(n11, n13)); + + less_2<const N1, N1> lec; + EATEST_VERIFY(lec(cn11, n13)); + EATEST_VERIFY(lec(n11, cn13)); + + less_2<N1, const N1> lec2; + EATEST_VERIFY(lec2(n11, cn13)); + EATEST_VERIFY(lec2(cn11, n13)); + + less_2<const N1, const N1> lecc; + EATEST_VERIFY(lecc(cn11, cn13)); + + less_2<volatile N1, N1> lev; + EATEST_VERIFY(lev(vn11, n13)); + EATEST_VERIFY(lev(n11, vn13)); + + less_2<N1, volatile N1> lev2; + EATEST_VERIFY(lev2(n11, vn13)); + EATEST_VERIFY(lev2(vn11, n13)); + + less_2<volatile N1, volatile N1> levv; + EATEST_VERIFY(levv(vn11, vn13)); + + less_2<const volatile N1, N1> lecv; + EATEST_VERIFY(lecv(cvn11, n13)); + EATEST_VERIFY(lecv(n11, cvn13)); + + less_2<N1, const volatile N1> lecv2; + EATEST_VERIFY(lecv2(n11, cvn13)); + EATEST_VERIFY(lecv2(cvn11, n13)); + + less_2<const volatile N1, const volatile N1> lecvcv; + EATEST_VERIFY(lecvcv(cvn11, cvn13)); } @@ -435,6 +540,7 @@ int TestFunctional() void Add(int addAmount) { value += addAmount; } int GetValue() { return value; } int& GetValueReference() { return value; } + void NoThrow(int inValue) EA_NOEXCEPT {} int value; }; @@ -444,6 +550,12 @@ int TestFunctional() bool called = false; }; + struct TestFunctorNoThrow + { + void operator()() EA_NOEXCEPT { called = true; } + bool called = false; + }; + struct TestFunctorArguments { void operator()(int i) { value = i; } @@ -457,6 +569,8 @@ int TestFunctional() static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::Add), TestStruct, int>::type, void>::value, "incorrect type for invoke_result"); static_assert(eastl::is_invocable<decltype(&TestStruct::Add), TestStruct, int>::value, "incorrect value for is_invocable"); + static_assert(eastl::is_nothrow_invocable<decltype(&TestStruct::NoThrow), TestStruct, int>::value, "incorrect value for is_nothrow_invocable"); + static_assert(!eastl::is_nothrow_invocable<decltype(&TestStruct::Add), TestStruct, int>::value, "incorrect value for is_nothrow_invocable"); } { TestStruct a(42); @@ -465,6 +579,8 @@ int TestFunctional() static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::Add), TestStruct *, int>::type, void>::value, "incorrect type for invoke_result"); static_assert(eastl::is_invocable<decltype(&TestStruct::Add), TestStruct *, int>::value, "incorrect value for is_invocable"); + static_assert(eastl::is_nothrow_invocable<decltype(&TestStruct::NoThrow), TestStruct *, int>::value, "incorrect value for is_nothrow_invocable"); + static_assert(!eastl::is_nothrow_invocable<decltype(&TestStruct::Add), TestStruct *, int>::value, "incorrect value for is_nothrow_invocable"); } { TestStruct a(42); @@ -474,6 +590,8 @@ int TestFunctional() static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::Add), eastl::reference_wrapper<TestStruct>, int>::type, void>::value, "incorrect type for invoke_result"); static_assert(eastl::is_invocable<decltype(&TestStruct::Add), eastl::reference_wrapper<TestStruct>, int>::value, "incorrect value for is_invocable"); + static_assert(eastl::is_nothrow_invocable<decltype(&TestStruct::NoThrow), eastl::reference_wrapper<TestStruct>, int>::value, "incorrect value for is_nothrow_invocable"); + static_assert(!eastl::is_nothrow_invocable<decltype(&TestStruct::Add), eastl::reference_wrapper<TestStruct>, int>::value, "incorrect value for is_nothrow_invocable"); } { TestStruct a(42); @@ -535,6 +653,16 @@ int TestFunctional() static_assert(eastl::is_same<typename eastl::invoke_result<decltype(f)>::type, void>::value, "incorrect type for invoke_result"); static_assert(eastl::is_invocable<decltype(f)>::value, "incorrect value for is_invocable"); + static_assert(!eastl::is_nothrow_invocable<decltype(f)>::value, "incorrect value for is_nothrow_invocable"); + } + { + TestFunctorNoThrow f; + eastl::invoke(f); + EATEST_VERIFY(f.called); + + static_assert(eastl::is_same<typename eastl::invoke_result<decltype(f)>::type, void>::value, "incorrect type for invoke_result"); + static_assert(eastl::is_invocable<decltype(f)>::value, "incorrect value for is_invocable"); + static_assert(eastl::is_nothrow_invocable<decltype(f)>::value, "incorrect value for is_nothrow_invocable"); } { TestFunctorArguments f; @@ -545,6 +673,43 @@ int TestFunctional() static_assert(eastl::is_invocable<decltype(f), int>::value, "incorrect value for is_invocable"); } { + struct TestInvokeConstAccess + { + void ConstMemberFunc(int i) const {} + void ConstVolatileMemberFunc(int i) const volatile {} + + int mI; + }; + + static_assert(eastl::is_invocable<decltype(&TestInvokeConstAccess::ConstMemberFunc), const TestInvokeConstAccess*, int>::value, "incorrect value for is_invocable"); + static_assert(eastl::is_invocable<decltype(&TestInvokeConstAccess::ConstVolatileMemberFunc), const volatile TestInvokeConstAccess*, int>::value, "incorrect value for is_invocable"); + } + { + struct TestReferenceWrapperInvoke + { + int NonConstMemberFunc(int i) { return i; } + int ConstMemberFunc(int i) const { return i; } + + int mI = 1; + const int mIC = 1; + }; + + TestReferenceWrapperInvoke testStruct; + int ret; + + ret = eastl::invoke(&TestReferenceWrapperInvoke::NonConstMemberFunc, eastl::ref(testStruct), 1); + EATEST_VERIFY(ret == 1); + + ret = eastl::invoke(&TestReferenceWrapperInvoke::ConstMemberFunc, eastl::ref(testStruct), 1); + EATEST_VERIFY(ret == 1); + + ret = eastl::invoke(&TestReferenceWrapperInvoke::mI, eastl::ref(testStruct)); + EATEST_VERIFY(ret == 1); + + ret = eastl::invoke(&TestReferenceWrapperInvoke::mIC, eastl::ref(testStruct)); + EATEST_VERIFY(ret == 1); + } + { static bool called = false; auto f = [] {called = true;}; eastl::invoke(f); @@ -565,16 +730,20 @@ int TestFunctional() { struct A {}; struct B : public A {}; + struct C : public A {}; struct TestStruct { A a() { return A(); }; B b() { return B(); }; + C c() EA_NOEXCEPT { return C(); }; }; static_assert(!eastl::is_invocable_r<B, decltype(&TestStruct::a), TestStruct>::value, "incorrect value for is_invocable_r"); static_assert(eastl::is_invocable_r<A, decltype(&TestStruct::b), TestStruct>::value, "incorrect value for is_invocable_r"); static_assert(eastl::is_invocable_r<B, decltype(&TestStruct::b), TestStruct>::value, "incorrect value for is_invocable_r"); + static_assert(!eastl::is_nothrow_invocable_r<B, decltype(&TestStruct::b), TestStruct>::value, "incorrect value for is_nothrow_invocable_r"); + static_assert(eastl::is_nothrow_invocable_r<C, decltype(&TestStruct::c), TestStruct>::value, "incorrect value for is_nothrow_invocable_r"); } } @@ -1322,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 eff6156..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); } @@ -994,9 +998,19 @@ int TestHash() EATEST_VERIFY(it != hashSet.end()); else EATEST_VERIFY(it == hashSet.end()); + + string::CtorSprintf cs; + string s(cs, "%d", i); + + it = hashSet.find_as(s); + if (i < kCount) + EATEST_VERIFY(it != hashSet.end()); + else + EATEST_VERIFY(it == hashSet.end()); } } + { // Test const containers. const hash_set<int> constHashSet; diff --git a/EASTL/test/source/TestIntrusiveHash.cpp b/EASTL/test/source/TestIntrusiveHash.cpp index 4fd8215..f089aab 100644 --- a/EASTL/test/source/TestIntrusiveHash.cpp +++ b/EASTL/test/source/TestIntrusiveHash.cpp @@ -617,6 +617,12 @@ int TestIntrusiveHash() itfc = ihmMW1Const.find_as(7.f); VERIFY(itfc->mKey == 7); + + itf = ihmMW1.find_as(8); + VERIFY(itf->mKey == 8); + + itfc = ihmMW1Const.find_as(8); + VERIFY(itfc->mKey == 8); // iterator find_as(const U& u, UHash uhash, BinaryPredicate predicate); diff --git a/EASTL/test/source/TestIterator.cpp b/EASTL/test/source/TestIterator.cpp index e168118..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> @@ -14,12 +15,15 @@ #include <EASTL/string.h> #include <EASTL/intrusive_list.h> #include <EASTL/memory.h> +#include <EASTL/unique_ptr.h> EA_DISABLE_ALL_VC_WARNINGS() #include <stdio.h> #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{}; @@ -139,6 +143,77 @@ int TestIterator_moveIterator() EATEST_VERIFY(*moveIter != *(constBeginMoveIter + 2)); } + { + // Ensure that move_iterator indeed move yielded value whenever possible. + auto x = eastl::make_unique<int>(42); + auto* pX = &x; + auto moveIter = eastl::make_move_iterator(pX); + + constexpr bool isCorrectReferenceType = eastl::is_same_v<decltype(moveIter)::reference, eastl::unique_ptr<int>&&>; + constexpr bool isCorrectReturnType = eastl::is_same_v<decltype(*moveIter), eastl::unique_ptr<int>&&>; + + static_assert(isCorrectReferenceType, "move_iterator::reference has wrong type."); + static_assert(isCorrectReturnType, "move_iterator::operator*() has wrong return type."); + EATEST_VERIFY(isCorrectReferenceType); + EATEST_VERIFY(isCorrectReturnType); + + auto pMoveX = *moveIter; + EATEST_VERIFY(*pMoveX == 42); + } + + // Bellow are regression tests that ensure we are covering the defect LWG 2106: http://cplusplus.github.io/LWG/lwg-defects.html#2106 + { + // Check that we support iterators yielding const references. + const int x = 42; + const int* pX = &x; + auto moveIter = eastl::make_move_iterator(pX); + + constexpr bool isCorrectReferenceType = eastl::is_same_v<decltype(moveIter)::reference, const int&&>; + constexpr bool isCorrectReturnType = eastl::is_same_v<decltype(*moveIter), const int&&>; + + static_assert(isCorrectReferenceType, "move_iterator::reference has wrong type."); + static_assert(isCorrectReturnType, "move_iterator::operator*() has wrong return type."); + EATEST_VERIFY(isCorrectReferenceType); + EATEST_VERIFY(isCorrectReturnType); + + auto pCopiedX = *moveIter; + EATEST_VERIFY(pCopiedX == 42); + } + + { + // Check that we support iterators yielding plain value (typically a proxy-iterator). + struct FakeProxyIterator + { + 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. + using reference = int; // Note that we are yielding by value. + + reference operator*() const { return 42; } + pointer operator->() { return 42; } + FakeProxyIterator& operator++() { return *this; } + FakeProxyIterator operator++(int) { return {}; } + + bool operator==(const FakeProxyIterator& rhs) { return true; }; + bool operator!=(const FakeProxyIterator& rhs) { return false; }; + }; + + FakeProxyIterator it = {}; + auto moveIter = eastl::make_move_iterator(it); + + constexpr bool isCorrectReferenceType = eastl::is_same_v<decltype(moveIter)::reference, int>; + constexpr bool isCorrectReturnType = eastl::is_same_v<decltype(*moveIter), int>; + + static_assert(isCorrectReferenceType, "move_iterator::reference has wrong type."); + static_assert(isCorrectReturnType, "move_iterator::operator*() has wrong return type."); + EATEST_VERIFY(isCorrectReferenceType); + EATEST_VERIFY(isCorrectReturnType); + + auto pCopiedX = *moveIter; + EATEST_VERIFY(pCopiedX == 42); + } + return nErrorCount; } @@ -178,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; @@ -349,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"); } @@ -383,6 +490,77 @@ 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"); + } + + { + // array cbegin - cend + int arr[3]{ 1, 2, 3 }; + auto b = eastl::cbegin(arr); + auto e = eastl::cend(arr); + EATEST_VERIFY(*b == 1); + + auto dist = eastl::distance(b,e); + 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 df4a195..0df8c88 100644 --- a/EASTL/test/source/TestMap.cpp +++ b/EASTL/test/source/TestMap.cpp @@ -120,6 +120,19 @@ int TestMap() } { + eastl::map<int*, int> m; + int* ip = (int*)(uintptr_t)0xDEADC0DE; + + m[ip] = 0; + + auto it = m.find_as(ip, eastl::less_2<int*, int*>{}); + EATEST_VERIFY(it != m.end()); + + it = m.find_as((int*)(uintptr_t)0xDEADC0DE, eastl::less_2<int*, int*>{}); + EATEST_VERIFY(it != m.end()); + } + + { // User reports that vector<map<enum,enum>> is crashing after the recent changes to add rvalue move and emplace support to rbtree. typedef eastl::map<int, int> IntIntMap; typedef eastl::vector<IntIntMap> IntIntMapArray; @@ -134,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); @@ -210,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 6c1fa4f..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 { { @@ -536,13 +578,13 @@ int TestOptional() // optional rvalue tests { - VERIFY(*optional<int>(1) == 1); - VERIFY( optional<int>(1).value() == 1); - VERIFY( optional<int>(1).value_or(0xdeadf00d) == 1); - VERIFY( optional<int>().value_or(0xdeadf00d) == 0xdeadf00d); - VERIFY( optional<int>(1).has_value() == true); - VERIFY( optional<int>().has_value() == false); - VERIFY( optional<IntStruct>(in_place, 10)->data == 10); + VERIFY(*optional<uint32_t>(1u) == 1u); + VERIFY(optional<uint32_t>(1u).value() == 1u); + VERIFY(optional<uint32_t>(1u).value_or(0xdeadf00d) == 1u); + VERIFY(optional<uint32_t>().value_or(0xdeadf00d) == 0xdeadf00d); + VERIFY(optional<uint32_t>(1u).has_value() == true); + VERIFY(optional<uint32_t>().has_value() == false); + VERIFY( optional<IntStruct>(in_place, 10)->data == 10); } @@ -611,7 +653,7 @@ int TestOptional() copyCtorCalledWithUninitializedValue = moveCtorCalledWithUninitializedValue = false; struct local { - int val; + uint32_t val; local() : val(0xabcdabcd) {} 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/TestSpan.cpp b/EASTL/test/source/TestSpan.cpp index 060950d..5a0ec07 100644 --- a/EASTL/test/source/TestSpan.cpp +++ b/EASTL/test/source/TestSpan.cpp @@ -295,7 +295,7 @@ void TestSpanContainerConversion(int& nErrorCount) { vector<int> v = {0, 1, 2, 3, 4, 5}; - span<const int, 3> s1(v); + span<const int, 6> s1(v); span<const int> s2(s1); VERIFY(s2.size() == (span<const int>::index_type)v.size()); @@ -391,6 +391,24 @@ void TestSpanSubViews(int& nErrorCount) VERIFY(first_span[3] == 9); } + { // empty range + span<int, 0> s{}; + + auto fixed_span = s.subspan<0, 0>(); + VERIFY(fixed_span.empty()); + fixed_span = s.first<0>(); + VERIFY(fixed_span.empty()); + fixed_span = s.last<0>(); + VERIFY(fixed_span.empty()); + + span<int> dynamic_span; + VERIFY(dynamic_span.empty()); + dynamic_span = s.first(0); + VERIFY(dynamic_span.empty()); + dynamic_span = s.last(0); + VERIFY(dynamic_span.empty()); + } + { // subspan: full range span<int, 10> s = arr1; 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 0fb6280..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,13 +624,55 @@ 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); + static_assert(is_reference<Class&&>::value == true, "is_reference failure"); + EATEST_VERIFY(GetType(is_reference<Class&&>()) == true); + static_assert(is_reference<const Class&>::value == true, "is_reference failure"); EATEST_VERIFY(GetType(is_reference<const Class&>()) == true); + static_assert(is_reference<const Class&&>::value == true, "is_reference failure"); + EATEST_VERIFY(GetType(is_reference<const Class&&>()) == true); + static_assert(is_reference<Class>::value == false, "is_reference failure"); EATEST_VERIFY(GetType(is_reference<Class>()) == false); @@ -634,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 @@ -646,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 @@ -674,6 +731,14 @@ int TestTypeTraits() static_assert(is_enum_v<Class> == false, "is_enum failure "); EATEST_VERIFY(GetType(is_enum<Class>()) == false); + static_assert(is_enum<Enum&>::value == false, "is_enum failure "); + static_assert(is_enum_v<Enum&> == false, "is_enum failure "); + EATEST_VERIFY(GetType(is_enum<Enum&>()) == false); + + static_assert(is_enum<Enum&&>::value == false, "is_enum failure "); + static_assert(is_enum_v<Enum&&> == false, "is_enum failure "); + EATEST_VERIFY(GetType(is_enum<Enum&&>()) == false); + // is_union static_assert(is_union<Union>::value == true, "is_union failure"); @@ -705,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"); @@ -717,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"); @@ -747,6 +822,9 @@ int TestTypeTraits() static_assert(is_object<Class&>::value == false, "is_object failure"); EATEST_VERIFY(GetType(is_object<Class&>()) == false); + static_assert(is_object<Class&&>::value == false, "is_object failure"); + EATEST_VERIFY(GetType(is_object<Class&&>()) == false); + // is_scalar static_assert(is_scalar<int>::value == true, "is_scalar failure"); @@ -803,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"); @@ -826,11 +906,26 @@ 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 + + // 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... enum UnderlyingTypeTest : uint16_t { firstVal = 0, secondVal = 1 }; - static_assert(sizeof(underlying_type<UnderlyingTypeTest>::type) == sizeof(uint16_t), "underlying_type failure"); + + constexpr bool isUnderlyingTypeCorrect = is_same_v<underlying_type_t<UnderlyingTypeTest>, uint16_t>; + static_assert(isUnderlyingTypeCorrect, "Wrong type for underlying_type_t."); + EATEST_VERIFY(isUnderlyingTypeCorrect); + + auto v1 = to_underlying(UnderlyingTypeTest::firstVal); + auto v2 = to_underlying(UnderlyingTypeTest::secondVal); + + constexpr bool isToUnderlyingReturnTypeCorrect = is_same_v<decltype(v1), uint16_t>; + static_assert(isToUnderlyingReturnTypeCorrect, "Wrong return type for to_underlying."); + EATEST_VERIFY(isToUnderlyingReturnTypeCorrect); + + EATEST_VERIFY(v1 == 0 && v2 == 1); #endif @@ -1038,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 "); @@ -1053,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 "); @@ -1064,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 @@ -1223,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"); @@ -1276,6 +1407,7 @@ int TestTypeTraits() static_assert(is_constructible<const void>::value == false, "is_constructible failure"); static_assert(is_constructible<int>::value == true, "is_constructible failure"); static_assert(is_constructible<int&>::value == false, "is_constructible failure"); + static_assert(is_constructible<int&&>::value == false, "is_constructible failure"); static_assert(is_constructible<int*>::value == true, "is_constructible failure"); static_assert(is_constructible<int[]>::value == false, "is_constructible failure"); static_assert(is_constructible<int[4]>::value == true, "is_constructible failure"); @@ -1365,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 @@ -1381,6 +1515,8 @@ int TestTypeTraits() // is_trivially_destructible static_assert(is_trivially_destructible<int>::value == true, "is_trivially_destructible failure"); + static_assert(is_trivially_destructible<int&>::value == true, "is_trivially_destructible failure"); + static_assert(is_trivially_destructible<int&&>::value == true, "is_trivially_destructible failure"); static_assert(is_trivially_destructible<char>::value == true, "is_trivially_destructible failure"); static_assert(is_trivially_destructible<char*>::value == true, "is_trivially_destructible failure"); static_assert(is_trivially_destructible<void>::value == false, "is_trivially_destructible failure"); @@ -1388,16 +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<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"); @@ -1573,6 +1718,125 @@ int TestTypeTraits() EATEST_VERIFY(u64 == UINT64_C(0xffffffffffffffff)); i64 = static_cast<eastl::make_signed<int64_t>::type>(u64); EATEST_VERIFY(i64 == -1); + + + static_assert(eastl::is_same_v<signed char, eastl::make_signed<unsigned char>::type>); + static_assert(eastl::is_same_v<short, eastl::make_signed<unsigned short>::type>); + static_assert(eastl::is_same_v<int, eastl::make_signed<unsigned int>::type>); + static_assert(eastl::is_same_v<long, eastl::make_signed<unsigned long>::type>); + static_assert(eastl::is_same_v<long long, eastl::make_signed<unsigned long long>::type>); + + static_assert(eastl::is_same_v<const signed char, eastl::make_signed<const unsigned char>::type>); + static_assert(eastl::is_same_v<const short, eastl::make_signed<const unsigned short>::type>); + static_assert(eastl::is_same_v<const int, eastl::make_signed<const unsigned int>::type>); + static_assert(eastl::is_same_v<const long, eastl::make_signed<const unsigned long>::type>); + static_assert(eastl::is_same_v<const long long, eastl::make_signed<const unsigned long long>::type>); + + static_assert(eastl::is_same_v<volatile signed char, eastl::make_signed<volatile unsigned char>::type>); + static_assert(eastl::is_same_v<volatile short, eastl::make_signed<volatile unsigned short>::type>); + static_assert(eastl::is_same_v<volatile int, eastl::make_signed<volatile unsigned int>::type>); + static_assert(eastl::is_same_v<volatile long, eastl::make_signed<volatile unsigned long>::type>); + static_assert(eastl::is_same_v<volatile long long, eastl::make_signed<volatile unsigned long long>::type>); + + static_assert(eastl::is_same_v<const volatile signed char, eastl::make_signed<const volatile unsigned char>::type>); + static_assert(eastl::is_same_v<const volatile short, eastl::make_signed<const volatile unsigned short>::type>); + static_assert(eastl::is_same_v<const volatile int, eastl::make_signed<const volatile unsigned int>::type>); + static_assert(eastl::is_same_v<const volatile long, eastl::make_signed<const volatile unsigned long>::type>); + static_assert(eastl::is_same_v<const volatile long long, eastl::make_signed<const volatile unsigned long long>::type>); + + static_assert(eastl::is_same_v<unsigned char, eastl::make_unsigned<signed char>::type>); + static_assert(eastl::is_same_v<unsigned short, eastl::make_unsigned<short>::type>); + static_assert(eastl::is_same_v<unsigned int, eastl::make_unsigned<int>::type>); + static_assert(eastl::is_same_v<unsigned long, eastl::make_unsigned<long>::type>); + static_assert(eastl::is_same_v<unsigned long long, eastl::make_unsigned<long long>::type>); + + static_assert(eastl::is_same_v<const unsigned char, eastl::make_unsigned<const signed char>::type>); + static_assert(eastl::is_same_v<const unsigned short, eastl::make_unsigned<const short>::type>); + static_assert(eastl::is_same_v<const unsigned int, eastl::make_unsigned<const int>::type>); + static_assert(eastl::is_same_v<const unsigned long, eastl::make_unsigned<const long>::type>); + static_assert(eastl::is_same_v<const unsigned long long, eastl::make_unsigned<const long long>::type>); + + static_assert(eastl::is_same_v<volatile unsigned char, eastl::make_unsigned<volatile signed char>::type>); + static_assert(eastl::is_same_v<volatile unsigned short, eastl::make_unsigned<volatile short>::type>); + static_assert(eastl::is_same_v<volatile unsigned int, eastl::make_unsigned<volatile int>::type>); + static_assert(eastl::is_same_v<volatile unsigned long, eastl::make_unsigned<volatile long>::type>); + static_assert(eastl::is_same_v<volatile unsigned long long, eastl::make_unsigned<volatile long long>::type>); + + static_assert(eastl::is_same_v<const volatile unsigned char, eastl::make_unsigned<const volatile signed char>::type>); + static_assert(eastl::is_same_v<const volatile unsigned short, eastl::make_unsigned<const volatile short>::type>); + static_assert(eastl::is_same_v<const volatile unsigned int, eastl::make_unsigned<const volatile int>::type>); + static_assert(eastl::is_same_v<const volatile unsigned long, eastl::make_unsigned<const volatile long>::type>); + static_assert(eastl::is_same_v<const volatile unsigned long long, eastl::make_unsigned<const volatile long long>::type>); + + static_assert(eastl::is_same_v<signed char, eastl::make_signed<signed char>::type>); + static_assert(eastl::is_same_v<short, eastl::make_signed<signed short>::type>); + static_assert(eastl::is_same_v<int, eastl::make_signed<signed int>::type>); + static_assert(eastl::is_same_v<long, eastl::make_signed<signed long>::type>); + static_assert(eastl::is_same_v<long long, eastl::make_signed<signed long long>::type>); + + static_assert(eastl::is_same_v<unsigned char, eastl::make_unsigned<unsigned char>::type>); + static_assert(eastl::is_same_v<unsigned short, eastl::make_unsigned<unsigned short>::type>); + static_assert(eastl::is_same_v<unsigned int, eastl::make_unsigned<unsigned int>::type>); + 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_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>); + + static_assert(eastl::is_same_v<__int128_t, eastl::make_signed<__int128_t>::type>); + static_assert(eastl::is_same_v<__int128_t, eastl::make_signed<__uint128_t>::type>); + #endif + + // 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 {}; + enum EnumUIntSize : unsigned int {}; + enum EnumULongSize : unsigned long {}; + enum EnumULongLongSize : unsigned long long {}; + + static_assert(eastl::is_signed_v<eastl::make_signed<EnumUCharSize>::type>); + static_assert(eastl::is_signed_v<eastl::make_signed<EnumUShortSize>::type>); + static_assert(eastl::is_signed_v<eastl::make_signed<EnumUIntSize>::type>); + static_assert(eastl::is_signed_v<eastl::make_signed<EnumULongSize>::type>); + static_assert(eastl::is_signed_v<eastl::make_signed<EnumULongLongSize>::type>); + static_assert(sizeof(EnumUCharSize) == sizeof(eastl::make_signed<EnumUCharSize>::type)); + static_assert(sizeof(EnumUShortSize) == sizeof(eastl::make_signed<EnumUShortSize>::type)); + static_assert(sizeof(EnumUIntSize) == sizeof(eastl::make_signed<EnumUIntSize>::type)); + static_assert(sizeof(EnumULongSize) == sizeof(eastl::make_signed<EnumULongSize>::type)); + static_assert(sizeof(EnumULongLongSize) == sizeof(eastl::make_signed<EnumULongLongSize>::type)); + + enum EnumCharSize : signed char {}; + enum EnumShortSize : short {}; + enum EnumIntSize : int {}; + enum EnumLongSize : long {}; + enum EnumLongLongSize : long long {}; + + static_assert(eastl::is_unsigned_v<eastl::make_unsigned<EnumCharSize>::type>); + static_assert(eastl::is_unsigned_v<eastl::make_unsigned<EnumShortSize>::type>); + static_assert(eastl::is_unsigned_v<eastl::make_unsigned<EnumIntSize>::type>); + static_assert(eastl::is_unsigned_v<eastl::make_unsigned<EnumLongSize>::type>); + static_assert(eastl::is_unsigned_v<eastl::make_unsigned<EnumLongLongSize>::type>); + static_assert(sizeof(EnumCharSize) == sizeof(eastl::make_unsigned<EnumCharSize>::type)); + static_assert(sizeof(EnumShortSize) == sizeof(eastl::make_unsigned<EnumShortSize>::type)); + static_assert(sizeof(EnumIntSize) == sizeof(eastl::make_unsigned<EnumIntSize>::type)); + static_assert(sizeof(EnumLongSize) == sizeof(eastl::make_unsigned<EnumLongSize>::type)); + static_assert(sizeof(EnumLongLongSize) == sizeof(eastl::make_unsigned<EnumLongLongSize>::type)); } // remove_const @@ -1685,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. @@ -1698,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 { @@ -1831,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 { @@ -2012,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"); @@ -2027,12 +2409,31 @@ int TestTypeTraits() } #endif - return nErrorCount; -} - + // is_complete_type + { + struct Foo + { + int x; + }; + struct FooEmpty + { + }; + struct Bar; + void FooFunc(); + static_assert(eastl::internal::is_complete_type_v<Foo>, "is_complete_type failure"); + static_assert(eastl::internal::is_complete_type_v<FooEmpty>, "is_complete_type failure"); + static_assert(!eastl::internal::is_complete_type_v<Bar>, "is_complete_type failure"); + static_assert(!eastl::internal::is_complete_type_v<void>, "is_complete_type failure"); + static_assert(!eastl::internal::is_complete_type_v<volatile void>, "is_complete_type failure"); + static_assert(!eastl::internal::is_complete_type_v<const void>, "is_complete_type failure"); + static_assert(!eastl::internal::is_complete_type_v<const volatile void>, "is_complete_type failure"); + static_assert(eastl::internal::is_complete_type_v<decltype(FooFunc)>, "is_complete_type failure"); + } + return nErrorCount; +} 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 8c71eda..2a78a89 100644 --- a/EASTL/test/source/TestVariant.cpp +++ b/EASTL/test/source/TestVariant.cpp @@ -7,12 +7,34 @@ #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" #include <EASTL/variant.h> +#if EASTL_EXCEPTIONS_ENABLED + +// Intentionally Non-Trivial. +// There are optimizations we can make in variant if the types are trivial that we don't currently do but can do. +template <typename T> +struct valueless_struct +{ + valueless_struct() {} + + valueless_struct(const valueless_struct&) {} + + ~valueless_struct() {} + + struct exception_tag {}; + + operator T() const { throw exception_tag{}; } +}; + +#endif + + int TestVariantAlternative() { using namespace eastl; @@ -270,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 } @@ -356,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 @@ -542,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); } @@ -555,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; @@ -625,32 +647,178 @@ int TestVariantInplaceCtors() return nErrorCount; } +// Many Compilers are smart and will fully inline the visitor in our unittests, +// Thereby not actually testing the recursive call. +EA_NO_INLINE int TestVariantVisitNoInline(const eastl::variant<int, bool, unsigned>& v) +{ + int nErrorCount = 0; + + bool bVisited = false; -int TestVariantVisitor() + struct MyVisitor + { + MyVisitor() = delete; + MyVisitor(bool& visited) : mVisited(visited) {}; + + void operator()(int) { mVisited = true; } + void operator()(bool) { mVisited = true; } + void operator()(unsigned) { mVisited = true; } + + bool& mVisited; + }; + + eastl::visit(MyVisitor(bVisited), v); + + EATEST_VERIFY(bVisited); + + return nErrorCount; +} + +EA_NO_INLINE int TestVariantVisit2NoInline(const eastl::variant<int, bool>& v0, const eastl::variant<int, bool>& v1) +{ + int nErrorCount = 0; + + bool bVisited = false; + + struct MyVisitor + { + MyVisitor() = delete; + MyVisitor(bool& visited) : mVisited(visited) {}; + + void operator()(int, int) { mVisited = true; } + void operator()(bool, int) { mVisited = true; } + void operator()(int, bool) { mVisited = true; } + void operator()(bool, bool) { mVisited = true; } + + bool& mVisited; + }; + + eastl::visit(MyVisitor(bVisited), v0, v1); + + EATEST_VERIFY(bVisited); + + return nErrorCount; +} + +EA_NO_INLINE int TestVariantVisit3tNoInline(const eastl::variant<int, bool>& v0, const eastl::variant<int, bool>& v1, const eastl::variant<int, bool>& v2) +{ + int nErrorCount = 0; + + bool bVisited = false; + + struct MyVisitor + { + MyVisitor() = delete; + MyVisitor(bool& visited) : mVisited(visited) {}; + + void operator()(int, int, int) { mVisited = true; } + void operator()(bool, int, int) { mVisited = true; } + void operator()(int, bool, int) { mVisited = true; } + void operator()(bool, bool, int) { mVisited = true; } + + void operator()(int, int, bool) { mVisited = true; } + void operator()(bool, int, bool) { mVisited = true; } + void operator()(int, bool, bool) { mVisited = true; } + void operator()(bool, bool, bool) { mVisited = true; } + + bool& mVisited; + }; + + eastl::visit(MyVisitor(bVisited), v0, v1, v2); + + EATEST_VERIFY(bVisited); + + return nErrorCount; +} + +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 + ); + } - // 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)); + } - // { - // v_t arr[] = {42, "rob", 42.0, 42L}; + { + double visitedValue = 0.0f; + + eastl::visit( + overloaded{ + [](int) { }, + [](string) { }, + [&](double d) { visitedValue = d; }, + [](long) { }}, + v + ); + + VERIFY(visitedValue == 42.0f); + } - // int count = 0; - // for (auto& e : arr) - // { - // eastl::visit(overloaded{[&](int) { count++; }, - // [&](string) { count++; }, - // [&](double) { count++; }, - // [&](long) { count++; }}, e); - // } - // } + #endif + + { + int count = 0; + + for (auto& e : arr) + { + eastl::visit( + eastl::make_overloaded( + [&](int) { count++; }, + [&](string) { count++; }, + [&](double) { count++; }, + [&](long) { count++; }), + e + ); + } + + VERIFY(count == EAArrayCount(arr)); + } + + { + double visitedValue = 0.0f; + + eastl::visit( + eastl::make_overloaded( + [](int) { }, + [](string) { }, + [&](double d) { visitedValue = d; }, + [](long) { }), + v + ); + + 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}; @@ -662,6 +830,14 @@ int TestVariantVisitor() } VERIFY(count == EAArrayCount(arr)); + + count = 0; + for (auto& e : arr) + { + eastl::visit<void>([&](auto){ count++; }, e); + } + + VERIFY(count == EAArrayCount(arr)); } { @@ -671,14 +847,171 @@ int TestVariantVisitor() struct MyVisitor { - MyVisitor& operator()(int) { bVisited = true; return *this; }; - MyVisitor& operator()(long) { return *this; }; - MyVisitor& operator()(string) { return *this; }; - MyVisitor& operator()(unsigned) { return *this; }; // not in variant + void operator()(int) { bVisited = true; }; + void operator()(long) { }; + void operator()(string) { }; + void operator()(unsigned) { }; // not in variant }; visit(MyVisitor{}, v); VERIFY(bVisited); + + bVisited = false; + + visit<void>(MyVisitor{}, v); + VERIFY(bVisited); + } + + { + static bool bVisited = false; + + variant<int, bool, unsigned> v = (int)1; + + struct MyVisitor + { + bool& operator()(int) { return bVisited; } + bool& operator()(bool) { return bVisited; } + bool& operator()(unsigned) { return bVisited; } + }; + + bool& ret = visit(MyVisitor{}, v); + ret = true; + VERIFY(bVisited); + + bVisited = false; + bool& ret2 = visit<bool&>(MyVisitor{}, v); + ret2 = true; + VERIFY(bVisited); + } + + { + variant<int, bool, unsigned> v = (int)1; + + struct MyVisitor + { + void operator()(int& i) { i = 2; } + void operator()(bool&) {} + void operator()(unsigned&) {} + }; + + visit(MyVisitor{}, v); + EATEST_VERIFY(get<0>(v) == (int)2); + + v = (int)1; + visit<void>(MyVisitor{}, v); + EATEST_VERIFY(get<0>(v) == (int)2); + } + + { + static bool bVisited = false; + + variant<int, bool, unsigned> v =(int)1; + + struct MyVisitor + { + void operator()(const int&) { bVisited = true; } + void operator()(const bool&) {} + void operator()(const unsigned&) {} + }; + + visit(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + + bVisited = false; + visit<void>(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + } + + { + static bool bVisited = false; + + const variant<int, bool, unsigned> v =(int)1; + + struct MyVisitor + { + void operator()(const int&) { bVisited = true; } + void operator()(const bool&) {} + void operator()(const unsigned&) {} + }; + + visit(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + + bVisited = false; + visit<void>(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + } + + { + static bool bVisited = false; + + struct MyVisitor + { + void operator()(int&&) { bVisited = true; } + void operator()(bool&&) {} + void operator()(unsigned&&) {} + }; + + visit(MyVisitor{}, variant<int, bool, unsigned>{(int)1}); + EATEST_VERIFY(bVisited); + + visit<void>(MyVisitor{}, variant<int, bool, unsigned>{(int)1}); + EATEST_VERIFY(bVisited); + } + + { + static bool bVisited = false; + + variant<int, bool, unsigned> v = (int)1; + + struct MyVisitor + { + bool&& operator()(int) { return eastl::move(bVisited); } + bool&& operator()(bool) { return eastl::move(bVisited); } + bool&& operator()(unsigned) { return eastl::move(bVisited); } + }; + + bool&& ret = visit(MyVisitor{}, v); + ret = true; + VERIFY(bVisited); + + bVisited = false; + bool&& ret2 = visit<bool&&>(MyVisitor{}, v); + ret2 = true; + VERIFY(bVisited); + } + + { + variant<int, bool, unsigned> v = (int)1; + + TestVariantVisitNoInline(v); + v = (bool)true; + TestVariantVisitNoInline(v); + v = (int)3; + TestVariantVisitNoInline(v); + } + + { + variant<int, bool> v0 = (int)1; + variant<int, bool> v1 = (bool)true; + + TestVariantVisit2NoInline(v0, v1); + v0 = (bool)false; + TestVariantVisit2NoInline(v0, v1); + v1 = (int)2; + TestVariantVisit2NoInline(v0, v1); + } + + { + variant<int, bool> v0 = (int)1; + variant<int, bool> v1 = (int)2; + variant<int, bool> v2 = (int)3; + + TestVariantVisit3tNoInline(v0, v1, v2); + v2 = (bool)false; + TestVariantVisit3tNoInline(v0, v1, v2); + v0 = (bool)true; + TestVariantVisit3tNoInline(v0, v1, v2); } { @@ -695,11 +1028,43 @@ int TestVariantVisitor() MultipleVisitor& operator()(string, string) { return *this; } }; - visit(MultipleVisitor{}, i, s); + MultipleVisitor& ret = visit(MultipleVisitor{}, i, s); + EA_UNUSED(ret); + VERIFY(bVisited); + + MultipleVisitor& ret2 = visit<MultipleVisitor&>(MultipleVisitor{}, i, s); + EA_UNUSED(ret2); VERIFY(bVisited); } { + bool bVisited = false; + + variant<int, bool> v0 = 0; + variant<int, bool> v1 = 1; + + struct MultipleVisitor + { + MultipleVisitor() = delete; + MultipleVisitor(bool& visited) : mVisited(visited) {}; + + void operator()(int, int) { mVisited = true; } + void operator()(int, bool) {} + void operator()(bool, int) {} + void operator()(bool, bool) {} + + bool& mVisited; + }; + + visit(MultipleVisitor(bVisited), v0, v1); + EATEST_VERIFY(bVisited); + + bVisited = false; + visit<void>(MultipleVisitor(bVisited), v0, v1); + EATEST_VERIFY(bVisited); + } + + { variant<int, string> v = 42; struct ModifyingVisitor @@ -724,24 +1089,128 @@ int TestVariantVisitor() VERIFY(visit(ReturningVisitor{}, v) == 42); } -#if !defined(EA_COMPILER_MSVC) + return nErrorCount; +} + +int TestVariantVisitorReturn() +{ + int nErrorCount = 0; + + { + static bool bVisited = false; + + eastl::variant<int, bool> v = (int)1; + + struct MyVisitor + { + bool operator()(int) { bVisited = true; return true; } + bool operator()(bool) { return false; } + }; + + eastl::visit<void>(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + } + { - variant<int, string> v = 42; + static bool bVisited = false; - struct ReturningDifferentTypesVisitor + eastl::variant<int, bool> v = (int)1; + + struct MyVisitor { - int operator()(int i) {return i;} - size_t operator()(string s) {return s.size();} + bool operator()(int) { bVisited = true; return true; } + bool operator()(bool) { return false; } }; - VERIFY(visit(ReturningDifferentTypesVisitor{}, v) == 42); + eastl::visit<const void>(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + } + + { + static bool bVisited = false; + + eastl::variant<int, bool> v = (int)1; + + struct MyVisitor + { + bool operator()(int) { bVisited = true; return true; } + bool operator()(bool) { return false; } + }; + + eastl::visit<volatile void>(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + } + + { + static bool bVisited = false; + + eastl::variant<int, bool> v = (int)1; + + struct MyVisitor + { + bool operator()(int) { bVisited = true; return true; } + bool operator()(bool) { return false; } + }; + + eastl::visit<const volatile void>(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + } + + { + static bool bVisited = false; + + eastl::variant<int, bool> v = (int)1; + + struct MyVisitor + { + bool operator()(int) { bVisited = true; return true; } + bool operator()(bool) { return false; } + }; + + int ret = eastl::visit<int>(MyVisitor{}, v); + EATEST_VERIFY(bVisited); + EATEST_VERIFY(ret); + } + + { + static bool bVisited = false; + + struct A {}; + struct B : public A {}; + struct C : public A {}; + + eastl::variant<int, bool> v = (int)1; + + struct MyVisitor + { + B operator()(int) { bVisited = true; return B{}; } + C operator()(bool) { return C{}; } + }; + + A ret = eastl::visit<A>(MyVisitor{}, v); + EA_UNUSED(ret); + EATEST_VERIFY(bVisited); + } + + { + static bool bVisited = false; + + eastl::variant<int, bool> v = (int)1; + + struct MyVisitor + { + MyVisitor operator()(int) { bVisited = true; return MyVisitor{}; } + MyVisitor operator()(bool) { return MyVisitor{}; } + }; + + MyVisitor ret = eastl::visit<MyVisitor>(MyVisitor{}, v); + EA_UNUSED(ret); + EATEST_VERIFY(bVisited); } -#endif return nErrorCount; } - int TestVariantAssignment() { using namespace eastl; @@ -825,6 +1294,328 @@ int TestVariantUserRegressionCopyMoveAssignmentOperatorLeak() return nErrorCount; } +int TestVariantRelationalOperators() +{ + int nErrorCount = 0; + + using VariantNoThrow = eastl::variant<int, bool, float>; + + // Equality + { + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ true }; + + EATEST_VERIFY((v1 == v2) == false); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 == v2) == true); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)0 }; + + EATEST_VERIFY((v1 == v2) == false); + } + } + + // Inequality + { + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ true }; + + EATEST_VERIFY((v1 != v2) == true); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 != v2) == false); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)0 }; + + EATEST_VERIFY((v1 != v2) == true); + } + } + + // Less Than + { + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ true }; + + EATEST_VERIFY((v1 < v2) == true); + } + + { + VariantNoThrow v1{ true }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 < v2) == false); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 < v2) == false); + } + + { + VariantNoThrow v1{ (int)0 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 < v2) == true); + } + } + + // Greater Than + { + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ true }; + + EATEST_VERIFY((v1 > v2) == false); + } + + { + VariantNoThrow v1{ true }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 > v2) == true); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 > v2) == false); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)0 }; + + EATEST_VERIFY((v1 > v2) == true); + } + } + + // Less Equal + { + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ true }; + + EATEST_VERIFY((v1 <= v2) == true); + } + + { + VariantNoThrow v1{ true }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 <= v2) == false); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 <= v2) == true); + } + + { + VariantNoThrow v1{ (int)0 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 <= v2) == true); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)0 }; + + EATEST_VERIFY((v1 <= v2) == false); + } + } + + // Greater Equal + { + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ true }; + + EATEST_VERIFY((v1 >= v2) == false); + } + + { + VariantNoThrow v1{ true }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 >= v2) == true); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 >= v2) == true); + } + + { + VariantNoThrow v1{ (int)0 }; + VariantNoThrow v2{ (int)1 }; + + EATEST_VERIFY((v1 >= v2) == false); + } + + { + VariantNoThrow v1{ (int)1 }; + VariantNoThrow v2{ (int)0 }; + + EATEST_VERIFY((v1 >= v2) == true); + } + } + +#if EASTL_EXCEPTIONS_ENABLED + + using VariantThrow = eastl::variant<int, bool, float>; + + auto make_variant_valueless = [](VariantThrow& v) + { + try + { + v.emplace<0>(valueless_struct<int>{}); + } + catch(const typename valueless_struct<int>::exception_tag &) + { + } + }; + + // Equality + { + { + VariantThrow v0{ (int)0 }; + VariantThrow v1{ (int)1 }; + + make_variant_valueless(v0); + make_variant_valueless(v1); + + EATEST_VERIFY((v0 == v1) == true); + } + } + + // Inequality + { + { + VariantThrow v0{ (int)0 }; + VariantThrow v1{ (int)1 }; + + make_variant_valueless(v0); + make_variant_valueless(v1); + + EATEST_VERIFY((v0 != v1) == false); + } + } + + // Less Than + { + { + VariantThrow v0{ (int)0 }; + VariantThrow v1{ (int)1 }; + + make_variant_valueless(v0); + + EATEST_VERIFY((v0 < v1) == true); + } + + { + VariantThrow v0{ (int)0 }; + VariantThrow v1{ (int)1 }; + + make_variant_valueless(v1); + + EATEST_VERIFY((v0 < v1) == false); + } + } + + // Greater Than + { + { + VariantThrow v0{ (int)1 }; + VariantThrow v1{ (int)0 }; + + make_variant_valueless(v0); + + EATEST_VERIFY((v0 > v1) == false); + } + + { + VariantThrow v0{ (int)1 }; + VariantThrow v1{ (int)0 }; + + make_variant_valueless(v1); + + EATEST_VERIFY((v0 > v1) == true); + } + } + + // Less Equal + { + { + VariantThrow v0{ (int)1 }; + VariantThrow v1{ (int)1 }; + + make_variant_valueless(v0); + + EATEST_VERIFY((v0 <= v1) == true); + } + + { + VariantThrow v0{ (int)1 }; + VariantThrow v1{ (int)0 }; + + make_variant_valueless(v1); + + EATEST_VERIFY((v0 <= v1) == false); + } + } + + // Greater Equal + { + { + VariantThrow v0{ (int)1 }; + VariantThrow v1{ (int)1 }; + + make_variant_valueless(v0); + + EATEST_VERIFY((v0 >= v1) == false); + } + + { + VariantThrow v0{ (int)1 }; + VariantThrow v1{ (int)0 }; + + make_variant_valueless(v1); + + EATEST_VERIFY((v0 >= v1) == true); + } + } + +#endif + + return nErrorCount; +} + int TestVariantUserRegressionIncompleteType() { @@ -848,6 +1639,155 @@ int TestVariantUserRegressionIncompleteType() return nErrorCount; } +#define EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(Type, VarName) \ + bool operator==(const Type & rhs) const { return VarName == rhs.VarName; } \ + bool operator!=(const Type & rhs) const { return VarName != rhs.VarName; } \ + bool operator<(const Type & rhs) const { return VarName < rhs.VarName; } \ + bool operator>(const Type & rhs) const { return VarName > rhs.VarName; } \ + bool operator<=(const Type & rhs) const { return VarName <= rhs.VarName; } \ + bool operator>=(const Type & rhs) const { return VarName >= rhs.VarName; } + +int TestBigVariantComparison() +{ + int nErrorCount = 0; + + struct A; + struct B; + struct C; + struct D; + struct E; + struct F; + struct G; + struct H; + struct I; + struct J; + struct K; + struct L; + struct M; + struct N; + struct O; + struct P; + struct Q; + struct R; + struct S; + struct T; + struct U; + struct V; + struct W; + struct X; + struct Y; + struct Z; + + using BigVariant = eastl::variant<A, B, C, D, E, F, G, H, I, J, K, L, M, N, + O, P, Q, R, S, T, U, V, W, X, Y, Z>; + + struct A { int a; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(A, a) }; + struct B { int b; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(B, b) }; + struct C { int c; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(C, c) }; + struct D { int d; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(D, d) }; + struct E { int e; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(E, e) }; + struct F { int f; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(F, f) }; + struct G { int g; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(G, g) }; + struct H { int h; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(H, h) }; + struct I { int i; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(I, i) }; + struct J { int j; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(J, j) }; + struct K { int k; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(K, k) }; + struct L { int l; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(L, l) }; + struct M { int m; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(M, m) }; + struct N { int n; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(N, n) }; + struct O { int o; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(O, o) }; + struct P { int p; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(P, p) }; + struct Q { int q; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(Q, q) }; + struct R { int r; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(R, r) }; + struct S { int s; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(S, s) }; + struct T { int t; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(T, t) }; + struct U { int u; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(U, u) }; + struct V { int v; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(V, v) }; + struct W { int w; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(W, w) }; + struct X { int x; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(X, x) }; + struct Y { int y; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(Y, y) }; + struct Z { int z; EASTL_TEST_BIG_VARIANT_RELATIONAL_OPS(Z, z) }; + + { + BigVariant v0{ A{0} }; + BigVariant v1{ A{1} }; + + VERIFY(v0 != v1); + } + + { + BigVariant v0{ A{0} }; + BigVariant v1{ A{1} }; + + VERIFY(v0 < v1); + } + + { + BigVariant v0{ A{0} }; + BigVariant v1{ A{0} }; + + VERIFY(v0 == v1); + } + + { + BigVariant v0{ A{1} }; + BigVariant v1{ A{0} }; + + VERIFY(v0 > v1); + } + + { + BigVariant v0{ A{0} }; + BigVariant v1{ A{1} }; + + VERIFY(v0 <= v1); + } + + { + BigVariant v0{ A{0} }; + BigVariant v1{ A{0} }; + + VERIFY(v0 <= v1); + } + + { + BigVariant v0{ A{0} }; + BigVariant v1{ A{0} }; + + VERIFY(v0 >= v1); + } + + { + BigVariant v0{ A{1} }; + BigVariant v1{ A{0} }; + + VERIFY(v0 >= v1); + } + + { + BigVariant v0{ A{0} }; + BigVariant v1{ B{0} }; + + VERIFY(v0 != v1); + } + + { + BigVariant v0{ A{0} }; + BigVariant v1{ B{0} }; + + VERIFY(v0 < v1); + } + + { + BigVariant v0{ A{0} }; + BigVariant v1{ B{0} }; + + VERIFY(v1 > v0); + } + + return nErrorCount; +} + int TestVariantGeneratingComparisonOverloads(); int TestVariant() @@ -866,25 +1806,18 @@ int TestVariant() nErrorCount += TestVariantEmplace(); nErrorCount += TestVariantRelOps(); nErrorCount += TestVariantInplaceCtors(); + nErrorCount += TestVariantVisitorOverloaded(); nErrorCount += TestVariantVisitor(); nErrorCount += TestVariantAssignment(); nErrorCount += TestVariantMoveOnly(); nErrorCount += TestVariantUserRegressionCopyMoveAssignmentOperatorLeak(); nErrorCount += TestVariantUserRegressionIncompleteType(); nErrorCount += TestVariantGeneratingComparisonOverloads(); + nErrorCount += TestBigVariantComparison(); + nErrorCount += TestVariantRelationalOperators(); return nErrorCount; } #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 962d2c4..132bab1 100644 --- a/EASTL/test/source/main.cpp +++ b/EASTL/test/source/main.cpp @@ -145,6 +145,7 @@ int EAMain(int argc, char* argv[]) testSuite.AddTest("VectorSet", TestVectorSet); testSuite.AddTest("AtomicBasic", TestAtomicBasic); testSuite.AddTest("AtomicAsm", TestAtomicAsm); + testSuite.AddTest("Bitcast", TestBitcast); nErrorCount += testSuite.Run(); diff --git a/examples/dpp-example.c b/examples/dpp-example.c index cc32009..2b9f26f 100644 --- a/examples/dpp-example.c +++ b/examples/dpp-example.c @@ -18,6 +18,27 @@ extern NTSTATUS NTAPI WrapperZwQuerySystemInformation(_In_ int SystemInformation _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength); +extern NTSTATUS NTAPI WrapperZwCreateFile(_Out_ PHANDLE FileHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK StatusBlock, + _In_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_ PVOID EaBuffer, + _In_ ULONG EaLength); +extern NTSTATUS NTAPI WrapperZwClose(_In_ HANDLE Handle); +extern NTSTATUS NTAPI WrapperZwWriteFile(_In_ HANDLE FileHandle, + _In_ HANDLE Event, + _In_ PIO_APC_ROUTINE ApcRoutine, + _In_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK StatusBlock, + _In_ PVOID Buffer, + _In_ ULONG Length, + _In_ PLARGE_INTEGER ByteOffset, + _In_ PULONG Key); int example_exception_handler(_In_ EXCEPTION_POINTERS * lpEP) { @@ -59,6 +80,40 @@ static void zw_test() } } +static NTSTATUS WriteToFile() +{ + UNICODE_STRING fileName = RTL_CONSTANT_STRING(L"\\??\\C:\\dpp-example-text.log"); + OBJECT_ATTRIBUTES objAttr; + IO_STATUS_BLOCK ioStatusBlock; + HANDLE fileHandle; + NTSTATUS status; + + InitializeObjectAttributes(&objAttr, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + status = WrapperZwCreateFile(&fileHandle, + GENERIC_WRITE, + &objAttr, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OVERWRITE_IF, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + + if (!NT_SUCCESS(status)) + { + return status; + } + + CHAR data[] = "Test data from the kernel driver\n"; + status = WrapperZwWriteFile(fileHandle, NULL, NULL, NULL, &ioStatusBlock, data, sizeof(data) - 1, NULL, NULL); + + WrapperZwClose(fileHandle); + return status; +} + NTSTATUS DriverEntry(struct _DRIVER_OBJECT * DriverObject, PUNICODE_STRING RegistryPath) { (void)DriverObject; @@ -86,6 +141,9 @@ NTSTATUS DriverEntry(struct _DRIVER_OBJECT * DriverObject, PUNICODE_STRING Regis _enable(); DbgPrint("%s\n", "Done with Disable/Enable Interrupts!"); + DbgPrint("%s\n", "WriteToFile"); + WriteToFile(); + return STATUS_SUCCESS; } diff --git a/update_EASTL.sh b/update_EASTL.sh index 010ffc8..4ec7972 100755 --- a/update_EASTL.sh +++ b/update_EASTL.sh @@ -3,5 +3,6 @@ MYDIR="$(dirname ${0})" cd "${MYDIR}" -git subtree pull --squash --prefix=EASTL 'https://github.com/electronicarts/EASTL' master -git subtree pull --squash --prefix=EASTL/test/packages/EABase 'https://github.com/electronicarts/EABase' master +# EASTL version 3.20.02 +git subtree pull --squash --prefix=EASTL 'https://github.com/electronicarts/EASTL' 1aa784643081404783ce6494eb2fcaba99d8f6a5 +git subtree pull --squash --prefix=EASTL/test/packages/EABase 'https://github.com/electronicarts/EABase' af4ae274795f4c40829428a2c4058c6f512530f4 |