Compare commits
124 Commits
43c59399d1
...
0.1.0-expe
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cc31f660c | |||
| 2d79e18b25 | |||
| 1d5a61f308 | |||
| 38e1c3f8b7 | |||
| 8d3fa1ac98 | |||
| 88e330336a | |||
| eca0d90d98 | |||
| d8adf47d10 | |||
| 8a834a9c05 | |||
| 35f0ba71ab | |||
| db7a40cb30 | |||
| 6a70f0c501 | |||
| 5629e31d49 | |||
| c16da1af53 | |||
| 88aba14620 | |||
| 23963e0389 | |||
| 0c6f33762a | |||
| 9024957ff2 | |||
| 68cd2c310a | |||
| 594acf219a | |||
| e498b39d3d | |||
| 9dd5e74788 | |||
| c596882c32 | |||
| 0e7ee5cde2 | |||
| 39d12bd7e4 | |||
| 319d1cc8cb | |||
| fc87332845 | |||
| 22952237df | |||
| f817afe6df | |||
| a3cebf03ec | |||
| 262ce1bb67 | |||
| bf22397123 | |||
| 2de1068ad8 | |||
| aa572542e2 | |||
| db855d27a1 | |||
| 04bb4be901 | |||
| a68a6d16b6 | |||
| a92422e8b2 | |||
| 343007ffd6 | |||
| 8c228fbf52 | |||
| a14fbd90db | |||
| 1e6beb83f2 | |||
| 7fb116c577 | |||
| 566052c8c2 | |||
| a0250ebaf8 | |||
| f88cf7ef3e | |||
| e6d525f2c3 | |||
| dd3a366c14 | |||
| 85199119b3 | |||
| 78d4955e03 | |||
| 8f36410346 | |||
| 87e48e5ca1 | |||
| 93b3a17f06 | |||
| 6ea768781d | |||
| dff6050a3b | |||
| ae964ebd0a | |||
| 8be89d0bbc | |||
| 80238de712 | |||
| f54386d102 | |||
| 6a37e91639 | |||
| 0a37460f24 | |||
| aa8cd7ed33 | |||
| 4845520225 | |||
| 68f0d19cac | |||
| 50b1d2bb29 | |||
| 00be872d5c | |||
| 312cfe4097 | |||
| d2b6e0c669 | |||
| d88eb4be5e | |||
| dca4eaa6eb | |||
| 8eab5e6538 | |||
| 589347118b | |||
| a3e0aa01cf | |||
| a3509295ff | |||
| cd7adbfd46 | |||
| a55f03fea0 | |||
| c73fc7620d | |||
| 5cfde63dd4 | |||
| 24dd4347d1 | |||
| ea625bb916 | |||
| 28367fe633 | |||
| d9f05d4241 | |||
| 5131fd6eae | |||
| ba9d35c1ff | |||
| 8113d3b39b | |||
| 89dc5b715e | |||
| e3e127752e | |||
| 8d02b0e0a9 | |||
| 7525c9a5dd | |||
| 9d901df68a | |||
| 2d06667c40 | |||
| fa29c4a063 | |||
| 66cfbfa3b3 | |||
| d0287dd5e8 | |||
| 123800bbbd | |||
| 3dc20f25cf | |||
| c96995ea92 | |||
| bd68754903 | |||
| f5c47fe677 | |||
| 84a41387ae | |||
| edc6c1924a | |||
| 5a5d34e908 | |||
| f60bd0e3e4 | |||
| 987299f9b5 | |||
| bcc39fdf55 | |||
| 54c795b9a2 | |||
| 1c01ce5bb6 | |||
| a952c31546 | |||
| 7107fc6b8a | |||
| 2d4dedd876 | |||
| edda2b83a1 | |||
| 19a37ea072 | |||
| 6230ec9df6 | |||
| f6f696d5a7 | |||
| 1c0c93ee25 | |||
| 646d0cb7a4 | |||
| 3859070d53 | |||
| bc93455d0e | |||
| fe60fc33e0 | |||
| 49feb0b12b | |||
| f8ef1da107 | |||
| 09bbcecc28 | |||
| 7a80a80a12 | |||
| 90c2a37a2c |
@@ -39,6 +39,21 @@ else ()
|
||||
add_compile_definitions ("BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||
endif ()
|
||||
|
||||
# Define compiler macros
|
||||
if (CMAKE_C_COMPILER_ID MATCHES "MSVC" AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
add_compile_definitions ("PLATFORM_COMPILER_MSVC=1")
|
||||
add_compile_definitions ("PLATFORM_COMPILER_NAME=MSVC")
|
||||
elseif (CMAKE_C_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
add_compile_definitions ("PLATFORM_COMPILER_GCC=1")
|
||||
add_compile_definitions ("PLATFORM_COMPILER_NAME=GCC")
|
||||
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_definitions ("PLATFORM_COMPILER_CLANG=1")
|
||||
add_compile_definitions ("PLATFORM_COMPILER_NAME=Clang")
|
||||
else ()
|
||||
add_compile_definitions ("PLATFORM_COMPILER_UNKNOWN=1")
|
||||
add_compile_definitions ("PLATFORM_COMPILER_NAME=${CMAKE_C_COMPILER_ID}/${CMAKE_CXX_COMPILER_ID}")
|
||||
endif ()
|
||||
|
||||
# Add subproject
|
||||
file (GLOB PROJECT_FOLDERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*")
|
||||
foreach (PROJECT_SUBDIRECTORY ${PROJECT_FOLDERS})
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
|
||||
};
|
||||
|
||||
FMemoryLeakChecker MemoryLeakChecker;
|
||||
FMemoryLeakChecker GMemoryLeakChecker;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -80,7 +80,7 @@ void* Malloc(size_t Count, size_t Alignment)
|
||||
|
||||
check(Result != nullptr);
|
||||
|
||||
check_code({ MemoryLeakChecker.AddMemoryAllocationCount(); });
|
||||
check_code({ GMemoryLeakChecker.AddMemoryAllocationCount(); });
|
||||
|
||||
return Result;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ void Free(void* Ptr)
|
||||
}
|
||||
# endif
|
||||
|
||||
check_code({ MemoryLeakChecker.ReleaseMemoryAllocationCount(); });
|
||||
check_code({ GMemoryLeakChecker.ReleaseMemoryAllocationCount(); });
|
||||
}
|
||||
|
||||
size_t QuantizeSize(size_t Count, size_t Alignment)
|
||||
|
||||
462
Redcraft.Utility/Source/Private/Miscellaneous/Console.cpp
Normal file
462
Redcraft.Utility/Source/Private/Miscellaneous/Console.cpp
Normal file
@@ -0,0 +1,462 @@
|
||||
#include "Miscellaneous/Console.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
# undef TEXT
|
||||
# include <windows.h>
|
||||
#elif PLATFORM_LINUX
|
||||
# include <sys/ioctl.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
NODISCARD bool InitANSIConsole()
|
||||
{
|
||||
static bool bResult = []
|
||||
{
|
||||
HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
DWORD ConsoleMode = 0;
|
||||
|
||||
if (!GetConsoleMode(Console, &ConsoleMode)) return false;
|
||||
|
||||
ConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
if (!SetConsoleMode(Console, ConsoleMode)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
();
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
EColor GForegroundColor = EColor::Default;
|
||||
EColor GBackgroundColor = EColor::Default;
|
||||
|
||||
EColor GetForegroundColor()
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
if (InitANSIConsole()) return GForegroundColor;
|
||||
|
||||
const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return EColor::Default;
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return EColor::Default;
|
||||
|
||||
const WORD Color = ConsoleInfo.wAttributes;
|
||||
|
||||
EColor Result = EColor::Black;
|
||||
|
||||
if (Color & FOREGROUND_RED) Result |= EColor::Red;
|
||||
if (Color & FOREGROUND_GREEN) Result |= EColor::Green;
|
||||
if (Color & FOREGROUND_BLUE) Result |= EColor::Blue;
|
||||
if (Color & FOREGROUND_INTENSITY) Result |= EColor::Intensity;
|
||||
|
||||
return Result;
|
||||
}
|
||||
# endif
|
||||
|
||||
return GForegroundColor;
|
||||
}
|
||||
|
||||
EColor GetBackgroundColor()
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
if (InitANSIConsole()) return GBackgroundColor;
|
||||
|
||||
const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return EColor::Default;
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return EColor::Default;
|
||||
|
||||
const WORD Color = ConsoleInfo.wAttributes;
|
||||
|
||||
EColor Result = EColor::Black;
|
||||
|
||||
if (Color & BACKGROUND_RED) Result |= EColor::Red;
|
||||
if (Color & BACKGROUND_GREEN) Result |= EColor::Green;
|
||||
if (Color & BACKGROUND_BLUE) Result |= EColor::Blue;
|
||||
if (Color & BACKGROUND_INTENSITY) Result |= EColor::Intensity;
|
||||
|
||||
return Result;
|
||||
}
|
||||
# endif
|
||||
|
||||
return GBackgroundColor;
|
||||
}
|
||||
|
||||
EColor SetForegroundColor(EColor InColor)
|
||||
{
|
||||
if (IsOutputRedirected()) return GetForegroundColor();
|
||||
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
if (!InitANSIConsole())
|
||||
{
|
||||
const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return GetForegroundColor();
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return GetForegroundColor();
|
||||
|
||||
WORD Color = ConsoleInfo.wAttributes & ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
|
||||
if (InColor == EColor::Default) InColor = EColor::White;
|
||||
|
||||
if (!!(InColor & EColor::Red)) Color |= FOREGROUND_RED;
|
||||
if (!!(InColor & EColor::Green)) Color |= FOREGROUND_GREEN;
|
||||
if (!!(InColor & EColor::Blue)) Color |= FOREGROUND_BLUE;
|
||||
if (!!(InColor & EColor::Intensity)) Color |= FOREGROUND_INTENSITY;
|
||||
|
||||
if (!SetConsoleTextAttribute(Console, Color)) return GetForegroundColor();
|
||||
|
||||
return InColor;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
# if PLATFORM_WINDOWS || PLATFORM_LINUX
|
||||
{
|
||||
int Result;
|
||||
|
||||
switch (InColor)
|
||||
{
|
||||
case EColor::Black: Result = std::fputs("\033[30m", stdout); break;
|
||||
case EColor::Red: Result = std::fputs("\033[31m", stdout); break;
|
||||
case EColor::Green: Result = std::fputs("\033[32m", stdout); break;
|
||||
case EColor::Yellow: Result = std::fputs("\033[33m", stdout); break;
|
||||
case EColor::Blue: Result = std::fputs("\033[34m", stdout); break;
|
||||
case EColor::Magenta: Result = std::fputs("\033[35m", stdout); break;
|
||||
case EColor::Cyan: Result = std::fputs("\033[36m", stdout); break;
|
||||
case EColor::White: Result = std::fputs("\033[37m", stdout); break;
|
||||
case EColor::BrightBlack: Result = std::fputs("\033[90m", stdout); break;
|
||||
case EColor::BrightRed: Result = std::fputs("\033[91m", stdout); break;
|
||||
case EColor::BrightGreen: Result = std::fputs("\033[92m", stdout); break;
|
||||
case EColor::BrightYellow: Result = std::fputs("\033[93m", stdout); break;
|
||||
case EColor::BrightBlue: Result = std::fputs("\033[94m", stdout); break;
|
||||
case EColor::BrightMagenta: Result = std::fputs("\033[95m", stdout); break;
|
||||
case EColor::BrightCyan: Result = std::fputs("\033[96m", stdout); break;
|
||||
case EColor::BrightWhite: Result = std::fputs("\033[97m", stdout); break;
|
||||
default: Result = std::fputs("\033[39m", stdout); break;
|
||||
}
|
||||
|
||||
if (Result == EOF) return GetForegroundColor();
|
||||
|
||||
return GForegroundColor = InColor;
|
||||
}
|
||||
# endif
|
||||
|
||||
return GetForegroundColor();
|
||||
}
|
||||
|
||||
EColor SetBackgroundColor(EColor InColor)
|
||||
{
|
||||
if (IsOutputRedirected()) return GetBackgroundColor();
|
||||
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
if (!InitANSIConsole())
|
||||
{
|
||||
const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return GetBackgroundColor();
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return GetBackgroundColor();
|
||||
|
||||
WORD Color = ConsoleInfo.wAttributes & ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
|
||||
|
||||
if (InColor == EColor::Default) InColor = EColor::Black;
|
||||
|
||||
if (!!(InColor & EColor::Red)) Color |= BACKGROUND_RED;
|
||||
if (!!(InColor & EColor::Green)) Color |= BACKGROUND_GREEN;
|
||||
if (!!(InColor & EColor::Blue)) Color |= BACKGROUND_BLUE;
|
||||
if (!!(InColor & EColor::Intensity)) Color |= BACKGROUND_INTENSITY;
|
||||
|
||||
if (!SetConsoleTextAttribute(Console, Color)) return GetBackgroundColor();
|
||||
|
||||
return InColor;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
# if PLATFORM_WINDOWS || PLATFORM_LINUX
|
||||
{
|
||||
int Result;
|
||||
|
||||
switch (InColor)
|
||||
{
|
||||
case EColor::Black: Result = std::fputs("\033[40m", stdout); break;
|
||||
case EColor::Red: Result = std::fputs("\033[41m", stdout); break;
|
||||
case EColor::Green: Result = std::fputs("\033[42m", stdout); break;
|
||||
case EColor::Yellow: Result = std::fputs("\033[43m", stdout); break;
|
||||
case EColor::Blue: Result = std::fputs("\033[44m", stdout); break;
|
||||
case EColor::Magenta: Result = std::fputs("\033[45m", stdout); break;
|
||||
case EColor::Cyan: Result = std::fputs("\033[46m", stdout); break;
|
||||
case EColor::White: Result = std::fputs("\033[47m", stdout); break;
|
||||
case EColor::BrightBlack: Result = std::fputs("\033[100m", stdout); break;
|
||||
case EColor::BrightRed: Result = std::fputs("\033[101m", stdout); break;
|
||||
case EColor::BrightGreen: Result = std::fputs("\033[102m", stdout); break;
|
||||
case EColor::BrightYellow: Result = std::fputs("\033[103m", stdout); break;
|
||||
case EColor::BrightBlue: Result = std::fputs("\033[104m", stdout); break;
|
||||
case EColor::BrightMagenta: Result = std::fputs("\033[105m", stdout); break;
|
||||
case EColor::BrightCyan: Result = std::fputs("\033[106m", stdout); break;
|
||||
case EColor::BrightWhite: Result = std::fputs("\033[107m", stdout); break;
|
||||
default: Result = std::fputs("\033[49m", stdout); break;
|
||||
}
|
||||
|
||||
if (Result == EOF) return GetBackgroundColor();
|
||||
|
||||
return GBackgroundColor = InColor;
|
||||
}
|
||||
# endif
|
||||
|
||||
return GetBackgroundColor();
|
||||
}
|
||||
|
||||
uint GetWindowWidth()
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return static_cast<uint>(-1);
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return static_cast<uint>(-1);
|
||||
|
||||
return static_cast<uint>(ConsoleInfo.srWindow.Right - ConsoleInfo.srWindow.Left + 1);
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
winsize Size;
|
||||
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &Size) == -1) return static_cast<uint>(-1);
|
||||
|
||||
return static_cast<uint>(Size.ws_col);
|
||||
}
|
||||
# endif
|
||||
|
||||
return static_cast<uint>(-1);
|
||||
}
|
||||
|
||||
uint GetWindowHeight()
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return static_cast<uint>(-1);
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return static_cast<uint>(-1);
|
||||
|
||||
return static_cast<uint>(ConsoleInfo.srWindow.Bottom - ConsoleInfo.srWindow.Top + 1);
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
winsize Size;
|
||||
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &Size) == -1) return static_cast<uint>(-1);
|
||||
|
||||
return static_cast<uint>(Size.ws_row);
|
||||
}
|
||||
# endif
|
||||
|
||||
return static_cast<uint>(-1);
|
||||
}
|
||||
|
||||
bool IsInputRedirected()
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
const HANDLE StandardInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
if (StandardInput == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
DWORD FileType = GetFileType(StandardInput);
|
||||
|
||||
return FileType != FILE_TYPE_CHAR;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
return isatty(fileno(stdin)) == 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsOutputRedirected()
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
const HANDLE StandardOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (StandardOutput == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
DWORD FileType = GetFileType(StandardOutput);
|
||||
|
||||
return FileType != FILE_TYPE_CHAR;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
return isatty(fileno(stdout)) == 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsErrorRedirected()
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
const HANDLE StandardError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
if (StandardError == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
DWORD FileType = GetFileType(StandardError);
|
||||
|
||||
return FileType != FILE_TYPE_CHAR;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
return isatty(fileno(stderr)) == 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if (IsOutputRedirected()) return;
|
||||
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
std::system("cls");
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
Ignore = std::fputs("\033[2J\033[1;1H", stdout);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
char Input(bool bEcho)
|
||||
{
|
||||
if (bEcho || IsOutputRedirected())
|
||||
{
|
||||
const int Result = std::getchar();
|
||||
|
||||
if (Result == EOF) return static_cast<char>(-1);
|
||||
|
||||
return static_cast<char>(Result);
|
||||
}
|
||||
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
const HANDLE Console = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return static_cast<char>(-1);
|
||||
|
||||
DWORD ConsoleMode = 0;
|
||||
|
||||
GetConsoleMode(Console, &ConsoleMode);
|
||||
|
||||
SetConsoleMode(Console, ConsoleMode & ~ENABLE_ECHO_INPUT);
|
||||
|
||||
const char Result = Input();
|
||||
|
||||
SetConsoleMode(Console, ConsoleMode);
|
||||
|
||||
return Result;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{ }
|
||||
# endif
|
||||
|
||||
return static_cast<char>(-1);
|
||||
}
|
||||
|
||||
FString InputLn(bool bEcho)
|
||||
{
|
||||
if (bEcho || IsOutputRedirected())
|
||||
{
|
||||
FString Result;
|
||||
|
||||
while (true)
|
||||
{
|
||||
const int Char = std::getchar();
|
||||
|
||||
if (Char == EOF || Char == '\n') break;
|
||||
|
||||
Result.PushBack(static_cast<char>(Char));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
const HANDLE Console = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
if (Console == INVALID_HANDLE_VALUE) return "";
|
||||
|
||||
DWORD ConsoleMode = 0;
|
||||
|
||||
GetConsoleMode(Console, &ConsoleMode);
|
||||
|
||||
SetConsoleMode(Console, ConsoleMode & ~ENABLE_ECHO_INPUT);
|
||||
|
||||
const FString Result = InputLn();
|
||||
|
||||
SetConsoleMode(Console, ConsoleMode);
|
||||
|
||||
return Result;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{ }
|
||||
# endif
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool Print(char Char)
|
||||
{
|
||||
return std::putchar(Char) != EOF;
|
||||
}
|
||||
|
||||
bool Error(char Char)
|
||||
{
|
||||
return std::fputc(Char, stderr) != EOF;
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
596
Redcraft.Utility/Source/Private/Miscellaneous/FileSystem.cpp
Normal file
596
Redcraft.Utility/Source/Private/Miscellaneous/FileSystem.cpp
Normal file
@@ -0,0 +1,596 @@
|
||||
#include <Miscellaneous/FileSystem.h>
|
||||
|
||||
#include "Numerics/Bit.h"
|
||||
#include "Numerics/Math.h"
|
||||
#include "Templates/ScopeHelper.h"
|
||||
#include "Containers/StaticArray.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
# undef TEXT
|
||||
# include <windows.h>
|
||||
# undef CreateDirectory
|
||||
#elif PLATFORM_LINUX
|
||||
# include <unistd.h>
|
||||
# include <dirent.h>
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4996)
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(FileSystem)
|
||||
|
||||
bool LoadFileToArray(TArray<uint8>& Result, FStringView Path)
|
||||
{
|
||||
if (!FileSystem::Exists(Path)) return false;
|
||||
|
||||
FILE* File = std::fopen(*Path, "rb");
|
||||
|
||||
if (File == nullptr) return false;
|
||||
|
||||
auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); });
|
||||
|
||||
if (std::fseek(File, 0, SEEK_END) != 0) return false;
|
||||
|
||||
const long Length = std::ftell(File);
|
||||
|
||||
if (!Math::IsWithin(Length, 0, TNumericLimits<long>::Max())) return false;
|
||||
|
||||
if (std::fseek(File, 0, SEEK_SET) != 0) return false;
|
||||
|
||||
Result.SetNum(Length);
|
||||
|
||||
if (std::fread(Result.GetData(), sizeof(uint8), Length, File) != static_cast<size_t>(Length)) return false;
|
||||
|
||||
FileGuard.Release();
|
||||
|
||||
if (std::fclose(File) != 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveArrayToFile(TArrayView<const uint8> Data, FStringView Path)
|
||||
{
|
||||
FILE* File = std::fopen(*Path, "wb");
|
||||
|
||||
if (File == nullptr) return false;
|
||||
|
||||
auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); });
|
||||
|
||||
if (std::fwrite(Data.GetData(), sizeof(uint8), Data.Num(), File) != Data.Num()) return false;
|
||||
|
||||
FileGuard.Release();
|
||||
|
||||
if (std::fclose(File) != 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <CCharType T>
|
||||
bool LoadFileToString(TString<T>& Result, FStringView Path, FileSystem::EEncoding Encoding /* = FileSystem::EEncoding::Default */, bool bVerify /* = false */)
|
||||
{
|
||||
if (!FileSystem::Exists(Path)) return false;
|
||||
|
||||
FILE* File = std::fopen(*Path, "rb");
|
||||
|
||||
if (File == nullptr) return false;
|
||||
|
||||
auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); });
|
||||
|
||||
if (std::fseek(File, 0, SEEK_END) != 0) return false;
|
||||
|
||||
long Length = std::ftell(File);
|
||||
|
||||
if (!Math::IsWithin(Length, 0, TNumericLimits<long>::Max())) return false;
|
||||
|
||||
if (std::fseek(File, 0, SEEK_SET) != 0) return false;
|
||||
|
||||
TStaticArray<uint8, 4> Buffer = { 0xAA, 0xAA, 0xAA, 0xAA };
|
||||
|
||||
Ignore = std::fread(Buffer.GetData(), sizeof(uint8), Buffer.Num(), File);
|
||||
|
||||
// Auto-detect the encoding if it is not specified.
|
||||
if (Encoding == FileSystem::EEncoding::Default)
|
||||
{
|
||||
// Check if the file is a UTF-32 encoded file.
|
||||
if (Buffer[0] == 0x00 && Buffer[1] == 0x00 && Buffer[2] == 0xFE && Buffer[3] == 0xFF) Encoding = FileSystem::EEncoding::UTF32BE;
|
||||
else if (Buffer[0] == 0xFF && Buffer[1] == 0xFE && Buffer[2] == 0x00 && Buffer[3] == 0x00) Encoding = FileSystem::EEncoding::UTF32LE;
|
||||
|
||||
// Check if the file is a UTF-16 encoded file.
|
||||
else if (Buffer[0] == 0xFF && Buffer[1] == 0xFE) Encoding = FileSystem::EEncoding::UTF16LE;
|
||||
else if (Buffer[0] == 0xFE && Buffer[1] == 0xFF) Encoding = FileSystem::EEncoding::UTF16BE;
|
||||
|
||||
// Check if the file is a UTF-8 encoded file.
|
||||
else if (Buffer[0] == 0xEF && Buffer[1] == 0xBB && Buffer[2] == 0xBF) Encoding = FileSystem::EEncoding::UTF8;
|
||||
|
||||
// Check if the file is a wide character encoded file.
|
||||
else if (Buffer[0] == 0x00 || Buffer[1] == 0x00 || Buffer[2] == 0x00 || Buffer[3] == 0x00) Encoding = FileSystem::EEncoding::Wide;
|
||||
|
||||
// Check if the file is a narrow character encoded file.
|
||||
else Encoding = FileSystem::EEncoding::Narrow;
|
||||
}
|
||||
|
||||
// Jump to the BOM character if the file is a UTF-8, UTF-16 or UTF-32 encoded file.
|
||||
switch (Encoding)
|
||||
{
|
||||
case FileSystem::EEncoding::Narrow:
|
||||
case FileSystem::EEncoding::Wide: { Length -= 0; if (std::fseek(File, 0, SEEK_SET) != 0) return false; } break;
|
||||
case FileSystem::EEncoding::UTF8: if (Buffer[0] == 0xEF && Buffer[1] == 0xBB && Buffer[2] == 0xBF) { Length -= 3; if (std::fseek(File, 3, SEEK_SET) != 0) return false; } break;
|
||||
case FileSystem::EEncoding::UTF16BE: if (Buffer[0] == 0xFE && Buffer[1] == 0xFF) { Length -= 2; if (std::fseek(File, 2, SEEK_SET) != 0) return false; } break;
|
||||
case FileSystem::EEncoding::UTF16LE: if (Buffer[0] == 0xFF && Buffer[1] == 0xFE) { Length -= 2; if (std::fseek(File, 2, SEEK_SET) != 0) return false; } break;
|
||||
case FileSystem::EEncoding::UTF32BE: if (Buffer[0] == 0x00 && Buffer[1] == 0x00 && Buffer[2] == 0xFE && Buffer[3] == 0xFF) { Length -= 4; if (std::fseek(File, 4, SEEK_SET) != 0) return false; } break;
|
||||
case FileSystem::EEncoding::UTF32LE: if (Buffer[0] == 0xFF && Buffer[1] == 0xFE && Buffer[2] == 0x00 && Buffer[3] == 0x00) { Length -= 4; if (std::fseek(File, 4, SEEK_SET) != 0) return false; } break;
|
||||
default: check_no_entry();
|
||||
}
|
||||
|
||||
check(Math::EEndian::Native == Math::EEndian::Big || Math::EEndian::Native == Math::EEndian::Little);
|
||||
|
||||
const bool bByteSwap =
|
||||
Math::EEndian::Native == Math::EEndian::Big ? Encoding == FileSystem::EEncoding::UTF16LE || Encoding == FileSystem::EEncoding::UTF32LE :
|
||||
Math::EEndian::Native == Math::EEndian::Little ? Encoding == FileSystem::EEncoding::UTF16BE || Encoding == FileSystem::EEncoding::UTF32BE : false;
|
||||
|
||||
const auto LoadImpl = [File, Length, bByteSwap]<typename U>(TString<U>& String) -> bool
|
||||
{
|
||||
if (Length % sizeof(U) != 0) return false;
|
||||
|
||||
String.Reserve(Length / sizeof(U));
|
||||
|
||||
while (true)
|
||||
{
|
||||
U Char;
|
||||
|
||||
const size_t ReadNum = std::fread(&Char, 1, sizeof(U), File);
|
||||
|
||||
if (ReadNum == 0) break;
|
||||
|
||||
if (ReadNum != sizeof(U)) return false;
|
||||
|
||||
if (bByteSwap) Char = Math::ByteSwap(static_cast<TMakeUnsigned<U>>(Char));
|
||||
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
if (!String.IsEmpty() && String.Back() == LITERAL(U, '\r') && Char == LITERAL(U, '\n'))
|
||||
{
|
||||
String.PopBack();
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
String.PushBack(Char);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
bool bCompatible = false;
|
||||
|
||||
if constexpr (CSameAs<T, char>) bCompatible |= Encoding == FileSystem::EEncoding::Narrow;
|
||||
else if constexpr (CSameAs<T, wchar>) bCompatible |= Encoding == FileSystem::EEncoding::Wide;
|
||||
else if constexpr (CSameAs<T, u8char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF8;
|
||||
else if constexpr (CSameAs<T, u16char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF16BE || Encoding == FileSystem::EEncoding::UTF16LE;
|
||||
else if constexpr (CSameAs<T, u32char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF32BE || Encoding == FileSystem::EEncoding::UTF32LE;
|
||||
|
||||
else static_assert(sizeof(T) == -1, "Unsupported character type");
|
||||
|
||||
if (!bCompatible || bVerify)
|
||||
{
|
||||
switch (Encoding)
|
||||
{
|
||||
case FileSystem::EEncoding::Narrow: { FString Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; }
|
||||
case FileSystem::EEncoding::Wide: { FWString Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; }
|
||||
case FileSystem::EEncoding::UTF8: { FU8String Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; }
|
||||
case FileSystem::EEncoding::UTF16BE:
|
||||
case FileSystem::EEncoding::UTF16LE: { FU16String Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; }
|
||||
case FileSystem::EEncoding::UTF32BE:
|
||||
case FileSystem::EEncoding::UTF32LE: { FU32String Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; }
|
||||
default: check_no_entry();
|
||||
}
|
||||
}
|
||||
|
||||
else if (!LoadImpl(Result)) return false;
|
||||
|
||||
FileGuard.Release();
|
||||
|
||||
if (std::fclose(File) != 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template REDCRAFTUTILITY_API bool LoadFileToString<char> (FString&, FStringView, FileSystem::EEncoding, bool);
|
||||
template REDCRAFTUTILITY_API bool LoadFileToString<wchar> (FWString&, FStringView, FileSystem::EEncoding, bool);
|
||||
template REDCRAFTUTILITY_API bool LoadFileToString<u8char> (FU8String&, FStringView, FileSystem::EEncoding, bool);
|
||||
template REDCRAFTUTILITY_API bool LoadFileToString<u16char>(FU16String&, FStringView, FileSystem::EEncoding, bool);
|
||||
template REDCRAFTUTILITY_API bool LoadFileToString<u32char>(FU32String&, FStringView, FileSystem::EEncoding, bool);
|
||||
|
||||
template <CCharType T>
|
||||
bool SaveStringToFile(TStringView<T> String, FStringView Path, FileSystem::EEncoding Encoding /* = FileSystem::EEncoding::Default */, bool bWithBOM /* = true */)
|
||||
{
|
||||
bool bCompatible = Encoding == FileSystem::EEncoding::Default;
|
||||
|
||||
if constexpr (CSameAs<T, char>) bCompatible |= Encoding == FileSystem::EEncoding::Narrow;
|
||||
else if constexpr (CSameAs<T, wchar>) bCompatible |= Encoding == FileSystem::EEncoding::Wide;
|
||||
else if constexpr (CSameAs<T, u8char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF8;
|
||||
else if constexpr (CSameAs<T, u16char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF16BE || Encoding == FileSystem::EEncoding::UTF16LE;
|
||||
else if constexpr (CSameAs<T, u32char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF32BE || Encoding == FileSystem::EEncoding::UTF32LE;
|
||||
|
||||
else static_assert(sizeof(T) == -1, "Unsupported character type");
|
||||
|
||||
if (bCompatible)
|
||||
{
|
||||
FILE* File = std::fopen(*Path, "wb");
|
||||
|
||||
if (File == nullptr) return false;
|
||||
|
||||
auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); });
|
||||
|
||||
if (bWithBOM)
|
||||
{
|
||||
if constexpr (CSameAs<T, u8char>)
|
||||
{
|
||||
if (std::fwrite(U8TEXT("\uFEFF"), 1, 3, File) != 3) return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<T, u16char>)
|
||||
{
|
||||
constexpr TStaticArray<uint8, 2> BufferBE = { 0xFE, 0xFF };
|
||||
constexpr TStaticArray<uint8, 2> BufferLE = { 0xFF, 0xFE };
|
||||
|
||||
if (Encoding == FileSystem::EEncoding::UTF16BE) { if (std::fwrite(BufferBE.GetData(), 1, BufferBE.Num(), File) != BufferBE.Num()) return false; }
|
||||
else if (Encoding == FileSystem::EEncoding::UTF16LE) { if (std::fwrite(BufferLE.GetData(), 1, BufferLE.Num(), File) != BufferLE.Num()) return false; }
|
||||
|
||||
else if (std::fwrite(U16TEXT("\uFEFF"), 1, sizeof(T), File) != sizeof(T)) return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<T, u32char>)
|
||||
{
|
||||
constexpr TStaticArray<uint8, 4> BufferBE = { 0x00, 0x00, 0xFE, 0xFF };
|
||||
constexpr TStaticArray<uint8, 4> BufferLE = { 0xFF, 0xFE, 0x00, 0x00 };
|
||||
|
||||
if (Encoding == FileSystem::EEncoding::UTF32BE) { if (std::fwrite(BufferBE.GetData() , 1, BufferBE.Num(), File) != BufferBE.Num()) return false; }
|
||||
else if (Encoding == FileSystem::EEncoding::UTF32LE) { if (std::fwrite(BufferLE.GetData() , 1, BufferLE.Num(), File) != BufferLE.Num()) return false; }
|
||||
|
||||
else if (std::fwrite(U32TEXT("\uFEFF"), 1, sizeof(T), File) != sizeof(T)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
check(Math::EEndian::Native == Math::EEndian::Big || Math::EEndian::Native == Math::EEndian::Little);
|
||||
|
||||
const bool bByteSwap =
|
||||
Math::EEndian::Native == Math::EEndian::Big ? Encoding == FileSystem::EEncoding::UTF16LE || Encoding == FileSystem::EEncoding::UTF32LE :
|
||||
Math::EEndian::Native == Math::EEndian::Little ? Encoding == FileSystem::EEncoding::UTF16BE || Encoding == FileSystem::EEncoding::UTF32BE : false;
|
||||
|
||||
for (T Char : String)
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
if (Char == LITERAL(T, '\n'))
|
||||
{
|
||||
T Return = LITERAL(T, '\r');
|
||||
|
||||
if (bByteSwap) Return = Math::ByteSwap(static_cast<TMakeUnsigned<T>>(Return));
|
||||
|
||||
if (std::fwrite(&Return, 1, sizeof(T), File) != sizeof(T)) return false;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
if (bByteSwap) Char = Math::ByteSwap(static_cast<TMakeUnsigned<T>>(Char));
|
||||
|
||||
if (std::fwrite(&Char, 1, sizeof(T), File) != sizeof(T)) return false;
|
||||
}
|
||||
|
||||
FileGuard.Release();
|
||||
|
||||
if (std::fclose(File) != 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FString PathWithNull;
|
||||
|
||||
PathWithNull.Reserve(Path.Num() + 1);
|
||||
|
||||
PathWithNull += Path;
|
||||
PathWithNull += '\0';
|
||||
|
||||
switch (Encoding)
|
||||
{
|
||||
case FileSystem::EEncoding::Narrow: { FString Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::Narrow, bWithBOM)) return false; break; }
|
||||
case FileSystem::EEncoding::Wide: { FWString Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::Wide, bWithBOM)) return false; break; }
|
||||
case FileSystem::EEncoding::UTF8: { FU8String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF8, bWithBOM)) return false; break; }
|
||||
case FileSystem::EEncoding::UTF16BE: { FU16String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF16BE, bWithBOM)) return false; break; }
|
||||
case FileSystem::EEncoding::UTF16LE: { FU16String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF16LE, bWithBOM)) return false; break; }
|
||||
case FileSystem::EEncoding::UTF32BE: { FU32String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF32BE, bWithBOM)) return false; break; }
|
||||
case FileSystem::EEncoding::UTF32LE: { FU32String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF32LE, bWithBOM)) return false; break; }
|
||||
default: check_no_entry(); return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template REDCRAFTUTILITY_API bool SaveStringToFile<char> (FStringView, FStringView, FileSystem::EEncoding, bool);
|
||||
template REDCRAFTUTILITY_API bool SaveStringToFile<wchar> (FWStringView, FStringView, FileSystem::EEncoding, bool);
|
||||
template REDCRAFTUTILITY_API bool SaveStringToFile<u8char> (FU8StringView, FStringView, FileSystem::EEncoding, bool);
|
||||
template REDCRAFTUTILITY_API bool SaveStringToFile<u16char>(FU16StringView, FStringView, FileSystem::EEncoding, bool);
|
||||
template REDCRAFTUTILITY_API bool SaveStringToFile<u32char>(FU32StringView, FStringView, FileSystem::EEncoding, bool);
|
||||
|
||||
size_t FileSize(FStringView Path)
|
||||
{
|
||||
if (!FileSystem::Exists(Path)) return static_cast<size_t>(-1);
|
||||
|
||||
FILE* File = std::fopen(*Path, "rb");
|
||||
|
||||
if (File == nullptr) return static_cast<size_t>(-1);
|
||||
|
||||
auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); });
|
||||
|
||||
if (std::fseek(File, 0, SEEK_END) != 0) return static_cast<size_t>(-1);
|
||||
|
||||
const long Length = std::ftell(File);
|
||||
|
||||
if (!Math::IsWithin(Length, 0, TNumericLimits<long>::Max())) return static_cast<size_t>(-1);
|
||||
|
||||
FileGuard.Release();
|
||||
|
||||
if (std::fclose(File) != 0) return static_cast<size_t>(-1);
|
||||
|
||||
return Length;
|
||||
}
|
||||
|
||||
bool Delete(FStringView Path)
|
||||
{
|
||||
return std::remove(*Path) == 0;
|
||||
}
|
||||
|
||||
bool Exists(FStringView Path)
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
DWORD Attributes = GetFileAttributesA(*Path);
|
||||
|
||||
if (Attributes == INVALID_FILE_ATTRIBUTES) return false;
|
||||
|
||||
return !(Attributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
struct stat FileInfo;
|
||||
|
||||
FileInfo.st_size = -1;
|
||||
|
||||
if (stat(*Path, &FileInfo) != 0) return false;
|
||||
|
||||
if (!S_ISREG(FileInfo.st_mode)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Copy(FStringView Destination, FStringView Source)
|
||||
{
|
||||
if (!FileSystem::Exists(Source)) return false;
|
||||
|
||||
FILE* FileA = std::fopen(*Source, "rb");
|
||||
|
||||
if (FileA == nullptr) return false;
|
||||
|
||||
auto FileGuardA = TScopeCallback([=] { Ignore = std::fclose(FileA); });
|
||||
|
||||
FILE* FileB = std::fopen(*Destination, "wb");
|
||||
|
||||
if (FileB == nullptr) return false;
|
||||
|
||||
auto FileGuardB = TScopeCallback([=] { Ignore = std::fclose(FileB); });
|
||||
|
||||
size_t ReadSize;
|
||||
|
||||
constexpr size_t BufferSize = 4096;
|
||||
|
||||
TStaticArray<uint8, BufferSize> Buffer;
|
||||
|
||||
do
|
||||
{
|
||||
ReadSize = std::fread(Buffer.GetData(), 1, BufferSize, FileA);
|
||||
|
||||
if (std::fwrite(Buffer.GetData(), 1, ReadSize, FileB) != ReadSize) return false;
|
||||
}
|
||||
while (ReadSize == BufferSize);
|
||||
|
||||
FileGuardA.Release();
|
||||
|
||||
if (std::fclose(FileA) != 0) return false;
|
||||
|
||||
FileGuardB.Release();
|
||||
|
||||
if (std::fclose(FileB) != 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rename(FStringView Destination, FStringView Source)
|
||||
{
|
||||
return std::rename(*Source, *Destination) == 0;
|
||||
}
|
||||
|
||||
bool CreateDirectory(FStringView Path, bool bRecursive /* = false */)
|
||||
{
|
||||
if (Path.Num() == 0) return false;
|
||||
|
||||
if (bRecursive)
|
||||
{
|
||||
if (Path.Back() == '/' || Path.Back() == '\\') Path = Path.First(Path.Num() - 1);
|
||||
|
||||
FStringView Parent = Path.First(Path.FindLastOf("/\\"));
|
||||
|
||||
if (!FileSystem::ExistsDirectory(Parent) && !FileSystem::CreateDirectory(Parent, true)) return false;
|
||||
}
|
||||
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
return CreateDirectoryA(*Path, nullptr) != 0;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
return mkdir(*Path, 0755) == 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeleteDirectory(FStringView Path, bool bRecursive /* = false */)
|
||||
{
|
||||
if (bRecursive)
|
||||
{
|
||||
FString Temp;
|
||||
|
||||
bool bSuccessfully = FileSystem::IterateDirectory(Path, [&](FStringView File, bool bIsDirectory) -> bool
|
||||
{
|
||||
Temp.Reset(false);
|
||||
|
||||
Temp += Path;
|
||||
Temp += '/';
|
||||
Temp += File;
|
||||
Temp += '\0';
|
||||
|
||||
if (bIsDirectory)
|
||||
{
|
||||
if (!FileSystem::DeleteDirectory(Temp, true)) return false;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (!FileSystem::Delete(Temp)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!bSuccessfully) return false;
|
||||
}
|
||||
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
return RemoveDirectoryA(*Path) != 0;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
return rmdir(*Path) == 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExistsDirectory(FStringView Path)
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
DWORD Attributes = GetFileAttributesA(*Path);
|
||||
|
||||
if (Attributes == INVALID_FILE_ATTRIBUTES) return false;
|
||||
|
||||
return Attributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
DIR* Directory = opendir(*Path);
|
||||
|
||||
if (Directory == nullptr) return false;
|
||||
|
||||
Ignore = closedir(Directory);
|
||||
|
||||
return true;
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IterateDirectory(FStringView Path, TFunctionRef<bool(FStringView /* Path */, bool /* bIsDirectory */)> Visitor)
|
||||
{
|
||||
# if PLATFORM_WINDOWS
|
||||
{
|
||||
FString FindPath;
|
||||
|
||||
FindPath.Reserve(Path.Num() + 3);
|
||||
|
||||
FindPath += Path;
|
||||
FindPath += '\\';
|
||||
FindPath += '*';
|
||||
FindPath += '\0';
|
||||
|
||||
WIN32_FIND_DATA FindData;
|
||||
|
||||
HANDLE FindHandle = FindFirstFileA(*FindPath, &FindData);
|
||||
|
||||
auto FindGuard = TScopeCallback([=] { Ignore = FindClose(FindHandle); });
|
||||
|
||||
if (FindHandle == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
do
|
||||
{
|
||||
const FStringView FilePath = FindData.cFileName;
|
||||
|
||||
if (FilePath == "." || FilePath == "..") continue;
|
||||
|
||||
const bool bIsDirectory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
|
||||
if (!Visitor(FilePath, bIsDirectory)) return false;
|
||||
}
|
||||
while (FindNextFileA(FindHandle, &FindData) != 0);
|
||||
|
||||
FindGuard.Release();
|
||||
|
||||
if (!FindClose(FindHandle)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
# elif PLATFORM_LINUX
|
||||
{
|
||||
DIR* Directory = opendir(*Path);
|
||||
|
||||
if (Directory == nullptr) return false;
|
||||
|
||||
auto DirectoryGuard = TScopeCallback([=] { Ignore = closedir(Directory); });
|
||||
|
||||
dirent* Entry;
|
||||
|
||||
while ((Entry = readdir(Directory)) != nullptr)
|
||||
{
|
||||
const FStringView FilePath = Entry->d_name;
|
||||
|
||||
if (FilePath == "." || FilePath == "..") continue;
|
||||
|
||||
const bool bIsDirectory = Entry->d_type == DT_DIR;
|
||||
|
||||
if (!Visitor(FilePath, bIsDirectory)) return false;
|
||||
}
|
||||
|
||||
DirectoryGuard.Release();
|
||||
|
||||
if (closedir(Directory) != 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NAMESPACE_END(FileSystem)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
#pragma warning(pop)
|
||||
51
Redcraft.Utility/Source/Private/Numerics/Random.cpp
Normal file
51
Redcraft.Utility/Source/Private/Numerics/Random.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "Numerics/Random.h"
|
||||
|
||||
#include "Templates/Atomic.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Math)
|
||||
|
||||
NAMESPACE_UNNAMED_BEGIN
|
||||
|
||||
TAtomic<uint32> GRandState = 586103306;
|
||||
|
||||
NAMESPACE_UNNAMED_END
|
||||
|
||||
uint32 Seed(uint32 InSeed)
|
||||
{
|
||||
uint32 OldSeed = GRandState.Load(EMemoryOrder::Relaxed);
|
||||
|
||||
if (InSeed != 0) GRandState.Store(InSeed, EMemoryOrder::Relaxed);
|
||||
|
||||
return OldSeed;
|
||||
}
|
||||
|
||||
uint32 Rand()
|
||||
{
|
||||
uint32 Result;
|
||||
|
||||
GRandState.FetchFn(
|
||||
[&Result](uint32 Value)
|
||||
{
|
||||
Result = Value;
|
||||
|
||||
Result ^= Result << 13;
|
||||
Result ^= Result >> 17;
|
||||
Result ^= Result << 5;
|
||||
|
||||
return Result;
|
||||
},
|
||||
EMemoryOrder::Relaxed
|
||||
);
|
||||
|
||||
return Result % 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
NAMESPACE_END(Math)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
368
Redcraft.Utility/Source/Private/Testing/Algorithms.cpp
Normal file
368
Redcraft.Utility/Source/Private/Testing/Algorithms.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Algorithms/Algorithms.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "Containers/List.h"
|
||||
#include "Ranges/Factory.h"
|
||||
#include "Numerics/Math.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
void TestBasic()
|
||||
{
|
||||
{
|
||||
TArray<int> Arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
auto Iter = Arr.Begin();
|
||||
|
||||
Algorithms::Advance(Iter, 5);
|
||||
|
||||
always_check(*Iter == 5);
|
||||
|
||||
always_check(Algorithms::Distance(Arr.Begin(), Iter) == 5);
|
||||
|
||||
always_check(Algorithms::Distance(Arr) == 10);
|
||||
|
||||
always_check(*Algorithms::Next(Iter) == 6);
|
||||
always_check(*Algorithms::Next(Iter, 2) == 7);
|
||||
always_check(*Algorithms::Prev(Iter) == 4);
|
||||
always_check(*Algorithms::Prev(Iter, 2) == 3);
|
||||
|
||||
always_check(Algorithms::Next(Iter, Arr.End()) == Arr.End());
|
||||
always_check(Algorithms::Next(Iter, 16, Arr.End()) == Arr.End());
|
||||
|
||||
always_check(Algorithms::Prev(Iter, 16, Arr.Begin()) == Arr.Begin());
|
||||
|
||||
Iter = Arr.Begin();
|
||||
|
||||
Algorithms::Advance(Iter, Arr.End());
|
||||
|
||||
always_check(Iter == Arr.End());
|
||||
|
||||
Iter = Arr.Begin();
|
||||
|
||||
always_check(Algorithms::Advance(Iter, 16, Arr.End()) == 6);
|
||||
|
||||
always_check(Iter == Arr.End());
|
||||
}
|
||||
|
||||
{
|
||||
TList<int> Arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
auto Iter = Arr.Begin();
|
||||
|
||||
Algorithms::Advance(Iter, 5);
|
||||
|
||||
always_check(*Iter == 5);
|
||||
|
||||
always_check(Algorithms::Distance(Arr.Begin(), Iter) == 5);
|
||||
|
||||
always_check(Algorithms::Distance(Arr) == 10);
|
||||
|
||||
always_check(*Algorithms::Next(Iter) == 6);
|
||||
always_check(*Algorithms::Next(Iter, 2) == 7);
|
||||
always_check(*Algorithms::Prev(Iter) == 4);
|
||||
always_check(*Algorithms::Prev(Iter, 2) == 3);
|
||||
|
||||
always_check(Algorithms::Next(Iter, Arr.End()) == Arr.End());
|
||||
always_check(Algorithms::Next(Iter, 16, Arr.End()) == Arr.End());
|
||||
|
||||
always_check(Algorithms::Prev(Iter, 16, Arr.Begin()) == Arr.Begin());
|
||||
|
||||
Iter = Arr.Begin();
|
||||
|
||||
Algorithms::Advance(Iter, Arr.End());
|
||||
|
||||
always_check(Iter == Arr.End());
|
||||
|
||||
Iter = Arr.Begin();
|
||||
|
||||
always_check(Algorithms::Advance(Iter, 16, Arr.End()) == 6);
|
||||
|
||||
always_check(Iter == Arr.End());
|
||||
}
|
||||
|
||||
{
|
||||
auto Arr = Ranges::Iota(0, 10);
|
||||
|
||||
auto Iter = Arr.Begin();
|
||||
|
||||
Algorithms::Advance(Iter, 5);
|
||||
|
||||
always_check(*Iter == 5);
|
||||
|
||||
always_check(Algorithms::Distance(Arr.Begin(), Iter) == 5);
|
||||
|
||||
always_check(Algorithms::Distance(Arr) == 10);
|
||||
|
||||
always_check(*Algorithms::Next(Iter) == 6);
|
||||
always_check(*Algorithms::Next(Iter, 2) == 7);
|
||||
|
||||
always_check(Algorithms::Next(Iter, Arr.End()) == Arr.End());
|
||||
always_check(Algorithms::Next(Iter, 16, Arr.End()) == Arr.End());
|
||||
|
||||
Iter = Arr.Begin();
|
||||
|
||||
Algorithms::Advance(Iter, Arr.End());
|
||||
|
||||
always_check(Iter == Arr.End());
|
||||
|
||||
Iter = Arr.Begin();
|
||||
|
||||
always_check(Algorithms::Advance(Iter, 16, Arr.End()) == 6);
|
||||
|
||||
always_check(Iter == Arr.End());
|
||||
}
|
||||
}
|
||||
|
||||
void TestSearch()
|
||||
{
|
||||
{
|
||||
TArray<int> Arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
TList<int> Brr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
auto Crr = Ranges::Iota(0, 10);
|
||||
|
||||
always_check( Algorithms::AllOf(Arr, [](int A) { return A < 10; }));
|
||||
always_check( Algorithms::AllOf(Brr, [](int A) { return A < 10; }));
|
||||
always_check( Algorithms::AllOf(Crr, [](int A) { return A < 10; }));
|
||||
always_check(!Algorithms::AllOf(Arr, [](int A) { return A > 5; }));
|
||||
always_check(!Algorithms::AllOf(Brr, [](int A) { return A > 5; }));
|
||||
always_check(!Algorithms::AllOf(Crr, [](int A) { return A > 5; }));
|
||||
|
||||
always_check( Algorithms::AllOf(Arr.Begin(), Arr.End(), [](int A) { return A < 10; }));
|
||||
always_check( Algorithms::AllOf(Brr.Begin(), Brr.End(), [](int A) { return A < 10; }));
|
||||
always_check( Algorithms::AllOf(Crr.Begin(), Crr.End(), [](int A) { return A < 10; }));
|
||||
always_check(!Algorithms::AllOf(Arr.Begin(), Arr.End(), [](int A) { return A > 5; }));
|
||||
always_check(!Algorithms::AllOf(Brr.Begin(), Brr.End(), [](int A) { return A > 5; }));
|
||||
always_check(!Algorithms::AllOf(Crr.Begin(), Crr.End(), [](int A) { return A > 5; }));
|
||||
|
||||
always_check(Algorithms::AnyOf(Arr, [](int A) { return A < 10; }));
|
||||
always_check(Algorithms::AnyOf(Brr, [](int A) { return A < 10; }));
|
||||
always_check(Algorithms::AnyOf(Crr, [](int A) { return A < 10; }));
|
||||
always_check(Algorithms::AnyOf(Arr, [](int A) { return A > 5; }));
|
||||
always_check(Algorithms::AnyOf(Brr, [](int A) { return A > 5; }));
|
||||
always_check(Algorithms::AnyOf(Crr, [](int A) { return A > 5; }));
|
||||
|
||||
always_check(Algorithms::AnyOf(Arr.Begin(), Arr.End(), [](int A) { return A < 10; }));
|
||||
always_check(Algorithms::AnyOf(Brr.Begin(), Brr.End(), [](int A) { return A < 10; }));
|
||||
always_check(Algorithms::AnyOf(Crr.Begin(), Crr.End(), [](int A) { return A < 10; }));
|
||||
always_check(Algorithms::AnyOf(Arr.Begin(), Arr.End(), [](int A) { return A > 5; }));
|
||||
always_check(Algorithms::AnyOf(Brr.Begin(), Brr.End(), [](int A) { return A > 5; }));
|
||||
always_check(Algorithms::AnyOf(Crr.Begin(), Crr.End(), [](int A) { return A > 5; }));
|
||||
|
||||
always_check(!Algorithms::NoneOf(Arr, [](int A) { return A < 10; }));
|
||||
always_check(!Algorithms::NoneOf(Brr, [](int A) { return A < 10; }));
|
||||
always_check(!Algorithms::NoneOf(Crr, [](int A) { return A < 10; }));
|
||||
always_check(!Algorithms::NoneOf(Arr, [](int A) { return A > 5; }));
|
||||
always_check(!Algorithms::NoneOf(Brr, [](int A) { return A > 5; }));
|
||||
always_check(!Algorithms::NoneOf(Crr, [](int A) { return A > 5; }));
|
||||
|
||||
always_check(!Algorithms::NoneOf(Arr.Begin(), Arr.End(), [](int A) { return A < 10; }));
|
||||
always_check(!Algorithms::NoneOf(Brr.Begin(), Brr.End(), [](int A) { return A < 10; }));
|
||||
always_check(!Algorithms::NoneOf(Crr.Begin(), Crr.End(), [](int A) { return A < 10; }));
|
||||
always_check(!Algorithms::NoneOf(Arr.Begin(), Arr.End(), [](int A) { return A > 5; }));
|
||||
always_check(!Algorithms::NoneOf(Brr.Begin(), Brr.End(), [](int A) { return A > 5; }));
|
||||
always_check(!Algorithms::NoneOf(Crr.Begin(), Crr.End(), [](int A) { return A > 5; }));
|
||||
|
||||
always_check( Algorithms::Contains(Arr, 5));
|
||||
always_check( Algorithms::Contains(Brr, 5));
|
||||
always_check( Algorithms::Contains(Crr, 5));
|
||||
always_check(!Algorithms::Contains(Arr, 10));
|
||||
always_check(!Algorithms::Contains(Brr, 10));
|
||||
always_check(!Algorithms::Contains(Crr, 10));
|
||||
|
||||
always_check( Algorithms::Contains(Arr.Begin(), Arr.End(), 5));
|
||||
always_check( Algorithms::Contains(Brr.Begin(), Brr.End(), 5));
|
||||
always_check( Algorithms::Contains(Crr.Begin(), Crr.End(), 5));
|
||||
always_check(!Algorithms::Contains(Arr.Begin(), Arr.End(), 10));
|
||||
always_check(!Algorithms::Contains(Brr.Begin(), Brr.End(), 10));
|
||||
always_check(!Algorithms::Contains(Crr.Begin(), Crr.End(), 10));
|
||||
|
||||
auto Projection = [](int A) { return A % 4; }; // Project to { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1 }
|
||||
|
||||
always_check(Algorithms::Find(Arr, 2, { }, Projection) == Algorithms::Next(Arr.Begin(), 2));
|
||||
always_check(Algorithms::Find(Brr, 2, { }, Projection) == Algorithms::Next(Brr.Begin(), 2));
|
||||
always_check(Algorithms::Find(Crr, 2, { }, Projection) == Algorithms::Next(Crr.Begin(), 2));
|
||||
|
||||
always_check(Algorithms::Find(Arr.Begin(), Arr.End(), 2, { }, Projection) == Algorithms::Next(Arr.Begin(), 2));
|
||||
always_check(Algorithms::Find(Brr.Begin(), Brr.End(), 2, { }, Projection) == Algorithms::Next(Brr.Begin(), 2));
|
||||
always_check(Algorithms::Find(Crr.Begin(), Crr.End(), 2, { }, Projection) == Algorithms::Next(Crr.Begin(), 2));
|
||||
|
||||
always_check(Algorithms::Find(Arr, 10) == Arr.End());
|
||||
always_check(Algorithms::Find(Brr, 10) == Brr.End());
|
||||
always_check(Algorithms::Find(Crr, 10) == Crr.End());
|
||||
|
||||
always_check(Algorithms::Find(Arr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Arr.Begin()));
|
||||
always_check(Algorithms::Find(Brr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Brr.Begin()));
|
||||
always_check(Algorithms::Find(Crr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Crr.Begin()));
|
||||
|
||||
always_check(Algorithms::Find(Arr, Ranges::Iota(4, 16)).IsEmpty());
|
||||
always_check(Algorithms::Find(Brr, Ranges::Iota(4, 16)).IsEmpty());
|
||||
always_check(Algorithms::Find(Crr, Ranges::Iota(4, 16)).IsEmpty());
|
||||
|
||||
always_check(Algorithms::FindIf(Arr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Arr.Begin(), 2));
|
||||
always_check(Algorithms::FindIf(Brr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Brr.Begin(), 2));
|
||||
always_check(Algorithms::FindIf(Crr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Crr.Begin(), 2));
|
||||
|
||||
always_check(Algorithms::FindIf(Arr.Begin(), Arr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Arr.Begin(), 2));
|
||||
always_check(Algorithms::FindIf(Brr.Begin(), Brr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Brr.Begin(), 2));
|
||||
always_check(Algorithms::FindIf(Crr.Begin(), Crr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Crr.Begin(), 2));
|
||||
|
||||
always_check(Algorithms::FindIf(Arr, [](int A) { return A == 10; }) == Arr.End());
|
||||
always_check(Algorithms::FindIf(Brr, [](int A) { return A == 10; }) == Brr.End());
|
||||
always_check(Algorithms::FindIf(Crr, [](int A) { return A == 10; }) == Crr.End());
|
||||
|
||||
always_check(Algorithms::FindIfNot(Arr, [](int A) { return A > 0; }, Projection) == Arr.Begin());
|
||||
always_check(Algorithms::FindIfNot(Brr, [](int A) { return A > 0; }, Projection) == Brr.Begin());
|
||||
always_check(Algorithms::FindIfNot(Crr, [](int A) { return A > 0; }, Projection) == Crr.Begin());
|
||||
|
||||
always_check(Algorithms::FindIfNot(Arr.Begin(), Arr.End(), [](int A) { return A > 0; }, Projection) == Arr.Begin());
|
||||
always_check(Algorithms::FindIfNot(Brr.Begin(), Brr.End(), [](int A) { return A > 0; }, Projection) == Brr.Begin());
|
||||
always_check(Algorithms::FindIfNot(Crr.Begin(), Crr.End(), [](int A) { return A > 0; }, Projection) == Crr.Begin());
|
||||
|
||||
always_check(Algorithms::FindIfNot(Arr, [](int A) { return A < 8; }) == Algorithms::Next(Arr.Begin(), 8));
|
||||
always_check(Algorithms::FindIfNot(Brr, [](int A) { return A < 8; }) == Algorithms::Next(Brr.Begin(), 8));
|
||||
always_check(Algorithms::FindIfNot(Crr, [](int A) { return A < 8; }) == Algorithms::Next(Crr.Begin(), 8));
|
||||
|
||||
always_check(Algorithms::FindLast(Arr, 2, { }, Projection) == Algorithms::Next(Arr.Begin(), 6));
|
||||
always_check(Algorithms::FindLast(Brr, 2, { }, Projection) == Algorithms::Next(Brr.Begin(), 6));
|
||||
always_check(Algorithms::FindLast(Crr, 2, { }, Projection) == Algorithms::Next(Crr.Begin(), 6));
|
||||
|
||||
always_check(Algorithms::FindLast(Arr.Begin(), Arr.End(), 2, { }, Projection) == Algorithms::Next(Arr.Begin(), 6));
|
||||
always_check(Algorithms::FindLast(Brr.Begin(), Brr.End(), 2, { }, Projection) == Algorithms::Next(Brr.Begin(), 6));
|
||||
always_check(Algorithms::FindLast(Crr.Begin(), Crr.End(), 2, { }, Projection) == Algorithms::Next(Crr.Begin(), 6));
|
||||
|
||||
always_check(Algorithms::FindLast(Arr, 10) == Arr.End());
|
||||
always_check(Algorithms::FindLast(Brr, 10) == Brr.End());
|
||||
always_check(Algorithms::FindLast(Crr, 10) == Crr.End());
|
||||
|
||||
always_check(Algorithms::FindLast(Arr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Arr.Begin(), 5));
|
||||
always_check(Algorithms::FindLast(Brr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Brr.Begin(), 5));
|
||||
always_check(Algorithms::FindLast(Crr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Crr.Begin(), 5));
|
||||
|
||||
always_check(Algorithms::FindLast(Arr, Ranges::Iota(4, 16)).IsEmpty());
|
||||
always_check(Algorithms::FindLast(Brr, Ranges::Iota(4, 16)).IsEmpty());
|
||||
always_check(Algorithms::FindLast(Crr, Ranges::Iota(4, 16)).IsEmpty());
|
||||
|
||||
always_check(Algorithms::FindLastIf(Arr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Arr.Begin(), 6));
|
||||
always_check(Algorithms::FindLastIf(Brr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Brr.Begin(), 6));
|
||||
always_check(Algorithms::FindLastIf(Crr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Crr.Begin(), 6));
|
||||
|
||||
always_check(Algorithms::FindLastIf(Arr.Begin(), Arr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Arr.Begin(), 6));
|
||||
always_check(Algorithms::FindLastIf(Brr.Begin(), Brr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Brr.Begin(), 6));
|
||||
always_check(Algorithms::FindLastIf(Crr.Begin(), Crr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Crr.Begin(), 6));
|
||||
|
||||
always_check(Algorithms::FindLastIf(Arr, [](int A) { return A == 10; }) == Arr.End());
|
||||
always_check(Algorithms::FindLastIf(Brr, [](int A) { return A == 10; }) == Brr.End());
|
||||
always_check(Algorithms::FindLastIf(Crr, [](int A) { return A == 10; }) == Crr.End());
|
||||
|
||||
always_check(Algorithms::FindLastIfNot(Arr, [](int A) { return A > 0; }, Projection) == Algorithms::Next(Arr.Begin(), 8));
|
||||
always_check(Algorithms::FindLastIfNot(Brr, [](int A) { return A > 0; }, Projection) == Algorithms::Next(Brr.Begin(), 8));
|
||||
always_check(Algorithms::FindLastIfNot(Crr, [](int A) { return A > 0; }, Projection) == Algorithms::Next(Crr.Begin(), 8));
|
||||
|
||||
always_check(Algorithms::FindLastIfNot(Arr.Begin(), Arr.End(), [](int A) { return A > 0; }, Projection) == Algorithms::Next(Arr.Begin(), 8));
|
||||
always_check(Algorithms::FindLastIfNot(Brr.Begin(), Brr.End(), [](int A) { return A > 0; }, Projection) == Algorithms::Next(Brr.Begin(), 8));
|
||||
always_check(Algorithms::FindLastIfNot(Crr.Begin(), Crr.End(), [](int A) { return A > 0; }, Projection) == Algorithms::Next(Crr.Begin(), 8));
|
||||
|
||||
always_check(Algorithms::FindLastIfNot(Arr, [](int A) { return A < 8; }) == Algorithms::Next(Arr.Begin(), 9));
|
||||
always_check(Algorithms::FindLastIfNot(Brr, [](int A) { return A < 8; }) == Algorithms::Next(Brr.Begin(), 9));
|
||||
always_check(Algorithms::FindLastIfNot(Crr, [](int A) { return A < 8; }) == Algorithms::Next(Crr.Begin(), 9));
|
||||
|
||||
always_check(Algorithms::FindAdjacent(Arr, { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Arr.Begin()));
|
||||
always_check(Algorithms::FindAdjacent(Brr, { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Brr.Begin()));
|
||||
always_check(Algorithms::FindAdjacent(Crr, { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Crr.Begin()));
|
||||
|
||||
always_check(Algorithms::FindAdjacent(Arr.Begin(), Arr.End(), { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Arr.Begin()));
|
||||
always_check(Algorithms::FindAdjacent(Brr.Begin(), Brr.End(), { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Brr.Begin()));
|
||||
always_check(Algorithms::FindAdjacent(Crr.Begin(), Crr.End(), { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Crr.Begin()));
|
||||
|
||||
always_check(Algorithms::FindAdjacent(Arr) == Arr.End());
|
||||
always_check(Algorithms::FindAdjacent(Brr) == Brr.End());
|
||||
always_check(Algorithms::FindAdjacent(Crr) == Crr.End());
|
||||
|
||||
always_check(Algorithms::Count(Arr, 2, { }, Projection) == 2);
|
||||
always_check(Algorithms::Count(Brr, 2, { }, Projection) == 2);
|
||||
always_check(Algorithms::Count(Crr, 2, { }, Projection) == 2);
|
||||
|
||||
always_check(Algorithms::Count(Arr.Begin(), Arr.End(), 2, { }, Projection) == 2);
|
||||
always_check(Algorithms::Count(Brr.Begin(), Brr.End(), 2, { }, Projection) == 2);
|
||||
always_check(Algorithms::Count(Crr.Begin(), Crr.End(), 2, { }, Projection) == 2);
|
||||
|
||||
always_check(Algorithms::Count(Arr, 10) == 0);
|
||||
always_check(Algorithms::Count(Brr, 10) == 0);
|
||||
always_check(Algorithms::Count(Crr, 10) == 0);
|
||||
|
||||
always_check(Algorithms::CountIf(Arr, [](int A) { return A == 2; }, Projection) == 2);
|
||||
always_check(Algorithms::CountIf(Brr, [](int A) { return A == 2; }, Projection) == 2);
|
||||
always_check(Algorithms::CountIf(Crr, [](int A) { return A == 2; }, Projection) == 2);
|
||||
|
||||
always_check(Algorithms::CountIf(Arr.Begin(), Arr.End(), [](int A) { return A == 2; }, Projection) == 2);
|
||||
always_check(Algorithms::CountIf(Brr.Begin(), Brr.End(), [](int A) { return A == 2; }, Projection) == 2);
|
||||
always_check(Algorithms::CountIf(Crr.Begin(), Crr.End(), [](int A) { return A == 2; }, Projection) == 2);
|
||||
|
||||
always_check(Algorithms::CountIf(Arr, [](int A) { return A == 10; }) == 0);
|
||||
always_check(Algorithms::CountIf(Brr, [](int A) { return A == 10; }) == 0);
|
||||
always_check(Algorithms::CountIf(Crr, [](int A) { return A == 10; }) == 0);
|
||||
|
||||
always_check(Algorithms::Mismatch(Arr, Arr, { }, Projection) == MakeTuple(Algorithms::Next(Arr.Begin(), 4), Algorithms::Next(Arr.Begin(), 4)));
|
||||
always_check(Algorithms::Mismatch(Brr, Brr, { }, Projection) == MakeTuple(Algorithms::Next(Brr.Begin(), 4), Algorithms::Next(Brr.Begin(), 4)));
|
||||
always_check(Algorithms::Mismatch(Crr, Crr, { }, Projection) == MakeTuple(Algorithms::Next(Crr.Begin(), 4), Algorithms::Next(Crr.Begin(), 4)));
|
||||
|
||||
always_check(Algorithms::Mismatch(Arr.Begin(), Arr.End(), Brr.Begin(), Brr.End(), { }, Projection) == MakeTuple(Algorithms::Next(Arr.Begin(), 4), Algorithms::Next(Brr.Begin(), 4)));
|
||||
always_check(Algorithms::Mismatch(Brr.Begin(), Brr.End(), Crr.Begin(), Crr.End(), { }, Projection) == MakeTuple(Algorithms::Next(Brr.Begin(), 4), Algorithms::Next(Crr.Begin(), 4)));
|
||||
always_check(Algorithms::Mismatch(Crr.Begin(), Crr.End(), Arr.Begin(), Arr.End(), { }, Projection) == MakeTuple(Algorithms::Next(Crr.Begin(), 4), Algorithms::Next(Arr.Begin(), 4)));
|
||||
|
||||
always_check(Algorithms::Mismatch(Arr, Brr, { }, Projection) == MakeTuple(Algorithms::Next(Arr.Begin(), 4), Algorithms::Next(Brr.Begin(), 4)));
|
||||
always_check(Algorithms::Mismatch(Brr, Crr, { }, Projection) == MakeTuple(Algorithms::Next(Brr.Begin(), 4), Algorithms::Next(Crr.Begin(), 4)));
|
||||
always_check(Algorithms::Mismatch(Crr, Arr, { }, Projection) == MakeTuple(Algorithms::Next(Crr.Begin(), 4), Algorithms::Next(Arr.Begin(), 4)));
|
||||
|
||||
always_check(Algorithms::Equal(Arr, Arr));
|
||||
always_check(Algorithms::Equal(Brr, Brr));
|
||||
always_check(Algorithms::Equal(Crr, Crr));
|
||||
|
||||
always_check(Algorithms::Equal(Arr.Begin(), Arr.End(), Brr.Begin(), Brr.End()));
|
||||
always_check(Algorithms::Equal(Brr.Begin(), Brr.End(), Crr.Begin(), Crr.End()));
|
||||
always_check(Algorithms::Equal(Crr.Begin(), Crr.End(), Arr.Begin(), Arr.End()));
|
||||
|
||||
always_check(Algorithms::Equal(Arr, Brr));
|
||||
always_check(Algorithms::Equal(Brr, Crr));
|
||||
always_check(Algorithms::Equal(Crr, Arr));
|
||||
|
||||
always_check(Algorithms::StartsWith(Arr, Ranges::Iota(0, 8)));
|
||||
always_check(Algorithms::StartsWith(Brr, Ranges::Iota(0, 8)));
|
||||
always_check(Algorithms::StartsWith(Crr, Ranges::Iota(0, 8)));
|
||||
|
||||
always_check(!Algorithms::StartsWith(Arr, Ranges::Iota(0, 8), { }, Projection));
|
||||
always_check(!Algorithms::StartsWith(Brr, Ranges::Iota(0, 8), { }, Projection));
|
||||
always_check(!Algorithms::StartsWith(Crr, Ranges::Iota(0, 8), { }, Projection));
|
||||
|
||||
always_check(Algorithms::EndsWith(Arr, Ranges::Iota(8, 10)));
|
||||
always_check(Algorithms::EndsWith(Brr, Ranges::Iota(8, 10)));
|
||||
always_check(Algorithms::EndsWith(Crr, Ranges::Iota(8, 10)));
|
||||
|
||||
always_check(Algorithms::EndsWith(Arr, Ranges::Iota(0, 2), { }, Projection));
|
||||
always_check(Algorithms::EndsWith(Brr, Ranges::Iota(0, 2), { }, Projection));
|
||||
always_check(Algorithms::EndsWith(Crr, Ranges::Iota(0, 2), { }, Projection));
|
||||
}
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestAlgorithms()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestBasic();
|
||||
NAMESPACE_PRIVATE::TestSearch();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Testing/ContainersTesting.h"
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Containers/Containers.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
@@ -7,17 +7,11 @@ NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
void TestContainers()
|
||||
{
|
||||
TestArray();
|
||||
TestStaticArray();
|
||||
TestArrayView();
|
||||
TestBitset();
|
||||
TestStaticBitset();
|
||||
TestList();
|
||||
}
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
NAMESPACE_UNNAMED_BEGIN
|
||||
|
||||
@@ -601,8 +595,22 @@ void TestList()
|
||||
}
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestContainers()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestArray();
|
||||
NAMESPACE_PRIVATE::TestStaticArray();
|
||||
NAMESPACE_PRIVATE::TestArrayView();
|
||||
NAMESPACE_PRIVATE::TestBitset();
|
||||
NAMESPACE_PRIVATE::TestStaticBitset();
|
||||
NAMESPACE_PRIVATE::TestList();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
230
Redcraft.Utility/Source/Private/Testing/Iterators.cpp
Normal file
230
Redcraft.Utility/Source/Private/Testing/Iterators.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Iterators/Iterators.h"
|
||||
#include "Containers/List.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
void TestMoveIterator()
|
||||
{
|
||||
{
|
||||
struct FTracker
|
||||
{
|
||||
FTracker() = default;
|
||||
FTracker(const FTracker&) { always_check_no_entry(); }
|
||||
FTracker(FTracker&&) = default;
|
||||
~FTracker() = default;
|
||||
FTracker& operator=(const FTracker&) { always_check_no_entry(); }
|
||||
FTracker& operator=(FTracker&&) = default;
|
||||
};
|
||||
|
||||
FTracker Arr[2];
|
||||
|
||||
auto First = MakeMoveIterator(&Arr[0]);
|
||||
auto Last = MakeMoveIterator(&Arr[2]);
|
||||
|
||||
FTracker Temp(*First++);
|
||||
|
||||
Temp = *First++;
|
||||
|
||||
always_check(First == Last);
|
||||
}
|
||||
|
||||
{
|
||||
int Arr[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
auto First = MakeMoveIterator(&Arr[0]);
|
||||
auto Last = MakeMoveIterator(&Arr[8]);
|
||||
|
||||
auto ConstFirst = MakeMoveIterator(&AsConst(Arr)[0]);
|
||||
auto ConstLast = MakeMoveIterator(&AsConst(Arr)[8]);
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
always_check(Last == ConstLast );
|
||||
|
||||
always_check(ConstLast - First == 8);
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstLast;
|
||||
|
||||
++Iter;
|
||||
--Jter;
|
||||
|
||||
always_check(*Iter++ == 1);
|
||||
always_check(*Jter-- == 7);
|
||||
|
||||
Iter += 2;
|
||||
Jter -= 2;
|
||||
|
||||
always_check(Iter[-1] == 3);
|
||||
always_check(Jter[ 1] == 5);
|
||||
|
||||
Iter = Iter - 2;
|
||||
Jter = Jter + 2;
|
||||
|
||||
always_check(*Iter == 2);
|
||||
always_check(*Jter == 6);
|
||||
|
||||
Iter = 2 + Iter;
|
||||
Jter = Jter - 2;
|
||||
|
||||
always_check(Iter - Jter == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TestReverseIterator()
|
||||
{
|
||||
int Arr[8] = { 7, 6, 5, 4, 3, 2, 1, 0 };
|
||||
|
||||
auto First = MakeReverseIterator(&Arr[8]);
|
||||
auto Last = MakeReverseIterator(&Arr[0]);
|
||||
|
||||
auto ConstFirst = MakeReverseIterator(&AsConst(Arr)[8]);
|
||||
auto ConstLast = MakeReverseIterator(&AsConst(Arr)[0]);
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
always_check(Last == ConstLast );
|
||||
|
||||
always_check(ConstLast - First == 8);
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstLast;
|
||||
|
||||
++Iter;
|
||||
--Jter;
|
||||
|
||||
always_check(*Iter++ == 1);
|
||||
always_check(*Jter-- == 7);
|
||||
|
||||
Iter += 2;
|
||||
Jter -= 2;
|
||||
|
||||
always_check(Iter[-1] == 3);
|
||||
always_check(Jter[ 1] == 5);
|
||||
|
||||
Iter = Iter - 2;
|
||||
Jter = Jter + 2;
|
||||
|
||||
always_check(*Iter == 2);
|
||||
always_check(*Jter == 6);
|
||||
|
||||
Iter = 2 + Iter;
|
||||
Jter = Jter - 2;
|
||||
|
||||
always_check(Iter - Jter == 0);
|
||||
}
|
||||
|
||||
void TestCountedIterator()
|
||||
{
|
||||
int Arr[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
|
||||
auto First = MakeCountedIterator(&Arr[0], 8);
|
||||
auto Last = First + 8;
|
||||
|
||||
auto ConstFirst = MakeCountedIterator(&AsConst(Arr)[0], 8);
|
||||
auto ConstLast = ConstFirst + 8;
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
always_check(Last == ConstLast );
|
||||
|
||||
always_check(ConstLast - First == 8);
|
||||
|
||||
always_check(Last == DefaultSentinel);
|
||||
always_check(DefaultSentinel == Last);
|
||||
|
||||
always_check(DefaultSentinel - First == 8);
|
||||
always_check(First - DefaultSentinel == -8);
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
always_check(Last == ConstLast );
|
||||
|
||||
always_check(Last - First == 8);
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstLast;
|
||||
|
||||
++Iter;
|
||||
--Jter;
|
||||
|
||||
always_check(*Iter++ == 1);
|
||||
always_check(*Jter-- == 7);
|
||||
|
||||
Iter += 2;
|
||||
Jter -= 2;
|
||||
|
||||
always_check(Iter[-1] == 3);
|
||||
always_check(Jter[ 1] == 5);
|
||||
|
||||
Iter = Iter - 2;
|
||||
Jter = Jter + 2;
|
||||
|
||||
always_check(*Iter == 2);
|
||||
always_check(*Jter == 6);
|
||||
|
||||
Iter = 2 + Iter;
|
||||
Jter = Jter - 2;
|
||||
|
||||
always_check(Iter - Jter == 0);
|
||||
}
|
||||
|
||||
void TestInsertIterator()
|
||||
{
|
||||
{
|
||||
TList<int> List = { 1, 2, 3 };
|
||||
|
||||
auto Iter = MakeFrontInserter(List);
|
||||
|
||||
*Iter++ = 1;
|
||||
*Iter++ = 2;
|
||||
*Iter++ = 3;
|
||||
|
||||
always_check(List == TList<int>({ 3, 2, 1, 1, 2, 3 }));
|
||||
}
|
||||
|
||||
{
|
||||
TList<int> List = { 1, 2, 3 };
|
||||
|
||||
auto Iter = MakeBackInserter(List);
|
||||
|
||||
*Iter++ = 1;
|
||||
*Iter++ = 2;
|
||||
*Iter++ = 3;
|
||||
|
||||
always_check(List == TList<int>({ 1, 2, 3, 1, 2, 3 }));
|
||||
}
|
||||
|
||||
{
|
||||
TList<int> List = { 1, 2, 3 };
|
||||
|
||||
auto Iter = MakeInserter(List, ++++List.Begin());
|
||||
|
||||
*Iter++ = 1;
|
||||
*Iter++ = 2;
|
||||
*Iter++ = 3;
|
||||
|
||||
always_check(List == TList<int>({ 1, 2, 1, 2, 3, 3 }));
|
||||
}
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestIterator()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestMoveIterator();
|
||||
NAMESPACE_PRIVATE::TestReverseIterator();
|
||||
NAMESPACE_PRIVATE::TestCountedIterator();
|
||||
NAMESPACE_PRIVATE::TestInsertIterator();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Testing/MemoryTesting.h"
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Memory/Memory.h"
|
||||
#include "Memory/Alignment.h"
|
||||
@@ -15,18 +15,7 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
void TestMemory()
|
||||
{
|
||||
TestAddress();
|
||||
TestAlignment();
|
||||
TestMemoryBuffer();
|
||||
TestMemoryMalloc();
|
||||
TestMemoryOperator();
|
||||
TestPointerTraits();
|
||||
TestUniquePointer();
|
||||
TestSharedPointer();
|
||||
TestInOutPointer();
|
||||
}
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
NAMESPACE_UNNAMED_BEGIN
|
||||
|
||||
@@ -255,23 +244,23 @@ void TestPointerTraits()
|
||||
always_check(!TPointerTraits<int64>::bIsPointer);
|
||||
|
||||
always_check(TPointerTraits<int64*>::bIsPointer);
|
||||
always_check((CSameAs<TPointerTraits<int64*>::PointerType, int64*>));
|
||||
always_check((CSameAs<TPointerTraits<int64*>::ElementType, int64>));
|
||||
always_check((CSameAs<TPointerTraits<int64*>::FPointerType, int64*>));
|
||||
always_check((CSameAs<TPointerTraits<int64*>::FElementType, int64>));
|
||||
always_check(TPointerTraits<int64*>::ToAddress(nullptr) == nullptr);
|
||||
|
||||
always_check(TPointerTraits<int64(*)[]>::bIsPointer);
|
||||
always_check((CSameAs<TPointerTraits<int64(*)[]>::PointerType, int64(*)[]>));
|
||||
always_check((CSameAs<TPointerTraits<int64(*)[]>::ElementType, int64>));
|
||||
always_check((CSameAs<TPointerTraits<int64(*)[]>::FPointerType, int64(*)[]>));
|
||||
always_check((CSameAs<TPointerTraits<int64(*)[]>::FElementType, int64>));
|
||||
always_check(TPointerTraits<int64*>::ToAddress(nullptr) == nullptr);
|
||||
|
||||
always_check(TPointerTraits<TSharedPtr<int64>>::bIsPointer);
|
||||
always_check((CSameAs<TPointerTraits<TSharedPtr<int64>>::PointerType, TSharedPtr<int64>>));
|
||||
always_check((CSameAs<TPointerTraits<TSharedPtr<int64>>::ElementType, int64>));
|
||||
always_check((CSameAs<TPointerTraits<TSharedPtr<int64>>::FPointerType, TSharedPtr<int64>>));
|
||||
always_check((CSameAs<TPointerTraits<TSharedPtr<int64>>::FElementType, int64>));
|
||||
always_check(TPointerTraits<TSharedPtr<int64>>::ToAddress(nullptr) == nullptr);
|
||||
|
||||
always_check(TPointerTraits<TSharedPtr<int64[]>>::bIsPointer);
|
||||
always_check((CSameAs<TPointerTraits<TSharedPtr<int64[]>>::PointerType, TSharedPtr<int64[]>>));
|
||||
always_check((CSameAs<TPointerTraits<TSharedPtr<int64[]>>::ElementType, int64>));
|
||||
always_check((CSameAs<TPointerTraits<TSharedPtr<int64[]>>::FPointerType, TSharedPtr<int64[]>>));
|
||||
always_check((CSameAs<TPointerTraits<TSharedPtr<int64[]>>::FElementType, int64>));
|
||||
always_check(TPointerTraits<TSharedPtr<int64[]>>::ToAddress(nullptr) == nullptr);
|
||||
|
||||
}
|
||||
@@ -1072,6 +1061,21 @@ void TestInOutPointer()
|
||||
}
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestMemory()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestAddress();
|
||||
NAMESPACE_PRIVATE::TestAlignment();
|
||||
NAMESPACE_PRIVATE::TestMemoryBuffer();
|
||||
NAMESPACE_PRIVATE::TestMemoryMalloc();
|
||||
NAMESPACE_PRIVATE::TestMemoryOperator();
|
||||
NAMESPACE_PRIVATE::TestPointerTraits();
|
||||
NAMESPACE_PRIVATE::TestUniquePointer();
|
||||
NAMESPACE_PRIVATE::TestSharedPointer();
|
||||
NAMESPACE_PRIVATE::TestInOutPointer();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Testing/MiscellaneousTesting.h"
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
@@ -10,12 +10,7 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
void TestMiscellaneous()
|
||||
{
|
||||
TestAssertionMacros();
|
||||
TestCompare();
|
||||
TestVarArgs();
|
||||
}
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
NAMESPACE_UNNAMED_BEGIN
|
||||
|
||||
@@ -285,6 +280,15 @@ void TestVarArgs()
|
||||
);
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestMiscellaneous()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestAssertionMacros();
|
||||
NAMESPACE_PRIVATE::TestCompare();
|
||||
NAMESPACE_PRIVATE::TestVarArgs();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
617
Redcraft.Utility/Source/Private/Testing/Numerics.cpp
Normal file
617
Redcraft.Utility/Source/Private/Testing/Numerics.cpp
Normal file
@@ -0,0 +1,617 @@
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Numerics/Numerics.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
void TestLiteral()
|
||||
{
|
||||
// always_check((CSameAs<decltype(0i8), int8 >));
|
||||
always_check((CSameAs<decltype(0i16), int16>));
|
||||
always_check((CSameAs<decltype(0i32), int32>));
|
||||
always_check((CSameAs<decltype(0i64), int64>));
|
||||
|
||||
always_check((CSameAs<decltype(0u8), uint8 >));
|
||||
always_check((CSameAs<decltype(0u16), uint16>));
|
||||
always_check((CSameAs<decltype(0u32), uint32>));
|
||||
always_check((CSameAs<decltype(0u64), uint64>));
|
||||
|
||||
always_check((CSameAs<decltype(0imax), intmax>));
|
||||
always_check((CSameAs<decltype(0umax), uintmax>));
|
||||
|
||||
always_check((CSameAs<decltype(0.0f32), float32>));
|
||||
always_check((CSameAs<decltype(0.0f64), float64>));
|
||||
}
|
||||
|
||||
void TestBit()
|
||||
{
|
||||
always_check(Math::ByteSwap<uint8 >(0x00 ) == 0x00 );
|
||||
always_check(Math::ByteSwap<uint16>(0x0011 ) == 0x1100 );
|
||||
always_check(Math::ByteSwap<uint32>(0x00112233 ) == 0x33221100 );
|
||||
always_check(Math::ByteSwap<uint64>(0x0011223344556677) == 0x7766554433221100);
|
||||
|
||||
always_check(Math::IsSingleBit(0b0000u) == false);
|
||||
always_check(Math::IsSingleBit(0b0001u) == true );
|
||||
always_check(Math::IsSingleBit(0b0010u) == true );
|
||||
always_check(Math::IsSingleBit(0b0011u) == false);
|
||||
always_check(Math::IsSingleBit(0b0100u) == true );
|
||||
always_check(Math::IsSingleBit(0b0101u) == false);
|
||||
always_check(Math::IsSingleBit(0b0110u) == false);
|
||||
always_check(Math::IsSingleBit(0b0111u) == false);
|
||||
always_check(Math::IsSingleBit(0b1000u) == true );
|
||||
always_check(Math::IsSingleBit(0b1001u) == false);
|
||||
|
||||
always_check(Math::CountAllZero(0b00000000u8) == 8);
|
||||
always_check(Math::CountAllZero(0b11111111u8) == 0);
|
||||
always_check(Math::CountAllZero(0b00011101u8) == 4);
|
||||
|
||||
always_check(Math::CountAllOne(0b00000000u8) == 0);
|
||||
always_check(Math::CountAllOne(0b11111111u8) == 8);
|
||||
always_check(Math::CountAllOne(0b00011101u8) == 4);
|
||||
|
||||
always_check(Math::CountLeftZero(0b00000000u8) == 8);
|
||||
always_check(Math::CountLeftZero(0b11111111u8) == 0);
|
||||
always_check(Math::CountLeftZero(0b00011100u8) == 3);
|
||||
|
||||
always_check(Math::CountLeftOne(0b00000000u8) == 0);
|
||||
always_check(Math::CountLeftOne(0b11111111u8) == 8);
|
||||
always_check(Math::CountLeftOne(0b11100011u8) == 3);
|
||||
|
||||
always_check(Math::CountRightZero(0b00000000u8) == 8);
|
||||
always_check(Math::CountRightZero(0b11111111u8) == 0);
|
||||
always_check(Math::CountRightZero(0b00011100u8) == 2);
|
||||
|
||||
always_check(Math::CountRightOne(0b00000000u8) == 0);
|
||||
always_check(Math::CountRightOne(0b11111111u8) == 8);
|
||||
always_check(Math::CountRightOne(0b11100011u8) == 2);
|
||||
|
||||
always_check(Math::BitWidth(0b0000u) == 0);
|
||||
always_check(Math::BitWidth(0b0001u) == 1);
|
||||
always_check(Math::BitWidth(0b0010u) == 2);
|
||||
always_check(Math::BitWidth(0b0011u) == 2);
|
||||
always_check(Math::BitWidth(0b0100u) == 3);
|
||||
always_check(Math::BitWidth(0b0101u) == 3);
|
||||
always_check(Math::BitWidth(0b0110u) == 3);
|
||||
always_check(Math::BitWidth(0b0111u) == 3);
|
||||
|
||||
always_check(Math::BitCeil(0b00000000u) == 0b00000001u);
|
||||
always_check(Math::BitCeil(0b00000001u) == 0b00000001u);
|
||||
always_check(Math::BitCeil(0b00000010u) == 0b00000010u);
|
||||
always_check(Math::BitCeil(0b00000011u) == 0b00000100u);
|
||||
always_check(Math::BitCeil(0b00000100u) == 0b00000100u);
|
||||
always_check(Math::BitCeil(0b00000101u) == 0b00001000u);
|
||||
always_check(Math::BitCeil(0b00000110u) == 0b00001000u);
|
||||
always_check(Math::BitCeil(0b00000111u) == 0b00001000u);
|
||||
always_check(Math::BitCeil(0b00001000u) == 0b00001000u);
|
||||
always_check(Math::BitCeil(0b00001001u) == 0b00010000u);
|
||||
|
||||
always_check(Math::BitFloor(0b00000000u) == 0b00000000u);
|
||||
always_check(Math::BitFloor(0b00000001u) == 0b00000001u);
|
||||
always_check(Math::BitFloor(0b00000010u) == 0b00000010u);
|
||||
always_check(Math::BitFloor(0b00000011u) == 0b00000010u);
|
||||
always_check(Math::BitFloor(0b00000100u) == 0b00000100u);
|
||||
always_check(Math::BitFloor(0b00000101u) == 0b00000100u);
|
||||
always_check(Math::BitFloor(0b00000110u) == 0b00000100u);
|
||||
always_check(Math::BitFloor(0b00000111u) == 0b00000100u);
|
||||
always_check(Math::BitFloor(0b00001000u) == 0b00001000u);
|
||||
always_check(Math::BitFloor(0b00001001u) == 0b00001000u);
|
||||
|
||||
always_check(Math::RotateLeft(0b00011101u8, 0) == 0b00011101u8);
|
||||
always_check(Math::RotateLeft(0b00011101u8, 1) == 0b00111010u8);
|
||||
always_check(Math::RotateLeft(0b00011101u8, 4) == 0b11010001u8);
|
||||
always_check(Math::RotateLeft(0b00011101u8, 9) == 0b00111010u8);
|
||||
always_check(Math::RotateLeft(0b00011101u8, -1) == 0b10001110u8);
|
||||
|
||||
always_check(Math::RotateRight(0b00011101u8, 0) == 0b00011101u8);
|
||||
always_check(Math::RotateRight(0b00011101u8, 1) == 0b10001110u8);
|
||||
always_check(Math::RotateRight(0b00011101u8, 4) == 0b11010001u8);
|
||||
always_check(Math::RotateRight(0b00011101u8, 9) == 0b10001110u8);
|
||||
always_check(Math::RotateRight(0b00011101u8, -1) == 0b00111010u8);
|
||||
}
|
||||
|
||||
void TestMath()
|
||||
{
|
||||
always_check( Math::IsWithin(0, 0, 1));
|
||||
always_check(!Math::IsWithin(1, 0, 1));
|
||||
always_check(!Math::IsWithin(2, 0, 1));
|
||||
|
||||
always_check( Math::IsWithinInclusive(0, 0, 1));
|
||||
always_check( Math::IsWithinInclusive(1, 0, 1));
|
||||
always_check(!Math::IsWithinInclusive(2, 0, 1));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Trunc(2.00), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Trunc(2.25), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Trunc(2.75), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Trunc(3.00), 3.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Trunc(-2.00), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Trunc(-2.25), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Trunc(-2.75), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Trunc(-3.00), -3.0, 1e-8));
|
||||
|
||||
always_check(Math::TruncTo<int>(2.00) == 2);
|
||||
always_check(Math::TruncTo<int>(2.25) == 2);
|
||||
always_check(Math::TruncTo<int>(2.75) == 2);
|
||||
always_check(Math::TruncTo<int>(3.00) == 3);
|
||||
|
||||
always_check(Math::TruncTo<int>(-2.00) == -2);
|
||||
always_check(Math::TruncTo<int>(-2.25) == -2);
|
||||
always_check(Math::TruncTo<int>(-2.75) == -2);
|
||||
always_check(Math::TruncTo<int>(-3.00) == -3);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Ceil(2.00), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Ceil(2.25), 3.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Ceil(2.75), 3.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Ceil(3.00), 3.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Ceil(-2.00), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Ceil(-2.25), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Ceil(-2.75), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Ceil(-3.00), -3.0, 1e-8));
|
||||
|
||||
always_check(Math::CeilTo<int>(2.00) == 2);
|
||||
always_check(Math::CeilTo<int>(2.25) == 3);
|
||||
always_check(Math::CeilTo<int>(2.75) == 3);
|
||||
always_check(Math::CeilTo<int>(3.00) == 3);
|
||||
|
||||
always_check(Math::CeilTo<int>(-2.00) == -2);
|
||||
always_check(Math::CeilTo<int>(-2.25) == -2);
|
||||
always_check(Math::CeilTo<int>(-2.75) == -2);
|
||||
always_check(Math::CeilTo<int>(-3.00) == -3);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Floor(2.00), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Floor(2.25), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Floor(2.75), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Floor(3.00), 3.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Floor(-2.00), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Floor(-2.25), -3.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Floor(-2.75), -3.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Floor(-3.00), -3.0, 1e-8));
|
||||
|
||||
always_check(Math::FloorTo<int>(2.00) == 2);
|
||||
always_check(Math::FloorTo<int>(2.25) == 2);
|
||||
always_check(Math::FloorTo<int>(2.75) == 2);
|
||||
always_check(Math::FloorTo<int>(3.00) == 3);
|
||||
|
||||
always_check(Math::FloorTo<int>(-2.00) == -2);
|
||||
always_check(Math::FloorTo<int>(-2.25) == -3);
|
||||
always_check(Math::FloorTo<int>(-2.75) == -3);
|
||||
always_check(Math::FloorTo<int>(-3.00) == -3);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Round(2.00), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Round(2.25), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Round(2.75), 3.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Round(3.00), 3.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Round(-2.00), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Round(-2.25), -2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Round(-2.75), -3.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Round(-3.00), -3.0, 1e-8));
|
||||
|
||||
always_check(Math::RoundTo<int>(2.00) == 2);
|
||||
always_check(Math::RoundTo<int>(2.25) == 2);
|
||||
always_check(Math::RoundTo<int>(2.75) == 3);
|
||||
always_check(Math::RoundTo<int>(3.00) == 3);
|
||||
|
||||
always_check(Math::RoundTo<int>(-2.00) == -2);
|
||||
always_check(Math::RoundTo<int>(-2.25) == -2);
|
||||
always_check(Math::RoundTo<int>(-2.75) == -3);
|
||||
always_check(Math::RoundTo<int>(-3.00) == -3);
|
||||
|
||||
always_check(Math::Abs(-1) == 1);
|
||||
always_check(Math::Abs( 0) == 0);
|
||||
always_check(Math::Abs( 1) == 1);
|
||||
|
||||
always_check(Math::Sign(-4) == -1);
|
||||
always_check(Math::Sign( 0) == 0);
|
||||
always_check(Math::Sign( 4) == 1);
|
||||
|
||||
always_check(Math::Min(1, 2, 3, 4, 5) == 1);
|
||||
always_check(Math::Min(5, 4, 3, 2, 1) == 1);
|
||||
always_check(Math::Max(1, 2, 3, 4, 5) == 5);
|
||||
always_check(Math::Max(5, 4, 3, 2, 1) == 5);
|
||||
|
||||
always_check(Math::MinIndex(1, 2, 3, 4, 5) == 0);
|
||||
always_check(Math::MinIndex(5, 4, 3, 2, 1) == 4);
|
||||
always_check(Math::MaxIndex(1, 2, 3, 4, 5) == 4);
|
||||
always_check(Math::MaxIndex(5, 4, 3, 2, 1) == 0);
|
||||
|
||||
always_check(Math::Div( 5, 2).Quotient == 2);
|
||||
always_check(Math::Div( 5, 2).Remainder == 1);
|
||||
always_check(Math::Div( 5, -2).Quotient == -2);
|
||||
always_check(Math::Div( 5, -2).Remainder == 1);
|
||||
always_check(Math::Div(-5, 2).Quotient == -2);
|
||||
always_check(Math::Div(-5, 2).Remainder == -1);
|
||||
always_check(Math::Div(-5, -2).Quotient == 2);
|
||||
always_check(Math::Div(-5, -2).Remainder == -1);
|
||||
|
||||
always_check(Math::DivAndCeil(4 + 0, 4) == 1);
|
||||
always_check(Math::DivAndCeil(4 + 1, 4) == 2);
|
||||
always_check(Math::DivAndCeil(4 + 3, 4) == 2);
|
||||
always_check(Math::DivAndCeil(4 + 4, 4) == 2);
|
||||
|
||||
always_check(Math::DivAndCeil(-4 - 0, 4) == -1);
|
||||
always_check(Math::DivAndCeil(-4 - 1, 4) == -1);
|
||||
always_check(Math::DivAndCeil(-4 - 3, 4) == -1);
|
||||
always_check(Math::DivAndCeil(-4 - 4, 4) == -2);
|
||||
|
||||
always_check(Math::DivAndFloor(4 + 0, 4) == 1);
|
||||
always_check(Math::DivAndFloor(4 + 1, 4) == 1);
|
||||
always_check(Math::DivAndFloor(4 + 3, 4) == 1);
|
||||
always_check(Math::DivAndFloor(4 + 4, 4) == 2);
|
||||
|
||||
always_check(Math::DivAndFloor(-4 - 0, 4) == -1);
|
||||
always_check(Math::DivAndFloor(-4 - 1, 4) == -2);
|
||||
always_check(Math::DivAndFloor(-4 - 3, 4) == -2);
|
||||
always_check(Math::DivAndFloor(-4 - 4, 4) == -2);
|
||||
|
||||
always_check(Math::DivAndRound(4 + 0, 4) == 1);
|
||||
always_check(Math::DivAndRound(4 + 1, 4) == 1);
|
||||
always_check(Math::DivAndRound(4 + 3, 4) == 2);
|
||||
always_check(Math::DivAndRound(4 + 4, 4) == 2);
|
||||
|
||||
always_check(Math::DivAndRound(-4 - 0, 4) == -1);
|
||||
always_check(Math::DivAndRound(-4 - 1, 4) == -1);
|
||||
always_check(Math::DivAndRound(-4 - 3, 4) == -2);
|
||||
always_check(Math::DivAndRound(-4 - 4, 4) == -2);
|
||||
|
||||
always_check(Math::IsNearlyEqual(4.0, 4.0));
|
||||
|
||||
always_check(Math::IsNearlyZero(0.0));
|
||||
|
||||
always_check(Math::IsInfinity( TNumericLimits<float32>::Infinity()));
|
||||
always_check(Math::IsInfinity(-TNumericLimits<float32>::Infinity()));
|
||||
|
||||
always_check(Math::IsNaN( TNumericLimits<float32>::QuietNaN()));
|
||||
always_check(Math::IsNaN(-TNumericLimits<float32>::QuietNaN()));
|
||||
always_check(Math::IsNaN( TNumericLimits<float32>::SignalingNaN()));
|
||||
always_check(Math::IsNaN(-TNumericLimits<float32>::SignalingNaN()));
|
||||
|
||||
always_check(Math::IsNaN(Math::NaN<float32>(4u)));
|
||||
|
||||
always_check(Math::IsNormal(1.0e4));
|
||||
always_check(Math::IsNormal(1.0e8));
|
||||
|
||||
always_check(!Math::IsNegative(+1.0));
|
||||
always_check(!Math::IsNegative(+0.0));
|
||||
always_check( Math::IsNegative(-0.0));
|
||||
always_check( Math::IsNegative(-1.0));
|
||||
|
||||
always_check(Math::Exponent(1.0) == 0);
|
||||
always_check(Math::Exponent(2.0) == 1);
|
||||
always_check(Math::Exponent(4.0) == 2);
|
||||
|
||||
always_check(Math::NaNPayload(Math::NaN<float32>(4u)) == 4u);
|
||||
|
||||
enum class ETest : uint16 { A = 65535 };
|
||||
|
||||
always_check(Math::NaNPayload<ETest>(Math::NaN<float32>(ETest::A)) == ETest::A);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::FMod(5.0, 2.0), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::FMod(5.0, 2.5), 0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::FMod(5.0, 3.0), 2.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::FMod(-5.0, 2.0), -1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::FMod(-5.0, 2.5), -0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::FMod(-5.0, 3.0), -2.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Remainder(5.0, 2.0), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Remainder(5.0, 2.5), 0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Remainder(5.0, 3.0), -1.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Remainder(-5.0, 2.0), -1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Remainder(-5.0, 2.5), -0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Remainder(-5.0, 3.0), 1.0, 1e-8));
|
||||
|
||||
always_check(Math::RemQuo(5.0, 2.0).Quotient == 2);
|
||||
always_check(Math::RemQuo(5.0, 2.5).Quotient == 2);
|
||||
always_check(Math::RemQuo(5.0, 3.0).Quotient == 2);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::RemQuo(5.0, 2.0).Remainder, 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::RemQuo(5.0, 2.5).Remainder, 0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::RemQuo(5.0, 3.0).Remainder, -1.0, 1e-8));
|
||||
|
||||
always_check(Math::RemQuo(-5.0, 2.0).Quotient == -2);
|
||||
always_check(Math::RemQuo(-5.0, 2.5).Quotient == -2);
|
||||
always_check(Math::RemQuo(-5.0, 3.0).Quotient == -2);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::RemQuo(-5.0, 2.0).Remainder, -1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::RemQuo(-5.0, 2.5).Remainder, -0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::RemQuo(-5.0, 3.0).Remainder, 1.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::ModF(123.456).IntegralPart, 123.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::ModF(123.456).FractionalPart, 0.456, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Exp(-1.5), 0.2231301601, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Exp(-1.0), 0.3678794412, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Exp( 0.0), 1.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Exp( 1.0), 2.7182818284, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Exp( 1.5), 4.4816890703, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Exp2(-1.5), 0.3535533906, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Exp2(-1.0), 0.5000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Exp2( 0.0), 1.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Exp2( 1.0), 2.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Exp2( 1.5), 2.8284271247, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::ExpMinus1(-1.5), -0.7768698398, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::ExpMinus1(-1.0), -0.6321205588, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::ExpMinus1( 0.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::ExpMinus1( 1.0), 1.7182818284, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::ExpMinus1( 1.5), 3.4816890703, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Log(0.5), -0.6931471806, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log(1.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log(1.5), 0.4054651081, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log(2.0), 0.6931471806, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log(2.5), 0.9162907319, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Log2(0.5), -1.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log2(1.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log2(1.5), 0.5849625007, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log2(2.0), 1.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log2(2.5), 1.3219280949, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Log10(0.5), -0.3010299957, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log10(1.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log10(1.5), 0.1760912591, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log10(2.0), 0.3010299957, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log10(2.5), 0.3979400087, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Log1Plus(0.5), 0.4054651081, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log1Plus(1.0), 0.6931471806, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log1Plus(1.5), 0.9162907319, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log1Plus(2.0), 1.0986122887, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Log1Plus(2.5), 1.2527629685, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Square(0.0), 0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Square(1.0), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Square(2.0), 4.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Square(3.0), 9.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Cube(0.0), 0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cube(1.0), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cube(2.0), 8.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cube(3.0), 27.0, 1e-8));
|
||||
|
||||
always_check(Math::Pow(2, 0) == 1);
|
||||
always_check(Math::Pow(2, 1) == 2);
|
||||
always_check(Math::Pow(2, 2) == 4);
|
||||
always_check(Math::Pow(2, 3) == 8);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Pow(2.0, 0.0), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Pow(2.0, 1.0), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Pow(2.0, 2.0), 4.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Pow(2.0, 3.0), 8.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Sqrt(0), 0, 1));
|
||||
always_check(Math::IsNearlyEqual(Math::Sqrt(1), 1, 1));
|
||||
always_check(Math::IsNearlyEqual(Math::Sqrt(4), 2, 1));
|
||||
always_check(Math::IsNearlyEqual(Math::Sqrt(8), 2, 1));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Sqrt(0.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sqrt(1.0), 1.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sqrt(2.0), 1.4142135624, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sqrt(3.0), 1.7320508076, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Cbrt(0), 0, 1));
|
||||
always_check(Math::IsNearlyEqual(Math::Cbrt(1), 1, 1));
|
||||
always_check(Math::IsNearlyEqual(Math::Cbrt(4), 1, 1));
|
||||
always_check(Math::IsNearlyEqual(Math::Cbrt(8), 2, 1));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Cbrt(0.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cbrt(1.0), 1.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cbrt(2.0), 1.2599210499, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cbrt(3.0), 1.4422495703, 1e-8));
|
||||
|
||||
always_check(Math::Sum(1, 2, 3, 4, 5) == 15);
|
||||
|
||||
always_check(Math::SquaredSum(1, 2, 3, 4, 5) == 55);
|
||||
|
||||
always_check(Math::Avg(1, 2, 3, 4, 5) == 3);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Hypot(1.0, 2.0, 3.0, 4.0, 5.0), 7.4161984871, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Sin(-9.0), -0.4121184852, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sin(-6.0), 0.2794154982, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sin(-2.0), -0.9092974268, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sin(-1.0), -0.8414709848, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sin( 0.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sin( 1.0), 0.8414709848, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sin( 2.0), 0.9092974268, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sin( 6.0), -0.2794154982, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Sin( 9.0), 0.4121184852, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Cos(-9.0), -0.9111302619, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cos(-6.0), 0.9601702866, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cos(-2.0), -0.4161468365, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cos(-1.0), 0.5403023059, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cos( 0.0), 1.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cos( 1.0), 0.5403023059, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cos( 2.0), -0.4161468365, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cos( 6.0), 0.9601702866, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Cos( 9.0), -0.9111302619, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Tan(-9.0), 0.4523156594, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Tan(-6.0), 0.2910061914, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Tan(-2.0), 2.1850398633, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Tan(-1.0), -1.5574077247, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Tan( 0.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Tan( 1.0), 1.5574077247, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Tan( 2.0), -2.1850398633, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Tan( 6.0), -0.2910061914, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Tan( 9.0), -0.4523156594, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Asin(-1.0), -1.5707963268, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Asin(-0.5), -0.5235987756, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Asin( 0.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Asin( 0.5), 0.5235987756, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Asin( 1.0), 1.5707963268, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Acos(-1.0), 3.1415926536, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Acos(-0.5), 2.0943951024, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Acos( 0.0), 1.5707963268, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Acos( 0.5), 1.0471975512, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Acos( 1.0), 0.0000000000, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Atan(-1.0), -0.7853981634, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Atan(-0.5), -0.4636476090, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Atan( 0.0), 0.0000000000, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Atan( 0.5), 0.4636476090, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Atan( 1.0), 0.7853981634, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Atan2(-1.0, -1.0), -2.3561944902, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Atan2(-0.5, -1.0), -2.6779450446, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Atan2( 0.0, -1.0), 3.1415926536, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Atan2( 0.5, -1.0), 2.6779450446, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Atan2( 1.0, -1.0), 2.3561944902, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh(-9.0), -4051.5419020, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh(-6.0), -201.71315737, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh(-2.0), -3.6268604078, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh(-1.0), -1.1752011936, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh( 0.0), 0.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh( 1.0), 1.1752011936, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh( 2.0), 3.6268604078, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh( 6.0), 201.71315737, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Sinh( 9.0), 4051.5419020, 1e-4));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh(-9.0), 4051.5420254, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh(-6.0), 201.71563612, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh(-2.0), 3.7621956911, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh(-1.0), 1.5430806348, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh( 0.0), 1.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh( 1.0), 1.5430806348, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh( 2.0), 3.7621956911, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh( 6.0), 201.71563612, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Cosh( 9.0), 4051.5420254, 1e-4));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh(-9.0), -1.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh(-6.0), -1.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh(-2.0), -0.9640275801, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh(-1.0), -0.7615941559, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh( 0.0), 0.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh( 1.0), 0.7615941559, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh( 2.0), 0.9640275801, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh( 6.0), 1.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Tanh( 9.0), 1.0000000000, 1e-4));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh(-9.0), -2.8934439858, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh(-6.0), -2.4917798526, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh(-2.0), -1.4436354752, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh(-1.0), -0.8813735870, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh( 0.0), 0.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh( 1.0), 0.8813735870, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh( 2.0), 1.4436354752, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh( 6.0), 2.4917798526, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Asinh( 9.0), 2.8934439858, 1e-4));
|
||||
|
||||
always_check(Math::IsNaN( Math::Acosh(-9.0) ));
|
||||
always_check(Math::IsNaN( Math::Acosh(-6.0) ));
|
||||
always_check(Math::IsNaN( Math::Acosh(-2.0) ));
|
||||
always_check(Math::IsNaN( Math::Acosh(-1.0) ));
|
||||
always_check(Math::IsNaN( Math::Acosh( 0.0) ));
|
||||
always_check(Math::IsNearlyEqual(Math::Acosh( 1.0), 0.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Acosh( 2.0), 1.3169578969, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Acosh( 6.0), 2.4778887302, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Acosh( 9.0), 2.8872709503, 1e-4));
|
||||
|
||||
always_check(Math::IsInfinity( Math::Atanh(-1.0) ));
|
||||
always_check(Math::IsNearlyEqual(Math::Atanh(-0.5), -0.5493061443, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Atanh( 0.0), 0.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Atanh( 0.5), 0.5493061443, 1e-4));
|
||||
always_check(Math::IsInfinity( Math::Atanh( 1.0) ));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Erf(-6.0), -1.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erf(-2.0), -0.9953222650, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erf(-1.0), -0.8427007929, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erf( 0.0), 0.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erf( 1.0), 0.8427007929, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erf( 2.0), 0.9953222650, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erf( 6.0), 1.0000000000, 1e-4));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Erfc(-6.0), 2.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erfc(-2.0), 1.9953222650, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erfc(-1.0), 1.8427007929, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erfc( 0.0), 1.0000000000, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erfc( 1.0), 0.1572992070, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erfc( 2.0), 0.0046777349, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Erfc( 6.0), 0.0000000000, 1e-4));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Gamma(-0.75), -4.8341465442, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Gamma(-0.50), -3.5449077018, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Gamma(-0.25), -4.9016668098, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Gamma( 0.25), 3.6256099082, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Gamma( 0.50), 1.7724538509, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::Gamma( 0.75), 1.2254167025, 1e-4));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::LogGamma(-0.75), 1.5757045971, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::LogGamma(-0.50), 1.2655121235, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::LogGamma(-0.25), 1.5895753125, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::LogGamma( 0.25), 1.2880225246, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::LogGamma( 0.50), 0.5723649429, 1e-4));
|
||||
always_check(Math::IsNearlyEqual(Math::LogGamma( 0.75), 0.2032809514, 1e-4));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::LdExp(1.0, 0), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::LdExp(1.0, 1), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::LdExp(1.0, 2), 4.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::LdExp(1.0, 3), 8.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::RadiansToDegrees(0.0), 0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::RadiansToDegrees(Math::TNumbers<float64>::Pi), 180.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::RadiansToDegrees(Math::TNumbers<float64>::TwoPi), 360.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::DegreesToRadians( 0.0), 0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::DegreesToRadians(180.0), Math::TNumbers<float64>::Pi, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::DegreesToRadians(360.0), Math::TNumbers<float64>::TwoPi, 1e-8));
|
||||
|
||||
always_check(Math::GCD(0, 0) == 0);
|
||||
always_check(Math::GCD(0, 1) == 1);
|
||||
always_check(Math::GCD(9, 6) == 3);
|
||||
|
||||
always_check(Math::LCM(0, 0) == 0);
|
||||
always_check(Math::LCM(0, 1) == 0);
|
||||
always_check(Math::LCM(9, 6) == 18);
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Clamp(0.0, 1.0, 2.0), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Clamp(1.0, 1.0, 2.0), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Clamp(2.0, 1.0, 2.0), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Clamp(3.0, 1.0, 2.0), 2.0, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::WrappingClamp(0.5, 0.0, 2.0), 0.5, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::WrappingClamp(1.5, 0.0, 2.0), 1.5, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::WrappingClamp(2.5, 0.0, 2.0), 0.5, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::WrappingClamp(3.5, 0.0, 2.0), 1.5, 1e-8));
|
||||
|
||||
always_check(Math::IsNearlyEqual(Math::Lerp(0.0, 2.0, 0.0), 0.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Lerp(0.0, 2.0, 0.5), 1.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Lerp(0.0, 2.0, 1.0), 2.0, 1e-8));
|
||||
always_check(Math::IsNearlyEqual(Math::Lerp(0.0, 2.0, 1.5), 3.0, 1e-8));
|
||||
|
||||
always_check(static_cast<uint8>(Math::LerpStable(0, 255, 0.0)) == 0);
|
||||
always_check(static_cast<uint8>(Math::LerpStable(0, 255, 0.5)) == 127);
|
||||
always_check(static_cast<uint8>(Math::LerpStable(0, 255, 1.0)) == 255);
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestNumeric()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestLiteral();
|
||||
NAMESPACE_PRIVATE::TestBit();
|
||||
NAMESPACE_PRIVATE::TestMath();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
471
Redcraft.Utility/Source/Private/Testing/Ranges.cpp
Normal file
471
Redcraft.Utility/Source/Private/Testing/Ranges.cpp
Normal file
@@ -0,0 +1,471 @@
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Ranges/Ranges.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "Containers/List.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
void TestConversion()
|
||||
{
|
||||
{
|
||||
const TArray<int> Arr = { 1, 2, 3, 4, 5 };
|
||||
const TList<int> List = { 1, 2, 3, 4, 5 };
|
||||
|
||||
const TArray<int> Brr = Ranges::View(List.Begin(), List.End()) | Ranges::To<TArray<int>>();
|
||||
const TList<int> Mist = Ranges::View(Arr.Begin(), Arr.End()) | Ranges::To<TList<int>>();
|
||||
|
||||
always_check(Arr == Brr);
|
||||
always_check(List == Mist);
|
||||
}
|
||||
|
||||
{
|
||||
const TArray<int> Arr = { 1, 2, 3, 4, 5 };
|
||||
const TList<int> List = { 1, 2, 3, 4, 5 };
|
||||
|
||||
const TArray<int> Brr = Ranges::View(List.Begin(), List.End()) | Ranges::To<TArray>();
|
||||
const TList<int> Mist = Ranges::View(Arr.Begin(), Arr.End()) | Ranges::To<TList>();
|
||||
|
||||
always_check(Arr == Brr);
|
||||
always_check(List == Mist);
|
||||
}
|
||||
}
|
||||
|
||||
void TestFactory()
|
||||
{
|
||||
{
|
||||
const TArray<int> Arr = { };
|
||||
const TArray<int> Brr = Ranges::Empty<int> | Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Arr == Brr);
|
||||
}
|
||||
|
||||
{
|
||||
const TArray<int> Arr = { 1 };
|
||||
const TArray<int> Brr = Ranges::Single(1) | Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Arr == Brr);
|
||||
}
|
||||
|
||||
{
|
||||
const TArray<int> Arr = { 0, 1, 2, 3, 4 };
|
||||
const TArray<int> Brr = Ranges::Iota(0, 5) | Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Arr == Brr);
|
||||
}
|
||||
|
||||
{
|
||||
auto View = Ranges::Iota(0, 5);
|
||||
|
||||
always_check(View.Num() == 5);
|
||||
always_check(!View.IsEmpty());
|
||||
always_check(!!View);
|
||||
|
||||
always_check(View.Front() == 0);
|
||||
|
||||
auto First = View.Begin();
|
||||
auto Last = View.End();
|
||||
|
||||
auto ConstFirst = AsConst(View).Begin();
|
||||
auto ConstLast = AsConst(View).End();
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
always_check(Last == ConstLast );
|
||||
|
||||
ConstFirst = First;
|
||||
ConstLast = Last;
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstLast;
|
||||
|
||||
++Iter;
|
||||
|
||||
always_check(*Iter++ == 1);
|
||||
}
|
||||
|
||||
{
|
||||
auto View = Ranges::Iota(0);
|
||||
|
||||
always_check(!View.IsEmpty());
|
||||
always_check(!!View);
|
||||
|
||||
always_check(View.Front() == 0);
|
||||
|
||||
auto First = View.Begin();
|
||||
auto Last = View.End();
|
||||
|
||||
auto ConstFirst = AsConst(View).Begin();
|
||||
auto ConstLast = AsConst(View).End();
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
|
||||
ConstFirst = First;
|
||||
ConstLast = Last;
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstLast;
|
||||
|
||||
++Iter;
|
||||
|
||||
always_check(*Iter++ == 1);
|
||||
}
|
||||
|
||||
{
|
||||
const TArray<int> Arr = { 0, 0, 0, 0, 0 };
|
||||
const TArray<int> Brr = Ranges::Repeat(0, 5) | Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Arr == Brr);
|
||||
}
|
||||
|
||||
{
|
||||
auto View = Ranges::Repeat(0, 8);
|
||||
|
||||
always_check(View.Num() == 8);
|
||||
always_check(!View.IsEmpty());
|
||||
always_check(!!View);
|
||||
|
||||
always_check(View.Front() == 0);
|
||||
always_check(View.Back() == 0);
|
||||
|
||||
auto First = View.Begin();
|
||||
auto Last = View.End();
|
||||
|
||||
auto ConstFirst = AsConst(View).Begin();
|
||||
auto ConstLast = AsConst(View).End();
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
always_check(Last == ConstLast );
|
||||
|
||||
always_check(ConstLast - First == 8);
|
||||
|
||||
ConstFirst = First;
|
||||
ConstLast = Last;
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstLast;
|
||||
|
||||
++Iter;
|
||||
--Jter;
|
||||
|
||||
always_check(*Iter++ == 0);
|
||||
always_check(*Jter-- == 0);
|
||||
|
||||
Iter += 2;
|
||||
Jter -= 2;
|
||||
|
||||
always_check(Iter[-1] == 0);
|
||||
always_check(Jter[ 1] == 0);
|
||||
|
||||
Iter = Iter - 2;
|
||||
Jter = Jter + 2;
|
||||
|
||||
always_check(*Iter == 0);
|
||||
always_check(*Jter == 0);
|
||||
|
||||
Iter = 2 + Iter;
|
||||
Jter = Jter - 2;
|
||||
|
||||
always_check(Iter - Jter == 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto View = Ranges::Repeat(0);
|
||||
|
||||
always_check(!View.IsEmpty());
|
||||
always_check(!!View);
|
||||
|
||||
always_check(View.Front() == 0);
|
||||
|
||||
auto First = View.Begin();
|
||||
auto Last = View.End();
|
||||
|
||||
auto ConstFirst = AsConst(View).Begin();
|
||||
auto ConstLast = AsConst(View).End();
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
|
||||
ConstFirst = First;
|
||||
ConstLast = Last;
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstFirst + 8;
|
||||
|
||||
++Iter;
|
||||
--Jter;
|
||||
|
||||
always_check(*Iter++ == 0);
|
||||
always_check(*Jter-- == 0);
|
||||
|
||||
Iter += 2;
|
||||
Jter -= 2;
|
||||
|
||||
always_check(Iter[-1] == 0);
|
||||
always_check(Jter[ 1] == 0);
|
||||
|
||||
Iter = Iter - 2;
|
||||
Jter = Jter + 2;
|
||||
|
||||
always_check(*Iter == 0);
|
||||
always_check(*Jter == 0);
|
||||
|
||||
Iter = 2 + Iter;
|
||||
Jter = Jter - 2;
|
||||
|
||||
always_check(Iter - Jter == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TestAllView()
|
||||
{
|
||||
TArray<int> Arr = { 0, 1, 2, 3, 4 };
|
||||
|
||||
TArray<int> Brr = Ranges::All(Arr) | Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Arr == Brr);
|
||||
|
||||
auto View = Ranges::All(MoveTemp(Arr));
|
||||
|
||||
Arr.Reset();
|
||||
|
||||
TArray<int> Crr = View | Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Brr == Crr);
|
||||
}
|
||||
|
||||
void TestMoveView()
|
||||
{
|
||||
{
|
||||
struct FTracker
|
||||
{
|
||||
FTracker() = default;
|
||||
FTracker(const FTracker&) { always_check_no_entry(); }
|
||||
FTracker(FTracker&&) = default;
|
||||
~FTracker() = default;
|
||||
FTracker& operator=(const FTracker&) { always_check_no_entry(); }
|
||||
FTracker& operator=(FTracker&&) = default;
|
||||
};
|
||||
|
||||
FTracker Arr[2];
|
||||
|
||||
auto View = Arr | Ranges::Move();
|
||||
|
||||
auto First = View.Begin();
|
||||
auto Last = View.End();
|
||||
|
||||
FTracker Temp(*First++);
|
||||
|
||||
Temp = *First++;
|
||||
|
||||
always_check(First == Last);
|
||||
}
|
||||
|
||||
{
|
||||
TArray<int> Arr = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
auto View = Arr | Ranges::Move();
|
||||
|
||||
always_check(View.Num() == 8);
|
||||
always_check(!View.IsEmpty());
|
||||
always_check(!!View);
|
||||
|
||||
always_check(View.Front() == 0);
|
||||
always_check(View.Back() == 7);
|
||||
|
||||
auto First = View.Begin();
|
||||
auto Last = View.End();
|
||||
|
||||
auto ConstFirst = AsConst(View).Begin();
|
||||
auto ConstLast = AsConst(View).End();
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
always_check(Last == ConstLast );
|
||||
|
||||
always_check(ConstLast - First == 8);
|
||||
|
||||
ConstFirst = First;
|
||||
ConstLast = Last;
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstLast;
|
||||
|
||||
++Iter;
|
||||
--Jter;
|
||||
|
||||
always_check(*Iter++ == 1);
|
||||
always_check(*Jter-- == 7);
|
||||
|
||||
Iter += 2;
|
||||
Jter -= 2;
|
||||
|
||||
always_check(Iter[-1] == 3);
|
||||
always_check(Jter[ 1] == 5);
|
||||
|
||||
Iter = Iter - 2;
|
||||
Jter = Jter + 2;
|
||||
|
||||
always_check(*Iter == 2);
|
||||
always_check(*Jter == 6);
|
||||
|
||||
Iter = 2 + Iter;
|
||||
Jter = Jter - 2;
|
||||
|
||||
always_check(Iter - Jter == 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto View = Ranges::Iota(0) | Ranges::Move();
|
||||
|
||||
always_check(!View.IsEmpty());
|
||||
always_check(!!View);
|
||||
|
||||
always_check(View.Front() == 0);
|
||||
|
||||
auto First = View.Begin();
|
||||
auto Last = View.End();
|
||||
|
||||
auto ConstFirst = AsConst(View).Begin();
|
||||
auto ConstLast = AsConst(View).End();
|
||||
|
||||
always_check(First == ConstFirst);
|
||||
|
||||
ConstFirst = First;
|
||||
ConstLast = Last;
|
||||
|
||||
auto Iter = ConstFirst;
|
||||
auto Jter = ConstLast;
|
||||
|
||||
++Iter;
|
||||
|
||||
always_check(*Iter++ == 1);
|
||||
}
|
||||
}
|
||||
|
||||
void TestMiscView()
|
||||
{
|
||||
{
|
||||
TArray<int> Arr = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
TArray<int> Brr = { 0, 2, 4, 6 };
|
||||
|
||||
TArray<int> Crr = Arr
|
||||
| Ranges::Filter([](int Value) { return Value % 2 == 0; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Brr == Crr);
|
||||
}
|
||||
|
||||
{
|
||||
TArray<int> Arr = { 0, 1, 2, 2, 1, 0 };
|
||||
TArray<int> Brr = { 0, 2, 4, 4, 2, 0 };
|
||||
|
||||
TArray<int> Crr = Arr
|
||||
| Ranges::Transform([](int Value) { return Value * 2; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Brr == Crr);
|
||||
}
|
||||
|
||||
{
|
||||
TArray<int> Arr = { 0, 1, 2, 3, 3, 2, 1, 0 };
|
||||
TArray<int> Brr = { 0, 2, 4, 4, 2, 0 };
|
||||
|
||||
TArray<int> Crr = Arr
|
||||
| Ranges::Filter ([](int Value) { return Value < 3; })
|
||||
| Ranges::Transform([](int Value) { return Value * 2; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
TArray<int> Drr = Arr
|
||||
| Ranges::Transform([](int Value) { return Value * 2; })
|
||||
| Ranges::Filter ([](int Value) { return Value < 6; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Brr == Crr);
|
||||
always_check(Brr == Drr);
|
||||
}
|
||||
|
||||
{
|
||||
TArray<int> Arr = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
TArray<int> Brr = Ranges::Iota(0)
|
||||
| Ranges::Take(8)
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
TArray<int> Crr = Ranges::Iota(0)
|
||||
| Ranges::TakeWhile([](int Value) { return Value < 8; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Arr == Brr);
|
||||
always_check(Arr == Crr);
|
||||
}
|
||||
|
||||
{
|
||||
TArray<int> Arr = { 0, 4, 7, 8, 3, 1, 10 };
|
||||
TArray<int> Brr = { 0, 2, 4 };
|
||||
|
||||
TArray<int> Crr = Arr
|
||||
| Ranges::Filter ([](int Value) { return Value % 2 == 0; })
|
||||
| Ranges::Take(3)
|
||||
| Ranges::Transform([](int Value) { return Value / 2; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
TArray<int> Drr = Arr
|
||||
| Ranges::Filter ([](int Value) { return Value % 2 == 0; })
|
||||
| Ranges::TakeWhile([](int Value) { return Value < 10; })
|
||||
| Ranges::Transform([](int Value) { return Value / 2; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
TArray<int> Err = Arr
|
||||
| Ranges::Filter ([](int Value) { return Value % 2 == 0; })
|
||||
| Ranges::Transform([](int Value) { return Value / 2; })
|
||||
| Ranges::Take(3)
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
TArray<int> Frr = Arr
|
||||
| Ranges::Filter ([](int Value) { return Value % 2 == 0; })
|
||||
| Ranges::Transform([](int Value) { return Value / 2; })
|
||||
| Ranges::TakeWhile([](int Value) { return Value < 5; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
TArray<int> Grr = Arr
|
||||
| Ranges::Take(6)
|
||||
| Ranges::Filter ([](int Value) { return Value % 2 == 0; })
|
||||
| Ranges::Transform([](int Value) { return Value / 2; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
TArray<int> Hrr = Arr
|
||||
| Ranges::TakeWhile([](int Value) { return Value < 10; })
|
||||
| Ranges::Filter ([](int Value) { return Value % 2 == 0; })
|
||||
| Ranges::Transform([](int Value) { return Value / 2; })
|
||||
| Ranges::To<TArray<int>>();
|
||||
|
||||
always_check(Brr == Crr);
|
||||
always_check(Brr == Drr);
|
||||
always_check(Brr == Err);
|
||||
always_check(Brr == Frr);
|
||||
always_check(Brr == Grr);
|
||||
always_check(Brr == Hrr);
|
||||
}
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestRange()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestConversion();
|
||||
NAMESPACE_PRIVATE::TestFactory();
|
||||
NAMESPACE_PRIVATE::TestAllView();
|
||||
NAMESPACE_PRIVATE::TestMoveView();
|
||||
NAMESPACE_PRIVATE::TestMiscView();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,26 +1,20 @@
|
||||
#include "Testing/StringTesting.h"
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "String/Char.h"
|
||||
#include "Strings/Char.h"
|
||||
#include "Memory/Memory.h"
|
||||
#include "String/String.h"
|
||||
#include "String/StringView.h"
|
||||
#include "Numerics/Numerics.h"
|
||||
#include "Strings/String.h"
|
||||
#include "Strings/StringView.h"
|
||||
#include "Strings/Convert.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
void TestString()
|
||||
{
|
||||
TestChar();
|
||||
TestStringView();
|
||||
TestTemplateString();
|
||||
TestStringConversion();
|
||||
}
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
void TestChar()
|
||||
{
|
||||
@@ -34,207 +28,81 @@ void TestChar()
|
||||
always_check(CCharType<unicodechar>);
|
||||
}
|
||||
|
||||
auto Test = []<typename T>(TInPlaceType<T>)
|
||||
{
|
||||
always_check(FChar::IsAlnum(TEXT('0')));
|
||||
always_check(FChar::IsAlpha(TEXT('A')));
|
||||
always_check(FChar::IsLower(TEXT('a')));
|
||||
always_check(FChar::IsUpper(TEXT('A')));
|
||||
always_check(FChar::IsDigit(TEXT('0')));
|
||||
always_check(FChar::IsCntrl(TEXT('\n')));
|
||||
always_check(FChar::IsGraph(TEXT('!')));
|
||||
always_check(FChar::IsSpace(TEXT('\t')));
|
||||
always_check(FChar::IsBlank(TEXT(' ')));
|
||||
always_check(FChar::IsPrint(TEXT('#')));
|
||||
always_check(FChar::IsPunct(TEXT('[')));
|
||||
}
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, '0')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, 'A')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, 'a')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, 'A')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, '0')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, '\n')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, '!')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, '\t')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, ' ')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, '#')));
|
||||
always_check(TChar<T>::IsASCII(LITERAL(T, '[')));
|
||||
|
||||
{
|
||||
always_check(FWChar::IsAlnum(WTEXT('0')));
|
||||
always_check(FWChar::IsAlpha(WTEXT('A')));
|
||||
always_check(FWChar::IsLower(WTEXT('a')));
|
||||
always_check(FWChar::IsUpper(WTEXT('A')));
|
||||
always_check(FWChar::IsDigit(WTEXT('0')));
|
||||
always_check(FWChar::IsCntrl(WTEXT('\n')));
|
||||
always_check(FWChar::IsGraph(WTEXT('!')));
|
||||
always_check(FWChar::IsSpace(WTEXT('\t')));
|
||||
always_check(FWChar::IsBlank(WTEXT(' ')));
|
||||
always_check(FWChar::IsPrint(WTEXT('#')));
|
||||
always_check(FWChar::IsPunct(WTEXT('[')));
|
||||
}
|
||||
always_check(TChar<T>::IsAlnum(LITERAL(T, '0')));
|
||||
always_check(TChar<T>::IsAlpha(LITERAL(T, 'A')));
|
||||
always_check(TChar<T>::IsLower(LITERAL(T, 'a')));
|
||||
always_check(TChar<T>::IsUpper(LITERAL(T, 'A')));
|
||||
always_check(TChar<T>::IsDigit(LITERAL(T, '0')));
|
||||
always_check(TChar<T>::IsCntrl(LITERAL(T, '\n')));
|
||||
always_check(TChar<T>::IsGraph(LITERAL(T, '!')));
|
||||
always_check(TChar<T>::IsSpace(LITERAL(T, '\t')));
|
||||
always_check(TChar<T>::IsBlank(LITERAL(T, ' ')));
|
||||
always_check(TChar<T>::IsPrint(LITERAL(T, '#')));
|
||||
always_check(TChar<T>::IsPunct(LITERAL(T, '[')));
|
||||
|
||||
{
|
||||
always_check(FU8Char::IsAlnum(U8TEXT('0')));
|
||||
always_check(FU8Char::IsAlpha(U8TEXT('A')));
|
||||
always_check(FU8Char::IsLower(U8TEXT('a')));
|
||||
always_check(FU8Char::IsUpper(U8TEXT('A')));
|
||||
always_check(FU8Char::IsDigit(U8TEXT('0')));
|
||||
always_check(FU8Char::IsCntrl(U8TEXT('\n')));
|
||||
always_check(FU8Char::IsGraph(U8TEXT('!')));
|
||||
always_check(FU8Char::IsSpace(U8TEXT('\t')));
|
||||
always_check(FU8Char::IsBlank(U8TEXT(' ')));
|
||||
always_check(FU8Char::IsPrint(U8TEXT('#')));
|
||||
always_check(FU8Char::IsPunct(U8TEXT('[')));
|
||||
}
|
||||
always_check(!TChar<T>::IsAlnum(LITERAL(T, '$')));
|
||||
always_check(!TChar<T>::IsAlpha(LITERAL(T, '0')));
|
||||
always_check(!TChar<T>::IsLower(LITERAL(T, 'A')));
|
||||
always_check(!TChar<T>::IsUpper(LITERAL(T, 'a')));
|
||||
always_check(!TChar<T>::IsDigit(LITERAL(T, 'I')));
|
||||
always_check(!TChar<T>::IsCntrl(LITERAL(T, '_')));
|
||||
always_check(!TChar<T>::IsGraph(LITERAL(T, ' ')));
|
||||
always_check(!TChar<T>::IsSpace(LITERAL(T, '=')));
|
||||
always_check(!TChar<T>::IsBlank(LITERAL(T, '+')));
|
||||
always_check(!TChar<T>::IsPrint(LITERAL(T, '\n')));
|
||||
always_check(!TChar<T>::IsPunct(LITERAL(T, 'H')));
|
||||
|
||||
{
|
||||
always_check(FU16Char::IsAlnum(U16TEXT('0')));
|
||||
always_check(FU16Char::IsAlpha(U16TEXT('A')));
|
||||
always_check(FU16Char::IsLower(U16TEXT('a')));
|
||||
always_check(FU16Char::IsUpper(U16TEXT('A')));
|
||||
always_check(FU16Char::IsDigit(U16TEXT('0')));
|
||||
always_check(FU16Char::IsCntrl(U16TEXT('\n')));
|
||||
always_check(FU16Char::IsGraph(U16TEXT('!')));
|
||||
always_check(FU16Char::IsSpace(U16TEXT('\t')));
|
||||
always_check(FU16Char::IsBlank(U16TEXT(' ')));
|
||||
always_check(FU16Char::IsPrint(U16TEXT('#')));
|
||||
always_check(FU16Char::IsPunct(U16TEXT('[')));
|
||||
}
|
||||
always_check( TChar<T>::IsDigit(LITERAL(T, 'F'), 16));
|
||||
always_check(!TChar<T>::IsDigit(LITERAL(T, 'G'), 16));
|
||||
|
||||
{
|
||||
always_check(FU32Char::IsAlnum(U32TEXT('0')));
|
||||
always_check(FU32Char::IsAlpha(U32TEXT('A')));
|
||||
always_check(FU32Char::IsLower(U32TEXT('a')));
|
||||
always_check(FU32Char::IsUpper(U32TEXT('A')));
|
||||
always_check(FU32Char::IsDigit(U32TEXT('0')));
|
||||
always_check(FU32Char::IsCntrl(U32TEXT('\n')));
|
||||
always_check(FU32Char::IsGraph(U32TEXT('!')));
|
||||
always_check(FU32Char::IsSpace(U32TEXT('\t')));
|
||||
always_check(FU32Char::IsBlank(U32TEXT(' ')));
|
||||
always_check(FU32Char::IsPrint(U32TEXT('#')));
|
||||
always_check(FU32Char::IsPunct(U32TEXT('[')));
|
||||
}
|
||||
always_check(TChar<T>::ToLower(LITERAL(T, 'i')) == LITERAL(T, 'i'));
|
||||
always_check(TChar<T>::ToUpper(LITERAL(T, 'l')) == LITERAL(T, 'L'));
|
||||
|
||||
{
|
||||
always_check(!FChar::IsAlnum(TEXT('$')));
|
||||
always_check(!FChar::IsAlpha(TEXT('0')));
|
||||
always_check(!FChar::IsLower(TEXT('A')));
|
||||
always_check(!FChar::IsUpper(TEXT('a')));
|
||||
always_check(!FChar::IsDigit(TEXT('I')));
|
||||
always_check(!FChar::IsCntrl(TEXT('_')));
|
||||
always_check(!FChar::IsGraph(TEXT(' ')));
|
||||
always_check(!FChar::IsSpace(TEXT('=')));
|
||||
always_check(!FChar::IsBlank(TEXT('+')));
|
||||
always_check(!FChar::IsPrint(TEXT('\n')));
|
||||
always_check(!FChar::IsPunct(TEXT('H')));
|
||||
}
|
||||
always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0')));
|
||||
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'f')));
|
||||
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'F')));
|
||||
|
||||
{
|
||||
always_check(!FWChar::IsAlnum(WTEXT('$')));
|
||||
always_check(!FWChar::IsAlpha(WTEXT('0')));
|
||||
always_check(!FWChar::IsLower(WTEXT('A')));
|
||||
always_check(!FWChar::IsUpper(WTEXT('a')));
|
||||
always_check(!FWChar::IsDigit(WTEXT('I')));
|
||||
always_check(!FWChar::IsCntrl(WTEXT('_')));
|
||||
always_check(!FWChar::IsGraph(WTEXT(' ')));
|
||||
always_check(!FWChar::IsSpace(WTEXT('=')));
|
||||
always_check(!FWChar::IsBlank(WTEXT('+')));
|
||||
always_check(!FWChar::IsPrint(WTEXT('\n')));
|
||||
always_check(!FWChar::IsPunct(WTEXT('H')));
|
||||
}
|
||||
always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0'), false));
|
||||
always_check(0xF != TChar<T>::ToDigit(LITERAL(T, 'f'), false));
|
||||
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'F'), false));
|
||||
|
||||
{
|
||||
always_check(!FU8Char::IsAlnum(U8TEXT('$')));
|
||||
always_check(!FU8Char::IsAlpha(U8TEXT('0')));
|
||||
always_check(!FU8Char::IsLower(U8TEXT('A')));
|
||||
always_check(!FU8Char::IsUpper(U8TEXT('a')));
|
||||
always_check(!FU8Char::IsDigit(U8TEXT('I')));
|
||||
always_check(!FU8Char::IsCntrl(U8TEXT('_')));
|
||||
always_check(!FU8Char::IsGraph(U8TEXT(' ')));
|
||||
always_check(!FU8Char::IsSpace(U8TEXT('=')));
|
||||
always_check(!FU8Char::IsBlank(U8TEXT('+')));
|
||||
always_check(!FU8Char::IsPrint(U8TEXT('\n')));
|
||||
always_check(!FU8Char::IsPunct(U8TEXT('H')));
|
||||
}
|
||||
always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0'), true));
|
||||
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'f'), true));
|
||||
always_check(0xF != TChar<T>::ToDigit(LITERAL(T, 'F'), true));
|
||||
|
||||
{
|
||||
always_check(!FU16Char::IsAlnum(U16TEXT('$')));
|
||||
always_check(!FU16Char::IsAlpha(U16TEXT('0')));
|
||||
always_check(!FU16Char::IsLower(U16TEXT('A')));
|
||||
always_check(!FU16Char::IsUpper(U16TEXT('a')));
|
||||
always_check(!FU16Char::IsDigit(U16TEXT('I')));
|
||||
always_check(!FU16Char::IsCntrl(U16TEXT('_')));
|
||||
always_check(!FU16Char::IsGraph(U16TEXT(' ')));
|
||||
always_check(!FU16Char::IsSpace(U16TEXT('=')));
|
||||
always_check(!FU16Char::IsBlank(U16TEXT('+')));
|
||||
always_check(!FU16Char::IsPrint(U16TEXT('\n')));
|
||||
always_check(!FU16Char::IsPunct(U16TEXT('H')));
|
||||
}
|
||||
always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0));
|
||||
always_check(LITERAL(T, 'f') != TChar<T>::FromDigit(0xF));
|
||||
always_check(LITERAL(T, 'F') == TChar<T>::FromDigit(0xF));
|
||||
|
||||
{
|
||||
always_check(!FU32Char::IsAlnum(U32TEXT('$')));
|
||||
always_check(!FU32Char::IsAlpha(U32TEXT('0')));
|
||||
always_check(!FU32Char::IsLower(U32TEXT('A')));
|
||||
always_check(!FU32Char::IsUpper(U32TEXT('a')));
|
||||
always_check(!FU32Char::IsDigit(U32TEXT('I')));
|
||||
always_check(!FU32Char::IsCntrl(U32TEXT('_')));
|
||||
always_check(!FU32Char::IsGraph(U32TEXT(' ')));
|
||||
always_check(!FU32Char::IsSpace(U32TEXT('=')));
|
||||
always_check(!FU32Char::IsBlank(U32TEXT('+')));
|
||||
always_check(!FU32Char::IsPrint(U32TEXT('\n')));
|
||||
always_check(!FU32Char::IsPunct(U32TEXT('H')));
|
||||
}
|
||||
always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0, false));
|
||||
always_check(LITERAL(T, 'f') != TChar<T>::FromDigit(0xF, false));
|
||||
always_check(LITERAL(T, 'F') == TChar<T>::FromDigit(0xF, false));
|
||||
|
||||
{
|
||||
always_check( FChar::IsDigit(TEXT('F'), 16));
|
||||
always_check(!FChar::IsDigit(TEXT('G'), 16));
|
||||
always_check( FWChar::IsDigit(WTEXT('F'), 16));
|
||||
always_check(!FWChar::IsDigit(WTEXT('G'), 16));
|
||||
always_check( FU8Char::IsDigit(U8TEXT('F'), 16));
|
||||
always_check(!FU8Char::IsDigit(U8TEXT('G'), 16));
|
||||
always_check( FU16Char::IsDigit(U16TEXT('F'), 16));
|
||||
always_check(!FU16Char::IsDigit(U16TEXT('G'), 16));
|
||||
always_check( FU32Char::IsDigit(U32TEXT('F'), 16));
|
||||
always_check(!FU32Char::IsDigit(U32TEXT('G'), 16));
|
||||
}
|
||||
always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0, true));
|
||||
always_check(LITERAL(T, 'f') == TChar<T>::FromDigit(0xF, true));
|
||||
always_check(LITERAL(T, 'F') != TChar<T>::FromDigit(0xF, true));
|
||||
};
|
||||
|
||||
{
|
||||
always_check(FChar::ToLower(TEXT('i')) == TEXT('i'));
|
||||
always_check(FChar::ToUpper(TEXT('l')) == TEXT('L'));
|
||||
always_check(FWChar::ToUpper(WTEXT('l')) == WTEXT('L'));
|
||||
always_check(FWChar::ToLower(WTEXT('i')) == WTEXT('i'));
|
||||
always_check(FU8Char::ToLower(U8TEXT('i')) == U8TEXT('i'));
|
||||
always_check(FU8Char::ToUpper(U8TEXT('l')) == U8TEXT('L'));
|
||||
always_check(FU16Char::ToLower(U16TEXT('i')) == U16TEXT('i'));
|
||||
always_check(FU16Char::ToUpper(U16TEXT('l')) == U16TEXT('L'));
|
||||
always_check(FU32Char::ToLower(U32TEXT('i')) == U32TEXT('i'));
|
||||
always_check(FU32Char::ToUpper(U32TEXT('l')) == U32TEXT('L'));
|
||||
}
|
||||
|
||||
{
|
||||
always_check(0x0 == FChar::ToDigit(TEXT('0')));
|
||||
always_check(0xF == FChar::ToDigit(TEXT('f')));
|
||||
always_check(0xF == FChar::ToDigit(TEXT('F')));
|
||||
always_check(0x0 == FWChar::ToDigit(WTEXT('0')));
|
||||
always_check(0xF == FWChar::ToDigit(WTEXT('f')));
|
||||
always_check(0xF == FWChar::ToDigit(WTEXT('F')));
|
||||
always_check(0x0 == FU8Char::ToDigit(U8TEXT('0')));
|
||||
always_check(0xF == FU8Char::ToDigit(U8TEXT('f')));
|
||||
always_check(0xF == FU8Char::ToDigit(U8TEXT('F')));
|
||||
always_check(0x0 == FU16Char::ToDigit(U16TEXT('0')));
|
||||
always_check(0xF == FU16Char::ToDigit(U16TEXT('f')));
|
||||
always_check(0xF == FU16Char::ToDigit(U16TEXT('F')));
|
||||
always_check(0x0 == FU16Char::ToDigit(U16TEXT('0')));
|
||||
always_check(0xF == FU16Char::ToDigit(U16TEXT('f')));
|
||||
always_check(0xF == FU16Char::ToDigit(U16TEXT('F')));
|
||||
}
|
||||
|
||||
{
|
||||
always_check(TEXT('0') == FChar::FromDigit(0x0));
|
||||
always_check(TEXT('f') != FChar::FromDigit(0xF));
|
||||
always_check(TEXT('F') == FChar::FromDigit(0xF));
|
||||
always_check(WTEXT('0') == FWChar::FromDigit(0x0));
|
||||
always_check(WTEXT('f') != FWChar::FromDigit(0xF));
|
||||
always_check(WTEXT('F') == FWChar::FromDigit(0xF));
|
||||
always_check(U8TEXT('0') == FU8Char::FromDigit(0x0));
|
||||
always_check(U8TEXT('f') != FU8Char::FromDigit(0xF));
|
||||
always_check(U8TEXT('F') == FU8Char::FromDigit(0xF));
|
||||
always_check(U16TEXT('0') == FU16Char::FromDigit(0x0));
|
||||
always_check(U16TEXT('f') != FU16Char::FromDigit(0xF));
|
||||
always_check(U16TEXT('F') == FU16Char::FromDigit(0xF));
|
||||
always_check(U16TEXT('0') == FU16Char::FromDigit(0x0));
|
||||
always_check(U16TEXT('f') != FU16Char::FromDigit(0xF));
|
||||
always_check(U16TEXT('F') == FU16Char::FromDigit(0xF));
|
||||
}
|
||||
Test(InPlaceType<char>);
|
||||
Test(InPlaceType<wchar>);
|
||||
Test(InPlaceType<u8char>);
|
||||
Test(InPlaceType<u16char>);
|
||||
Test(InPlaceType<u32char>);
|
||||
Test(InPlaceType<unicodechar>);
|
||||
}
|
||||
|
||||
void TestStringView()
|
||||
@@ -317,6 +185,24 @@ void TestStringView()
|
||||
always_check(View.FindLastNotOf(LITERAL(T, "Hello! Goodbye!")) == 25);
|
||||
always_check(View.FindLastNotOf(LITERAL(T, '!')) == 27);
|
||||
}
|
||||
|
||||
{
|
||||
always_check(LITERAL_VIEW(T, " ABC ").TrimStart() == LITERAL(T, "ABC "));
|
||||
always_check(LITERAL_VIEW(T, " ABC ").TrimEnd() == LITERAL(T, " ABC" ));
|
||||
always_check(LITERAL_VIEW(T, " ABC ").TrimStartAndEnd() == LITERAL(T, "ABC" ));
|
||||
|
||||
always_check(LITERAL_VIEW(T, " A\0C ").TrimToNullTerminator() == LITERAL(T, " A"));
|
||||
}
|
||||
|
||||
{
|
||||
always_check( LITERAL_VIEW(T, "012345678900").IsASCII());
|
||||
always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").IsASCII());
|
||||
|
||||
always_check( LITERAL_VIEW(T, "012345678900").template IsInteger<uint64>(10));
|
||||
always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").template IsInteger<uint64>(10));
|
||||
always_check(!LITERAL_VIEW(T, "0123456789AB").template IsInteger<uint64>(10));
|
||||
always_check( LITERAL_VIEW(T, "0123456789AB").template IsInteger<uint64>(16));
|
||||
}
|
||||
};
|
||||
|
||||
Test(InPlaceType<char>);
|
||||
@@ -327,7 +213,7 @@ void TestStringView()
|
||||
Test(InPlaceType<unicodechar>);
|
||||
}
|
||||
|
||||
void TestTemplateString()
|
||||
void TestString()
|
||||
{
|
||||
auto Test = []<typename T>(TInPlaceType<T>)
|
||||
{
|
||||
@@ -538,6 +424,14 @@ void TestTemplateString()
|
||||
always_check(Str.FindLastNotOf(LITERAL(T, '!')) == 27);
|
||||
}
|
||||
|
||||
{
|
||||
always_check(TString(LITERAL(T, " ABC ")).TrimStart() == LITERAL(T, "ABC "));
|
||||
always_check(TString(LITERAL(T, " ABC ")).TrimEnd() == LITERAL(T, " ABC" ));
|
||||
always_check(TString(LITERAL(T, " ABC ")).TrimStartAndEnd() == LITERAL(T, "ABC" ));
|
||||
|
||||
always_check(TString(LITERAL(T, " A\0C ")).TrimToNullTerminator() == LITERAL(T, " A"));
|
||||
}
|
||||
|
||||
{
|
||||
always_check(TString(LITERAL(T, "\u4E38\u8FA3")).ToString() == TEXT("\u4E38\u8FA3"));
|
||||
always_check(TString(LITERAL(T, "\u4E38\u8FA3")).ToWString() == WTEXT("\u4E38\u8FA3"));
|
||||
@@ -556,48 +450,28 @@ void TestTemplateString()
|
||||
Test(InPlaceType<unicodechar>);
|
||||
}
|
||||
|
||||
void TestStringConversion()
|
||||
void TestConvert()
|
||||
{
|
||||
auto Test = []<typename T>(TInPlaceType<T>)
|
||||
{
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), true ) == LITERAL(T, "#True#" ));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), false) == LITERAL(T, "#False#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +0) == LITERAL(T, "#0#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 0) == LITERAL(T, "#0#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -0) == LITERAL(T, "#0#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 42) == LITERAL(T, "#42#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +0.0) == LITERAL(T, "#0.000000#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 0.0) == LITERAL(T, "#0.000000#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -0.0) == LITERAL(T, "#-0.000000#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 3.140000) == LITERAL(T, "#3.140000#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +NAMESPACE_STD::numeric_limits<float>::infinity()) == LITERAL(T, "#Infinity#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -NAMESPACE_STD::numeric_limits<float>::infinity()) == LITERAL(T, "#-Infinity#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +NAMESPACE_STD::numeric_limits<float>::quiet_NaN()) == LITERAL(T, "#NaN#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -NAMESPACE_STD::numeric_limits<float>::quiet_NaN()) == LITERAL(T, "#-NaN#"));
|
||||
|
||||
auto CheckParseArithmetic = []<typename U>(TStringView<T> View, U Result)
|
||||
{
|
||||
U Object;
|
||||
|
||||
always_check(View.Parse(LITERAL(T, "{0:}"), Object) == 1);
|
||||
if constexpr (CSameAs<U, bool>) always_check(View.Parse(Object));
|
||||
else if constexpr (CIntegral<U>) always_check(View.Parse(Object));
|
||||
else if constexpr (CFloatingPoint<U>) always_check(View.Parse(Object));
|
||||
|
||||
if constexpr (CFloatingPoint<U>)
|
||||
{
|
||||
always_check(NAMESPACE_STD::isinf(Result) == NAMESPACE_STD::isinf(Object));
|
||||
always_check(NAMESPACE_STD::isnan(Result) == NAMESPACE_STD::isnan(Object));
|
||||
always_check(Math::IsInfinity(Result) == Math::IsInfinity(Object));
|
||||
always_check(Math::IsNaN(Result) == Math::IsNaN(Object));
|
||||
|
||||
always_check(NAMESPACE_STD::signbit(Result) == NAMESPACE_STD::signbit(Object));
|
||||
always_check(Math::IsNegative(Result) == Math::IsNegative(Object));
|
||||
|
||||
if (NAMESPACE_STD::isinf(Result) || NAMESPACE_STD::isnan(Result)) return;
|
||||
if (Math::IsInfinity(Result) || Math::IsNaN(Result)) return;
|
||||
|
||||
constexpr auto Epsilon = 1e-3;
|
||||
|
||||
always_check(NAMESPACE_STD::abs(Object - Result) < Epsilon);
|
||||
always_check(Math::IsNearlyEqual(Object, Result, 1e-4));
|
||||
}
|
||||
else always_check(Object == Result);
|
||||
};
|
||||
@@ -627,22 +501,13 @@ void TestStringConversion()
|
||||
CheckParseArithmetic(LITERAL(T, "-0b101010"), static_cast<U>(-0b101010));
|
||||
};
|
||||
|
||||
CheckParseInt(InPlaceType<bool>);
|
||||
|
||||
CheckParseInt(InPlaceType<int8>);
|
||||
CheckParseInt(InPlaceType<int16>);
|
||||
CheckParseInt(InPlaceType<int32>);
|
||||
CheckParseInt(InPlaceType<int64>);
|
||||
|
||||
CheckParseInt(InPlaceType<uint8>);
|
||||
CheckParseInt(InPlaceType<uint16>);
|
||||
CheckParseInt(InPlaceType<uint32>);
|
||||
CheckParseInt(InPlaceType<uint64>);
|
||||
|
||||
auto CheckParseFloat = [&]<typename U>(TInPlaceType<U>)
|
||||
{
|
||||
CheckParseInt(InPlaceType<U>);
|
||||
|
||||
CheckParseArithmetic(LITERAL(T, "+3.14"), static_cast<U>( +3.14));
|
||||
CheckParseArithmetic(LITERAL(T, "+3.14e2"), static_cast<U>( +3.14e2));
|
||||
CheckParseArithmetic(LITERAL(T, "+3.14e-2"), static_cast<U>( +3.14e-2));
|
||||
@@ -658,18 +523,38 @@ void TestStringConversion()
|
||||
CheckParseArithmetic(LITERAL(T, "-3.14e-2"), static_cast<U>( -3.14e-2));
|
||||
CheckParseArithmetic(LITERAL(T, "-0x1.91eb86p1"), static_cast<U>(-0x1.91eb86p1));
|
||||
|
||||
CheckParseArithmetic(LITERAL(T, "+Infinity"), +NAMESPACE_STD::numeric_limits<U>::infinity());
|
||||
CheckParseArithmetic(LITERAL(T, " Infinity"), +NAMESPACE_STD::numeric_limits<U>::infinity());
|
||||
CheckParseArithmetic(LITERAL(T, "-Infinity"), -NAMESPACE_STD::numeric_limits<U>::infinity());
|
||||
CheckParseArithmetic(LITERAL(T, "+Infinity"), +TNumericLimits<U>::Infinity());
|
||||
CheckParseArithmetic(LITERAL(T, " Infinity"), +TNumericLimits<U>::Infinity());
|
||||
CheckParseArithmetic(LITERAL(T, "-Infinity"), -TNumericLimits<U>::Infinity());
|
||||
|
||||
CheckParseArithmetic(LITERAL(T, "+NaN"), +NAMESPACE_STD::numeric_limits<U>::quiet_NaN());
|
||||
CheckParseArithmetic(LITERAL(T, " NaN"), +NAMESPACE_STD::numeric_limits<U>::quiet_NaN());
|
||||
CheckParseArithmetic(LITERAL(T, "-NaN"), -NAMESPACE_STD::numeric_limits<U>::quiet_NaN());
|
||||
CheckParseArithmetic(LITERAL(T, "+NaN"), +TNumericLimits<U>::QuietNaN());
|
||||
CheckParseArithmetic(LITERAL(T, " NaN"), +TNumericLimits<U>::QuietNaN());
|
||||
CheckParseArithmetic(LITERAL(T, "-NaN"), -TNumericLimits<U>::QuietNaN());
|
||||
};
|
||||
|
||||
CheckParseFloat(InPlaceType<float>);
|
||||
CheckParseFloat(InPlaceType<double>);
|
||||
CheckParseFloat(InPlaceType<long double>);
|
||||
|
||||
{
|
||||
always_check( LITERAL_VIEW(T, "true" ).ToBool());
|
||||
always_check(!LITERAL_VIEW(T, "false").ToBool());
|
||||
always_check( LITERAL_VIEW(T, "True" ).ToBool());
|
||||
always_check(!LITERAL_VIEW(T, "False").ToBool());
|
||||
}
|
||||
|
||||
{
|
||||
always_check(LITERAL_VIEW(T, "42" ).ToInt() == 42 );
|
||||
always_check(LITERAL_VIEW(T, "FF" ).ToInt(16) == 255);
|
||||
always_check(LITERAL_VIEW(T, "-42").ToInt() == -42);
|
||||
always_check(LITERAL_VIEW(T, "0" ).ToInt() == 0 );
|
||||
}
|
||||
|
||||
{
|
||||
always_check(LITERAL_VIEW(T, "3.14" ).ToFloat() == 3.14f);
|
||||
always_check(LITERAL_VIEW(T, "3.14e+00").ToFloat() == 3.14f);
|
||||
always_check(LITERAL_VIEW(T, "-3.14" ).ToFloat() == -3.14f);
|
||||
always_check(LITERAL_VIEW(T, "0.0" ).ToFloat() == 0.0f);
|
||||
}
|
||||
};
|
||||
|
||||
Test(InPlaceType<char>);
|
||||
@@ -680,6 +565,84 @@ void TestStringConversion()
|
||||
Test(InPlaceType<unicodechar>);
|
||||
}
|
||||
|
||||
void TestFormatting()
|
||||
{
|
||||
auto Test = []<typename T>(TInPlaceType<T>)
|
||||
{
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), LITERAL(T, "Hello, World!")) == LITERAL(T, "#Hello, World!#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), true ) == LITERAL(T, "#True#" ));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), false) == LITERAL(T, "#False#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), +0) == LITERAL(T, "#0#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), 0) == LITERAL(T, "#0#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), -0) == LITERAL(T, "#0#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), 42) == LITERAL(T, "#42#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), +0.0) == LITERAL(T, "#0#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), 0.0) == LITERAL(T, "#0#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), -0.0) == LITERAL(T, "#-0#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), 3.14) == LITERAL(T, "#3.14#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0:.6F}#"), +0.0) == LITERAL(T, "#0.000000#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0:.6F}#"), 0.0) == LITERAL(T, "#0.000000#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0:.6F}#"), -0.0) == LITERAL(T, "#-0.000000#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0:.6F}#"), 3.14) == LITERAL(T, "#3.140000#"));
|
||||
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), +TNumericLimits<float>::Infinity()) == LITERAL(T, "#Infinity#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), -TNumericLimits<float>::Infinity()) == LITERAL(T, "#-Infinity#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), +TNumericLimits<float>::QuietNaN()) == LITERAL(T, "#NaN#"));
|
||||
always_check(TString<T>::Format(LITERAL(T, "#{0}#"), -TNumericLimits<float>::QuietNaN()) == LITERAL(T, "#-NaN#"));
|
||||
|
||||
{
|
||||
always_check(TString<T>::FromBool(true ) == LITERAL(T, "True" ));
|
||||
always_check(TString<T>::FromBool(false) == LITERAL(T, "False"));
|
||||
}
|
||||
|
||||
{
|
||||
always_check(TString<T>::FromInt(42) == LITERAL(T, "42" ));
|
||||
always_check(TString<T>::FromInt(255, 16) == LITERAL(T, "FF" ));
|
||||
always_check(TString<T>::FromInt(-42) == LITERAL(T, "-42" ));
|
||||
always_check(TString<T>::FromInt(0) == LITERAL(T, "0" ));
|
||||
always_check(TString<T>::FromInt(1234567890) == LITERAL(T, "1234567890"));
|
||||
always_check(TString<T>::FromInt(255, 2) == LITERAL(T, "11111111" ));
|
||||
always_check(TString<T>::FromInt(255, 8) == LITERAL(T, "377" ));
|
||||
always_check(TString<T>::FromInt(255, 36) == LITERAL(T, "73" ));
|
||||
}
|
||||
|
||||
{
|
||||
always_check(TString<T>::FromFloat(3.14f) == LITERAL(T, "3.14" ));
|
||||
always_check(TString<T>::FromFloat(0.0f) == LITERAL(T, "0" ));
|
||||
always_check(TString<T>::FromFloat(-3.14f) == LITERAL(T, "-3.14" ));
|
||||
always_check(TString<T>::FromFloat(3.14f, true, false) == LITERAL(T, "3.14" ));
|
||||
always_check(TString<T>::FromFloat(3.14f, false, true) == LITERAL(T, "3.14e+00"));
|
||||
always_check(TString<T>::FromFloat(3.14f, false, false, 2) == LITERAL(T, "1.92p+1" ));
|
||||
always_check(TString<T>::FromFloat(1.0f / 3.0f, true, false, 5) == LITERAL(T, "0.33333" ));
|
||||
}
|
||||
};
|
||||
|
||||
Test(InPlaceType<char>);
|
||||
Test(InPlaceType<wchar>);
|
||||
Test(InPlaceType<u8char>);
|
||||
Test(InPlaceType<u16char>);
|
||||
Test(InPlaceType<u32char>);
|
||||
Test(InPlaceType<unicodechar>);
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestString()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestChar();
|
||||
NAMESPACE_PRIVATE::TestStringView();
|
||||
NAMESPACE_PRIVATE::TestString();
|
||||
NAMESPACE_PRIVATE::TestConvert();
|
||||
NAMESPACE_PRIVATE::TestFormatting();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Testing/TemplatesTesting.h"
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
@@ -14,20 +14,7 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
void TestTemplates()
|
||||
{
|
||||
TestInvoke();
|
||||
TestReferenceWrapper();
|
||||
TestOptional();
|
||||
TestVariant();
|
||||
TestAny();
|
||||
TestTuple();
|
||||
TestFunction();
|
||||
TestAtomic();
|
||||
TestScopeHelper();
|
||||
TestPropagateConst();
|
||||
TestMiscTemplates();
|
||||
}
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
NAMESPACE_UNNAMED_BEGIN
|
||||
|
||||
@@ -64,9 +51,9 @@ void TestInvoke()
|
||||
|
||||
void TestReferenceWrapper()
|
||||
{
|
||||
typedef int32(*FuncType)(int32, int32, int32);
|
||||
FuncType TempA = [](int32 A, int32 B, int32 C) -> int32 { return A * B * C; };
|
||||
TReferenceWrapper<FuncType> TempB(TempA);
|
||||
typedef int32(*FFuncType)(int32, int32, int32);
|
||||
FFuncType TempA = [](int32 A, int32 B, int32 C) -> int32 { return A * B * C; };
|
||||
TReferenceWrapper<FFuncType> TempB(TempA);
|
||||
always_check(TempB(1, 1, 1) == 1);
|
||||
TempB.Get() = &TestFunctionA;
|
||||
always_check(TempA(1, 1, 1) == 3);
|
||||
@@ -330,14 +317,15 @@ void TestVariant()
|
||||
}
|
||||
|
||||
{
|
||||
using VariantType = TVariant<int32, int64, float64>;
|
||||
VariantType TempArray[] = { 10, 15ll, 1.5 };
|
||||
using FVariantType = TVariant<int32, int64, float64>;
|
||||
FVariantType TempArray[] = { 10, 15ll, 1.5 };
|
||||
|
||||
for(auto&& TempA : TempArray)
|
||||
{
|
||||
Visit(
|
||||
[](auto&& A)
|
||||
{
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
using T = TRemoveCVRef<decltype(A)>;
|
||||
if constexpr (CSameAs<T, int32>) always_check(A == 10);
|
||||
else if constexpr (CSameAs<T, int64>) always_check(A == 15ll);
|
||||
@@ -347,11 +335,12 @@ void TestVariant()
|
||||
TempA
|
||||
);
|
||||
|
||||
VariantType TempB = Visit([](auto&& A) -> VariantType { return A + A; }, TempA);
|
||||
FVariantType TempB = Visit([](auto&& A) -> FVariantType { return A + A; }, TempA);
|
||||
|
||||
Visit(
|
||||
[](auto&& A, auto&& B)
|
||||
{
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
using T = TRemoveCVRef<decltype(A)>;
|
||||
if constexpr (CSameAs<T, int32>) always_check(A == 10 && B == 20);
|
||||
else if constexpr (CSameAs<T, int64>) always_check(A == 15ll && B == 30ll);
|
||||
@@ -366,6 +355,7 @@ void TestVariant()
|
||||
Visit(
|
||||
[](auto&& A)
|
||||
{
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
using T = TRemoveCVRef<decltype(A)>;
|
||||
if constexpr (CSameAs<T, int32>) always_check(A == 20);
|
||||
else if constexpr (CSameAs<T, int64>) always_check(A == 30ll);
|
||||
@@ -417,6 +407,7 @@ void TestVariant()
|
||||
|
||||
auto TestQualifiers = [&bIsConst, &bIsLValue, &bIsRValue](auto&& A) -> int32
|
||||
{
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
using T = decltype(A);
|
||||
always_check(A == 10);
|
||||
always_check(CConst<TRemoveReference<T>> == bIsConst);
|
||||
@@ -429,6 +420,8 @@ void TestVariant()
|
||||
bIsLValue = true;
|
||||
bIsRValue = false;
|
||||
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
|
||||
TVariant<int32> TempLA = 10;
|
||||
auto ReturnLA = Visit(TestQualifiers, TempLA);
|
||||
always_check((CSameAs<int32, decltype(ReturnLA)>));
|
||||
@@ -488,6 +481,8 @@ void TestVariant()
|
||||
const TVariant<int32> TempRD = TempLC;
|
||||
auto ReturnRD = Visit<int32>(TestQualifiers, MoveTemp(TempRD));
|
||||
always_check((CSameAs<int32, decltype(ReturnRD)>));
|
||||
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
}
|
||||
|
||||
{
|
||||
@@ -869,11 +864,11 @@ void TestTuple()
|
||||
// always_check((CSameAs<TTupleElement<4, TTuple<double, float&, char&&>>, double>));
|
||||
|
||||
{
|
||||
using Type = TTuple<int8, uint8, int16, uint16, int32, uint32, int64, uint64, int8, uint8, int16, uint16, int32, uint32, int64, uint64,
|
||||
using FType = TTuple<int8, uint8, int16, uint16, int32, uint32, int64, uint64, int8, uint8, int16, uint16, int32, uint32, int64, uint64,
|
||||
int8, uint8, int16, uint16, int32, uint32, int64, uint64, int8, uint8, int16, uint16, int32, uint32, int64, uint64,
|
||||
int8, uint8, int16, uint16, int32, uint32, int64, uint64, int8, uint8, int16, uint16, int32, uint32, int64, uint64>;
|
||||
|
||||
Type Temp;
|
||||
FType Temp;
|
||||
|
||||
Temp.First = 0;
|
||||
Temp.Second = 0;
|
||||
@@ -892,20 +887,20 @@ void TestTuple()
|
||||
Temp.Fifteenth = 0;
|
||||
Temp.Sixteenth = 0;
|
||||
|
||||
always_check(CDefaultConstructible<Type>);
|
||||
always_check(CTriviallyDefaultConstructible<Type>);
|
||||
always_check(CConstructibleFrom<Type>);
|
||||
always_check(CTriviallyConstructibleFrom<Type>);
|
||||
always_check(CCopyConstructible<Type>);
|
||||
always_check(CTriviallyCopyConstructible<Type>);
|
||||
always_check(CMoveConstructible<Type>);
|
||||
always_check(CTriviallyMoveConstructible<Type>);
|
||||
always_check(CCopyAssignable<Type>);
|
||||
always_check(CTriviallyCopyAssignable<Type>);
|
||||
always_check(CMoveAssignable<Type>);
|
||||
always_check(CTriviallyMoveAssignable<Type>);
|
||||
always_check(CDestructible<Type>);
|
||||
always_check(CTriviallyDestructible<Type>);
|
||||
always_check(CDefaultConstructible<FType>);
|
||||
always_check(CTriviallyDefaultConstructible<FType>);
|
||||
always_check(CConstructibleFrom<FType>);
|
||||
always_check(CTriviallyConstructibleFrom<FType>);
|
||||
always_check(CCopyConstructible<FType>);
|
||||
always_check(CTriviallyCopyConstructible<FType>);
|
||||
always_check(CMoveConstructible<FType>);
|
||||
always_check(CTriviallyMoveConstructible<FType>);
|
||||
always_check(CCopyAssignable<FType>);
|
||||
always_check(CTriviallyCopyAssignable<FType>);
|
||||
always_check(CMoveAssignable<FType>);
|
||||
always_check(CTriviallyMoveAssignable<FType>);
|
||||
always_check(CDestructible<FType>);
|
||||
always_check(CTriviallyDestructible<FType>);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1006,7 +1001,7 @@ void TestTuple()
|
||||
{
|
||||
TTuple<int32, char> TempA = { 1, 'A' };
|
||||
|
||||
TempA.Visit([](auto&& A) { A++; });
|
||||
TempA.Visit([](auto&& A) -> void { ++A; });
|
||||
|
||||
TempA.Visit(
|
||||
[]<typename T> (T&& A)
|
||||
@@ -1084,25 +1079,25 @@ struct FFunctionDebug
|
||||
void Print(int32 In) { Output[Index++] = In; }
|
||||
};
|
||||
|
||||
FFunctionDebug FunctionDebug;
|
||||
FFunctionDebug GFunctionDebug;
|
||||
|
||||
struct FPrintAdd
|
||||
{
|
||||
FPrintAdd(int32 InNum) : Num(InNum) { }
|
||||
void F(int32 I) const { FunctionDebug.Print(Num + I); }
|
||||
void F(int32 I) const { GFunctionDebug.Print(Num + I); }
|
||||
int32 Num;
|
||||
};
|
||||
|
||||
void PrintNum(int32 I)
|
||||
{
|
||||
FunctionDebug.Print(I);
|
||||
GFunctionDebug.Print(I);
|
||||
}
|
||||
|
||||
struct FPrintNum
|
||||
{
|
||||
void operator()(int32 I) const
|
||||
{
|
||||
FunctionDebug.Print(I);
|
||||
GFunctionDebug.Print(I);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1310,7 +1305,7 @@ void TestFunction()
|
||||
AddDisplay(314159, 1);
|
||||
|
||||
TFunction<int32(FPrintAdd const&)> Num = &FPrintAdd::Num;
|
||||
FunctionDebug.Print(Num(Foo));
|
||||
GFunctionDebug.Print(Num(Foo));
|
||||
|
||||
TFunction<void(int32)> AddDisplay2 = [Foo](int32 A) { Foo.F(A); };
|
||||
AddDisplay2(2);
|
||||
@@ -1326,21 +1321,21 @@ void TestFunction()
|
||||
return Fac(N);
|
||||
};
|
||||
|
||||
for (int32 I = 5; I < 8; ++I) FunctionDebug.Print(Factorial(I));
|
||||
for (int32 I = 5; I < 8; ++I) GFunctionDebug.Print(Factorial(I));
|
||||
|
||||
always_check(FunctionDebug.Index == 12);
|
||||
always_check(FunctionDebug.Output[0] == -9);
|
||||
always_check(FunctionDebug.Output[1] == 42);
|
||||
always_check(FunctionDebug.Output[2] == 31337);
|
||||
always_check(FunctionDebug.Output[3] == 314160);
|
||||
always_check(FunctionDebug.Output[4] == 314160);
|
||||
always_check(FunctionDebug.Output[5] == 314159);
|
||||
always_check(FunctionDebug.Output[6] == 314161);
|
||||
always_check(FunctionDebug.Output[7] == 314162);
|
||||
always_check(FunctionDebug.Output[8] == 18);
|
||||
always_check(FunctionDebug.Output[9] == 120);
|
||||
always_check(FunctionDebug.Output[10] == 720);
|
||||
always_check(FunctionDebug.Output[11] == 5040);
|
||||
always_check(GFunctionDebug.Index == 12);
|
||||
always_check(GFunctionDebug.Output[0] == -9);
|
||||
always_check(GFunctionDebug.Output[1] == 42);
|
||||
always_check(GFunctionDebug.Output[2] == 31337);
|
||||
always_check(GFunctionDebug.Output[3] == 314160);
|
||||
always_check(GFunctionDebug.Output[4] == 314160);
|
||||
always_check(GFunctionDebug.Output[5] == 314159);
|
||||
always_check(GFunctionDebug.Output[6] == 314161);
|
||||
always_check(GFunctionDebug.Output[7] == 314162);
|
||||
always_check(GFunctionDebug.Output[8] == 18);
|
||||
always_check(GFunctionDebug.Output[9] == 120);
|
||||
always_check(GFunctionDebug.Output[10] == 720);
|
||||
always_check(GFunctionDebug.Output[11] == 5040);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1613,6 +1608,23 @@ void TestMiscTemplates()
|
||||
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
void TestTemplates()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestInvoke();
|
||||
NAMESPACE_PRIVATE::TestReferenceWrapper();
|
||||
NAMESPACE_PRIVATE::TestOptional();
|
||||
NAMESPACE_PRIVATE::TestVariant();
|
||||
NAMESPACE_PRIVATE::TestAny();
|
||||
NAMESPACE_PRIVATE::TestTuple();
|
||||
NAMESPACE_PRIVATE::TestFunction();
|
||||
NAMESPACE_PRIVATE::TestAtomic();
|
||||
NAMESPACE_PRIVATE::TestScopeHelper();
|
||||
NAMESPACE_PRIVATE::TestPropagateConst();
|
||||
NAMESPACE_PRIVATE::TestMiscTemplates();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Testing/TypeTraitsTesting.h"
|
||||
#include "Testing/Testing.h"
|
||||
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
@@ -14,7 +14,7 @@ NAMESPACE_BEGIN(Testing)
|
||||
|
||||
NAMESPACE_UNNAMED_BEGIN
|
||||
|
||||
int32 TestObject;
|
||||
int32 GTestObject;
|
||||
void TestFunction() { }
|
||||
|
||||
struct FTestStructA { };
|
||||
5
Redcraft.Utility/Source/Public/Algorithms/Algorithms.h
Normal file
5
Redcraft.Utility/Source/Public/Algorithms/Algorithms.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Algorithms/Basic.h"
|
||||
#include "Algorithms/Search.h"
|
||||
188
Redcraft.Utility/Source/Public/Algorithms/Basic.h
Normal file
188
Redcraft.Utility/Source/Public/Algorithms/Basic.h
Normal file
@@ -0,0 +1,188 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Numerics/Math.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Algorithms)
|
||||
|
||||
/** Increments given iterator 'Iter' by 'N' elements. */
|
||||
template <CInputOrOutputIterator I>
|
||||
FORCEINLINE constexpr void Advance(I& Iter, ptrdiff N)
|
||||
{
|
||||
if constexpr (CRandomAccessIterator<I>)
|
||||
{
|
||||
Iter += N;
|
||||
}
|
||||
|
||||
else if constexpr (CBidirectionalIterator<I>)
|
||||
{
|
||||
for (; N > 0; --N) ++Iter;
|
||||
for (; N < 0; ++N) --Iter;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
checkf(N >= 0, TEXT("The iterator must satisfy the CBidirectionalIterator in order to be decremented."));
|
||||
|
||||
for (; N > 0; --N) ++Iter;
|
||||
}
|
||||
}
|
||||
|
||||
/** Increments given iterator 'Iter' to the 'Sent' position. */
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S>
|
||||
FORCEINLINE constexpr void Advance(I& Iter, S Sent)
|
||||
{
|
||||
if constexpr (CAssignableFrom<I&, S>)
|
||||
{
|
||||
Iter = Sent;
|
||||
}
|
||||
|
||||
else if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
Algorithms::Advance(Iter, Sent - Iter);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
for (; Iter != Sent; ++Iter);
|
||||
}
|
||||
}
|
||||
|
||||
/** Increments given iterator 'Iter' by 'N' elements, up to the 'Sent' position. */
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S>
|
||||
FORCEINLINE constexpr ptrdiff Advance(I& Iter, ptrdiff N, S Sent)
|
||||
{
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
const ptrdiff Distance = Sent - Iter;
|
||||
|
||||
if (Math::Abs(N) > Math::Abs(Distance))
|
||||
{
|
||||
Algorithms::Advance(Iter, Sent);
|
||||
|
||||
return N - Distance;
|
||||
}
|
||||
|
||||
Algorithms::Advance(Iter, N);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
else if constexpr (CBidirectionalIterator<I>)
|
||||
{
|
||||
for (; N > 0 && Iter != Sent; --N) ++Iter;
|
||||
for (; N < 0 && Iter != Sent; ++N) --Iter;
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
checkf(N >= 0, TEXT("The iterator must satisfy the CBidirectionalIterator in order to be decremented."));
|
||||
|
||||
for (; N > 0 && Iter != Sent; --N) ++Iter;
|
||||
|
||||
return N;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return The number of hops from 'First' to 'Last'. */
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S>
|
||||
NODISCARD FORCEINLINE constexpr ptrdiff Distance(I First, S Last)
|
||||
{
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
return Last - First;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ptrdiff Result = 0;
|
||||
|
||||
for (; First != Last; ++First) ++Result;
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return The size of the range. The range will not be modified if it is a sized range. */
|
||||
template <CRange R>
|
||||
NODISCARD FORCEINLINE constexpr ptrdiff Distance(R&& Range)
|
||||
{
|
||||
if constexpr (CSizedRange<R&>)
|
||||
{
|
||||
return static_cast<ptrdiff>(Ranges::Num(Range));
|
||||
}
|
||||
|
||||
else return Algorithms::Distance(Ranges::Begin(Range), Ranges::End(Range));
|
||||
}
|
||||
|
||||
/** @return The 1-th successor of iterator 'Iter'. */
|
||||
template <CInputOrOutputIterator I>
|
||||
NODISCARD FORCEINLINE constexpr I Next(I Iter)
|
||||
{
|
||||
return ++Iter;
|
||||
}
|
||||
|
||||
/** @return The 'N'-th successor of iterator 'Iter'. */
|
||||
template <CInputOrOutputIterator I>
|
||||
NODISCARD FORCEINLINE constexpr I Next(I Iter, ptrdiff N)
|
||||
{
|
||||
Algorithms::Advance(Iter, N);
|
||||
return Iter;
|
||||
}
|
||||
|
||||
/** @return The successor of iterator 'Iter' to the 'Sent' position. */
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S>
|
||||
NODISCARD FORCEINLINE constexpr I Next(I Iter, S Sent)
|
||||
{
|
||||
Algorithms::Advance(Iter, Sent);
|
||||
return Iter;
|
||||
}
|
||||
|
||||
/** @return The 'N'-th successor of iterator 'Iter', up to the 'Sent' position. */
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S>
|
||||
NODISCARD FORCEINLINE constexpr I Next(I Iter, ptrdiff N, S Sent)
|
||||
{
|
||||
Algorithms::Advance(Iter, N, Sent);
|
||||
return Iter;
|
||||
}
|
||||
|
||||
/** @return The 1-th predecessor of iterator 'Iter'. */
|
||||
template <CBidirectionalIterator I>
|
||||
NODISCARD FORCEINLINE constexpr I Prev(I Iter)
|
||||
{
|
||||
return --Iter;
|
||||
}
|
||||
|
||||
/** @return The 'N'-th predecessor of iterator 'Iter'. */
|
||||
template <CBidirectionalIterator I>
|
||||
NODISCARD FORCEINLINE constexpr I Prev(I Iter, ptrdiff N)
|
||||
{
|
||||
Algorithms::Advance(Iter, -N);
|
||||
return Iter;
|
||||
}
|
||||
|
||||
/** @return The predecessor of iterator 'Iter', up to the 'First' position. */
|
||||
template <CBidirectionalIterator I>
|
||||
NODISCARD FORCEINLINE constexpr I Prev(I Iter, ptrdiff N, I First)
|
||||
{
|
||||
Algorithms::Advance(Iter, -N, First);
|
||||
return Iter;
|
||||
}
|
||||
|
||||
NAMESPACE_END(Algorithms)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
1755
Redcraft.Utility/Source/Public/Algorithms/Search.h
Normal file
1755
Redcraft.Utility/Source/Public/Algorithms/Search.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Memory/Allocator.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Memory/Allocators.h"
|
||||
#include "Memory/MemoryOperator.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/Factory.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
#include "Miscellaneous/ConstantIterator.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
@@ -27,48 +30,54 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using AllocatorType = Allocator;
|
||||
using FElementType = T;
|
||||
using FAllocatorType = Allocator;
|
||||
|
||||
using Reference = T&;
|
||||
using ConstReference = const T&;
|
||||
using FReference = T&;
|
||||
using FConstReference = const T&;
|
||||
|
||||
using Iterator = TIteratorImpl<false>;
|
||||
using ConstIterator = TIteratorImpl<true >;
|
||||
using FIterator = TIteratorImpl<false>;
|
||||
using FConstIterator = TIteratorImpl<true >;
|
||||
|
||||
using ReverseIterator = TReverseIterator< Iterator>;
|
||||
using ConstReverseIterator = TReverseIterator<ConstIterator>;
|
||||
using FReverseIterator = TReverseIterator< FIterator>;
|
||||
using FConstReverseIterator = TReverseIterator<FConstIterator>;
|
||||
|
||||
static_assert(CContiguousIterator< Iterator>);
|
||||
static_assert(CContiguousIterator<ConstIterator>);
|
||||
static_assert(CContiguousIterator< FIterator>);
|
||||
static_assert(CContiguousIterator<FConstIterator>);
|
||||
|
||||
/** Default constructor. Constructs an empty container with a default-constructed allocator. */
|
||||
FORCEINLINE TArray() : TArray(0) { }
|
||||
|
||||
/** Constructs the container with 'Count' default instances of T. */
|
||||
explicit TArray(size_t Count) requires (CDefaultConstructible<ElementType>)
|
||||
explicit TArray(size_t Count) requires (CDefaultConstructible<T>)
|
||||
{
|
||||
Impl.ArrayNum = Count;
|
||||
Impl.ArrayMax = Impl->CalculateSlackReserve(Num());
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::DefaultConstruct<ElementType>(Impl.Pointer, Num());
|
||||
Memory::DefaultConstruct<FElementType>(Impl.Pointer, Num());
|
||||
}
|
||||
|
||||
/** Constructs the container with 'Count' copies of elements with 'InValue'. */
|
||||
TArray(size_t Count, const ElementType& InValue) requires (CCopyConstructible<ElementType>)
|
||||
: TArray(MakeCountedConstantIterator(InValue, Count), DefaultSentinel)
|
||||
FORCEINLINE explicit TArray(size_t Count, const FElementType& InValue) requires (CCopyConstructible<T>)
|
||||
: TArray(Ranges::Repeat(InValue, Count))
|
||||
{ }
|
||||
|
||||
/** Constructs the container with the contents of the range ['First', 'Last'). */
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<ElementType, TIteratorReferenceType<I>> && CMovable<ElementType>)
|
||||
TArray(I First, S Last)
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<T, TIteratorReference<I>> && CMovable<T>)
|
||||
explicit TArray(I First, S Last)
|
||||
{
|
||||
if constexpr (CForwardIterator<I>)
|
||||
{
|
||||
if (CSizedSentinelFor<S, I>) { checkf(First <= Last, TEXT("Illegal range iterator. Please check First <= Last.")); }
|
||||
size_t Count = 0;
|
||||
|
||||
const size_t Count = Iteration::Distance(First, Last);
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
|
||||
|
||||
Count = Last - First;
|
||||
}
|
||||
else for (I Iter = First; Iter != Last; ++Iter) ++Count;
|
||||
|
||||
Impl.ArrayNum = Count;
|
||||
Impl.ArrayMax = Impl->CalculateSlackReserve(Num());
|
||||
@@ -76,7 +85,7 @@ public:
|
||||
|
||||
for (size_t Index = 0; Index != Count; ++Index)
|
||||
{
|
||||
new (Impl.Pointer + Index) ElementType(*First++);
|
||||
new (Impl.Pointer + Index) FElementType(*First++);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -93,18 +102,22 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs the container with the contents of the range. */
|
||||
template <CInputRange R> requires (!CSameAs<TRemoveCVRef<R>, TArray> && CConstructibleFrom<T, TRangeReference<R>> && CMovable<T>)
|
||||
FORCEINLINE explicit TArray(R&& Range) : TArray(Ranges::Begin(Range), Ranges::End(Range)) { }
|
||||
|
||||
/** Copy constructor. Constructs the container with the copy of the contents of 'InValue'. */
|
||||
TArray(const TArray& InValue) requires (CCopyConstructible<ElementType>)
|
||||
TArray(const TArray& InValue) requires (CCopyConstructible<T>)
|
||||
{
|
||||
Impl.ArrayNum = InValue.Num();
|
||||
Impl.ArrayMax = Impl->CalculateSlackReserve(Num());
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::CopyConstruct<ElementType>(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
Memory::CopyConstruct<FElementType>(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
}
|
||||
|
||||
/** Move constructor. After the move, 'InValue' is guaranteed to be empty. */
|
||||
TArray(TArray&& InValue) requires (CMoveConstructible<ElementType>)
|
||||
TArray(TArray&& InValue) requires (CMoveConstructible<T>)
|
||||
{
|
||||
Impl.ArrayNum = InValue.Num();
|
||||
|
||||
@@ -122,14 +135,14 @@ public:
|
||||
Impl.ArrayMax = Impl->CalculateSlackReserve(Num());
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
}
|
||||
|
||||
InValue.Reset();
|
||||
}
|
||||
|
||||
/** Constructs the container with the contents of the initializer list. */
|
||||
FORCEINLINE TArray(initializer_list<ElementType> IL) requires (CCopyConstructible<ElementType>) : TArray(Iteration::Begin(IL), Iteration::End(IL)) { }
|
||||
FORCEINLINE TArray(initializer_list<FElementType> IL) requires (CCopyConstructible<T>) : TArray(Ranges::Begin(IL), Ranges::End(IL)) { }
|
||||
|
||||
/** Destructs the array. The destructors of the elements are called and the used storage is deallocated. */
|
||||
~TArray()
|
||||
@@ -139,7 +152,7 @@ public:
|
||||
}
|
||||
|
||||
/** Copy assignment operator. Replaces the contents with a copy of the contents of 'InValue'. */
|
||||
TArray& operator=(const TArray& InValue) requires (CCopyable<ElementType>)
|
||||
TArray& operator=(const TArray& InValue) requires (CCopyable<T>)
|
||||
{
|
||||
if (&InValue == this) UNLIKELY return *this;
|
||||
|
||||
@@ -157,7 +170,7 @@ public:
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::CopyConstruct<ElementType>(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
Memory::CopyConstruct<FElementType>(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -170,7 +183,7 @@ public:
|
||||
else if (InValue.Num() <= Max())
|
||||
{
|
||||
Memory::CopyAssign(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
Memory::CopyConstruct<ElementType>(Impl.Pointer + Num(), InValue.Impl.Pointer + Num(), InValue.Num() - Num());
|
||||
Memory::CopyConstruct<FElementType>(Impl.Pointer + Num(), InValue.Impl.Pointer + Num(), InValue.Num() - Num());
|
||||
}
|
||||
else check_no_entry();
|
||||
|
||||
@@ -180,7 +193,7 @@ public:
|
||||
}
|
||||
|
||||
/** Move assignment operator. After the move, 'InValue' is guaranteed to be empty. */
|
||||
TArray& operator=(TArray&& InValue) requires (CMovable<ElementType>)
|
||||
TArray& operator=(TArray&& InValue) requires (CMovable<T>)
|
||||
{
|
||||
if (&InValue == this) UNLIKELY return *this;
|
||||
|
||||
@@ -214,7 +227,7 @@ public:
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
|
||||
InValue.Reset();
|
||||
|
||||
@@ -229,7 +242,7 @@ public:
|
||||
else if (InValue.Num() <= Max())
|
||||
{
|
||||
Memory::MoveAssign(Impl.Pointer, InValue.Impl.Pointer, Num());
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer + Num(), InValue.Impl.Pointer + Num(), InValue.Num() - Num());
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer + Num(), InValue.Impl.Pointer + Num(), InValue.Num() - Num());
|
||||
}
|
||||
else check_no_entry();
|
||||
|
||||
@@ -241,46 +254,46 @@ public:
|
||||
}
|
||||
|
||||
/** Replaces the contents with those identified by initializer list. */
|
||||
TArray& operator=(initializer_list<ElementType> IL) requires (CCopyable<ElementType>)
|
||||
TArray& operator=(initializer_list<FElementType> IL) requires (CCopyable<T>)
|
||||
{
|
||||
size_t NumToAllocate = GetNum(IL);
|
||||
size_t NumToAllocate = Ranges::Num(IL);
|
||||
|
||||
NumToAllocate = NumToAllocate > Max() ? Impl->CalculateSlackGrow(GetNum(IL), Max()) : NumToAllocate;
|
||||
NumToAllocate = NumToAllocate < Max() ? Impl->CalculateSlackShrink(GetNum(IL), Max()) : NumToAllocate;
|
||||
NumToAllocate = NumToAllocate > Max() ? Impl->CalculateSlackGrow (Ranges::Num(IL), Max()) : NumToAllocate;
|
||||
NumToAllocate = NumToAllocate < Max() ? Impl->CalculateSlackShrink(Ranges::Num(IL), Max()) : NumToAllocate;
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
Memory::Destruct(Impl.Pointer, Num());
|
||||
Impl->Deallocate(Impl.Pointer);
|
||||
|
||||
Impl.ArrayNum = GetNum(IL);
|
||||
Impl.ArrayNum = Ranges::Num(IL);
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::CopyConstruct<ElementType>(Impl.Pointer, NAMESPACE_REDCRAFT::GetData(IL), Num());
|
||||
Memory::CopyConstruct<FElementType>(Impl.Pointer, Ranges::GetData(IL), Num());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (GetNum(IL) <= Num())
|
||||
if (Ranges::Num(IL) <= Num())
|
||||
{
|
||||
Memory::CopyAssign(Impl.Pointer, NAMESPACE_REDCRAFT::GetData(IL), GetNum(IL));
|
||||
Memory::Destruct(Impl.Pointer + GetNum(IL), Num() - GetNum(IL));
|
||||
Memory::CopyAssign(Impl.Pointer, Ranges::GetData(IL), Ranges::Num(IL));
|
||||
Memory::Destruct(Impl.Pointer + Ranges::Num(IL), Num() - Ranges::Num(IL));
|
||||
}
|
||||
else if (GetNum(IL) <= Max())
|
||||
else if (Ranges::Num(IL) <= Max())
|
||||
{
|
||||
Memory::CopyAssign(Impl.Pointer, NAMESPACE_REDCRAFT::GetData(IL), Num());
|
||||
Memory::CopyConstruct<ElementType>(Impl.Pointer + Num(), NAMESPACE_REDCRAFT::GetData(IL) + Num(), GetNum(IL) - Num());
|
||||
Memory::CopyAssign(Impl.Pointer, Ranges::GetData(IL), Num());
|
||||
Memory::CopyConstruct<FElementType>(Impl.Pointer + Num(), Ranges::GetData(IL) + Num(), Ranges::Num(IL) - Num());
|
||||
}
|
||||
else check_no_entry();
|
||||
|
||||
Impl.ArrayNum = GetNum(IL);
|
||||
Impl.ArrayNum = Ranges::Num(IL);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Compares the contents of two arrays. */
|
||||
NODISCARD friend bool operator==(const TArray& LHS, const TArray& RHS) requires (CWeaklyEqualityComparable<ElementType>)
|
||||
NODISCARD friend bool operator==(const TArray& LHS, const TArray& RHS) requires (CWeaklyEqualityComparable<T>)
|
||||
{
|
||||
if (LHS.Num() != RHS.Num()) return false;
|
||||
|
||||
@@ -293,7 +306,7 @@ public:
|
||||
}
|
||||
|
||||
/** Compares the contents of 'LHS' and 'RHS' lexicographically. */
|
||||
NODISCARD friend auto operator<=>(const TArray& LHS, const TArray& RHS) requires (CSynthThreeWayComparable<ElementType>)
|
||||
NODISCARD friend auto operator<=>(const TArray& LHS, const TArray& RHS) requires (CSynthThreeWayComparable<T>)
|
||||
{
|
||||
const size_t NumToCompare = LHS.Num() < RHS.Num() ? LHS.Num() : RHS.Num();
|
||||
|
||||
@@ -306,7 +319,7 @@ public:
|
||||
}
|
||||
|
||||
/** Inserts 'InValue' before 'Iter' in the container. */
|
||||
Iterator Insert(ConstIterator Iter, const ElementType& InValue) requires (CCopyable<ElementType>)
|
||||
FIterator Insert(FConstIterator Iter, const FElementType& InValue) requires (CCopyable<T>)
|
||||
{
|
||||
checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
@@ -318,26 +331,26 @@ public:
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Num() + 1;
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, InsertIndex);
|
||||
new (Impl.Pointer + InsertIndex) ElementType(InValue);
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, InsertIndex);
|
||||
new (Impl.Pointer + InsertIndex) FElementType(InValue);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex);
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
Impl->Deallocate(OldAllocation);
|
||||
|
||||
return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
}
|
||||
|
||||
if (InsertIndex != Num())
|
||||
{
|
||||
new (Impl.Pointer + Num()) ElementType(MoveTemp(Impl.Pointer[Num() - 1]));
|
||||
new (Impl.Pointer + Num()) FElementType(MoveTemp(Impl.Pointer[Num() - 1]));
|
||||
|
||||
for (size_t Index = Num() - 1; Index != InsertIndex; --Index)
|
||||
{
|
||||
@@ -346,15 +359,15 @@ public:
|
||||
|
||||
Impl.Pointer[InsertIndex] = InValue;
|
||||
}
|
||||
else new (Impl.Pointer + Num()) ElementType(InValue);
|
||||
else new (Impl.Pointer + Num()) FElementType(InValue);
|
||||
|
||||
Impl.ArrayNum = Num() + 1;
|
||||
|
||||
return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
}
|
||||
|
||||
/** Inserts 'InValue' before 'Iter' in the container. */
|
||||
Iterator Insert(ConstIterator Iter, ElementType&& InValue) requires (CMovable<ElementType>)
|
||||
FIterator Insert(FConstIterator Iter, FElementType&& InValue) requires (CMovable<T>)
|
||||
{
|
||||
checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
@@ -366,26 +379,26 @@ public:
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Num() + 1;
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, InsertIndex);
|
||||
new (Impl.Pointer + InsertIndex) ElementType(MoveTemp(InValue));
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, InsertIndex);
|
||||
new (Impl.Pointer + InsertIndex) FElementType(MoveTemp(InValue));
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex);
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
Impl->Deallocate(OldAllocation);
|
||||
|
||||
return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
}
|
||||
|
||||
if (InsertIndex != Num())
|
||||
{
|
||||
new (Impl.Pointer + Num()) ElementType(MoveTemp(Impl.Pointer[Num() - 1]));
|
||||
new (Impl.Pointer + Num()) FElementType(MoveTemp(Impl.Pointer[Num() - 1]));
|
||||
|
||||
for (size_t Index = Num() - 1; Index != InsertIndex; --Index)
|
||||
{
|
||||
@@ -394,36 +407,42 @@ public:
|
||||
|
||||
Impl.Pointer[InsertIndex] = MoveTemp(InValue);
|
||||
}
|
||||
else new (Impl.Pointer + Num()) ElementType(MoveTemp(InValue));
|
||||
else new (Impl.Pointer + Num()) FElementType(MoveTemp(InValue));
|
||||
|
||||
Impl.ArrayNum = Num() + 1;
|
||||
|
||||
return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
}
|
||||
|
||||
/** Inserts 'Count' copies of the 'InValue' before 'Iter' in the container. */
|
||||
Iterator Insert(ConstIterator Iter, size_t Count, const ElementType& InValue) requires (CCopyable<ElementType>)
|
||||
FIterator Insert(FConstIterator Iter, size_t Count, const FElementType& InValue) requires (CCopyable<T>)
|
||||
{
|
||||
checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
return Insert(Iter, MakeCountedConstantIterator(InValue, Count), DefaultSentinel);
|
||||
return Insert(Iter, Ranges::Repeat(InValue, Count));
|
||||
}
|
||||
|
||||
/** Inserts elements from range ['First', 'Last') before 'Iter'. */
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<ElementType, TIteratorReferenceType<I>>
|
||||
&& CAssignableFrom<ElementType&, TIteratorReferenceType<I>> && CMovable<ElementType>)
|
||||
Iterator Insert(ConstIterator Iter, I First, S Last)
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<T, TIteratorReference<I>> && CAssignableFrom<T&, TIteratorReference<I>> && CMovable<T>)
|
||||
FIterator Insert(FConstIterator Iter, I First, S Last)
|
||||
{
|
||||
checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
if constexpr (CForwardIterator<I>)
|
||||
{
|
||||
if (CSizedSentinelFor<S, I>) { checkf(First <= Last, TEXT("Illegal range iterator. Please check First <= Last.")); }
|
||||
|
||||
const size_t InsertIndex = Iter - Begin();
|
||||
const size_t Count = Iteration::Distance(First, Last);
|
||||
|
||||
if (Count == 0) return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
size_t Count = 0;
|
||||
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
|
||||
|
||||
Count = Last - First;
|
||||
}
|
||||
else for (I Jter = First; Jter != Last; ++Jter) ++Count;
|
||||
|
||||
if (Count == 0) return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
|
||||
const size_t NumToAllocate = Num() + Count > Max() ? Impl->CalculateSlackGrow(Num() + Count, Max()) : Max();
|
||||
|
||||
@@ -431,26 +450,26 @@ public:
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Num() + Count;
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, InsertIndex);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, InsertIndex);
|
||||
|
||||
for (size_t Index = InsertIndex; Index != InsertIndex + Count; ++Index)
|
||||
{
|
||||
new (Impl.Pointer + Index) ElementType(*First++);
|
||||
new (Impl.Pointer + Index) FElementType(*First++);
|
||||
}
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer + InsertIndex + Count, OldAllocation + InsertIndex, NumToDestruct - InsertIndex);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer + InsertIndex + Count, OldAllocation + InsertIndex, NumToDestruct - InsertIndex);
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
Impl->Deallocate(OldAllocation);
|
||||
|
||||
return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -492,7 +511,7 @@ public:
|
||||
|
||||
for (size_t TargetIndex = IndexO - 1; TargetIndex != IndexD - 1; --TargetIndex)
|
||||
{
|
||||
new (Impl.Pointer + TargetIndex) ElementType(MoveTemp(Impl.Pointer[TargetIndex - Count]));
|
||||
new (Impl.Pointer + TargetIndex) FElementType(MoveTemp(Impl.Pointer[TargetIndex - Count]));
|
||||
}
|
||||
|
||||
for (size_t TargetIndex = IndexD - 1; TargetIndex != IndexC - 1; --TargetIndex)
|
||||
@@ -507,14 +526,14 @@ public:
|
||||
|
||||
for (size_t TargetIndex = IndexB; TargetIndex != IndexC; ++TargetIndex)
|
||||
{
|
||||
new (Impl.Pointer + TargetIndex) ElementType(*First++);
|
||||
new (Impl.Pointer + TargetIndex) FElementType(*First++);
|
||||
}
|
||||
|
||||
check(First == Last);
|
||||
|
||||
Impl.ArrayNum = Num() + Count;
|
||||
|
||||
return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -523,15 +542,22 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Inserts elements from initializer list before 'Iter' in the container. */
|
||||
FORCEINLINE Iterator Insert(ConstIterator Iter, initializer_list<ElementType> IL) requires (CCopyable<ElementType>)
|
||||
/** Inserts elements from range before 'Iter'. */
|
||||
template <CInputRange R> requires (CConstructibleFrom<T, TRangeReference<R>> && CAssignableFrom<T&, TRangeReference<R>> && CMovable<T>)
|
||||
FORCEINLINE FIterator Insert(FConstIterator Iter, R&& Range)
|
||||
{
|
||||
return Insert(Iter, Iteration::Begin(IL), Iteration::End(IL));
|
||||
return Insert(Iter, Ranges::Begin(Range), Ranges::End(Range));
|
||||
}
|
||||
|
||||
/** Inserts elements from initializer list before 'Iter' in the container. */
|
||||
FORCEINLINE FIterator Insert(FConstIterator Iter, initializer_list<FElementType> IL) requires (CCopyable<T>)
|
||||
{
|
||||
return Insert(Iter, Ranges::Begin(IL), Ranges::End(IL));
|
||||
}
|
||||
|
||||
/** Inserts a new element into the container directly before 'Iter'. */
|
||||
template <typename... Ts> requires (CConstructibleFrom<ElementType, Ts...> && CMovable<ElementType>)
|
||||
Iterator Emplace(ConstIterator Iter, Ts&&... Args)
|
||||
template <typename... Ts> requires (CConstructibleFrom<T, Ts...> && CMovable<T>)
|
||||
FIterator Emplace(FConstIterator Iter, Ts&&... Args)
|
||||
{
|
||||
checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
@@ -543,43 +569,43 @@ public:
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Num() + 1;
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, InsertIndex);
|
||||
new (Impl.Pointer + InsertIndex) ElementType(Forward<Ts>(Args)...);
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, InsertIndex);
|
||||
new (Impl.Pointer + InsertIndex) FElementType(Forward<Ts>(Args)...);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex);
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
Impl->Deallocate(OldAllocation);
|
||||
|
||||
return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
}
|
||||
|
||||
if (InsertIndex != Num())
|
||||
{
|
||||
new (Impl.Pointer + Num()) ElementType(MoveTemp(Impl.Pointer[Num() - 1]));
|
||||
new (Impl.Pointer + Num()) FElementType(MoveTemp(Impl.Pointer[Num() - 1]));
|
||||
|
||||
for (size_t Index = Num() - 1; Index != InsertIndex; --Index)
|
||||
{
|
||||
Impl.Pointer[Index] = MoveTemp(Impl.Pointer[Index - 1]);
|
||||
}
|
||||
|
||||
Impl.Pointer[InsertIndex] = ElementType(Forward<Ts>(Args)...);
|
||||
Impl.Pointer[InsertIndex] = FElementType(Forward<Ts>(Args)...);
|
||||
}
|
||||
else new (Impl.Pointer + Num()) ElementType(Forward<Ts>(Args)...);
|
||||
else new (Impl.Pointer + Num()) FElementType(Forward<Ts>(Args)...);
|
||||
|
||||
Impl.ArrayNum = Num() + 1;
|
||||
|
||||
return Iterator(this, Impl.Pointer + InsertIndex);
|
||||
return FIterator(this, Impl.Pointer + InsertIndex);
|
||||
}
|
||||
|
||||
/** Removes the element at 'Iter' in the container. Without changing the order of elements. */
|
||||
FORCEINLINE Iterator StableErase(ConstIterator Iter, bool bAllowShrinking = true) requires (CMovable<ElementType>)
|
||||
FORCEINLINE FIterator StableErase(FConstIterator Iter, bool bAllowShrinking = true) requires (CMovable<T>)
|
||||
{
|
||||
checkf(IsValidIterator(Iter) && Iter != End(), TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
@@ -587,33 +613,33 @@ public:
|
||||
}
|
||||
|
||||
/** Removes the elements in the range ['First', 'Last') in the container. Without changing the order of elements. */
|
||||
Iterator StableErase(ConstIterator First, ConstIterator Last, bool bAllowShrinking = true) requires (CMovable<ElementType>)
|
||||
FIterator StableErase(FConstIterator First, FConstIterator Last, bool bAllowShrinking = true) requires (CMovable<T>)
|
||||
{
|
||||
checkf(IsValidIterator(First) && IsValidIterator(Last) && First <= Last, TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
const size_t EraseIndex = First - Begin();
|
||||
const size_t EraseCount = Last - First;
|
||||
|
||||
if (EraseCount == 0) return Iterator(this, Impl.Pointer + EraseIndex);
|
||||
if (EraseCount == 0) return FIterator(this, Impl.Pointer + EraseIndex);
|
||||
|
||||
const size_t NumToAllocate = bAllowShrinking ? Impl->CalculateSlackShrink(Num() - EraseCount, Max()) : Max();
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Num() - EraseCount;
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, EraseIndex);
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer + EraseIndex, OldAllocation + EraseIndex + EraseCount, NumToDestruct - EraseIndex - EraseCount);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, EraseIndex);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer + EraseIndex, OldAllocation + EraseIndex + EraseCount, NumToDestruct - EraseIndex - EraseCount);
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
Impl->Deallocate(OldAllocation);
|
||||
|
||||
return Iterator(this, Impl.Pointer + EraseIndex);
|
||||
return FIterator(this, Impl.Pointer + EraseIndex);
|
||||
}
|
||||
|
||||
for (size_t Index = EraseIndex + EraseCount; Index != Num(); ++Index)
|
||||
@@ -625,11 +651,11 @@ public:
|
||||
|
||||
Impl.ArrayNum = Num() - EraseCount;
|
||||
|
||||
return Iterator(this, Impl.Pointer + EraseIndex);
|
||||
return FIterator(this, Impl.Pointer + EraseIndex);
|
||||
}
|
||||
|
||||
/** Removes the element at 'Iter' in the container. But it may change the order of elements. */
|
||||
FORCEINLINE Iterator Erase(ConstIterator Iter, bool bAllowShrinking = true) requires (CMovable<ElementType>)
|
||||
FORCEINLINE FIterator Erase(FConstIterator Iter, bool bAllowShrinking = true) requires (CMovable<T>)
|
||||
{
|
||||
checkf(IsValidIterator(Iter) && Iter != End(), TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
@@ -637,33 +663,33 @@ public:
|
||||
}
|
||||
|
||||
/** Removes the elements in the range ['First', 'Last') in the container. But it may change the order of elements. */
|
||||
Iterator Erase(ConstIterator First, ConstIterator Last, bool bAllowShrinking = true) requires (CMovable<ElementType>)
|
||||
FIterator Erase(FConstIterator First, FConstIterator Last, bool bAllowShrinking = true) requires (CMovable<T>)
|
||||
{
|
||||
checkf(IsValidIterator(First) && IsValidIterator(Last) && First <= Last, TEXT("Read access violation. Please check IsValidIterator()."));
|
||||
|
||||
const size_t EraseIndex = First - Begin();
|
||||
const size_t EraseCount = Last - First;
|
||||
|
||||
if (EraseCount == 0) return Iterator(this, Impl.Pointer + EraseIndex);
|
||||
if (EraseCount == 0) return FIterator(this, Impl.Pointer + EraseIndex);
|
||||
|
||||
const size_t NumToAllocate = bAllowShrinking ? Impl->CalculateSlackShrink(Num() - EraseCount, Max()) : Max();
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Num() - EraseCount;
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, EraseIndex);
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer + EraseIndex, OldAllocation + EraseIndex + EraseCount, NumToDestruct - EraseIndex - EraseCount);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, EraseIndex);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer + EraseIndex, OldAllocation + EraseIndex + EraseCount, NumToDestruct - EraseIndex - EraseCount);
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
Impl->Deallocate(OldAllocation);
|
||||
|
||||
return Iterator(this, Impl.Pointer + EraseIndex);
|
||||
return FIterator(this, Impl.Pointer + EraseIndex);
|
||||
}
|
||||
|
||||
for (size_t Index = 0; Index != EraseCount; ++Index)
|
||||
@@ -677,24 +703,24 @@ public:
|
||||
|
||||
Impl.ArrayNum = Num() - EraseCount;
|
||||
|
||||
return Iterator(this, Impl.Pointer + EraseIndex);
|
||||
return FIterator(this, Impl.Pointer + EraseIndex);
|
||||
}
|
||||
|
||||
/** Appends the given element value to the end of the container. */
|
||||
FORCEINLINE void PushBack(const ElementType& InValue) requires (CCopyable<ElementType>)
|
||||
FORCEINLINE void PushBack(const FElementType& InValue) requires (CCopyable<T>)
|
||||
{
|
||||
EmplaceBack(InValue);
|
||||
}
|
||||
|
||||
/** Appends the given element value to the end of the container. */
|
||||
FORCEINLINE void PushBack(ElementType&& InValue) requires (CMovable<ElementType>)
|
||||
FORCEINLINE void PushBack(FElementType&& InValue) requires (CMovable<T>)
|
||||
{
|
||||
EmplaceBack(MoveTemp(InValue));
|
||||
}
|
||||
|
||||
/** Appends a new element to the end of the container. */
|
||||
template <typename... Ts> requires (CConstructibleFrom<ElementType, Ts...> && CMovable<ElementType>)
|
||||
ElementType& EmplaceBack(Ts&&... Args)
|
||||
template <typename... Ts> requires (CConstructibleFrom<T, Ts...> && CMovable<T>)
|
||||
FElementType& EmplaceBack(Ts&&... Args)
|
||||
{
|
||||
const size_t NumToAllocate = Num() + 1 > Max() ? Impl->CalculateSlackGrow(Num() + 1, Max()) : Max();
|
||||
|
||||
@@ -702,15 +728,15 @@ public:
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Num() + 1;
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, Num() - 1);
|
||||
new (Impl.Pointer + Num() - 1) ElementType(Forward<Ts>(Args)...);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, Num() - 1);
|
||||
new (Impl.Pointer + Num() - 1) FElementType(Forward<Ts>(Args)...);
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
Impl->Deallocate(OldAllocation);
|
||||
@@ -718,7 +744,7 @@ public:
|
||||
return Impl.Pointer[Num() - 1];
|
||||
}
|
||||
|
||||
new (Impl.Pointer + Num()) ElementType(Forward<Ts>(Args)...);
|
||||
new (Impl.Pointer + Num()) FElementType(Forward<Ts>(Args)...);
|
||||
|
||||
Impl.ArrayNum = Num() + 1;
|
||||
|
||||
@@ -726,13 +752,13 @@ public:
|
||||
}
|
||||
|
||||
/** Removes the last element of the container. The array cannot be empty. */
|
||||
FORCEINLINE void PopBack(bool bAllowShrinking = true) requires (CMovable<ElementType>)
|
||||
FORCEINLINE void PopBack(bool bAllowShrinking = true) requires (CMovable<T>)
|
||||
{
|
||||
Erase(End() - 1, bAllowShrinking);
|
||||
}
|
||||
|
||||
/** Resizes the container to contain 'Count' elements. Additional default elements are appended. */
|
||||
void SetNum(size_t Count, bool bAllowShrinking = true) requires (CDefaultConstructible<ElementType> && CMovable<ElementType>)
|
||||
void SetNum(size_t Count, bool bAllowShrinking = true) requires (CDefaultConstructible<T> && CMovable<T>)
|
||||
{
|
||||
size_t NumToAllocate = Count;
|
||||
|
||||
@@ -741,7 +767,7 @@ public:
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Count;
|
||||
@@ -750,12 +776,12 @@ public:
|
||||
|
||||
if (NumToDestruct <= Num())
|
||||
{
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, NumToDestruct);
|
||||
Memory::DefaultConstruct<ElementType>(Impl.Pointer + NumToDestruct, Num() - NumToDestruct);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, NumToDestruct);
|
||||
Memory::DefaultConstruct<FElementType>(Impl.Pointer + NumToDestruct, Num() - NumToDestruct);
|
||||
}
|
||||
else
|
||||
{
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, Num());
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, Num());
|
||||
}
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
@@ -770,7 +796,7 @@ public:
|
||||
}
|
||||
else if (Count <= Max())
|
||||
{
|
||||
Memory::DefaultConstruct<ElementType>(Impl.Pointer + Num(), Count - Num());
|
||||
Memory::DefaultConstruct<FElementType>(Impl.Pointer + Num(), Count - Num());
|
||||
}
|
||||
else check_no_entry();
|
||||
|
||||
@@ -778,7 +804,7 @@ public:
|
||||
}
|
||||
|
||||
/** Resizes the container to contain 'Count' elements. Additional copies of 'InValue' are appended. */
|
||||
void SetNum(size_t Count, const ElementType& InValue, bool bAllowShrinking = true) requires (CCopyConstructible<ElementType> && CMovable<ElementType>)
|
||||
void SetNum(size_t Count, const FElementType& InValue, bool bAllowShrinking = true) requires (CCopyConstructible<T> && CMovable<T>)
|
||||
{
|
||||
size_t NumToAllocate = Count;
|
||||
|
||||
@@ -787,7 +813,7 @@ public:
|
||||
|
||||
if (NumToAllocate != Max())
|
||||
{
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = Num();
|
||||
|
||||
Impl.ArrayNum = Count;
|
||||
@@ -796,16 +822,16 @@ public:
|
||||
|
||||
if (NumToDestruct <= Num())
|
||||
{
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, NumToDestruct);
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, NumToDestruct);
|
||||
|
||||
for (size_t Index = NumToDestruct; Index != Num(); ++Index)
|
||||
{
|
||||
new (Impl.Pointer + Index) ElementType(InValue);
|
||||
new (Impl.Pointer + Index) FElementType(InValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, Num());
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, Num());
|
||||
}
|
||||
|
||||
Memory::Destruct(OldAllocation, NumToDestruct);
|
||||
@@ -822,7 +848,7 @@ public:
|
||||
{
|
||||
for (size_t Index = Num(); Index != Count; ++Index)
|
||||
{
|
||||
new (Impl.Pointer + Index) ElementType(InValue);
|
||||
new (Impl.Pointer + Index) FElementType(InValue);
|
||||
}
|
||||
}
|
||||
else check_no_entry();
|
||||
@@ -831,19 +857,19 @@ public:
|
||||
}
|
||||
|
||||
/** Increase the max capacity of the array to a value that's greater or equal to 'Count'. */
|
||||
void Reserve(size_t Count) requires (CMovable<ElementType>)
|
||||
void Reserve(size_t Count) requires (CMovable<T>)
|
||||
{
|
||||
if (Count <= Max()) return;
|
||||
|
||||
const size_t NumToAllocate = Impl->CalculateSlackReserve(Count);
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
|
||||
check(NumToAllocate > Max());
|
||||
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, Num());
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, Num());
|
||||
|
||||
Memory::Destruct(OldAllocation, Num());
|
||||
Impl->Deallocate(OldAllocation);
|
||||
@@ -858,32 +884,32 @@ public:
|
||||
|
||||
if (NumToAllocate == Max()) return;
|
||||
|
||||
ElementType* OldAllocation = Impl.Pointer;
|
||||
FElementType* OldAllocation = Impl.Pointer;
|
||||
|
||||
Impl.ArrayMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(Max());
|
||||
|
||||
Memory::MoveConstruct<ElementType>(Impl.Pointer, OldAllocation, Num());
|
||||
Memory::MoveConstruct<FElementType>(Impl.Pointer, OldAllocation, Num());
|
||||
Memory::Destruct(OldAllocation, Num());
|
||||
|
||||
Impl->Deallocate(OldAllocation);
|
||||
}
|
||||
|
||||
/** @return The pointer to the underlying element storage. */
|
||||
NODISCARD FORCEINLINE ElementType* GetData() { return Impl.Pointer; }
|
||||
NODISCARD FORCEINLINE const ElementType* GetData() const { return Impl.Pointer; }
|
||||
NODISCARD FORCEINLINE FElementType* GetData() { return Impl.Pointer; }
|
||||
NODISCARD FORCEINLINE const FElementType* GetData() const { return Impl.Pointer; }
|
||||
|
||||
/** @return The iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE Iterator Begin() { return Iterator(this, Impl.Pointer); }
|
||||
NODISCARD FORCEINLINE ConstIterator Begin() const { return ConstIterator(this, Impl.Pointer); }
|
||||
NODISCARD FORCEINLINE Iterator End() { return Iterator(this, Impl.Pointer + Num()); }
|
||||
NODISCARD FORCEINLINE ConstIterator End() const { return ConstIterator(this, Impl.Pointer + Num()); }
|
||||
NODISCARD FORCEINLINE FIterator Begin() { return FIterator(this, Impl.Pointer); }
|
||||
NODISCARD FORCEINLINE FConstIterator Begin() const { return FConstIterator(this, Impl.Pointer); }
|
||||
NODISCARD FORCEINLINE FIterator End() { return FIterator(this, Impl.Pointer + Num()); }
|
||||
NODISCARD FORCEINLINE FConstIterator End() const { return FConstIterator(this, Impl.Pointer + Num()); }
|
||||
|
||||
/** @return The reverse iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE ReverseIterator RBegin() { return ReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE ConstReverseIterator RBegin() const { return ConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE ReverseIterator REnd() { return ReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE ConstReverseIterator REnd() const { return ConstReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE FReverseIterator RBegin() { return FReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE FConstReverseIterator RBegin() const { return FConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE FReverseIterator REnd() { return FReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE FConstReverseIterator REnd() const { return FConstReverseIterator(Begin()); }
|
||||
|
||||
/** @return The number of elements in the container. */
|
||||
NODISCARD FORCEINLINE size_t Num() const { return Impl.ArrayNum; }
|
||||
@@ -895,17 +921,17 @@ public:
|
||||
NODISCARD FORCEINLINE bool IsEmpty() const { return Num() == 0; }
|
||||
|
||||
/** @return true if the iterator is valid, false otherwise. */
|
||||
NODISCARD FORCEINLINE bool IsValidIterator(ConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
NODISCARD FORCEINLINE bool IsValidIterator(FConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
|
||||
/** @return The reference to the requested element. */
|
||||
NODISCARD FORCEINLINE ElementType& operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return Impl.Pointer[Index]; }
|
||||
NODISCARD FORCEINLINE const ElementType& operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return Impl.Pointer[Index]; }
|
||||
NODISCARD FORCEINLINE FElementType& operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return Impl.Pointer[Index]; }
|
||||
NODISCARD FORCEINLINE const FElementType& operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return Impl.Pointer[Index]; }
|
||||
|
||||
/** @return The reference to the first or last element. */
|
||||
NODISCARD FORCEINLINE ElementType& Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE const ElementType& Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE ElementType& Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE const ElementType& Back() const { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE FElementType& Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE const FElementType& Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE FElementType& Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE const FElementType& Back() const { return *(End() - 1); }
|
||||
|
||||
/** Erases all elements from the container. After this call, Num() returns zero. */
|
||||
void Reset(bool bAllowShrinking = true)
|
||||
@@ -929,11 +955,11 @@ public:
|
||||
}
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for TArray. */
|
||||
NODISCARD friend FORCEINLINE size_t GetTypeHash(const TArray& A) requires (CHashable<ElementType>)
|
||||
NODISCARD friend FORCEINLINE size_t GetTypeHash(const TArray& A) requires (CHashable<T>)
|
||||
{
|
||||
size_t Result = 0;
|
||||
|
||||
for (ConstIterator Iter = A.Begin(); Iter != A.End(); ++Iter)
|
||||
for (FConstIterator Iter = A.Begin(); Iter != A.End(); ++Iter)
|
||||
{
|
||||
Result = HashCombine(Result, GetTypeHash(*Iter));
|
||||
}
|
||||
@@ -942,7 +968,7 @@ public:
|
||||
}
|
||||
|
||||
/** Overloads the Swap algorithm for TArray. */
|
||||
friend void Swap(TArray& A, TArray& B) requires (CMovable<ElementType>)
|
||||
friend void Swap(TArray& A, TArray& B) requires (CMovable<T>)
|
||||
{
|
||||
const bool bIsTransferable =
|
||||
A.Impl->IsTransferable(A.Impl.Pointer) &&
|
||||
@@ -966,13 +992,13 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
ALLOCATOR_WRAPPER_BEGIN(AllocatorType, ElementType, Impl)
|
||||
ALLOCATOR_WRAPPER_BEGIN(FAllocatorType, FElementType, Impl)
|
||||
{
|
||||
size_t ArrayNum;
|
||||
size_t ArrayMax;
|
||||
ElementType* Pointer;
|
||||
FElementType* Pointer;
|
||||
}
|
||||
ALLOCATOR_WRAPPER_END(AllocatorType, ElementType, Impl)
|
||||
ALLOCATOR_WRAPPER_END(FAllocatorType, FElementType, Impl)
|
||||
|
||||
private:
|
||||
|
||||
@@ -981,7 +1007,7 @@ private:
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = TRemoveCV<T>;
|
||||
using FElementType = T;
|
||||
|
||||
FORCEINLINE TIteratorImpl() = default;
|
||||
|
||||
@@ -1058,7 +1084,10 @@ private:
|
||||
};
|
||||
|
||||
template <typename I, typename S>
|
||||
TArray(I, S) -> TArray<TIteratorElementType<I>>;
|
||||
TArray(I, S) -> TArray<TIteratorElement<I>>;
|
||||
|
||||
template <typename R>
|
||||
TArray(R) -> TArray<TRangeElement<R>>;
|
||||
|
||||
template <typename T>
|
||||
TArray(initializer_list<T>) -> TArray<T>;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Memory/Allocator.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "Containers/StaticArray.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
@@ -29,15 +29,15 @@ class TArrayView
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using FElementType = TRemoveCV<T>;
|
||||
|
||||
using Reference = T&;
|
||||
using FReference = T&;
|
||||
|
||||
class Iterator;
|
||||
class FIterator;
|
||||
|
||||
using ReverseIterator = TReverseIterator<Iterator>;
|
||||
using FReverseIterator = TReverseIterator<FIterator>;
|
||||
|
||||
static_assert(CContiguousIterator<Iterator>);
|
||||
static_assert(CContiguousIterator<FIterator>);
|
||||
|
||||
static constexpr size_t Extent = InExtent;
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
}
|
||||
|
||||
/** Constructs an array view that is a view over the range ['InFirst', 'InFirst' + 'Count'). */
|
||||
template <CContiguousIterator I> requires (CConvertibleTo<TIteratorElementType<I>(*)[], ElementType(*)[]>)
|
||||
template <CContiguousIterator I> requires (CConvertibleTo<TIteratorReference<I>, T> && CSameAs<TRemoveCVRef<TIteratorReference<I>>, TRemoveCVRef<T>>)
|
||||
FORCEINLINE constexpr explicit (Extent != DynamicExtent) TArrayView(I InFirst, size_t InCount)
|
||||
{
|
||||
checkf(Extent == DynamicExtent || Extent == InCount, TEXT("Illegal range count. Please check InCount."));
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
}
|
||||
|
||||
/** Constructs an array view that is a view over the range ['InFirst', 'InLast'). */
|
||||
template <CContiguousIterator I, CSizedSentinelFor<I> S> requires (CConvertibleTo<TIteratorElementType<I>(*)[], ElementType(*)[]>)
|
||||
template <CContiguousIterator I, CSizedSentinelFor<I> S> requires (CConvertibleTo<TIteratorReference<I>, T> && CSameAs<TRemoveCVRef<TIteratorReference<I>>, TRemoveCVRef<T>>)
|
||||
FORCEINLINE constexpr explicit (Extent != DynamicExtent) TArrayView(I InFirst, S InLast)
|
||||
{
|
||||
checkf(Extent == DynamicExtent || Extent == InLast - InFirst, TEXT("Illegal range iterator. Please check InLast - InFirst."));
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
|
||||
/** Constructs an array view that is a view over the array 'InArray'. */
|
||||
template <size_t N> requires (Extent == DynamicExtent || N == Extent)
|
||||
FORCEINLINE constexpr TArrayView(ElementType(&InArray)[N])
|
||||
FORCEINLINE constexpr TArrayView(FElementType(&InArray)[N])
|
||||
{
|
||||
Impl.Pointer = InArray;
|
||||
|
||||
@@ -93,23 +93,23 @@ public:
|
||||
}
|
||||
|
||||
/** Constructs an array view that is a view over the array 'InArray'. */
|
||||
template <typename U, size_t N> requires (CConvertibleTo<U(*)[], ElementType(*)[]>)
|
||||
template <typename U, size_t N> requires (CConvertibleTo<U(*)[], FElementType(*)[]>)
|
||||
FORCEINLINE constexpr TArrayView(TStaticArray<U, N>& InArray) : TArrayView(InArray.GetData(), InArray.Num()) { }
|
||||
|
||||
/** Constructs an array view that is a view over the array 'InArray'. */
|
||||
template <typename U, size_t N> requires (CConvertibleTo<const U(*)[], ElementType(*)[]>)
|
||||
template <typename U, size_t N> requires (CConvertibleTo<const U(*)[], FElementType(*)[]>)
|
||||
FORCEINLINE constexpr TArrayView(const TStaticArray<U, N>& InArray) : TArrayView(InArray.GetData(), InArray.Num()) { }
|
||||
|
||||
/** Constructs an array view that is a view over the array 'InArray'. */
|
||||
template <typename U, typename Allocator> requires (CConvertibleTo<U(*)[], ElementType(*)[]>)
|
||||
template <typename U, typename Allocator> requires (CConvertibleTo<U(*)[], FElementType(*)[]>)
|
||||
FORCEINLINE constexpr TArrayView(TArray<U, Allocator>& InArray) : TArrayView(InArray.GetData(), InArray.Num()) { }
|
||||
|
||||
/** Constructs an array view that is a view over the array 'InArray'. */
|
||||
template <typename U, typename Allocator> requires (CConvertibleTo<const U(*)[], ElementType(*)[]>)
|
||||
template <typename U, typename Allocator> requires (CConvertibleTo<const U(*)[], FElementType(*)[]>)
|
||||
FORCEINLINE constexpr TArrayView(const TArray<U, Allocator>& InArray) : TArrayView(InArray.GetData(), InArray.Num()) { }
|
||||
|
||||
/** Converting constructor from another array view 'InValue'. */
|
||||
template <typename U, size_t N> requires ((Extent == DynamicExtent || N == DynamicExtent || N == Extent) && CConvertibleTo<U(*)[], ElementType(*)[]>)
|
||||
template <typename U, size_t N> requires ((Extent == DynamicExtent || N == DynamicExtent || N == Extent) && CConvertibleTo<U(*)[], FElementType(*)[]>)
|
||||
FORCEINLINE constexpr explicit (Extent != DynamicExtent && N == DynamicExtent) TArrayView(TArrayView<U, N> InValue)
|
||||
{
|
||||
checkf(Extent == DynamicExtent || Extent == InValue.Num(), TEXT("Illegal view extent. Please check InValue.Num()."));
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
FORCEINLINE constexpr TArrayView& operator=(const TArrayView&) noexcept = default;
|
||||
|
||||
/** Compares the contents of two array views. */
|
||||
NODISCARD friend constexpr bool operator==(TArrayView LHS, TArrayView RHS) requires (CWeaklyEqualityComparable<ElementType>)
|
||||
NODISCARD friend constexpr bool operator==(TArrayView LHS, TArrayView RHS) requires (CWeaklyEqualityComparable<FElementType>)
|
||||
{
|
||||
if (LHS.Num() != RHS.Num()) return false;
|
||||
|
||||
@@ -142,7 +142,7 @@ public:
|
||||
}
|
||||
|
||||
/** Compares the contents of two array views. */
|
||||
NODISCARD friend constexpr auto operator<=>(TArrayView LHS, TArrayView RHS) requires (CSynthThreeWayComparable<ElementType>)
|
||||
NODISCARD friend constexpr auto operator<=>(TArrayView LHS, TArrayView RHS) requires (CSynthThreeWayComparable<FElementType>)
|
||||
{
|
||||
const size_t NumToCompare = LHS.Num() < RHS.Num() ? LHS.Num() : RHS.Num();
|
||||
|
||||
@@ -156,36 +156,36 @@ public:
|
||||
|
||||
/** Obtains an array view that is a view over the first 'Count' elements of this array view. */
|
||||
template <size_t Count> requires (Extent == DynamicExtent || Extent >= Count)
|
||||
NODISCARD FORCEINLINE constexpr TArrayView<ElementType, Count> First() const
|
||||
NODISCARD FORCEINLINE constexpr TArrayView<T, Count> First() const
|
||||
{
|
||||
checkf(Count <= Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
return TArrayView<ElementType, Count>(Begin(), Count);
|
||||
return TArrayView<T, Count>(Begin(), Count);
|
||||
}
|
||||
|
||||
/** Obtains an array view that is a view over the first 'Count' elements of this array view. */
|
||||
NODISCARD FORCEINLINE constexpr TArrayView<ElementType, DynamicExtent> First(size_t Count) const
|
||||
NODISCARD FORCEINLINE constexpr TArrayView<T, DynamicExtent> First(size_t Count) const
|
||||
{
|
||||
checkf(Count <= Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
return TArrayView<ElementType, DynamicExtent>(Begin(), Count);
|
||||
return TArrayView<T, DynamicExtent>(Begin(), Count);
|
||||
}
|
||||
|
||||
/** Obtains an array view that is a view over the last 'Count' elements of this array view. */
|
||||
template <size_t Count> requires (Extent == DynamicExtent || Extent >= Count)
|
||||
NODISCARD FORCEINLINE constexpr TArrayView<ElementType, Count> Last() const
|
||||
NODISCARD FORCEINLINE constexpr TArrayView<T, Count> Last() const
|
||||
{
|
||||
checkf(Count <= Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
return TArrayView<ElementType, Count>(End() - Count, Count);
|
||||
return TArrayView<T, Count>(End() - Count, Count);
|
||||
}
|
||||
|
||||
/** Obtains an array view that is a view over the last 'Count' elements of this array view. */
|
||||
NODISCARD FORCEINLINE constexpr TArrayView<ElementType, DynamicExtent> Last(size_t Count) const
|
||||
NODISCARD FORCEINLINE constexpr TArrayView<T, DynamicExtent> Last(size_t Count) const
|
||||
{
|
||||
checkf(Count <= Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
return TArrayView<ElementType, DynamicExtent>(End() - Count, Count);
|
||||
return TArrayView<T, DynamicExtent>(End() - Count, Count);
|
||||
}
|
||||
|
||||
/** Obtains an array view that is a view over the 'Count' elements of this array view starting at 'Offset'. */
|
||||
@@ -198,11 +198,11 @@ public:
|
||||
|
||||
if constexpr (Count != DynamicExtent)
|
||||
{
|
||||
return TArrayView<ElementType, SubviewExtent>(Begin() + Offset, Count);
|
||||
return TArrayView<T, SubviewExtent>(Begin() + Offset, Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TArrayView<ElementType, SubviewExtent>(Begin() + Offset, Num() - Offset);
|
||||
return TArrayView<T, SubviewExtent>(Begin() + Offset, Num() - Offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,20 +213,20 @@ public:
|
||||
|
||||
if (Count != DynamicExtent)
|
||||
{
|
||||
return TArrayView<ElementType, DynamicExtent>(Begin() + Offset, Count);
|
||||
return TArrayView<T, DynamicExtent>(Begin() + Offset, Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TArrayView<ElementType, DynamicExtent>(Begin() + Offset, Num() - Offset);
|
||||
return TArrayView<T, DynamicExtent>(Begin() + Offset, Num() - Offset);
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtains an array view to the object representation of the elements of the array view. */
|
||||
NODISCARD FORCEINLINE constexpr auto AsBytes()
|
||||
{
|
||||
constexpr size_t BytesExtent = Extent != DynamicExtent ? sizeof(ElementType) * Extent : DynamicExtent;
|
||||
constexpr size_t BytesExtent = Extent != DynamicExtent ? sizeof(FElementType) * Extent : DynamicExtent;
|
||||
|
||||
if constexpr (!CConst<ElementType>)
|
||||
if constexpr (!CConst<FElementType>)
|
||||
{
|
||||
return TArrayView<uint8, BytesExtent>(reinterpret_cast<uint8*>(GetData()), NumBytes());
|
||||
}
|
||||
@@ -239,47 +239,47 @@ public:
|
||||
/** Obtains an array view to the object representation of the elements of the array view. */
|
||||
NODISCARD FORCEINLINE constexpr auto AsBytes() const
|
||||
{
|
||||
constexpr size_t BytesExtent = Extent != DynamicExtent ? sizeof(ElementType) * Extent : DynamicExtent;
|
||||
constexpr size_t BytesExtent = Extent != DynamicExtent ? sizeof(FElementType) * Extent : DynamicExtent;
|
||||
|
||||
return TArrayView<const uint8, BytesExtent>(reinterpret_cast<const uint8*>(GetData()), NumBytes());
|
||||
}
|
||||
|
||||
/** @return The pointer to the underlying element storage. */
|
||||
NODISCARD FORCEINLINE constexpr ElementType* GetData() const { return Impl.Pointer; }
|
||||
NODISCARD FORCEINLINE constexpr T* GetData() const { return Impl.Pointer; }
|
||||
|
||||
/** @return The iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE constexpr Iterator Begin() const { return Iterator(this, Impl.Pointer); }
|
||||
NODISCARD FORCEINLINE constexpr Iterator End() const { return Iterator(this, Impl.Pointer + Num()); }
|
||||
NODISCARD FORCEINLINE constexpr FIterator Begin() const { return FIterator(this, Impl.Pointer); }
|
||||
NODISCARD FORCEINLINE constexpr FIterator End() const { return FIterator(this, Impl.Pointer + Num()); }
|
||||
|
||||
/** @return The reverse iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE constexpr ReverseIterator RBegin() const { return ReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr ReverseIterator REnd() const { return ReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE constexpr FReverseIterator RBegin() const { return FReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr FReverseIterator REnd() const { return FReverseIterator(Begin()); }
|
||||
|
||||
/** @return The number of elements in the container. */
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const { if constexpr (Extent == DynamicExtent) { return Impl.ArrayNum; } return Extent; }
|
||||
|
||||
/** @return The number of bytes in the container. */
|
||||
NODISCARD FORCEINLINE constexpr size_t NumBytes() const { return Num() * sizeof(ElementType); }
|
||||
NODISCARD FORCEINLINE constexpr size_t NumBytes() const { return Num() * sizeof(FElementType); }
|
||||
|
||||
/** @return true if the container is empty, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return Num() == 0; }
|
||||
|
||||
/** @return true if the iterator is valid, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsValidIterator(Iterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
NODISCARD FORCEINLINE constexpr bool IsValidIterator(FIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
|
||||
/** @return The reference to the requested element. */
|
||||
NODISCARD FORCEINLINE constexpr Reference operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return Impl.Pointer[Index]; }
|
||||
NODISCARD FORCEINLINE constexpr FReference operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return Impl.Pointer[Index]; }
|
||||
|
||||
/** @return The reference to the first or last element. */
|
||||
NODISCARD FORCEINLINE constexpr Reference Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr Reference Back() const { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE constexpr FReference Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr FReference Back() const { return *(End() - 1); }
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for TArrayView. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(TArrayView A) requires (CHashable<ElementType>)
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(TArrayView A) requires (CHashable<FElementType>)
|
||||
{
|
||||
size_t Result = 0;
|
||||
|
||||
for (Iterator Iter = A.Begin(); Iter != A.End(); ++Iter)
|
||||
for (FIterator Iter = A.Begin(); Iter != A.End(); ++Iter)
|
||||
{
|
||||
Result = HashCombine(Result, GetTypeHash(*Iter));
|
||||
}
|
||||
@@ -291,7 +291,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
struct FImplWithoutNum { ElementType* Pointer; };
|
||||
struct FImplWithoutNum { T* Pointer; };
|
||||
|
||||
struct FImplWithNum : FImplWithoutNum { size_t ArrayNum; };
|
||||
|
||||
@@ -299,42 +299,42 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
class Iterator final
|
||||
class FIterator final
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = TRemoveCV<T>;
|
||||
using FElementType = TRemoveCV<T>;
|
||||
|
||||
FORCEINLINE constexpr Iterator() = default;
|
||||
FORCEINLINE constexpr Iterator(const Iterator&) = default;
|
||||
FORCEINLINE constexpr Iterator(Iterator&&) = default;
|
||||
FORCEINLINE constexpr Iterator& operator=(const Iterator&) = default;
|
||||
FORCEINLINE constexpr Iterator& operator=(Iterator&&) = default;
|
||||
FORCEINLINE constexpr FIterator() = default;
|
||||
FORCEINLINE constexpr FIterator(const FIterator&) = default;
|
||||
FORCEINLINE constexpr FIterator(FIterator&&) = default;
|
||||
FORCEINLINE constexpr FIterator& operator=(const FIterator&) = default;
|
||||
FORCEINLINE constexpr FIterator& operator=(FIterator&&) = default;
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const Iterator& LHS, const Iterator& RHS) { return LHS.Pointer == RHS.Pointer; }
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const FIterator& LHS, const FIterator& RHS) { return LHS.Pointer == RHS.Pointer; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const Iterator& LHS, const Iterator& RHS) { return LHS.Pointer <=> RHS.Pointer; }
|
||||
NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const FIterator& LHS, const FIterator& RHS) { return LHS.Pointer <=> RHS.Pointer; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr T& operator*() const { CheckThis(true ); return *Pointer; }
|
||||
NODISCARD FORCEINLINE constexpr T* operator->() const { CheckThis(false); return Pointer; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr T& operator[](ptrdiff Index) const { Iterator Temp = *this + Index; return *Temp; }
|
||||
NODISCARD FORCEINLINE constexpr T& operator[](ptrdiff Index) const { FIterator Temp = *this + Index; return *Temp; }
|
||||
|
||||
FORCEINLINE constexpr Iterator& operator++() { ++Pointer; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr Iterator& operator--() { --Pointer; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr FIterator& operator++() { ++Pointer; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr FIterator& operator--() { --Pointer; CheckThis(); return *this; }
|
||||
|
||||
FORCEINLINE constexpr Iterator operator++(int) { Iterator Temp = *this; ++*this; return Temp; }
|
||||
FORCEINLINE constexpr Iterator operator--(int) { Iterator Temp = *this; --*this; return Temp; }
|
||||
FORCEINLINE constexpr FIterator operator++(int) { FIterator Temp = *this; ++*this; return Temp; }
|
||||
FORCEINLINE constexpr FIterator operator--(int) { FIterator Temp = *this; --*this; return Temp; }
|
||||
|
||||
FORCEINLINE constexpr Iterator& operator+=(ptrdiff Offset) { Pointer += Offset; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr Iterator& operator-=(ptrdiff Offset) { Pointer -= Offset; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr FIterator& operator+=(ptrdiff Offset) { Pointer += Offset; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr FIterator& operator-=(ptrdiff Offset) { Pointer -= Offset; CheckThis(); return *this; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr Iterator operator+(Iterator Iter, ptrdiff Offset) { Iterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
NODISCARD friend FORCEINLINE constexpr Iterator operator+(ptrdiff Offset, Iterator Iter) { Iterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
NODISCARD friend FORCEINLINE constexpr FIterator operator+(FIterator Iter, ptrdiff Offset) { FIterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
NODISCARD friend FORCEINLINE constexpr FIterator operator+(ptrdiff Offset, FIterator Iter) { FIterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr Iterator operator-(ptrdiff Offset) const { Iterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
NODISCARD FORCEINLINE constexpr FIterator operator-(ptrdiff Offset) const { FIterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const Iterator& LHS, const Iterator& RHS) { LHS.CheckThis(); RHS.CheckThis(); return LHS.Pointer - RHS.Pointer; }
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const FIterator& LHS, const FIterator& RHS) { LHS.CheckThis(); RHS.CheckThis(); return LHS.Pointer - RHS.Pointer; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -345,11 +345,11 @@ public:
|
||||
T* Pointer = nullptr;
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE constexpr Iterator(const TArrayView* InContainer, T* InPointer)
|
||||
FORCEINLINE constexpr FIterator(const TArrayView* InContainer, T* InPointer)
|
||||
: Owner(InContainer), Pointer(InPointer)
|
||||
{ }
|
||||
# else
|
||||
FORCEINLINE constexpr Iterator(const TArrayView* InContainer, T* InPointer)
|
||||
FORCEINLINE constexpr FIterator(const TArrayView* InContainer, T* InPointer)
|
||||
: Pointer(InPointer)
|
||||
{ }
|
||||
# endif
|
||||
@@ -367,7 +367,7 @@ public:
|
||||
};
|
||||
|
||||
template <typename I, typename S>
|
||||
TArrayView(I, S) -> TArrayView<TRemoveReference<TIteratorReferenceType<I>>>;
|
||||
TArrayView(I, S) -> TArrayView<TRemoveReference<TIteratorReference<I>>>;
|
||||
|
||||
template <typename T, size_t N>
|
||||
TArrayView(T(&)[N]) -> TArrayView<T, N>;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Memory/Memory.h"
|
||||
#include "Memory/Allocator.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "Templates/Noncopyable.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Memory/Allocators.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Memory/MemoryOperator.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
@@ -20,7 +21,7 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
template <CUnsignedIntegral InBlockType> requires (!CSameAs<InBlockType, bool>)
|
||||
using TDefaultBitsetAllocator = TInlineAllocator<(40 - 3 * sizeof(size_t)) / sizeof(InBlockType)>;
|
||||
|
||||
template <CUnsignedIntegral InBlockType, CAllocator<InBlockType> Allocator = TDefaultBitsetAllocator<InBlockType>> requires (!CSameAs<InBlockType, bool>)
|
||||
template <CUnsignedIntegral InBlockType, CAllocator<InBlockType> Allocator = TDefaultBitsetAllocator<InBlockType>> requires (CAllocatableObject<InBlockType> && !CSameAs<InBlockType, bool>)
|
||||
class TBitset
|
||||
{
|
||||
private:
|
||||
@@ -30,23 +31,23 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
using BlockType = InBlockType;
|
||||
using ElementType = bool;
|
||||
using AllocatorType = Allocator;
|
||||
using FBlockType = InBlockType;
|
||||
using FElementType = bool;
|
||||
using FAllocatorType = Allocator;
|
||||
|
||||
class Reference;
|
||||
using ConstReference = bool;
|
||||
class FReference;
|
||||
using FConstReference = bool;
|
||||
|
||||
using Iterator = TIteratorImpl<false>;
|
||||
using ConstIterator = TIteratorImpl<true >;
|
||||
using FIterator = TIteratorImpl<false>;
|
||||
using FConstIterator = TIteratorImpl<true >;
|
||||
|
||||
using ReverseIterator = TReverseIterator< Iterator>;
|
||||
using ConstReverseIterator = TReverseIterator<ConstIterator>;
|
||||
using FReverseIterator = TReverseIterator< FIterator>;
|
||||
using FConstReverseIterator = TReverseIterator<FConstIterator>;
|
||||
|
||||
static_assert(CRandomAccessIterator< Iterator>);
|
||||
static_assert(CRandomAccessIterator<ConstIterator>);
|
||||
static_assert(CRandomAccessIterator< FIterator>);
|
||||
static_assert(CRandomAccessIterator<FConstIterator>);
|
||||
|
||||
static constexpr size_t BlockWidth = sizeof(BlockType) * 8;
|
||||
static constexpr size_t BlockWidth = sizeof(FBlockType) * 8;
|
||||
|
||||
/** Default constructor. Constructs an empty bitset. */
|
||||
FORCEINLINE TBitset() : TBitset(0) { }
|
||||
@@ -62,59 +63,63 @@ public:
|
||||
/** Constructs a bitset from an integer. */
|
||||
TBitset(size_t InCount, uint64 InValue) : TBitset(InCount > 64 ? InCount : 64)
|
||||
{
|
||||
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
|
||||
static_assert(sizeof(FBlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
|
||||
|
||||
if constexpr (sizeof(BlockType) == sizeof(uint8))
|
||||
if constexpr (sizeof(FBlockType) == sizeof(uint8))
|
||||
{
|
||||
Impl.Pointer[0] = static_cast<BlockType>(InValue >> 0);
|
||||
Impl.Pointer[1] = static_cast<BlockType>(InValue >> 8);
|
||||
Impl.Pointer[2] = static_cast<BlockType>(InValue >> 16);
|
||||
Impl.Pointer[3] = static_cast<BlockType>(InValue >> 24);
|
||||
Impl.Pointer[4] = static_cast<BlockType>(InValue >> 32);
|
||||
Impl.Pointer[5] = static_cast<BlockType>(InValue >> 40);
|
||||
Impl.Pointer[6] = static_cast<BlockType>(InValue >> 48);
|
||||
Impl.Pointer[7] = static_cast<BlockType>(InValue >> 56);
|
||||
Impl.Pointer[0] = static_cast<FBlockType>(InValue >> 0);
|
||||
Impl.Pointer[1] = static_cast<FBlockType>(InValue >> 8);
|
||||
Impl.Pointer[2] = static_cast<FBlockType>(InValue >> 16);
|
||||
Impl.Pointer[3] = static_cast<FBlockType>(InValue >> 24);
|
||||
Impl.Pointer[4] = static_cast<FBlockType>(InValue >> 32);
|
||||
Impl.Pointer[5] = static_cast<FBlockType>(InValue >> 40);
|
||||
Impl.Pointer[6] = static_cast<FBlockType>(InValue >> 48);
|
||||
Impl.Pointer[7] = static_cast<FBlockType>(InValue >> 56);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint16))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint16))
|
||||
{
|
||||
Impl.Pointer[0] = static_cast<BlockType>(InValue >> 0);
|
||||
Impl.Pointer[1] = static_cast<BlockType>(InValue >> 16);
|
||||
Impl.Pointer[2] = static_cast<BlockType>(InValue >> 32);
|
||||
Impl.Pointer[3] = static_cast<BlockType>(InValue >> 48);
|
||||
Impl.Pointer[0] = static_cast<FBlockType>(InValue >> 0);
|
||||
Impl.Pointer[1] = static_cast<FBlockType>(InValue >> 16);
|
||||
Impl.Pointer[2] = static_cast<FBlockType>(InValue >> 32);
|
||||
Impl.Pointer[3] = static_cast<FBlockType>(InValue >> 48);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint32))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint32))
|
||||
{
|
||||
Impl.Pointer[0] = static_cast<BlockType>(InValue >> 0);
|
||||
Impl.Pointer[1] = static_cast<BlockType>(InValue >> 32);
|
||||
Impl.Pointer[0] = static_cast<FBlockType>(InValue >> 0);
|
||||
Impl.Pointer[1] = static_cast<FBlockType>(InValue >> 32);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint64))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint64))
|
||||
{
|
||||
Impl.Pointer[0] = static_cast<BlockType>(InValue >> 0);
|
||||
Impl.Pointer[0] = static_cast<FBlockType>(InValue >> 0);
|
||||
}
|
||||
else check_no_entry();
|
||||
|
||||
size_t BlockInteger = sizeof(uint64) / sizeof(BlockType);
|
||||
size_t BlockInteger = sizeof(uint64) / sizeof(FBlockType);
|
||||
|
||||
Memory::Memset(Impl.Pointer + BlockInteger, 0, (NumBlocks() - BlockInteger) * sizeof(BlockType));
|
||||
Memory::Memset(Impl.Pointer + BlockInteger, 0, (NumBlocks() - BlockInteger) * sizeof(FBlockType));
|
||||
|
||||
Impl.BitsetNum = InCount;
|
||||
}
|
||||
|
||||
/** Constructs the bitset with the bits of the range ['First', 'Last'). */
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<bool, TIteratorReferenceType<I>>)
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<bool, TIteratorReference<I>>)
|
||||
TBitset(I First, S Last)
|
||||
{
|
||||
if constexpr (CForwardIterator<I>)
|
||||
{
|
||||
if (CSizedSentinelFor<S, I>) { checkf(First <= Last, TEXT("Illegal range iterator. Please check First <= Last.")); }
|
||||
size_t Count = 0;
|
||||
|
||||
const size_t InCount = Iteration::Distance(First, Last);
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
|
||||
|
||||
new (this) TBitset(InCount);
|
||||
Count = Last - First;
|
||||
}
|
||||
else for (I Iter = First; Iter != Last; ++Iter) ++Count;
|
||||
|
||||
BlockType* CurrentBlock = Impl.Pointer - 1;
|
||||
new (this) TBitset(Count);
|
||||
|
||||
for (Reference Ref: *this) Ref = *First++;
|
||||
for (FReference Ref: *this) Ref = *First++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -128,6 +133,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs the bitset with the bits of the range. */
|
||||
template <CInputRange R> requires (!CSameAs<TRemoveCVRef<R>, TBitset> && CConstructibleFrom<bool, TRangeReference<R>>)
|
||||
FORCEINLINE explicit TBitset(R&& Range) : TBitset(Ranges::Begin(Range), Ranges::End(Range)) { }
|
||||
|
||||
/** Copy constructor. Constructs the bitset with the copy of the bits of 'InValue'. */
|
||||
FORCEINLINE TBitset(const TBitset& InValue)
|
||||
{
|
||||
@@ -135,7 +144,7 @@ public:
|
||||
Impl.BlocksMax = Impl->CalculateSlackReserve(NumBlocks());
|
||||
Impl.Pointer = Impl->Allocate(MaxBlocks());
|
||||
|
||||
Memory::Memcpy(Impl.Pointer, InValue.Impl.Pointer, NumBlocks() * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, InValue.Impl.Pointer, NumBlocks() * sizeof(FBlockType));
|
||||
}
|
||||
|
||||
/** Move constructor. After the move, 'InValue' is guaranteed to be empty. */
|
||||
@@ -157,12 +166,12 @@ public:
|
||||
Impl.BlocksMax = Impl->CalculateSlackReserve(NumBlocks());
|
||||
Impl.Pointer = Impl->Allocate(MaxBlocks());
|
||||
|
||||
Memory::Memcpy(Impl.Pointer, InValue.Impl.Pointer, NumBlocks() * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, InValue.Impl.Pointer, NumBlocks() * sizeof(FBlockType));
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs the bitset with the bits of the initializer list. */
|
||||
FORCEINLINE TBitset(initializer_list<bool> IL) : TBitset(Iteration::Begin(IL), Iteration::End(IL)) { }
|
||||
FORCEINLINE TBitset(initializer_list<bool> IL) : TBitset(Ranges::Begin(IL), Ranges::End(IL)) { }
|
||||
|
||||
/** Destructs the bitset. The storage is deallocated. */
|
||||
~TBitset()
|
||||
@@ -188,7 +197,7 @@ public:
|
||||
Impl.BlocksMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(MaxBlocks());
|
||||
|
||||
Memory::Memcpy(Impl.Pointer, InValue.Impl.Pointer, NumBlocks() * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, InValue.Impl.Pointer, NumBlocks() * sizeof(FBlockType));
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -197,7 +206,7 @@ public:
|
||||
|
||||
Impl.BitsetNum = InValue.Num();
|
||||
|
||||
Memory::Memcpy(Impl.Pointer, InValue.Impl.Pointer, NumBlocks() * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, InValue.Impl.Pointer, NumBlocks() * sizeof(FBlockType));
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -230,9 +239,9 @@ public:
|
||||
/** Replaces the bits with those identified by initializer list. */
|
||||
TBitset& operator=(initializer_list<bool> IL)
|
||||
{
|
||||
auto First = Iteration::Begin(IL);
|
||||
auto First = Ranges::Begin(IL);
|
||||
|
||||
const size_t BlocksCount = (GetNum(IL) + BlockWidth - 1) / BlockWidth;
|
||||
const size_t BlocksCount = (Ranges::Num(IL) + BlockWidth - 1) / BlockWidth;
|
||||
|
||||
size_t NumToAllocate = BlocksCount;
|
||||
|
||||
@@ -243,18 +252,18 @@ public:
|
||||
{
|
||||
Impl->Deallocate(Impl.Pointer);
|
||||
|
||||
Impl.BitsetNum = GetNum(IL);
|
||||
Impl.BitsetNum = Ranges::Num(IL);
|
||||
Impl.BlocksMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(MaxBlocks());
|
||||
|
||||
for (Reference Ref : *this) Ref = *First++;
|
||||
for (FReference Ref : *this) Ref = *First++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Impl.BitsetNum = GetNum(IL);
|
||||
Impl.BitsetNum = Ranges::Num(IL);
|
||||
|
||||
for (Reference Ref : *this) Ref = *First++;
|
||||
for (FReference Ref : *this) Ref = *First++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -271,7 +280,7 @@ public:
|
||||
if (LHS.Impl.Pointer[Index] != RHS.Impl.Pointer[Index]) return false;
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = LHS.Num() % BlockWidth != 0 ? (1ull << LHS.Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = LHS.Num() % BlockWidth != 0 ? (1ull << LHS.Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
return (LHS.Impl.Pointer[LHS.NumBlocks() - 1] & LastBlockBitmask) == (RHS.Impl.Pointer[LHS.NumBlocks() - 1] & LastBlockBitmask);
|
||||
}
|
||||
@@ -301,7 +310,7 @@ public:
|
||||
Impl.Pointer[Index] &= InValue.Impl.Pointer[Index];
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = InValue.Num() % BlockWidth != 0 ? (1ull << InValue.Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = InValue.Num() % BlockWidth != 0 ? (1ull << InValue.Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
Impl.Pointer[LastBlock] &= InValue.Impl.Pointer[LastBlock] & LastBlockBitmask;
|
||||
|
||||
@@ -339,7 +348,7 @@ public:
|
||||
Impl.Pointer[Index] |= InValue.Impl.Pointer[Index];
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = InValue.Num() % BlockWidth != 0 ? (1ull << InValue.Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = InValue.Num() % BlockWidth != 0 ? (1ull << InValue.Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
Impl.Pointer[LastBlock] |= InValue.Impl.Pointer[LastBlock] & LastBlockBitmask;
|
||||
}
|
||||
@@ -372,7 +381,7 @@ public:
|
||||
Impl.Pointer[Index] ^= InValue.Impl.Pointer[Index];
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = InValue.Num() % BlockWidth != 0 ? (1ull << InValue.Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = InValue.Num() % BlockWidth != 0 ? (1ull << InValue.Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
Impl.Pointer[LastBlock] ^= InValue.Impl.Pointer[LastBlock] & LastBlockBitmask;
|
||||
}
|
||||
@@ -473,7 +482,7 @@ public:
|
||||
if (Impl.Pointer[Index] != -1) return false;
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
return (Impl.Pointer[NumBlocks() - 1] | ~LastBlockBitmask) == -1;
|
||||
}
|
||||
@@ -488,7 +497,7 @@ public:
|
||||
if (Impl.Pointer[Index] != 0) return true;
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
return (Impl.Pointer[NumBlocks() - 1] & LastBlockBitmask) != 0;
|
||||
}
|
||||
@@ -503,24 +512,24 @@ public:
|
||||
|
||||
size_t Result = 0;
|
||||
|
||||
constexpr auto BlockCount = [](BlockType Block)
|
||||
constexpr auto BlockCount = [](FBlockType Block)
|
||||
{
|
||||
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
|
||||
static_assert(sizeof(FBlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
|
||||
|
||||
if constexpr (sizeof(BlockType) == sizeof(uint8))
|
||||
if constexpr (sizeof(FBlockType) == sizeof(uint8))
|
||||
{
|
||||
Block = (Block & 0x55ull) + ((Block >> 1) & 0x55ull);
|
||||
Block = (Block & 0x33ull) + ((Block >> 2) & 0x33ull);
|
||||
Block = (Block & 0x0Full) + ((Block >> 4) & 0x0Full);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint16))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint16))
|
||||
{
|
||||
Block = (Block & 0x5555ull) + ((Block >> 1) & 0x5555ull);
|
||||
Block = (Block & 0x3333ull) + ((Block >> 2) & 0x3333ull);
|
||||
Block = (Block & 0x0F0Full) + ((Block >> 4) & 0x0F0Full);
|
||||
Block = (Block & 0x00FFull) + ((Block >> 8) & 0x00FFull);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint32))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint32))
|
||||
{
|
||||
Block = (Block & 0x55555555ull) + ((Block >> 1) & 0x55555555ull);
|
||||
Block = (Block & 0x33333333ull) + ((Block >> 2) & 0x33333333ull);
|
||||
@@ -528,7 +537,7 @@ public:
|
||||
Block = (Block & 0x00FF00FFull) + ((Block >> 8) & 0x00FF00FFull);
|
||||
Block = (Block & 0x0000FFFFull) + ((Block >> 16) & 0x0000FFFFull);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint64))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint64))
|
||||
{
|
||||
Block = (Block & 0x5555555555555555ull) + ((Block >> 1) & 0x5555555555555555ull);
|
||||
Block = (Block & 0x3333333333333333ull) + ((Block >> 2) & 0x3333333333333333ull);
|
||||
@@ -547,7 +556,7 @@ public:
|
||||
Result += BlockCount(Impl.Pointer[Index]);
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
Result += BlockCount(Impl.Pointer[NumBlocks() - 1] & LastBlockBitmask);
|
||||
|
||||
@@ -557,7 +566,7 @@ public:
|
||||
/** Sets all bits to true. */
|
||||
TBitset& Set(bool InValue = true)
|
||||
{
|
||||
Memory::Memset(Impl.Pointer, static_cast<uint8>(InValue ? -1 : 0), NumBlocks() * sizeof(BlockType));
|
||||
Memory::Memset(Impl.Pointer, static_cast<uint8>(InValue ? -1 : 0), NumBlocks() * sizeof(FBlockType));
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -591,17 +600,17 @@ public:
|
||||
checkf(Impl.Pointer[Index] != 0, TEXT("The bitset can not be represented in uint64. Please check Num()."));
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const BlockType LastBlock = Impl.Pointer[NumBlocks() - 1] & LastBlockBitmask;
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlock = Impl.Pointer[NumBlocks() - 1] & LastBlockBitmask;
|
||||
|
||||
checkf(LastBlock != 0, TEXT("The bitset can not be represented in uint64. Please check Num()."));
|
||||
}
|
||||
|
||||
uint64 Result = 0;
|
||||
|
||||
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
|
||||
static_assert(sizeof(FBlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
|
||||
|
||||
if constexpr (sizeof(BlockType) == sizeof(uint8))
|
||||
if constexpr (sizeof(FBlockType) == sizeof(uint8))
|
||||
{
|
||||
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
|
||||
Result |= static_cast<uint64>(Impl.Pointer[1]) << 8;
|
||||
@@ -612,19 +621,19 @@ public:
|
||||
Result |= static_cast<uint64>(Impl.Pointer[6]) << 48;
|
||||
Result |= static_cast<uint64>(Impl.Pointer[7]) << 56;
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint16))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint16))
|
||||
{
|
||||
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
|
||||
Result |= static_cast<uint64>(Impl.Pointer[1]) << 16;
|
||||
Result |= static_cast<uint64>(Impl.Pointer[2]) << 32;
|
||||
Result |= static_cast<uint64>(Impl.Pointer[3]) << 48;
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint32))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint32))
|
||||
{
|
||||
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
|
||||
Result |= static_cast<uint64>(Impl.Pointer[1]) << 32;
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint64))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint64))
|
||||
{
|
||||
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
|
||||
}
|
||||
@@ -661,7 +670,7 @@ public:
|
||||
|
||||
if (NumToAllocate != MaxBlocks())
|
||||
{
|
||||
BlockType* OldAllocation = Impl.Pointer;
|
||||
FBlockType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = NumBlocks();
|
||||
|
||||
Impl.BitsetNum = InCount;
|
||||
@@ -670,11 +679,11 @@ public:
|
||||
|
||||
if (NumToDestruct <= Num())
|
||||
{
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, NumToDestruct * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, NumToDestruct * sizeof(FBlockType));
|
||||
}
|
||||
else
|
||||
{
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, BlocksCount * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, BlocksCount * sizeof(FBlockType));
|
||||
}
|
||||
|
||||
Impl->Deallocate(OldAllocation);
|
||||
@@ -697,12 +706,12 @@ public:
|
||||
NumToAllocate = NumToAllocate > MaxBlocks() ? Impl->CalculateSlackGrow(BlocksCount, MaxBlocks()) : NumToAllocate;
|
||||
NumToAllocate = NumToAllocate < MaxBlocks() ? (bAllowShrinking ? Impl->CalculateSlackShrink(BlocksCount, MaxBlocks()) : MaxBlocks()) : NumToAllocate;
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const BlockType BlocksValueToSet = static_cast<BlockType>(InValue ? -1 : 0);
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType BlocksValueToSet = static_cast<FBlockType>(InValue ? -1 : 0);
|
||||
|
||||
if (NumToAllocate != MaxBlocks())
|
||||
{
|
||||
BlockType* OldAllocation = Impl.Pointer;
|
||||
FBlockType* OldAllocation = Impl.Pointer;
|
||||
const size_t NumToDestruct = NumBlocks();
|
||||
|
||||
Impl.BitsetNum = InCount;
|
||||
@@ -713,16 +722,16 @@ public:
|
||||
{
|
||||
if (NumToDestruct != 0)
|
||||
{
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, (NumToDestruct - 1) * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, (NumToDestruct - 1) * sizeof(FBlockType));
|
||||
|
||||
Impl.Pointer[NumToDestruct - 1] = OldAllocation[NumToDestruct - 1] & LastBlockBitmask | BlocksValueToSet & ~LastBlockBitmask;
|
||||
}
|
||||
|
||||
Memory::Memset(Impl.Pointer + NumToDestruct, static_cast<uint8>(BlocksValueToSet), (BlocksCount - NumToDestruct) * sizeof(BlockType));
|
||||
Memory::Memset(Impl.Pointer + NumToDestruct, static_cast<uint8>(BlocksValueToSet), (BlocksCount - NumToDestruct) * sizeof(FBlockType));
|
||||
}
|
||||
else
|
||||
{
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, BlocksCount * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, BlocksCount * sizeof(FBlockType));
|
||||
}
|
||||
|
||||
Impl->Deallocate(OldAllocation);
|
||||
@@ -739,7 +748,7 @@ public:
|
||||
Impl.Pointer[NumBlocks() - 1] = Impl.Pointer[NumBlocks() - 1] & LastBlockBitmask | BlocksValueToSet & ~LastBlockBitmask;
|
||||
}
|
||||
|
||||
Memory::Memset(Impl.Pointer + NumBlocks(), static_cast<uint8>(BlocksValueToSet), (BlocksCount - NumBlocks()) * sizeof(BlockType));
|
||||
Memory::Memset(Impl.Pointer + NumBlocks(), static_cast<uint8>(BlocksValueToSet), (BlocksCount - NumBlocks()) * sizeof(FBlockType));
|
||||
}
|
||||
|
||||
Impl.BitsetNum = InCount;
|
||||
@@ -753,14 +762,14 @@ public:
|
||||
const size_t BlocksCount = (InCount + BlockWidth - 1) / BlockWidth;
|
||||
|
||||
const size_t NumToAllocate = Impl->CalculateSlackReserve(BlocksCount);
|
||||
BlockType* OldAllocation = Impl.Pointer;
|
||||
FBlockType* OldAllocation = Impl.Pointer;
|
||||
|
||||
check(NumToAllocate > MaxBlocks());
|
||||
|
||||
Impl.BlocksMax = NumToAllocate;
|
||||
Impl.Pointer = Impl->Allocate(MaxBlocks());
|
||||
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, NumBlocks() * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, NumBlocks() * sizeof(FBlockType));
|
||||
|
||||
Impl->Deallocate(OldAllocation);
|
||||
}
|
||||
@@ -774,31 +783,31 @@ public:
|
||||
|
||||
if (NumToAllocate == MaxBlocks()) return;
|
||||
|
||||
BlockType* OldAllocation = Impl.Pointer;
|
||||
FBlockType* OldAllocation = Impl.Pointer;
|
||||
|
||||
Impl.BitsetNum = NumToAllocate * BlockWidth;
|
||||
Impl.Pointer = Impl->Allocate(MaxBlocks());
|
||||
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, NumBlocks() * sizeof(BlockType));
|
||||
Memory::Memcpy(Impl.Pointer, OldAllocation, NumBlocks() * sizeof(FBlockType));
|
||||
|
||||
Impl->Deallocate(OldAllocation);
|
||||
}
|
||||
|
||||
/** @return The pointer to the underlying element storage. */
|
||||
NODISCARD FORCEINLINE BlockType* GetData() { return Impl.Pointer; }
|
||||
NODISCARD FORCEINLINE const BlockType* GetData() const { return Impl.Pointer; }
|
||||
NODISCARD FORCEINLINE FBlockType* GetData() { return Impl.Pointer; }
|
||||
NODISCARD FORCEINLINE const FBlockType* GetData() const { return Impl.Pointer; }
|
||||
|
||||
/** @return The iterator to the first or end bit. */
|
||||
NODISCARD FORCEINLINE Iterator Begin() { return Iterator(this, Impl.Pointer, 0); }
|
||||
NODISCARD FORCEINLINE ConstIterator Begin() const { return ConstIterator(this, Impl.Pointer, 0); }
|
||||
NODISCARD FORCEINLINE Iterator End() { return Iterator(this, Impl.Pointer, Num()); }
|
||||
NODISCARD FORCEINLINE ConstIterator End() const { return ConstIterator(this, Impl.Pointer, Num()); }
|
||||
NODISCARD FORCEINLINE FIterator Begin() { return FIterator(this, Impl.Pointer, 0); }
|
||||
NODISCARD FORCEINLINE FConstIterator Begin() const { return FConstIterator(this, Impl.Pointer, 0); }
|
||||
NODISCARD FORCEINLINE FIterator End() { return FIterator(this, Impl.Pointer, Num()); }
|
||||
NODISCARD FORCEINLINE FConstIterator End() const { return FConstIterator(this, Impl.Pointer, Num()); }
|
||||
|
||||
/** @return The reverse iterator to the first or end bit. */
|
||||
NODISCARD FORCEINLINE ReverseIterator RBegin() { return ReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE ConstReverseIterator RBegin() const { return ConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE ReverseIterator REnd() { return ReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE ConstReverseIterator REnd() const { return ConstReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE FReverseIterator RBegin() { return FReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE FConstReverseIterator RBegin() const { return FConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE FReverseIterator REnd() { return FReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE FConstReverseIterator REnd() const { return FConstReverseIterator(Begin()); }
|
||||
|
||||
/** @return The number of bits in the bitset. */
|
||||
NODISCARD FORCEINLINE size_t Num() const { return Impl.BitsetNum; }
|
||||
@@ -816,17 +825,17 @@ public:
|
||||
NODISCARD FORCEINLINE bool IsEmpty() const { return Num() == 0; }
|
||||
|
||||
/** @return true if the iterator is valid, false otherwise. */
|
||||
NODISCARD FORCEINLINE bool IsValidIterator(ConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
NODISCARD FORCEINLINE bool IsValidIterator(FConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
|
||||
/** @return The reference to the requested bit. */
|
||||
NODISCARD FORCEINLINE Reference operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return *(Begin() + Index); }
|
||||
NODISCARD FORCEINLINE ConstReference operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return *(Begin() + Index); }
|
||||
NODISCARD FORCEINLINE FReference operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return *(Begin() + Index); }
|
||||
NODISCARD FORCEINLINE FConstReference operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return *(Begin() + Index); }
|
||||
|
||||
/** @return The reference to the first or last bit. */
|
||||
NODISCARD FORCEINLINE Reference Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE ConstReference Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE Reference Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE ConstReference Back() const { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE FReference Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE FConstReference Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE FReference Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE FConstReference Back() const { return *(End() - 1); }
|
||||
|
||||
/** Erases all bits from the bitset. After this call, Num() returns zero. */
|
||||
void Reset(bool bAllowShrinking = true)
|
||||
@@ -859,7 +868,7 @@ public:
|
||||
Result = HashCombine(Result, GetTypeHash(A.Impl.Pointer[Index]));
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = A.Num() % BlockWidth != 0 ? (1ull << A.Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = A.Num() % BlockWidth != 0 ? (1ull << A.Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
return HashCombine(Result, GetTypeHash(A.Impl.Pointer[A.NumBlocks() - 1] & LastBlockBitmask));
|
||||
}
|
||||
@@ -889,27 +898,27 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
ALLOCATOR_WRAPPER_BEGIN(AllocatorType, BlockType, Impl)
|
||||
ALLOCATOR_WRAPPER_BEGIN(FAllocatorType, FBlockType, Impl)
|
||||
{
|
||||
size_t BitsetNum;
|
||||
size_t BlocksMax;
|
||||
BlockType* Pointer;
|
||||
FBlockType* Pointer;
|
||||
}
|
||||
ALLOCATOR_WRAPPER_END(AllocatorType, BlockType, Impl)
|
||||
ALLOCATOR_WRAPPER_END(FAllocatorType, FBlockType, Impl)
|
||||
|
||||
public:
|
||||
|
||||
class Reference final : private FSingleton
|
||||
class FReference final : private FSingleton
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE Reference& operator=(bool InValue) { Data = (Data & ~Mask) | (InValue ? Mask : 0); return *this; }
|
||||
FORCEINLINE FReference& operator=(bool InValue) { Data = (Data & ~Mask) | (InValue ? Mask : 0); return *this; }
|
||||
|
||||
FORCEINLINE Reference& operator=(const Reference& InValue) { *this = static_cast<bool>(InValue); return *this; }
|
||||
FORCEINLINE FReference& operator=(const FReference& InValue) { *this = static_cast<bool>(InValue); return *this; }
|
||||
|
||||
FORCEINLINE Reference& operator&=(bool InValue) { Data &= InValue ? -1 : ~Mask; return *this; }
|
||||
FORCEINLINE Reference& operator|=(bool InValue) { Data |= InValue ? Mask : 0; return *this; }
|
||||
FORCEINLINE Reference& operator^=(bool InValue) { *this = InValue ^ *this; return *this; }
|
||||
FORCEINLINE FReference& operator&=(bool InValue) { Data &= InValue ? -1 : ~Mask; return *this; }
|
||||
FORCEINLINE FReference& operator|=(bool InValue) { Data |= InValue ? Mask : 0; return *this; }
|
||||
FORCEINLINE FReference& operator^=(bool InValue) { *this = InValue ^ *this; return *this; }
|
||||
|
||||
FORCEINLINE bool operator~() const { return !*this; }
|
||||
|
||||
@@ -917,14 +926,14 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
FORCEINLINE Reference(BlockType& InData, BlockType InMask)
|
||||
FORCEINLINE FReference(FBlockType& InData, FBlockType InMask)
|
||||
: Data(InData), Mask(InMask)
|
||||
{ }
|
||||
|
||||
BlockType& Data;
|
||||
BlockType Mask;
|
||||
FBlockType& Data;
|
||||
FBlockType Mask;
|
||||
|
||||
friend Iterator;
|
||||
friend FIterator;
|
||||
|
||||
};
|
||||
|
||||
@@ -935,7 +944,7 @@ private:
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = bool;
|
||||
using FElementType = bool;
|
||||
|
||||
FORCEINLINE TIteratorImpl() = default;
|
||||
|
||||
@@ -958,8 +967,8 @@ private:
|
||||
|
||||
NODISCARD friend FORCEINLINE strong_ordering operator<=>(const TIteratorImpl& LHS, const TIteratorImpl& RHS) { check(LHS.Pointer == RHS.Pointer); return LHS.BitOffset <=> RHS.BitOffset; }
|
||||
|
||||
NODISCARD FORCEINLINE Reference operator*() const requires (!bConst) { CheckThis(true); return Reference(*(Pointer + BitOffset / BlockWidth), 1ull << BitOffset % BlockWidth); }
|
||||
NODISCARD FORCEINLINE ConstReference operator*() const requires ( bConst) { CheckThis(true); return (*(Pointer + BitOffset / BlockWidth) & (1ull << BitOffset % BlockWidth)); }
|
||||
NODISCARD FORCEINLINE FReference operator*() const requires (!bConst) { CheckThis(true); return FReference(*(Pointer + BitOffset / BlockWidth), 1ull << BitOffset % BlockWidth); }
|
||||
NODISCARD FORCEINLINE FConstReference operator*() const requires ( bConst) { CheckThis(true); return (*(Pointer + BitOffset / BlockWidth) & (1ull << BitOffset % BlockWidth)); }
|
||||
|
||||
NODISCARD FORCEINLINE auto operator[](ptrdiff Index) const { TIteratorImpl Temp = *this + Index; return *Temp; }
|
||||
|
||||
@@ -985,17 +994,17 @@ private:
|
||||
const TBitset* Owner = nullptr;
|
||||
# endif
|
||||
|
||||
using BlockPtr = TConditional<bConst, const BlockType*, BlockType*>;
|
||||
using FBlockPtr = TConditional<bConst, const FBlockType*, FBlockType*>;
|
||||
|
||||
BlockPtr Pointer = nullptr;
|
||||
FBlockPtr Pointer = nullptr;
|
||||
size_t BitOffset = 0;
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE TIteratorImpl(const TBitset* InContainer, BlockPtr InPointer, size_t Offset)
|
||||
FORCEINLINE TIteratorImpl(const TBitset* InContainer, FBlockPtr InPointer, size_t Offset)
|
||||
: Owner(InContainer), Pointer(InPointer), BitOffset(Offset)
|
||||
{ }
|
||||
# else
|
||||
FORCEINLINE TIteratorImpl(const TBitset* InContainer, BlockPtr InPointer, size_t Offset)
|
||||
FORCEINLINE TIteratorImpl(const TBitset* InContainer, FBlockPtr InPointer, size_t Offset)
|
||||
: Pointer(InPointer), BitOffset(Offset)
|
||||
{ }
|
||||
# endif
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "Containers/StaticArray.h"
|
||||
#include "Containers/ArrayView.h"
|
||||
|
||||
@@ -1,765 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Templates/Invoke.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/Noncopyable.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename T> using WithReference = T&;
|
||||
|
||||
template <typename I> struct TIteratorElementType { using Type = typename I::ElementType; };
|
||||
|
||||
template <typename T> struct TIteratorElementType<T*> { using Type = TRemoveCV<T>; };
|
||||
|
||||
template <typename I> struct TIteratorPointerType { using Type = void; };
|
||||
|
||||
template <typename T> struct TIteratorPointerType<T*> { using Type = T*; };
|
||||
|
||||
template <typename I> requires (requires(I& Iter) { { Iter.operator->() } -> CPointer; })
|
||||
struct TIteratorPointerType<I> { using Type = decltype(DeclVal<I&>().operator->()); };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <typename T>
|
||||
concept CReferenceable = requires { typename NAMESPACE_PRIVATE::WithReference<T>; };
|
||||
|
||||
template <typename T>
|
||||
concept CDereferenceable = requires(T& A) { { *A } -> CReferenceable; };
|
||||
|
||||
template <typename I>
|
||||
using TIteratorElementType = typename NAMESPACE_PRIVATE::TIteratorElementType<TRemoveCVRef<I>>::Type;
|
||||
|
||||
template <typename I>
|
||||
using TIteratorPointerType = typename NAMESPACE_PRIVATE::TIteratorPointerType<TRemoveCVRef<I>>::Type;
|
||||
|
||||
template <CReferenceable I>
|
||||
using TIteratorReferenceType = decltype(*DeclVal<I&>());
|
||||
|
||||
template <CReferenceable I> requires (requires(I& Iter) { { MoveTemp(*Iter) } -> CReferenceable; })
|
||||
using TIteratorRValueReferenceType = decltype(MoveTemp(*DeclVal<I&>()));
|
||||
|
||||
template <typename I>
|
||||
concept CIndirectlyReadable =
|
||||
requires(const TRemoveCVRef<I> Iter)
|
||||
{
|
||||
typename TIteratorElementType<I>;
|
||||
typename TIteratorReferenceType<I>;
|
||||
typename TIteratorRValueReferenceType<I>;
|
||||
{ *Iter } -> CSameAs<TIteratorReferenceType<I>>;
|
||||
{ MoveTemp(*Iter) } -> CSameAs<TIteratorRValueReferenceType<I>>;
|
||||
}
|
||||
&& CSameAs<TIteratorElementType<I>, TRemoveCVRef<TIteratorElementType<I>>>
|
||||
&& CCommonReference<TIteratorReferenceType<I>&&, TIteratorElementType<I>&>
|
||||
&& CCommonReference<TIteratorReferenceType<I>&&, TIteratorRValueReferenceType<I>&&>
|
||||
&& CCommonReference<TIteratorRValueReferenceType<I>&&, const TIteratorElementType<I>&>;
|
||||
|
||||
template <typename I, typename T>
|
||||
concept CIndirectlyWritable =
|
||||
requires(I&& Iter, T&& A)
|
||||
{
|
||||
*Iter = Forward<T>(A);
|
||||
*Forward<I>(Iter) = Forward<T>(A);
|
||||
const_cast<const TIteratorReferenceType<I>&&>(*Iter) = Forward<T>(A);
|
||||
const_cast<const TIteratorReferenceType<I>&&>(*Forward<I>(Iter)) = Forward<T>(A);
|
||||
};
|
||||
|
||||
template <typename I>
|
||||
concept CWeaklyIncrementable = CMovable<I>
|
||||
&& requires(I Iter) { { ++Iter } -> CSameAs<I&>; Iter++; };
|
||||
|
||||
template <typename I>
|
||||
concept CIncrementable = CRegular<I> && CWeaklyIncrementable<I>
|
||||
&& requires(I Iter) { { Iter++ } -> CSameAs<I>; };
|
||||
|
||||
template <typename I>
|
||||
concept CInputOrOutputIterator = CWeaklyIncrementable<I>
|
||||
&& requires(I Iter) { { *Iter } -> CReferenceable; };
|
||||
|
||||
template <typename S, typename I>
|
||||
concept CSentinelFor = CSemiregular<S> && CInputOrOutputIterator<I> && CWeaklyEqualityComparable<S, I>;
|
||||
|
||||
template <typename S, typename I>
|
||||
inline constexpr bool bDisableSizedSentinelFor = false;
|
||||
|
||||
template <typename S, typename I>
|
||||
concept CSizedSentinelFor = CSentinelFor<S, I> && CPartiallyOrdered<S, I> && !bDisableSizedSentinelFor<TRemoveCV<S>, TRemoveCV<I>>
|
||||
&& requires(const I& Iter, const S& Sentinel) { Sentinel - Iter; Iter - Sentinel; };
|
||||
|
||||
template <typename I>
|
||||
concept CInputIterator = CInputOrOutputIterator<I> && CIndirectlyReadable<I>;
|
||||
|
||||
template <typename I, typename T>
|
||||
concept COutputIterator = CInputOrOutputIterator<I> && CIndirectlyWritable<I, T>
|
||||
&& requires(I Iter, T&& A) { *Iter++ = Forward<T>(A); };
|
||||
|
||||
template <typename I>
|
||||
concept CForwardIterator = CInputIterator<I> && CIncrementable<I> && CSentinelFor<I, I>;
|
||||
|
||||
template <typename I>
|
||||
concept CBidirectionalIterator = CForwardIterator<I>
|
||||
&& requires(I Iter) {
|
||||
{ --Iter } -> CSameAs<I&>;
|
||||
{ Iter-- } -> CSameAs<I >;
|
||||
};
|
||||
|
||||
template <typename I>
|
||||
concept CRandomAccessIterator = CBidirectionalIterator<I> && CTotallyOrdered<I> && CSizedSentinelFor<I, I>
|
||||
&& requires(I Iter, const I Jter, const ptrdiff N) {
|
||||
{ Iter += N } -> CSameAs<I&>;
|
||||
{ Jter + N } -> CSameAs<I >;
|
||||
{ N + Jter } -> CSameAs<I >;
|
||||
{ Iter -= N } -> CSameAs<I&>;
|
||||
{ Jter - N } -> CSameAs<I >;
|
||||
{ Jter[N] } -> CSameAs<TIteratorReferenceType<I>>;
|
||||
};
|
||||
|
||||
template <typename I>
|
||||
concept CContiguousIterator = CRandomAccessIterator<I> && CLValueReference<TIteratorReferenceType<I>>
|
||||
&& CSameAs<TIteratorElementType<I>, TRemoveCVRef<TIteratorReferenceType<I>>>
|
||||
&& requires(I& Iter)
|
||||
{
|
||||
{ ToAddress(Iter) } -> CSameAs<TAddPointer<TIteratorReferenceType<I>>>;
|
||||
};
|
||||
|
||||
static_assert(CContiguousIterator<int32*>);
|
||||
|
||||
/** A iterator adaptor for reverse-order traversal. */
|
||||
template <CBidirectionalIterator I>
|
||||
class TReverseIterator final
|
||||
{
|
||||
public:
|
||||
|
||||
using IteratorType = I;
|
||||
|
||||
using ElementType = TIteratorElementType<I>;
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator() = default;
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator(const TReverseIterator&) = default;
|
||||
FORCEINLINE constexpr TReverseIterator(TReverseIterator&&) = default;
|
||||
FORCEINLINE constexpr TReverseIterator& operator=(const TReverseIterator&) = default;
|
||||
FORCEINLINE constexpr TReverseIterator& operator=(TReverseIterator&&) = default;
|
||||
|
||||
FORCEINLINE constexpr ~TReverseIterator() = default;
|
||||
|
||||
template <typename T = IteratorType> requires (!CSameAs<TReverseIterator, TRemoveCVRef<T>> && CConstructibleFrom<IteratorType, T>)
|
||||
FORCEINLINE constexpr explicit TReverseIterator(T&& InValue) : Current(Forward<T>(InValue)) { }
|
||||
|
||||
template <CBidirectionalIterator J> requires (!CSameAs<IteratorType, J> && CConstructibleFrom<IteratorType, const J&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const J&, IteratorType>) TReverseIterator(const TReverseIterator<J>& InValue) : Current(InValue.GetBase()) { }
|
||||
|
||||
template <CBidirectionalIterator J> requires (!CSameAs<IteratorType, J> && CConstructibleFrom<IteratorType, J>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<J&&, IteratorType>) TReverseIterator(TReverseIterator<J>&& InValue) : Current(MoveTemp(InValue).GetBase()) { }
|
||||
|
||||
template <CBidirectionalIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<const J&, IteratorType> && CAssignableFrom<IteratorType&, const J&>)
|
||||
FORCEINLINE constexpr TReverseIterator& operator=(const TReverseIterator<J>& InValue) { Current = InValue.GetBase(); return *this; }
|
||||
|
||||
template <CBidirectionalIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<J&&, IteratorType> && CAssignableFrom<IteratorType&, J&&>)
|
||||
FORCEINLINE constexpr TReverseIterator& operator=(TReverseIterator<J>&& InValue) { Current = MoveTemp(InValue).GetBase(); return *this; }
|
||||
|
||||
template <CBidirectionalIterator J> requires (CSentinelFor<J, IteratorType>)
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TReverseIterator& LHS, const TReverseIterator<J>& RHS) { return LHS.GetBase() == RHS.GetBase(); }
|
||||
|
||||
template <CBidirectionalIterator J> requires (CSizedSentinelFor<J, IteratorType>)
|
||||
NODISCARD friend FORCEINLINE constexpr TCompareThreeWayResult<J, IteratorType> operator<=>(const TReverseIterator& LHS, const TReverseIterator<J>& RHS) { return RHS.GetBase() <=> LHS.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator*() const { IteratorType Temp = GetBase(); return *--Temp; }
|
||||
NODISCARD FORCEINLINE constexpr TIteratorPointerType<IteratorType> operator->() const { IteratorType Temp = GetBase(); return ToAddress(--Temp); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator[](ptrdiff Index) const requires (CRandomAccessIterator<IteratorType>) { return GetBase()[-Index - 1]; }
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator& operator++() { --Current; return *this; }
|
||||
FORCEINLINE constexpr TReverseIterator& operator--() { ++Current; return *this; }
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator operator++(int) { TReverseIterator Temp = *this; --Current; return Temp; }
|
||||
FORCEINLINE constexpr TReverseIterator operator--(int) { TReverseIterator Temp = *this; ++Current; return Temp; }
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator& operator+=(ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { Current -= Offset; return *this; }
|
||||
FORCEINLINE constexpr TReverseIterator& operator-=(ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { Current += Offset; return *this; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr TReverseIterator operator+(TReverseIterator Iter, ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { TReverseIterator Temp = Iter; Temp -= Offset; return Temp; }
|
||||
NODISCARD friend FORCEINLINE constexpr TReverseIterator operator+(ptrdiff Offset, TReverseIterator Iter) requires (CRandomAccessIterator<IteratorType>) { TReverseIterator Temp = Iter; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TReverseIterator operator-(ptrdiff Offset) const requires (CRandomAccessIterator<IteratorType>) { TReverseIterator Temp = *this; Temp += Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TReverseIterator& LHS, const TReverseIterator& RHS) { return RHS.GetBase() - LHS.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const IteratorType& GetBase() const& { return Current; }
|
||||
NODISCARD FORCEINLINE constexpr IteratorType GetBase() && { return Current; }
|
||||
|
||||
private:
|
||||
|
||||
IteratorType Current;
|
||||
|
||||
};
|
||||
|
||||
static_assert(CRandomAccessIterator<TReverseIterator<int32*>>);
|
||||
|
||||
template <typename I, typename J> requires (!CSizedSentinelFor<I, J>)
|
||||
inline constexpr bool bDisableSizedSentinelFor<TReverseIterator<I>, TReverseIterator<J>> = true;
|
||||
|
||||
/** An iterator adaptor which dereferences to a rvalue reference. */
|
||||
template <CInputIterator I>
|
||||
class TMoveIterator final
|
||||
{
|
||||
public:
|
||||
|
||||
using IteratorType = I;
|
||||
|
||||
using ElementType = TIteratorElementType<I>;
|
||||
|
||||
FORCEINLINE constexpr TMoveIterator() = default;
|
||||
|
||||
FORCEINLINE constexpr TMoveIterator(const TMoveIterator&) = default;
|
||||
FORCEINLINE constexpr TMoveIterator(TMoveIterator&&) = default;
|
||||
FORCEINLINE constexpr TMoveIterator& operator=(const TMoveIterator&) = default;
|
||||
FORCEINLINE constexpr TMoveIterator& operator=(TMoveIterator&&) = default;
|
||||
|
||||
FORCEINLINE constexpr ~TMoveIterator() = default;
|
||||
|
||||
template <typename T = IteratorType> requires (!CSameAs<TMoveIterator, TRemoveCVRef<T>> && CConstructibleFrom<IteratorType, T>)
|
||||
FORCEINLINE constexpr explicit TMoveIterator(T&& InValue) : Current(Forward<T>(InValue)) { }
|
||||
|
||||
template <CInputIterator J> requires (!CSameAs<IteratorType, J> && CConstructibleFrom<IteratorType, const J&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const J&, IteratorType>) TMoveIterator(const TMoveIterator<J>& InValue) : Current(InValue.GetBase()) { }
|
||||
|
||||
template <CInputIterator J> requires (!CSameAs<IteratorType, J> && CConstructibleFrom<IteratorType, J>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<J&&, IteratorType>) TMoveIterator(TMoveIterator<J>&& InValue) : Current(MoveTemp(InValue).GetBase()) { }
|
||||
|
||||
template <CInputIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<const J&, IteratorType> && CAssignableFrom<IteratorType&, const J&>)
|
||||
FORCEINLINE constexpr TMoveIterator& operator=(const TMoveIterator<J>& InValue) { Current = InValue.GetBase(); return *this; }
|
||||
|
||||
template <CInputIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<J&&, IteratorType> && CAssignableFrom<IteratorType&, J&&>)
|
||||
FORCEINLINE constexpr TMoveIterator& operator=(TMoveIterator<J>&& InValue) { Current = MoveTemp(InValue).GetBase(); return *this; }
|
||||
|
||||
template <CInputIterator J> requires (CSentinelFor<J, IteratorType>)
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TMoveIterator& LHS, const TMoveIterator<J>& RHS) { return LHS.GetBase() == RHS.GetBase(); }
|
||||
|
||||
template <CInputIterator J> requires (CSizedSentinelFor<J, IteratorType>)
|
||||
NODISCARD friend FORCEINLINE constexpr TCompareThreeWayResult<J, IteratorType> operator<=>(const TMoveIterator& LHS, const TMoveIterator<J>& RHS) { return LHS.GetBase() <=> RHS.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorRValueReferenceType<IteratorType> operator*() const { return MoveTemp(*GetBase()); }
|
||||
NODISCARD FORCEINLINE constexpr TIteratorPointerType<IteratorType> operator->() const = delete;
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorRValueReferenceType<IteratorType> operator[](ptrdiff Index) const requires (CRandomAccessIterator<IteratorType>) { return MoveTemp(GetBase()[Index]); }
|
||||
|
||||
FORCEINLINE constexpr TMoveIterator& operator++() { ++Current; return *this; }
|
||||
FORCEINLINE constexpr TMoveIterator& operator--() requires (CBidirectionalIterator<IteratorType>) { --Current; return *this; }
|
||||
|
||||
FORCEINLINE constexpr void operator++(int) { Current++; }
|
||||
FORCEINLINE constexpr TMoveIterator operator++(int) requires (CForwardIterator<IteratorType>) { return TMoveIterator(Current++); }
|
||||
FORCEINLINE constexpr TMoveIterator operator--(int) requires (CBidirectionalIterator<IteratorType>) { return TMoveIterator(Current--); }
|
||||
|
||||
FORCEINLINE constexpr TMoveIterator& operator+=(ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { Current += Offset; return *this; }
|
||||
FORCEINLINE constexpr TMoveIterator& operator-=(ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { Current -= Offset; return *this; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr TMoveIterator operator+(TMoveIterator Iter, ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { TMoveIterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
NODISCARD friend FORCEINLINE constexpr TMoveIterator operator+(ptrdiff Offset, TMoveIterator Iter) requires (CRandomAccessIterator<IteratorType>) { TMoveIterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TMoveIterator operator-(ptrdiff Offset) const requires (CRandomAccessIterator<IteratorType>) { TMoveIterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TMoveIterator& LHS, const TMoveIterator& RHS) { return LHS.GetBase() - RHS.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const IteratorType& GetBase() const& { return Current; }
|
||||
NODISCARD FORCEINLINE constexpr IteratorType GetBase() && { return Current; }
|
||||
|
||||
private:
|
||||
|
||||
IteratorType Current;
|
||||
|
||||
};
|
||||
|
||||
static_assert(CRandomAccessIterator<TMoveIterator<int32*>>);
|
||||
|
||||
/** A sentinel adaptor for use with TMoveIterator. */
|
||||
template <CSemiregular S>
|
||||
class TMoveSentinel
|
||||
{
|
||||
public:
|
||||
|
||||
using SentinelType = S;
|
||||
|
||||
FORCEINLINE constexpr TMoveSentinel() = default;
|
||||
|
||||
FORCEINLINE constexpr TMoveSentinel(const TMoveSentinel&) = default;
|
||||
FORCEINLINE constexpr TMoveSentinel(TMoveSentinel&&) = default;
|
||||
FORCEINLINE constexpr TMoveSentinel& operator=(const TMoveSentinel&) = default;
|
||||
FORCEINLINE constexpr TMoveSentinel& operator=(TMoveSentinel&&) = default;
|
||||
|
||||
FORCEINLINE constexpr ~TMoveSentinel() = default;
|
||||
|
||||
template <typename T = SentinelType> requires (!CSameAs<TMoveSentinel, TRemoveCVRef<T>> && CConstructibleFrom<SentinelType, T>)
|
||||
FORCEINLINE constexpr explicit TMoveSentinel(T&& InValue) : Last(Forward<T>(InValue)) { }
|
||||
|
||||
template <CSemiregular T> requires (!CSameAs<SentinelType, T> && CConstructibleFrom<SentinelType, const T&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const T&, SentinelType>) TMoveSentinel(const TMoveSentinel<T>& InValue) : Last(InValue.GetBase()) { }
|
||||
|
||||
template <CSemiregular T> requires (!CSameAs<SentinelType, T> && CConstructibleFrom<SentinelType, T>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<T&&, SentinelType>) TMoveSentinel(TMoveSentinel<T>&& InValue) : Last(MoveTemp(InValue).GetBase()) { }
|
||||
|
||||
template <CSemiregular T> requires (!CSameAs<SentinelType, T> && CConvertibleTo<const T&, SentinelType> && CAssignableFrom<SentinelType&, const T&>)
|
||||
FORCEINLINE constexpr TMoveSentinel& operator=(const TMoveSentinel<T>& InValue) { Last = InValue.GetBase(); return *this; }
|
||||
|
||||
template <CSemiregular T> requires (!CSameAs<SentinelType, T> && CConvertibleTo<T&&, SentinelType> && CAssignableFrom<SentinelType&, T&&>)
|
||||
FORCEINLINE constexpr TMoveSentinel& operator=(TMoveSentinel<T>&& InValue) { Last = MoveTemp(InValue).GetBase(); return *this; }
|
||||
|
||||
template <CInputIterator I> requires (CSentinelFor<SentinelType, I>)
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const TMoveIterator<I>& InValue) const& { return GetBase() == InValue.GetBase(); }
|
||||
|
||||
template <CInputIterator I> requires (CSizedSentinelFor<SentinelType, I>)
|
||||
NODISCARD FORCEINLINE constexpr TCompareThreeWayResult<SentinelType, I> operator<=>(const TMoveIterator<I>& InValue) const& { return GetBase() <=> InValue.GetBase(); }
|
||||
|
||||
template <CInputIterator I> requires (CSizedSentinelFor<SentinelType, I>)
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TMoveSentinel& Sentinel, const TMoveIterator<I>& Iter) { return Sentinel.GetBase() - Iter.GetBase(); }
|
||||
|
||||
template <CInputIterator I> requires (CSizedSentinelFor<SentinelType, I>)
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TMoveIterator<I>& Iter, const TMoveSentinel& Sentinel) { return Iter.GetBase() - Sentinel.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const SentinelType& GetBase() const& { return Last; }
|
||||
NODISCARD FORCEINLINE constexpr SentinelType GetBase() && { return Last; }
|
||||
|
||||
private:
|
||||
|
||||
SentinelType Last;
|
||||
|
||||
};
|
||||
|
||||
static_assert(CSizedSentinelFor<TMoveSentinel<int32*>, TMoveIterator<int32*>>);
|
||||
|
||||
struct FDefaultSentinel { explicit FDefaultSentinel() = default; };
|
||||
|
||||
inline constexpr FDefaultSentinel DefaultSentinel{ };
|
||||
|
||||
struct FUnreachableSentinel
|
||||
{
|
||||
explicit FUnreachableSentinel() = default;
|
||||
|
||||
template<CInputOrOutputIterator I>
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const I&) const& { return false; }
|
||||
};
|
||||
|
||||
inline constexpr FUnreachableSentinel UnreachableSentinel{ };
|
||||
|
||||
/** An iterator adaptor that tracks the distance to the end of the range. */
|
||||
template <CInputOrOutputIterator I>
|
||||
class TCountedIterator final
|
||||
{
|
||||
public:
|
||||
|
||||
using IteratorType = I;
|
||||
|
||||
using ElementType = TIteratorElementType<I>;
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<IteratorType>) : Length(1), MaxLength(0) { }
|
||||
# else
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<IteratorType>) = default;
|
||||
# endif
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator(const TCountedIterator&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator(TCountedIterator&&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(const TCountedIterator&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(TCountedIterator&&) = default;
|
||||
|
||||
FORCEINLINE constexpr ~TCountedIterator() = default;
|
||||
|
||||
template <typename T = IteratorType> requires (!CSameAs<TCountedIterator, TRemoveCVRef<T>> && CConstructibleFrom<IteratorType, T>)
|
||||
FORCEINLINE constexpr explicit TCountedIterator(T&& InValue, ptrdiff N) : Current(Forward<T>(InValue)), Length(N) { check_code({ MaxLength = N; }); }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConstructibleFrom<IteratorType, const J&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const J&, IteratorType>) TCountedIterator(const TCountedIterator<J>& InValue) : Current(InValue.GetBase()), Length(InValue.Num()) { check_code({ MaxLength = InValue.MaxLength; }); }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConstructibleFrom<IteratorType, J>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<J&&, IteratorType>) TCountedIterator(TCountedIterator<J>&& InValue) : Current(MoveTemp(InValue).GetBase()), Length(InValue.Num()) { check_code({ MaxLength = InValue.MaxLength; }); }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<const J&, IteratorType> && CAssignableFrom<IteratorType&, const J&>)
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(const TCountedIterator<J>& InValue) { Current = InValue.GetBase(); Length = InValue.Num(); check_code({ MaxLength = InValue.MaxLength; }); return *this; }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<J&&, IteratorType> && CAssignableFrom<IteratorType&, J&&>)
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(TCountedIterator<J>&& InValue) { Current = MoveTemp(InValue).GetBase(); Length = InValue.Num(); check_code({ MaxLength = InValue.MaxLength; }); return *this; }
|
||||
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.Length == RHS.Length; }
|
||||
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.Length <=> RHS.Length; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(FDefaultSentinel) const& { return Length == static_cast<ptrdiff>(0); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(FDefaultSentinel) const& { return static_cast<ptrdiff>(0) <=> Length; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator*() { CheckThis(true); return *Current; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator*() const requires (CDereferenceable<const IteratorType>) { CheckThis(true); return *Current; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorPointerType<IteratorType> operator->() const requires (CContiguousIterator<IteratorType>) { CheckThis(false); return ToAddress(Current); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator[](ptrdiff Index) const requires (CRandomAccessIterator<IteratorType>) { TCountedIterator Temp = *this + Index; return *Temp; }
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator& operator++() { ++Current; --Length; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr TCountedIterator& operator--() requires (CBidirectionalIterator<IteratorType>) { --Current; ++Length; CheckThis(); return *this; }
|
||||
|
||||
FORCEINLINE constexpr decltype(auto) operator++(int) { --Length; CheckThis(); return Current++; }
|
||||
FORCEINLINE constexpr TCountedIterator operator++(int) requires (CForwardIterator<IteratorType>) { TCountedIterator Temp = *this; ++Current; --Length; CheckThis(); return Temp; }
|
||||
FORCEINLINE constexpr TCountedIterator operator--(int) requires (CBidirectionalIterator<IteratorType>) { TCountedIterator Temp = *this; --Current; ++Length; CheckThis(); return Temp; }
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator& operator+=(ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { Current += Offset; Length -= Offset; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr TCountedIterator& operator-=(ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { Current -= Offset; Length += Offset; CheckThis(); return *this; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr TCountedIterator operator+(TCountedIterator Iter, ptrdiff Offset) requires (CRandomAccessIterator<IteratorType>) { TCountedIterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
NODISCARD friend FORCEINLINE constexpr TCountedIterator operator+(ptrdiff Offset, TCountedIterator Iter) requires (CRandomAccessIterator<IteratorType>) { TCountedIterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TCountedIterator operator-(ptrdiff Offset) const requires (CRandomAccessIterator<IteratorType>) { TCountedIterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { LHS.CheckThis(); RHS.CheckThis(); return LHS.Length - RHS.Length; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, FDefaultSentinel) { LHS.CheckThis(); return -LHS.Num(); }
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(FDefaultSentinel, const TCountedIterator& RHS) { RHS.CheckThis(); return RHS.Num(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const IteratorType& GetBase() const& { CheckThis(); return Current; }
|
||||
NODISCARD FORCEINLINE constexpr IteratorType GetBase() && { CheckThis(); return Current; }
|
||||
NODISCARD FORCEINLINE constexpr ptrdiff Num() const { CheckThis(); return Length; }
|
||||
|
||||
private:
|
||||
|
||||
IteratorType Current;
|
||||
ptrdiff Length;
|
||||
|
||||
# if DO_CHECK
|
||||
ptrdiff MaxLength;
|
||||
# endif
|
||||
|
||||
FORCEINLINE void CheckThis(bool bExceptEnd = false) const
|
||||
{
|
||||
checkf(static_cast<ptrdiff>(0) <= Length && Length <= MaxLength, TEXT("Read access violation. Please check Num()."));
|
||||
checkf(!(bExceptEnd && Length == static_cast<ptrdiff>(0)), TEXT("Read access violation. Please check Num()."));
|
||||
}
|
||||
|
||||
template <CInputOrOutputIterator J>
|
||||
friend class TCountedIterator;
|
||||
|
||||
};
|
||||
|
||||
static_assert(CContiguousIterator<TCountedIterator<int32*>>);
|
||||
static_assert(CSizedSentinelFor<FDefaultSentinel, TCountedIterator<int32*>>);
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
/** An output iterator adapter that wraps a callable object. */
|
||||
template <CMoveConstructible F>
|
||||
class TOutputIterator final : private FNoncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
using Outputer = F;
|
||||
|
||||
private:
|
||||
|
||||
class FIndirectionProxy : private FSingleton
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr FIndirectionProxy(TOutputIterator& InIter) : Iter(InIter) { check_code({ bIsProduced = false; }); }
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE ~FIndirectionProxy()
|
||||
{
|
||||
checkf(bIsProduced, TEXT("Exception output, Ensures that the value is assigned to the output iterator."));
|
||||
}
|
||||
# endif
|
||||
|
||||
template <typename T> requires (CInvocable<Outputer, T>)
|
||||
FORCEINLINE constexpr void operator=(T&& InValue) const
|
||||
{
|
||||
checkf(!bIsProduced, TEXT("Exception output, Ensure that no multiple values are assigned to the output iterator."));
|
||||
Invoke(Iter.Storage, Forward<T>(InValue));
|
||||
check_code({ bIsProduced = true; });
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TOutputIterator& Iter;
|
||||
|
||||
# if DO_CHECK
|
||||
mutable bool bIsProduced;
|
||||
# endif
|
||||
|
||||
};
|
||||
|
||||
class FPostIncrementProxy : private FSingleton
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr FPostIncrementProxy(TOutputIterator& InIter) : Iter(InIter) { check_code({ bIsProduced = false; }); }
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE ~FPostIncrementProxy()
|
||||
{
|
||||
checkf(bIsProduced, TEXT("Exception output, Ensures that the value is assigned to the output iterator."));
|
||||
}
|
||||
# endif
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FIndirectionProxy operator*() const
|
||||
{
|
||||
checkf(!bIsProduced, TEXT("Exception output, Ensure that no multiple values are assigned to the output iterator."));
|
||||
check_code({ bIsProduced = true; });
|
||||
return FIndirectionProxy(Iter);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TOutputIterator& Iter;
|
||||
|
||||
# if DO_CHECK
|
||||
mutable bool bIsProduced;
|
||||
# endif
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr TOutputIterator() requires (CDefaultConstructible<Outputer>) { check_code({ bIsProduced = false; }); }
|
||||
|
||||
template <typename T> requires (!CSameAs<TOutputIterator, TRemoveCVRef<T>> && CConstructibleFrom<Outputer, T>)
|
||||
FORCEINLINE constexpr explicit TOutputIterator(T&& InOutputer) : Storage(Forward<T>(InOutputer)) { check_code({ bIsProduced = false; }); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FIndirectionProxy operator*()
|
||||
{
|
||||
checkf(!bIsProduced, TEXT("Exception output, Ensure that no multiple values are assigned to the output iterator."));
|
||||
check_code({ bIsProduced = true; });
|
||||
return FIndirectionProxy(*this);
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr TOutputIterator& operator++() { check_code({ bIsProduced = false; }); return *this; }
|
||||
|
||||
FORCEINLINE constexpr FPostIncrementProxy operator++(int)
|
||||
{
|
||||
checkf(!bIsProduced, TEXT("Exception output, Ensure that no multiple values are assigned to the output iterator."));
|
||||
return FPostIncrementProxy(*this);
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const Outputer& GetOutputer() const& { return Storage; }
|
||||
NODISCARD FORCEINLINE constexpr Outputer GetOutputer() && { return Storage; }
|
||||
|
||||
private:
|
||||
|
||||
Outputer Storage;
|
||||
|
||||
# if DO_CHECK
|
||||
bool bIsProduced;
|
||||
# endif
|
||||
|
||||
};
|
||||
|
||||
static_assert(COutputIterator<TOutputIterator<void(*)(int32)>, int32>);
|
||||
|
||||
template <typename F>
|
||||
TOutputIterator(F) -> TOutputIterator<F>;
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/** Creates a TReverseIterator of type inferred from the argument. */
|
||||
template <typename I> requires (CBidirectionalIterator<TDecay<I>> && CConstructibleFrom<TDecay<I>, I>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeReverseIterator(I&& Iter)
|
||||
{
|
||||
return TReverseIterator<TDecay<I>>(Forward<I>(Iter));
|
||||
}
|
||||
|
||||
/** Creates a TMoveIterator of type inferred from the argument. */
|
||||
template <typename I> requires (CInputIterator<TDecay<I>> && CConstructibleFrom<TDecay<I>, I>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeMoveIterator(I&& Iter)
|
||||
{
|
||||
return TMoveIterator<TDecay<I>>(Forward<I>(Iter));
|
||||
}
|
||||
|
||||
/** Creates a TMoveSentinel of type inferred from the argument. */
|
||||
template <typename I> requires (CSemiregular<TDecay<I>> && CConstructibleFrom<TDecay<I>, I>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeMoveSentinel(I&& Iter)
|
||||
{
|
||||
return TMoveSentinel<TDecay<I>>(Forward<I>(Iter));
|
||||
}
|
||||
|
||||
/** Creates a TCountedIterator of type inferred from the argument. */
|
||||
template <typename I> requires (CInputOrOutputIterator<TDecay<I>> && CConstructibleFrom<TDecay<I>, I>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeCountedIterator(I&& Iter, ptrdiff N)
|
||||
{
|
||||
return TCountedIterator<TDecay<I>>(Forward<I>(Iter), N);
|
||||
}
|
||||
|
||||
/** Creates an iterator adapter inserted in the front of the container. */
|
||||
template <typename C>
|
||||
NODISCARD FORCEINLINE constexpr auto MakeFrontInserter(C& Container)
|
||||
{
|
||||
return NAMESPACE_PRIVATE::TOutputIterator([&Container]<typename T>(T&& A) { Container.PushFront(Forward<T>(A)); });
|
||||
}
|
||||
|
||||
/** Creates an iterator adapter inserted in the back of the container. */
|
||||
template <typename C>
|
||||
NODISCARD FORCEINLINE constexpr auto MakeBackInserter(C& Container)
|
||||
{
|
||||
return NAMESPACE_PRIVATE::TOutputIterator([&Container]<typename T>(T&& A) { Container.PushBack(Forward<T>(A)); });
|
||||
}
|
||||
|
||||
/** Creates an iterator adapter inserted in the container. */
|
||||
template <typename C>
|
||||
NODISCARD FORCEINLINE constexpr auto MakeInserter(C& Container, const typename C::ConstIterator& InIter)
|
||||
{
|
||||
return NAMESPACE_PRIVATE::TOutputIterator([&Container, Iter = InIter]<typename T>(T&& A) mutable { Iter = Container.Insert(Iter, Forward<T>(A)); });
|
||||
}
|
||||
|
||||
NAMESPACE_BEGIN(Iteration)
|
||||
|
||||
/** Increments given iterator 'Iter' by 'N' elements. */
|
||||
template <CInputIterator I>
|
||||
FORCEINLINE constexpr void Advance(I& Iter, ptrdiff N)
|
||||
{
|
||||
if constexpr (CRandomAccessIterator<I>)
|
||||
{
|
||||
Iter += N;
|
||||
}
|
||||
else if constexpr (CBidirectionalIterator<I>)
|
||||
{
|
||||
for (; N > 0; --N) ++Iter;
|
||||
for (; N < 0; ++N) --Iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkf(N >= 0, TEXT("The iterator must satisfy the CBidirectionalIterator in order to be decremented."));
|
||||
for (; N > 0; --N) ++Iter;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return The number of hops from 'First' to 'Last'. */
|
||||
template <CInputIterator I, CSentinelFor<I> S>
|
||||
FORCEINLINE constexpr ptrdiff Distance(I First, S Last)
|
||||
{
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
return Last - First;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrdiff Result = 0;
|
||||
for (; First != Last; ++First) ++Result;
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return The 'N'-th successor of iterator 'Iter'. */
|
||||
template <CInputIterator I>
|
||||
FORCEINLINE constexpr I Next(I Iter, TMakeUnsigned<ptrdiff> N = 1)
|
||||
{
|
||||
Advance(Iter, N);
|
||||
return Iter;
|
||||
}
|
||||
|
||||
/** @return The 'N'-th predecessor of iterator 'Iter'. */
|
||||
template <CBidirectionalIterator I>
|
||||
FORCEINLINE constexpr I Prev(I Iter, TMakeUnsigned<ptrdiff> N = 1)
|
||||
{
|
||||
Advance(Iter, -N);
|
||||
return Iter;
|
||||
}
|
||||
|
||||
/** @return The iterator to the beginning of a container. */
|
||||
template <typename T> requires (requires(T&& Container) { { Container.Begin() } -> CForwardIterator; })
|
||||
FORCEINLINE constexpr decltype(auto) Begin(T&& Container)
|
||||
{
|
||||
return Container.Begin();
|
||||
}
|
||||
|
||||
/** Overloads the Begin algorithm for arrays. */
|
||||
template <typename T, size_t N> FORCEINLINE constexpr T* Begin( T(& Container)[N]) { return Container; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr T* Begin( T(&& Container)[N]) { return Container; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr const T* Begin(const T(& Container)[N]) { return Container; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr const T* Begin(const T(&& Container)[N]) { return Container; }
|
||||
|
||||
/** Overloads the Begin algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr decltype(auto) Begin(initializer_list<T> Container)
|
||||
{
|
||||
return Container.begin();
|
||||
}
|
||||
|
||||
/** @return The iterator to the end of a container. */
|
||||
template <typename T> requires (requires(T&& Container) { { Container.End() } -> CForwardIterator; })
|
||||
FORCEINLINE constexpr decltype(auto) End(T&& Container)
|
||||
{
|
||||
return Container.End();
|
||||
}
|
||||
|
||||
/** Overloads the End algorithm for arrays. */
|
||||
template <typename T, size_t N> FORCEINLINE constexpr T* End( T(& Container)[N]) { return Container + N; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr T* End( T(&& Container)[N]) { return Container + N; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr const T* End(const T(& Container)[N]) { return Container + N; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr const T* End(const T(&& Container)[N]) { return Container + N; }
|
||||
|
||||
/** Overloads the End algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr decltype(auto) End(initializer_list<T> Container)
|
||||
{
|
||||
return Container.end();
|
||||
}
|
||||
|
||||
/** @return The reverse iterator to the beginning of a container. */
|
||||
template <typename T> requires (requires(T&& Container) { { Container.RBegin() } -> CForwardIterator; })
|
||||
FORCEINLINE constexpr decltype(auto) RBegin(T&& Container)
|
||||
{
|
||||
return Container.RBegin();
|
||||
}
|
||||
|
||||
/** Overloads the RBegin algorithm for arrays. */
|
||||
template <typename T, size_t N> FORCEINLINE constexpr decltype(auto) RBegin( T(& Container)[N]) { return TReverseIterator(End(Container)); }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr decltype(auto) RBegin( T(&& Container)[N]) { return TReverseIterator(End(Container)); }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr decltype(auto) RBegin(const T(& Container)[N]) { return TReverseIterator(End(Container)); }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr decltype(auto) RBegin(const T(&& Container)[N]) { return TReverseIterator(End(Container)); }
|
||||
|
||||
/** Overloads the RBegin algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr decltype(auto) RBegin(initializer_list<T> Container)
|
||||
{
|
||||
return TReverseIterator(Container.end());
|
||||
}
|
||||
|
||||
/** @return The reverse iterator to the end of a container. */
|
||||
template <typename T> requires (requires(T&& Container) { { Container.REnd() } -> CForwardIterator; })
|
||||
FORCEINLINE constexpr decltype(auto) REnd(T&& Container)
|
||||
{
|
||||
return Container.REnd();
|
||||
}
|
||||
|
||||
/** Overloads the REnd algorithm for arrays. */
|
||||
template <typename T, size_t N> FORCEINLINE constexpr decltype(auto) REnd( T(& Container)[N]) { return TReverseIterator(Begin(Container)); }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr decltype(auto) REnd( T(&& Container)[N]) { return TReverseIterator(Begin(Container)); }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr decltype(auto) REnd(const T(& Container)[N]) { return TReverseIterator(Begin(Container)); }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr decltype(auto) REnd(const T(&& Container)[N]) { return TReverseIterator(Begin(Container)); }
|
||||
|
||||
/** Overloads the REnd algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr decltype(auto) REnd(initializer_list<T> Container)
|
||||
{
|
||||
return TReverseIterator(Container.begin());
|
||||
}
|
||||
|
||||
NAMESPACE_END(Iteration)
|
||||
|
||||
#define ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT public: \
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) begin() { return Begin(); } \
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) begin() const { return Begin(); } \
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) end() { return End(); } \
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) end() const { return End(); }
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,16 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Memory/Allocator.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Memory/Allocators.h"
|
||||
#include "Memory/MemoryOperator.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/Factory.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
#include "Miscellaneous/ConstantIterator.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
@@ -28,20 +31,20 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using AllocatorType = Allocator;
|
||||
using FElementType = T;
|
||||
using FAllocatorType = Allocator;
|
||||
|
||||
using Reference = T&;
|
||||
using ConstReference = const T&;
|
||||
using FReference = T&;
|
||||
using FConstReference = const T&;
|
||||
|
||||
using Iterator = TIteratorImpl<false>;
|
||||
using ConstIterator = TIteratorImpl<true >;
|
||||
using FIterator = TIteratorImpl<false>;
|
||||
using FConstIterator = TIteratorImpl<true >;
|
||||
|
||||
using ReverseIterator = TReverseIterator< Iterator>;
|
||||
using ConstReverseIterator = TReverseIterator<ConstIterator>;
|
||||
using FReverseIterator = TReverseIterator< FIterator>;
|
||||
using FConstReverseIterator = TReverseIterator<FConstIterator>;
|
||||
|
||||
static_assert(CBidirectionalIterator< Iterator>);
|
||||
static_assert(CBidirectionalIterator<ConstIterator>);
|
||||
static_assert(CBidirectionalIterator< FIterator>);
|
||||
static_assert(CBidirectionalIterator<FConstIterator>);
|
||||
|
||||
/** Default constructor. Constructs an empty container with a default-constructed allocator. */
|
||||
TList()
|
||||
@@ -54,7 +57,7 @@ public:
|
||||
}
|
||||
|
||||
/** Constructs the container with 'Count' default instances of T. */
|
||||
explicit TList(size_t Count) requires (CDefaultConstructible<ElementType>) : TList()
|
||||
explicit TList(size_t Count) requires (CDefaultConstructible<FElementType>) : TList()
|
||||
{
|
||||
FNode* EndNode = Impl.HeadNode->PrevNode;
|
||||
|
||||
@@ -75,12 +78,12 @@ public:
|
||||
}
|
||||
|
||||
/** Constructs the container with 'Count' copies of elements with 'InValue'. */
|
||||
TList(size_t Count, const ElementType& InValue) requires (CCopyable<ElementType>)
|
||||
: TList(MakeCountedConstantIterator(InValue, Count), DefaultSentinel)
|
||||
TList(size_t Count, const FElementType& InValue) requires (CCopyable<FElementType>)
|
||||
: TList(Ranges::Repeat(InValue, Count))
|
||||
{ }
|
||||
|
||||
/** Constructs the container with the contents of the range ['First', 'Last'). */
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<ElementType, TIteratorReferenceType<I>>)
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<FElementType, TIteratorReference<I>>)
|
||||
TList(I First, S Last) : TList()
|
||||
{
|
||||
FNode* EndNode = Impl.HeadNode->PrevNode;
|
||||
@@ -101,14 +104,18 @@ public:
|
||||
Impl.HeadNode->PrevNode = EndNode;
|
||||
}
|
||||
|
||||
/** Constructs the container with the contents of the range. */
|
||||
template <CInputRange R> requires (!CSameAs<TRemoveCVRef<R>, TList> && CConstructibleFrom<FElementType, TRangeReference<R>>)
|
||||
FORCEINLINE explicit TList(R&& Range) : TList(Ranges::Begin(Range), Ranges::End(Range)) { }
|
||||
|
||||
/** Copy constructor. Constructs the container with the copy of the contents of 'InValue'. */
|
||||
FORCEINLINE TList(const TList& InValue) requires (CCopyConstructible<ElementType>) : TList(InValue.Begin(), InValue.End()) { }
|
||||
FORCEINLINE TList(const TList& InValue) requires (CCopyConstructible<FElementType>) : TList(InValue.Begin(), InValue.End()) { }
|
||||
|
||||
/** Move constructor. After the move, 'InValue' is guaranteed to be empty. */
|
||||
FORCEINLINE TList(TList&& InValue) : TList() { Swap(*this, InValue); }
|
||||
|
||||
/** Constructs the container with the contents of the initializer list. */
|
||||
FORCEINLINE TList(initializer_list<ElementType> IL) requires (CCopyConstructible<ElementType>) : TList(Iteration::Begin(IL), Iteration::End(IL)) { }
|
||||
FORCEINLINE TList(initializer_list<FElementType> IL) requires (CCopyConstructible<FElementType>) : TList(Ranges::Begin(IL), Ranges::End(IL)) { }
|
||||
|
||||
/** Destructs the list. The destructors of the elements are called and the used storage is deallocated. */
|
||||
~TList()
|
||||
@@ -129,12 +136,12 @@ public:
|
||||
}
|
||||
|
||||
/** Copy assignment operator. Replaces the contents with a copy of the contents of 'InValue'. */
|
||||
TList& operator=(const TList& InValue) requires (CCopyable<ElementType>)
|
||||
TList& operator=(const TList& InValue) requires (CCopyable<FElementType>)
|
||||
{
|
||||
if (&InValue == this) UNLIKELY return *this;
|
||||
|
||||
Iterator ThisIter = Begin();
|
||||
ConstIterator OtherIter = InValue.Begin();
|
||||
FIterator ThisIter = Begin();
|
||||
FConstIterator OtherIter = InValue.Begin();
|
||||
|
||||
while (ThisIter != End() && OtherIter != InValue.End())
|
||||
{
|
||||
@@ -166,12 +173,12 @@ public:
|
||||
FORCEINLINE TList& operator=(TList&& InValue) { Swap(*this, InValue); InValue.Reset(); return *this; }
|
||||
|
||||
/** Replaces the contents with those identified by initializer list. */
|
||||
TList& operator=(initializer_list<ElementType> IL) requires (CCopyable<ElementType>)
|
||||
TList& operator=(initializer_list<FElementType> IL) requires (CCopyable<FElementType>)
|
||||
{
|
||||
Iterator ThisIter = Begin();
|
||||
const ElementType* OtherIter = Iteration::Begin(IL);
|
||||
FIterator ThisIter = Begin();
|
||||
const FElementType* OtherIter = Ranges::Begin(IL);
|
||||
|
||||
while (ThisIter != End() && OtherIter != Iteration::End(IL))
|
||||
while (ThisIter != End() && OtherIter != Ranges::End(IL))
|
||||
{
|
||||
*ThisIter = *OtherIter;
|
||||
|
||||
@@ -181,29 +188,29 @@ public:
|
||||
|
||||
if (ThisIter == End())
|
||||
{
|
||||
while (OtherIter != Iteration::End(IL))
|
||||
while (OtherIter != Ranges::End(IL))
|
||||
{
|
||||
EmplaceBack(*OtherIter);
|
||||
++OtherIter;
|
||||
}
|
||||
}
|
||||
else if (OtherIter == Iteration::End(IL))
|
||||
else if (OtherIter == Ranges::End(IL))
|
||||
{
|
||||
Erase(ThisIter, End());
|
||||
}
|
||||
|
||||
Impl.ListNum = GetNum(IL);
|
||||
Impl.ListNum = Ranges::Num(IL);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Compares the contents of two lists. */
|
||||
NODISCARD friend bool operator==(const TList& LHS, const TList& RHS) requires (CWeaklyEqualityComparable<ElementType>)
|
||||
NODISCARD friend bool operator==(const TList& LHS, const TList& RHS) requires (CWeaklyEqualityComparable<FElementType>)
|
||||
{
|
||||
if (LHS.Num() != RHS.Num()) return false;
|
||||
|
||||
ConstIterator LHSIter = LHS.Begin();
|
||||
ConstIterator RHSIter = RHS.Begin();
|
||||
FConstIterator LHSIter = LHS.Begin();
|
||||
FConstIterator RHSIter = RHS.Begin();
|
||||
|
||||
while (LHSIter != LHS.End())
|
||||
{
|
||||
@@ -219,10 +226,10 @@ public:
|
||||
}
|
||||
|
||||
/** Compares the contents of 'LHS' and 'RHS' lexicographically. */
|
||||
NODISCARD friend auto operator<=>(const TList& LHS, const TList& RHS) requires (CSynthThreeWayComparable<ElementType>)
|
||||
NODISCARD friend auto operator<=>(const TList& LHS, const TList& RHS) requires (CSynthThreeWayComparable<FElementType>)
|
||||
{
|
||||
ConstIterator LHSIter = LHS.Begin();
|
||||
ConstIterator RHSIter = RHS.Begin();
|
||||
FConstIterator LHSIter = LHS.Begin();
|
||||
FConstIterator RHSIter = RHS.Begin();
|
||||
|
||||
while (LHSIter != LHS.End() && RHSIter != RHS.End())
|
||||
{
|
||||
@@ -236,22 +243,22 @@ public:
|
||||
}
|
||||
|
||||
/** Inserts 'InValue' before 'Iter' in the container. */
|
||||
FORCEINLINE Iterator Insert(ConstIterator Iter, const ElementType& InValue) requires (CCopyConstructible<ElementType>) { return Emplace(Iter, InValue); }
|
||||
FORCEINLINE FIterator Insert(FConstIterator Iter, const FElementType& InValue) requires (CCopyConstructible<FElementType>) { return Emplace(Iter, InValue); }
|
||||
|
||||
/** Inserts 'InValue' before 'Iter' in the container. */
|
||||
FORCEINLINE Iterator Insert(ConstIterator Iter, ElementType&& InValue) requires (CMoveConstructible<ElementType>) { return Emplace(Iter, MoveTemp(InValue)); }
|
||||
FORCEINLINE FIterator Insert(FConstIterator Iter, FElementType&& InValue) requires (CMoveConstructible<FElementType>) { return Emplace(Iter, MoveTemp(InValue)); }
|
||||
|
||||
/** Inserts 'Count' copies of the 'InValue' before 'Iter' in the container. */
|
||||
Iterator Insert(ConstIterator Iter, size_t Count, const ElementType& InValue) requires (CCopyConstructible<ElementType>)
|
||||
FIterator Insert(FConstIterator Iter, size_t Count, const FElementType& InValue) requires (CCopyConstructible<FElementType>)
|
||||
{
|
||||
return Insert(Iter, MakeCountedConstantIterator(InValue, Count), DefaultSentinel);
|
||||
return Insert(Iter, Ranges::Repeat(InValue, Count));
|
||||
}
|
||||
|
||||
/** Inserts elements from range ['First', 'Last') before 'Iter'. */
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<ElementType, TIteratorReferenceType<I>>)
|
||||
Iterator Insert(ConstIterator Iter, I First, S Last)
|
||||
template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<FElementType, TIteratorReference<I>>)
|
||||
FIterator Insert(FConstIterator Iter, I First, S Last)
|
||||
{
|
||||
if (First == Last) return Iterator(Iter.Pointer);
|
||||
if (First == Last) return FIterator(Iter.Pointer);
|
||||
|
||||
FNode* InsertNode = Iter.Pointer->PrevNode;
|
||||
|
||||
@@ -279,15 +286,19 @@ public:
|
||||
InsertNode->NextNode = Iter.Pointer;
|
||||
Iter.Pointer->PrevNode = InsertNode;
|
||||
|
||||
return Iterator(FirstNode);
|
||||
return FIterator(FirstNode);
|
||||
}
|
||||
|
||||
/** Inserts elements from range ['First', 'Last') before 'Iter'. */
|
||||
template <CInputRange R> requires (CConstructibleFrom<FElementType, TRangeReference<R>>)
|
||||
FORCEINLINE FIterator Insert(FConstIterator Iter, R&& Range) { return Insert(Iter, Ranges::Begin(Range), Ranges::End(Range)); }
|
||||
|
||||
/** Inserts elements from initializer list before 'Iter' in the container. */
|
||||
FORCEINLINE Iterator Insert(ConstIterator Iter, initializer_list<ElementType> IL) requires (CCopyConstructible<ElementType>) { return Insert(Iter, Iteration::Begin(IL), Iteration::End(IL)); }
|
||||
FORCEINLINE FIterator Insert(FConstIterator Iter, initializer_list<FElementType> IL) requires (CCopyConstructible<FElementType>) { return Insert(Iter, Ranges::Begin(IL), Ranges::End(IL)); }
|
||||
|
||||
/** Inserts a new element into the container directly before 'Iter'. */
|
||||
template <typename... Ts> requires (CConstructibleFrom<ElementType, Ts...>)
|
||||
Iterator Emplace(ConstIterator Iter, Ts&&... Args)
|
||||
template <typename... Ts> requires (CConstructibleFrom<FElementType, Ts...>)
|
||||
FIterator Emplace(FConstIterator Iter, Ts&&... Args)
|
||||
{
|
||||
FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, Forward<Ts>(Args)...);
|
||||
|
||||
@@ -299,11 +310,11 @@ public:
|
||||
Node->PrevNode->NextNode = Node;
|
||||
Node->NextNode->PrevNode = Node;
|
||||
|
||||
return Iterator(Node);
|
||||
return FIterator(Node);
|
||||
}
|
||||
|
||||
/** Removes the element at 'Iter' in the container. */
|
||||
Iterator Erase(ConstIterator Iter)
|
||||
FIterator Erase(FConstIterator Iter)
|
||||
{
|
||||
FNode* NodeToErase = Iter.Pointer;
|
||||
|
||||
@@ -319,11 +330,11 @@ public:
|
||||
|
||||
--Impl.ListNum;
|
||||
|
||||
return Iterator(NextNode);
|
||||
return FIterator(NextNode);
|
||||
}
|
||||
|
||||
/** Removes the elements in the range ['First', 'Last') in the container. */
|
||||
Iterator Erase(ConstIterator First, ConstIterator Last)
|
||||
FIterator Erase(FConstIterator First, FConstIterator Last)
|
||||
{
|
||||
FNode* FirstToErase = First.Pointer;
|
||||
FNode* LastToErase = Last.Pointer;
|
||||
@@ -343,45 +354,45 @@ public:
|
||||
FirstToErase = NextNode;
|
||||
}
|
||||
|
||||
return Iterator(LastToErase);
|
||||
return FIterator(LastToErase);
|
||||
}
|
||||
|
||||
/** Appends the given element value to the end of the container. */
|
||||
FORCEINLINE void PushBack(const ElementType& InValue) requires (CCopyConstructible<ElementType>) { EmplaceBack(InValue); }
|
||||
FORCEINLINE void PushBack(const FElementType& InValue) requires (CCopyConstructible<FElementType>) { EmplaceBack(InValue); }
|
||||
|
||||
/** Appends the given element value to the end of the container. */
|
||||
FORCEINLINE void PushBack(ElementType&& InValue) requires (CMoveConstructible<ElementType>) { EmplaceBack(MoveTemp(InValue)); }
|
||||
FORCEINLINE void PushBack(FElementType&& InValue) requires (CMoveConstructible<FElementType>) { EmplaceBack(MoveTemp(InValue)); }
|
||||
|
||||
/** Appends a new element to the end of the container. */
|
||||
template <typename... Ts> requires (CConstructibleFrom<ElementType, Ts...>)
|
||||
FORCEINLINE Reference EmplaceBack(Ts&&... Args) { return *Emplace(End(), Forward<Ts>(Args)...); }
|
||||
template <typename... Ts> requires (CConstructibleFrom<FElementType, Ts...>)
|
||||
FORCEINLINE FReference EmplaceBack(Ts&&... Args) { return *Emplace(End(), Forward<Ts>(Args)...); }
|
||||
|
||||
/** Removes the last element of the container. The list cannot be empty. */
|
||||
FORCEINLINE void PopBack() { Erase(--End()); }
|
||||
|
||||
/** Prepends the given element value to the beginning of the container. */
|
||||
FORCEINLINE void PushFront(const ElementType& InValue) requires (CCopyConstructible<ElementType>) { EmplaceFront(InValue); }
|
||||
FORCEINLINE void PushFront(const FElementType& InValue) requires (CCopyConstructible<FElementType>) { EmplaceFront(InValue); }
|
||||
|
||||
/** Prepends the given element value to the beginning of the container. */
|
||||
FORCEINLINE void PushFront(ElementType&& InValue) requires (CMoveConstructible<ElementType>) { EmplaceFront(MoveTemp(InValue)); }
|
||||
FORCEINLINE void PushFront(FElementType&& InValue) requires (CMoveConstructible<FElementType>) { EmplaceFront(MoveTemp(InValue)); }
|
||||
|
||||
/** Prepends a new element to the beginning of the container. */
|
||||
template <typename... Ts> requires (CConstructibleFrom<ElementType, Ts...>)
|
||||
FORCEINLINE Reference EmplaceFront(Ts&&... Args) { return *Emplace(Begin(), Forward<Ts>(Args)...); }
|
||||
template <typename... Ts> requires (CConstructibleFrom<FElementType, Ts...>)
|
||||
FORCEINLINE FReference EmplaceFront(Ts&&... Args) { return *Emplace(Begin(), Forward<Ts>(Args)...); }
|
||||
|
||||
/** Removes the first element of the container. The list cannot be empty. */
|
||||
FORCEINLINE void PopFront() { Erase(Begin()); }
|
||||
|
||||
/** Resizes the container to contain 'Count' elements. Additional default elements are appended. */
|
||||
void SetNum(size_t Count) requires (CDefaultConstructible<ElementType>)
|
||||
void SetNum(size_t Count) requires (CDefaultConstructible<FElementType>)
|
||||
{
|
||||
if (Count == Impl.ListNum) return;
|
||||
|
||||
if (Count < Impl.ListNum)
|
||||
{
|
||||
Iterator First = End();
|
||||
FIterator First = End();
|
||||
|
||||
Iteration::Advance(First, Count - Impl.ListNum);
|
||||
for (size_t Index = 0; Index != Impl.ListNum - Count; ++Index) --First;
|
||||
|
||||
Erase(First, End());
|
||||
|
||||
@@ -409,15 +420,15 @@ public:
|
||||
}
|
||||
|
||||
/** Resizes the container to contain 'Count' elements. Additional copies of 'InValue' are appended. */
|
||||
void SetNum(size_t Count, const ElementType& InValue) requires (CCopyConstructible<ElementType>)
|
||||
void SetNum(size_t Count, const FElementType& InValue) requires (CCopyConstructible<FElementType>)
|
||||
{
|
||||
if (Count == Impl.ListNum) return;
|
||||
|
||||
if (Count < Impl.ListNum)
|
||||
{
|
||||
Iterator First = End();
|
||||
FIterator First = End();
|
||||
|
||||
Iteration::Advance(First, Count - Impl.ListNum);
|
||||
for (size_t Index = 0; Index != Impl.ListNum - Count; ++Index) --First;
|
||||
|
||||
Erase(First, End());
|
||||
|
||||
@@ -445,16 +456,16 @@ public:
|
||||
}
|
||||
|
||||
/** @return The iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE Iterator Begin() { return Iterator(Impl.HeadNode->NextNode); }
|
||||
NODISCARD FORCEINLINE ConstIterator Begin() const { return ConstIterator(Impl.HeadNode->NextNode); }
|
||||
NODISCARD FORCEINLINE Iterator End() { return Iterator(Impl.HeadNode); }
|
||||
NODISCARD FORCEINLINE ConstIterator End() const { return ConstIterator(Impl.HeadNode); }
|
||||
NODISCARD FORCEINLINE FIterator Begin() { return FIterator(Impl.HeadNode->NextNode); }
|
||||
NODISCARD FORCEINLINE FConstIterator Begin() const { return FConstIterator(Impl.HeadNode->NextNode); }
|
||||
NODISCARD FORCEINLINE FIterator End() { return FIterator(Impl.HeadNode); }
|
||||
NODISCARD FORCEINLINE FConstIterator End() const { return FConstIterator(Impl.HeadNode); }
|
||||
|
||||
/** @return The reverse iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE ReverseIterator RBegin() { return ReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE ConstReverseIterator RBegin() const { return ConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE ReverseIterator REnd() { return ReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE ConstReverseIterator REnd() const { return ConstReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE FReverseIterator RBegin() { return FReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE FConstReverseIterator RBegin() const { return FConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE FReverseIterator REnd() { return FReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE FConstReverseIterator REnd() const { return FConstReverseIterator(Begin()); }
|
||||
|
||||
/** @return The number of elements in the container. */
|
||||
NODISCARD FORCEINLINE size_t Num() const { return Impl.ListNum; }
|
||||
@@ -463,7 +474,7 @@ public:
|
||||
NODISCARD FORCEINLINE bool IsEmpty() const { return Num() == 0; }
|
||||
|
||||
/** @return true if the iterator is valid, false otherwise. */
|
||||
NODISCARD FORCEINLINE bool IsValidIterator(ConstIterator Iter) const
|
||||
NODISCARD FORCEINLINE bool IsValidIterator(FConstIterator Iter) const
|
||||
{
|
||||
FNode* Current = Impl.HeadNode;
|
||||
|
||||
@@ -481,10 +492,10 @@ public:
|
||||
}
|
||||
|
||||
/** @return The reference to the first or last element. */
|
||||
NODISCARD FORCEINLINE ElementType& Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE const ElementType& Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE ElementType& Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE const ElementType& Back() const { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE FElementType& Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE const FElementType& Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE FElementType& Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE const FElementType& Back() const { return *(End() - 1); }
|
||||
|
||||
/** Erases all elements from the container. After this call, Num() returns zero. */
|
||||
void Reset()
|
||||
@@ -508,11 +519,11 @@ public:
|
||||
}
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for TList. */
|
||||
NODISCARD friend FORCEINLINE size_t GetTypeHash(const TList& A) requires (CHashable<ElementType>)
|
||||
NODISCARD friend FORCEINLINE size_t GetTypeHash(const TList& A) requires (CHashable<FElementType>)
|
||||
{
|
||||
size_t Result = 0;
|
||||
|
||||
for (const ElementType& Element : A)
|
||||
for (const FElementType& Element : A)
|
||||
{
|
||||
Result = HashCombine(Result, GetTypeHash(Element));
|
||||
}
|
||||
@@ -535,7 +546,7 @@ private:
|
||||
{
|
||||
FNode* PrevNode;
|
||||
FNode* NextNode;
|
||||
ElementType Value;
|
||||
FElementType Value;
|
||||
|
||||
FORCEINLINE FNode() = default;
|
||||
|
||||
@@ -543,21 +554,21 @@ private:
|
||||
FORCEINLINE FNode(FInPlace, Ts&&... Args) : Value(Forward<Ts>(Args)...) { }
|
||||
};
|
||||
|
||||
static_assert(CMultipleAllocator<AllocatorType, FNode>);
|
||||
static_assert(CMultipleAllocator<FAllocatorType, FNode>);
|
||||
|
||||
ALLOCATOR_WRAPPER_BEGIN(AllocatorType, FNode, Impl)
|
||||
ALLOCATOR_WRAPPER_BEGIN(FAllocatorType, FNode, Impl)
|
||||
{
|
||||
FNode* HeadNode;
|
||||
size_t ListNum;
|
||||
}
|
||||
ALLOCATOR_WRAPPER_END(AllocatorType, FNode, Impl)
|
||||
ALLOCATOR_WRAPPER_END(FAllocatorType, FNode, Impl)
|
||||
|
||||
template <bool bConst, typename U>
|
||||
class TIteratorImpl final
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = TRemoveCV<T>;
|
||||
using FElementType = TRemoveCV<T>;
|
||||
|
||||
FORCEINLINE TIteratorImpl() = default;
|
||||
|
||||
@@ -598,7 +609,10 @@ private:
|
||||
};
|
||||
|
||||
template <typename I, typename S>
|
||||
TList(I, S) -> TList<TIteratorElementType<I>>;
|
||||
TList(I, S) -> TList<TIteratorElement<I>>;
|
||||
|
||||
template <typename R>
|
||||
TList(R) -> TList<TRangeElement<R>>;
|
||||
|
||||
template <typename T>
|
||||
TList(initializer_list<T>) -> TList<T>;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Templates/Meta.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/Meta.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
@@ -15,7 +16,7 @@ NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
/** TStaticArray is a container that encapsulates fixed size arrays. */
|
||||
template <CObject T, size_t N>
|
||||
template <CObject T, size_t N> requires (!CConst<T> && !CVolatile<T>)
|
||||
struct TStaticArray
|
||||
{
|
||||
private:
|
||||
@@ -25,22 +26,22 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using FElementType = T;
|
||||
|
||||
using Reference = T&;
|
||||
using ConstReference = const T&;
|
||||
using FReference = T&;
|
||||
using FConstReference = const T&;
|
||||
|
||||
using Iterator = TIteratorImpl<false>;
|
||||
using ConstIterator = TIteratorImpl<true >;
|
||||
using FIterator = TIteratorImpl<false>;
|
||||
using FConstIterator = TIteratorImpl<true >;
|
||||
|
||||
using ReverseIterator = TReverseIterator< Iterator>;
|
||||
using ConstReverseIterator = TReverseIterator<ConstIterator>;
|
||||
using FReverseIterator = TReverseIterator< FIterator>;
|
||||
using FConstReverseIterator = TReverseIterator<FConstIterator>;
|
||||
|
||||
static_assert(CContiguousIterator< Iterator>);
|
||||
static_assert(CContiguousIterator<ConstIterator>);
|
||||
static_assert(CContiguousIterator< FIterator>);
|
||||
static_assert(CContiguousIterator<FConstIterator>);
|
||||
|
||||
/** Compares the contents of two arrays. */
|
||||
NODISCARD friend constexpr bool operator==(const TStaticArray& LHS, const TStaticArray& RHS) requires (CWeaklyEqualityComparable<ElementType>)
|
||||
NODISCARD friend constexpr bool operator==(const TStaticArray& LHS, const TStaticArray& RHS) requires (CWeaklyEqualityComparable<FElementType>)
|
||||
{
|
||||
if (LHS.Num() != RHS.Num()) return false;
|
||||
|
||||
@@ -53,7 +54,7 @@ public:
|
||||
}
|
||||
|
||||
/** Compares the contents of 'LHS' and 'RHS' lexicographically. */
|
||||
NODISCARD friend constexpr auto operator<=>(const TStaticArray& LHS, const TStaticArray& RHS) requires (CSynthThreeWayComparable<ElementType>)
|
||||
NODISCARD friend constexpr auto operator<=>(const TStaticArray& LHS, const TStaticArray& RHS) requires (CSynthThreeWayComparable<FElementType>)
|
||||
{
|
||||
const size_t NumToCompare = LHS.Num() < RHS.Num() ? LHS.Num() : RHS.Num();
|
||||
|
||||
@@ -66,20 +67,20 @@ public:
|
||||
}
|
||||
|
||||
/** @return The pointer to the underlying element storage. */
|
||||
NODISCARD FORCEINLINE constexpr ElementType* GetData() { return _; }
|
||||
NODISCARD FORCEINLINE constexpr const ElementType* GetData() const { return _; }
|
||||
NODISCARD FORCEINLINE constexpr FElementType* GetData() { return _; }
|
||||
NODISCARD FORCEINLINE constexpr const FElementType* GetData() const { return _; }
|
||||
|
||||
/** @return The iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE constexpr Iterator Begin() { return Iterator(this, _); }
|
||||
NODISCARD FORCEINLINE constexpr ConstIterator Begin() const { return ConstIterator(this, _); }
|
||||
NODISCARD FORCEINLINE constexpr Iterator End() { return Iterator(this, _ + Num()); }
|
||||
NODISCARD FORCEINLINE constexpr ConstIterator End() const { return ConstIterator(this, _ + Num()); }
|
||||
NODISCARD FORCEINLINE constexpr FIterator Begin() { return FIterator(this, _); }
|
||||
NODISCARD FORCEINLINE constexpr FConstIterator Begin() const { return FConstIterator(this, _); }
|
||||
NODISCARD FORCEINLINE constexpr FIterator End() { return FIterator(this, _ + Num()); }
|
||||
NODISCARD FORCEINLINE constexpr FConstIterator End() const { return FConstIterator(this, _ + Num()); }
|
||||
|
||||
/** @return The reverse iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE constexpr ReverseIterator RBegin() { return ReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr ConstReverseIterator RBegin() const { return ConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr ReverseIterator REnd() { return ReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE constexpr ConstReverseIterator REnd() const { return ConstReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE constexpr FReverseIterator RBegin() { return FReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr FConstReverseIterator RBegin() const { return FConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr FReverseIterator REnd() { return FReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE constexpr FConstReverseIterator REnd() const { return FConstReverseIterator(Begin()); }
|
||||
|
||||
/** @return The number of elements in the container. */
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const { return N; }
|
||||
@@ -88,24 +89,24 @@ public:
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return Num() == 0; }
|
||||
|
||||
/** @return true if the iterator is valid, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsValidIterator(ConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
NODISCARD FORCEINLINE constexpr bool IsValidIterator(FConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
|
||||
/** @return The reference to the requested element. */
|
||||
NODISCARD FORCEINLINE constexpr ElementType& operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return _[Index]; }
|
||||
NODISCARD FORCEINLINE constexpr const ElementType& operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return _[Index]; }
|
||||
NODISCARD FORCEINLINE constexpr FElementType& operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return _[Index]; }
|
||||
NODISCARD FORCEINLINE constexpr const FElementType& operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return _[Index]; }
|
||||
|
||||
/** @return The reference to the first or last element. */
|
||||
NODISCARD FORCEINLINE constexpr ElementType& Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr const ElementType& Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr ElementType& Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE constexpr const ElementType& Back() const { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE constexpr FElementType& Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr const FElementType& Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr FElementType& Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE constexpr const FElementType& Back() const { return *(End() - 1); }
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for TStaticArray. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TStaticArray& A) requires (CHashable<ElementType>)
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TStaticArray& A) requires (CHashable<FElementType>)
|
||||
{
|
||||
size_t Result = 0;
|
||||
|
||||
for (ConstIterator Iter = A.Begin(); Iter != A.End(); ++Iter)
|
||||
for (FConstIterator Iter = A.Begin(); Iter != A.End(); ++Iter)
|
||||
{
|
||||
Result = HashCombine(Result, GetTypeHash(*Iter));
|
||||
}
|
||||
@@ -114,7 +115,7 @@ public:
|
||||
}
|
||||
|
||||
/** Overloads the Swap algorithm for TStaticArray. */
|
||||
friend FORCEINLINE constexpr void Swap(TStaticArray& A, TStaticArray& B) requires (CSwappable<ElementType>) { Swap(A._, B._); }
|
||||
friend FORCEINLINE constexpr void Swap(TStaticArray& A, TStaticArray& B) requires (CSwappable<FElementType>) { Swap(A._, B._); }
|
||||
|
||||
ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT
|
||||
|
||||
@@ -127,7 +128,7 @@ private:
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = TRemoveCV<T>;
|
||||
using FElementType = TRemoveCV<T>;
|
||||
|
||||
FORCEINLINE constexpr TIteratorImpl() = default;
|
||||
|
||||
@@ -224,6 +225,8 @@ NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
|
||||
NAMESPACE_STD_BEGIN
|
||||
|
||||
// Support structure binding, should not be directly used.
|
||||
@@ -245,3 +248,5 @@ template <size_t Index, typename T, size_t N> FORCEINLINE constexpr decltype(aut
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "Templates/Noncopyable.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Memory/MemoryOperator.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
@@ -25,9 +25,7 @@ using TDefaultBitsetBlockType =
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
#if 1
|
||||
|
||||
template <size_t N, CUnsignedIntegral InBlockType = NAMESPACE_PRIVATE::TDefaultBitsetBlockType<N>> requires (!CSameAs<InBlockType, bool>)
|
||||
template <size_t N, CUnsignedIntegral InBlockType = NAMESPACE_PRIVATE::TDefaultBitsetBlockType<N>> requires (!CConst<InBlockType> && !CVolatile<InBlockType> && !CSameAs<InBlockType, bool>)
|
||||
class TStaticBitset
|
||||
{
|
||||
private:
|
||||
@@ -37,22 +35,22 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
using BlockType = InBlockType;
|
||||
using ElementType = bool;
|
||||
using FBlockType = InBlockType;
|
||||
using FElementType = bool;
|
||||
|
||||
class Reference;
|
||||
using ConstReference = bool;
|
||||
class FReference;
|
||||
using FConstReference = bool;
|
||||
|
||||
using Iterator = TIteratorImpl<false>;
|
||||
using ConstIterator = TIteratorImpl<true >;
|
||||
using FIterator = TIteratorImpl<false>;
|
||||
using FConstIterator = TIteratorImpl<true >;
|
||||
|
||||
using ReverseIterator = TReverseIterator< Iterator>;
|
||||
using ConstReverseIterator = TReverseIterator<ConstIterator>;
|
||||
using FReverseIterator = TReverseIterator< FIterator>;
|
||||
using FConstReverseIterator = TReverseIterator<FConstIterator>;
|
||||
|
||||
static_assert(CRandomAccessIterator< Iterator>);
|
||||
static_assert(CRandomAccessIterator<ConstIterator>);
|
||||
static_assert(CRandomAccessIterator< FIterator>);
|
||||
static_assert(CRandomAccessIterator<FConstIterator>);
|
||||
|
||||
static constexpr size_t BlockWidth = sizeof(BlockType) * 8;
|
||||
static constexpr size_t BlockWidth = sizeof(FBlockType) * 8;
|
||||
|
||||
/** Default constructor. Constructs an empty bitset. */
|
||||
FORCEINLINE constexpr TStaticBitset() = default;
|
||||
@@ -60,38 +58,38 @@ public:
|
||||
/** Constructs a bitset from an integer. */
|
||||
constexpr TStaticBitset(uint64 InValue)
|
||||
{
|
||||
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TStaticBitset is unexpected");
|
||||
static_assert(sizeof(FBlockType) <= sizeof(uint64), "The block width of TStaticBitset is unexpected");
|
||||
|
||||
if constexpr (sizeof(BlockType) == sizeof(uint8))
|
||||
if constexpr (sizeof(FBlockType) == sizeof(uint8))
|
||||
{
|
||||
if constexpr (N > 0) Impl[0] = static_cast<BlockType>(InValue >> 0);
|
||||
if constexpr (N > 8) Impl[1] = static_cast<BlockType>(InValue >> 8);
|
||||
if constexpr (N > 16) Impl[2] = static_cast<BlockType>(InValue >> 16);
|
||||
if constexpr (N > 24) Impl[3] = static_cast<BlockType>(InValue >> 24);
|
||||
if constexpr (N > 32) Impl[4] = static_cast<BlockType>(InValue >> 32);
|
||||
if constexpr (N > 40) Impl[5] = static_cast<BlockType>(InValue >> 40);
|
||||
if constexpr (N > 48) Impl[6] = static_cast<BlockType>(InValue >> 48);
|
||||
if constexpr (N > 56) Impl[7] = static_cast<BlockType>(InValue >> 56);
|
||||
if constexpr (N > 0) Impl[0] = static_cast<FBlockType>(InValue >> 0);
|
||||
if constexpr (N > 8) Impl[1] = static_cast<FBlockType>(InValue >> 8);
|
||||
if constexpr (N > 16) Impl[2] = static_cast<FBlockType>(InValue >> 16);
|
||||
if constexpr (N > 24) Impl[3] = static_cast<FBlockType>(InValue >> 24);
|
||||
if constexpr (N > 32) Impl[4] = static_cast<FBlockType>(InValue >> 32);
|
||||
if constexpr (N > 40) Impl[5] = static_cast<FBlockType>(InValue >> 40);
|
||||
if constexpr (N > 48) Impl[6] = static_cast<FBlockType>(InValue >> 48);
|
||||
if constexpr (N > 56) Impl[7] = static_cast<FBlockType>(InValue >> 56);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint16))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint16))
|
||||
{
|
||||
if constexpr (N > 0) Impl[0] = static_cast<BlockType>(InValue >> 0);
|
||||
if constexpr (N > 16) Impl[1] = static_cast<BlockType>(InValue >> 16);
|
||||
if constexpr (N > 32) Impl[2] = static_cast<BlockType>(InValue >> 32);
|
||||
if constexpr (N > 48) Impl[3] = static_cast<BlockType>(InValue >> 48);
|
||||
if constexpr (N > 0) Impl[0] = static_cast<FBlockType>(InValue >> 0);
|
||||
if constexpr (N > 16) Impl[1] = static_cast<FBlockType>(InValue >> 16);
|
||||
if constexpr (N > 32) Impl[2] = static_cast<FBlockType>(InValue >> 32);
|
||||
if constexpr (N > 48) Impl[3] = static_cast<FBlockType>(InValue >> 48);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint32))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint32))
|
||||
{
|
||||
if constexpr (N > 0) Impl[0] = static_cast<BlockType>(InValue >> 0);
|
||||
if constexpr (N > 32) Impl[1] = static_cast<BlockType>(InValue >> 32);
|
||||
if constexpr (N > 0) Impl[0] = static_cast<FBlockType>(InValue >> 0);
|
||||
if constexpr (N > 32) Impl[1] = static_cast<FBlockType>(InValue >> 32);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint64))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint64))
|
||||
{
|
||||
if constexpr (N > 0) Impl[0] = static_cast<BlockType>(InValue >> 0);
|
||||
if constexpr (N > 0) Impl[0] = static_cast<FBlockType>(InValue >> 0);
|
||||
}
|
||||
else check_no_entry();
|
||||
|
||||
constexpr size_t BlockInteger = sizeof(uint64) / sizeof(BlockType);
|
||||
constexpr size_t BlockInteger = sizeof(uint64) / sizeof(FBlockType);
|
||||
|
||||
if constexpr ((N + BlockWidth - 1) / BlockWidth <= BlockInteger) return;
|
||||
|
||||
@@ -126,7 +124,7 @@ public:
|
||||
if (LHS.Impl[Index] != RHS.Impl[Index]) return false;
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = LHS.Num() % BlockWidth != 0 ? (1ull << LHS.Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = LHS.Num() % BlockWidth != 0 ? (1ull << LHS.Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
return (LHS.Impl[LHS.NumBlocks() - 1] & LastBlockBitmask) == (RHS.Impl[LHS.NumBlocks() - 1] & LastBlockBitmask);
|
||||
}
|
||||
@@ -269,7 +267,7 @@ public:
|
||||
if (Impl[Index] != -1) return false;
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
return (Impl[NumBlocks() - 1] | ~LastBlockBitmask) == -1;
|
||||
}
|
||||
@@ -284,7 +282,7 @@ public:
|
||||
if (Impl[Index] != 0) return true;
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
return (Impl[NumBlocks() - 1] & LastBlockBitmask) != 0;
|
||||
}
|
||||
@@ -299,24 +297,24 @@ public:
|
||||
|
||||
size_t Result = 0;
|
||||
|
||||
constexpr auto BlockCount = [](BlockType Block) constexpr
|
||||
constexpr auto BlockCount = [](FBlockType Block) constexpr
|
||||
{
|
||||
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TStaticBitset is unexpected");
|
||||
static_assert(sizeof(FBlockType) <= sizeof(uint64), "The block width of TStaticBitset is unexpected");
|
||||
|
||||
if constexpr (sizeof(BlockType) == sizeof(uint8))
|
||||
if constexpr (sizeof(FBlockType) == sizeof(uint8))
|
||||
{
|
||||
Block = (Block & 0x55ull) + ((Block >> 1) & 0x55ull);
|
||||
Block = (Block & 0x33ull) + ((Block >> 2) & 0x33ull);
|
||||
Block = (Block & 0x0Full) + ((Block >> 4) & 0x0Full);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint16))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint16))
|
||||
{
|
||||
Block = (Block & 0x5555ull) + ((Block >> 1) & 0x5555ull);
|
||||
Block = (Block & 0x3333ull) + ((Block >> 2) & 0x3333ull);
|
||||
Block = (Block & 0x0F0Full) + ((Block >> 4) & 0x0F0Full);
|
||||
Block = (Block & 0x00FFull) + ((Block >> 8) & 0x00FFull);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint32))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint32))
|
||||
{
|
||||
Block = (Block & 0x55555555ull) + ((Block >> 1) & 0x55555555ull);
|
||||
Block = (Block & 0x33333333ull) + ((Block >> 2) & 0x33333333ull);
|
||||
@@ -324,7 +322,7 @@ public:
|
||||
Block = (Block & 0x00FF00FFull) + ((Block >> 8) & 0x00FF00FFull);
|
||||
Block = (Block & 0x0000FFFFull) + ((Block >> 16) & 0x0000FFFFull);
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint64))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint64))
|
||||
{
|
||||
Block = (Block & 0x5555555555555555ull) + ((Block >> 1) & 0x5555555555555555ull);
|
||||
Block = (Block & 0x3333333333333333ull) + ((Block >> 2) & 0x3333333333333333ull);
|
||||
@@ -343,7 +341,7 @@ public:
|
||||
Result += BlockCount(Impl[Index]);
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
Result += BlockCount(Impl[NumBlocks() - 1] & LastBlockBitmask);
|
||||
|
||||
@@ -355,7 +353,7 @@ public:
|
||||
{
|
||||
for (size_t Index = 0; Index != NumBlocks(); ++Index)
|
||||
{
|
||||
Impl[Index] = static_cast<BlockType>(InValue ? -1 : 0);
|
||||
Impl[Index] = static_cast<FBlockType>(InValue ? -1 : 0);
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -387,20 +385,20 @@ public:
|
||||
{
|
||||
for (size_t Index = 64 / BlockWidth; Index < NumBlocks() - 1; ++Index)
|
||||
{
|
||||
checkf(Impl.Pointer[Index] != 0, TEXT("The bitset can not be represented in uint64. Please check Num()."));
|
||||
checkf(Impl[Index] != 0, TEXT("The bitset can not be represented in uint64. Please check Num()."));
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const BlockType LastBlock = Impl.Pointer[NumBlocks() - 1] & LastBlockBitmask;
|
||||
const FBlockType LastBlockBitmask = Num() % BlockWidth != 0 ? (1ull << Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlock = Impl[NumBlocks() - 1] & LastBlockBitmask;
|
||||
|
||||
checkf(LastBlock != 0, TEXT("The bitset can not be represented in uint64. Please check Num()."));
|
||||
}
|
||||
|
||||
uint64 Result = 0;
|
||||
|
||||
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TStaticBitset is unexpected");
|
||||
static_assert(sizeof(FBlockType) <= sizeof(uint64), "The block width of TStaticBitset is unexpected");
|
||||
|
||||
if constexpr (sizeof(BlockType) == sizeof(uint8))
|
||||
if constexpr (sizeof(FBlockType) == sizeof(uint8))
|
||||
{
|
||||
if constexpr (N > 0) Result |= static_cast<uint64>(Impl[0]) << 0;
|
||||
if constexpr (N > 8) Result |= static_cast<uint64>(Impl[1]) << 8;
|
||||
@@ -411,19 +409,19 @@ public:
|
||||
if constexpr (N > 48) Result |= static_cast<uint64>(Impl[6]) << 48;
|
||||
if constexpr (N > 56) Result |= static_cast<uint64>(Impl[7]) << 56;
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint16))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint16))
|
||||
{
|
||||
if constexpr (N > 0) Result |= static_cast<uint64>(Impl[0]) << 0;
|
||||
if constexpr (N > 16) Result |= static_cast<uint64>(Impl[1]) << 16;
|
||||
if constexpr (N > 32) Result |= static_cast<uint64>(Impl[2]) << 32;
|
||||
if constexpr (N > 48) Result |= static_cast<uint64>(Impl[3]) << 48;
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint32))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint32))
|
||||
{
|
||||
if constexpr (N > 0) Result |= static_cast<uint64>(Impl[0]) << 0;
|
||||
if constexpr (N > 32) Result |= static_cast<uint64>(Impl[1]) << 32;
|
||||
}
|
||||
else if constexpr (sizeof(BlockType) == sizeof(uint64))
|
||||
else if constexpr (sizeof(FBlockType) == sizeof(uint64))
|
||||
{
|
||||
if constexpr (N > 0) Result |= static_cast<uint64>(Impl[0]) << 0;
|
||||
}
|
||||
@@ -435,20 +433,20 @@ public:
|
||||
}
|
||||
|
||||
/** @return The pointer to the underlying element storage. */
|
||||
NODISCARD FORCEINLINE constexpr BlockType* GetData() { return Impl; }
|
||||
NODISCARD FORCEINLINE constexpr const BlockType* GetData() const { return Impl; }
|
||||
NODISCARD FORCEINLINE constexpr FBlockType* GetData() { return Impl; }
|
||||
NODISCARD FORCEINLINE constexpr const FBlockType* GetData() const { return Impl; }
|
||||
|
||||
/** @return The iterator to the first or end bit. */
|
||||
NODISCARD FORCEINLINE constexpr Iterator Begin() { return Iterator(this, Impl, 0); }
|
||||
NODISCARD FORCEINLINE constexpr ConstIterator Begin() const { return ConstIterator(this, Impl, 0); }
|
||||
NODISCARD FORCEINLINE constexpr Iterator End() { return Iterator(this, Impl, Num()); }
|
||||
NODISCARD FORCEINLINE constexpr ConstIterator End() const { return ConstIterator(this, Impl, Num()); }
|
||||
NODISCARD FORCEINLINE constexpr FIterator Begin() { return FIterator(this, Impl, 0); }
|
||||
NODISCARD FORCEINLINE constexpr FConstIterator Begin() const { return FConstIterator(this, Impl, 0); }
|
||||
NODISCARD FORCEINLINE constexpr FIterator End() { return FIterator(this, Impl, Num()); }
|
||||
NODISCARD FORCEINLINE constexpr FConstIterator End() const { return FConstIterator(this, Impl, Num()); }
|
||||
|
||||
/** @return The reverse iterator to the first or end bit. */
|
||||
NODISCARD FORCEINLINE constexpr ReverseIterator RBegin() { return ReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr ConstReverseIterator RBegin() const { return ConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr ReverseIterator REnd() { return ReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE constexpr ConstReverseIterator REnd() const { return ConstReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE constexpr FReverseIterator RBegin() { return FReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr FConstReverseIterator RBegin() const { return FConstReverseIterator(End()); }
|
||||
NODISCARD FORCEINLINE constexpr FReverseIterator REnd() { return FReverseIterator(Begin()); }
|
||||
NODISCARD FORCEINLINE constexpr FConstReverseIterator REnd() const { return FConstReverseIterator(Begin()); }
|
||||
|
||||
/** @return The number of bits in the bitset. */
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const { return N; }
|
||||
@@ -460,17 +458,17 @@ public:
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return Num() == 0; }
|
||||
|
||||
/** @return true if the iterator is valid, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsValidIterator(ConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
NODISCARD FORCEINLINE constexpr bool IsValidIterator(FConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
|
||||
/** @return The reference to the requested bit. */
|
||||
NODISCARD FORCEINLINE constexpr Reference operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return *(Begin() + Index); }
|
||||
NODISCARD FORCEINLINE constexpr ConstReference operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return *(Begin() + Index); }
|
||||
NODISCARD FORCEINLINE constexpr FReference operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return *(Begin() + Index); }
|
||||
NODISCARD FORCEINLINE constexpr FConstReference operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return *(Begin() + Index); }
|
||||
|
||||
/** @return The reference to the first or last bit. */
|
||||
NODISCARD FORCEINLINE constexpr Reference Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr ConstReference Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr Reference Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE constexpr ConstReference Back() const { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE constexpr FReference Front() { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr FConstReference Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr FReference Back() { return *(End() - 1); }
|
||||
NODISCARD FORCEINLINE constexpr FConstReference Back() const { return *(End() - 1); }
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for TStaticBitset. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TStaticBitset& A)
|
||||
@@ -484,7 +482,7 @@ public:
|
||||
Result = HashCombine(Result, GetTypeHash(A.Impl[Index]));
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = A.Num() % BlockWidth != 0 ? (1ull << A.Num() % BlockWidth) - 1 : -1;
|
||||
const FBlockType LastBlockBitmask = A.Num() % BlockWidth != 0 ? (1ull << A.Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
return HashCombine(Result, GetTypeHash(A.Impl[A.NumBlocks() - 1] & LastBlockBitmask));
|
||||
}
|
||||
@@ -496,21 +494,21 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
BlockType Impl[N != 0 ? (N + BlockWidth - 1) / BlockWidth : 1];
|
||||
FBlockType Impl[N != 0 ? (N + BlockWidth - 1) / BlockWidth : 1];
|
||||
|
||||
public:
|
||||
|
||||
class Reference final : private FSingleton
|
||||
class FReference final : private FSingleton
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr Reference& operator=(bool InValue) { Data = (Data & ~Mask) | (InValue ? Mask : 0); return *this; }
|
||||
FORCEINLINE constexpr FReference& operator=(bool InValue) { Data = (Data & ~Mask) | (InValue ? Mask : 0); return *this; }
|
||||
|
||||
FORCEINLINE constexpr Reference& operator=(const Reference& InValue) { *this = static_cast<bool>(InValue); return *this; }
|
||||
FORCEINLINE constexpr FReference& operator=(const FReference& InValue) { *this = static_cast<bool>(InValue); return *this; }
|
||||
|
||||
FORCEINLINE constexpr Reference& operator&=(bool InValue) { Data &= InValue ? -1 : ~Mask; return *this; }
|
||||
FORCEINLINE constexpr Reference& operator|=(bool InValue) { Data |= InValue ? Mask : 0; return *this; }
|
||||
FORCEINLINE constexpr Reference& operator^=(bool InValue) { *this = InValue ^ *this; return *this; }
|
||||
FORCEINLINE constexpr FReference& operator&=(bool InValue) { Data &= InValue ? -1 : ~Mask; return *this; }
|
||||
FORCEINLINE constexpr FReference& operator|=(bool InValue) { Data |= InValue ? Mask : 0; return *this; }
|
||||
FORCEINLINE constexpr FReference& operator^=(bool InValue) { *this = InValue ^ *this; return *this; }
|
||||
|
||||
FORCEINLINE constexpr bool operator~() const { return !*this; }
|
||||
|
||||
@@ -518,14 +516,14 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
FORCEINLINE constexpr Reference(BlockType& InData, BlockType InMask)
|
||||
FORCEINLINE constexpr FReference(FBlockType& InData, FBlockType InMask)
|
||||
: Data(InData), Mask(InMask)
|
||||
{ }
|
||||
|
||||
BlockType& Data;
|
||||
BlockType Mask;
|
||||
FBlockType& Data;
|
||||
FBlockType Mask;
|
||||
|
||||
friend Iterator;
|
||||
friend FIterator;
|
||||
|
||||
};
|
||||
|
||||
@@ -538,7 +536,7 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = bool;
|
||||
using FElementType = bool;
|
||||
|
||||
FORCEINLINE constexpr TIteratorImpl() = default;
|
||||
|
||||
@@ -561,8 +559,8 @@ private:
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TIteratorImpl& LHS, const TIteratorImpl& RHS) { check(LHS.Pointer == RHS.Pointer); return LHS.BitOffset <=> RHS.BitOffset; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr Reference operator*() const requires (!bConst) { CheckThis(true); return Reference(*(Pointer + BitOffset / BlockWidth), 1ull << BitOffset % BlockWidth); }
|
||||
NODISCARD FORCEINLINE constexpr ConstReference operator*() const requires ( bConst) { CheckThis(true); return (*(Pointer + BitOffset / BlockWidth) & (1ull << BitOffset % BlockWidth)); }
|
||||
NODISCARD FORCEINLINE constexpr FReference operator*() const requires (!bConst) { CheckThis(true); return FReference(*(Pointer + BitOffset / BlockWidth), 1ull << BitOffset % BlockWidth); }
|
||||
NODISCARD FORCEINLINE constexpr FConstReference operator*() const requires ( bConst) { CheckThis(true); return (*(Pointer + BitOffset / BlockWidth) & (1ull << BitOffset % BlockWidth)); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto operator[](ptrdiff Index) const { TIteratorImpl Temp = *this + Index; return *Temp; }
|
||||
|
||||
@@ -588,17 +586,17 @@ private:
|
||||
const TStaticBitset* Owner = nullptr;
|
||||
# endif
|
||||
|
||||
using BlockPtr = TConditional<bConst, const BlockType*, BlockType*>;
|
||||
using FBlockPtr = TConditional<bConst, const FBlockType*, FBlockType*>;
|
||||
|
||||
BlockPtr Pointer = nullptr;
|
||||
FBlockPtr Pointer = nullptr;
|
||||
size_t BitOffset = 0;
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE constexpr TIteratorImpl(const TStaticBitset* InContainer, BlockPtr InPointer, size_t Offset)
|
||||
FORCEINLINE constexpr TIteratorImpl(const TStaticBitset* InContainer, FBlockPtr InPointer, size_t Offset)
|
||||
: Owner(InContainer), Pointer(InPointer), BitOffset(Offset)
|
||||
{ }
|
||||
# else
|
||||
FORCEINLINE constexpr TIteratorImpl(const TStaticBitset* InContainer, BlockPtr InPointer, size_t Offset)
|
||||
FORCEINLINE constexpr TIteratorImpl(const TStaticBitset* InContainer, FBlockPtr InPointer, size_t Offset)
|
||||
: Pointer(InPointer), BitOffset(Offset)
|
||||
{ }
|
||||
# endif
|
||||
@@ -617,8 +615,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
250
Redcraft.Utility/Source/Public/Iterators/BasicIterator.h
Normal file
250
Redcraft.Utility/Source/Public/Iterators/BasicIterator.h
Normal file
@@ -0,0 +1,250 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
#if PLATFORM_COMPILER_GCC
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wnon-template-friend"
|
||||
#endif
|
||||
|
||||
/** A concept specifies a type is an input iterator. It is input iterator, incrementable, and sentinel for itself. */
|
||||
template <typename I>
|
||||
concept CForwardIterator = CInputIterator<I> && CIncrementable<I> && CSentinelFor<I, I>;
|
||||
|
||||
/** This is an example of a forward iterator, indicate the traits that define a forward iterator. */
|
||||
template <CReferenceable T>
|
||||
struct IForwardIterator /* : IInputIterator<T>, IIncrementable, ISentinelFor<IForwardIterator> */
|
||||
{
|
||||
// ~Begin CInputIterator.
|
||||
|
||||
using FElementType = TRemoveCVRef<T>;
|
||||
|
||||
// ~End CInputIterator.
|
||||
|
||||
// ~Begin CIncrementable and CSentinelFor<IForwardIterator>.
|
||||
|
||||
IForwardIterator();
|
||||
IForwardIterator(const IForwardIterator&);
|
||||
IForwardIterator(IForwardIterator&&); // Also satisfies IInputIterator.
|
||||
IForwardIterator* operator=(const IForwardIterator&);
|
||||
IForwardIterator* operator=(IForwardIterator&&); // Also satisfies IInputIterator.
|
||||
|
||||
friend bool operator==(const IForwardIterator&, const IForwardIterator&);
|
||||
|
||||
// ~End CIncrementable and CSentinelFor<IForwardIterator>.
|
||||
|
||||
// ~Begin CInputIterator.
|
||||
|
||||
T operator*() const; // Optional satisfies CIndirectlyWritable.
|
||||
|
||||
IForwardIterator& operator++(); // Also satisfies CIncrementable.
|
||||
|
||||
IForwardIterator operator++(int); // Also satisfies CIncrementable.
|
||||
|
||||
// ~End CInputIterator.
|
||||
};
|
||||
|
||||
// Use IForwardIterator<int> represents a forward iterator.
|
||||
static_assert(CForwardIterator<IForwardIterator<int&>>);
|
||||
static_assert( COutputIterator<IForwardIterator<int&>, int>);
|
||||
|
||||
/** A concept specifies a type is a bidirectional iterator. Add the decrement operator to the forward iterator. */
|
||||
template <typename I>
|
||||
concept CBidirectionalIterator = CForwardIterator<I>
|
||||
&& requires(I Iter) {
|
||||
{ --Iter } -> CSameAs<I&>;
|
||||
{ Iter-- } -> CSameAs<I >;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is an example of a bidirectional iterator, indicate the traits that define a bidirectional iterator.
|
||||
* Regardless of the order in which the increment and decrement operators are applied,
|
||||
* the result is always the same if both operations are performed the same number of times.
|
||||
*/
|
||||
template <CReferenceable T>
|
||||
struct IBidirectionalIterator /* : IForwardIterator<T> */
|
||||
{
|
||||
// ~Begin CForwardIterator.
|
||||
|
||||
using FElementType = TRemoveCVRef<T>;
|
||||
|
||||
IBidirectionalIterator();
|
||||
IBidirectionalIterator(const IBidirectionalIterator&);
|
||||
IBidirectionalIterator(IBidirectionalIterator&&);
|
||||
IBidirectionalIterator* operator=(const IBidirectionalIterator&);
|
||||
IBidirectionalIterator* operator=(IBidirectionalIterator&&);
|
||||
|
||||
friend bool operator==(const IBidirectionalIterator&, const IBidirectionalIterator&);
|
||||
|
||||
T operator*() const;
|
||||
|
||||
// ~End CForwardIterator.
|
||||
|
||||
IBidirectionalIterator& operator++(); // Also satisfies CForwardIterator.
|
||||
IBidirectionalIterator& operator--();
|
||||
|
||||
IBidirectionalIterator operator++(int); // Also satisfies CForwardIterator.
|
||||
IBidirectionalIterator operator--(int);
|
||||
};
|
||||
|
||||
// Use IBidirectionalIterator<int> represents a bidirectional iterator.
|
||||
static_assert(CBidirectionalIterator<IBidirectionalIterator<int&>>);
|
||||
static_assert( COutputIterator<IBidirectionalIterator<int&>, int>);
|
||||
|
||||
/**
|
||||
* A concept specifies a type is a random access iterator.
|
||||
* Add the three-way comparison, addition, subtraction and subscript operators to the bidirectional iterator.
|
||||
*/
|
||||
template <typename I>
|
||||
concept CRandomAccessIterator = CBidirectionalIterator<I> && CTotallyOrdered<I> && CSizedSentinelFor<I, I>
|
||||
&& requires(I Iter, const I Jter, const ptrdiff N) {
|
||||
{ Iter += N } -> CSameAs<I&>;
|
||||
{ Jter + N } -> CSameAs<I >;
|
||||
{ N + Jter } -> CSameAs<I >;
|
||||
{ Iter -= N } -> CSameAs<I&>;
|
||||
{ Jter - N } -> CSameAs<I >;
|
||||
{ Jter[N] } -> CSameAs<TIteratorReference<I>>;
|
||||
};
|
||||
|
||||
/** This is an example of a random access iterator, indicate the traits that define a random access iterator. */
|
||||
template <CReferenceable T>
|
||||
struct IRandomAccessIterator /* : IBidirectionalIterator<T>, ISizedSentinelFor<IRandomAccessIterator> */
|
||||
{
|
||||
// ~Begin CBidirectionalIterator.
|
||||
|
||||
using FElementType = TRemoveCVRef<T>;
|
||||
|
||||
// ~End CBidirectionalIterator.
|
||||
|
||||
// ~Begin CBidirectionalIterator and CSizedSentinelFor<IRandomAccessIterator>.
|
||||
|
||||
IRandomAccessIterator();
|
||||
IRandomAccessIterator(const IRandomAccessIterator&);
|
||||
IRandomAccessIterator(IRandomAccessIterator&&);
|
||||
IRandomAccessIterator* operator=(const IRandomAccessIterator&);
|
||||
IRandomAccessIterator* operator=(IRandomAccessIterator&&);
|
||||
|
||||
friend bool operator==(const IRandomAccessIterator&, const IRandomAccessIterator&);
|
||||
|
||||
// ~End CBidirectionalIterator and CSizedSentinelFor<IRandomAccessIterator>.
|
||||
|
||||
friend strong_ordering operator<=>(const IRandomAccessIterator&, const IRandomAccessIterator&);
|
||||
|
||||
T operator*() const; // Also satisfies CBidirectionalIterator.
|
||||
|
||||
T operator[](ptrdiff) const;
|
||||
|
||||
// ~Begin CBidirectionalIterator.
|
||||
|
||||
IRandomAccessIterator& operator++();
|
||||
IRandomAccessIterator& operator--();
|
||||
|
||||
IRandomAccessIterator operator++(int);
|
||||
IRandomAccessIterator operator--(int);
|
||||
|
||||
// ~End CBidirectionalIterator.
|
||||
|
||||
IRandomAccessIterator& operator+=(ptrdiff);
|
||||
IRandomAccessIterator& operator-=(ptrdiff);
|
||||
|
||||
IRandomAccessIterator operator+(ptrdiff) const;
|
||||
IRandomAccessIterator operator-(ptrdiff) const;
|
||||
|
||||
friend IRandomAccessIterator operator+(ptrdiff, const IRandomAccessIterator&);
|
||||
|
||||
friend ptrdiff operator-(const IRandomAccessIterator&, const IRandomAccessIterator&); // Also satisfies CSizedSentinelFor<IRandomAccessIterator>.
|
||||
};
|
||||
|
||||
// Use IRandomAccessIterator<int> represents a random access iterator
|
||||
static_assert(CRandomAccessIterator<IRandomAccessIterator<int&>>);
|
||||
static_assert( COutputIterator<IRandomAccessIterator<int&>, int>);
|
||||
|
||||
/**
|
||||
* A concept specifies a type is a contiguous iterator.
|
||||
* Add the operator-> to the random access iterator and requires the operator* returns a true reference type.
|
||||
*/
|
||||
template <typename I>
|
||||
concept CContiguousIterator = CRandomAccessIterator<I> && CLValueReference<TIteratorReference<I>>
|
||||
&& CSameAs<TIteratorElement<I>, TRemoveCVRef<TIteratorReference<I>>>
|
||||
&& CSameAs<TIteratorPointer<I>, TAddPointer<TIteratorReference<I>>>
|
||||
&& requires(I& Iter)
|
||||
{
|
||||
{ ToAddress(Iter) } -> CSameAs<TAddPointer<TIteratorReference<I>>>;
|
||||
};
|
||||
|
||||
/** This is an example of a contiguous iterator, indicate the traits that define a contiguous iterator. */
|
||||
template <CLValueReference T>
|
||||
struct IContiguousIterator /* : IRandomAccessIterator<T> */
|
||||
{
|
||||
// ~Begin CRandomAccessIterator.
|
||||
|
||||
using FElementType = TRemoveCVRef<T>;
|
||||
|
||||
IContiguousIterator();
|
||||
IContiguousIterator(const IContiguousIterator&);
|
||||
IContiguousIterator(IContiguousIterator&&);
|
||||
IContiguousIterator* operator=(const IContiguousIterator&);
|
||||
IContiguousIterator* operator=(IContiguousIterator&&);
|
||||
|
||||
friend bool operator==(const IContiguousIterator&, const IContiguousIterator&);
|
||||
|
||||
friend strong_ordering operator<=>(const IContiguousIterator&, const IContiguousIterator&);
|
||||
|
||||
// ~End CRandomAccessIterator.
|
||||
|
||||
/**
|
||||
* Dereference operator. See IForwardIterator.
|
||||
* Specify, the return type must be a true reference type and refer to an element of a contiguous sequence, not a proxy class.
|
||||
* Also satisfies CRandomAccessIterator.
|
||||
*/
|
||||
T operator*() const;
|
||||
|
||||
/** Indirection operator. Return the address of the element that the iterator is pointing to. */
|
||||
TAddPointer<T> operator->() const;
|
||||
|
||||
// ~Begin CRandomAccessIterator.
|
||||
|
||||
T operator[](ptrdiff) const;
|
||||
|
||||
IContiguousIterator& operator++();
|
||||
IContiguousIterator& operator--();
|
||||
|
||||
IContiguousIterator operator++(int);
|
||||
IContiguousIterator operator--(int);
|
||||
|
||||
IContiguousIterator& operator+=(ptrdiff);
|
||||
IContiguousIterator& operator-=(ptrdiff);
|
||||
|
||||
IContiguousIterator operator+(ptrdiff) const;
|
||||
IContiguousIterator operator-(ptrdiff) const;
|
||||
|
||||
friend IContiguousIterator operator+(ptrdiff, const IContiguousIterator&);
|
||||
|
||||
friend ptrdiff operator-(const IContiguousIterator&, const IContiguousIterator&);
|
||||
|
||||
// ~End CRandomAccessIterator.
|
||||
};
|
||||
|
||||
// Use IContiguousIterator<int> represents a contiguous iterator
|
||||
static_assert(CContiguousIterator<IContiguousIterator<int&>>);
|
||||
static_assert( COutputIterator<IContiguousIterator<int&>, int>);
|
||||
|
||||
// The int* is the most typical example of a contiguous iterator
|
||||
static_assert(CContiguousIterator<int*>);
|
||||
|
||||
#if PLATFORM_COMPILER_GCC
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
145
Redcraft.Utility/Source/Public/Iterators/CountedIterator.h
Normal file
145
Redcraft.Utility/Source/Public/Iterators/CountedIterator.h
Normal file
@@ -0,0 +1,145 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Memory/Address.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename T> class TCountedIteratorImpl { };
|
||||
template <CIndirectlyReadable T> class TCountedIteratorImpl<T> { public: using FElementType = TIteratorElement<T>; };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/**
|
||||
* An iterator adaptor that tracks the distance to the end of the range.
|
||||
* When based on an input or output iterator, the counted iterator satisfies at least an input or output iterator
|
||||
* up to a contiguous iterator. When based on an output iterator, the counted iterator satisfies an output iterator.
|
||||
* When based on iterator satisfies sentinel for itself, the counted iterator satisfies sized sentinel for itself.
|
||||
*/
|
||||
template <CInputOrOutputIterator I>
|
||||
class TCountedIterator final : public NAMESPACE_PRIVATE::TCountedIteratorImpl<I>
|
||||
{
|
||||
public:
|
||||
|
||||
using FIteratorType = I;
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<I>) : Length(1), MaxLength(0) { }
|
||||
# else
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<I>) = default;
|
||||
# endif
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator(const TCountedIterator&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator(TCountedIterator&&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(const TCountedIterator&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(TCountedIterator&&) = default;
|
||||
FORCEINLINE constexpr ~TCountedIterator() = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TCountedIterator(FIteratorType InValue, ptrdiff N) : Current(MoveTemp(InValue)), Length(N) { check_code({ MaxLength = N; }); }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<I, J> && CConstructibleFrom<I, const J&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const J&, I>) TCountedIterator(const TCountedIterator<J>& InValue)
|
||||
: Current(InValue.GetBase()), Length(InValue.Num())
|
||||
{
|
||||
check_code({ MaxLength = InValue.MaxLength; });
|
||||
}
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<I, J> && CConvertibleTo<const J&, I> && CAssignableFrom<I&, const J&>)
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(const TCountedIterator<J>& InValue)
|
||||
{
|
||||
Current = InValue.GetBase();
|
||||
Length = InValue.Num();
|
||||
|
||||
check_code({ MaxLength = InValue.MaxLength; });
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <CInputOrOutputIterator J> requires (CCommonType<I, J>)
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.Num() == RHS.Num(); }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (CCommonType<I, J>)
|
||||
NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.Num() <=> RHS.Num(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(FDefaultSentinel) const& { return Num() == static_cast<ptrdiff>(0); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReference<I> operator*() { CheckThis(true); return *GetBase(); }
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReference<I> operator*() const requires (CDereferenceable<const I>) { CheckThis(true); return *GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto operator->() const requires (requires(const I Iter) { { ToAddress(Iter) } -> CSameAs<TIteratorPointer<I>>; }) { return ToAddress(GetBase()); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReference<I> operator[](ptrdiff Index) const requires (CRandomAccessIterator<I>) { TCountedIterator Temp = *this + Index; return *Temp; }
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator& operator++() { ++Current; --Length; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr TCountedIterator& operator--() requires (CBidirectionalIterator<I>) { --Current; ++Length; CheckThis(); return *this; }
|
||||
|
||||
FORCEINLINE constexpr auto operator++(int) { --Length; CheckThis(); return Current++; }
|
||||
FORCEINLINE constexpr TCountedIterator operator++(int) requires (CForwardIterator<I>) { TCountedIterator Temp = *this; ++Current; --Length; CheckThis(); return Temp; }
|
||||
FORCEINLINE constexpr TCountedIterator operator--(int) requires (CBidirectionalIterator<I>) { TCountedIterator Temp = *this; --Current; ++Length; CheckThis(); return Temp; }
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator& operator+=(ptrdiff Offset) requires (CRandomAccessIterator<I>) { Current += Offset; Length -= Offset; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr TCountedIterator& operator-=(ptrdiff Offset) requires (CRandomAccessIterator<I>) { Current -= Offset; Length += Offset; CheckThis(); return *this; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TCountedIterator operator+(ptrdiff Offset) const requires (CRandomAccessIterator<I>) { TCountedIterator Temp = *this; Temp += Offset; return Temp; }
|
||||
NODISCARD FORCEINLINE constexpr TCountedIterator operator-(ptrdiff Offset) const requires (CRandomAccessIterator<I>) { TCountedIterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr TCountedIterator operator+(ptrdiff Offset, TCountedIterator Iter) requires (CRandomAccessIterator<I>) { return Iter + Offset; }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (CCommonType<I, J>)
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return RHS.Num() - LHS.Num(); }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, FDefaultSentinel) { LHS.CheckThis(); return -LHS.Num(); }
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(FDefaultSentinel, const TCountedIterator& RHS) { RHS.CheckThis(); return RHS.Num(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const FIteratorType& GetBase() const& { CheckThis(); return Current; }
|
||||
NODISCARD FORCEINLINE constexpr FIteratorType GetBase() && { CheckThis(); return MoveTemp(Current); }
|
||||
NODISCARD FORCEINLINE constexpr ptrdiff Num() const { CheckThis(); return Length; }
|
||||
|
||||
private:
|
||||
|
||||
FIteratorType Current;
|
||||
ptrdiff Length;
|
||||
|
||||
# if DO_CHECK
|
||||
ptrdiff MaxLength;
|
||||
# endif
|
||||
|
||||
FORCEINLINE void CheckThis(bool bExceptEnd = false) const
|
||||
{
|
||||
checkf(static_cast<ptrdiff>(0) <= Length && Length <= MaxLength, TEXT("Read access violation. Please check Num()."));
|
||||
checkf(!(bExceptEnd && Length == static_cast<ptrdiff>(0)), TEXT("Read access violation. Please check Num()."));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static_assert( CInputIterator<TCountedIterator< IInputIterator<int&>>>);
|
||||
static_assert( CForwardIterator<TCountedIterator< IForwardIterator<int&>>>);
|
||||
static_assert(CBidirectionalIterator<TCountedIterator<IBidirectionalIterator<int&>>>);
|
||||
static_assert( CRandomAccessIterator<TCountedIterator< IRandomAccessIterator<int&>>>);
|
||||
static_assert( CContiguousIterator<TCountedIterator< IContiguousIterator<int&>>>);
|
||||
|
||||
static_assert(COutputIterator<TCountedIterator<IOutputIterator<int&>>, int>);
|
||||
|
||||
static_assert(CSizedSentinelFor<TCountedIterator<IForwardIterator<int>>, TCountedIterator<IForwardIterator<int>>>);
|
||||
|
||||
/** Creates a TCountedIterator of type inferred from the argument. */
|
||||
template <typename I> requires (CInputOrOutputIterator<TDecay<I>> && CConstructibleFrom<TDecay<I>, I>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeCountedIterator(I&& Iter, ptrdiff N)
|
||||
{
|
||||
return TCountedIterator<TDecay<I>>(Forward<I>(Iter), N);
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
203
Redcraft.Utility/Source/Public/Iterators/InsertIterator.h
Normal file
203
Redcraft.Utility/Source/Public/Iterators/InsertIterator.h
Normal file
@@ -0,0 +1,203 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/Noncopyable.h"
|
||||
#include "Templates/Invoke.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename F> class TInsertProxy;
|
||||
template <typename F> class TPostIncrementProxy;
|
||||
template <typename F> class TInsertIterator;
|
||||
|
||||
template <typename F>
|
||||
class TInsertProxy final : private FSingleton
|
||||
{
|
||||
public:
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE ~TInsertProxy() { checkf(bIsProduced, TEXT("Exception insert, Ensures that the value is assigned to the inserter.")); }
|
||||
# endif
|
||||
|
||||
template <typename T> requires (CInvocable<F, T>)
|
||||
FORCEINLINE constexpr void operator=(T&& InValue) const
|
||||
{
|
||||
checkf(!bIsProduced, TEXT("Exception insert, Ensure that no multiple values are assigned to the inserter."));
|
||||
Invoke(Iter.Storage, Forward<T>(InValue));
|
||||
check_code({ bIsProduced = true; });
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TInsertIterator<F>& Iter;
|
||||
|
||||
# if DO_CHECK
|
||||
mutable bool bIsProduced;
|
||||
# endif
|
||||
|
||||
FORCEINLINE constexpr TInsertProxy(TInsertIterator<F>& InIter) : Iter(InIter) { check_code({ bIsProduced = false; }); }
|
||||
|
||||
template <typename> friend class TPostIncrementProxy;
|
||||
template <typename> friend class TInsertIterator;
|
||||
};
|
||||
|
||||
static_assert(CAssignableFrom<TInsertProxy<void(*)(int)>, int>);
|
||||
|
||||
template <typename F>
|
||||
class TPostIncrementProxy : private FSingleton
|
||||
{
|
||||
public:
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE ~TPostIncrementProxy() { checkf(bIsProduced, TEXT("Exception insert, Ensures that the value is assigned to the inserter.")); }
|
||||
# endif
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TInsertProxy<F> operator*() const
|
||||
{
|
||||
checkf(!bIsProduced, TEXT("Exception insert, Ensure that no multiple values are assigned to the inserter."));
|
||||
check_code({ bIsProduced = true; });
|
||||
return TInsertProxy(Iter);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TInsertIterator<F>& Iter;
|
||||
|
||||
# if DO_CHECK
|
||||
mutable bool bIsProduced;
|
||||
# endif
|
||||
|
||||
FORCEINLINE constexpr TPostIncrementProxy(TInsertIterator<F>& InIter) : Iter(InIter) { check_code({ bIsProduced = false; }); }
|
||||
|
||||
template <typename> friend class TInsertProxy;
|
||||
template <typename> friend class TInsertIterator;
|
||||
};
|
||||
|
||||
static_assert(CIndirectlyWritable<TPostIncrementProxy<void(*)(int)>, int>);
|
||||
|
||||
template <typename F>
|
||||
class TInsertIterator final : private FNoncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr TInsertIterator() requires (CDefaultConstructible<F>) = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TInsertIterator(F InInserter) : Storage(MoveTemp(InInserter)) { check_code({ bIsProduced = false; }); }
|
||||
|
||||
FORCEINLINE constexpr TInsertIterator(TInsertIterator&&) = default;
|
||||
FORCEINLINE constexpr TInsertIterator& operator=(TInsertIterator&&) = default;
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TInsertProxy<F> operator*()
|
||||
{
|
||||
checkf(!bIsProduced, TEXT("Exception insert, Ensure that no multiple values are assigned to the inserter."));
|
||||
check_code({ bIsProduced = true; });
|
||||
return TInsertProxy<F>(*this);
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr TInsertIterator& operator++() { check_code({ bIsProduced = false; }); return *this; }
|
||||
|
||||
FORCEINLINE constexpr TPostIncrementProxy<F> operator++(int)
|
||||
{
|
||||
checkf(!bIsProduced, TEXT("Exception insert, Ensure that no multiple values are assigned to the inserter."));
|
||||
return TPostIncrementProxy<F>(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
F Storage;
|
||||
|
||||
# if DO_CHECK
|
||||
bool bIsProduced;
|
||||
# endif
|
||||
|
||||
template <typename> friend class TInsertProxy;
|
||||
template <typename> friend class TPostIncrementProxy;
|
||||
};
|
||||
|
||||
static_assert(COutputIterator<TInsertIterator<void(*)(int)>, int>);
|
||||
|
||||
template <typename C>
|
||||
class FFrontInserter
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr explicit FFrontInserter(C& InContainer) : Container(AddressOf(InContainer)) { }
|
||||
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr void operator()(T&& A) { Container->PushFront(Forward<T>(A)); }
|
||||
|
||||
private:
|
||||
|
||||
C* Container;
|
||||
|
||||
};
|
||||
|
||||
template <typename C>
|
||||
class FBackInserter
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr explicit FBackInserter(C& InContainer) : Container(AddressOf(InContainer)) { }
|
||||
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr void operator()(T&& A) { Container->PushBack(Forward<T>(A)); }
|
||||
|
||||
private:
|
||||
|
||||
C* Container;
|
||||
|
||||
};
|
||||
|
||||
template <typename C>
|
||||
class FInserter
|
||||
{
|
||||
public:
|
||||
|
||||
template <typename I>
|
||||
FORCEINLINE constexpr FInserter(C& InContainer, I&& InIter) : Container(AddressOf(InContainer)), Iter(Forward<I>(InIter)) { }
|
||||
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr void operator()(T&& A) { Iter = Container->Insert(Iter, Forward<T>(A)); ++Iter; }
|
||||
|
||||
private:
|
||||
|
||||
C* Container;
|
||||
typename C::FConstIterator Iter;
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/** Creates an iterator adapter inserted in the front of the container. */
|
||||
template <typename C>
|
||||
NODISCARD FORCEINLINE constexpr auto MakeFrontInserter(C& Container)
|
||||
{
|
||||
return NAMESPACE_PRIVATE::TInsertIterator(NAMESPACE_PRIVATE::FFrontInserter(Container));
|
||||
}
|
||||
|
||||
/** Creates an iterator adapter inserted in the back of the container. */
|
||||
template <typename C>
|
||||
NODISCARD FORCEINLINE constexpr auto MakeBackInserter(C& Container)
|
||||
{
|
||||
return NAMESPACE_PRIVATE::TInsertIterator(NAMESPACE_PRIVATE::FBackInserter(Container));
|
||||
}
|
||||
|
||||
/** Creates an iterator adapter inserted in the container. */
|
||||
template <typename C, typename I>
|
||||
NODISCARD FORCEINLINE constexpr auto MakeInserter(C& Container, I&& InIter)
|
||||
{
|
||||
return NAMESPACE_PRIVATE::TInsertIterator(NAMESPACE_PRIVATE::FInserter(Container, Forward<I>(InIter)));
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
10
Redcraft.Utility/Source/Public/Iterators/Iterators.h
Normal file
10
Redcraft.Utility/Source/Public/Iterators/Iterators.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
#include "Iterators/MoveIterator.h"
|
||||
#include "Iterators/CountedIterator.h"
|
||||
#include "Iterators/InsertIterator.h"
|
||||
154
Redcraft.Utility/Source/Public/Iterators/MoveIterator.h
Normal file
154
Redcraft.Utility/Source/Public/Iterators/MoveIterator.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Memory/Address.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
/**
|
||||
* An iterator adaptor which dereferences to a rvalue reference.
|
||||
* When based on at least an input iterator, the move iterator satisfies at least an input iterator
|
||||
* up to a random access iterator.
|
||||
*/
|
||||
template <CInputIterator I>
|
||||
class TMoveIterator final
|
||||
{
|
||||
public:
|
||||
|
||||
using FIteratorType = I;
|
||||
|
||||
using FElementType = TIteratorElement<I>;
|
||||
|
||||
FORCEINLINE constexpr TMoveIterator() requires (CDefaultConstructible<I>) = default;
|
||||
|
||||
FORCEINLINE constexpr TMoveIterator(const TMoveIterator&) = default;
|
||||
FORCEINLINE constexpr TMoveIterator(TMoveIterator&&) = default;
|
||||
FORCEINLINE constexpr TMoveIterator& operator=(const TMoveIterator&) = default;
|
||||
FORCEINLINE constexpr TMoveIterator& operator=(TMoveIterator&&) = default;
|
||||
FORCEINLINE constexpr ~TMoveIterator() = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TMoveIterator(FIteratorType InValue) : Current(MoveTemp(InValue)) { }
|
||||
|
||||
template <CInputIterator J> requires (!CSameAs<I, J> && CConstructibleFrom<I, const J&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const J&, I>) TMoveIterator(const TMoveIterator<J>& InValue) : Current(InValue.GetBase()) { }
|
||||
|
||||
template <CInputIterator J> requires (!CSameAs<I, J> && CConvertibleTo<const J&, I> && CAssignableFrom<I&, const J&>)
|
||||
FORCEINLINE constexpr TMoveIterator& operator=(const TMoveIterator<J>& InValue) { Current = InValue.GetBase(); return *this; }
|
||||
|
||||
template <CInputIterator J> requires (CEqualityComparable<I, J>)
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TMoveIterator& LHS, const TMoveIterator<J>& RHS) { return LHS.GetBase() == RHS.GetBase(); }
|
||||
|
||||
template <CInputIterator J> requires (CThreeWayComparable<I, J>)
|
||||
NODISCARD friend FORCEINLINE constexpr TCompareThreeWayResult<I, J> operator<=>(const TMoveIterator& LHS, const TMoveIterator<J>& RHS) { return LHS.GetBase() <=> RHS.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorRValueReference<I> operator*() const { return MoveTemp(*GetBase()); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorRValueReference<I> operator[](ptrdiff Index) const requires (CRandomAccessIterator<I>) { return MoveTemp(GetBase()[Index]); }
|
||||
|
||||
FORCEINLINE constexpr TMoveIterator& operator++() { ++Current; return *this; }
|
||||
FORCEINLINE constexpr TMoveIterator& operator--() requires (CBidirectionalIterator<I>) { --Current; return *this; }
|
||||
|
||||
FORCEINLINE constexpr void operator++(int) { Current++; }
|
||||
FORCEINLINE constexpr TMoveIterator operator++(int) requires (CForwardIterator<I>) { return TMoveIterator(Current++); }
|
||||
FORCEINLINE constexpr TMoveIterator operator--(int) requires (CBidirectionalIterator<I>) { return TMoveIterator(Current--); }
|
||||
|
||||
FORCEINLINE constexpr TMoveIterator& operator+=(ptrdiff Offset) requires (CRandomAccessIterator<I>) { Current += Offset; return *this; }
|
||||
FORCEINLINE constexpr TMoveIterator& operator-=(ptrdiff Offset) requires (CRandomAccessIterator<I>) { Current -= Offset; return *this; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TMoveIterator operator+(ptrdiff Offset) const requires (CRandomAccessIterator<I>) { TMoveIterator Temp = *this; Temp += Offset; return Temp; }
|
||||
NODISCARD FORCEINLINE constexpr TMoveIterator operator-(ptrdiff Offset) const requires (CRandomAccessIterator<I>) { TMoveIterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr TMoveIterator operator+(ptrdiff Offset, const TMoveIterator& Iter) requires (CRandomAccessIterator<I>) { return Iter + Offset; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TMoveIterator& LHS, const TMoveIterator& RHS) requires (CSizedSentinelFor<I, I>) { return LHS.GetBase() - RHS.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const FIteratorType& GetBase() const& { return Current; }
|
||||
NODISCARD FORCEINLINE constexpr FIteratorType GetBase() && { return MoveTemp(Current); }
|
||||
|
||||
private:
|
||||
|
||||
FIteratorType Current;
|
||||
|
||||
};
|
||||
|
||||
template <typename I, typename J> requires (!CSizedSentinelFor<I, J>)
|
||||
inline constexpr bool bDisableSizedSentinelFor<TMoveIterator<I>, TMoveIterator<J>> = true;
|
||||
|
||||
static_assert( CInputIterator<TMoveIterator< IInputIterator<int&>>>);
|
||||
static_assert( CForwardIterator<TMoveIterator< IForwardIterator<int&>>>);
|
||||
static_assert(CBidirectionalIterator<TMoveIterator<IBidirectionalIterator<int&>>>);
|
||||
static_assert( CRandomAccessIterator<TMoveIterator< IRandomAccessIterator<int&>>>);
|
||||
static_assert( CRandomAccessIterator<TMoveIterator< IContiguousIterator<int&>>>);
|
||||
|
||||
/**
|
||||
* A sentinel adaptor for use with TMoveIterator.
|
||||
* Whether based on un-sized or sized sentinel, the move sentinel satisfies the corresponding concept.
|
||||
*/
|
||||
template <CSemiregular S>
|
||||
class TMoveSentinel
|
||||
{
|
||||
public:
|
||||
|
||||
using FSentinelType = S;
|
||||
|
||||
FORCEINLINE constexpr TMoveSentinel() = default;
|
||||
FORCEINLINE constexpr TMoveSentinel(const TMoveSentinel&) = default;
|
||||
FORCEINLINE constexpr TMoveSentinel(TMoveSentinel&&) = default;
|
||||
FORCEINLINE constexpr TMoveSentinel& operator=(const TMoveSentinel&) = default;
|
||||
FORCEINLINE constexpr TMoveSentinel& operator=(TMoveSentinel&&) = default;
|
||||
FORCEINLINE constexpr ~TMoveSentinel() = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TMoveSentinel(FSentinelType InValue) : Last(InValue) { }
|
||||
|
||||
template <CSemiregular T> requires (!CSameAs<S, T> && CConstructibleFrom<S, const T&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const T&, S>) TMoveSentinel(const TMoveSentinel<T>& InValue) : Last(InValue.Last) { }
|
||||
|
||||
template <CSemiregular T> requires (!CSameAs<S, T> && CConvertibleTo<const T&, S> && CAssignableFrom<S&, const T&>)
|
||||
FORCEINLINE constexpr TMoveSentinel& operator=(const TMoveSentinel<T>& InValue) { Last = InValue.GetBase(); return *this; }
|
||||
|
||||
template <CInputIterator I> requires (CSentinelFor<S, I>)
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const TMoveIterator<I>& InValue) const& { return GetBase() == InValue.GetBase(); }
|
||||
|
||||
template <CInputIterator I> requires (CSizedSentinelFor<S, I>)
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TMoveSentinel& Sentinel, const TMoveIterator<I>& Iter) { return Sentinel.GetBase() - Iter.GetBase(); }
|
||||
|
||||
template <CInputIterator I> requires (CSizedSentinelFor<S, I>)
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TMoveIterator<I>& Iter, const TMoveSentinel& Sentinel) { return Iter.GetBase() - Sentinel.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const FSentinelType& GetBase() const& { return Last; }
|
||||
NODISCARD FORCEINLINE constexpr FSentinelType GetBase() && { return MoveTemp(Last); }
|
||||
|
||||
private:
|
||||
|
||||
FSentinelType Last;
|
||||
|
||||
};
|
||||
|
||||
static_assert( CSentinelFor<TMoveSentinel< ISentinelFor<IInputIterator<int>>>, TMoveIterator<IInputIterator<int>>>);
|
||||
static_assert(CSizedSentinelFor<TMoveSentinel<ISizedSentinelFor<IInputIterator<int>>>, TMoveIterator<IInputIterator<int>>>);
|
||||
|
||||
/** Creates a TMoveIterator of type inferred from the argument. */
|
||||
template <typename I> requires (CInputIterator<TDecay<I>> && CConstructibleFrom<TDecay<I>, I>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeMoveIterator(I&& Iter)
|
||||
{
|
||||
return TMoveIterator<TDecay<I>>(Forward<I>(Iter));
|
||||
}
|
||||
|
||||
/** Creates a TMoveSentinel of type inferred from the argument. */
|
||||
template <typename I> requires (CSemiregular<TDecay<I>> && CConstructibleFrom<TDecay<I>, I>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeMoveSentinel(I&& Iter)
|
||||
{
|
||||
return TMoveSentinel<TDecay<I>>(Forward<I>(Iter));
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
100
Redcraft.Utility/Source/Public/Iterators/ReverseIterator.h
Normal file
100
Redcraft.Utility/Source/Public/Iterators/ReverseIterator.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Memory/Address.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
/**
|
||||
* An iterator adaptor for reverse-order traversal.
|
||||
* When based on at least a bidirectional iterator, the reverse iterator satisfies at least a bidirectional iterator
|
||||
* up to a random access iterator. When based on an output iterator, the reverse iterator satisfies an output iterator.
|
||||
*/
|
||||
template <CBidirectionalIterator I>
|
||||
class TReverseIterator final
|
||||
{
|
||||
public:
|
||||
|
||||
using FIteratorType = I;
|
||||
|
||||
using FElementType = TIteratorElement<I>;
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator() = default;
|
||||
FORCEINLINE constexpr TReverseIterator(const TReverseIterator&) = default;
|
||||
FORCEINLINE constexpr TReverseIterator(TReverseIterator&&) = default;
|
||||
FORCEINLINE constexpr TReverseIterator& operator=(const TReverseIterator&) = default;
|
||||
FORCEINLINE constexpr TReverseIterator& operator=(TReverseIterator&&) = default;
|
||||
FORCEINLINE constexpr ~TReverseIterator() = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TReverseIterator(FIteratorType InValue) : Current(InValue) { }
|
||||
|
||||
template <CBidirectionalIterator J> requires (!CSameAs<I, J> && CConstructibleFrom<I, const J&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const J&, I>) TReverseIterator(const TReverseIterator<J>& InValue) : Current(InValue.GetBase()) { }
|
||||
|
||||
template <CBidirectionalIterator J> requires (!CSameAs<I, J> && CConvertibleTo<const J&, I> && CAssignableFrom<I&, const J&>)
|
||||
FORCEINLINE constexpr TReverseIterator& operator=(const TReverseIterator<J>& InValue) { Current = InValue.GetBase(); return *this; }
|
||||
|
||||
template <CBidirectionalIterator J> requires (CEqualityComparable<I, J>)
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TReverseIterator& LHS, const TReverseIterator<J>& RHS) { return LHS.GetBase() == RHS.GetBase(); }
|
||||
|
||||
template <CBidirectionalIterator J> requires (CThreeWayComparable<I, J>)
|
||||
NODISCARD friend FORCEINLINE constexpr TCompareThreeWayResult<I, J> operator<=>(const TReverseIterator& LHS, const TReverseIterator<J>& RHS) { return RHS.GetBase() <=> LHS.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReference<I> operator*() const { FIteratorType Temp = GetBase(); return *--Temp; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto operator->() const requires (requires(const I Iter) { { ToAddress(Iter) } -> CSameAs<TIteratorPointer<I>>; }) { FIteratorType Temp = GetBase(); return ToAddress(--Temp); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReference<I> operator[](ptrdiff Index) const requires (CRandomAccessIterator<I>) { return GetBase()[-Index - 1]; }
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator& operator++() { --Current; return *this; }
|
||||
FORCEINLINE constexpr TReverseIterator& operator--() { ++Current; return *this; }
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator operator++(int) { TReverseIterator Temp = *this; ++*this; return Temp; }
|
||||
FORCEINLINE constexpr TReverseIterator operator--(int) { TReverseIterator Temp = *this; --*this; return Temp; }
|
||||
|
||||
FORCEINLINE constexpr TReverseIterator& operator+=(ptrdiff Offset) requires (CRandomAccessIterator<I>) { Current -= Offset; return *this; }
|
||||
FORCEINLINE constexpr TReverseIterator& operator-=(ptrdiff Offset) requires (CRandomAccessIterator<I>) { Current += Offset; return *this; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TReverseIterator operator+(ptrdiff Offset) const requires (CRandomAccessIterator<I>) { TReverseIterator Temp = *this; Temp += Offset; return Temp; }
|
||||
NODISCARD FORCEINLINE constexpr TReverseIterator operator-(ptrdiff Offset) const requires (CRandomAccessIterator<I>) { TReverseIterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr TReverseIterator operator+(ptrdiff Offset, const TReverseIterator& Iter) requires (CRandomAccessIterator<I>) { return Iter + Offset; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TReverseIterator& LHS, const TReverseIterator& RHS) requires (CSizedSentinelFor<I, I>) { return RHS.GetBase() - LHS.GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const FIteratorType& GetBase() const& { return Current; }
|
||||
NODISCARD FORCEINLINE constexpr FIteratorType GetBase() && { return MoveTemp(Current); }
|
||||
|
||||
private:
|
||||
|
||||
FIteratorType Current;
|
||||
|
||||
};
|
||||
|
||||
template <typename I, typename J> requires (!CSizedSentinelFor<I, J>)
|
||||
inline constexpr bool bDisableSizedSentinelFor<TReverseIterator<I>, TReverseIterator<J>> = true;
|
||||
|
||||
static_assert(CBidirectionalIterator<TReverseIterator<IBidirectionalIterator<int&>>>);
|
||||
static_assert( CRandomAccessIterator<TReverseIterator< IRandomAccessIterator<int&>>>);
|
||||
static_assert( CRandomAccessIterator<TReverseIterator< IContiguousIterator<int&>>>);
|
||||
|
||||
static_assert(COutputIterator<TReverseIterator<IBidirectionalIterator<int&>>, int>);
|
||||
|
||||
/** Creates a TReverseIterator of type inferred from the argument. */
|
||||
template <typename I> requires (CBidirectionalIterator<TDecay<I>> && CConstructibleFrom<TDecay<I>, I>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeReverseIterator(I&& Iter)
|
||||
{
|
||||
return TReverseIterator<TDecay<I>>(Forward<I>(Iter));
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
115
Redcraft.Utility/Source/Public/Iterators/Sentinel.h
Normal file
115
Redcraft.Utility/Source/Public/Iterators/Sentinel.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
#if PLATFORM_COMPILER_GCC
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wnon-template-friend"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A concept specifies a type is a sentinel for an iterator and expression 'Iter == Sentinel' is valid.
|
||||
* In addition, the type must be default constructible and copyable.
|
||||
*/
|
||||
template <typename S, typename I>
|
||||
concept CSentinelFor = CSemiregular<S> && CInputOrOutputIterator<I> && CWeaklyEqualityComparable<S, I>;
|
||||
|
||||
/** This is an example of a sentinel for an iterator, indicate the traits that define a sentinel for an iterator. */
|
||||
template <CInputOrOutputIterator I>
|
||||
struct ISentinelFor
|
||||
{
|
||||
ISentinelFor();
|
||||
ISentinelFor(const ISentinelFor&);
|
||||
ISentinelFor* operator=(const ISentinelFor&);
|
||||
|
||||
bool operator==(const I&) const&;
|
||||
};
|
||||
|
||||
// Use ISentinelFor represents a sentinel for an iterator.
|
||||
static_assert(CSentinelFor<ISentinelFor<IInputOrOutputIterator<int>>, IInputOrOutputIterator<int>>);
|
||||
|
||||
// The CSentinelFor requires this code is valid.
|
||||
static_assert(
|
||||
requires(ISentinelFor<IInputOrOutputIterator<int>> Sentinel, IInputOrOutputIterator<int> Iter)
|
||||
{
|
||||
{ Iter == Sentinel } -> CBooleanTestable;
|
||||
{ Sentinel == Iter } -> CBooleanTestable;
|
||||
}
|
||||
);
|
||||
|
||||
/** Disable the CSizedSentinelFor concept for specific types. */
|
||||
template <typename S, typename I>
|
||||
inline constexpr bool bDisableSizedSentinelFor = false;
|
||||
|
||||
/**
|
||||
* A concept specifies a type is a sized sentinel for an iterator and expressions 'Sentinel - Iter' and 'Iter - Sentinel' are valid,
|
||||
* and the 'Sentinel - Iter' is equal to negative 'Iter - Sentinel'.
|
||||
* In addition, the type must be default constructible and copyable.
|
||||
*/
|
||||
template <typename S, typename I>
|
||||
concept CSizedSentinelFor = CSentinelFor<S, I>
|
||||
&& !bDisableSizedSentinelFor<TRemoveCVRef<S>, TRemoveCVRef<I>>
|
||||
&& requires(const I& Iter, const S& Sentinel)
|
||||
{
|
||||
{ Sentinel - Iter } -> CSameAs<ptrdiff>;
|
||||
{ Iter - Sentinel } -> CSameAs<ptrdiff>;
|
||||
};
|
||||
|
||||
/** This is an example of a sized sentinel for an iterator, indicate the traits that define a sized sentinel for an iterator. */
|
||||
template <CInputOrOutputIterator I>
|
||||
struct ISizedSentinelFor /* : ISentinelFor<I> */
|
||||
{
|
||||
ISizedSentinelFor(); // Also satisfies ISentinelFor<I>.
|
||||
ISizedSentinelFor(const ISizedSentinelFor&);
|
||||
ISizedSentinelFor(ISizedSentinelFor&&); // Also satisfies ISentinelFor<I>.
|
||||
ISizedSentinelFor& operator=(const ISizedSentinelFor&);
|
||||
ISizedSentinelFor& operator=(ISizedSentinelFor&&); // Also satisfies ISentinelFor<I>.
|
||||
|
||||
bool operator==(const I&) const&; // Also satisfies ISentinelFor<I>.
|
||||
|
||||
/** Subtraction operator. The 'Sentinel - Iter' is equal to negative 'Iter - Sentinel'. */
|
||||
friend ptrdiff operator-(const I&, const ISizedSentinelFor&);
|
||||
friend ptrdiff operator-(const ISizedSentinelFor&, const I&);
|
||||
};
|
||||
|
||||
// Use ISizedSentinelFor represents a sized sentinel for an iterator.
|
||||
static_assert(CSizedSentinelFor<ISizedSentinelFor<IInputOrOutputIterator<int>>, IInputOrOutputIterator<int>>);
|
||||
|
||||
// The CSentinelFor requires this code is valid.
|
||||
static_assert(
|
||||
requires(ISizedSentinelFor<IInputOrOutputIterator<int>> Sentinel, IInputOrOutputIterator<int> Iter)
|
||||
{
|
||||
{ Iter == Sentinel } -> CBooleanTestable;
|
||||
{ Sentinel == Iter } -> CBooleanTestable;
|
||||
{ Iter - Sentinel } -> CSameAs<ptrdiff>;
|
||||
{ Sentinel - Iter } -> CSameAs<ptrdiff>;
|
||||
}
|
||||
);
|
||||
|
||||
struct FDefaultSentinel { explicit FDefaultSentinel() = default; };
|
||||
|
||||
inline constexpr FDefaultSentinel DefaultSentinel{ };
|
||||
|
||||
struct FUnreachableSentinel
|
||||
{
|
||||
explicit FUnreachableSentinel() = default;
|
||||
|
||||
template <CWeaklyIncrementable I>
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const I&) const& { return false; }
|
||||
};
|
||||
|
||||
inline constexpr FUnreachableSentinel UnreachableSentinel{ };
|
||||
|
||||
#if PLATFORM_COMPILER_GCC
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
315
Redcraft.Utility/Source/Public/Iterators/Utility.h
Normal file
315
Redcraft.Utility/Source/Public/Iterators/Utility.h
Normal file
@@ -0,0 +1,315 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename T> using TWithReference = T&;
|
||||
|
||||
template <typename I> struct TIteratorElementImpl { };
|
||||
template <typename T> struct TIteratorElementImpl<T*> { using FType = TRemoveCV<T>; };
|
||||
|
||||
template <typename I> requires (requires { typename I::FElementType; })
|
||||
struct TIteratorElementImpl<I> { using FType = typename I::FElementType; };
|
||||
|
||||
template <typename I> struct TIteratorPointerImpl { };
|
||||
template <typename T> struct TIteratorPointerImpl<T*> { using FType = T*; };
|
||||
|
||||
template <typename I> requires (requires(I& Iter) { { Iter.operator->() } -> CPointer; })
|
||||
struct TIteratorPointerImpl<I> { using FType = decltype(DeclVal<I&>().operator->()); };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <typename T>
|
||||
concept CReferenceable = requires { typename NAMESPACE_PRIVATE::TWithReference<T>; };
|
||||
|
||||
template <typename T>
|
||||
concept CDereferenceable = requires(T& A) { { *A } -> CReferenceable; };
|
||||
|
||||
template <typename I>
|
||||
using TIteratorElement = typename NAMESPACE_PRIVATE::TIteratorElementImpl<TRemoveCVRef<I>>::FType;
|
||||
|
||||
template <typename I>
|
||||
using TIteratorPointer = typename NAMESPACE_PRIVATE::TIteratorPointerImpl<TRemoveCVRef<I>>::FType;
|
||||
|
||||
template <CReferenceable I>
|
||||
using TIteratorReference = decltype(*DeclVal<I&>());
|
||||
|
||||
template <CReferenceable I> requires (requires(I& Iter) { { MoveTemp(*Iter) } -> CReferenceable; })
|
||||
using TIteratorRValueReference = decltype(MoveTemp(*DeclVal<I&>()));
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename I>
|
||||
concept CIndirectlyReadable =
|
||||
requires(const I Iter)
|
||||
{
|
||||
typename TIteratorElement<I>;
|
||||
typename TIteratorReference<I>;
|
||||
typename TIteratorRValueReference<I>;
|
||||
{ *Iter } -> CSameAs<TIteratorReference<I>>;
|
||||
{ MoveTemp(*Iter) } -> CSameAs<TIteratorRValueReference<I>>;
|
||||
}
|
||||
&& CSameAs<TIteratorElement<I>, TRemoveCVRef<TIteratorElement<I>>>
|
||||
&& CCommonReference<TIteratorReference<I>&&, TIteratorElement<I>&>
|
||||
&& CCommonReference<TIteratorReference<I>&&, TIteratorRValueReference<I>&&>
|
||||
&& CCommonReference<TIteratorRValueReference<I>&&, const TIteratorElement<I>&>;
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/** A concept specifies a type is indirectly readable by expression '*Iter'. */
|
||||
template <typename I>
|
||||
concept CIndirectlyReadable = NAMESPACE_PRIVATE::CIndirectlyReadable<TRemoveCVRef<I>>;
|
||||
|
||||
/** This is an example of an indirectly readable type, indicate the traits that define an indirectly readable type. */
|
||||
template <CReferenceable T>
|
||||
struct IIndirectlyReadable
|
||||
{
|
||||
/**
|
||||
* The element type of the indirectly readable type.
|
||||
* It must be a non-const, non-volatile and non-reference type and can be referenced, i.e. not a void type.
|
||||
*/
|
||||
using FElementType = TRemoveCVRef<T>;
|
||||
|
||||
/**
|
||||
* Indirectly read the element from the indirectly readable type.
|
||||
* The return type may not be const FElementType&, this concept only requires that the return type
|
||||
* and FElementType has some relationship, such as copy constructible to FElementType if the type is copyable.
|
||||
* This means that returning a proxy class castable to FElementType is also valid.
|
||||
* If this is an iterator adaptor, use decltype(auto) to forward the return value.
|
||||
*/
|
||||
T operator*() const;
|
||||
};
|
||||
|
||||
// Use IIndirectlyReadable<int> represents an indirectly readable type and int is the regular element type.
|
||||
static_assert(CIndirectlyReadable<IIndirectlyReadable<int>> && CRegular<int>);
|
||||
|
||||
// The CIndirectlyReadable requires this code is valid.
|
||||
static_assert(
|
||||
requires(IIndirectlyReadable<int> Iter, int& A)
|
||||
{
|
||||
A = *Iter;
|
||||
}
|
||||
);
|
||||
|
||||
/** A concept specifies a type is indirectly writable by expression '*Iter = A'. */
|
||||
template <typename I, typename T>
|
||||
concept CIndirectlyWritable =
|
||||
requires(I&& Iter, T&& A)
|
||||
{
|
||||
*Iter = Forward<T>(A);
|
||||
*Forward<I>(Iter) = Forward<T>(A);
|
||||
const_cast<const TIteratorReference<I>&&>(*Iter) = Forward<T>(A);
|
||||
const_cast<const TIteratorReference<I>&&>(*Forward<I>(Iter)) = Forward<T>(A);
|
||||
};
|
||||
|
||||
/** This is an example of an indirectly writable type, indicate the traits that define an indirectly writable type. */
|
||||
template <CReferenceable T>
|
||||
struct IIndirectlyWritable
|
||||
{
|
||||
/**
|
||||
* Indirectly write the element from the indirectly writable type.
|
||||
* The return type may not be T&, this concept only requires that the return type and T has some relationship,
|
||||
* such as can be assigned from T& if the type is copyable or T&& if the type is movable.
|
||||
* This means that returning a proxy class can be assigned from T is also valid.
|
||||
* If this is also an indirectly readable type, the equivalent value is read after writing.
|
||||
* If this is an iterator adaptor, use decltype(auto) to forward the return value.
|
||||
*/
|
||||
T operator*() const;
|
||||
};
|
||||
|
||||
// Use IIndirectlyWritable<int> represents an indirectly writable type and int is the regular element type.
|
||||
static_assert(CIndirectlyWritable<IIndirectlyWritable<int&>, int> && CRegular<int>);
|
||||
|
||||
// The CIndirectlyWritable requires this code is valid.
|
||||
static_assert(
|
||||
requires(IIndirectlyWritable<int&> Iter, int& A)
|
||||
{
|
||||
*Iter = A;
|
||||
}
|
||||
);
|
||||
|
||||
/** A concept specifies a type is incrementable by expression '++Iter' and the type must be movable. */
|
||||
template <typename I>
|
||||
concept CWeaklyIncrementable = CMovable<I>
|
||||
&& requires(I Iter) { { ++Iter } -> CSameAs<I&>; Iter++; };
|
||||
|
||||
/** This is an example of a weakly incrementable type, indicate the traits that define a weakly incrementable type. */
|
||||
struct IWeaklyIncrementable
|
||||
{
|
||||
IWeaklyIncrementable(IWeaklyIncrementable&&);
|
||||
IWeaklyIncrementable* operator=(IWeaklyIncrementable&&);
|
||||
|
||||
IWeaklyIncrementable& operator++();
|
||||
|
||||
/** Post-increment operator. Specify, the concept not requires the return type is any specific type, so the return type can be void. */
|
||||
void operator++(int);
|
||||
};
|
||||
|
||||
// Use IWeaklyIncrementable represents a weakly incrementable type.
|
||||
static_assert(CWeaklyIncrementable<IWeaklyIncrementable>);
|
||||
|
||||
/**
|
||||
* A concept specifies a type is incrementable by expression 'Iter++' and the expression returns the original value.
|
||||
* In addition, the type must be default constructible, copyable and weakly equality comparable.
|
||||
*/
|
||||
template <typename I>
|
||||
concept CIncrementable = CRegular<I> && CWeaklyIncrementable<I>
|
||||
&& requires(I Iter) { { Iter++ } -> CSameAs<I>; };
|
||||
|
||||
/**
|
||||
* This is an example of an incrementable type, indicate the traits that define an incrementable type.
|
||||
* The copy object of this type produced by copy constructor, copy assignment or post-increment
|
||||
* should produce the same effect as the original object when incrementing.
|
||||
*/
|
||||
struct IIncrementable /* : IWeaklyIncrementable */
|
||||
{
|
||||
IIncrementable();
|
||||
IIncrementable(const IIncrementable&);
|
||||
IIncrementable(IIncrementable&&); // Also satisfies IWeaklyIncrementable.
|
||||
IIncrementable* operator=(const IIncrementable&);
|
||||
IIncrementable* operator=(IIncrementable&&); // Also satisfies IWeaklyIncrementable.
|
||||
|
||||
friend bool operator==(const IIncrementable&, const IIncrementable&);
|
||||
|
||||
IIncrementable& operator++(); // Also satisfies IWeaklyIncrementable.
|
||||
|
||||
/** Post-increment operator. Specify, the concept requires the return value is the original value before incrementing. */
|
||||
IIncrementable operator++(int);
|
||||
};
|
||||
|
||||
// Use IIncrementable represents an incrementable type.
|
||||
static_assert(CIncrementable<IIncrementable>);
|
||||
|
||||
/**
|
||||
* A concept specifies a type is potentially an iterator. It only requires weakly incrementable and dereferenceable.
|
||||
* This concept should only be used in scenarios where the specific type of the iterator is not important, such as iterator adapters.
|
||||
*/
|
||||
template <typename I>
|
||||
concept CInputOrOutputIterator = CWeaklyIncrementable<I>
|
||||
&& requires(I Iter) { { *Iter } -> CReferenceable; };
|
||||
|
||||
/** This is an example of an input or output iterator, indicate the traits that define an input or output iterator. */
|
||||
template <CReferenceable T>
|
||||
struct IInputOrOutputIterator /* : IWeaklyIncrementable */
|
||||
{
|
||||
// ~Begin CWeklyIncrementable.
|
||||
|
||||
IInputOrOutputIterator(IInputOrOutputIterator&&);
|
||||
IInputOrOutputIterator* operator=(IInputOrOutputIterator&&);
|
||||
|
||||
IInputOrOutputIterator& operator++();
|
||||
|
||||
void operator++(int);
|
||||
|
||||
// ~End CWeklyIncrementable.
|
||||
|
||||
/** Dereference operator. It does not matter what the return type is, as long as it is referenceable. */
|
||||
T operator*() const;
|
||||
};
|
||||
|
||||
// Use IInputOrOutputIterator represents an input or output iterator.
|
||||
static_assert(CInputOrOutputIterator<IInputOrOutputIterator<int>>);
|
||||
|
||||
/** A concept specifies a type is an input iterator. */
|
||||
template <typename I>
|
||||
concept CInputIterator = CInputOrOutputIterator<I> && CIndirectlyReadable<I>;
|
||||
|
||||
/** This is an example of an input iterator, indicate the traits that define an input iterator. */
|
||||
template <CReferenceable T>
|
||||
struct IInputIterator /* : IInputOrOutputIterator, IIndirectlyReadable */
|
||||
{
|
||||
// ~Begin CIndirectlyReadable.
|
||||
|
||||
using FElementType = TRemoveCVRef<T>;
|
||||
|
||||
// ~End CIndirectlyReadable.
|
||||
|
||||
// ~Begin CInputOrOutputIterator.
|
||||
|
||||
IInputIterator(IInputIterator&&);
|
||||
IInputIterator* operator=(IInputIterator&&);
|
||||
|
||||
T operator*() const; // Also satisfies CIndirectlyReadable.
|
||||
|
||||
IInputIterator& operator++();
|
||||
|
||||
void operator++(int);
|
||||
|
||||
// ~End CInputOrOutputIterator.
|
||||
};
|
||||
|
||||
// Use IInputIterator<int> represents an input iterator and int is the regular element type.
|
||||
static_assert(CInputIterator<IInputIterator<int>> && CRegular<int>);
|
||||
|
||||
// The CInputIterator requires this code is valid.
|
||||
static_assert(
|
||||
requires(IInputIterator<int> Iter, int& A)
|
||||
{
|
||||
++Iter;
|
||||
Iter++;
|
||||
A = *++Iter;
|
||||
A = *Iter;
|
||||
}
|
||||
);
|
||||
|
||||
/** A concept specifies a type is an output iterator and expression '*Iter++ = A' is valid to write the element. */
|
||||
template <typename I, typename T>
|
||||
concept COutputIterator = CInputOrOutputIterator<I> && CIndirectlyWritable<I, T>
|
||||
&& requires(I Iter) { { Iter++ } -> CIndirectlyWritable<T>; };
|
||||
|
||||
/** This is an example of an output iterator, indicate the traits that define an output iterator. */
|
||||
template <CReferenceable T>
|
||||
struct IOutputIterator /* : IInputOrOutputIterator, IIndirectlyWritable<T> */
|
||||
{
|
||||
// ~Begin CIndirectlyWritable.
|
||||
|
||||
IOutputIterator(IOutputIterator&&);
|
||||
IOutputIterator* operator=(IOutputIterator&&);
|
||||
|
||||
T operator*() const; // Also satisfies CIndirectlyWritable.
|
||||
|
||||
IOutputIterator& operator++();
|
||||
|
||||
/**
|
||||
* Post-increment operator.
|
||||
* Specify, the concept not requires the return type is self type,
|
||||
* but requires the expression '*Iter++ = A;' is equivalent to '*Iter = A; ++Iter;'.
|
||||
* This means that returning a proxy class that satisfies CIndirectlyWritable<T> is also valid.
|
||||
* Also satisfies CIndirectlyWritable.
|
||||
*/
|
||||
IIndirectlyWritable<T> operator++(int);
|
||||
|
||||
// ~End CIndirectlyWritable.
|
||||
};
|
||||
|
||||
// Use IOutputIterator<int> represents an output iterator and int is the regular element type.
|
||||
static_assert(COutputIterator<IOutputIterator<int&>, int> && CRegular<int>);
|
||||
|
||||
// The CInputIterator requires this code is valid.
|
||||
static_assert(
|
||||
requires(IOutputIterator<int&> Iter, int& A)
|
||||
{
|
||||
++Iter;
|
||||
Iter++;
|
||||
*++Iter = A;
|
||||
*Iter++ = A;
|
||||
*Iter = A;
|
||||
}
|
||||
);
|
||||
|
||||
#define ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT public: \
|
||||
NODISCARD FORCEINLINE constexpr auto begin() { return Begin(); } \
|
||||
NODISCARD FORCEINLINE constexpr auto begin() const { return Begin(); } \
|
||||
NODISCARD FORCEINLINE constexpr auto end() { return End(); } \
|
||||
NODISCARD FORCEINLINE constexpr auto end() const { return End(); }
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -17,7 +17,7 @@ concept CAllocatableObject = CObject<T> && !CConst<T> && !CVolatile<T> && CDestr
|
||||
|
||||
template <typename A, typename T = int>
|
||||
concept CAllocator = !CSameAs<A, FAllocatorInterface> && CAllocatableObject<T>
|
||||
&& requires (typename A::template ForElementType<T>& Allocator, T* InPtr, size_t Num, size_t NumAllocated)
|
||||
&& requires (typename A::template TForElementType<T>& Allocator, T* InPtr, size_t Num, size_t NumAllocated)
|
||||
{
|
||||
{ Allocator.Allocate(Num) } -> CSameAs<T*>;
|
||||
{ Allocator.Deallocate(InPtr) } -> CSameAs<void>;
|
||||
@@ -47,15 +47,15 @@ struct FAllocatorInterface
|
||||
static constexpr bool bSupportsMultipleAllocation = true;
|
||||
|
||||
template <CAllocatableObject T>
|
||||
class ForElementType /*: private FSingleton*/
|
||||
class TForElementType /*: private FSingleton*/
|
||||
{
|
||||
public:
|
||||
|
||||
ForElementType() = default;
|
||||
ForElementType(const ForElementType&) = delete;
|
||||
ForElementType(ForElementType&&) = delete;
|
||||
ForElementType& operator=(const ForElementType&) = delete;
|
||||
ForElementType& operator=(ForElementType&&) = delete;
|
||||
TForElementType() = default;
|
||||
TForElementType(const TForElementType&) = delete;
|
||||
TForElementType(TForElementType&&) = delete;
|
||||
TForElementType& operator=(const TForElementType&) = delete;
|
||||
TForElementType& operator=(TForElementType&&) = delete;
|
||||
|
||||
/** Allocates uninitialized storage. If 'InNum' is zero, return nullptr. */
|
||||
NODISCARD FORCEINLINE T* Allocate(size_t InNum) = delete;
|
||||
@@ -110,7 +110,7 @@ struct FAllocatorInterface
|
||||
\
|
||||
}; \
|
||||
\
|
||||
PREPROCESSOR_JOIN(T, Name)<typename Allocator::template ForElementType<Type>> Name;
|
||||
PREPROCESSOR_JOIN(T, Name)<typename Allocator::template TForElementType<Type>> Name;
|
||||
|
||||
/** This is heap allocator that calls Memory::Malloc() directly for memory allocation. */
|
||||
struct FHeapAllocator
|
||||
@@ -118,15 +118,15 @@ struct FHeapAllocator
|
||||
static constexpr bool bSupportsMultipleAllocation = true;
|
||||
|
||||
template <CAllocatableObject T>
|
||||
class ForElementType /*: private FSingleton*/
|
||||
class TForElementType /*: private FSingleton*/
|
||||
{
|
||||
public:
|
||||
|
||||
ForElementType() = default;
|
||||
ForElementType(const ForElementType&) = delete;
|
||||
ForElementType(ForElementType&&) = delete;
|
||||
ForElementType& operator=(const ForElementType&) = delete;
|
||||
ForElementType& operator=(ForElementType&&) = delete;
|
||||
TForElementType() = default;
|
||||
TForElementType(const TForElementType&) = delete;
|
||||
TForElementType(TForElementType&&) = delete;
|
||||
TForElementType& operator=(const TForElementType&) = delete;
|
||||
TForElementType& operator=(TForElementType&&) = delete;
|
||||
|
||||
NODISCARD FORCEINLINE T* Allocate(size_t InNum)
|
||||
{
|
||||
@@ -197,15 +197,15 @@ struct TInlineAllocator
|
||||
static constexpr bool bSupportsMultipleAllocation = false;
|
||||
|
||||
template <CAllocatableObject T>
|
||||
class ForElementType /*: private FSingleton*/
|
||||
class TForElementType /*: private FSingleton*/
|
||||
{
|
||||
public:
|
||||
|
||||
ForElementType() = default;
|
||||
ForElementType(const ForElementType&) = delete;
|
||||
ForElementType(ForElementType&&) = delete;
|
||||
ForElementType& operator=(const ForElementType&) = delete;
|
||||
ForElementType& operator=(ForElementType&&) = delete;
|
||||
TForElementType() = default;
|
||||
TForElementType(const TForElementType&) = delete;
|
||||
TForElementType(TForElementType&&) = delete;
|
||||
TForElementType& operator=(const TForElementType&) = delete;
|
||||
TForElementType& operator=(TForElementType&&) = delete;
|
||||
|
||||
NODISCARD FORCEINLINE T* Allocate(size_t InNum)
|
||||
{
|
||||
@@ -279,15 +279,15 @@ struct FNullAllocator
|
||||
static constexpr bool bSupportsMultipleAllocation = true;
|
||||
|
||||
template <CAllocatableObject T>
|
||||
class ForElementType /*: private FSingleton*/
|
||||
class TForElementType /*: private FSingleton*/
|
||||
{
|
||||
public:
|
||||
|
||||
ForElementType() = default;
|
||||
ForElementType(const ForElementType&) = delete;
|
||||
ForElementType(ForElementType&&) = delete;
|
||||
ForElementType& operator=(const ForElementType&) = delete;
|
||||
ForElementType& operator=(ForElementType&&) = delete;
|
||||
TForElementType() = default;
|
||||
TForElementType(const TForElementType&) = delete;
|
||||
TForElementType(TForElementType&&) = delete;
|
||||
TForElementType& operator=(const TForElementType&) = delete;
|
||||
TForElementType& operator=(TForElementType&&) = delete;
|
||||
|
||||
NODISCARD FORCEINLINE T* Allocate(size_t InNum) { check_no_entry(); return nullptr; }
|
||||
|
||||
@@ -14,7 +14,7 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename RT, typename ST>
|
||||
using TRawPointer = TConditional<CVoid<RT>, typename TPointerTraits<TRemoveCVRef<ST>>::ElementType*, RT>;
|
||||
using TRawPointer = TConditional<CVoid<RT>, typename TPointerTraits<TRemoveCVRef<ST>>::FElementType*, RT>;
|
||||
|
||||
template <bool bEnableInput, typename RT, typename ST, typename... Ts>
|
||||
class FInOutPtr final : private FSingleton
|
||||
@@ -57,7 +57,7 @@ NAMESPACE_PRIVATE_END
|
||||
template <typename RT = void, typename ST, typename... Ts> requires ((CVoid<RT>) || (CPointer<RT>)
|
||||
&& (requires(NAMESPACE_PRIVATE::TRawPointer<RT, ST>* RPtr, ST& SPtr, Ts&&... Args) { SPtr.Reset(RPtr, Forward<Ts>(Args)...); })
|
||||
|| (CConstructibleFrom<ST, NAMESPACE_PRIVATE::TRawPointer<RT, ST>, Ts...> && CMoveAssignable<ST>)
|
||||
&& requires { typename TPointerTraits<TRemoveCV<ST>>::ElementType; })
|
||||
&& requires { typename TPointerTraits<TRemoveCV<ST>>::FElementType; })
|
||||
auto OutPtr(ST& InPtr, Ts&&... Args)
|
||||
{
|
||||
return NAMESPACE_PRIVATE::FInOutPtr<false, NAMESPACE_PRIVATE::TRawPointer<RT, ST>, ST, Ts...>(InPtr, Forward<Ts>(Args)...);
|
||||
@@ -67,7 +67,7 @@ template <typename RT = void, typename ST, typename... Ts> requires ((CVoid<RT>)
|
||||
&& (requires(NAMESPACE_PRIVATE::TRawPointer<RT, ST>* RPtr, ST& SPtr, Ts&&... Args) { SPtr.Reset(RPtr, Forward<Ts>(Args)...); })
|
||||
|| (CConstructibleFrom<ST, NAMESPACE_PRIVATE::TRawPointer<RT, ST>, Ts...> && CMoveAssignable<ST>)
|
||||
&& requires(ST& SPtr) { { SPtr.Release() } -> CConvertibleTo<NAMESPACE_PRIVATE::TRawPointer<RT, ST>>; }
|
||||
&& requires { typename TPointerTraits<TRemoveCV<ST>>::ElementType; })
|
||||
&& requires { typename TPointerTraits<TRemoveCV<ST>>::FElementType; })
|
||||
auto InOutPtr(ST& InPtr, Ts&&... Args)
|
||||
{
|
||||
return NAMESPACE_PRIVATE::FInOutPtr<true, NAMESPACE_PRIVATE::TRawPointer<RT, ST>, ST, Ts...>(InPtr, Forward<Ts>(Args)...);
|
||||
|
||||
@@ -19,10 +19,10 @@ struct TPointerTraits<T*>
|
||||
{
|
||||
static constexpr bool bIsPointer = true;
|
||||
|
||||
using PointerType = T*;
|
||||
using ElementType = T;
|
||||
using FPointerType = T*;
|
||||
using FElementType = T;
|
||||
|
||||
static FORCEINLINE constexpr ElementType* ToAddress(PointerType InPtr)
|
||||
static FORCEINLINE constexpr FElementType* ToAddress(FPointerType InPtr)
|
||||
{
|
||||
return InPtr;
|
||||
}
|
||||
@@ -34,10 +34,10 @@ struct TPointerTraits<T(*)[]>
|
||||
{
|
||||
static constexpr bool bIsPointer = true;
|
||||
|
||||
using PointerType = T(*)[];
|
||||
using ElementType = T;
|
||||
using FPointerType = T(*)[];
|
||||
using FElementType = T;
|
||||
|
||||
static FORCEINLINE constexpr ElementType* ToAddress(PointerType InPtr)
|
||||
static FORCEINLINE constexpr FElementType* ToAddress(FPointerType InPtr)
|
||||
{
|
||||
return InPtr;
|
||||
}
|
||||
@@ -50,10 +50,10 @@ struct TPointerTraits<T(*)[]>
|
||||
{ \
|
||||
static constexpr bool bIsPointer = true; \
|
||||
\
|
||||
using PointerType = TPtr<T>; \
|
||||
using ElementType = TPtr<T>::ElementType; \
|
||||
using FPointerType = TPtr<T>; \
|
||||
using FElementType = TPtr<T>::FElementType; \
|
||||
\
|
||||
static FORCEINLINE constexpr ElementType* ToAddress(const PointerType& InPtr) \
|
||||
static FORCEINLINE constexpr FElementType* ToAddress(const FPointerType& InPtr) \
|
||||
{ \
|
||||
return InPtr.Get(); \
|
||||
} \
|
||||
|
||||
@@ -58,18 +58,18 @@ class alignas(Memory::ConstructiveInterference) FSharedController : private FSin
|
||||
{
|
||||
private:
|
||||
|
||||
using RefCounter = TAtomic<size_t>;
|
||||
using FRefCounter = TAtomic<size_t>;
|
||||
|
||||
// Ensure that counters are lock-free for performance.
|
||||
static_assert(RefCounter::bIsAlwaysLockFree);
|
||||
static_assert(FRefCounter::bIsAlwaysLockFree);
|
||||
|
||||
// When this count is zero the object is destroyed.
|
||||
// This count is the number of TSharedRef and TSharedPtr.
|
||||
RefCounter SharedReferenceCount;
|
||||
FRefCounter SharedReferenceCount;
|
||||
|
||||
// When this count is zero the controller is destroyed.
|
||||
// If SharedCounter is not zero this count is one more than the number of TWeakPtr.
|
||||
RefCounter WeakReferenceCount;
|
||||
FRefCounter WeakReferenceCount;
|
||||
|
||||
public:
|
||||
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
virtual void DestroyThis() { delete this; }
|
||||
|
||||
// Get shared reference count, no definite operation order.
|
||||
FORCEINLINE RefCounter::ValueType GetSharedReferenceCount()
|
||||
FORCEINLINE FRefCounter::FValueType GetSharedReferenceCount()
|
||||
{
|
||||
// Get the shared reference count as EMemoryOrder::Relaxed,
|
||||
// since this count is for reference only and has no guarantees,
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
// if the shared reference count is zero return false.
|
||||
bool AddSharedReferenceIfUnexpired()
|
||||
{
|
||||
RefCounter::ValueType OldSharedReferenceCount = GetSharedReferenceCount();
|
||||
FRefCounter::FValueType OldSharedReferenceCount = GetSharedReferenceCount();
|
||||
|
||||
// We need to make sure we don't increase the reference count from zero to one.
|
||||
while (true)
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
// where EMemoryOrder::Release ensures that the side effects of all operations
|
||||
// on the shared reference count of all threads are visible to this thread,
|
||||
// preventing the shared reference count from actually going to zero.
|
||||
RefCounter::ValueType OldSharedReferenceCount = SharedReferenceCount.FetchSub(1, EMemoryOrder::Release);
|
||||
FRefCounter::FValueType OldSharedReferenceCount = SharedReferenceCount.FetchSub(1, EMemoryOrder::Release);
|
||||
|
||||
// Make sure the shared reference count is not zero before.
|
||||
check(OldSharedReferenceCount != 0);
|
||||
@@ -166,7 +166,7 @@ public:
|
||||
{
|
||||
// The use of EMemoryOrder is the same as in ReleaseSharedReference().
|
||||
|
||||
RefCounter::ValueType OldWeakReferenceCount = WeakReferenceCount.FetchSub(1, EMemoryOrder::Release);
|
||||
FRefCounter::FValueType OldWeakReferenceCount = WeakReferenceCount.FetchSub(1, EMemoryOrder::Release);
|
||||
|
||||
check(OldWeakReferenceCount != 0);
|
||||
|
||||
@@ -522,7 +522,7 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
using SharedFromThisType = TSharedFromThis;
|
||||
using FSharedFromThisType = TSharedFromThis;
|
||||
|
||||
// Here it is updated by the private constructor of TSharedRef or TSharedPtr.
|
||||
mutable TWeakPtr<T> WeakThis;
|
||||
@@ -539,12 +539,12 @@ class TSharedRef final
|
||||
{
|
||||
private:
|
||||
|
||||
using Helper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
using FHelper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using WeakType = TWeakPtr<T>;
|
||||
using FElementType = T;
|
||||
using FWeakType = TWeakPtr<T>;
|
||||
|
||||
/** TSharedRef cannot be initialized by nullptr. */
|
||||
TSharedRef() = delete;
|
||||
@@ -610,18 +610,18 @@ public:
|
||||
FORCEINLINE ~TSharedRef() { Controller->ReleaseSharedReference(); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
FORCEINLINE TSharedRef& operator=(const TSharedRef& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedRef& operator=(const TSharedRef& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TSharedRef& operator=(const TSharedRef<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedRef& operator=(const TSharedRef<U>& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) { return FHelper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TSharedRef& operator=(TSharedRef<U>&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TSharedRef& operator=(TSharedRef<U>&& InValue) { return FHelper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Compares the pointer values of two TSharedRef. */
|
||||
template <typename U> requires (CEqualityComparable<T*, TRemoveExtent<U>*>)
|
||||
@@ -732,15 +732,15 @@ private:
|
||||
{
|
||||
check(!((Pointer == nullptr) ^ (Controller == nullptr)));
|
||||
|
||||
if constexpr (CClass<T> && !CVolatile<T> && requires { typename T::SharedFromThisType; })
|
||||
if constexpr (CClass<T> && !CVolatile<T> && requires { typename T::FSharedFromThisType; })
|
||||
{
|
||||
using SharedFromThisType = T::SharedFromThisType;
|
||||
using FSharedFromThisType = typename T::FSharedFromThisType;
|
||||
|
||||
if constexpr (CDerivedFrom<T, SharedFromThisType>)
|
||||
if constexpr (CDerivedFrom<T, FSharedFromThisType>)
|
||||
{
|
||||
if (Pointer != nullptr)
|
||||
{
|
||||
const SharedFromThisType& SharedFromThis = *Pointer;
|
||||
const FSharedFromThisType& SharedFromThis = *Pointer;
|
||||
checkf(!SharedFromThis.DoesSharedInstanceExist(), TEXT("This object is incorrectly managed by multiple TSharedRef or TSharedPtr."));
|
||||
SharedFromThis.WeakThis = ConstCast<TRemoveCV<T>>(*this);
|
||||
}
|
||||
@@ -762,12 +762,12 @@ class TSharedRef<T[]> final
|
||||
{
|
||||
private:
|
||||
|
||||
using Helper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
using FHelper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using WeakType = TWeakPtr<T>;
|
||||
using FElementType = T;
|
||||
using FWeakType = TWeakPtr<T>;
|
||||
|
||||
/** TSharedRef cannot be initialized by nullptr. */
|
||||
TSharedRef() = delete;
|
||||
@@ -835,18 +835,18 @@ public:
|
||||
FORCEINLINE ~TSharedRef() { Controller->ReleaseSharedReference(); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
FORCEINLINE TSharedRef& operator=(const TSharedRef& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedRef& operator=(const TSharedRef& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TSharedRef& operator=(const TSharedRef<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedRef& operator=(const TSharedRef<U>& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) { return FHelper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TSharedRef& operator=(TSharedRef<U>&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TSharedRef& operator=(TSharedRef<U>&& InValue) { return FHelper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Compares the pointer values of two TSharedRef. */
|
||||
template <typename U> requires (CEqualityComparable<T*, TRemoveExtent<U>*>)
|
||||
@@ -972,12 +972,12 @@ class TSharedPtr final
|
||||
{
|
||||
private:
|
||||
|
||||
using Helper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
using FHelper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using WeakType = TWeakPtr<T>;
|
||||
using FElementType = T;
|
||||
using FWeakType = TWeakPtr<T>;
|
||||
|
||||
/** Constructs an empty shared pointer. */
|
||||
FORCEINLINE constexpr TSharedPtr() : Pointer(nullptr), Controller(nullptr) { }
|
||||
@@ -1073,22 +1073,22 @@ public:
|
||||
FORCEINLINE ~TSharedPtr() { if (Controller != nullptr) Controller->ReleaseSharedReference(); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedPtr& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedPtr& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedPtr<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedPtr<U>& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedRef<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedRef<U>& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
FORCEINLINE TSharedPtr& operator=(TSharedPtr&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TSharedPtr& operator=(TSharedPtr&& InValue) { return FHelper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TSharedPtr& operator=(TSharedPtr<U>&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TSharedPtr& operator=(TSharedPtr<U>&& InValue) { return FHelper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
template <typename U, typename E> requires (CConvertibleTo<U*, T*> && !CArray<U> && (CDestructible<E> || CLValueReference<E>))
|
||||
@@ -1215,15 +1215,15 @@ private:
|
||||
{
|
||||
check(!((Pointer == nullptr) ^ (Controller == nullptr)));
|
||||
|
||||
if constexpr (CClass<T> && !CVolatile<T> && requires { typename T::SharedFromThisType; })
|
||||
if constexpr (CClass<T> && !CVolatile<T> && requires { typename T::FSharedFromThisType; })
|
||||
{
|
||||
using SharedFromThisType = T::SharedFromThisType;
|
||||
using FSharedFromThisType = typename T::FSharedFromThisType;
|
||||
|
||||
if constexpr (CDerivedFrom<T, SharedFromThisType>)
|
||||
if constexpr (CDerivedFrom<T, FSharedFromThisType>)
|
||||
{
|
||||
if (Pointer != nullptr)
|
||||
{
|
||||
const SharedFromThisType& SharedFromThis = *Pointer;
|
||||
const FSharedFromThisType& SharedFromThis = *Pointer;
|
||||
checkf(!SharedFromThis.DoesSharedInstanceExist(), TEXT("This object is incorrectly managed by multiple TSharedRef or TSharedPtr."));
|
||||
SharedFromThis.WeakThis = ConstCast<TRemoveCV<T>>(*this);
|
||||
}
|
||||
@@ -1245,12 +1245,12 @@ class TSharedPtr<T[]> final
|
||||
{
|
||||
private:
|
||||
|
||||
using Helper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
using FHelper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using WeakType = TWeakPtr<T>;
|
||||
using FElementType = T;
|
||||
using FWeakType = TWeakPtr<T>;
|
||||
|
||||
/** Constructs an empty shared pointer. */
|
||||
FORCEINLINE constexpr TSharedPtr() : Pointer(nullptr), Controller(nullptr) { }
|
||||
@@ -1348,22 +1348,22 @@ public:
|
||||
FORCEINLINE ~TSharedPtr() { if (Controller != nullptr) Controller->ReleaseSharedReference(); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedPtr& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedPtr& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedPtr<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedPtr<U>& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedRef<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }
|
||||
FORCEINLINE TSharedPtr& operator=(const TSharedRef<U>& InValue) { return FHelper::CopySharedReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
FORCEINLINE TSharedPtr& operator=(TSharedPtr&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TSharedPtr& operator=(TSharedPtr&& InValue) { return FHelper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TSharedPtr& operator=(TSharedPtr<U>&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TSharedPtr& operator=(TSharedPtr<U>&& InValue) { return FHelper::MoveSharedReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
template <typename U, typename E> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U> && (CDestructible<E> || CLValueReference<E>))
|
||||
@@ -1505,11 +1505,11 @@ class TWeakPtr final
|
||||
{
|
||||
private:
|
||||
|
||||
using Helper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
using FHelper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using FElementType = T;
|
||||
|
||||
/** Constructs an empty TWeakPtr */
|
||||
FORCEINLINE constexpr TWeakPtr() : Pointer(nullptr), Controller(nullptr) { }
|
||||
@@ -1564,26 +1564,26 @@ public:
|
||||
FORCEINLINE ~TWeakPtr() { if (Controller != nullptr) Controller->ReleaseWeakReference(); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
FORCEINLINE TWeakPtr& operator=(const TWeakPtr& InValue) { return Helper::CopyWeakReference(*this, InValue); }
|
||||
FORCEINLINE TWeakPtr& operator=(const TWeakPtr& InValue) { return FHelper::CopyWeakReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TWeakPtr& operator=(const TWeakPtr<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }
|
||||
FORCEINLINE TWeakPtr& operator=(const TWeakPtr<U>& InValue) { return FHelper::CopyWeakReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
FORCEINLINE TWeakPtr& operator=(TWeakPtr&& InValue) { return Helper::MoveWeakReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TWeakPtr& operator=(TWeakPtr&& InValue) { return FHelper::MoveWeakReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Replaces the managed object with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TWeakPtr& operator=(TWeakPtr<U>&& InValue) { return Helper::MoveWeakReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TWeakPtr& operator=(TWeakPtr<U>&& InValue) { return FHelper::MoveWeakReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Assignment operator sets this weak pointer from a shared reference. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TWeakPtr& operator=(const TSharedRef<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }
|
||||
FORCEINLINE TWeakPtr& operator=(const TSharedRef<U>& InValue) { return FHelper::CopyWeakReference(*this, InValue); }
|
||||
|
||||
/** Assignment operator sets this weak pointer from a shared pointer. */
|
||||
template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
|
||||
FORCEINLINE TWeakPtr& operator=(const TSharedPtr<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }
|
||||
FORCEINLINE TWeakPtr& operator=(const TSharedPtr<U>& InValue) { return FHelper::CopyWeakReference(*this, InValue); }
|
||||
|
||||
/** Effectively the same as calling Reset(). */
|
||||
FORCEINLINE TWeakPtr& operator=(nullptr_t) { Reset(); return *this; }
|
||||
@@ -1639,11 +1639,11 @@ class TWeakPtr<T[]> final
|
||||
{
|
||||
private:
|
||||
|
||||
using Helper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
using FHelper = NAMESPACE_PRIVATE::FSharedHelper;
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using FElementType = T;
|
||||
|
||||
/** Constructs an empty TWeakPtr */
|
||||
FORCEINLINE constexpr TWeakPtr() : Pointer(nullptr), Controller(nullptr) { }
|
||||
@@ -1698,26 +1698,26 @@ public:
|
||||
FORCEINLINE ~TWeakPtr() { if (Controller != nullptr) Controller->ReleaseWeakReference(); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
FORCEINLINE TWeakPtr& operator=(const TWeakPtr& InValue) { return Helper::CopyWeakReference(*this, InValue); }
|
||||
FORCEINLINE TWeakPtr& operator=(const TWeakPtr& InValue) { return FHelper::CopyWeakReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TWeakPtr& operator=(const TWeakPtr<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }
|
||||
FORCEINLINE TWeakPtr& operator=(const TWeakPtr<U>& InValue) { return FHelper::CopyWeakReference(*this, InValue); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
FORCEINLINE TWeakPtr& operator=(TWeakPtr&& InValue) { return Helper::MoveWeakReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TWeakPtr& operator=(TWeakPtr&& InValue) { return FHelper::MoveWeakReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Replaces the managed array with the one managed by 'InValue'. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TWeakPtr& operator=(TWeakPtr<U>&& InValue) { return Helper::MoveWeakReference(*this, MoveTemp(InValue)); }
|
||||
FORCEINLINE TWeakPtr& operator=(TWeakPtr<U>&& InValue) { return FHelper::MoveWeakReference(*this, MoveTemp(InValue)); }
|
||||
|
||||
/** Assignment operator sets this weak pointer from a shared reference. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TWeakPtr& operator=(const TSharedRef<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }
|
||||
FORCEINLINE TWeakPtr& operator=(const TSharedRef<U>& InValue) { return FHelper::CopyWeakReference(*this, InValue); }
|
||||
|
||||
/** Assignment operator sets this weak pointer from a shared pointer. */
|
||||
template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
|
||||
FORCEINLINE TWeakPtr& operator=(const TSharedPtr<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }
|
||||
FORCEINLINE TWeakPtr& operator=(const TSharedPtr<U>& InValue) { return FHelper::CopyWeakReference(*this, InValue); }
|
||||
|
||||
/** Effectively the same as calling Reset(). */
|
||||
FORCEINLINE TWeakPtr& operator=(nullptr_t) { Reset(); return *this; }
|
||||
|
||||
@@ -179,8 +179,8 @@ class TUniqueRef final : private FSingleton
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using DeleterType = E;
|
||||
using FElementType = T;
|
||||
using FDeleterType = E;
|
||||
|
||||
/** TUniqueRef cannot be initialized by nullptr. */
|
||||
TUniqueRef() = delete;
|
||||
@@ -308,8 +308,8 @@ class TUniqueRef<T[], E> final : private FSingleton
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using DeleterType = E;
|
||||
using FElementType = T;
|
||||
using FDeleterType = E;
|
||||
|
||||
/** TUniqueRef cannot be initialized by nullptr. */
|
||||
TUniqueRef() = delete;
|
||||
@@ -439,8 +439,8 @@ class TUniquePtr final : private FNoncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using DeleterType = E;
|
||||
using FElementType = T;
|
||||
using FDeleterType = E;
|
||||
|
||||
/** Constructs a TUniquePtr that owns nothing. Value-initializes the stored pointer and the stored deleter. */
|
||||
FORCEINLINE constexpr TUniquePtr() requires(CDefaultConstructible<E> && !CPointer<E>) : Storage(nullptr) { }
|
||||
@@ -585,8 +585,8 @@ class TUniquePtr<T[], E> final : private FNoncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
using DeleterType = E;
|
||||
using FElementType = T;
|
||||
using FDeleterType = E;
|
||||
|
||||
/** Constructs a TUniquePtr that owns nothing. Value-initializes the stored pointer and the stored deleter. */
|
||||
FORCEINLINE constexpr TUniquePtr() requires(CDefaultConstructible<E> && !CPointer<E>) : Storage(nullptr) { }
|
||||
|
||||
@@ -42,6 +42,8 @@ NAMESPACE_PRIVATE_END
|
||||
# define DO_CHECK 0
|
||||
#endif
|
||||
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
|
||||
#define always_check(InExpr) RS_CHECK_IMPL(InExpr)
|
||||
#define always_checkf(InExpr, InFormat, ...) RS_CHECK_F_IMPL(InExpr, InFormat, ##__VA_ARGS__)
|
||||
#define always_check_no_entry() always_checkf(false, "Enclosing block should never be called.")
|
||||
@@ -77,6 +79,8 @@ NAMESPACE_PRIVATE_END
|
||||
|
||||
#endif
|
||||
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
32
Redcraft.Utility/Source/Public/Miscellaneous/BitwiseEnum.h
Normal file
32
Redcraft.Utility/Source/Public/Miscellaneous/BitwiseEnum.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
#define ENABLE_ENUM_CLASS_BITWISE_OPERATIONS(Enum) \
|
||||
NODISCARD FORCEINLINE constexpr Enum operator| (Enum LHS, Enum RHS) { return static_cast<Enum>(static_cast<TUnderlyingType<Enum>>(LHS) | static_cast<TUnderlyingType<Enum>>(RHS)); } \
|
||||
NODISCARD FORCEINLINE constexpr Enum operator& (Enum LHS, Enum RHS) { return static_cast<Enum>(static_cast<TUnderlyingType<Enum>>(LHS) & static_cast<TUnderlyingType<Enum>>(RHS)); } \
|
||||
NODISCARD FORCEINLINE constexpr Enum operator^ (Enum LHS, Enum RHS) { return static_cast<Enum>(static_cast<TUnderlyingType<Enum>>(LHS) ^ static_cast<TUnderlyingType<Enum>>(RHS)); } \
|
||||
FORCEINLINE constexpr Enum& operator|=(Enum& LHS, Enum RHS) { LHS = LHS | RHS; return LHS; } \
|
||||
FORCEINLINE constexpr Enum& operator&=(Enum& LHS, Enum RHS) { LHS = LHS & RHS; return LHS; } \
|
||||
FORCEINLINE constexpr Enum& operator^=(Enum& LHS, Enum RHS) { LHS = LHS ^ RHS; return LHS; } \
|
||||
NODISCARD FORCEINLINE constexpr bool operator! (Enum E ) { return !static_cast<TUnderlyingType<Enum>>(E); } \
|
||||
NODISCARD FORCEINLINE constexpr Enum operator~ (Enum E ) { return static_cast<Enum>(~static_cast<TUnderlyingType<Enum>>(E)); }
|
||||
|
||||
#define FRIEND_ENUM_CLASS_BITWISE_OPERATIONS(Enum) \
|
||||
friend constexpr Enum operator| (Enum , Enum); \
|
||||
friend constexpr Enum operator& (Enum , Enum); \
|
||||
friend constexpr Enum operator^ (Enum , Enum); \
|
||||
friend constexpr Enum& operator|=(Enum&, Enum); \
|
||||
friend constexpr Enum& operator&=(Enum&, Enum); \
|
||||
friend constexpr Enum& operator^=(Enum&, Enum); \
|
||||
friend constexpr bool operator! (Enum ); \
|
||||
friend constexpr Enum operator~ (Enum );
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -12,17 +12,21 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
// The result of the three-way comparison operator is the built-in type of the compiler, which is directly introduced here
|
||||
|
||||
typedef NAMESPACE_STD::partial_ordering partial_ordering;
|
||||
typedef NAMESPACE_STD::weak_ordering weak_ordering;
|
||||
typedef NAMESPACE_STD::strong_ordering strong_ordering;
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
|
||||
using partial_ordering = NAMESPACE_STD::partial_ordering;
|
||||
using weak_ordering = NAMESPACE_STD::weak_ordering;
|
||||
using strong_ordering = NAMESPACE_STD::strong_ordering;
|
||||
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template<int32> struct TCommonComparisonCategoryBasic { };
|
||||
template<> struct TCommonComparisonCategoryBasic<0> { using Type = strong_ordering; };
|
||||
template<> struct TCommonComparisonCategoryBasic<2> { using Type = partial_ordering; };
|
||||
template<> struct TCommonComparisonCategoryBasic<4> { using Type = weak_ordering; };
|
||||
template<> struct TCommonComparisonCategoryBasic<6> { using Type = partial_ordering; };
|
||||
template<> struct TCommonComparisonCategoryBasic<0> { using FType = strong_ordering; };
|
||||
template<> struct TCommonComparisonCategoryBasic<2> { using FType = partial_ordering; };
|
||||
template<> struct TCommonComparisonCategoryBasic<4> { using FType = weak_ordering; };
|
||||
template<> struct TCommonComparisonCategoryBasic<6> { using FType = partial_ordering; };
|
||||
|
||||
template <typename... Ts>
|
||||
struct TCommonComparisonCategoryImpl
|
||||
@@ -38,7 +42,7 @@ struct TCommonComparisonCategoryImpl
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <typename... Ts>
|
||||
using TCommonComparisonCategory = typename NAMESPACE_PRIVATE::TCommonComparisonCategoryImpl<Ts...>::Type;
|
||||
using TCommonComparisonCategory = typename NAMESPACE_PRIVATE::TCommonComparisonCategoryImpl<Ts...>::FType;
|
||||
|
||||
template <typename T, typename OrderingType>
|
||||
concept CThreeWayComparesAs = CSameAs<TCommonComparisonCategory<T, OrderingType>, OrderingType>;
|
||||
|
||||
217
Redcraft.Utility/Source/Public/Miscellaneous/Console.h
Normal file
217
Redcraft.Utility/Source/Public/Miscellaneous/Console.h
Normal file
@@ -0,0 +1,217 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Strings/Formatting.h"
|
||||
#include "Strings/StringView.h"
|
||||
#include "Strings/String.h"
|
||||
#include "Miscellaneous/BitwiseEnum.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
/** An enumeration that defines the color of the console. */
|
||||
enum class EColor : uint8
|
||||
{
|
||||
Default = 0xFF,
|
||||
|
||||
Black = 0b0000,
|
||||
Red = 0b0001,
|
||||
Green = 0b0010,
|
||||
Blue = 0b0100,
|
||||
Intensity = 0b1000,
|
||||
|
||||
Cyan = Green | Blue,
|
||||
Magenta = Blue | Red,
|
||||
Yellow = Red | Green,
|
||||
|
||||
White = Red | Green | Blue,
|
||||
|
||||
BrightBlack = Intensity | Black,
|
||||
BrightRed = Intensity | Red,
|
||||
BrightGreen = Intensity | Green,
|
||||
BrightBlue = Intensity | Blue,
|
||||
BrightYellow = Intensity | Yellow,
|
||||
BrightMagenta = Intensity | Magenta,
|
||||
BrightCyan = Intensity | Cyan,
|
||||
BrightWhite = Intensity | White
|
||||
};
|
||||
|
||||
ENABLE_ENUM_CLASS_BITWISE_OPERATIONS(EColor)
|
||||
|
||||
/** @return The color of the console. */
|
||||
NODISCARD REDCRAFTUTILITY_API EColor GetForegroundColor();
|
||||
NODISCARD REDCRAFTUTILITY_API EColor GetBackgroundColor();
|
||||
|
||||
/** Set the color of the console. Returns the color that was successfully set. */
|
||||
REDCRAFTUTILITY_API EColor SetForegroundColor(EColor InColor);
|
||||
REDCRAFTUTILITY_API EColor SetBackgroundColor(EColor InColor);
|
||||
|
||||
/** @return The size of the console window. */
|
||||
NODISCARD REDCRAFTUTILITY_API uint GetWindowWidth();
|
||||
NODISCARD REDCRAFTUTILITY_API uint GetWindowHeight();
|
||||
|
||||
/** @return true if the standard stream is redirected. */
|
||||
NODISCARD REDCRAFTUTILITY_API bool IsInputRedirected();
|
||||
NODISCARD REDCRAFTUTILITY_API bool IsOutputRedirected();
|
||||
NODISCARD REDCRAFTUTILITY_API bool IsErrorRedirected();
|
||||
|
||||
/** Clear the console screen. */
|
||||
REDCRAFTUTILITY_API void Clear();
|
||||
|
||||
/** @return The input character from the standard input. */
|
||||
NODISCARD REDCRAFTUTILITY_API char Input(bool bEcho = true);
|
||||
|
||||
/** @return The input line from the standard input. */
|
||||
NODISCARD REDCRAFTUTILITY_API FString InputLn(bool bEcho = true);
|
||||
|
||||
/** Print the character to the standard output. */
|
||||
REDCRAFTUTILITY_API bool Print(char Char);
|
||||
|
||||
/** Print the formatted string to the standard output. */
|
||||
template <CFormattable... Ts>
|
||||
FORCEINLINE bool Print(FStringView Fmt, Ts&&... Args)
|
||||
{
|
||||
struct FStandardOutputIterator
|
||||
{
|
||||
FORCEINLINE constexpr FStandardOutputIterator& operator=(char Char)
|
||||
{
|
||||
bError |= !Print(Char);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr bool operator==(FDefaultSentinel) const { return bError; }
|
||||
|
||||
FORCEINLINE constexpr FStandardOutputIterator& operator*() { return *this; }
|
||||
FORCEINLINE constexpr FStandardOutputIterator& operator++() { return *this; }
|
||||
FORCEINLINE constexpr FStandardOutputIterator& operator++(int) { return *this; }
|
||||
|
||||
bool bError = false;
|
||||
};
|
||||
|
||||
static_assert(COutputIterator<FStandardOutputIterator, char>);
|
||||
|
||||
FStandardOutputIterator Iter;
|
||||
|
||||
auto Range = Ranges::View(Iter, DefaultSentinel);
|
||||
|
||||
Iter = Algorithms::Format(Range, Fmt, Forward<Ts>(Args)...);
|
||||
|
||||
return Iter != DefaultSentinel;
|
||||
}
|
||||
|
||||
/** Print the value to the standard output. */
|
||||
template <CFormattable T>
|
||||
FORCEINLINE bool Print(T&& Value)
|
||||
{
|
||||
if constexpr (CSameAs<TRemoveCVRef<T>, char>)
|
||||
{
|
||||
return Print(static_cast<char>(Value));
|
||||
}
|
||||
|
||||
else if constexpr (CConvertibleTo<T, FStringView>)
|
||||
{
|
||||
return Print(static_cast<FStringView>(Value));
|
||||
}
|
||||
|
||||
else return Print(TEXT("{0}"), Forward<T>(Value));
|
||||
}
|
||||
|
||||
/** Print the newline character to the standard output. */
|
||||
FORCEINLINE bool PrintLn()
|
||||
{
|
||||
return Print(TEXT("\n"));
|
||||
}
|
||||
|
||||
/** Print the string to the standard output and append the newline character. */
|
||||
template <CFormattable... Ts>
|
||||
FORCEINLINE bool PrintLn(FStringView Fmt, Ts&&... Args)
|
||||
{
|
||||
return Print(Fmt, Forward<Ts>(Args)...) && Print(TEXT("\n"));
|
||||
}
|
||||
|
||||
/** Print the value to the standard output and append the newline character. */
|
||||
template <CFormattable T>
|
||||
FORCEINLINE bool PrintLn(T&& Value)
|
||||
{
|
||||
return Print(Forward<T>(Value)) && Print(TEXT("\n"));
|
||||
}
|
||||
|
||||
/** Print the character to the standard error. */
|
||||
REDCRAFTUTILITY_API bool Error(char Char);
|
||||
|
||||
/** Print the formatted string to the standard error. */
|
||||
template <CFormattable... Ts>
|
||||
FORCEINLINE bool Error(FStringView Fmt, Ts&&... Args)
|
||||
{
|
||||
struct FStandardOutputIterator
|
||||
{
|
||||
FORCEINLINE constexpr FStandardOutputIterator& operator=(char Char)
|
||||
{
|
||||
bError |= !Error(Char);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr bool operator==(FDefaultSentinel) const { return bError; }
|
||||
|
||||
FORCEINLINE constexpr FStandardOutputIterator& operator*() { return *this; }
|
||||
FORCEINLINE constexpr FStandardOutputIterator& operator++() { return *this; }
|
||||
FORCEINLINE constexpr FStandardOutputIterator& operator++(int) { return *this; }
|
||||
|
||||
bool bError = false;
|
||||
};
|
||||
|
||||
static_assert(COutputIterator<FStandardOutputIterator, char>);
|
||||
|
||||
FStandardOutputIterator Iter;
|
||||
|
||||
auto Range = Ranges::View(Iter, DefaultSentinel);
|
||||
|
||||
Iter = Algorithms::Format(Range, Fmt, Forward<Ts>(Args)...);
|
||||
|
||||
return Iter != DefaultSentinel;
|
||||
}
|
||||
|
||||
/** Print the value to the standard error. */
|
||||
template <CFormattable T>
|
||||
FORCEINLINE bool Error(T&& Value)
|
||||
{
|
||||
if constexpr (CSameAs<TRemoveCVRef<T>, char>)
|
||||
{
|
||||
return Error(static_cast<char>(Value));
|
||||
}
|
||||
|
||||
else if constexpr (CConvertibleTo<T, FStringView>)
|
||||
{
|
||||
return Error(static_cast<FStringView>(Value));
|
||||
}
|
||||
|
||||
else return Error(TEXT("{0}"), Forward<T>(Value));
|
||||
}
|
||||
|
||||
/** Print the newline character to the standard error. */
|
||||
FORCEINLINE bool ErrorLn()
|
||||
{
|
||||
return Error(TEXT("\n"));
|
||||
}
|
||||
|
||||
/** Print the string to the standard error and append the newline character. */
|
||||
template <CFormattable... Ts>
|
||||
FORCEINLINE bool ErrorLn(FStringView Fmt, Ts&&... Args)
|
||||
{
|
||||
return Error(Fmt, Forward<Ts>(Args)...) && Error(TEXT("\n"));
|
||||
}
|
||||
|
||||
/** Print the value to the standard error and append the newline character. */
|
||||
template <CFormattable T>
|
||||
FORCEINLINE bool ErrorLn(T&& Value)
|
||||
{
|
||||
return Error(Forward<T>(Value)) && Error(TEXT("\n"));
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,226 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
/** An iterator that always points to the same value. */
|
||||
template <typename T> requires (CDestructible<T> || CLValueReference<T>)
|
||||
class TConstantIterator final
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = TRemoveCV<T>;
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator() = default;
|
||||
|
||||
FORCEINLINE constexpr ~TConstantIterator() = default;
|
||||
|
||||
template <typename U = T> requires (!CSameAs<TConstantIterator, TRemoveCVRef<U>> && CConstructibleFrom<T, U&&>)
|
||||
FORCEINLINE constexpr explicit TConstantIterator(U&& InValue) : Value(Forward<U>(InValue)) { }
|
||||
|
||||
template <typename U> requires (CConstructibleFrom<T, const U&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const U&, T>) TConstantIterator(const TConstantIterator<U>& InValue) : Value(InValue.Value) { }
|
||||
|
||||
template <typename U> requires (CConstructibleFrom<T, U&&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<U&&, T>) TConstantIterator(TConstantIterator<U>&& InValue) : Value(MoveTemp(InValue.Value)) { }
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator(const TConstantIterator&) requires (CCopyConstructible<T>) = default;
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator(TConstantIterator&&) requires (CMoveConstructible<T>) = default;
|
||||
|
||||
template <typename U> requires (CConvertibleTo<const U&, T> && CAssignableFrom<T&, const U&>)
|
||||
FORCEINLINE constexpr TConstantIterator& operator=(const TConstantIterator<U>& InValue) { Value = InValue.Value; return *this; }
|
||||
|
||||
template <typename U> requires (CConvertibleTo<U&&, T> && CAssignableFrom<T&, U&&>)
|
||||
FORCEINLINE constexpr TConstantIterator& operator=(TConstantIterator<U>&& InValue) { Value = MoveTemp(InValue.Value); return *this; }
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator& operator=(const TConstantIterator&) requires (CCopyAssignable<T>) = default;
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator& operator=(TConstantIterator&&) requires (CMoveAssignable<T>) = default;
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const T& operator*() const { return Value; }
|
||||
NODISCARD FORCEINLINE constexpr const T* operator->() const { return AddressOf(Value); }
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator& operator++() { return *this; }
|
||||
|
||||
FORCEINLINE constexpr void operator++(int) { }
|
||||
|
||||
private:
|
||||
|
||||
T Value;
|
||||
|
||||
template <typename U> requires (CDestructible<U> || CLValueReference<U>)
|
||||
friend class TConstantIterator;
|
||||
|
||||
};
|
||||
|
||||
static_assert(CInputIterator<TConstantIterator<int>>);
|
||||
|
||||
/** An iterator that always points to the same value. */
|
||||
template <typename T>
|
||||
class TConstantIterator<T&> final
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = TRemoveCV<T>;
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator() = default;
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator(const TConstantIterator&) = default;
|
||||
FORCEINLINE constexpr TConstantIterator(TConstantIterator&&) = default;
|
||||
FORCEINLINE constexpr TConstantIterator& operator=(const TConstantIterator&) = default;
|
||||
FORCEINLINE constexpr TConstantIterator& operator=(TConstantIterator&&) = default;
|
||||
|
||||
FORCEINLINE constexpr ~TConstantIterator() = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TConstantIterator(const T& InValue) : Ptr(AddressOf(InValue)) { }
|
||||
|
||||
FORCEINLINE constexpr explicit TConstantIterator(const T&& InValue) = delete;
|
||||
|
||||
template <typename U> requires (CConvertibleTo<U*, T*>)
|
||||
FORCEINLINE constexpr TConstantIterator(const TConstantIterator<U>& InValue) : Ptr(InValue.Ptr) { }
|
||||
|
||||
template <typename U> requires (CConvertibleTo<U*, T*>)
|
||||
FORCEINLINE constexpr TConstantIterator& operator=(const TConstantIterator<U>& InValue) { Ptr = InValue.Ptr; return *this; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const T& operator*() const { return *Ptr; }
|
||||
NODISCARD FORCEINLINE constexpr const T* operator->() const { return Ptr; }
|
||||
|
||||
FORCEINLINE constexpr TConstantIterator& operator++() { return *this; }
|
||||
|
||||
FORCEINLINE constexpr void operator++(int) { }
|
||||
|
||||
private:
|
||||
|
||||
const T* Ptr;
|
||||
|
||||
template <typename U> requires (CDestructible<U> || CLValueReference<U>)
|
||||
friend class TConstantIterator;
|
||||
|
||||
};
|
||||
|
||||
static_assert(CInputIterator<TConstantIterator<int&>>);
|
||||
|
||||
/** An iterator adapter specialization that tracks the distance of a constant iterator to the end of the range. */
|
||||
template <typename T> requires (CDestructible<T> || CLValueReference<T>)
|
||||
class TCountedIterator<TConstantIterator<T>> final
|
||||
{
|
||||
public:
|
||||
|
||||
using IteratorType = TConstantIterator<T>;
|
||||
|
||||
using ElementType = typename TConstantIterator<T>::ElementType;
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<IteratorType>) : Length(1), MaxLength(0) { }
|
||||
# else
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<IteratorType>) = default;
|
||||
# endif
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator(const TCountedIterator&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator(TCountedIterator&&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(const TCountedIterator&) = default;
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(TCountedIterator&&) = default;
|
||||
|
||||
FORCEINLINE constexpr ~TCountedIterator() = default;
|
||||
|
||||
template <typename U = IteratorType> requires (!CSameAs<TCountedIterator, TRemoveCVRef<U>> && CConstructibleFrom<IteratorType, U>)
|
||||
FORCEINLINE constexpr explicit TCountedIterator(U&& InValue, ptrdiff N) : Current(Forward<U>(InValue)), Length(N) { check_code({ MaxLength = N; }); }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConstructibleFrom<IteratorType, const J&>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const J&, IteratorType>) TCountedIterator(const TCountedIterator<J>& InValue) : Current(InValue.GetBase()), Length(InValue.Num()) { check_code({ MaxLength = InValue.MaxLength; }); }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConstructibleFrom<IteratorType, J>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<J&&, IteratorType>) TCountedIterator(TCountedIterator<J>&& InValue) : Current(MoveTemp(InValue).GetBase()), Length(InValue.Num()) { check_code({ MaxLength = InValue.MaxLength; }); }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<const J&, IteratorType> && CAssignableFrom<IteratorType&, const J&>)
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(const TCountedIterator<J>& InValue) { Current = InValue.GetBase(); Length = InValue.Num(); check_code({ MaxLength = InValue.MaxLength; }); return *this; }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<J&&, IteratorType> && CAssignableFrom<IteratorType&, J&&>)
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(TCountedIterator<J>&& InValue) { Current = MoveTemp(InValue).GetBase(); Length = InValue.Num(); check_code({ MaxLength = InValue.MaxLength; }); return *this; }
|
||||
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.Length == RHS.Length; }
|
||||
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.Length <=> RHS.Length; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(FDefaultSentinel) const& { return Length == static_cast<ptrdiff>(0); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(FDefaultSentinel) const& { return static_cast<ptrdiff>(0) <=> Length; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const TRemoveReference<T>& operator*() const { CheckThis(true ); return *Current; }
|
||||
NODISCARD FORCEINLINE constexpr const TRemoveReference<T>* operator->() const { CheckThis(false); return ToAddress(Current); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const TRemoveReference<T>& operator[](ptrdiff) const { return *this; }
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator& operator++() { --Length; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr TCountedIterator& operator--() { ++Length; CheckThis(); return *this; }
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator operator++(int) { TCountedIterator Temp = *this; --Length; CheckThis(); return Temp; }
|
||||
FORCEINLINE constexpr TCountedIterator operator--(int) { TCountedIterator Temp = *this; ++Length; CheckThis(); return Temp; }
|
||||
|
||||
FORCEINLINE constexpr TCountedIterator& operator+=(ptrdiff Offset) { Length -= Offset; CheckThis(); return *this; }
|
||||
FORCEINLINE constexpr TCountedIterator& operator-=(ptrdiff Offset) { Length += Offset; CheckThis(); return *this; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr TCountedIterator operator+(TCountedIterator Iter, ptrdiff Offset) { TCountedIterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
NODISCARD friend FORCEINLINE constexpr TCountedIterator operator+(ptrdiff Offset, TCountedIterator Iter) { TCountedIterator Temp = Iter; Temp += Offset; return Temp; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TCountedIterator operator-(ptrdiff Offset) const { TCountedIterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { LHS.CheckThis(); RHS.CheckThis(); return LHS.Length - RHS.Length; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, FDefaultSentinel) { LHS.CheckThis(); return -LHS.Num(); }
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(FDefaultSentinel, const TCountedIterator& RHS) { RHS.CheckThis(); return RHS.Num(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const IteratorType& GetBase() const& { CheckThis(); return Current; }
|
||||
NODISCARD FORCEINLINE constexpr IteratorType GetBase() && { CheckThis(); return Current; }
|
||||
NODISCARD FORCEINLINE constexpr ptrdiff Num() const { CheckThis(); return Length; }
|
||||
|
||||
private:
|
||||
|
||||
IteratorType Current;
|
||||
ptrdiff Length;
|
||||
|
||||
# if DO_CHECK
|
||||
ptrdiff MaxLength;
|
||||
# endif
|
||||
|
||||
FORCEINLINE void CheckThis(bool bExceptEnd = false) const
|
||||
{
|
||||
checkf(static_cast<ptrdiff>(0) <= Length && Length <= MaxLength, TEXT("Read access violation. Please check Num()."));
|
||||
checkf(!(bExceptEnd && Length == static_cast<ptrdiff>(0)), TEXT("Read access violation. Please check Num()."));
|
||||
}
|
||||
|
||||
template <CInputOrOutputIterator J>
|
||||
friend class TCountedIterator;
|
||||
|
||||
};
|
||||
|
||||
static_assert(CRandomAccessIterator<TCountedIterator<TConstantIterator<int>>>);
|
||||
static_assert(CSizedSentinelFor<FDefaultSentinel, TCountedIterator<TConstantIterator<int>>>);
|
||||
|
||||
template <typename T> requires (CDestructible<T> || CLValueReference<T>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeConstantIterator(T&& Value)
|
||||
{
|
||||
return TConstantIterator<T>(Forward<T>(Value));
|
||||
}
|
||||
|
||||
template <typename T> requires (CDestructible<T> || CLValueReference<T>)
|
||||
NODISCARD FORCEINLINE constexpr auto MakeCountedConstantIterator(T&& Value, ptrdiff N)
|
||||
{
|
||||
return TCountedIterator<TConstantIterator<T>>(MakeConstantIterator(Forward<T>(Value)), N);
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -9,6 +9,8 @@ NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
|
||||
#define NORETURN [[noreturn]]
|
||||
#define CARRIES_DEPENDENCY [[carries_dependency]]
|
||||
#define DEPRECATED(Message) [[deprecated(Message)]]
|
||||
@@ -42,6 +44,8 @@ using type_info = NAMESPACE_STD::type_info;
|
||||
template <typename T>
|
||||
using initializer_list = NAMESPACE_STD::initializer_list<T>;
|
||||
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
116
Redcraft.Utility/Source/Public/Miscellaneous/FileSystem.h
Normal file
116
Redcraft.Utility/Source/Public/Miscellaneous/FileSystem.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/Function.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "Strings/StringView.h"
|
||||
#include "Strings/String.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(FileSystem)
|
||||
|
||||
/** The encoding of the text file. */
|
||||
enum class EEncoding : uint8
|
||||
{
|
||||
Default,
|
||||
Narrow,
|
||||
Wide,
|
||||
UTF8,
|
||||
UTF16BE,
|
||||
UTF16LE,
|
||||
UTF32BE,
|
||||
UTF32LE,
|
||||
};
|
||||
|
||||
/** Loads the file at the specified path into the byte array. */
|
||||
REDCRAFTUTILITY_API bool LoadFileToArray(TArray<uint8>& Result, FStringView Path);
|
||||
|
||||
/** Saves the byte array to the file at the specified path. */
|
||||
REDCRAFTUTILITY_API bool SaveArrayToFile(TArrayView<const uint8> Data, FStringView Path);
|
||||
|
||||
/**
|
||||
* Loads the file at the specified path into the string.
|
||||
*
|
||||
* @param Result - The string to load the file into.
|
||||
* @param Path - The path to the file to load.
|
||||
* @param Encoding - The encoding of the file. The default value indicates automatic detection.
|
||||
* @param bVerify - Whether to verify the character validity of the file.
|
||||
*
|
||||
* @return true if the file was successfully loaded, false otherwise.
|
||||
*/
|
||||
template <CCharType T>
|
||||
REDCRAFTUTILITY_API bool LoadFileToString(TString<T>& Result, FStringView Path, FileSystem::EEncoding Encoding = FileSystem::EEncoding::Default, bool bVerify = false);
|
||||
|
||||
/**
|
||||
* Saves the string to the file at the specified path.
|
||||
*
|
||||
* @param String - The string to save to the file.
|
||||
* @param Path - The path to the file to save.
|
||||
* @param Encoding - The encoding of the file. The default value indicates the same as the string.
|
||||
* @param bWithBOM - Whether to write the BOM character at the beginning of the file. Not valid for narrow and wide encoding.
|
||||
*
|
||||
* @return true if the file was successfully saved, false otherwise.
|
||||
*/
|
||||
template <CCharType T>
|
||||
REDCRAFTUTILITY_API bool SaveStringToFile(TStringView<T> String, FStringView Path, FileSystem::EEncoding Encoding = FileSystem::EEncoding::Default, bool bWithBOM = true);
|
||||
|
||||
/**
|
||||
* Saves the string to the file at the specified path.
|
||||
*
|
||||
* @param String - The string to save to the file.
|
||||
* @param Path - The path to the file to save.
|
||||
* @param Encoding - The encoding of the file. The default value indicates the same as the string.
|
||||
* @param bWithBOM - Whether to write the BOM character at the beginning of the file. Not valid for narrow and wide encoding.
|
||||
*
|
||||
* @return true if the file was successfully saved, false otherwise.
|
||||
*/
|
||||
template <typename T> requires (CConvertibleTo<T&&, FStringView> || CConvertibleTo<T&&, FWStringView>
|
||||
|| CConvertibleTo<T&&, FU8StringView> || CConvertibleTo<T&&, FU16StringView> || CConvertibleTo<T&&, FU32StringView>)
|
||||
bool SaveStringToFile(T&& String, FStringView Path, FileSystem::EEncoding Encoding = FileSystem::EEncoding::Default, bool bWithBOM = true)
|
||||
{
|
||||
if constexpr (CConvertibleTo<T&&, FStringView>) return SaveStringToFile(FStringView (Forward<T>(String)), Path, Encoding, bWithBOM);
|
||||
else if constexpr (CConvertibleTo<T&&, FWStringView>) return SaveStringToFile(FWStringView (Forward<T>(String)), Path, Encoding, bWithBOM);
|
||||
else if constexpr (CConvertibleTo<T&&, FU8StringView>) return SaveStringToFile(FU8StringView (Forward<T>(String)), Path, Encoding, bWithBOM);
|
||||
else if constexpr (CConvertibleTo<T&&, FU16StringView>) return SaveStringToFile(FU16StringView(Forward<T>(String)), Path, Encoding, bWithBOM);
|
||||
else if constexpr (CConvertibleTo<T&&, FU32StringView>) return SaveStringToFile(FU32StringView(Forward<T>(String)), Path, Encoding, bWithBOM);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @return The size of the file at the specified path. */
|
||||
REDCRAFTUTILITY_API size_t FileSize(FStringView Path);
|
||||
|
||||
/** Deletes the file at the specified path. */
|
||||
REDCRAFTUTILITY_API bool Delete(FStringView Path);
|
||||
|
||||
/** @return true if the regular file at the specified path exists, false otherwise. */
|
||||
REDCRAFTUTILITY_API bool Exists(FStringView Path);
|
||||
|
||||
/** Copies the file from the source path to the destination path. */
|
||||
REDCRAFTUTILITY_API bool Copy(FStringView Destination, FStringView Source);
|
||||
|
||||
/** Renames the file from the source path to the destination path. */
|
||||
REDCRAFTUTILITY_API bool Rename(FStringView Destination, FStringView Source);
|
||||
|
||||
/** Creates the directory at the specified path. If recursive, it will not delete the created items on failure. */
|
||||
REDCRAFTUTILITY_API bool CreateDirectory(FStringView Path, bool bRecursive = false);
|
||||
|
||||
/** Deletes the directory at the specified path. If recursive, it will not recreate the deleted items on failure. */
|
||||
REDCRAFTUTILITY_API bool DeleteDirectory(FStringView Path, bool bRecursive = false);
|
||||
|
||||
/** @return true if the directory at the specified path exists, false otherwise. */
|
||||
REDCRAFTUTILITY_API bool ExistsDirectory(FStringView Path);
|
||||
|
||||
/** Iterates items in the directory at the specified path. */
|
||||
REDCRAFTUTILITY_API bool IterateDirectory(FStringView Path, TFunctionRef<bool(FStringView /* Path */, bool /* bIsDirectory */)> Visitor);
|
||||
|
||||
NAMESPACE_END(FileSystem)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -14,7 +14,9 @@ NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
// Build information macro
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
|
||||
// Platform information macro
|
||||
|
||||
#ifndef PLATFORM_NAME
|
||||
# error "PLATFORM_NAME must be defined."
|
||||
@@ -32,6 +34,8 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
# define PLATFORM_UNKNOWN 0
|
||||
#endif
|
||||
|
||||
// Build information macro
|
||||
|
||||
#ifndef BUILD_TYPE
|
||||
# error "BUILD_TYPE must be defined."
|
||||
#endif
|
||||
@@ -52,7 +56,29 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
# define BUILD_UNKNOWN 0
|
||||
#endif
|
||||
|
||||
// Whether the CPU is x86/x64 (i.e. both 32 and 64-bit variants)
|
||||
// Compiler information macro
|
||||
|
||||
#ifndef PLATFORM_COMPILER_NAME
|
||||
# error "COMPILER_NAME must be defined."
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_COMPILER_MSVC
|
||||
# define PLATFORM_COMPILER_MSVC 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_COMPILER_CLANG
|
||||
# define PLATFORM_COMPILER_CLANG 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_COMPILER_GCC
|
||||
# define PLATFORM_COMPILER_GCC 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_COMPILER_UNKNOWN
|
||||
# define PLATFORM_COMPILER_UNKNOWN 0
|
||||
#endif
|
||||
|
||||
// Architecture information macro
|
||||
|
||||
#ifndef PLATFORM_CPU_X86_FAMILY
|
||||
# if (defined(_M_IX86) || defined(__i386__) || defined(_M_X64) || defined(__amd64__) || defined(__x86_64__))
|
||||
@@ -62,8 +88,6 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Whether the CPU is AArch32/AArch64 (i.e. both 32 and 64-bit variants)
|
||||
|
||||
#ifndef PLATFORM_CPU_ARM_FAMILY
|
||||
# if (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || defined(_M_ARM64))
|
||||
# define PLATFORM_CPU_ARM_FAMILY 1
|
||||
@@ -72,9 +96,171 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_CPU_UNKNOWN_FAMILY
|
||||
# define PLATFORM_CPU_UNKNOWN_FAMILY (!(PLATFORM_CPU_X86_FAMILY || PLATFORM_CPU_ARM_FAMILY))
|
||||
#endif
|
||||
|
||||
// CPU bits information macro
|
||||
|
||||
#ifndef PLATFORM_CPU_BITS
|
||||
# if (defined(_M_X64) || defined(__amd64__) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
|
||||
# define PLATFORM_CPU_BITS 64
|
||||
# elif (defined(_M_IX86) || defined(__i386__) || defined(__arm__) || defined(_M_ARM))
|
||||
# define PLATFORM_CPU_BITS 32
|
||||
# else
|
||||
# define PLATFORM_CPU_BITS 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Endian information macro
|
||||
|
||||
#if !defined(PLATFORM_LITTLE_ENDIAN) && !defined(PLATFORM_BIG_ENDIAN)
|
||||
# if PLATFORM_COMPILER_MSVC
|
||||
# define PLATFORM_LITTLE_ENDIAN 1
|
||||
# define PLATFORM_BIG_ENDIAN 0
|
||||
# elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define PLATFORM_LITTLE_ENDIAN 1
|
||||
# define PLATFORM_BIG_ENDIAN 0
|
||||
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define PLATFORM_LITTLE_ENDIAN 0
|
||||
# define PLATFORM_BIG_ENDIAN 1
|
||||
# else
|
||||
# define PLATFORM_LITTLE_ENDIAN 0
|
||||
# define PLATFORM_BIG_ENDIAN 0
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_LITTLE_ENDIAN
|
||||
# define PLATFORM_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_BIG_ENDIAN
|
||||
# define PLATFORM_BIG_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_CPU_MIXED_ENDIAN
|
||||
# define PLATFORM_MIXED_ENDIAN (!(PLATFORM_LITTLE_ENDIAN || PLATFORM_BIG_ENDIAN))
|
||||
#endif
|
||||
|
||||
// Integral type information macro
|
||||
|
||||
#ifndef PLATFORM_HAS_INT8
|
||||
# define PLATFORM_HAS_INT8 1
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_INT16
|
||||
# define PLATFORM_HAS_INT16 1
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_INT32
|
||||
# define PLATFORM_HAS_INT32 1
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_INT64
|
||||
# define PLATFORM_HAS_INT64 1
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_INT128
|
||||
# if defined(__SIZEOF_INT128__)
|
||||
# define PLATFORM_HAS_INT128 (__SIZEOF_INT128__ == 16)
|
||||
# else
|
||||
# define PLATFORM_HAS_INT128 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_INTMAX_BITS
|
||||
# if PLATFORM_HAS_INT128
|
||||
# define PLATFORM_INTMAX_BITS 128
|
||||
# else
|
||||
# define PLATFORM_INTMAX_BITS 64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !(PLATFORM_HAS_INT8 && PLATFORM_HAS_INT16 && PLATFORM_HAS_INT32 && PLATFORM_HAS_INT64)
|
||||
# error "64-bit and below integral types must be supported."
|
||||
#endif
|
||||
|
||||
// Floating point type information macro
|
||||
|
||||
#ifndef PLATFORM_HAS_FLOAT16
|
||||
# if defined(__STDCPP_FLOAT16_T__)
|
||||
# define PLATFORM_HAS_FLOAT16 1
|
||||
# else
|
||||
# define PLATFORM_HAS_FLOAT16 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_FLOAT32
|
||||
# define PLATFORM_HAS_FLOAT32 1
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_FLOAT64
|
||||
# define PLATFORM_HAS_FLOAT64 1
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_FLOAT128
|
||||
# if defined(__STDCPP_FLOAT128_T__)
|
||||
# define PLATFORM_HAS_FLOAT128 1
|
||||
# else
|
||||
# define PLATFORM_HAS_FLOAT128 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_BFLOAT16
|
||||
# if defined(__STDCPP_BFLOAT16_T__)
|
||||
# define PLATFORM_HAS_BFLOAT16 1
|
||||
# else
|
||||
# define PLATFORM_HAS_BFLOAT16 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Character type information macro
|
||||
|
||||
#ifndef PLATFORM_HAS_WCHAR
|
||||
# define PLATFORM_HAS_WCHAR 1
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_U8CHAR
|
||||
# ifdef __cpp_char8_t
|
||||
# define PLATFORM_HAS_U8CHAR 1
|
||||
# else
|
||||
# define PLATFORM_HAS_U8CHAR 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_U16CHAR
|
||||
# ifdef __cpp_unicode_characters
|
||||
# define PLATFORM_HAS_U16CHAR 1
|
||||
# else
|
||||
# define PLATFORM_HAS_U16CHAR 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_U32CHAR
|
||||
# ifdef __cpp_unicode_characters
|
||||
# define PLATFORM_HAS_U32CHAR 1
|
||||
# else
|
||||
# define PLATFORM_HAS_U32CHAR 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_HAS_UNICODECHAR
|
||||
# ifdef __cpp_unicode_characters
|
||||
# define PLATFORM_HAS_UNICODECHAR 1
|
||||
# else
|
||||
# define PLATFORM_HAS_UNICODECHAR 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !(PLATFORM_HAS_WCHAR && PLATFORM_HAS_U8CHAR && PLATFORM_HAS_U16CHAR && PLATFORM_HAS_U32CHAR)
|
||||
# error "All character types must be supported."
|
||||
#endif
|
||||
|
||||
// Function type macros
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_COMPILER_MSVC
|
||||
|
||||
# define VARARGS __cdecl
|
||||
# define CDECL __cdecl
|
||||
@@ -83,7 +269,7 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
# define FORCENOINLINE __declspec(noinline)
|
||||
# define RESTRICT __restrict
|
||||
|
||||
#elif PLATFORM_LINUX
|
||||
#elif PLATFORM_COMPILER_CLANG || PLATFORM_COMPILER_GCC
|
||||
|
||||
# define VARARGS
|
||||
# define CDECL
|
||||
@@ -110,10 +296,10 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
// DLL export and import macros
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_COMPILER_MSVC
|
||||
# define DLLEXPORT __declspec(dllexport)
|
||||
# define DLLIMPORT __declspec(dllimport)
|
||||
#elif PLATFORM_LINUX
|
||||
#elif PLATFORM_COMPILER_CLANG || PLATFORM_COMPILER_GCC
|
||||
# define DLLEXPORT __attribute__((visibility("default")))
|
||||
# define DLLIMPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
@@ -122,13 +308,13 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
// Optimization macros
|
||||
|
||||
#if !defined(__clang__)
|
||||
#if PLATFORM_COMPILER_MSVC
|
||||
# define PRAGMA_DISABLE_OPTIMIZATION_ACTUAL _Pragma("optimize(\"\", off)")
|
||||
# define PRAGMA_ENABLE_OPTIMIZATION_ACTUAL _Pragma("optimize(\"\", on)")
|
||||
#elif defined(_MSC_VER)
|
||||
#elif PLATFORM_COMPILER_CLANG
|
||||
# define PRAGMA_DISABLE_OPTIMIZATION_ACTUAL _Pragma("clang optimize off")
|
||||
# define PRAGMA_ENABLE_OPTIMIZATION_ACTUAL _Pragma("clang optimize on")
|
||||
#elif defined(__GNUC__ )
|
||||
#elif PLATFORM_COMPILER_GCC
|
||||
# define PRAGMA_DISABLE_OPTIMIZATION_ACTUAL _Pragma("GCC push_options") _Pragma("GCC optimize (\"O0\")")
|
||||
# define PRAGMA_ENABLE_OPTIMIZATION_ACTUAL _Pragma("GCC pop_options")
|
||||
#else
|
||||
@@ -144,13 +330,6 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
# define PRAGMA_ENABLE_OPTIMIZATION PRAGMA_ENABLE_OPTIMIZATION_ACTUAL
|
||||
#endif
|
||||
|
||||
// Unsigned integral types
|
||||
|
||||
using uint8 = NAMESPACE_STD::uint8_t;
|
||||
using uint16 = NAMESPACE_STD::uint16_t;
|
||||
using uint32 = NAMESPACE_STD::uint32_t;
|
||||
using uint64 = NAMESPACE_STD::uint64_t;
|
||||
|
||||
// Signed integral types
|
||||
|
||||
using int8 = NAMESPACE_STD::int8_t;
|
||||
@@ -158,35 +337,142 @@ using int16 = NAMESPACE_STD::int16_t;
|
||||
using int32 = NAMESPACE_STD::int32_t;
|
||||
using int64 = NAMESPACE_STD::int64_t;
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
using int128 = __int128;
|
||||
#endif
|
||||
|
||||
using int8_least = NAMESPACE_STD::int_least8_t;
|
||||
using int16_least = NAMESPACE_STD::int_least16_t;
|
||||
using int32_least = NAMESPACE_STD::int_least32_t;
|
||||
using int64_least = NAMESPACE_STD::int_least64_t;
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
using int128_least = int128;
|
||||
#endif
|
||||
|
||||
using int8_fast = NAMESPACE_STD::int_fast8_t;
|
||||
using int16_fast = NAMESPACE_STD::int_fast16_t;
|
||||
using int32_fast = NAMESPACE_STD::int_fast32_t;
|
||||
using int64_fast = NAMESPACE_STD::int_fast64_t;
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
using int128_fast = int128;
|
||||
#endif
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
using intmax = int128;
|
||||
#else
|
||||
using intmax = int64;
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(int8) == 1, "int8 must be 1 byte");
|
||||
static_assert(sizeof(int16) == 2, "int16 must be 2 bytes");
|
||||
static_assert(sizeof(int32) == 4, "int32 must be 4 bytes");
|
||||
static_assert(sizeof(int64) == 8, "int64 must be 8 bytes");
|
||||
|
||||
static_assert(sizeof(int8_least) >= 1, "int8_least must be at least 1 byte");
|
||||
static_assert(sizeof(int16_least) >= 2, "int16_least must be at least 2 bytes");
|
||||
static_assert(sizeof(int32_least) >= 4, "int32_least must be at least 4 bytes");
|
||||
static_assert(sizeof(int64_least) >= 8, "int64_least must be at least 8 bytes");
|
||||
|
||||
static_assert(sizeof(int8_fast) >= 1, "int8_fast must be at least 1 byte");
|
||||
static_assert(sizeof(int16_fast) >= 2, "int16_fast must be at least 2 bytes");
|
||||
static_assert(sizeof(int32_fast) >= 4, "int32_fast must be at least 4 bytes");
|
||||
static_assert(sizeof(int64_fast) >= 8, "int64_fast must be at least 8 bytes");
|
||||
|
||||
static_assert(static_cast<int8>(0xFF) == -1, "int8 use two's complement");
|
||||
static_assert(static_cast<int16>(0xFFFF) == -1, "int16 use two's complement");
|
||||
static_assert(static_cast<int32>(0xFFFFFFFF) == -1, "int32 use two's complement");
|
||||
static_assert(static_cast<int64>(0xFFFFFFFFFFFFFFFF) == -1, "int64 use two's complement");
|
||||
|
||||
// Unsigned integral types
|
||||
|
||||
using uint = unsigned int;
|
||||
using byte = unsigned char;
|
||||
|
||||
using uint8 = NAMESPACE_STD::uint8_t;
|
||||
using uint16 = NAMESPACE_STD::uint16_t;
|
||||
using uint32 = NAMESPACE_STD::uint32_t;
|
||||
using uint64 = NAMESPACE_STD::uint64_t;
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
using uint128 = unsigned __int128;
|
||||
#endif
|
||||
|
||||
using uint8_least = NAMESPACE_STD::uint_least8_t;
|
||||
using uint16_least = NAMESPACE_STD::uint_least16_t;
|
||||
using uint32_least = NAMESPACE_STD::uint_least32_t;
|
||||
using uint64_least = NAMESPACE_STD::uint_least64_t;
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
using uint128_least = uint128;
|
||||
#endif
|
||||
|
||||
using uint8_fast = NAMESPACE_STD::uint_fast8_t;
|
||||
using uint16_fast = NAMESPACE_STD::uint_fast16_t;
|
||||
using uint32_fast = NAMESPACE_STD::uint_fast32_t;
|
||||
using uint64_fast = NAMESPACE_STD::uint_fast64_t;
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
using uint128_fast = uint128;
|
||||
#endif
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
using uintmax = uint128;
|
||||
#else
|
||||
using uintmax = uint64;
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(uint8) == 1, "uint8 must be 1 byte");
|
||||
static_assert(sizeof(uint16) == 2, "uint16 must be 2 bytes");
|
||||
static_assert(sizeof(uint32) == 4, "uint32 must be 4 bytes");
|
||||
static_assert(sizeof(uint64) == 8, "uint64 must be 8 bytes");
|
||||
|
||||
static_assert(sizeof(uint8_least) >= 1, "uint8_least must be at least 1 byte");
|
||||
static_assert(sizeof(uint16_least) >= 2, "uint16_least must be at least 2 bytes");
|
||||
static_assert(sizeof(uint32_least) >= 4, "uint32_least must be at least 4 bytes");
|
||||
static_assert(sizeof(uint64_least) >= 8, "uint64_least must be at least 8 bytes");
|
||||
|
||||
static_assert(sizeof(uint8_fast) >= 1, "uint8_fast must be at least 1 byte");
|
||||
static_assert(sizeof(uint16_fast) >= 2, "uint16_fast must be at least 2 bytes");
|
||||
static_assert(sizeof(uint32_fast) >= 4, "uint32_fast must be at least 4 bytes");
|
||||
static_assert(sizeof(uint64_fast) >= 8, "uint64_fast must be at least 8 bytes");
|
||||
|
||||
static_assert(static_cast<uint8 >(-1) > static_cast< uint8>(0), "uint8 must be unsigned");
|
||||
static_assert(static_cast<uint16>(-1) > static_cast<uint16>(0), "uint16 must be unsigned");
|
||||
static_assert(static_cast<uint32>(-1) > static_cast<uint32>(0), "uint32 must be unsigned");
|
||||
static_assert(static_cast<uint64>(-1) > static_cast<uint64>(0), "uint64 must be unsigned");
|
||||
|
||||
// Floating point types
|
||||
|
||||
#if defined(__STDCPP_FLOAT16_T__)
|
||||
#if PLATFORM_HAS_FLOAT16
|
||||
using float16 = NAMESPACE_STD::float16_t;
|
||||
#endif
|
||||
|
||||
#if defined(__STDCPP_FLOAT32_T__)
|
||||
#if PLATFORM_HAS_FLOAT32
|
||||
# ifdef __STDCPP_FLOAT32_T__
|
||||
using float32 = NAMESPACE_STD::float32_t;
|
||||
# else
|
||||
using float32 = float;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__STDCPP_FLOAT64_T__)
|
||||
#if PLATFORM_HAS_FLOAT64
|
||||
# ifdef __STDCPP_FLOAT64_T__
|
||||
using float64 = NAMESPACE_STD::float64_t;
|
||||
# else
|
||||
using float64 = double;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__STDCPP_FLOAT128_T__)
|
||||
#if PLATFORM_HAS_FLOAT128
|
||||
using float128 = NAMESPACE_STD::float128_t;
|
||||
#endif
|
||||
|
||||
#if defined(__STDCPP_BFLOAT16_T__)
|
||||
#if PLATFORM_HAS_BFLOAT16
|
||||
using bfloat16 = NAMESPACE_STD::bfloat16_t;
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(float32) == 4);
|
||||
static_assert(sizeof(float64) == 8);
|
||||
|
||||
// Character types
|
||||
|
||||
// The 'char' typically represents the user-preferred locale narrow encoded character set.
|
||||
@@ -210,10 +496,13 @@ static_assert(!PLATFORM_LINUX || sizeof(wchar) == sizeof(u32char), "wchar repr
|
||||
// Pointer types
|
||||
|
||||
using uintptr = NAMESPACE_STD::uintptr_t;
|
||||
using intptr = NAMESPACE_STD::intptr_t;
|
||||
using ptrdiff = NAMESPACE_STD::ptrdiff_t;
|
||||
using size_t = NAMESPACE_STD::size_t;
|
||||
using ssize_t = intptr_t;
|
||||
|
||||
static_assert(sizeof(uintptr) == sizeof(void*), "uintptr must be the same size as a pointer");
|
||||
static_assert(sizeof(ptrdiff) == sizeof(void*), "ptrdiff must be the same size as a pointer");
|
||||
static_assert(sizeof(size_t) == sizeof(void*), "size_t must be the same size as a pointer");
|
||||
static_assert(static_cast<uintptr>(-1) > static_cast<uintptr>(0), "uintptr must be unsigned");
|
||||
|
||||
// Null types
|
||||
|
||||
@@ -240,6 +529,8 @@ NAMESPACE_PRIVATE_END
|
||||
#define U32TEXT(X) U32TEXT_PASTE(X)
|
||||
#define UNICODETEXT(X) U32TEXT_PASTE(X)
|
||||
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "AssertionMacros.h"
|
||||
#include "CoreTypes.h"
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -84,6 +85,10 @@ NORETURN FORCEINLINE void Unreachable()
|
||||
{
|
||||
NAMESPACE_STD::unreachable();
|
||||
}
|
||||
# else
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ NAMESPACE_PRIVATE_END
|
||||
#define VARARGS_ACCESS_COPY(ContextName, ContextSource) NAMESPACE_STD::va_list ContextName; va_copy(ContextName, ContextSource)
|
||||
|
||||
/** Accesses the next variadic function argument. */
|
||||
#define VARARGS_ACCESS(ContextName, Type) (NAMESPACE_PRIVATE::VarArgsAssert<Type>, va_arg(ContextName, Type))
|
||||
#define VARARGS_ACCESS(ContextName, Type) (NAMESPACE_REDCRAFT::NAMESPACE_PRIVATE::VarArgsAssert<Type>, va_arg(ContextName, Type))
|
||||
|
||||
/** Ends traversal of the variadic function arguments. */
|
||||
#define VARARGS_ACCESS_END(ContextName) va_end(ContextName)
|
||||
|
||||
244
Redcraft.Utility/Source/Public/Numerics/Bit.h
Normal file
244
Redcraft.Utility/Source/Public/Numerics/Bit.h
Normal file
@@ -0,0 +1,244 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Numerics/Limits.h"
|
||||
#include "Numerics/Literals.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
#include <bit>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Math)
|
||||
|
||||
/** @return The reinterpreted value of the given value. */
|
||||
template <CTriviallyCopyable T, CTriviallyCopyable U> requires (sizeof(T) == sizeof(U))
|
||||
FORCEINLINE constexpr T BitCast(const U& Value)
|
||||
{
|
||||
return __builtin_bit_cast(T, Value);
|
||||
}
|
||||
|
||||
/** @return The value of reversed byte order of the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T ByteSwap(T Value)
|
||||
{
|
||||
static_assert(sizeof(T) <= 16, "ByteSwap only works with T up to 128 bits");
|
||||
|
||||
if constexpr (sizeof(T) == 1) return Value;
|
||||
|
||||
# if PLATFORM_COMPILER_MSVC
|
||||
{
|
||||
if constexpr (sizeof(T) == 2) return _byteswap_ushort(Value);
|
||||
if constexpr (sizeof(T) == 4) return _byteswap_ulong(Value);
|
||||
if constexpr (sizeof(T) == 8) return _byteswap_uint64(Value);
|
||||
}
|
||||
# elif PLATFORM_COMPILER_CLANG || PLATFORM_COMPILER_GCC
|
||||
{
|
||||
if constexpr (sizeof(T) == 2) return __builtin_bswap16(Value);
|
||||
if constexpr (sizeof(T) == 4) return __builtin_bswap32(Value);
|
||||
if constexpr (sizeof(T) == 8) return __builtin_bswap64(Value);
|
||||
}
|
||||
# else
|
||||
{
|
||||
if constexpr (sizeof(T) == 2) return (Value << 8) | (Value >> 8); // AB -> BA
|
||||
|
||||
if constexpr (sizeof(T) == 4)
|
||||
{
|
||||
T Result = 0;
|
||||
|
||||
Result = ((Result << 8) & 0xFF00FF00u32) | ((Result >> 8) & 0x00FF00FFu32); // ABCD -> BADC
|
||||
|
||||
return (Result << 16) | (Result >> 16);
|
||||
}
|
||||
|
||||
if constexpr (sizeof(T) == 8)
|
||||
{
|
||||
T Result = Value;
|
||||
|
||||
Result = ((Result << 8) & 0xFF00FF00FF00FF00u64) | ((Result >> 8) & 0x00FF00FF00FF00FFu64); // ABCDEFGH -> BADCFEHG
|
||||
Result = ((Result << 16) & 0xFFFF0000FFFF0000u64) | ((Result >> 16) & 0x0000FFFF0000FFFFu64); // BADCFEHG -> DCBAHGFE
|
||||
|
||||
return (Result << 32) | (Result >> 32);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
# if PLATFORM_HAS_INT128
|
||||
{
|
||||
if constexpr (sizeof(T) == 16)
|
||||
{
|
||||
T Result = Value;
|
||||
|
||||
Result = ((Result << 8) & 0xFF00FF00FF00FF00FF00FF00FF00FF00u128) | ((Result >> 8) & 0x00FF00FF00FF00FF00FF00FF00FF00FFu128); // ABCDEFGHIJKLMNOP -> BADCFEHGJILKMONP
|
||||
Result = ((Result << 16) & 0xFFFF0000FFFF0000FFFF0000FFFF0000u128) | ((Result >> 16) & 0x0000FFFF0000FFFF0000FFFF0000FFFFu128); // BADCFEHGJILKMONP -> DCBAHGFEJIKLNOPM
|
||||
Result = ((Result << 32) & 0xFFFFFFFF00000000FFFFFFFF00000000u128) | ((Result >> 32) & 0x00000000FFFFFFFF00000000FFFFFFFFu128); // DCBAHGFEJIKLNOPM -> HGFEDCBAKJILMOPN
|
||||
|
||||
return (Result << 64) | (Result >> 64);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @return true if the given value is power of two, false otherwise. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr bool IsSingleBit(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value;
|
||||
|
||||
else return Value && !(Value & (Value - 1));
|
||||
}
|
||||
|
||||
/** @return The number of all zeros in the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountAllZero(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value ? 0 : 1;
|
||||
|
||||
else return static_cast<uint>(TNumericLimits<T>::Digits - NAMESPACE_STD::popcount(Value));
|
||||
}
|
||||
|
||||
/** @return The number of all ones in the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountAllOne(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value ? 1 : 0;
|
||||
|
||||
else return static_cast<uint>(NAMESPACE_STD::popcount(Value));
|
||||
}
|
||||
|
||||
/** @return The number of leading zeros in the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountLeftZero(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value ? 0 : 1;
|
||||
|
||||
else return static_cast<uint>(NAMESPACE_STD::countl_zero(Value));
|
||||
}
|
||||
|
||||
/** @return The number of leading ones in the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountLeftOne(T Value)
|
||||
{
|
||||
return Math::CountLeftZero<T>(~Value);
|
||||
}
|
||||
|
||||
/** @return The number of trailing zeros in the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountRightZero(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value ? 0 : 1;
|
||||
|
||||
else return static_cast<uint>(NAMESPACE_STD::countr_zero(Value));
|
||||
}
|
||||
|
||||
/** @return The number of trailing ones in the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountRightOne(T Value)
|
||||
{
|
||||
return Math::CountRightZero<T>(~Value);
|
||||
}
|
||||
|
||||
/** @return The smallest number of bits that can represent the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T BitWidth(T Value)
|
||||
{
|
||||
return TNumericLimits<T>::Digits - Math::CountLeftZero(Value);
|
||||
}
|
||||
|
||||
/** @return The smallest integral power of two not less than the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T BitCeil(T Value)
|
||||
{
|
||||
if (Value <= 1u) return static_cast<T>(1);
|
||||
|
||||
return static_cast<T>(1) << Math::BitWidth(static_cast<T>(Value - 1));
|
||||
}
|
||||
|
||||
/** @return The largest integral power of two not greater than the given value. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T BitFloor(T Value)
|
||||
{
|
||||
if (Value == 0u) return static_cast<T>(0);
|
||||
|
||||
return static_cast<T>(1) << (Math::BitWidth(static_cast<T>(Value)) - 1);
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T> FORCEINLINE constexpr T RotateLeft (T Value, int Offset);
|
||||
template <CUnsignedIntegral T> FORCEINLINE constexpr T RotateRight(T Value, int Offset);
|
||||
|
||||
/** @return The value bitwise left-rotation by the given offset. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T RotateLeft(T Value, int Offset)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value;
|
||||
|
||||
else
|
||||
{
|
||||
if (Offset >= 0)
|
||||
{
|
||||
const auto Remainder = Offset % TNumericLimits<T>::Digits;
|
||||
|
||||
return static_cast<T>((Value << Remainder) | (Value >> (TNumericLimits<T>::Digits - Remainder)));
|
||||
}
|
||||
|
||||
return Math::RotateRight(Value, -Offset);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return The value bitwise right-rotation by the given offset. */
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T RotateRight(T Value, int Offset)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value;
|
||||
|
||||
else
|
||||
{
|
||||
if (Offset >= 0)
|
||||
{
|
||||
const auto Remainder = Offset % TNumericLimits<T>::Digits;
|
||||
|
||||
return static_cast<T>((Value >> Remainder) | (Value << (TNumericLimits<T>::Digits - Remainder)));
|
||||
}
|
||||
return Math::RotateLeft(Value, -Offset);
|
||||
}
|
||||
}
|
||||
|
||||
/** The enum indicates the endianness of scalar types. */
|
||||
enum class EEndian
|
||||
{
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
|
||||
|
||||
Little = __ORDER_LITTLE_ENDIAN__,
|
||||
Big = __ORDER_BIG_ENDIAN__,
|
||||
Native = __BYTE_ORDER__,
|
||||
|
||||
#elif PLATFORM_LITTLE_ENDIAN
|
||||
|
||||
Little,
|
||||
Big,
|
||||
Native = Little,
|
||||
|
||||
#elif PLATFORM_BIG_ENDIAN
|
||||
|
||||
Little,
|
||||
Big,
|
||||
Native = Big,
|
||||
|
||||
#else
|
||||
|
||||
Little,
|
||||
Big,
|
||||
Native,
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
NAMESPACE_END(Math)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
92
Redcraft.Utility/Source/Public/Numerics/Limits.h
Normal file
92
Redcraft.Utility/Source/Public/Numerics/Limits.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
static_assert(CHAR_BIT == 8, "CHAR_BIT must be 8");
|
||||
|
||||
enum class EFloatRoundMode : uint8
|
||||
{
|
||||
TowardZero,
|
||||
ToNearest,
|
||||
Upward,
|
||||
Downward,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
enum class EFloatDenormMode : uint8
|
||||
{
|
||||
Absent,
|
||||
Present,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
template <CArithmetic T>
|
||||
struct TNumericLimits;
|
||||
|
||||
template <typename T> struct TNumericLimits<const T> : public TNumericLimits<T> { };
|
||||
template <typename T> struct TNumericLimits< volatile T> : public TNumericLimits<T> { };
|
||||
template <typename T> struct TNumericLimits<const volatile T> : public TNumericLimits<T> { };
|
||||
|
||||
template <CArithmetic T>
|
||||
struct TNumericLimits
|
||||
{
|
||||
static_assert(!CFloatingPoint<T> || NAMESPACE_STD::numeric_limits<T>::has_infinity, "Floating point types must have infinity.");
|
||||
static_assert(!CFloatingPoint<T> || NAMESPACE_STD::numeric_limits<T>::has_quiet_NaN, "Floating point types must have quiet NaN.");
|
||||
static_assert(!CFloatingPoint<T> || NAMESPACE_STD::numeric_limits<T>::has_signaling_NaN, "Floating point types must have signaling NaN.");
|
||||
|
||||
static constexpr bool bIsExact = NAMESPACE_STD::numeric_limits<T>::is_exact;
|
||||
|
||||
static constexpr EFloatRoundMode RoundMode =
|
||||
NAMESPACE_STD::numeric_limits<T>::round_style == NAMESPACE_STD::round_toward_zero ? EFloatRoundMode::TowardZero :
|
||||
NAMESPACE_STD::numeric_limits<T>::round_style == NAMESPACE_STD::round_to_nearest ? EFloatRoundMode::ToNearest :
|
||||
NAMESPACE_STD::numeric_limits<T>::round_style == NAMESPACE_STD::round_toward_infinity ? EFloatRoundMode::Upward :
|
||||
NAMESPACE_STD::numeric_limits<T>::round_style == NAMESPACE_STD::round_toward_neg_infinity ? EFloatRoundMode::Downward :
|
||||
EFloatRoundMode::Unknown;
|
||||
|
||||
static constexpr EFloatDenormMode DenormMode =
|
||||
NAMESPACE_STD::numeric_limits<T>::has_denorm == NAMESPACE_STD::denorm_absent ? EFloatDenormMode::Absent :
|
||||
NAMESPACE_STD::numeric_limits<T>::has_denorm == NAMESPACE_STD::denorm_present ? EFloatDenormMode::Present :
|
||||
EFloatDenormMode::Unknown;
|
||||
|
||||
static constexpr bool bHasDenormLoss = NAMESPACE_STD::numeric_limits<T>::has_denorm_loss;
|
||||
|
||||
static constexpr bool bIsIEEE754 = NAMESPACE_STD::numeric_limits<T>::is_iec559 && sizeof(T) <= 8;
|
||||
|
||||
static constexpr bool bIsModulo = NAMESPACE_STD::numeric_limits<T>::is_modulo;
|
||||
|
||||
static constexpr int Radix = NAMESPACE_STD::numeric_limits<T>::radix;
|
||||
|
||||
static constexpr int Digits = NAMESPACE_STD::numeric_limits<T>::digits;
|
||||
static constexpr int Digits10 = NAMESPACE_STD::numeric_limits<T>::digits10;
|
||||
|
||||
static constexpr int MaxExponent = NAMESPACE_STD::numeric_limits<T>::max_exponent;
|
||||
static constexpr int MaxExponent10 = NAMESPACE_STD::numeric_limits<T>::max_exponent10;
|
||||
static constexpr int MinExponent = NAMESPACE_STD::numeric_limits<T>::min_exponent;
|
||||
static constexpr int MinExponent10 = NAMESPACE_STD::numeric_limits<T>::min_exponent10;
|
||||
|
||||
static constexpr bool bInterrupt = NAMESPACE_STD::numeric_limits<T>::traps;
|
||||
|
||||
static constexpr T Min() { return NAMESPACE_STD::numeric_limits<T>::lowest(); }
|
||||
static constexpr T Max() { return NAMESPACE_STD::numeric_limits<T>::max(); }
|
||||
|
||||
static constexpr T Epsilon() { return NAMESPACE_STD::numeric_limits<T>::epsilon(); }
|
||||
|
||||
static constexpr T Infinity() { return NAMESPACE_STD::numeric_limits<T>::infinity(); }
|
||||
static constexpr T QuietNaN() { return NAMESPACE_STD::numeric_limits<T>::quiet_NaN(); }
|
||||
static constexpr T SignalingNaN() { return NAMESPACE_STD::numeric_limits<T>::signaling_NaN(); }
|
||||
|
||||
static constexpr T MinNormal() { return NAMESPACE_STD::numeric_limits<T>::min(); }
|
||||
static constexpr T MinDenorm() { return NAMESPACE_STD::numeric_limits<T>::denorm_min(); }
|
||||
};
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
133
Redcraft.Utility/Source/Public/Numerics/Literals.h
Normal file
133
Redcraft.Utility/Source/Public/Numerics/Literals.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
#if PLATFORM_COMPILER_MSVC
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4455)
|
||||
#elif PLATFORM_COMPILER_GCC || PLATFORM_COMPILER_CLANG
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wliteral-suffix"
|
||||
#endif
|
||||
|
||||
FORCEINLINE constexpr int8 operator""i8 (unsigned long long int Value) { return static_cast<int8 >(Value); }
|
||||
FORCEINLINE constexpr int16 operator""i16(unsigned long long int Value) { return static_cast<int16>(Value); }
|
||||
FORCEINLINE constexpr int32 operator""i32(unsigned long long int Value) { return static_cast<int32>(Value); }
|
||||
FORCEINLINE constexpr int64 operator""i64(unsigned long long int Value) { return static_cast<int64>(Value); }
|
||||
|
||||
FORCEINLINE constexpr int8 operator""I8 (unsigned long long int Value) { return static_cast<int8 >(Value); }
|
||||
FORCEINLINE constexpr int16 operator""I16(unsigned long long int Value) { return static_cast<int16>(Value); }
|
||||
FORCEINLINE constexpr int32 operator""I32(unsigned long long int Value) { return static_cast<int32>(Value); }
|
||||
FORCEINLINE constexpr int64 operator""I64(unsigned long long int Value) { return static_cast<int64>(Value); }
|
||||
|
||||
FORCEINLINE constexpr uint8 operator""u8 (unsigned long long int Value) { return static_cast<uint8 >(Value); }
|
||||
FORCEINLINE constexpr uint16 operator""u16(unsigned long long int Value) { return static_cast<uint16>(Value); }
|
||||
FORCEINLINE constexpr uint32 operator""u32(unsigned long long int Value) { return static_cast<uint32>(Value); }
|
||||
FORCEINLINE constexpr uint64 operator""u64(unsigned long long int Value) { return static_cast<uint64>(Value); }
|
||||
|
||||
FORCEINLINE constexpr uint8 operator""U8 (unsigned long long int Value) { return static_cast<uint8 >(Value); }
|
||||
FORCEINLINE constexpr uint16 operator""U16(unsigned long long int Value) { return static_cast<uint16>(Value); }
|
||||
FORCEINLINE constexpr uint32 operator""U32(unsigned long long int Value) { return static_cast<uint32>(Value); }
|
||||
FORCEINLINE constexpr uint64 operator""U64(unsigned long long int Value) { return static_cast<uint64>(Value); }
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
|
||||
FORCEINLINE constexpr int128 operator""i128(const char* Str);
|
||||
FORCEINLINE constexpr uint128 operator""u128(const char* Str);
|
||||
|
||||
FORCEINLINE constexpr int128 operator""I128(const char* Str) { return operator""i128(Str); }
|
||||
FORCEINLINE constexpr uint128 operator""U128(const char* Str) { return operator""u128(Str); }
|
||||
|
||||
FORCEINLINE constexpr int128 operator""i128(const char* Str)
|
||||
{
|
||||
return static_cast<int128>(operator""u128(Str));
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr uint128 operator""u128(const char* Str)
|
||||
{
|
||||
constexpr uint8 DigitFromChar[] =
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
uint128 Result = 0;
|
||||
|
||||
uint Base = 10;
|
||||
|
||||
size_t BeginIndex = 0;
|
||||
|
||||
if (Str[0] == '0')
|
||||
{
|
||||
if (Str[1] == 'x' || Str[1] == 'X')
|
||||
{
|
||||
Base = 16;
|
||||
BeginIndex += 2;
|
||||
}
|
||||
else if (Str[1] == 'b' || Str[1] == 'B')
|
||||
{
|
||||
Base = 2;
|
||||
BeginIndex += 2;
|
||||
}
|
||||
else Base = 8;
|
||||
}
|
||||
|
||||
for (size_t I = BeginIndex; Str[I]; ++I)
|
||||
{
|
||||
Result = Result * Base + DigitFromChar[Str[I]];
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
FORCEINLINE constexpr intmax operator""imax(const char* Str) { return operator""i128(Str); }
|
||||
FORCEINLINE constexpr intmax operator""IMAX(const char* Str) { return operator""I128(Str); }
|
||||
FORCEINLINE constexpr uintmax operator""umax(const char* Str) { return operator""u128(Str); }
|
||||
FORCEINLINE constexpr uintmax operator""UMAX(const char* Str) { return operator""U128(Str); }
|
||||
#else
|
||||
FORCEINLINE constexpr intmax operator""imax(unsigned long long int Value) { return operator""i64(Value); }
|
||||
FORCEINLINE constexpr intmax operator""IMAX(unsigned long long int Value) { return operator""I64(Value); }
|
||||
FORCEINLINE constexpr uintmax operator""umax(unsigned long long int Value) { return operator""u64(Value); }
|
||||
FORCEINLINE constexpr uintmax operator""UMAX(unsigned long long int Value) { return operator""U64(Value); }
|
||||
#endif
|
||||
|
||||
#ifndef __STDCPP_FLOAT32_T__
|
||||
FORCEINLINE constexpr float32 operator""f32(long double Value) { return static_cast<float32>(Value); }
|
||||
FORCEINLINE constexpr float32 operator""F32(long double Value) { return static_cast<float32>(Value); }
|
||||
#endif
|
||||
|
||||
#ifndef __STDCPP_FLOAT64_T__
|
||||
FORCEINLINE constexpr float64 operator""f64(long double Value) { return static_cast<float64>(Value); }
|
||||
FORCEINLINE constexpr float64 operator""F64(long double Value) { return static_cast<float64>(Value); }
|
||||
#endif
|
||||
|
||||
#if PLATFORM_COMPILER_MSVC
|
||||
# pragma warning(pop)
|
||||
#elif PLATFORM_COMPILER_GCC || PLATFORM_COMPILER_CLANG
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
1081
Redcraft.Utility/Source/Public/Numerics/Math.h
Normal file
1081
Redcraft.Utility/Source/Public/Numerics/Math.h
Normal file
File diff suppressed because it is too large
Load Diff
40
Redcraft.Utility/Source/Public/Numerics/Numbers.h
Normal file
40
Redcraft.Utility/Source/Public/Numerics/Numbers.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Math)
|
||||
|
||||
template <CFloatingPoint T>
|
||||
struct TNumbers
|
||||
{
|
||||
static constexpr T E = static_cast<T>(2.71828182845904523536028747135266249775724709369995);
|
||||
static constexpr T Log2E = static_cast<T>(1.44269504088896340735992468100189213742664595415299);
|
||||
static constexpr T Log10E = static_cast<T>(0.43429448190325182765112891891660508229439700580367);
|
||||
static constexpr T Pi = static_cast<T>(3.14159265358979323846264338327950288419716939937511);
|
||||
static constexpr T HalfPi = static_cast<T>(1.57079632679489661923132169163975144209858469968756);
|
||||
static constexpr T TwoPi = static_cast<T>(6.28318530717958647692528676655900576839433879875022);
|
||||
static constexpr T SquaredPi = static_cast<T>(9.86960440108935861883449099987615113531369940724079);
|
||||
static constexpr T InvPi = static_cast<T>(0.31830988618379067153776752674502872406891929148091);
|
||||
static constexpr T InvSqrtPi = static_cast<T>(0.56418958354775628694807945156077258584405062932900);
|
||||
static constexpr T Ln2 = static_cast<T>(0.69314718055994530941723212145817656807550013436026);
|
||||
static constexpr T Ln10 = static_cast<T>(2.30258509299404568401799145468436420760110148862877);
|
||||
static constexpr T Sqrt2 = static_cast<T>(1.41421356237309504880168872420969807856967187537694);
|
||||
static constexpr T Sqrt3 = static_cast<T>(1.73205080756887729352744634150587236694280525381038);
|
||||
static constexpr T InvSqrt2 = static_cast<T>(0.70710678118654752440084436210484903928483593768847);
|
||||
static constexpr T InvSqrt3 = static_cast<T>(0.57735026918962576450914878050195745564760175127013);
|
||||
static constexpr T HalfSqrt2 = static_cast<T>(0.70710678118654752440084436210484903928483593768847);
|
||||
static constexpr T HalfSqrt3 = static_cast<T>(0.86602540378443864676372317075293618347140262690519);
|
||||
static constexpr T EGamma = static_cast<T>(0.57721566490153286060651209008240243104215933593992);
|
||||
static constexpr T GoldenRatio = static_cast<T>(1.61803398874989484820458683436563811772030917980576);
|
||||
};
|
||||
|
||||
NAMESPACE_END(Math)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
9
Redcraft.Utility/Source/Public/Numerics/Numerics.h
Normal file
9
Redcraft.Utility/Source/Public/Numerics/Numerics.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Numerics/Literals.h"
|
||||
#include "Numerics/Limits.h"
|
||||
#include "Numerics/Numbers.h"
|
||||
#include "Numerics/Bit.h"
|
||||
#include "Numerics/Math.h"
|
||||
#include "Numerics/Random.h"
|
||||
88
Redcraft.Utility/Source/Public/Numerics/Random.h
Normal file
88
Redcraft.Utility/Source/Public/Numerics/Random.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Numerics/Bit.h"
|
||||
#include "Numerics/Math.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Math)
|
||||
|
||||
/** Seeds the random number generator. Return the previous seed. */
|
||||
NODISCARD REDCRAFTUTILITY_API uint32 Seed(uint32 InSeed = 0);
|
||||
|
||||
/** @return The generated random number within the range of [0, 0x7FFFFFFF). */
|
||||
NODISCARD REDCRAFTUTILITY_API uint32 Rand();
|
||||
|
||||
/** @return The generated random number within the range of [0, A). */
|
||||
template <CIntegral T>
|
||||
NODISCARD FORCEINLINE T Rand(T A)
|
||||
{
|
||||
constexpr uint32 RandStateNum = 0x7FFFFFFF;
|
||||
|
||||
if (A <= 0) return 0;
|
||||
|
||||
if (A <= RandStateNum) return Rand() % A;
|
||||
|
||||
constexpr uint32 BlockSize = Math::BitFloor(RandStateNum);
|
||||
constexpr uint BlockWidth = Math::CountRightZero(BlockSize);
|
||||
|
||||
const T BlockNum = Math::DivAndCeil(A, BlockSize);
|
||||
|
||||
T Result = 0;
|
||||
|
||||
for (T I = 0; I < BlockNum; ++I)
|
||||
{
|
||||
Result ^= Rand();
|
||||
|
||||
Result <<= BlockWidth;
|
||||
}
|
||||
|
||||
return Math::Abs(Result) % A;
|
||||
}
|
||||
|
||||
/** @return The generated random number within the range of [0, A). */
|
||||
template <CFloatingPoint T>
|
||||
NODISCARD FORCEINLINE T Rand(T A)
|
||||
{
|
||||
constexpr uint32 RandStateNum = 0x7FFFFFFF;
|
||||
|
||||
if (Math::IsNegative(A)) return TNumericLimits<T>::QuietNaN();
|
||||
|
||||
constexpr size_t BlockNum = Math::DivAndCeil(sizeof(T), 4);
|
||||
|
||||
T Multiplier = A;
|
||||
|
||||
Multiplier /= BlockNum;
|
||||
Multiplier /= RandStateNum;
|
||||
|
||||
T Result = 0;
|
||||
|
||||
for (size_t I = 0; I < BlockNum; ++I)
|
||||
{
|
||||
Result += Rand() * Multiplier;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/** @return The generated random number within the range of [A, B). */
|
||||
template <CArithmetic T, CArithmetic U> requires (CCommonType<T, U>)
|
||||
NODISCARD FORCEINLINE auto RandWithin(T A, U B)
|
||||
{
|
||||
using FCommonT = TCommonType<T, U>;
|
||||
|
||||
if (A == B) return static_cast<FCommonT>(A);
|
||||
|
||||
if (A > B) return Math::RandWithin(B, A);
|
||||
|
||||
return static_cast<FCommonT>(A + Math::Rand(B - A));
|
||||
}
|
||||
|
||||
NAMESPACE_END(Math)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
183
Redcraft.Utility/Source/Public/Ranges/AllView.h
Normal file
183
Redcraft.Utility/Source/Public/Ranges/AllView.h
Normal file
@@ -0,0 +1,183 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/Pipe.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/**
|
||||
* A view adapter that references other range.
|
||||
* No matter which it is base range, the reference view always satisfies the same range concept.
|
||||
*/
|
||||
template <CRange R> requires (CObject<R>)
|
||||
class TRefView : public IBasicViewInterface<TRefView<R>>
|
||||
{
|
||||
private:
|
||||
|
||||
// Use the function to check constructability.
|
||||
static void Func(R&);
|
||||
static void Func(R&&) = delete;
|
||||
|
||||
public:
|
||||
|
||||
template <typename T> requires (!CSameAs<TRemoveCVRef<T>, TRefView> && CConvertibleTo<T, R&> && requires { Func(DeclVal<T>()); })
|
||||
FORCEINLINE constexpr TRefView(T&& InRange) : Ptr(AddressOf(static_cast<R&>(Forward<T>(InRange)))) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TRangeIterator<R> Begin() const { return Ranges::Begin(*Ptr); }
|
||||
NODISCARD FORCEINLINE constexpr TRangeSentinel<R> End() const { return Ranges::End (*Ptr); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto GetData() const requires (CContiguousRange<R>) { return Ranges::GetData(*Ptr); }
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedRange<R>) { return Ranges::Num (*Ptr); }
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const requires (requires(R Range) { Ranges::IsEmpty(Range); }) { return Ranges::IsEmpty(*Ptr); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr R& GetBase() const { return *Ptr; }
|
||||
|
||||
private:
|
||||
|
||||
R* Ptr;
|
||||
|
||||
};
|
||||
|
||||
template <typename R>
|
||||
TRefView(R&) -> TRefView<R>;
|
||||
|
||||
static_assert( CInputRange<TRefView<IRange< IInputIterator<int&>>>>);
|
||||
static_assert( CForwardRange<TRefView<IRange< IForwardIterator<int&>>>>);
|
||||
static_assert(CBidirectionalRange<TRefView<IRange<IBidirectionalIterator<int&>>>>);
|
||||
static_assert( CRandomAccessRange<TRefView<IRange< IRandomAccessIterator<int&>>>>);
|
||||
static_assert( CContiguousRange<TRefView<IRange< IContiguousIterator<int&>>>>);
|
||||
|
||||
static_assert(CCommonRange<TRefView<ICommonRange< IForwardIterator<int>>>>);
|
||||
static_assert( CSizedRange<TRefView< ISizedRange<IInputOrOutputIterator<int>>>>);
|
||||
static_assert( CView<TRefView< IRange<IInputOrOutputIterator<int>>>>);
|
||||
|
||||
static_assert(COutputRange<TRefView<IRange<IOutputIterator<int&>>>, int>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename T>
|
||||
constexpr bool bEnableBorrowedRange<Ranges::TRefView<T>> = true;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/**
|
||||
* A view adapter that has unique ownership of a range.
|
||||
* No matter which it is base range, the reference view always satisfies the same range concept.
|
||||
* Specify, the base range type must be movable, and the owning view always is movable but not copyable.
|
||||
*/
|
||||
template <CRange R> requires (CMovable<R> && !NAMESPACE_PRIVATE::TIsInitializerList<R>::Value)
|
||||
class TOwningView : public IBasicViewInterface<TOwningView<R>>
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr TOwningView() requires (CDefaultConstructible<R>) = default;
|
||||
|
||||
FORCEINLINE constexpr TOwningView(const TOwningView&) = delete;
|
||||
FORCEINLINE constexpr TOwningView(TOwningView&&) = default;
|
||||
|
||||
FORCEINLINE constexpr TOwningView(R&& InRange) : Base(MoveTemp(InRange)) { }
|
||||
|
||||
FORCEINLINE constexpr TOwningView& operator=(const TOwningView&) = delete;
|
||||
FORCEINLINE constexpr TOwningView& operator=(TOwningView&&) = default;
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() { return Ranges::Begin(Base); }
|
||||
NODISCARD FORCEINLINE constexpr auto End() { return Ranges::End (Base); }
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() const requires (CRange<const R>) { return Ranges::Begin(Base); }
|
||||
NODISCARD FORCEINLINE constexpr auto End() const requires (CRange<const R>) { return Ranges::End (Base); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto GetData() requires (CContiguousRange< R>) { return Ranges::GetData(Base); }
|
||||
NODISCARD FORCEINLINE constexpr auto GetData() const requires (CContiguousRange<const R>) { return Ranges::GetData(Base); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() requires (CSizedRange< R>) { return Ranges::Num(Base); }
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedRange<const R>) { return Ranges::Num(Base); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() requires (requires( R Base) { Ranges::IsEmpty(Base); }) { return Ranges::IsEmpty(Base); }
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const requires (requires(const R Base) { Ranges::IsEmpty(Base); }) { return Ranges::IsEmpty(Base); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr R& GetBase() & { return Base; }
|
||||
NODISCARD FORCEINLINE constexpr R&& GetBase() && { return MoveTemp(Base); }
|
||||
NODISCARD FORCEINLINE constexpr const R& GetBase() const& { return AsConst(Base); }
|
||||
NODISCARD FORCEINLINE constexpr const R&& GetBase() const&& { return MoveTemp(AsConst(Base)); }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS R Base;
|
||||
|
||||
};
|
||||
|
||||
static_assert( CInputRange<TOwningView<IRange< IInputIterator<int&>>>>);
|
||||
static_assert( CForwardRange<TOwningView<IRange< IForwardIterator<int&>>>>);
|
||||
static_assert(CBidirectionalRange<TOwningView<IRange<IBidirectionalIterator<int&>>>>);
|
||||
static_assert( CRandomAccessRange<TOwningView<IRange< IRandomAccessIterator<int&>>>>);
|
||||
static_assert( CContiguousRange<TOwningView<IRange< IContiguousIterator<int&>>>>);
|
||||
|
||||
static_assert(CCommonRange<TOwningView<ICommonRange< IForwardIterator<int>>>>);
|
||||
static_assert( CSizedRange<TOwningView< ISizedRange<IInputOrOutputIterator<int>>>>);
|
||||
static_assert( CView<TOwningView< IRange<IInputOrOutputIterator<int>>>>);
|
||||
|
||||
static_assert(COutputRange<TOwningView<IRange<IOutputIterator<int&>>>, int>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename T>
|
||||
constexpr bool bEnableBorrowedRange<Ranges::TOwningView<T>> = bEnableBorrowedRange<T>;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** Creates A view adapter that includes all elements of a range. */
|
||||
template <CViewableRange R>
|
||||
NODISCARD FORCEINLINE constexpr auto All(R&& InRange)
|
||||
{
|
||||
if constexpr (CView<TDecay<R>>)
|
||||
{
|
||||
return TDecay<R>(Forward<R>(InRange));
|
||||
}
|
||||
|
||||
else if constexpr (requires { TRefView(Forward<R>(InRange)); })
|
||||
{
|
||||
return TRefView(Forward<R>(InRange));
|
||||
}
|
||||
|
||||
else return TOwningView(Forward<R>(InRange));
|
||||
}
|
||||
|
||||
/** Creates A view adapter that includes all elements of a range. */
|
||||
NODISCARD FORCEINLINE constexpr auto All()
|
||||
{
|
||||
using FClosure = decltype([]<CViewableRange R> requires (requires { Ranges::All(DeclVal<R>()); }) (R&& Base)
|
||||
{
|
||||
return Ranges::All(Forward<R>(Base));
|
||||
});
|
||||
|
||||
return TAdaptorClosure<FClosure>();
|
||||
}
|
||||
|
||||
/** A view adapter that includes all elements of a range. */
|
||||
template <CViewableRange R>
|
||||
using TAllView = decltype(Ranges::All(DeclVal<R>()));
|
||||
|
||||
static_assert( CInputRange<TAllView<IRange< IInputIterator<int&>>>>);
|
||||
static_assert( CForwardRange<TAllView<IRange< IForwardIterator<int&>>>>);
|
||||
static_assert(CBidirectionalRange<TAllView<IRange<IBidirectionalIterator<int&>>>>);
|
||||
static_assert( CRandomAccessRange<TAllView<IRange< IRandomAccessIterator<int&>>>>);
|
||||
static_assert( CContiguousRange<TAllView<IRange< IContiguousIterator<int&>>>>);
|
||||
|
||||
static_assert(CCommonRange<TAllView<ICommonRange< IForwardIterator<int>>>>);
|
||||
static_assert( CSizedRange<TAllView< ISizedRange<IInputOrOutputIterator<int>>>>);
|
||||
static_assert( CView<TAllView< IRange<IInputOrOutputIterator<int>>>>);
|
||||
|
||||
static_assert(COutputRange<TAllView<IRange<IOutputIterator<int&>>>, int>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
170
Redcraft.Utility/Source/Public/Ranges/Conversion.h
Normal file
170
Redcraft.Utility/Source/Public/Ranges/Conversion.h
Normal file
@@ -0,0 +1,170 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/AllView.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Ranges/TransformView.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
// NOTE: In the STL, use std::from_range_t as a disambiguation tag that resolves ambiguity
|
||||
// introduced by the list-initialization. For example, for the following code:
|
||||
//
|
||||
// R RangeOfInts = /* ... */;
|
||||
// static_assert(CRange<R> and CSameAs<TRangeElement<R>, int>);
|
||||
//
|
||||
// TArray Arr(RangeOfInts);
|
||||
// TArray Brr{RangeOfInts};
|
||||
//
|
||||
// If R is TArray<int> than decltype(Arr) is TArray<int> and decltype(Brr) is TArray<int>,
|
||||
// otherwise, decltype(Arr) is TArray<int> and decltype(Brr) is TArray<R>.
|
||||
//
|
||||
// But Redcraft can't use the std::from_range_t tag because list-initialization is discouraged.
|
||||
|
||||
/** A concept specifies a container that can reserve size. */
|
||||
template <typename C>
|
||||
concept CReservableContainer = CSizedRange<C>
|
||||
&& requires (C& Container, size_t N)
|
||||
{
|
||||
Container.Reserve(N);
|
||||
{ Container.Num() } -> CSameAs<size_t>;
|
||||
{ Container.Max() } -> CSameAs<size_t>;
|
||||
};
|
||||
|
||||
/** A concept specifies a container that can append elements. */
|
||||
template <typename C, typename T>
|
||||
concept CAppendableContainer =
|
||||
requires (C& Container, T&& Object)
|
||||
{
|
||||
requires
|
||||
(
|
||||
requires { Container.EmplaceBack (Forward<T>(Object)); } ||
|
||||
requires { Container.PushBack (Forward<T>(Object)); } ||
|
||||
requires { Container.Emplace(Container.End(), Forward<T>(Object)); } ||
|
||||
requires { Container.Insert (Container.End(), Forward<T>(Object)); }
|
||||
);
|
||||
};
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
template <typename T, CAppendableContainer<T> C>
|
||||
FORCEINLINE constexpr void AppendTo(C& Container, T&& Object)
|
||||
{
|
||||
if constexpr (requires { Container.EmplaceBack(Forward<T>(Object)); })
|
||||
{
|
||||
Container.EmplaceBack(Forward<T>(Object));
|
||||
}
|
||||
|
||||
else if constexpr (requires { Container.PushBack(Forward<T>(Object)); })
|
||||
{
|
||||
Container.PushBack(Forward<T>(Object));
|
||||
}
|
||||
|
||||
else if constexpr (requires { Container.Emplace(Container.End(), Forward<T>(Object)); })
|
||||
{
|
||||
Container.Emplace(Container.End(), Forward<T>(Object));
|
||||
}
|
||||
|
||||
else /* if constexpr (requires { Container.Insert(Container.End(), Forward<T>(Object)); }) */
|
||||
{
|
||||
Container.Insert(Container.End(), Forward<T>(Object));
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs a non-view object from the elements of the range. */
|
||||
template <typename C, CInputRange R, typename... Ts> requires (!CView<C>)
|
||||
NODISCARD FORCEINLINE constexpr auto To(R&& Range, Ts&&... Args)
|
||||
{
|
||||
if constexpr (!CInputRange<C> || CConvertibleTo<TRangeReference<R>, TRangeElement<C>>)
|
||||
{
|
||||
if constexpr (CConstructibleFrom<C, R, Ts...>)
|
||||
{
|
||||
return C(Forward<R>(Range), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
else if constexpr (CCommonRange<R> && CInputRange<R> && CConstructibleFrom<C, TRangeIterator<R>, TRangeSentinel<R>, Ts...>)
|
||||
{
|
||||
return C(Ranges::Begin(Range), Ranges::End(Range), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
else if constexpr (CConstructibleFrom<C, Ts...> && CAppendableContainer<C, TRangeReference<R>>)
|
||||
{
|
||||
C Result(Forward<Ts>(Args)...);
|
||||
|
||||
if constexpr (CSizedRange<R> && CReservableContainer<C>)
|
||||
{
|
||||
Result.Reserve(Ranges::Num(Range));
|
||||
}
|
||||
|
||||
for (TRangeReference<R> Element : Range)
|
||||
{
|
||||
Ranges::AppendTo(Result, Forward<TRangeReference<R>>(Element));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
else static_assert(sizeof(R) == -1, "The container type is not constructible from a range");
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (CInputRange<TRangeReference<C>>)
|
||||
{
|
||||
return Ranges::To<C>(Ranges::All(Range) | Ranges::Transform([]<typename T>(T&& Element) { return Ranges::To<TRangeElement<C>>(Forward<T>(Element)); }), Forward<Args>(Args)...);
|
||||
}
|
||||
|
||||
else static_assert(sizeof(R) == -1, "The container type is not constructible from a range");
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs a non-view object from the elements of the range. */
|
||||
template <template <typename...> typename C, CInputRange R, typename... Ts>
|
||||
NODISCARD FORCEINLINE constexpr auto To(R&& Range, Ts&&... Args)
|
||||
{
|
||||
if constexpr (requires { C(DeclVal<R>(), DeclVal<Ts>()...); })
|
||||
{
|
||||
return Ranges::To<decltype(C(DeclVal<R>(), DeclVal<Ts>()...))>(Forward<R>(Range), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
else if constexpr (requires { C(DeclVal<TRangeIterator<R>>(), DeclVal<TRangeSentinel<R>>(), DeclVal<Args>()...); })
|
||||
{
|
||||
return Ranges::To<decltype(C(DeclVal<TRangeIterator<R>>(), DeclVal<TRangeSentinel<R>>(), DeclVal<Args>()...))>(Forward<R>(Range), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
else static_assert(sizeof(R) == -1, "The container type is not constructible from a range");
|
||||
}
|
||||
|
||||
/** Constructs a non-view object from the elements of the range. */
|
||||
template <typename C, typename... Ts> requires (!CView<C>)
|
||||
NODISCARD FORCEINLINE constexpr auto To(Ts&&... Args)
|
||||
{
|
||||
using FClosure = decltype([]<CInputRange R, typename... Us> requires (requires { Ranges::To<C>(DeclVal<R>(), DeclVal<Us>()...); }) (R&& Range, Us&&... Args)
|
||||
{
|
||||
return Ranges::To<C>(Forward<R>(Range), Forward<Us>(Args)...);
|
||||
});
|
||||
|
||||
return TAdaptorClosure<FClosure, TDecay<Ts>...>(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Constructs a non-view object from the elements of the range. */
|
||||
template <template <typename...> typename C, typename... Ts>
|
||||
NODISCARD FORCEINLINE constexpr auto To(Ts&&... Args)
|
||||
{
|
||||
using FClosure = decltype([]<CInputRange R, typename... Us> requires (requires { Ranges::To<C>(DeclVal<R>(), DeclVal<Us>()...); }) (R&& Range, Us&&... Args)
|
||||
{
|
||||
return Ranges::To<C>(Forward<R>(Range), Forward<Us>(Args)...);
|
||||
});
|
||||
|
||||
return TAdaptorClosure<FClosure, TDecay<Ts>...>(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
357
Redcraft.Utility/Source/Public/Ranges/Factory.h
Normal file
357
Redcraft.Utility/Source/Public/Ranges/Factory.h
Normal file
@@ -0,0 +1,357 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** A view type that produces a view of no elements of a particular type. */
|
||||
template <CObject T>
|
||||
class TEmptyView : public IBasicViewInterface<TEmptyView<T>>
|
||||
{
|
||||
public:
|
||||
|
||||
using FElementType = T;
|
||||
using FReference = T&;
|
||||
using FIterator = T*;
|
||||
using FSentinel = T*;
|
||||
|
||||
FORCEINLINE constexpr TEmptyView() = default;
|
||||
|
||||
NODISCARD static FORCEINLINE constexpr FIterator Begin() { return nullptr; }
|
||||
NODISCARD static FORCEINLINE constexpr FSentinel End() { return nullptr; }
|
||||
NODISCARD static FORCEINLINE constexpr T* GetData() { return nullptr; }
|
||||
NODISCARD static FORCEINLINE constexpr size_t Num() { return 0; }
|
||||
NODISCARD static FORCEINLINE constexpr bool IsEmpty() { return true; }
|
||||
|
||||
};
|
||||
|
||||
static_assert(CContiguousRange<TEmptyView<int>>);
|
||||
static_assert( CCommonRange<TEmptyView<int>>);
|
||||
static_assert( CSizedRange<TEmptyView<int>>);
|
||||
static_assert( CView<TEmptyView<int>>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename T>
|
||||
constexpr bool bEnableBorrowedRange<Ranges::TEmptyView<T>> = true;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** A view type that contains exactly one element of a specified value. */
|
||||
template <CObject T> requires (CMoveConstructible<T>)
|
||||
class TSingleView : public IBasicViewInterface<TSingleView<T>>
|
||||
{
|
||||
public:
|
||||
|
||||
using FElementType = T;
|
||||
|
||||
using FReference = T&;
|
||||
using FConstReference = const T&;
|
||||
|
||||
using FIterator = T*;
|
||||
using FConstIterator = const T*;
|
||||
|
||||
using FSentinel = T*;
|
||||
using FConstSentinel = const T*;
|
||||
|
||||
FORCEINLINE constexpr TSingleView() requires (CDefaultConstructible<T>) = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TSingleView(const T& InValue) requires (CCopyConstructible<T>) : Value(InValue) { }
|
||||
|
||||
FORCEINLINE constexpr explicit TSingleView(T&& InValue) : Value(MoveTemp(InValue)) { }
|
||||
|
||||
template <typename... Ts> requires (CConstructibleFrom<T, Ts...>)
|
||||
FORCEINLINE constexpr explicit TSingleView(FInPlace, Ts&&... Args) : Value(Forward<Ts>(Args)...) { }
|
||||
|
||||
FORCEINLINE constexpr FIterator Begin() { return GetData(); }
|
||||
FORCEINLINE constexpr FConstIterator Begin() const { return GetData(); }
|
||||
FORCEINLINE constexpr FSentinel End() { return GetData() + 1; }
|
||||
FORCEINLINE constexpr FConstSentinel End() const { return GetData() + 1; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr T* GetData() { return AddressOf(Value); }
|
||||
NODISCARD FORCEINLINE constexpr const T* GetData() const { return AddressOf(Value); }
|
||||
|
||||
NODISCARD static FORCEINLINE constexpr size_t Num() { return 1; }
|
||||
NODISCARD static FORCEINLINE constexpr bool IsEmpty() { return false; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS T Value;
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
TSingleView(T) -> TSingleView<T>;
|
||||
|
||||
static_assert(CContiguousRange<TSingleView<int>>);
|
||||
static_assert( CCommonRange<TSingleView<int>>);
|
||||
static_assert( CSizedRange<TSingleView<int>>);
|
||||
static_assert( CView<TSingleView<int>>);
|
||||
|
||||
/** A view type that generates a sequence of elements by repeatedly incrementing an initial value. Can be either bounded or unbounded. */
|
||||
template <CWeaklyIncrementable W, CWeaklyEqualityComparable<W> S = FUnreachableSentinel> requires (CSemiregular<S> && CCopyable<W>)
|
||||
class TIotaView : public IBasicViewInterface<TIotaView<W, S>>
|
||||
{
|
||||
private:
|
||||
|
||||
class FIteratorImpl;
|
||||
class FSentinelImpl;
|
||||
|
||||
public:
|
||||
|
||||
using FElementType = TRemoveCV<W>;
|
||||
|
||||
using FReference = W;
|
||||
|
||||
using FIterator = FIteratorImpl;
|
||||
|
||||
using FSentinel = TConditional<CSameAs<W, S>, FIteratorImpl, FSentinelImpl>;
|
||||
|
||||
FORCEINLINE constexpr TIotaView() requires (CDefaultConstructible<W>) = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TIotaView(W InValue) requires (CDefaultConstructible<S>) : First(InValue), Last() { }
|
||||
|
||||
FORCEINLINE constexpr explicit TIotaView(TIdentity<W> InValue, TIdentity<S> InLast) : First(InValue), Last(InLast) { }
|
||||
|
||||
FORCEINLINE constexpr explicit TIotaView(FIterator InFirst, FSentinel InLast) : First(InFirst.Value), Last(InLast.Value) { }
|
||||
|
||||
FORCEINLINE constexpr explicit TIotaView(FIterator InFirst, FUnreachableSentinel) requires (CSameAs<S, FUnreachableSentinel>) : First(InFirst.Value) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FIterator Begin() const { return FIterator(First); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FSentinel End() const { return FSentinel(Last); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires ((CIntegral<W> && CIntegral<S>) || CSizedSentinelFor<S, W>) { return Last - First; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return First == Last; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS W First;
|
||||
NO_UNIQUE_ADDRESS S Last;
|
||||
|
||||
class FIteratorImpl final
|
||||
{
|
||||
public:
|
||||
|
||||
using FElementType = TRemoveCV<W>;
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl() requires (CDefaultConstructible<W>) = default;
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const FIteratorImpl& LHS, const FIteratorImpl& RHS) requires (CEqualityComparable<W>) { return LHS.Value == RHS.Value; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FReference operator*() const { return Value; }
|
||||
NODISCARD FORCEINLINE constexpr const W* operator->() const { return AddressOf(Value); }
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl& operator++() { ++Value; return *this; }
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl operator++(int) { FIteratorImpl Temp = *this; ++*this; return Temp; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS W Value;
|
||||
|
||||
constexpr explicit FIteratorImpl(W InValue) : Value(InValue) { }
|
||||
|
||||
friend FSentinelImpl;
|
||||
friend TIotaView;
|
||||
};
|
||||
|
||||
class FSentinelImpl final
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl() = default;
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const FIteratorImpl& InValue) const& { return Value == InValue.Value; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS S Value;
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl(S InValue) : Value(InValue) { }
|
||||
|
||||
friend TIotaView;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
TIotaView(T, U) -> TIotaView<T, U>;
|
||||
|
||||
static_assert(CForwardRange<TIotaView<int>>);
|
||||
static_assert( CView<TIotaView<int>>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr bool bEnableBorrowedRange<Ranges::TIotaView<T, U>> = true;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** A view type that generates a sequence of elements by repeatedly producing the same value. Can be either bounded or unbounded. */
|
||||
template <CObject W, bool bIsUnreachable = true> requires (CMoveConstructible<W> && CSameAs<W, TRemoveCV<W>>)
|
||||
class TRepeatView : public IBasicViewInterface<TRepeatView<W, bIsUnreachable>>
|
||||
{
|
||||
private:
|
||||
|
||||
class FIteratorImpl;
|
||||
|
||||
public:
|
||||
|
||||
using FElementType = W;
|
||||
|
||||
using FReference = const W&;
|
||||
|
||||
using FIterator = FIteratorImpl;
|
||||
|
||||
using FSentinel = TConditional<bIsUnreachable, FUnreachableSentinel, FIterator>;
|
||||
|
||||
FORCEINLINE constexpr TRepeatView() requires (CDefaultConstructible<W>) = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TRepeatView(const W& InValue) requires (bIsUnreachable && CCopyConstructible<W>) : Value(InValue) { }
|
||||
|
||||
FORCEINLINE constexpr explicit TRepeatView(W&& InValue) requires (bIsUnreachable) : Value(MoveTemp(InValue)) { }
|
||||
|
||||
FORCEINLINE constexpr explicit TRepeatView(const W& InValue, size_t InCount) requires (!bIsUnreachable && CCopyConstructible<W>) : Value(MoveTemp(InValue)), Count(InCount) { }
|
||||
|
||||
FORCEINLINE constexpr explicit TRepeatView(W&& InValue, size_t InCount) requires (!bIsUnreachable) : Value(MoveTemp(InValue)), Count(InCount) { }
|
||||
|
||||
template <typename... Ts> requires (CConstructibleFrom<W, Ts...>)
|
||||
FORCEINLINE constexpr explicit TRepeatView(FInPlace, Ts&&... Args, size_t InCount) : Value(Forward<Ts>(Args)...), Count(InCount) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FIterator Begin() const { return FIterator(Value, 0); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FSentinel End() const
|
||||
{
|
||||
if constexpr (bIsUnreachable)
|
||||
{
|
||||
return UnreachableSentinel;
|
||||
}
|
||||
|
||||
else return FSentinel(Value, Count);
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires (!bIsUnreachable) { return Count; }
|
||||
|
||||
private:
|
||||
|
||||
using FSizeType = TConditional<bIsUnreachable, FUnreachableSentinel, size_t>;
|
||||
|
||||
NO_UNIQUE_ADDRESS W Value;
|
||||
|
||||
NO_UNIQUE_ADDRESS FSizeType Count;
|
||||
|
||||
class FIteratorImpl final
|
||||
{
|
||||
public:
|
||||
|
||||
using FElementType = W;
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl() requires (CDefaultConstructible<W>) = default;
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const FIteratorImpl& LHS, const FIteratorImpl& RHS) { return LHS.Current == RHS.Current; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const FIteratorImpl& LHS, const FIteratorImpl& RHS) { return LHS.Current <=> RHS.Current; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FReference operator*() const { return *Ptr; }
|
||||
NODISCARD FORCEINLINE constexpr const W* operator->() const { return Ptr; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FReference operator[](ptrdiff) const { return *Ptr; }
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl& operator++() { ++Current; return *this; }
|
||||
FORCEINLINE constexpr FIteratorImpl& operator--() { --Current; return *this; }
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl operator++(int) { FIteratorImpl Temp = *this; ++*this; return Temp; }
|
||||
FORCEINLINE constexpr FIteratorImpl operator--(int) { FIteratorImpl Temp = *this; --*this; return Temp; }
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl& operator+=(ptrdiff Offset) { Current += Offset; return *this; }
|
||||
FORCEINLINE constexpr FIteratorImpl& operator-=(ptrdiff Offset) { Current -= Offset; return *this; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr FIteratorImpl operator+(FIteratorImpl Iter, ptrdiff Offset) { FIteratorImpl Temp = Iter; Temp += Offset; return Temp; }
|
||||
NODISCARD friend FORCEINLINE constexpr FIteratorImpl operator+(ptrdiff Offset, FIteratorImpl Iter) { FIteratorImpl Temp = Iter; Temp += Offset; return Temp; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FIteratorImpl operator-(ptrdiff Offset) const { FIteratorImpl Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const FIteratorImpl& LHS, const FIteratorImpl& RHS) { return LHS.Current - RHS.Current; }
|
||||
|
||||
private:
|
||||
|
||||
const W* Ptr;
|
||||
|
||||
NO_UNIQUE_ADDRESS size_t Current;
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl(const W& InObject, size_t InCurrent) : Ptr(AddressOf(InObject)), Current(InCurrent) { }
|
||||
|
||||
friend TRepeatView;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template <typename W>
|
||||
TRepeatView(W) -> TRepeatView<W>;
|
||||
|
||||
template <typename W>
|
||||
TRepeatView(W, size_t) -> TRepeatView<W, false>;
|
||||
|
||||
static_assert(CRandomAccessRange<TRepeatView<int, false>>);
|
||||
static_assert( CCommonRange<TRepeatView<int, false>>);
|
||||
static_assert( CSizedRange<TRepeatView<int, false>>);
|
||||
static_assert( CView<TRepeatView<int, false>>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** A view of no elements of a particular type. */
|
||||
template <CObject T>
|
||||
inline constexpr TEmptyView<T> Empty;
|
||||
|
||||
/** Creates a view that contains exactly one element of a specified value. */
|
||||
template <typename T> requires (CObject<TDecay<T>> && CMoveConstructible<TDecay<T>>)
|
||||
NODISCARD FORCEINLINE constexpr TSingleView<TDecay<T>> Single(T&& Value)
|
||||
{
|
||||
return TSingleView<TDecay<T>>(Forward<T>(Value));
|
||||
}
|
||||
|
||||
/** Creates a view that generates a sequence of elements by repeatedly incrementing an initial value. */
|
||||
template <typename W> requires (CWeaklyIncrementable<TDecay<W>> && CCopyable<TDecay<W>>)
|
||||
NODISCARD FORCEINLINE constexpr TIotaView<TDecay<W>> Iota(W&& Value)
|
||||
{
|
||||
return TIotaView<TDecay<W>>(Forward<W>(Value));
|
||||
}
|
||||
|
||||
/** Creates a view that generates a sequence of elements by repeatedly incrementing an initial value. */
|
||||
template <typename W, typename S> requires (CWeaklyIncrementable<TDecay<W>> && CWeaklyEqualityComparable<W, S> && CCopyable<TDecay<W>> && CSemiregular<TDecay<S>>)
|
||||
NODISCARD FORCEINLINE constexpr TIotaView<TDecay<W>, TDecay<S>> Iota(W&& Value, S&& Last)
|
||||
{
|
||||
return TIotaView<TDecay<W>, TDecay<S>>(Forward<W>(Value), Forward<S>(Last));
|
||||
}
|
||||
|
||||
/** Creates a view that generates a sequence of elements by repeatedly producing the same value. */
|
||||
template <typename W> requires (CObject<TDecay<W>> && CMoveConstructible<TDecay<W>>)
|
||||
NODISCARD FORCEINLINE constexpr TRepeatView<TDecay<W>> Repeat(W&& Value)
|
||||
{
|
||||
return TRepeatView<TDecay<W>>(Forward<W>(Value));
|
||||
}
|
||||
|
||||
/** Creates a view that generates a sequence of elements by repeatedly producing the same value. */
|
||||
template <typename W> requires (CObject<TDecay<W>> && CMoveConstructible<TDecay<W>>)
|
||||
NODISCARD FORCEINLINE constexpr TRepeatView<TDecay<W>, false> Repeat(W&& Value, size_t Count)
|
||||
{
|
||||
return TRepeatView<TDecay<W>, false>(Forward<W>(Value), Count);
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
191
Redcraft.Utility/Source/Public/Ranges/FilterView.h
Normal file
191
Redcraft.Utility/Source/Public/Ranges/FilterView.h
Normal file
@@ -0,0 +1,191 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/Invoke.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/Pipe.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/AllView.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/**
|
||||
* A view adapter that consists of the elements of a range that satisfies a predicate.
|
||||
* When based on an input view, the filter view satisfies at least an input view up to a bidirectional view.
|
||||
* When based on a common view, the filter view satisfies a common view.
|
||||
*/
|
||||
template <CInputRange V, CPredicate<TRangeReference<V>> Pred> requires (CView<V> && CObject<Pred> && CMoveConstructible<Pred>)
|
||||
class TFilterView : public IBasicViewInterface<TFilterView<V, Pred>>
|
||||
{
|
||||
private:
|
||||
|
||||
class FIteratorImpl;
|
||||
class FSentinelImpl;
|
||||
|
||||
public:
|
||||
|
||||
using FElementType = TRangeElement<V>;
|
||||
using FReference = TRangeReference<V>;
|
||||
|
||||
using FIterator = FIteratorImpl;
|
||||
|
||||
using FSentinel = TConditional<CCommonRange<V>, FIteratorImpl, FSentinelImpl>;
|
||||
|
||||
FORCEINLINE constexpr TFilterView() requires (CDefaultConstructible<V> && CDefaultConstructible<Pred>) = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TFilterView(V InBase, Pred InPredicate) : Base(MoveTemp(InBase)), Predicate(MoveTemp(InPredicate)) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FIterator Begin()
|
||||
{
|
||||
FIterator Iter(*this, Ranges::Begin(Base));
|
||||
|
||||
do
|
||||
{
|
||||
if (Iter == End()) break;
|
||||
|
||||
if (InvokeResult<bool>(GetPredicate(), *Iter)) break;
|
||||
|
||||
++Iter;
|
||||
}
|
||||
while (false);
|
||||
|
||||
if constexpr (!CForwardRange<V>) return MoveTemp(Iter);
|
||||
|
||||
return Iter;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FSentinel End() { return FSentinel(*this, Ranges::End(Base)); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() const& requires (CCopyConstructible<V>) { return Base; }
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() && { return MoveTemp(Base); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const Pred& GetPredicate() const { return Predicate; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS V Base;
|
||||
|
||||
NO_UNIQUE_ADDRESS Pred Predicate;
|
||||
|
||||
class FIteratorImpl final
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
using FElementType = TIteratorElement<TRangeIterator<V>>;
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl() requires (CDefaultConstructible<TRangeIterator<V>>) { } // Use '{ }' instead of '= default;' to avoid MSVC bug.
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const FIteratorImpl& LHS, const FIteratorImpl& RHS)
|
||||
{
|
||||
return LHS.GetBase() == RHS.GetBase();
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TRangeReference<V> operator*() const { return *GetBase(); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto operator->() const requires (requires(const TRangeIterator<V> Iter) { { ToAddress(Iter) } -> CSameAs<TIteratorPointer<TRangeIterator<V>>>; }) { return ToAddress(GetBase()); }
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl& operator++()
|
||||
{
|
||||
do ++Current; while (*this != Owner->End() && !InvokeResult<bool>(Owner->GetPredicate(), *Current));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl& operator--() requires (CBidirectionalIterator<TRangeIterator<V>>)
|
||||
{
|
||||
do --Current; while (!InvokeResult<bool>(Owner->GetPredicate(), *Current));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr void operator++(int) { ++*this; }
|
||||
FORCEINLINE constexpr FIteratorImpl operator++(int) requires (CForwardIterator<TRangeIterator<V>>) { FIteratorImpl Temp = *this; ++*this; return Temp; }
|
||||
FORCEINLINE constexpr FIteratorImpl operator--(int) requires (CBidirectionalIterator<TRangeIterator<V>>) { FIteratorImpl Temp = *this; --*this; return Temp; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const TRangeIterator<V>& GetBase() const& { return Current; }
|
||||
NODISCARD FORCEINLINE constexpr TRangeIterator<V> GetBase() && { return MoveTemp(Current); }
|
||||
|
||||
private:
|
||||
|
||||
TFilterView* Owner;
|
||||
|
||||
NO_UNIQUE_ADDRESS TRangeIterator<V> Current;
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl(TFilterView& InOwner, TRangeIterator<V> InCurrent) : Owner(&InOwner), Current(MoveTemp(InCurrent)) { }
|
||||
|
||||
friend FSentinelImpl;
|
||||
|
||||
friend TFilterView;
|
||||
};
|
||||
|
||||
class FSentinelImpl final
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl() = default;
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const FIteratorImpl& InValue) const& { return Current == InValue.Current; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TRangeSentinel<V> GetBase() const { return Current; }
|
||||
|
||||
private:
|
||||
|
||||
TRangeSentinel<V> Current;
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl(TFilterView& InOwner, TRangeSentinel<V> InCurrent) : Current(InCurrent) { }
|
||||
|
||||
friend TFilterView;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template <typename R, typename Pred>
|
||||
TFilterView(R&&, Pred) -> TFilterView<TAllView<R>, Pred>;
|
||||
|
||||
static_assert( CInputRange<TFilterView<TAllView<IRange< IInputIterator<int&>>>, bool(*)(int)>>);
|
||||
static_assert( CForwardRange<TFilterView<TAllView<IRange< IForwardIterator<int&>>>, bool(*)(int)>>);
|
||||
static_assert(CBidirectionalRange<TFilterView<TAllView<IRange<IBidirectionalIterator<int&>>>, bool(*)(int)>>);
|
||||
static_assert(CBidirectionalRange<TFilterView<TAllView<IRange< IRandomAccessIterator<int&>>>, bool(*)(int)>>);
|
||||
static_assert(CBidirectionalRange<TFilterView<TAllView<IRange< IContiguousIterator<int&>>>, bool(*)(int)>>);
|
||||
|
||||
static_assert(CCommonRange<TFilterView<TAllView<ICommonRange<IForwardIterator<int>>>, bool(*)(int)>>);
|
||||
static_assert( CView<TFilterView<TAllView< IRange< IInputIterator<int>>>, bool(*)(int)>>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** Creates A view adapter that consists of the elements of a range that satisfies a predicate. */
|
||||
template <CViewableRange R, typename Pred> requires (requires { TFilterView(DeclVal<R>(), DeclVal<Pred>()); })
|
||||
NODISCARD FORCEINLINE constexpr auto Filter(R&& Base, Pred&& Predicate)
|
||||
{
|
||||
return TFilterView(Forward<R>(Base), Forward<Pred>(Predicate));
|
||||
}
|
||||
|
||||
/** Creates A view adapter that consists of the elements of a range that satisfies a predicate. */
|
||||
template <typename Pred>
|
||||
NODISCARD FORCEINLINE constexpr auto Filter(Pred&& Predicate)
|
||||
{
|
||||
using FClosure = decltype([]<CViewableRange R, typename T> requires (requires { Ranges::Filter(DeclVal<R>(), DeclVal<T>()); }) (R&& Base, T&& Predicate)
|
||||
{
|
||||
return Ranges::Filter(Forward<R>(Base), Forward<T>(Predicate));
|
||||
});
|
||||
|
||||
return TAdaptorClosure<FClosure, TDecay<Pred>>(Forward<Pred>(Predicate));
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
118
Redcraft.Utility/Source/Public/Ranges/MoveView.h
Normal file
118
Redcraft.Utility/Source/Public/Ranges/MoveView.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/MoveIterator.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/Pipe.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/AllView.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/**
|
||||
* A view adapter which dereferences to a rvalue reference.
|
||||
* When based on an input view, the move view satisfies at least an input view up to a random access view.
|
||||
* When based on a common view, the move view satisfies a common view.
|
||||
*/
|
||||
template <CInputRange V> requires (CView<V>)
|
||||
class TMoveView : public IBasicViewInterface<TMoveView<V>>
|
||||
{
|
||||
public:
|
||||
|
||||
using FElementType = TRangeElement<V>;
|
||||
using FReference = TRangeRValueReference<V>;
|
||||
|
||||
FORCEINLINE constexpr TMoveView() requires (CDefaultConstructible<V>) = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TMoveView(V InBase) : Base(MoveTemp(InBase)) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() requires (!CSimpleView<V>)
|
||||
{
|
||||
return MakeMoveIterator(Ranges::Begin(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() const requires (CRange<const V>)
|
||||
{
|
||||
return MakeMoveIterator(Ranges::Begin(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto End() requires (!CSimpleView<V>)
|
||||
{
|
||||
if constexpr (CCommonRange<V>)
|
||||
{
|
||||
return MakeMoveIterator(Ranges::End(Base));
|
||||
}
|
||||
else return MakeMoveSentinel(Ranges::End(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto End() const requires (CRange<const V>)
|
||||
{
|
||||
if constexpr (CCommonRange<V>)
|
||||
{
|
||||
return MakeMoveIterator(Ranges::End(Base));
|
||||
}
|
||||
else return MakeMoveSentinel(Ranges::End(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() requires (CSizedRange< V>) { return Ranges::Num(Base); }
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedRange<const V>) { return Ranges::Num(Base); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() const& requires (CCopyConstructible<V>) { return Base; }
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() && { return MoveTemp(Base); }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS V Base;
|
||||
|
||||
};
|
||||
|
||||
template <typename R>
|
||||
TMoveView(R&&) -> TMoveView<TAllView<R>>;
|
||||
|
||||
static_assert( CInputRange<TMoveView<TAllView<IRange< IInputIterator<int&>>>>>);
|
||||
static_assert( CForwardRange<TMoveView<TAllView<IRange< IForwardIterator<int&>>>>>);
|
||||
static_assert(CBidirectionalRange<TMoveView<TAllView<IRange<IBidirectionalIterator<int&>>>>>);
|
||||
static_assert( CRandomAccessRange<TMoveView<TAllView<IRange< IRandomAccessIterator<int&>>>>>);
|
||||
static_assert( CRandomAccessRange<TMoveView<TAllView<IRange< IContiguousIterator<int&>>>>>);
|
||||
|
||||
static_assert(CCommonRange<TMoveView<TAllView<ICommonRange<IForwardIterator<int>>>>>);
|
||||
static_assert( CView<TMoveView<TAllView< IRange< IInputIterator<int>>>>>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename T>
|
||||
constexpr bool bEnableBorrowedRange<Ranges::TMoveView<T>> = bEnableBorrowedRange<T>;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** Creates A view adapter that dereferences to a rvalue reference. */
|
||||
template <CViewableRange R> requires (requires { TMoveView(DeclVal<R>()); })
|
||||
NODISCARD FORCEINLINE constexpr auto Move(R&& Base)
|
||||
{
|
||||
return TMoveView(Forward<R>(Base));
|
||||
}
|
||||
|
||||
/** Creates A view adapter that dereferences to a rvalue reference. */
|
||||
NODISCARD FORCEINLINE constexpr auto Move()
|
||||
{
|
||||
using FClosure = decltype([]<CViewableRange R> requires (requires { Ranges::Move(DeclVal<R>()); }) (R&& Base)
|
||||
{
|
||||
return Ranges::Move(Forward<R>(Base));
|
||||
});
|
||||
|
||||
return TAdaptorClosure<FClosure>();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
146
Redcraft.Utility/Source/Public/Ranges/Pipe.h
Normal file
146
Redcraft.Utility/Source/Public/Ranges/Pipe.h
Normal file
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Templates/Tuple.h"
|
||||
#include "Templates/Invoke.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/**
|
||||
* An interface class template for defining a range adaptor closure.
|
||||
* When the derived class has a unary operator() with range as a reference and is not itself a range,
|
||||
* the derived class is the range adaptor closure type and its objects can participate in pipe operations.
|
||||
* Specify, the unary operator() with any reference qualifier or cv-qualifier must be defined and has same effect.
|
||||
* Not directly instantiable.
|
||||
*/
|
||||
template <CObject D> requires (CSameAs<D, TRemoveCV<D>>)
|
||||
class IAdaptorClosure { };
|
||||
|
||||
/** An adaptor closure helper that wraps a callable object. */
|
||||
template <CDefaultConstructible F, CMoveConstructible... Ts> requires (CEmpty<F> && ... && CSameAs<TDecay<Ts>, Ts>)
|
||||
class TAdaptorClosure : public IAdaptorClosure<TAdaptorClosure<F, Ts...>>
|
||||
{
|
||||
public:
|
||||
|
||||
template <typename... Us> requires (CConstructibleFrom<TTuple<Ts...>, Us...> && ... && CSameAs<TDecay<Us>, Ts>)
|
||||
FORCEINLINE constexpr explicit TAdaptorClosure(Us&&... InArgs) : Args(Forward<Us>(InArgs)...) { }
|
||||
|
||||
template <typename R> requires (CInvocable<F, R, Ts&...>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator()(R&& Range) &
|
||||
{
|
||||
return [this, &Range]<size_t... Indices>(TIndexSequence<Indices...>)
|
||||
{
|
||||
return Invoke(F(), Forward<R>(Range), Args.template GetValue<Indices>()...);
|
||||
}
|
||||
(TMakeIndexSequence<sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
template <typename R> requires (CInvocable<F, R, const Ts&...>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator()(R&& Range) const&
|
||||
{
|
||||
return [this, &Range]<size_t... Indices>(TIndexSequence<Indices...>)
|
||||
{
|
||||
return Invoke(F(), Forward<R>(Range), Args.template GetValue<Indices>()...);
|
||||
}
|
||||
(TMakeIndexSequence<sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
template <typename R> requires (CInvocable<F, R, Ts&&...>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator()(R&& Range) &&
|
||||
{
|
||||
return [this, &Range]<size_t... Indices>(TIndexSequence<Indices...>)
|
||||
{
|
||||
return Invoke(F(), Forward<R>(Range), MoveTemp(Args).template GetValue<Indices>()...);
|
||||
}
|
||||
(TMakeIndexSequence<sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
template <typename R> requires (CInvocable<F, R, const Ts&&...>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator()(R&& Range) const&&
|
||||
{
|
||||
return [this, &Range]<size_t... Indices>(TIndexSequence<Indices...>)
|
||||
{
|
||||
return Invoke(F(), Forward<R>(Range), MoveTemp(Args).template GetValue<Indices>()...);
|
||||
}
|
||||
(TMakeIndexSequence<sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS TTuple<Ts...> Args;
|
||||
|
||||
};
|
||||
|
||||
/** A pipe closure that wraps two adaptor closures. */
|
||||
template <CMoveConstructible T, CMoveConstructible U>
|
||||
requires (CSameAs<TRemoveCVRef<T>, T> && CDerivedFrom<T, IAdaptorClosure<T>>
|
||||
&& CSameAs<TRemoveCVRef<U>, U> && CDerivedFrom<U, IAdaptorClosure<U>>)
|
||||
class TPipeClosure final : public IAdaptorClosure<TPipeClosure<T, U>>
|
||||
{
|
||||
public:
|
||||
|
||||
template <typename V, typename W>
|
||||
requires (CSameAs<TRemoveCVRef<V>, T> && CConstructibleFrom<T, V>
|
||||
&& CSameAs<TRemoveCVRef<W>, U> && CConstructibleFrom<U, W>)
|
||||
FORCEINLINE constexpr explicit TPipeClosure(V InLHS, W InRHS)
|
||||
: LHS(Forward<V>(InLHS)), RHS(Forward<W>(InRHS))
|
||||
{ }
|
||||
|
||||
template <typename R> requires (CInvocable<T&, R> && CInvocable<U&, TInvokeResult<T&, R>>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator()(R&& Range) &
|
||||
{
|
||||
return Forward<R>(Range) | LHS | RHS;
|
||||
}
|
||||
|
||||
template <typename R> requires (CInvocable<const T&, R> && CInvocable<const U&, TInvokeResult<const T&, R>>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator()(R&& Range) const&
|
||||
{
|
||||
return Forward<R>(Range) | LHS | RHS;
|
||||
}
|
||||
|
||||
template <typename R> requires (CInvocable<T&&, R> && CInvocable<U&&, TInvokeResult<T&&, R>>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator()(R&& Range) &&
|
||||
{
|
||||
return Forward<R>(Range) | MoveTemp(LHS) | MoveTemp(RHS);
|
||||
}
|
||||
|
||||
template <typename R> requires (CInvocable<const T&&, R> && CInvocable<const U&&, TInvokeResult<const T&&, R>>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator()(R&& Range) const&&
|
||||
{
|
||||
return Forward<R>(Range) | MoveTemp(LHS) | MoveTemp(RHS);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS T LHS;
|
||||
NO_UNIQUE_ADDRESS U RHS;
|
||||
};
|
||||
|
||||
/** Apply the range adaptor closure to the range. */
|
||||
template <CRange R, CInvocable<R> T> requires (CDerivedFrom<TRemoveCVRef<T>, IAdaptorClosure<TRemoveCVRef<T>>>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator|(R&& Range, T&& Closure)
|
||||
{
|
||||
return Invoke(Forward<T>(Closure), Forward<R>(Range));
|
||||
}
|
||||
|
||||
/** Create a pipe closure that wraps two adaptor closures. */
|
||||
template <CMoveConstructible T, CMoveConstructible U>
|
||||
requires (CDerivedFrom<TRemoveCVRef<T>, IAdaptorClosure<TRemoveCVRef<T>>>
|
||||
&& CDerivedFrom<TRemoveCVRef<U>, IAdaptorClosure<TRemoveCVRef<U>>>)
|
||||
NODISCARD FORCEINLINE constexpr auto operator|(T&& LHS, U&& RHS)
|
||||
{
|
||||
return TPipeClosure<TRemoveCVRef<T>, TRemoveCVRef<U>>(Forward<T>(LHS), Forward<U>(RHS));
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
14
Redcraft.Utility/Source/Public/Ranges/Ranges.h
Normal file
14
Redcraft.Utility/Source/Public/Ranges/Ranges.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/Conversion.h"
|
||||
#include "Ranges/Factory.h"
|
||||
#include "Ranges/Pipe.h"
|
||||
#include "Ranges/AllView.h"
|
||||
#include "Ranges/MoveView.h"
|
||||
#include "Ranges/FilterView.h"
|
||||
#include "Ranges/TransformView.h"
|
||||
#include "Ranges/TakeView.h"
|
||||
#include "Ranges/TakeWhileView.h"
|
||||
186
Redcraft.Utility/Source/Public/Ranges/TakeView.h
Normal file
186
Redcraft.Utility/Source/Public/Ranges/TakeView.h
Normal file
@@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/CountedIterator.h"
|
||||
#include "Numerics/Math.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/Pipe.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/AllView.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/**
|
||||
* A view adapter that includes a specified number of elements from the beginning of a range.
|
||||
* When based on any view, the take view satisfies the corresponding any view.
|
||||
* When based on a random access and sized view, the take view satisfies a common view.
|
||||
*/
|
||||
template <CView V>
|
||||
class TTakeView : public IBasicViewInterface<TTakeView<V>>
|
||||
{
|
||||
private:
|
||||
|
||||
template <bool bConst> class FSentinelImpl;
|
||||
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr TTakeView() requires (CDefaultConstructible<V>) = default;
|
||||
|
||||
FORCEINLINE constexpr TTakeView(V InBase, size_t InCount) : Base(MoveTemp(InBase)), Count(InCount) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() requires (!CSimpleView<V>)
|
||||
{
|
||||
if constexpr (CSizedRange<V>)
|
||||
{
|
||||
if constexpr (CRandomAccessRange<V>)
|
||||
{
|
||||
return Ranges::Begin(Base);
|
||||
}
|
||||
else return MakeCountedIterator(Ranges::Begin(Base), Num());
|
||||
}
|
||||
else return MakeCountedIterator(Ranges::Begin(Base), Count);
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() const requires (CRange<const V>)
|
||||
{
|
||||
if constexpr (CSizedRange<const V>)
|
||||
{
|
||||
if constexpr (CRandomAccessRange<const V>)
|
||||
{
|
||||
return Ranges::Begin(Base);
|
||||
}
|
||||
else return MakeCountedIterator(Ranges::Begin(Base), Num());
|
||||
}
|
||||
else return MakeCountedIterator(Ranges::Begin(Base), Count);
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto End() requires (!CSimpleView<V>)
|
||||
{
|
||||
if constexpr (CSizedRange<V>)
|
||||
{
|
||||
if constexpr (CRandomAccessRange<V>)
|
||||
{
|
||||
return Ranges::Begin(Base) + Num();
|
||||
}
|
||||
else return DefaultSentinel;
|
||||
}
|
||||
else return FSentinelImpl<false>(Ranges::End(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto End() const requires (CRange<const V>)
|
||||
{
|
||||
if constexpr (CSizedRange<const V>)
|
||||
{
|
||||
if constexpr (CRandomAccessRange<const V>)
|
||||
{
|
||||
return Ranges::Begin(Base) + Num();
|
||||
}
|
||||
else return DefaultSentinel;
|
||||
}
|
||||
else return FSentinelImpl<true>(Ranges::End(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() requires (CSizedRange< V>) { return Math::Min(Ranges::Num(Base), Count); }
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedRange<const V>) { return Math::Min(Ranges::Num(Base), Count); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() const& requires (CCopyConstructible<V>) { return Base; }
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() && { return MoveTemp(Base); }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS V Base;
|
||||
|
||||
size_t Count;
|
||||
|
||||
template <bool bConst>
|
||||
class FSentinelImpl final
|
||||
{
|
||||
private:
|
||||
|
||||
using FBase = TConditional<bConst, const V, V>;
|
||||
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl() = default;
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl(FSentinelImpl<!bConst> Sentinel) requires (bConst && CConvertibleTo<TRangeSentinel<V>, TRangeSentinel<FBase>>)
|
||||
: Current(Sentinel.Current)
|
||||
{ }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const TCountedIterator<TRangeIterator<FBase>>& InValue) const&
|
||||
{
|
||||
return InValue.Num() == 0 || InValue.GetBase() == Current;
|
||||
}
|
||||
|
||||
template <bool bOther = !bConst> requires (CSentinelFor<TRangeSentinel<FBase>, TRangeIterator<TConditional<bOther, const V, V>>>)
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const TCountedIterator<TRangeIterator<TConditional<bOther, const V, V>>>& InValue)
|
||||
{
|
||||
return InValue.Num() == 0 || InValue.GetBase() == Current;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TRangeSentinel<FBase> GetBase() const { return Current; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS TRangeSentinel<FBase> Current;
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl(TRangeSentinel<FBase> InCurrent) : Current(InCurrent) { }
|
||||
|
||||
friend TTakeView;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template <typename R>
|
||||
TTakeView(R&&, size_t) -> TTakeView<TAllView<R>>;
|
||||
|
||||
static_assert( CInputRange<TTakeView<TAllView<IRange< IInputIterator<int&>>>>>);
|
||||
static_assert( CForwardRange<TTakeView<TAllView<IRange< IForwardIterator<int&>>>>>);
|
||||
static_assert(CBidirectionalRange<TTakeView<TAllView<IRange<IBidirectionalIterator<int&>>>>>);
|
||||
static_assert( CRandomAccessRange<TTakeView<TAllView<IRange< IRandomAccessIterator<int&>>>>>);
|
||||
static_assert( CContiguousRange<TTakeView<TAllView<IRange< IContiguousIterator<int&>>>>>);
|
||||
|
||||
static_assert(CCommonRange<TTakeView<TAllView<ISizedRange< IRandomAccessIterator<int>>>>>);
|
||||
static_assert( CSizedRange<TTakeView<TAllView<ISizedRange<IInputOrOutputIterator<int>>>>>);
|
||||
static_assert( CView<TTakeView<TAllView< IRange<IInputOrOutputIterator<int>>>>>);
|
||||
|
||||
static_assert(COutputRange<TTakeView<TAllView<IRange<IOutputIterator<int&>>>>, int>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename T>
|
||||
constexpr bool bEnableBorrowedRange<Ranges::TTakeView<T>> = bEnableBorrowedRange<T>;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** Creates A view adapter that includes a specified number of elements from the beginning of a range. */
|
||||
template <CViewableRange R> requires (requires { TTakeView(DeclVal<R>(), DeclVal<size_t>()); })
|
||||
NODISCARD FORCEINLINE constexpr auto Take(R&& Base, size_t Count)
|
||||
{
|
||||
return TTakeView(Forward<R>(Base), Count);
|
||||
}
|
||||
|
||||
/** Creates A view adapter that includes a specified number of elements from the beginning of a range. */
|
||||
NODISCARD FORCEINLINE constexpr auto Take(size_t Count)
|
||||
{
|
||||
using FClosure = decltype([]<CViewableRange R> requires (requires { Ranges::Take(DeclVal<R>(), DeclVal<size_t>()); }) (R&& Base, size_t Count)
|
||||
{
|
||||
return Ranges::Take(Forward<R>(Base), Count);
|
||||
});
|
||||
|
||||
return TAdaptorClosure<FClosure, size_t>(Count);
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
149
Redcraft.Utility/Source/Public/Ranges/TakeWhileView.h
Normal file
149
Redcraft.Utility/Source/Public/Ranges/TakeWhileView.h
Normal file
@@ -0,0 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/CountedIterator.h"
|
||||
#include "Numerics/Math.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/Pipe.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/AllView.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/**
|
||||
* A view adapter that includes elements that satisfy the predicate from the beginning of the range.
|
||||
* When based on an input view, the take while view satisfies at least an input view up to a contiguous view.
|
||||
* When based on a forward and output view, the take while view satisfies an output view.
|
||||
*/
|
||||
template <CInputRange V, CPredicate<TRangeReference<V>> Pred> requires (CView<V> && CObject<Pred> && CMoveConstructible<Pred>)
|
||||
class TTakeWhileView : public IBasicViewInterface<TTakeWhileView<V, Pred>>
|
||||
{
|
||||
private:
|
||||
|
||||
template <bool bConst> class FSentinelImpl;
|
||||
|
||||
public:
|
||||
|
||||
using FElementType = TRangeElement<V>;
|
||||
using FReference = TRangeReference<V>;
|
||||
|
||||
FORCEINLINE constexpr TTakeWhileView() requires (CDefaultConstructible<V>&& CDefaultConstructible<Pred>) = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TTakeWhileView(V InBase, Pred InPredicate) : Base(MoveTemp(InBase)), Predicate(MoveTemp(InPredicate)) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() requires (!CSimpleView<V>)
|
||||
{
|
||||
return Ranges::Begin(Base);
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() const requires (CRange<const V> && CPredicate<const Pred&, TRangeReference<const V>>)
|
||||
{
|
||||
return Ranges::Begin(Base);
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto End() requires (!CSimpleView<V>)
|
||||
{
|
||||
return FSentinelImpl<false>(Ranges::End(Base), AddressOf(Predicate));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto End() const requires (CRange<const V> && CPredicate<const Pred&, TRangeReference<const V>>)
|
||||
{
|
||||
return FSentinelImpl<true>(Ranges::End(Base), AddressOf(Predicate));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() const& requires (CCopyConstructible<V>) { return Base; }
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() && { return MoveTemp(Base); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const Pred& GetPredicate() const { return Predicate; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS V Base;
|
||||
|
||||
NO_UNIQUE_ADDRESS Pred Predicate;
|
||||
|
||||
template <bool bConst>
|
||||
class FSentinelImpl final
|
||||
{
|
||||
private:
|
||||
|
||||
using FBase = TConditional<bConst, const V, V>;
|
||||
using FPred = TConditional<bConst, const Pred, Pred>;
|
||||
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl() = default;
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl(FSentinelImpl<!bConst> Sentinel) requires (bConst && CConvertibleTo<TRangeSentinel<V>, TRangeSentinel<FBase>>)
|
||||
: Current(Sentinel.Current), Predicate(Sentinel.Predicate)
|
||||
{ }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const TRangeIterator<FBase>& InValue) const&
|
||||
{
|
||||
return InValue == Current || !InvokeResult<bool>(*Predicate, *InValue);
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TRangeSentinel<FBase> GetBase() const { return Current; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS TRangeSentinel<FBase> Current;
|
||||
|
||||
FPred* Predicate;
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl(TRangeSentinel<FBase> InCurrent, FPred* InPredicate) : Current(InCurrent), Predicate(InPredicate) { }
|
||||
|
||||
friend TTakeWhileView;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template <typename R, typename Pred>
|
||||
TTakeWhileView(R&&, Pred) -> TTakeWhileView<TAllView<R>, Pred>;
|
||||
|
||||
static_assert( CInputRange<TTakeWhileView<TAllView<IRange< IInputIterator<int&>>>, bool(*)(int)>>);
|
||||
static_assert( CForwardRange<TTakeWhileView<TAllView<IRange< IForwardIterator<int&>>>, bool(*)(int)>>);
|
||||
static_assert(CBidirectionalRange<TTakeWhileView<TAllView<IRange<IBidirectionalIterator<int&>>>, bool(*)(int)>>);
|
||||
static_assert( CRandomAccessRange<TTakeWhileView<TAllView<IRange< IRandomAccessIterator<int&>>>, bool(*)(int)>>);
|
||||
static_assert( CContiguousRange<TTakeWhileView<TAllView<IRange< IContiguousIterator<int&>>>, bool(*)(int)>>);
|
||||
|
||||
static_assert(CView<TTakeWhileView<TAllView<IRange<IInputIterator<int>>>, bool(*)(int)>>);
|
||||
|
||||
static_assert(COutputRange<TTakeWhileView<TAllView<IRange<IForwardIterator<int&>>>, bool(*)(int)>, int>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** Creates A view adapter that includes elements that satisfy the predicate from the beginning of the range. */
|
||||
template <CViewableRange R, typename Pred> requires (requires { TTakeWhileView(DeclVal<R>(), DeclVal<Pred>()); })
|
||||
NODISCARD FORCEINLINE constexpr auto TakeWhile(R&& Base, Pred&& Predicate)
|
||||
{
|
||||
return TTakeWhileView(Forward<R>(Base), Forward<Pred>(Predicate));
|
||||
}
|
||||
|
||||
/** Creates A view adapter that includes elements that satisfy the predicate from the beginning of the range. */
|
||||
template <typename Pred>
|
||||
NODISCARD FORCEINLINE constexpr auto TakeWhile(Pred&& Predicate)
|
||||
{
|
||||
using FClosure = decltype([]<CViewableRange R, typename T> requires (requires { Ranges::TakeWhile(DeclVal<R>(), DeclVal<T>()); }) (R&& Base, T&& Predicate)
|
||||
{
|
||||
return Ranges::TakeWhile(Forward<R>(Base), Forward<T>(Predicate));
|
||||
});
|
||||
|
||||
return TAdaptorClosure<FClosure, TDecay<Pred>>(Forward<Pred>(Predicate));
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
236
Redcraft.Utility/Source/Public/Ranges/TransformView.h
Normal file
236
Redcraft.Utility/Source/Public/Ranges/TransformView.h
Normal file
@@ -0,0 +1,236 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Ranges/View.h"
|
||||
#include "Ranges/Pipe.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Ranges/AllView.h"
|
||||
#include "Templates/Invoke.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/**
|
||||
* A view adapter of a sequence that applies a transformation function to each element.
|
||||
* When based on an input view, the transform view satisfies at least an input view up to a random access view.
|
||||
* When based on a common view, the transform view satisfies a common view. When based on a sized view,
|
||||
* the transform view satisfies a sized view. When based on a forward view and the function return
|
||||
* an assignable value, the transform view satisfies an output view.
|
||||
*/
|
||||
template <CInputRange V, CMoveConstructible F> requires (CView<V> && CObject<F>
|
||||
&& CRegularInvocable<F&, TRangeReference<V>> && CReferenceable<TInvokeResult<F&, TRangeReference<V>>>)
|
||||
class TTransformView : public IBasicViewInterface<TTransformView<V, F>>
|
||||
{
|
||||
private:
|
||||
|
||||
template <bool bConst> class FIteratorImpl;
|
||||
template <bool bConst> class FSentinelImpl;
|
||||
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr TTransformView() requires (CDefaultConstructible<V>&& CDefaultConstructible<F>) = default;
|
||||
|
||||
FORCEINLINE constexpr explicit TTransformView(V InBase, F InFunc) : Base(MoveTemp(InBase)), Func(MoveTemp(InFunc)) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin()
|
||||
{
|
||||
return FIteratorImpl<false>(*this, Ranges::Begin(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto Begin() const requires (CRange<const V> && CRegularInvocable<const F&, TRangeReference<const V>>)
|
||||
{
|
||||
return FIteratorImpl<true>(*this, Ranges::Begin(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto End()
|
||||
{
|
||||
if constexpr (CCommonRange<V>)
|
||||
{
|
||||
return FIteratorImpl<false>(*this, Ranges::End(Base));
|
||||
}
|
||||
|
||||
else return FSentinelImpl<false>(*this, Ranges::End(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto End() const requires (CRange<const V> && CRegularInvocable<const F&, TRangeReference<const V>>)
|
||||
{
|
||||
if constexpr (CCommonRange<const V>)
|
||||
{
|
||||
return FIteratorImpl<true>(*this, Ranges::End(Base));
|
||||
}
|
||||
|
||||
else return FSentinelImpl<true>(*this, Ranges::End(Base));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() requires (CSizedRange< V>) { return Ranges::Num(Base); }
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedRange<const V>) { return Ranges::Num(Base); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() const& requires (CCopyConstructible<V>) { return Base; }
|
||||
NODISCARD FORCEINLINE constexpr V GetBase() && { return MoveTemp(Base); }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS V Base;
|
||||
NO_UNIQUE_ADDRESS F Func;
|
||||
|
||||
template <bool bConst>
|
||||
class FIteratorImpl
|
||||
{
|
||||
private:
|
||||
|
||||
using FOwner = TConditional<bConst, const TTransformView, TTransformView>;
|
||||
using FBase = TConditional<bConst, const V, V>;
|
||||
using FFunc = TConditional<bConst, const F, F>;
|
||||
|
||||
public:
|
||||
|
||||
using FElementType = TRemoveCVRef<TInvokeResult<FFunc&, TRangeReference<FBase>>>;
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl() requires (CDefaultConstructible<TRangeIterator<FBase>>) = default;
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl(FIteratorImpl<!bConst> Iter) requires (bConst && CConvertibleTo<TRangeIterator<V>, TRangeIterator<FBase>>)
|
||||
: Owner(Iter.Owner), Current(MoveTemp(Iter).GetBase())
|
||||
{ }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const FIteratorImpl& LHS, const FIteratorImpl& RHS) requires (CEqualityComparable<TRangeIterator<FBase>>)
|
||||
{
|
||||
return LHS.GetBase() == RHS.GetBase();
|
||||
}
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr auto operator<=>(const FIteratorImpl& LHS, const FIteratorImpl& RHS) requires (CThreeWayComparable<TRangeIterator<FBase>>)
|
||||
{
|
||||
return LHS.GetBase() <=> RHS.GetBase();
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) operator*() const { return Invoke(Owner->Func, *GetBase()); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) operator[](ptrdiff Index) const requires (CRandomAccessRange<FBase>) { return Invoke(Owner->Func, GetBase()[Index]); }
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl& operator++() { ++Current; return *this; }
|
||||
FORCEINLINE constexpr FIteratorImpl& operator--() requires (CBidirectionalRange<FBase>) { --Current; return *this; }
|
||||
|
||||
FORCEINLINE constexpr void operator++(int) { ++*this; }
|
||||
FORCEINLINE constexpr FIteratorImpl operator++(int) requires (CForwardRange<FBase>) { FIteratorImpl Temp = *this; ++*this; return Temp; }
|
||||
FORCEINLINE constexpr FIteratorImpl operator--(int) requires (CBidirectionalRange<FBase>) { FIteratorImpl Temp = *this; --*this; return Temp; }
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl& operator+=(ptrdiff Offset) requires (CRandomAccessRange<FBase>) { Current += Offset; return *this; }
|
||||
FORCEINLINE constexpr FIteratorImpl& operator-=(ptrdiff Offset) requires (CRandomAccessRange<FBase>) { Current -= Offset; return *this; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr FIteratorImpl operator+(ptrdiff Offset) const requires (CRandomAccessRange<FBase>) { FIteratorImpl Temp = *this; Temp += Offset; return Temp; }
|
||||
NODISCARD FORCEINLINE constexpr FIteratorImpl operator-(ptrdiff Offset) const requires (CRandomAccessRange<FBase>) { FIteratorImpl Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr FIteratorImpl operator+(ptrdiff Offset, const FIteratorImpl& Iter) requires (CRandomAccessRange<FBase>) { return Iter + Offset; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const FIteratorImpl& LHS, const FIteratorImpl& RHS) requires (CSizedSentinelFor<TRangeIterator<FBase>, TRangeIterator<FBase>>)
|
||||
{
|
||||
return LHS.GetBase() - RHS.GetBase();
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr const TRangeIterator<FBase>& GetBase() const& { return Current; }
|
||||
NODISCARD FORCEINLINE constexpr TRangeIterator<FBase> GetBase() && { return MoveTemp(Current); }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS FOwner* Owner;
|
||||
|
||||
NO_UNIQUE_ADDRESS TRangeIterator<FBase> Current;
|
||||
|
||||
FORCEINLINE constexpr FIteratorImpl(FOwner& InOwner, TRangeIterator<FBase> InCurrent) : Owner(&InOwner), Current(MoveTemp(InCurrent)) { }
|
||||
|
||||
template <bool> friend class FIteratorImpl;
|
||||
template <bool> friend class FSentinelImpl;
|
||||
|
||||
friend TTransformView;
|
||||
};
|
||||
|
||||
template <bool bConst>
|
||||
class FSentinelImpl
|
||||
{
|
||||
private:
|
||||
|
||||
using FOwner = TConditional<bConst, const TTransformView, TTransformView>;
|
||||
using FBase = TConditional<bConst, const V, V>;
|
||||
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl() requires (CDefaultConstructible<TRangeSentinel<FBase>>) = default;
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl(FSentinelImpl<!bConst> Sentinel) requires (bConst && CConvertibleTo<TRangeSentinel<V>, TRangeSentinel<FBase>>)
|
||||
: Current(Sentinel.GetBase())
|
||||
{ }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const FIteratorImpl<bConst>& InValue) const& { return Current == InValue.GetBase(); }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const FIteratorImpl<bConst>& LHS, const FSentinelImpl& RHS)
|
||||
requires CSizedSentinelFor<TRangeSentinel<FBase>, TRangeIterator<FBase>>
|
||||
{
|
||||
return LHS.GetBase() - RHS.GetBase();
|
||||
}
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const FSentinelImpl& LHS, const FIteratorImpl<bConst>& RHS)
|
||||
requires CSizedSentinelFor<TRangeSentinel<FBase>, TRangeIterator<FBase>>
|
||||
{
|
||||
return LHS.GetBase() - RHS.GetBase();
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TRangeSentinel<FBase> GetBase() const { return Current; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS TRangeSentinel<FBase> Current;
|
||||
|
||||
FORCEINLINE constexpr FSentinelImpl(FOwner& InOwner, TRangeSentinel<FBase> InCurrent) : Current(InCurrent) { }
|
||||
|
||||
friend TTransformView;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template <typename R, typename F>
|
||||
TTransformView(R&&, F) -> TTransformView<TAllView<R>, F>;
|
||||
|
||||
static_assert( CInputRange<TTransformView<TAllView<IRange< IInputIterator<int&>>>, int(*)(int)>>);
|
||||
static_assert( CForwardRange<TTransformView<TAllView<IRange< IForwardIterator<int&>>>, int(*)(int)>>);
|
||||
static_assert(CBidirectionalRange<TTransformView<TAllView<IRange<IBidirectionalIterator<int&>>>, int(*)(int)>>);
|
||||
static_assert( CRandomAccessRange<TTransformView<TAllView<IRange< IRandomAccessIterator<int&>>>, int(*)(int)>>);
|
||||
static_assert( CRandomAccessRange<TTransformView<TAllView<IRange< IContiguousIterator<int&>>>, int(*)(int)>>);
|
||||
|
||||
static_assert(CCommonRange<TTransformView<TAllView<ICommonRange<IForwardIterator<int>>>, int(*)(int)>>);
|
||||
static_assert( CSizedRange<TTransformView<TAllView< ISizedRange< IInputIterator<int>>>, int(*)(int)>>);
|
||||
static_assert( CView<TTransformView<TAllView< IRange< IInputIterator<int>>>, int(*)(int)>>);
|
||||
|
||||
static_assert(COutputRange<TTransformView<TAllView<IRange<IForwardIterator<int>>>, int&(*)(int)>, int>);
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** Creates A view adapter of a sequence that applies a transformation function to each element. */
|
||||
template <CViewableRange R, typename F> requires (requires { TTransformView(DeclVal<R>(), DeclVal<F>()); })
|
||||
NODISCARD FORCEINLINE constexpr auto Transform(R&& Base, F&& Func)
|
||||
{
|
||||
return TTransformView(Forward<R>(Base), Forward<F>(Func));
|
||||
}
|
||||
|
||||
/** Creates A view adapter of a sequence that applies a transformation function to each element. */
|
||||
template <typename F>
|
||||
NODISCARD FORCEINLINE constexpr auto Transform(F&& Func)
|
||||
{
|
||||
using FClosure = decltype([]<CViewableRange R, typename T> requires (requires { Ranges::Transform(DeclVal<R>(), DeclVal<T>()); }) (R&& Base, T&& Func)
|
||||
{
|
||||
return Ranges::Transform(Forward<R>(Base), Forward<T>(Func));
|
||||
});
|
||||
|
||||
return TAdaptorClosure<FClosure, TDecay<F>>(Forward<F>(Func));
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
380
Redcraft.Utility/Source/Public/Ranges/Utility.h
Normal file
380
Redcraft.Utility/Source/Public/Ranges/Utility.h
Normal file
@@ -0,0 +1,380 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
/**
|
||||
* The bool value that indicates whether the range always is borrowed range.
|
||||
* When the range always is borrowed range, it means that the iterators and sentinels
|
||||
* of the range remain valid even if the range object is destructed.
|
||||
*/
|
||||
template <typename R>
|
||||
inline constexpr bool bEnableBorrowedRange = false;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** @return The iterator to the beginning of a container. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& requires(T&& Container) { { Container.Begin() } -> CInputOrOutputIterator; })
|
||||
NODISCARD FORCEINLINE constexpr auto Begin(T&& Container)
|
||||
{
|
||||
return Container.Begin();
|
||||
}
|
||||
|
||||
/** Overloads the Begin algorithm for arrays. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& CArray<TRemoveReference<T>>)
|
||||
NODISCARD FORCEINLINE constexpr auto Begin(T&& Container)
|
||||
{
|
||||
return Container + 0;
|
||||
}
|
||||
|
||||
/** Overloads the Begin algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
NODISCARD FORCEINLINE constexpr const T* Begin(initializer_list<T>& Container)
|
||||
{
|
||||
return Container.begin();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename R>
|
||||
using TRangeIterator = decltype(Ranges::Begin(DeclVal<R&>()));
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** @return The iterator to the end of a container. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& requires(T&& Container) { { Container.End() } -> CSentinelFor<TRangeIterator<T>>; })
|
||||
NODISCARD FORCEINLINE constexpr auto End(T&& Container)
|
||||
{
|
||||
return Container.End();
|
||||
}
|
||||
|
||||
/** Overloads the End algorithm for arrays. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& CBoundedArray<TRemoveReference<T>>)
|
||||
NODISCARD FORCEINLINE constexpr auto End(T&& Container)
|
||||
{
|
||||
return Container + TExtent<TRemoveReference<T>>;
|
||||
}
|
||||
|
||||
/** Overloads the End algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
NODISCARD FORCEINLINE constexpr const T* End(initializer_list<T>& Container)
|
||||
{
|
||||
return Container.end();
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename R>
|
||||
using TRangeSentinel = decltype(Ranges::End(DeclVal<R&>()));
|
||||
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** @return The reverse iterator to the beginning of a container. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& requires(T&& Container) { { Container.RBegin() } -> CInputOrOutputIterator; })
|
||||
NODISCARD FORCEINLINE constexpr auto RBegin(T&& Container)
|
||||
{
|
||||
return Container.RBegin();
|
||||
}
|
||||
|
||||
/** Overloads the RBegin algorithm for synthesized. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& !requires(T&& Container) { { Container.RBegin() } -> CInputOrOutputIterator; }
|
||||
&& (CSameAs<TRangeIterator<T>, TRangeSentinel<T>> && CBidirectionalIterator<TRangeIterator<T>>))
|
||||
NODISCARD FORCEINLINE constexpr auto RBegin(T&& Container)
|
||||
{
|
||||
return MakeReverseIterator(Ranges::End(Forward<T>(Container)));
|
||||
}
|
||||
|
||||
/** @return The reverse iterator to the end of a container. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& requires(T&& Container) { { Container.REnd() } -> CSentinelFor<decltype(Ranges::RBegin(DeclVal<T&>()))>; })
|
||||
NODISCARD FORCEINLINE constexpr auto REnd(T&& Container)
|
||||
{
|
||||
return Container.REnd();
|
||||
}
|
||||
|
||||
/** Overloads the REnd algorithm for synthesized. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& !requires(T&& Container) { { Container.REnd() } -> CSentinelFor<decltype(Ranges::RBegin(DeclVal<T&>()))>; }
|
||||
&& (CSameAs<TRangeIterator<T>, TRangeSentinel<T>> && CBidirectionalIterator<TRangeIterator<T>>))
|
||||
NODISCARD FORCEINLINE constexpr auto REnd(T&& Container)
|
||||
{
|
||||
return MakeReverseIterator(Ranges::Begin(Forward<T>(Container)));
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename R>
|
||||
using TRangeElement = TIteratorElement<TRangeIterator<R>>;
|
||||
|
||||
template <typename R>
|
||||
using TRangePointer = TIteratorPointer<TRangeIterator<R>>;
|
||||
|
||||
template <typename R>
|
||||
using TRangeReference = TIteratorReference<TRangeIterator<R>>;
|
||||
|
||||
template <typename R>
|
||||
using TRangeRValueReference = TIteratorRValueReference<TRangeIterator<R>>;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** @return The pointer to the container element storage. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& requires(T&& Container) { { Container.GetData() } -> CSameAs<TAddPointer<TRangeReference<T>>>; })
|
||||
NODISCARD FORCEINLINE constexpr auto GetData(T&& Container)
|
||||
{
|
||||
return Container.GetData();
|
||||
}
|
||||
|
||||
/** Overloads the GetData algorithm for synthesized. */
|
||||
template <typename T> requires ((CLValueReference<T> || bEnableBorrowedRange<TRemoveCVRef<T>>)
|
||||
&& !requires(T&& Container) { { Container.GetData() } -> CSameAs<TAddPointer<TRangeReference<T>>>; }
|
||||
&& requires(T&& Container) { { Ranges::Begin(Forward<T>(Container)) } -> CContiguousIterator; })
|
||||
NODISCARD FORCEINLINE constexpr auto GetData(T&& Container)
|
||||
{
|
||||
return ToAddress(Ranges::Begin(Forward<T>(Container)));
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
/** Disable the CSizedRange concept for specific types. */
|
||||
template <typename R>
|
||||
inline constexpr bool bDisableSizedRange = false;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** @return The number of elements in the container. */
|
||||
template <typename T> requires (!bDisableSizedRange<TRemoveCVRef<T>>
|
||||
&& requires(T&& Container) { { Container.Num() } -> CSameAs<size_t>; })
|
||||
NODISCARD FORCEINLINE constexpr size_t Num(T&& Container)
|
||||
{
|
||||
return Container.Num();
|
||||
}
|
||||
|
||||
/** Overloads the Num algorithm for arrays. */
|
||||
template <typename T> requires (!bDisableSizedRange<TRemoveCVRef<T>>
|
||||
&& CBoundedArray<TRemoveReference<T>>)
|
||||
NODISCARD FORCEINLINE constexpr size_t Num(T&& Container)
|
||||
{
|
||||
return TExtent<TRemoveReference<T>>;
|
||||
}
|
||||
|
||||
/** Overloads the Num algorithm for synthesized. */
|
||||
template <typename T> requires (!bDisableSizedRange<TRemoveCVRef<T>>
|
||||
&& !requires(T&& Container) { { Container.Num() } -> CSameAs<size_t>; } && !CBoundedArray<TRemoveReference<T>>
|
||||
&& CSizedSentinelFor<TRangeSentinel<T>, TRangeIterator<T>> && CForwardIterator<TRangeIterator<T>>)
|
||||
NODISCARD FORCEINLINE constexpr size_t Num(T&& Container)
|
||||
{
|
||||
return Ranges::End(Forward<T>(Container)) - Ranges::Begin(Forward<T>(Container));
|
||||
}
|
||||
|
||||
/** Overloads the Num algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
NODISCARD FORCEINLINE constexpr size_t Num(initializer_list<T>& Container)
|
||||
{
|
||||
return Container.size();
|
||||
}
|
||||
|
||||
/** @return true if the container is empty, false otherwise. */
|
||||
template <typename T> requires (requires(T&& Container) { { Container.IsEmpty() } -> CBooleanTestable; })
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty(T&& Container)
|
||||
{
|
||||
return Container.IsEmpty();
|
||||
}
|
||||
|
||||
/** Overloads the IsEmpty algorithm for synthesized. */
|
||||
template <typename T> requires ((CBoundedArray<TRemoveReference<T>>
|
||||
|| requires(T&& Container) { { Container.Num() } -> CSameAs<size_t>; })
|
||||
&& !requires(T&& Container) { { Container.IsEmpty() } -> CBooleanTestable; })
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty(T&& Container)
|
||||
{
|
||||
return Ranges::Num(Forward<T>(Container)) == 0;
|
||||
}
|
||||
|
||||
/** Overloads the IsEmpty algorithm for synthesized. */
|
||||
template <typename T> requires (!CBoundedArray<TRemoveReference<T>>
|
||||
&& !requires(T&& Container) { { Container.Num() } -> CSameAs<size_t>; }
|
||||
&& !requires(T&& Container) { { Container.IsEmpty() } -> CBooleanTestable; }
|
||||
&& CForwardIterator<TRangeIterator<T>>)
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty(T&& Container)
|
||||
{
|
||||
return Ranges::End(Forward<T>(Container)) == Ranges::Begin(Forward<T>(Container));
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
/**
|
||||
* A concept specifies a type is a range.
|
||||
* A range is an iterator-sentinel pair that represents a sequence of elements.
|
||||
* This concept does not require that iterator-sentinel pair can be fetched multiple times
|
||||
* if the range does not satisfy the forward range, otherwise it is equality-preserving.
|
||||
* Again, this means that const R may not be a range if R is a range,
|
||||
* e.g. fetching the iterator-sentinel pair from the input range may require moving the iterator
|
||||
* directly from the range object and thus the range object may be modified.
|
||||
*/
|
||||
template <typename R>
|
||||
concept CRange =
|
||||
requires(R Range)
|
||||
{
|
||||
typename TRangeIterator<R>;
|
||||
typename TRangeSentinel<R>;
|
||||
}
|
||||
&& CInputOrOutputIterator<TRangeIterator<R>>
|
||||
&& CSentinelFor<TRangeSentinel<R>, TRangeIterator<R>>;
|
||||
|
||||
/** This is an example of a range type, indicate the traits that define a range type. */
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S = ISentinelFor<I>>
|
||||
struct IRange
|
||||
{
|
||||
/**
|
||||
* Get the iterator-sentinel pair.
|
||||
* If the function is const, it means that the const IRange satisfies CRange.
|
||||
*/
|
||||
I Begin() /* const */;
|
||||
S End() /* const */;
|
||||
};
|
||||
|
||||
// Use IRange<...> represents an range type.
|
||||
static_assert(CRange<IRange<IInputOrOutputIterator<int&>>>);
|
||||
|
||||
/**
|
||||
* A concept specifies a type is a borrowed range.
|
||||
* When the range is borrowed range, it means that the iterators and sentinels
|
||||
* of the range remain valid even if the range value (note not object) is destructed.
|
||||
*/
|
||||
template <typename R>
|
||||
concept CBorrowedRange = CRange<R> && (CLValueReference<R> || bEnableBorrowedRange<TRemoveCVRef<R>>);
|
||||
|
||||
/**
|
||||
* A concept specifies a type is a sized range.
|
||||
* Indicates the expression 'Ranges::Num(Range)' can get the size of the range at constant time
|
||||
* without modifying the range object. A sized range may support fetched size before fetched iterator-sentinel pair
|
||||
* if the range does not satisfy the forward range, otherwise it is equality-preserving.
|
||||
*/
|
||||
template <typename R>
|
||||
concept CSizedRange = CRange<R>
|
||||
&& requires(R Range)
|
||||
{
|
||||
{ Ranges::Num(Range) } -> CConvertibleTo<size_t>;
|
||||
};
|
||||
|
||||
/** This is an example of a sized range type, indicate the traits that define a sized range type. */
|
||||
template <CInputOrOutputIterator I, CSizedSentinelFor<I> S = ISizedSentinelFor<I>>
|
||||
struct ISizedRange /* : IRange<I, S> */
|
||||
{
|
||||
// ~Begin CRange.
|
||||
|
||||
I Begin() /* const */;
|
||||
S End() /* const */;
|
||||
|
||||
// ~End CRange.
|
||||
|
||||
/**
|
||||
* Get the number of elements in the range.
|
||||
* The function is optional if the range size can be computed indirectly from the iterator-sentinel pair.
|
||||
* If this function is provided so that types that satisfy CSizedRange but do not satisfy the comments
|
||||
* requirements of CSizedRange are undefined behavior, this should be resolved by specializing bDisableSizedRange.
|
||||
* If the function is const, it means that the const ISizedRange satisfies CSizedRange.
|
||||
*/
|
||||
size_t Num() /* const */;
|
||||
};
|
||||
|
||||
// Use ISizedRange<...> represents a sized range type.
|
||||
static_assert(CSizedRange<ISizedRange<IInputOrOutputIterator<int&>>>);
|
||||
|
||||
/** A concept specifies a type is a range with an input iterator. */
|
||||
template <typename R>
|
||||
concept CInputRange = CRange<R> && CInputIterator<TRangeIterator<R>>;
|
||||
|
||||
// Use IRange<IInputIterator<...>> represents an input range type.
|
||||
static_assert(CInputRange<IRange<IInputIterator<int&>>>);
|
||||
|
||||
/** A concept specifies a type is a range with an output iterator. */
|
||||
template <typename R, typename T>
|
||||
concept COutputRange = CRange<R> && COutputIterator<TRangeIterator<R>, T>;
|
||||
|
||||
// Use IRange<IOutputIterator<...>, int> represents an output range type.
|
||||
static_assert(COutputRange<IRange<IOutputIterator<int&>>, int>);
|
||||
|
||||
/** A concept specifies a type is a range with a forward iterator. */
|
||||
template <typename R>
|
||||
concept CForwardRange = CInputRange<R> && CForwardIterator<TRangeIterator<R>>;
|
||||
|
||||
// Use IRange<IForwardIterator<...>> represents a forward range type.
|
||||
static_assert(CForwardRange<IRange<IForwardIterator<int&>>>);
|
||||
|
||||
/** A concept specifies a type is a range with a bidirectional iterator. */
|
||||
template <typename R>
|
||||
concept CBidirectionalRange = CForwardRange<R> && CBidirectionalIterator<TRangeIterator<R>>;
|
||||
|
||||
// Use IRange<IBidirectionalIterator<...>> represents a bidirectional range type.
|
||||
static_assert(CBidirectionalRange<IRange<IBidirectionalIterator<int&>>>);
|
||||
|
||||
/** A concept specifies a type is a range with a random access iterator. */
|
||||
template <typename R>
|
||||
concept CRandomAccessRange = CBidirectionalRange<R> && CRandomAccessIterator<TRangeIterator<R>>;
|
||||
|
||||
// Use IRange<IRandomAccessIterator<...>> represents a random access range type.
|
||||
static_assert(CRandomAccessRange<IRange<IRandomAccessIterator<int&>>>);
|
||||
|
||||
/** A concept specifies a type is a range with a contiguous iterator. */
|
||||
template <typename R>
|
||||
concept CContiguousRange = CRandomAccessRange<R> && CContiguousIterator<TRangeIterator<R>>
|
||||
&& requires(R& Range)
|
||||
{
|
||||
{ Ranges::GetData(Range) } -> CSameAs<TAddPointer<TRangeReference<R>>>;
|
||||
};
|
||||
|
||||
/** This is an example of a contiguous range type, indicate the traits that define a contiguous range type. */
|
||||
template <CContiguousIterator I, CSentinelFor<I> S = ISentinelFor<I>>
|
||||
struct IContiguousRange /* : IRange<I, S> */
|
||||
{
|
||||
// ~Begin CRange.
|
||||
|
||||
I Begin() /* const */;
|
||||
S End() /* const */;
|
||||
|
||||
// ~End CRange.
|
||||
|
||||
/**
|
||||
* Get the pointer to the container element storage.
|
||||
* The function is optional if the range size can be computed indirectly from the iterator.
|
||||
* If the function is provided, then the expression 'ToAddress(Ranges::Begin(Range)) == Ranges::GetData(Range)'
|
||||
* must be satisfied to always be true.
|
||||
* If the function is const, it means that the const IContiguousRange satisfies CContiguousRange.
|
||||
*/
|
||||
TIteratorPointer<I> GetData() /* const */;
|
||||
};
|
||||
|
||||
// Use IContiguousRange<...> represents a contiguous range type.
|
||||
static_assert(CContiguousRange<IContiguousRange<IContiguousIterator<int&>>>);
|
||||
|
||||
/** A concept specifies a type is a range and its iterator and sentinel types are the same. */
|
||||
template <typename R>
|
||||
concept CCommonRange = CRange<R> && CSameAs<TRangeIterator<R>, TRangeSentinel<R>>;
|
||||
|
||||
/** This is an example of a common range type, indicate the traits that define a common range type. */
|
||||
template <CForwardIterator I>
|
||||
using ICommonRange = IRange<I, I>;
|
||||
|
||||
// Use TCommonRange<...> represents a common range type.
|
||||
static_assert(CCommonRange<ICommonRange<IForwardIterator<int&>>>);
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
156
Redcraft.Utility/Source/Public/Ranges/View.h
Normal file
156
Redcraft.Utility/Source/Public/Ranges/View.h
Normal file
@@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Iterators/Utility.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/ReverseIterator.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Ranges/Utility.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** An interface class template for defining a view. Not directly instantiable. */
|
||||
template <CClass T> requires (CSameAs<T, TRemoveCV<T>>)
|
||||
class IBasicViewInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** @return The pointer to the underlying element storage. */
|
||||
NODISCARD FORCEINLINE constexpr auto GetData() requires (CContiguousIterator<TRangeIterator< T>>) { return ToAddress(Ranges::Begin(static_cast< T&>(*this))); }
|
||||
NODISCARD FORCEINLINE constexpr auto GetData() const requires (CContiguousIterator<TRangeIterator<const T>>) { return ToAddress(Ranges::Begin(static_cast<const T&>(*this))); }
|
||||
|
||||
/** @return The reverse iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE constexpr auto RBegin() requires (CBidirectionalRange< T> && CCommonRange< T>) { return MakeReverseIterator(Ranges::End (static_cast< T&>(*this))); }
|
||||
NODISCARD FORCEINLINE constexpr auto REnd() requires (CBidirectionalRange< T> && CCommonRange< T>) { return MakeReverseIterator(Ranges::Begin(static_cast< T&>(*this))); }
|
||||
NODISCARD FORCEINLINE constexpr auto RBegin() const requires (CBidirectionalRange<const T> && CCommonRange<const T>) { return MakeReverseIterator(Ranges::End (static_cast<const T&>(*this))); }
|
||||
NODISCARD FORCEINLINE constexpr auto REnd() const requires (CBidirectionalRange<const T> && CCommonRange<const T>) { return MakeReverseIterator(Ranges::Begin(static_cast<const T&>(*this))); }
|
||||
|
||||
/** @return The number of elements in the container. */
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() requires (CForwardRange< T> && CSizedSentinelFor<TRangeSentinel< T>, TRangeIterator< T>>) { T& Derived = static_cast< T&>(*this); return Ranges::End(Derived) - Ranges::Begin(Derived); }
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CForwardRange<const T> && CSizedSentinelFor<TRangeSentinel<const T>, TRangeIterator<const T>>) { const T& Derived = static_cast<const T&>(*this); return Ranges::End(Derived) - Ranges::Begin(Derived); }
|
||||
|
||||
/** @return true if the container is empty, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() requires (CSizedRange< T> || CForwardRange< T>) { T& Derived = static_cast< T&>(*this); if constexpr (CSizedRange< T>) return Ranges::Num(Derived) == 0; else return Ranges::Begin(Derived) == Ranges::End(Derived); }
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const requires (CSizedRange<const T> || CForwardRange<const T>) { const T& Derived = static_cast<const T&>(*this); if constexpr (CSizedRange<const T>) return Ranges::Num(Derived) == 0; else return Ranges::Begin(Derived) == Ranges::End(Derived); }
|
||||
|
||||
/** @return true if the container is empty, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr explicit operator bool() requires (requires { Ranges::IsEmpty(DeclVal< T&>()); }) { return !Ranges::IsEmpty(static_cast< T&>(*this)); }
|
||||
NODISCARD FORCEINLINE constexpr explicit operator bool() const requires (requires { Ranges::IsEmpty(DeclVal<const T&>()); }) { return !Ranges::IsEmpty(static_cast<const T&>(*this)); }
|
||||
|
||||
/** @return The reference to the requested element. */
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) operator[](size_t Index) requires (CRandomAccessRange< T>) { return Ranges::Begin(static_cast< T&>(*this))[Index]; }
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) operator[](size_t Index) const requires (CRandomAccessRange<const T>) { return Ranges::Begin(static_cast<const T&>(*this))[Index]; }
|
||||
|
||||
/** @return The reference to the first or last element. */
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) Front() requires (CForwardRange< T>) { return *Ranges::Begin(static_cast< T&>(*this)); }
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) Front() const requires (CForwardRange<const T>) { return *Ranges::Begin(static_cast<const T&>(*this)); }
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) Back() requires (CBidirectionalRange< T> && CCommonRange< T>) { return *Ranges::RBegin(static_cast< T&>(*this)); }
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) Back() const requires (CBidirectionalRange<const T> && CCommonRange<const T>) { return *Ranges::RBegin(static_cast<const T&>(*this)); }
|
||||
|
||||
// ~Begin ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT.
|
||||
|
||||
NODISCARD FORCEINLINE constexpr auto begin() requires (CRange< T>) { return Ranges::Begin(static_cast< T&>(*this)); }
|
||||
NODISCARD FORCEINLINE constexpr auto end() requires (CRange< T>) { return Ranges::End (static_cast< T&>(*this)); }
|
||||
NODISCARD FORCEINLINE constexpr auto begin() const requires (CRange<const T>) { return Ranges::Begin(static_cast<const T&>(*this)); }
|
||||
NODISCARD FORCEINLINE constexpr auto end() const requires (CRange<const T>) { return Ranges::End (static_cast<const T&>(*this)); }
|
||||
|
||||
// ~End ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT.
|
||||
|
||||
private:
|
||||
|
||||
FORCEINLINE constexpr IBasicViewInterface() = default;
|
||||
FORCEINLINE constexpr IBasicViewInterface(const IBasicViewInterface&) = default;
|
||||
FORCEINLINE constexpr IBasicViewInterface(IBasicViewInterface&&) = default;
|
||||
FORCEINLINE constexpr IBasicViewInterface& operator=(const IBasicViewInterface&) = default;
|
||||
FORCEINLINE constexpr IBasicViewInterface& operator=(IBasicViewInterface&&) = default;
|
||||
FORCEINLINE constexpr ~IBasicViewInterface() = default;
|
||||
|
||||
friend T;
|
||||
};
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
/**
|
||||
* A concept specifies that a range is a view, that is, it has constant time copy, move and assignment.
|
||||
* Specify, a view can be movable only but not copyable, or it can be both movable and copyable.
|
||||
*/
|
||||
template <typename V>
|
||||
concept CView = CRange<V> && CMovable<V> && CDerivedFrom<V, Ranges::IBasicViewInterface<TRemoveCVRef<V>>>;
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename T> struct TIsInitializerList : FFalse { };
|
||||
template <typename T> struct TIsInitializerList<initializer_list<T>> : FTrue { };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/** A concept specifies that a viewable range that can be converted into a view through Ranges::All. */
|
||||
template <typename R>
|
||||
concept CViewableRange = CRange<R>
|
||||
&& ((CView<TRemoveCVRef<R>> && CConstructibleFrom<TRemoveCVRef<R>, R>)
|
||||
|| (!CView<TRemoveCVRef<R>> && (CLValueReference<R> || (CMovable<TRemoveReference<R>>
|
||||
&& !NAMESPACE_PRIVATE::TIsInitializerList<TRemoveCVRef<R>>::Value))));
|
||||
|
||||
/** A concept specifies that a view uses the same iterator and sentinel type for both const and non-const views. */
|
||||
template <typename V>
|
||||
concept CSimpleView = CView<V> && CRange<const V>
|
||||
&& CSameAs<TRangeIterator<V>, TRangeIterator<const V>>
|
||||
&& CSameAs<TRangeSentinel<V>, TRangeSentinel<const V>>;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** A simple view that combines an iterator-sentinel pair into a view. */
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S = I>
|
||||
class TRangeView : public IBasicViewInterface<TRangeView<I, S>>
|
||||
{
|
||||
public:
|
||||
|
||||
FORCEINLINE constexpr TRangeView() requires (CDefaultConstructible<I>) = default;
|
||||
|
||||
FORCEINLINE constexpr TRangeView(I InFirst, S InLast) : First(MoveTemp(InFirst)), Last(InLast) { }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr I Begin() requires (!CCopyable<I>) { return MoveTemp(First); }
|
||||
NODISCARD FORCEINLINE constexpr I Begin() const requires ( CCopyable<I>) { return First; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr S End() const { return Last; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedSentinelFor<S, I>) { return Last - First; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return First == Last; }
|
||||
|
||||
private:
|
||||
|
||||
NO_UNIQUE_ADDRESS I First;
|
||||
NO_UNIQUE_ADDRESS S Last;
|
||||
|
||||
};
|
||||
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S>
|
||||
TRangeView(I, S) -> TRangeView<I, S>;
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
template <typename I, typename S>
|
||||
constexpr bool bEnableBorrowedRange<Ranges::TRangeView<I, S>> = true;
|
||||
|
||||
NAMESPACE_BEGIN(Ranges)
|
||||
|
||||
/** Creates A simple view that combines an iterator-sentinel pair. */
|
||||
template <CInputOrOutputIterator I, CSentinelFor<I> S = I>
|
||||
NODISCARD FORCEINLINE constexpr TRangeView<I, S> View(I First, S Last)
|
||||
{
|
||||
return TRangeView<I, S>(MoveTemp(First), Last);
|
||||
}
|
||||
|
||||
NAMESPACE_END(Ranges)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,822 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Templates/Optional.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
#include <locale>
|
||||
#include <climits>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
template <typename T>
|
||||
concept CCharType = CSameAs<T, char> || CSameAs<T, wchar> || CSameAs<T, u8char> || CSameAs<T, u16char> || CSameAs<T, u32char>;
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <CCharType>
|
||||
struct TLiteral;
|
||||
|
||||
template <>
|
||||
struct TLiteral<char>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr char Select(const char X, const wchar , const u8char , const u16char , const u32char ) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const char* Select(const char* X, const wchar*, const u8char*, const u16char*, const u32char*) { return X; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TLiteral<wchar>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr wchar Select(const char , const wchar X, const u8char , const u16char , const u32char ) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const wchar* Select(const char*, const wchar* X, const u8char*, const u16char*, const u32char*) { return X; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TLiteral<u8char>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr u8char Select(const char , const wchar , const u8char X, const u16char , const u32char ) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const u8char* Select(const char*, const wchar*, const u8char* X, const u16char*, const u32char*) { return X; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TLiteral<u16char>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr u16char Select(const char , const wchar , const u8char , const u16char X, const u32char ) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const u16char* Select(const char*, const wchar*, const u8char*, const u16char* X, const u32char*) { return X; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TLiteral<u32char>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr u32char Select(const char , const wchar , const u8char , const u16char , const u32char X) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const u32char* Select(const char*, const wchar*, const u8char*, const u16char*, const u32char* X) { return X; }
|
||||
};
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/** Templated literal struct to allow selection of string literals based on the character type provided, and not on compiler switches. */
|
||||
#define LITERAL(CharType, StringLiteral) NAMESPACE_PRIVATE::TLiteral<CharType>::Select(TEXT(StringLiteral), WTEXT(StringLiteral), U8TEXT(StringLiteral), U16TEXT(StringLiteral), U32TEXT(StringLiteral))
|
||||
|
||||
/** Set of utility functions operating on a single character. Implemented based on user-preferred locale and ISO 30112 "i18n". */
|
||||
template <CCharType T>
|
||||
struct TChar
|
||||
{
|
||||
using CharType = T;
|
||||
|
||||
/** The maximum number of code units required to represent a single character. if unknown, guess 1. */
|
||||
static constexpr size_t MaxCodeUnitLength =
|
||||
CSameAs<CharType, char> ? MB_LEN_MAX :
|
||||
CSameAs<CharType, wchar> ?
|
||||
PLATFORM_WINDOWS ? 2 :
|
||||
PLATFORM_LINUX ? 1 : 1 :
|
||||
CSameAs<CharType, u8char> ? 4 :
|
||||
CSameAs<CharType, u16char> ? 2 :
|
||||
CSameAs<CharType, u32char> ? 1 : 1;
|
||||
|
||||
/** Whether the character type is fixed-length. */
|
||||
static constexpr bool bIsFixedLength = MaxCodeUnitLength == 1;
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsValid(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
if ((InChar & 0b10000000) == 0b00000000) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char> || CSameAs<CharType, u32char>)
|
||||
{
|
||||
if (InChar >= 0xD800 && InChar <= 0xDBFF) return false;
|
||||
if (InChar >= 0xDC00 && InChar <= 0xDFFF) return false;
|
||||
|
||||
return InChar <= 0x10FFFF;
|
||||
}
|
||||
|
||||
// Windows uses UTF-16 encoding for wchar.
|
||||
else if constexpr (PLATFORM_WINDOWS && (CSameAs<CharType, wchar>))
|
||||
{
|
||||
return TChar::IsValid(static_cast<u16char>(InChar));
|
||||
}
|
||||
|
||||
// Linux uses UTF-32 encoding for wchar.
|
||||
else if constexpr (PLATFORM_LINUX && (CSameAs<CharType, wchar>))
|
||||
{
|
||||
return TChar::IsValid(static_cast<u32char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsNonch(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
if (InChar >= U16TEXT('\uFDD0') && InChar <= U16TEXT('\uFDEF')) return true;
|
||||
|
||||
if (InChar == U16TEXT('\uFFFE')) return true;
|
||||
if (InChar == U16TEXT('\uFFFF')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
if (InChar >= U32TEXT('\uFDD0') && InChar <= U32TEXT('\uFDEF')) return true;
|
||||
|
||||
if ((InChar & 0x0000FFFE) == 0x0000FFFE) return TChar::IsValid(InChar);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Windows uses UTF-16 encoding for wchar.
|
||||
else if constexpr (PLATFORM_WINDOWS && (CSameAs<CharType, wchar>))
|
||||
{
|
||||
return TChar::IsNonch(static_cast<u16char>(InChar));
|
||||
}
|
||||
|
||||
// Linux uses UTF-32 encoding for wchar.
|
||||
else if constexpr (PLATFORM_LINUX && (CSameAs<CharType, wchar>))
|
||||
{
|
||||
return TChar::IsNonch(static_cast<u32char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsAlnum(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isalnum(InChar, Loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TChar::IsAlpha(InChar) || TChar::IsDigit(InChar);
|
||||
}
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsAlpha(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isalpha(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0041>..<U005A>;<U0061>..<U007A>;
|
||||
*/
|
||||
if ((InChar >= U8TEXT('\u0041') && InChar <= U8TEXT('\u005A')) ||
|
||||
(InChar >= U8TEXT('\u0061') && InChar <= U8TEXT('\u007A')))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char> || CSameAs<CharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= LITERAL(CharType, '\u007F'), TEXT("TChar::IsAlpha() only supports basic latin block."));
|
||||
|
||||
if (InChar > LITERAL(CharType, '\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsAlpha(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsLower(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::islower(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0061>..<U007A>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0061') && InChar <= U8TEXT('\u007A')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsLower() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsLower(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsLower() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsLower(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsUpper(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isupper(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0041>..<U005A>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0041') && InChar <= U8TEXT('\u005A')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsUpper() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsUpper(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsUpper() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsUpper(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsDigit(CharType InChar)
|
||||
{
|
||||
/* <U0030>..<U0039>; */
|
||||
return (InChar >= LITERAL(CharType, '0') && InChar <= LITERAL(CharType, '9'));
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsDigit(CharType InChar, int Base)
|
||||
{
|
||||
checkf(Base >= 2 && Base <= 36, TEXT("Base must be in the range [2, 36]."));
|
||||
|
||||
/* <U0030>..<U0039>;<U0041>..<U0046>;<U0061>..<U0066>; */
|
||||
return
|
||||
(InChar >= LITERAL(CharType, '0') && InChar < LITERAL(CharType, '0') + Base ) ||
|
||||
(InChar >= LITERAL(CharType, 'a') && InChar < LITERAL(CharType, 'a') + Base - 10) ||
|
||||
(InChar >= LITERAL(CharType, 'A') && InChar < LITERAL(CharType, 'A') + Base - 10);
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsCntrl(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::iscntrl(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/* <U0000>..<U001F>;<U007F>; */
|
||||
return (InChar >= U8TEXT('\u0000') && InChar <= U8TEXT('\u001F')) || InChar == U8TEXT('\u007F');
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
/* <U0000>..<U001F>;<U007F>..<U009F>;<U2028>;<U2029>; */
|
||||
return
|
||||
(InChar >= U16TEXT('\u0000') && InChar <= U16TEXT('\u001F')) ||
|
||||
(InChar >= U16TEXT('\u007F') && InChar <= U16TEXT('\u009F')) ||
|
||||
(InChar == U16TEXT('\u2028') || InChar == U16TEXT('\u2029'));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
/* <U0000>..<U001F>;<U007F>..<U009F>;<U2028>;<U2029>; */
|
||||
return
|
||||
(InChar >= U32TEXT('\u0000') && InChar <= U32TEXT('\u001F')) ||
|
||||
(InChar >= U32TEXT('\u007F') && InChar <= U32TEXT('\u009F')) ||
|
||||
(InChar == U32TEXT('\u2028') || InChar == U32TEXT('\u2029'));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsGraph(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isgraph(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0021>..<U007E>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0021') && InChar <= U8TEXT('\u007E')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsGraph() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsGraph(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsGraph() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsGraph(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsSpace(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isspace(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* ISO/IEC 6429
|
||||
* <U0009>..<U000D>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0009') && InChar <= U8TEXT('\u000D')) return true;
|
||||
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0020>;
|
||||
*/
|
||||
if (InChar == U8TEXT('\u0020')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
/*
|
||||
* ISO/IEC 6429
|
||||
* <U0009>..<U000D>;
|
||||
*/
|
||||
if (InChar >= U16TEXT('\u0009') && InChar <= U16TEXT('\u000D')) return true;
|
||||
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0020>;
|
||||
*/
|
||||
if (InChar == U16TEXT('\u0020')) return true;
|
||||
|
||||
/*
|
||||
* OGHAM
|
||||
* <U1680>;
|
||||
*/
|
||||
if (InChar == U16TEXT('\u1680')) return true;
|
||||
|
||||
/*
|
||||
* MONGOL
|
||||
* <U180E>;
|
||||
*/
|
||||
if (InChar == U16TEXT('\u180E')) return true;
|
||||
|
||||
/*
|
||||
* GENERAL PUNCTUATION
|
||||
* <U2000>..<U2006>;<U2008>..<U200A>;<U2028>;<U2029>;<U205F>;
|
||||
*/
|
||||
if ((InChar >= U16TEXT('\u2000') && InChar <= U16TEXT('\u2006')) ||
|
||||
(InChar >= U16TEXT('\u2008') && InChar <= U16TEXT('\u200A')) ||
|
||||
(InChar == U16TEXT('\u2028') || InChar == U16TEXT('\u2029')) ||
|
||||
(InChar == U16TEXT('\u205F')))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* CJK SYMBOLS AND PUNCTUATION, HIRAGANA
|
||||
* <U3000>;
|
||||
*/
|
||||
if (InChar == U16TEXT('\u3000')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
/*
|
||||
* ISO/IEC 6429
|
||||
* <U0009>..<U000D>;
|
||||
*/
|
||||
if (InChar >= U32TEXT('\u0009') && InChar <= U32TEXT('\u000D')) return true;
|
||||
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0020>;
|
||||
*/
|
||||
if (InChar == U32TEXT('\u0020')) return true;
|
||||
|
||||
/*
|
||||
* OGHAM
|
||||
* <U1680>;
|
||||
*/
|
||||
if (InChar == U32TEXT('\u1680')) return true;
|
||||
|
||||
/*
|
||||
* MONGOL
|
||||
* <U180E>;
|
||||
*/
|
||||
if (InChar == U32TEXT('\u180E')) return true;
|
||||
|
||||
/*
|
||||
* GENERAL PUNCTUATION
|
||||
* <U2000>..<U2006>;<U2008>..<U200A>;<U2028>;<U2029>;<U205F>;
|
||||
*/
|
||||
if ((InChar >= U32TEXT('\u2000') && InChar <= U32TEXT('\u2006')) ||
|
||||
(InChar >= U32TEXT('\u2008') && InChar <= U32TEXT('\u200A')) ||
|
||||
(InChar == U32TEXT('\u2028') || InChar == U32TEXT('\u2029')) ||
|
||||
(InChar == U32TEXT('\u205F')))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* CJK SYMBOLS AND PUNCTUATION, HIRAGANA
|
||||
* <U3000>;
|
||||
*/
|
||||
if (InChar == U32TEXT('\u3000')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsBlank(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isblank(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/* <U0009>;<U0020>; */
|
||||
return InChar == U8TEXT('\u0009') || InChar == U8TEXT('\u0020');
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
/* <U0009>;<U0020>;<U1680>;<U180E>;<U2000>..<U2006>;<U2008>..<U200A>;<U205F>;<U3000>; */
|
||||
return
|
||||
(InChar >= U16TEXT('\u2000') && InChar <= U16TEXT('\u2006')) ||
|
||||
(InChar == U16TEXT('\u0009') || InChar == U16TEXT('\u0020')) ||
|
||||
(InChar == U16TEXT('\u1680') || InChar == U16TEXT('\u180E')) ||
|
||||
(InChar == U16TEXT('\u2008') || InChar == U16TEXT('\u200A')) ||
|
||||
(InChar == U16TEXT('\u205F') || InChar == U16TEXT('\u3000'));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
/* <U0009>;<U0020>;<U1680>;<U180E>;<U2000>..<U2006>;<U2008>..<U200A>;<U205F>;<U3000>; */
|
||||
return
|
||||
(InChar >= U32TEXT('\u2000') && InChar <= U32TEXT('\u2006')) ||
|
||||
(InChar == U32TEXT('\u0009') || InChar == U32TEXT('\u0020')) ||
|
||||
(InChar == U32TEXT('\u1680') || InChar == U32TEXT('\u180E')) ||
|
||||
(InChar == U32TEXT('\u2008') || InChar == U32TEXT('\u200A')) ||
|
||||
(InChar == U32TEXT('\u205F') || InChar == U32TEXT('\u3000'));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsPrint(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isprint(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0020>..<U007E>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0020') && InChar <= U8TEXT('\u007E')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsPrint() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsPrint(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsPrint() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsPrint(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsPunct(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::ispunct(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0021>..<U002F>;<U003A>..<U0040>;<U005B>..<U0060>;<U007B>..<U007E>;
|
||||
*/
|
||||
if ((InChar >= U8TEXT('\u0021') && InChar <= U8TEXT('\u002F')) ||
|
||||
(InChar >= U8TEXT('\u003A') && InChar <= U8TEXT('\u0040')) ||
|
||||
(InChar >= U8TEXT('\u005B') && InChar <= U8TEXT('\u0060')) ||
|
||||
(InChar >= U8TEXT('\u007B') && InChar <= U8TEXT('\u007E')))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsPunct() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsPunct(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsPunct() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsPunct(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr CharType ToLower(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return static_cast<CharType>(NAMESPACE_STD::tolower(InChar, Loc));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* (<U0041>,<U0061>);(<U0042>,<U0062>);(<U0043>,<U0063>);(<U0044>,<U0064>);
|
||||
* (<U0045>,<U0065>);(<U0046>,<U0066>);(<U0047>,<U0067>);(<U0048>,<U0068>);
|
||||
* (<U0049>,<U0069>);(<U004A>,<U006A>);(<U004B>,<U006B>);(<U004C>,<U006C>);
|
||||
* (<U004D>,<U006D>);(<U004E>,<U006E>);(<U004F>,<U006F>);(<U0050>,<U0070>);
|
||||
* (<U0051>,<U0071>);(<U0052>,<U0072>);(<U0053>,<U0073>);(<U0054>,<U0074>);
|
||||
* (<U0055>,<U0075>);(<U0056>,<U0076>);(<U0057>,<U0077>);(<U0058>,<U0078>);
|
||||
* (<U0059>,<U0079>);(<U005A>,<U007A>);
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0041') && InChar <= U8TEXT('\u005A')) return InChar + U8TEXT('\u0020');
|
||||
|
||||
return InChar;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::ToLower() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return static_cast<u16char>(TChar<u8char>::ToLower(static_cast<u8char>(InChar)));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::ToLower() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return static_cast<u16char>(TChar<u8char>::ToLower(static_cast<u8char>(InChar)));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return InChar;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr CharType ToUpper(CharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char> || CSameAs<CharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return static_cast<CharType>(NAMESPACE_STD::toupper(InChar, Loc));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* (<U0061>,<U0041>);(<U0062>,<U0042>);(<U0063>,<U0043>);(<U0064>,<U0044>);/
|
||||
* (<U0065>,<U0045>);(<U0066>,<U0046>);(<U0067>,<U0047>);(<U0068>,<U0048>);/
|
||||
* (<U0069>,<U0049>);(<U006A>,<U004A>);(<U006B>,<U004B>);(<U006C>,<U004C>);/
|
||||
* (<U006D>,<U004D>);(<U006E>,<U004E>);(<U006F>,<U004F>);(<U0070>,<U0050>);/
|
||||
* (<U0071>,<U0051>);(<U0072>,<U0052>);(<U0073>,<U0053>);(<U0074>,<U0054>);/
|
||||
* (<U0075>,<U0055>);(<U0076>,<U0056>);(<U0077>,<U0057>);(<U0078>,<U0058>);/
|
||||
* (<U0079>,<U0059>);(<U007A>,<U005A>);
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0061') && InChar <= U8TEXT('\u007A')) return InChar - U8TEXT('\u0020');
|
||||
|
||||
return InChar;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::ToUpper() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return static_cast<u16char>(TChar<u8char>::ToUpper(static_cast<u8char>(InChar)));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<CharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::ToUpper() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return static_cast<u16char>(TChar<u8char>::ToUpper(static_cast<u8char>(InChar)));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(CharType) == -1, "Unsupported character type");
|
||||
|
||||
return InChar;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr TOptional<unsigned> ToDigit(CharType InChar)
|
||||
{
|
||||
switch (InChar)
|
||||
{
|
||||
case LITERAL(CharType, '0'): return 0;
|
||||
case LITERAL(CharType, '1'): return 1;
|
||||
case LITERAL(CharType, '2'): return 2;
|
||||
case LITERAL(CharType, '3'): return 3;
|
||||
case LITERAL(CharType, '4'): return 4;
|
||||
case LITERAL(CharType, '5'): return 5;
|
||||
case LITERAL(CharType, '6'): return 6;
|
||||
case LITERAL(CharType, '7'): return 7;
|
||||
case LITERAL(CharType, '8'): return 8;
|
||||
case LITERAL(CharType, '9'): return 9;
|
||||
case LITERAL(CharType, 'a'): return 10;
|
||||
case LITERAL(CharType, 'b'): return 11;
|
||||
case LITERAL(CharType, 'c'): return 12;
|
||||
case LITERAL(CharType, 'd'): return 13;
|
||||
case LITERAL(CharType, 'e'): return 14;
|
||||
case LITERAL(CharType, 'f'): return 15;
|
||||
case LITERAL(CharType, 'g'): return 16;
|
||||
case LITERAL(CharType, 'h'): return 17;
|
||||
case LITERAL(CharType, 'i'): return 18;
|
||||
case LITERAL(CharType, 'j'): return 19;
|
||||
case LITERAL(CharType, 'k'): return 20;
|
||||
case LITERAL(CharType, 'l'): return 21;
|
||||
case LITERAL(CharType, 'm'): return 22;
|
||||
case LITERAL(CharType, 'n'): return 23;
|
||||
case LITERAL(CharType, 'o'): return 24;
|
||||
case LITERAL(CharType, 'p'): return 25;
|
||||
case LITERAL(CharType, 'q'): return 26;
|
||||
case LITERAL(CharType, 'r'): return 27;
|
||||
case LITERAL(CharType, 's'): return 28;
|
||||
case LITERAL(CharType, 't'): return 29;
|
||||
case LITERAL(CharType, 'u'): return 30;
|
||||
case LITERAL(CharType, 'v'): return 31;
|
||||
case LITERAL(CharType, 'w'): return 32;
|
||||
case LITERAL(CharType, 'x'): return 33;
|
||||
case LITERAL(CharType, 'y'): return 34;
|
||||
case LITERAL(CharType, 'z'): return 35;
|
||||
case LITERAL(CharType, 'A'): return 10;
|
||||
case LITERAL(CharType, 'B'): return 11;
|
||||
case LITERAL(CharType, 'C'): return 12;
|
||||
case LITERAL(CharType, 'D'): return 13;
|
||||
case LITERAL(CharType, 'E'): return 14;
|
||||
case LITERAL(CharType, 'F'): return 15;
|
||||
case LITERAL(CharType, 'G'): return 16;
|
||||
case LITERAL(CharType, 'H'): return 17;
|
||||
case LITERAL(CharType, 'I'): return 18;
|
||||
case LITERAL(CharType, 'J'): return 19;
|
||||
case LITERAL(CharType, 'K'): return 20;
|
||||
case LITERAL(CharType, 'L'): return 21;
|
||||
case LITERAL(CharType, 'M'): return 22;
|
||||
case LITERAL(CharType, 'N'): return 23;
|
||||
case LITERAL(CharType, 'O'): return 24;
|
||||
case LITERAL(CharType, 'P'): return 25;
|
||||
case LITERAL(CharType, 'Q'): return 26;
|
||||
case LITERAL(CharType, 'R'): return 27;
|
||||
case LITERAL(CharType, 'S'): return 28;
|
||||
case LITERAL(CharType, 'T'): return 29;
|
||||
case LITERAL(CharType, 'U'): return 30;
|
||||
case LITERAL(CharType, 'V'): return 31;
|
||||
case LITERAL(CharType, 'W'): return 32;
|
||||
case LITERAL(CharType, 'X'): return 33;
|
||||
case LITERAL(CharType, 'Y'): return 34;
|
||||
case LITERAL(CharType, 'Z'): return 35;
|
||||
default: return Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr TOptional<CharType> FromDigit(int InDigit)
|
||||
{
|
||||
if (InDigit < 0 || InDigit > 35) return Invalid;
|
||||
|
||||
return LITERAL(CharType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")[InDigit];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using FChar = TChar<char>;
|
||||
using FWChar = TChar<wchar>;
|
||||
using FU8Char = TChar<u8char>;
|
||||
using FU16Char = TChar<u16char>;
|
||||
using FU32Char = TChar<u32char>;
|
||||
using FUnicodeChar = TChar<unicodechar>;
|
||||
|
||||
static_assert(FUnicodeChar::bIsFixedLength);
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,581 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// NOTE: This file is not intended to be included directly, it is included by 'String/String.h'.
|
||||
|
||||
#include "Templates/Tuple.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4146)
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
// NOTE: These functions are used to format an object to a string and parse a string to an object.
|
||||
// If the user-defined overloads a function with the 'Fmt' parameter, fill-and-align needs to be handled.
|
||||
// The formatting function should produce a string that can be parsed by the parsing function, if the parsing function exists.
|
||||
|
||||
// NOTE: These functions are recommended for debug programs.
|
||||
|
||||
#define LEFT_BRACE LITERAL(T, '{')
|
||||
#define RIGHT_BRACE LITERAL(T, '}')
|
||||
|
||||
#define ESCAPE_LEFT_BRACE TStringView(LITERAL(T, "<[{"))
|
||||
#define ESCAPE_RIGHT_BRACE TStringView(LITERAL(T, "}]>"))
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <CCharType T, bool bIsFormat>
|
||||
struct TStringHelper
|
||||
{
|
||||
FORCEINLINE static bool FormatObject(auto& Result, TStringView<T> Fmt, auto& Object) requires (bIsFormat)
|
||||
{
|
||||
using U = TRemoveCVRef<decltype(Object)>;
|
||||
|
||||
if constexpr (!CConst<TRemoveReference<decltype(Object)>>)
|
||||
{
|
||||
checkf(false, TEXT("Unsafe formatting for a variable that is non-const."));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (Fmt.IsEmpty())
|
||||
{
|
||||
if constexpr (CArithmetic<U>)
|
||||
{
|
||||
constexpr const T* DigitToChar = LITERAL(T, "9876543210123456789");
|
||||
constexpr size_t ZeroIndex = 9;
|
||||
|
||||
if constexpr (CSameAs<U, bool>)
|
||||
{
|
||||
Result += Object ? LITERAL(T, "True") : LITERAL(T, "False");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
else if constexpr (CIntegral<U>)
|
||||
{
|
||||
U Value = Object;
|
||||
|
||||
const bool bNegative = Object < 0;
|
||||
|
||||
constexpr size_t BufferSize = 32;
|
||||
|
||||
T Buffer[BufferSize];
|
||||
|
||||
size_t Index = BufferSize;
|
||||
|
||||
do Buffer[--Index] = DigitToChar[ZeroIndex + Value % 10]; while (Value /= 10);
|
||||
|
||||
if (bNegative) Buffer[--Index] = LITERAL(T, '-');
|
||||
|
||||
const T* Begin = Buffer + Index;
|
||||
const T* End = Buffer + BufferSize;
|
||||
|
||||
Result.Append(Begin, End);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
else if constexpr (CFloatingPoint<U>)
|
||||
{
|
||||
if (NAMESPACE_STD::isinf(Object) && !NAMESPACE_STD::signbit(Object)) { Result += LITERAL(T, "Infinity"); return true; }
|
||||
if (NAMESPACE_STD::isinf(Object) && NAMESPACE_STD::signbit(Object)) { Result += LITERAL(T, "-Infinity"); return true; }
|
||||
|
||||
if (NAMESPACE_STD::isnan(Object) && !NAMESPACE_STD::signbit(Object)) { Result += LITERAL(T, "NaN"); return true; }
|
||||
if (NAMESPACE_STD::isnan(Object) && NAMESPACE_STD::signbit(Object)) { Result += LITERAL(T, "-NaN"); return true; }
|
||||
|
||||
U Value = NAMESPACE_STD::round(Object * static_cast<U>(1e6));
|
||||
|
||||
const bool bNegative = NAMESPACE_STD::signbit(Object);
|
||||
|
||||
TString<T, TInlineAllocator<32>> Buffer;
|
||||
|
||||
for (size_t Index = 0; Index <= 6 || static_cast<signed>(Value) != 0; ++Index)
|
||||
{
|
||||
Buffer += DigitToChar[ZeroIndex + static_cast<signed>(NAMESPACE_STD::fmod(Value, 10))];
|
||||
|
||||
if (Index == 5) Buffer += LITERAL(T, '.');
|
||||
|
||||
Value /= 10;
|
||||
}
|
||||
|
||||
if (bNegative) Buffer += LITERAL(T, '-');
|
||||
|
||||
Result.Append(Buffer.RBegin(), Buffer.REnd());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
else static_assert(sizeof(U) == -1, "Unsupported arithmetic type");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FORCEINLINE static bool ParseObject(TStringView<T>& View, TStringView<T> Fmt, auto& Object) requires (!bIsFormat)
|
||||
{
|
||||
using U = TRemoveCVRef<decltype(Object)>;
|
||||
|
||||
if constexpr (CConst<TRemoveReference<decltype(Object)>>)
|
||||
{
|
||||
checkf(false, TEXT("Cannot assign to a variable that is const."));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CArithmetic<U>)
|
||||
{
|
||||
checkf(Fmt.IsEmpty(), TEXT("Formatted parsing of arithmetic types not implemented."));
|
||||
|
||||
// Skip leading white spaces.
|
||||
while (!View.IsEmpty() && TChar<T>::IsSpace(View.Front())) View.RemovePrefix(1);
|
||||
|
||||
if (View.IsEmpty()) return false;
|
||||
|
||||
bool bNegative = false;
|
||||
|
||||
// Handle optional sign.
|
||||
if (View.Front() == LITERAL(T, '+'))
|
||||
{
|
||||
View.RemovePrefix(1);
|
||||
}
|
||||
else if (View.Front() == LITERAL(T, '-'))
|
||||
{
|
||||
bNegative = true;
|
||||
View.RemovePrefix(1);
|
||||
}
|
||||
|
||||
// Handle boolean conversion.
|
||||
else if constexpr (CSameAs<U, bool>)
|
||||
{
|
||||
bool bIsTrue = false;
|
||||
bool bIsFalse = false;
|
||||
|
||||
bIsTrue |= View.StartsWith(LITERAL(T, "true")) || View.StartsWith(LITERAL(T, "True")) || View.StartsWith(LITERAL(T, "TRUE"));
|
||||
bIsFalse |= View.StartsWith(LITERAL(T, "false")) || View.StartsWith(LITERAL(T, "False")) || View.StartsWith(LITERAL(T, "FALSE"));
|
||||
|
||||
if (bIsTrue) { View.RemovePrefix(4); Object = true; return true; }
|
||||
if (bIsFalse) { View.RemovePrefix(5); Object = false; return true; }
|
||||
}
|
||||
|
||||
// Handle floating-point conversion.
|
||||
if constexpr (CFloatingPoint<U>)
|
||||
{
|
||||
bool bIsInfinity = false;
|
||||
bool bIsNaN = false;
|
||||
|
||||
bIsInfinity |= View.StartsWith(LITERAL(T, "infinity")) || View.StartsWith(LITERAL(T, "Infinity")) || View.StartsWith(LITERAL(T, "INFINITY"));
|
||||
bIsNaN |= View.StartsWith(LITERAL(T, "nan")) || View.StartsWith(LITERAL(T, "NaN")) || View.StartsWith(LITERAL(T, "NAN"));
|
||||
|
||||
if (bIsInfinity) { View.RemovePrefix(8); Object = bNegative ? -NAMESPACE_STD::numeric_limits<U>::infinity() : NAMESPACE_STD::numeric_limits<U>::infinity(); return true; }
|
||||
if (bIsNaN) { View.RemovePrefix(3); Object = bNegative ? -NAMESPACE_STD::numeric_limits<U>::quiet_NaN() : NAMESPACE_STD::numeric_limits<U>::quiet_NaN(); return true; }
|
||||
}
|
||||
|
||||
unsigned Base = 0;
|
||||
|
||||
// Auto detect base.
|
||||
{
|
||||
if (View.Num() >= 2 && View.Front() == LITERAL(T, '0'))
|
||||
{
|
||||
if (View[1] == LITERAL(T, 'x') || View[1] == LITERAL(T, 'X'))
|
||||
{
|
||||
Base = 16;
|
||||
View.RemovePrefix(2);
|
||||
}
|
||||
else if (View[1] == LITERAL(T, 'b') || View[1] == LITERAL(T, 'B'))
|
||||
{
|
||||
Base = 2;
|
||||
View.RemovePrefix(2);
|
||||
}
|
||||
else if (TChar<T>::IsDigit(View.Front(), 8))
|
||||
{
|
||||
Base = 8;
|
||||
View.RemovePrefix(1);
|
||||
}
|
||||
else Base = 10;
|
||||
}
|
||||
else Base = 10;
|
||||
}
|
||||
|
||||
// Parse the number.
|
||||
auto ToNumber = [&View]<typename NumberType>(TInPlaceType<NumberType>, unsigned Base, NumberType Init = static_cast<NumberType>(0)) -> NumberType
|
||||
{
|
||||
NumberType Result = Init;
|
||||
|
||||
while (!View.IsEmpty() && (Base == 10 ? TChar<T>::IsDigit(View.Front()) : TChar<T>::IsDigit(View.Front(), Base)))
|
||||
{
|
||||
Result = static_cast<NumberType>(Result * Base + *TChar<T>::ToDigit(View.Front()));
|
||||
|
||||
View.RemovePrefix(1);
|
||||
}
|
||||
|
||||
return Result;
|
||||
};
|
||||
|
||||
// Handle integral conversion.
|
||||
if constexpr (CIntegral<U>)
|
||||
{
|
||||
using UnsignedU = TMakeUnsigned<TConditional<!CSameAs<U, bool>, U, int>>;
|
||||
|
||||
if (View.IsEmpty()) return false;
|
||||
|
||||
// The integral number must start with a digit.
|
||||
if (!TChar<T>::IsDigit(View.Front(), Base)) return false;
|
||||
|
||||
// Parse the integral number.
|
||||
UnsignedU Number = ToNumber(InPlaceType<UnsignedU>, Base);
|
||||
|
||||
Object = static_cast<U>(bNegative ? -Number : Number);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle floating-point conversion.
|
||||
else if constexpr (CFloatingPoint<U>)
|
||||
{
|
||||
if (View.IsEmpty()) return false;
|
||||
|
||||
// The floating-point number must start with a digit or a dot.
|
||||
if (!(TChar<T>::IsDigit(View.Front(), Base) || View.Front() == LITERAL(T, '.'))) return false;
|
||||
|
||||
size_t IntegralBeginNum = View.Num();
|
||||
|
||||
// Parse the integral number.
|
||||
Object = ToNumber(InPlaceType<U>, Base);
|
||||
|
||||
size_t IntegralLength = IntegralBeginNum - View.Num();
|
||||
|
||||
// Parse the fractional number.
|
||||
if (!View.IsEmpty() && View.Front() == LITERAL(T, '.'))
|
||||
{
|
||||
View.RemovePrefix(1);
|
||||
|
||||
U InvBase = 1 / static_cast<U>(Base);
|
||||
|
||||
size_t FractionBeginNum = View.Num();
|
||||
|
||||
Object = ToNumber(InPlaceType<U>, Base, Object);
|
||||
|
||||
size_t FractionLength = FractionBeginNum - View.Num();
|
||||
|
||||
Object *= NAMESPACE_STD::pow(InvBase, static_cast<U>(FractionLength));
|
||||
}
|
||||
else if (IntegralLength == 0) return false;
|
||||
|
||||
// For floating point numbers apply the symbols directly
|
||||
Object = static_cast<U>(bNegative ? -Object : Object);
|
||||
|
||||
if (View.IsEmpty()) return true;
|
||||
|
||||
if (Base != 10 && Base != 16) return true;
|
||||
|
||||
bool bHasExponent = false;
|
||||
|
||||
bHasExponent |= Base == 10 && View.Front() == LITERAL(T, 'e');
|
||||
bHasExponent |= Base == 10 && View.Front() == LITERAL(T, 'E');
|
||||
bHasExponent |= Base == 16 && View.Front() == LITERAL(T, 'p');
|
||||
bHasExponent |= Base == 16 && View.Front() == LITERAL(T, 'P');
|
||||
|
||||
if (!bHasExponent) return true;
|
||||
|
||||
View.RemovePrefix(1);
|
||||
|
||||
if (View.IsEmpty()) return false;
|
||||
|
||||
// Parse the exponent number.
|
||||
{
|
||||
bool bNegativeExponent = false;
|
||||
|
||||
if (View.Front() == LITERAL(T, '+'))
|
||||
{
|
||||
View.RemovePrefix(1);
|
||||
}
|
||||
else if (View.Front() == LITERAL(T, '-'))
|
||||
{
|
||||
bNegativeExponent = true;
|
||||
View.RemovePrefix(1);
|
||||
}
|
||||
|
||||
// The exponent number must start with a digit.
|
||||
if (!TChar<T>::IsDigit(View.Front())) return false;
|
||||
|
||||
U Exponent = ToNumber(InPlaceType<U>, 10);
|
||||
|
||||
Exponent = bNegativeExponent ? -Exponent : Exponent;
|
||||
|
||||
Object *= static_cast<U>(NAMESPACE_STD::pow(static_cast<U>(Base == 16 ? 2 : 10), Exponent));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
else static_assert(sizeof(U) == -1, "Unsupported arithmetic type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FORCEINLINE static size_t Do(auto& Result, TStringView<T> Fmt, auto ArgsTuple)
|
||||
{
|
||||
size_t FormattedObjectNum = 0;
|
||||
|
||||
size_t ArgsIndex = 0;
|
||||
|
||||
auto ParseFormat = [&FormattedObjectNum, &ArgsIndex, ArgsTuple](auto& Self, auto& String, TStringView<T>& Fmt) -> bool
|
||||
{
|
||||
bool bIsFullyFormatted = true;
|
||||
|
||||
while (!Fmt.IsEmpty())
|
||||
{
|
||||
if (Fmt.StartsWith(ESCAPE_LEFT_BRACE))
|
||||
{
|
||||
Fmt.RemovePrefix(ESCAPE_LEFT_BRACE.Num());
|
||||
|
||||
if constexpr (!bIsFormat)
|
||||
{
|
||||
if (!String.StartsWith(LEFT_BRACE)) return false;
|
||||
|
||||
String.RemovePrefix(1);
|
||||
}
|
||||
else String += LEFT_BRACE;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Fmt.StartsWith(ESCAPE_RIGHT_BRACE))
|
||||
{
|
||||
Fmt.RemovePrefix(ESCAPE_RIGHT_BRACE.Num());
|
||||
|
||||
if constexpr (!bIsFormat)
|
||||
{
|
||||
if (!String.StartsWith(RIGHT_BRACE)) return false;
|
||||
|
||||
String.RemovePrefix(1);
|
||||
}
|
||||
else String += RIGHT_BRACE;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Fmt.StartsWith(LEFT_BRACE))
|
||||
{
|
||||
Fmt.RemovePrefix(1);
|
||||
|
||||
int SubplaceholderNum = -1;
|
||||
|
||||
size_t PlaceholderBegin = -1;
|
||||
size_t PlaceholderEnd = -1;
|
||||
|
||||
// Find the end of the placeholder.
|
||||
do
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
PlaceholderBegin = Fmt.FindFirstOf(LEFT_BRACE, PlaceholderBegin + 1);
|
||||
|
||||
if (PlaceholderBegin == INDEX_NONE) break;
|
||||
|
||||
if (Fmt.First(PlaceholderBegin + 1).EndsWith(ESCAPE_LEFT_BRACE))
|
||||
{
|
||||
++PlaceholderBegin;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
PlaceholderEnd = Fmt.FindFirstOf(RIGHT_BRACE, PlaceholderEnd + 1);
|
||||
|
||||
if (PlaceholderEnd == INDEX_NONE) break;
|
||||
|
||||
if (Fmt.Substr(PlaceholderEnd).StartsWith(ESCAPE_RIGHT_BRACE))
|
||||
{
|
||||
++PlaceholderEnd;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
if (PlaceholderEnd == INDEX_NONE)
|
||||
{
|
||||
checkf(false, TEXT("Unmatched '{' in format string."));
|
||||
|
||||
if constexpr (bIsFormat) String += Fmt;
|
||||
|
||||
Fmt = LITERAL(T, "");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
++SubplaceholderNum;
|
||||
}
|
||||
while (PlaceholderBegin != INDEX_NONE && PlaceholderBegin < PlaceholderEnd);
|
||||
|
||||
TStringView Subfmt = Fmt.First(PlaceholderEnd);
|
||||
|
||||
Fmt.RemovePrefix(PlaceholderEnd + 1);
|
||||
|
||||
bool bIsSuccessful = true;
|
||||
|
||||
// The subformat string size are usually smaller than 16.
|
||||
TString<T, TInlineAllocator<16>> FormattedSubfmt;
|
||||
|
||||
// Recursively format the subformat string.
|
||||
if (SubplaceholderNum > 0)
|
||||
{
|
||||
if constexpr (bIsFormat) bIsSuccessful = Self(Self, FormattedSubfmt, Subfmt);
|
||||
|
||||
else bIsSuccessful = TStringHelper<T, true>::Do(FormattedSubfmt, Subfmt, ArgsTuple);
|
||||
|
||||
Subfmt = FormattedSubfmt;
|
||||
}
|
||||
|
||||
if (bIsSuccessful)
|
||||
{
|
||||
// Find the placeholder index delimiter.
|
||||
size_t IndexLength = Subfmt.FindFirstOf(LITERAL(T, ':'));
|
||||
|
||||
if (IndexLength == INDEX_NONE) IndexLength = Subfmt.Num();
|
||||
|
||||
TStringView PlaceholderIndex = Subfmt.First(IndexLength);
|
||||
TStringView PlaceholderSubfmt = IndexLength != Subfmt.Num() ? Subfmt.Substr(IndexLength + 1) : LITERAL(T, "");
|
||||
|
||||
size_t Index;
|
||||
|
||||
if (IndexLength != 0)
|
||||
{
|
||||
if (PlaceholderIndex.FindFirstNotOf(LITERAL(T, "0123456789")) != INDEX_NONE)
|
||||
{
|
||||
checkf(false, TEXT("Invalid placeholder index."));
|
||||
|
||||
if constexpr (bIsFormat)
|
||||
{
|
||||
String += LEFT_BRACE;
|
||||
String += Subfmt;
|
||||
String += RIGHT_BRACE;
|
||||
|
||||
bIsFullyFormatted = false;
|
||||
}
|
||||
else return false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
verify(PlaceholderIndex.Parse(LITERAL(T, "{}"), Index) == 1);
|
||||
}
|
||||
else Index = ArgsIndex++;
|
||||
|
||||
checkf(Index < ArgsTuple.Num(), TEXT("Argument not found."));
|
||||
|
||||
bIsSuccessful = ArgsTuple.Visit(
|
||||
[&String, Subfmt = PlaceholderSubfmt](auto& Object) mutable
|
||||
{
|
||||
if (Subfmt.StartsWith(LITERAL(T, ':'))) Subfmt.RemovePrefix(1);
|
||||
|
||||
if constexpr (bIsFormat) return TStringHelper::FormatObject(String, Subfmt, Object);
|
||||
|
||||
else return TStringHelper::ParseObject(String, Subfmt, Object);
|
||||
},
|
||||
Index
|
||||
);
|
||||
}
|
||||
|
||||
if (!bIsSuccessful)
|
||||
{
|
||||
if constexpr (bIsFormat)
|
||||
{
|
||||
String += LEFT_BRACE;
|
||||
String += Subfmt;
|
||||
String += RIGHT_BRACE;
|
||||
|
||||
bIsFullyFormatted = false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
else ++FormattedObjectNum;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
check_code({ if (Fmt.StartsWith(RIGHT_BRACE)) check_no_entry(); });
|
||||
|
||||
if constexpr (!bIsFormat)
|
||||
{
|
||||
if (TChar<T>::IsSpace(Fmt.Front()))
|
||||
{
|
||||
Fmt.RemovePrefix(1);
|
||||
|
||||
while (TChar<T>::IsSpace(String.Front()))
|
||||
{
|
||||
String.RemovePrefix(1);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!String.StartsWith(Fmt.Front())) return false;
|
||||
|
||||
String.RemovePrefix(1);
|
||||
}
|
||||
else String += Fmt.Front();
|
||||
|
||||
Fmt.RemovePrefix(1);
|
||||
}
|
||||
|
||||
return bIsFullyFormatted;
|
||||
};
|
||||
|
||||
bool bIsSuccessful = ParseFormat(ParseFormat, Result, Fmt);
|
||||
|
||||
if constexpr (bIsFormat) return bIsSuccessful;
|
||||
|
||||
return FormattedObjectNum;
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <CCharType T, CAllocator<T> Allocator>
|
||||
template <typename ... Ts>
|
||||
TString<T, Allocator> TString<T, Allocator>::Format(TStringView<ElementType> Fmt, const Ts&... Args)
|
||||
{
|
||||
// The Unreal Engine says that the starting buffer size catches 99.97% of printf calls.
|
||||
constexpr size_t ReserveBufferSize = 512;
|
||||
|
||||
TString Result;
|
||||
|
||||
Result.Reserve(ReserveBufferSize);
|
||||
|
||||
NAMESPACE_PRIVATE::TStringHelper<ElementType, true>::Do(Result, Fmt, ForwardAsTuple(Args...));
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
template <CCharType T>
|
||||
template <typename ... Ts>
|
||||
size_t TStringView<T>::Parse(TStringView Fmt, Ts&... Args) const
|
||||
{
|
||||
TStringView View = *this;
|
||||
|
||||
return NAMESPACE_PRIVATE::TStringHelper<ElementType, false>::Do(View, Fmt, ForwardAsTuple(Args...));
|
||||
}
|
||||
|
||||
#undef LEFT_BRACE
|
||||
#undef RIGHT_BRACE
|
||||
|
||||
#undef ESCAPE_LEFT_BRACE
|
||||
#undef ESCAPE_RIGHT_BRACE
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
#pragma warning(pop)
|
||||
@@ -1,424 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "String/Char.h"
|
||||
#include "Memory/Allocator.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "Containers/ArrayView.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Memory/MemoryOperator.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
#include "Miscellaneous/ConstantIterator.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
template <CCharType T, CAllocator<T> Allocator>
|
||||
class TString;
|
||||
|
||||
/**
|
||||
* The class template TStringView describes an object that can refer to a constant contiguous sequence of char-like objects
|
||||
* with the first element of the sequence at position zero. Provides a set of convenient string processing functions.
|
||||
*/
|
||||
template <CCharType T>
|
||||
class TStringView : public TArrayView<const T>
|
||||
{
|
||||
private:
|
||||
|
||||
using Super = TArrayView<const T>;
|
||||
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
|
||||
using Reference = typename Super::Reference;
|
||||
|
||||
using Iterator = typename Super::Iterator;
|
||||
using ReverseIterator = typename Super::ReverseIterator;
|
||||
|
||||
static_assert(CContiguousIterator<Iterator>);
|
||||
|
||||
/** Constructs an empty string view. */
|
||||
FORCEINLINE constexpr TStringView() = default;
|
||||
|
||||
/** Constructs a string view that is a view over the range ['InFirst', 'InFirst' + 'Count'). */
|
||||
template <CContiguousIterator I> requires (CConvertibleTo<TIteratorElementType<I>(*)[], const ElementType(*)[]>)
|
||||
FORCEINLINE constexpr TStringView(I InFirst, size_t InCount) : Super(InFirst, InCount) { }
|
||||
|
||||
/** Constructs a string view that is a view over the range ['InFirst', 'InLast'). */
|
||||
template <CContiguousIterator I, CSizedSentinelFor<I> S> requires (CConvertibleTo<TIteratorElementType<I>(*)[], const ElementType(*)[]>)
|
||||
FORCEINLINE constexpr TStringView(I InFirst, S InLast) : Super(InFirst, InLast) { }
|
||||
|
||||
/** Constructs a string view that is a view over the string 'InString'. */
|
||||
template <typename Allocator>
|
||||
FORCEINLINE constexpr TStringView(const TString<ElementType, Allocator>& InString);
|
||||
|
||||
/** Constructs a string view that is a view over the range ['InPtr', 'InPtr' + 'Count'). */
|
||||
FORCEINLINE constexpr TStringView(const ElementType* InPtr, size_t Count) : Super(InPtr, Count)
|
||||
{
|
||||
checkf(InPtr != nullptr, TEXT("TStringView cannot be initialized by nullptr. Please check the pointer."));
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr TStringView(nullptr_t, size_t) = delete;
|
||||
|
||||
/** Constructs a string view that is a view over the range ['InPtr', '\0'). */
|
||||
FORCEINLINE constexpr TStringView(const ElementType* InPtr)
|
||||
{
|
||||
checkf(InPtr != nullptr, TEXT("TStringView cannot be initialized by nullptr. Please check the pointer."));
|
||||
|
||||
size_t Length = 0;
|
||||
|
||||
if constexpr (CSameAs<ElementType, char>)
|
||||
{
|
||||
Length = NAMESPACE_STD::strlen(InPtr);
|
||||
}
|
||||
else if constexpr (CSameAs<ElementType, wchar>)
|
||||
{
|
||||
Length = NAMESPACE_STD::wcslen(InPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (InPtr[Length] != LITERAL(ElementType, '\0')) ++Length;
|
||||
}
|
||||
|
||||
*this = TStringView(InPtr, Length);
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr TStringView(nullptr_t) = delete;
|
||||
|
||||
/** Defaulted copy constructor copies the size and data pointer. */
|
||||
FORCEINLINE constexpr TStringView(const TStringView&) = default;
|
||||
|
||||
/** Assigns other to *this. This defaulted assignment operator performs a shallow copy of the data pointer and the size. */
|
||||
FORCEINLINE constexpr TStringView& operator=(const TStringView&) noexcept = default;
|
||||
|
||||
/** Compares the contents of two string views. */
|
||||
NODISCARD friend constexpr bool operator==(TStringView LHS, TStringView RHS) { return static_cast<Super>(LHS) == static_cast<Super>(RHS); }
|
||||
|
||||
/** Compares the contents of a string view and a character. */
|
||||
NODISCARD friend constexpr bool operator==(TStringView LHS, ElementType RHS) { return LHS == TStringView(&RHS, 1); }
|
||||
NODISCARD friend constexpr bool operator==(ElementType LHS, TStringView RHS) { return TStringView(&LHS, 1) == RHS; }
|
||||
|
||||
/** Compares the contents of two string views. */
|
||||
NODISCARD friend constexpr auto operator<=>(TStringView LHS, TStringView RHS) { return static_cast<Super>(LHS) <=> static_cast<Super>(RHS); }
|
||||
|
||||
/** Compares the contents of a string view and a character. */
|
||||
NODISCARD friend constexpr auto operator<=>(TStringView LHS, ElementType RHS) { return LHS <=> TStringView(&RHS, 1); }
|
||||
NODISCARD friend constexpr auto operator<=>(ElementType LHS, TStringView RHS) { return TStringView(&LHS, 1) <=> RHS; }
|
||||
|
||||
/** Shrinks the view by moving its start forward. */
|
||||
FORCEINLINE constexpr void RemovePrefix(size_t Count)
|
||||
{
|
||||
checkf(Count <= this->Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
*this = Substr(Count);
|
||||
}
|
||||
|
||||
/** Shrinks the view by moving its end backward. */
|
||||
FORCEINLINE constexpr void RemoveSuffix(size_t Count)
|
||||
{
|
||||
checkf(Count <= this->Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
*this = Substr(0, this->Num() - Count);
|
||||
}
|
||||
|
||||
/** Copies the elements of this string view to the destination buffer without null-termination. */
|
||||
FORCEINLINE constexpr size_t Copy(ElementType* Dest, size_t Count = DynamicExtent, size_t Offset = 0) const
|
||||
{
|
||||
checkf(Dest != nullptr, TEXT("Illegal destination buffer. Please check the pointer."));
|
||||
|
||||
checkf(Offset <= this->Num() && (Count == DynamicExtent || Offset + Count <= this->Num()), TEXT("Illegal subview range. Please check Offset and Count."));
|
||||
|
||||
if (Count == DynamicExtent)
|
||||
{
|
||||
Count = this->Num() - Offset;
|
||||
}
|
||||
|
||||
Memory::CopyAssign(Dest, this->GetData() + Offset, Count);
|
||||
|
||||
return Count;
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr size_t Copy(nullptr_t, size_t Count = DynamicExtent, size_t Offset = 0) const = delete;
|
||||
|
||||
/** Obtains an array view that is a view over the first 'Count' elements of this array view. */
|
||||
NODISCARD FORCEINLINE constexpr TStringView First(size_t Count) const
|
||||
{
|
||||
checkf(Count <= this->Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
return Substr(0, Count);
|
||||
}
|
||||
|
||||
/** Obtains an array view that is a view over the last 'Count' elements of this array view. */
|
||||
NODISCARD FORCEINLINE constexpr TStringView Last(size_t Count) const
|
||||
{
|
||||
checkf(Count <= this->Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
return Substr(this->Num() - Count);
|
||||
}
|
||||
|
||||
/** Obtains a string view that is a view over the 'Count' elements of this string view starting at 'Offset'. */
|
||||
NODISCARD FORCEINLINE constexpr TStringView Substr(size_t Offset, size_t Count = DynamicExtent) const
|
||||
{
|
||||
checkf(Offset <= this->Num() && (Count == DynamicExtent || Offset + Count <= this->Num()), TEXT("Illegal subview range. Please check Offset and Count."));
|
||||
|
||||
Super Temp = this->Subview(Offset, Count);
|
||||
|
||||
return TStringView(Temp.GetData(), Temp.Num());
|
||||
}
|
||||
|
||||
/** @return true if the string view starts with the given prefix, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool StartsWith(TStringView Prefix) const
|
||||
{
|
||||
return this->Num() >= Prefix.Num() && Substr(0, Prefix.Num()) == Prefix;
|
||||
}
|
||||
|
||||
/** @return true if the string view starts with the given prefix, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool StartsWith(ElementType Prefix) const
|
||||
{
|
||||
return this->Num() >= 1 && this->Front() == Prefix;
|
||||
}
|
||||
|
||||
/** @return true if the string view ends with the given suffix, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool EndsWith(TStringView Suffix) const
|
||||
{
|
||||
return this->Num() >= Suffix.Num() && Substr(this->Num() - Suffix.Num(), Suffix.Num()) == Suffix;
|
||||
}
|
||||
|
||||
/** @return true if the string view ends with the given suffix, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool EndsWith(ElementType Suffix) const
|
||||
{
|
||||
return this->Num() >= 1 && this->Back() == Suffix;
|
||||
}
|
||||
|
||||
/** @return true if the string view contains the given substring, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool Contains(TStringView View) const
|
||||
{
|
||||
return Find(View) != INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return true if the string view contains the given character, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool Contains(ElementType Char) const
|
||||
{
|
||||
return Find(Char) != INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return true if the string view contains character that satisfy the given predicate, false otherwise. */
|
||||
template <CPredicate<ElementType> F>
|
||||
NODISCARD FORCEINLINE constexpr bool Contains(F&& InPredicate) const
|
||||
{
|
||||
return Find(Forward<F>(InPredicate)) != INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the given substring, or INDEX_NONE if not found. */
|
||||
NODISCARD constexpr size_t Find(TStringView View, size_t Index = 0) const
|
||||
{
|
||||
if (Index >= this->Num()) return INDEX_NONE;
|
||||
|
||||
if (View.Num() > this->Num()) return INDEX_NONE;
|
||||
|
||||
if (View.Num() == 0) return Index;
|
||||
|
||||
for (; Index != this->Num() - View.Num() + 1; ++Index)
|
||||
{
|
||||
if (Substr(Index).StartsWith(View))
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD constexpr size_t Find(ElementType Char, size_t Index = 0) const
|
||||
{
|
||||
if (Index >= this->Num()) return INDEX_NONE;
|
||||
|
||||
for (; Index != this->Num(); ++Index)
|
||||
{
|
||||
if ((*this)[Index] == Char)
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the character that satisfy the given predicate, or INDEX_NONE if not found. */
|
||||
template <CPredicate<ElementType> F>
|
||||
NODISCARD constexpr size_t Find(F&& InPredicate, size_t Index = 0) const
|
||||
{
|
||||
if (Index >= this->Num()) return INDEX_NONE;
|
||||
|
||||
for (; Index != this->Num(); ++Index)
|
||||
{
|
||||
if (InvokeResult<bool>(Forward<F>(InPredicate), (*this)[Index]))
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the given substring, or INDEX_NONE if not found. */
|
||||
NODISCARD constexpr size_t RFind(TStringView View, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
if (Index != INDEX_NONE && Index >= this->Num()) return INDEX_NONE;
|
||||
|
||||
if (View.Num() > this->Num()) return INDEX_NONE;
|
||||
|
||||
if (Index == INDEX_NONE) Index = this->Num();
|
||||
|
||||
if (View.Num() == 0) return Index;
|
||||
|
||||
for (; Index != View.Num() - 1; --Index)
|
||||
{
|
||||
if (Substr(0, Index).EndsWith(View))
|
||||
{
|
||||
return Index - View.Num();
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD constexpr size_t RFind(ElementType Char, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
if (Index != INDEX_NONE && Index >= this->Num()) return INDEX_NONE;
|
||||
|
||||
if (Index == INDEX_NONE) Index = this->Num();
|
||||
|
||||
for (; Index != 0; --Index)
|
||||
{
|
||||
if ((*this)[Index - 1] == Char)
|
||||
{
|
||||
return Index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the character that satisfy the given predicate, or INDEX_NONE if not found. */
|
||||
template <CPredicate<ElementType> F>
|
||||
NODISCARD constexpr size_t RFind(F&& InPredicate, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
if (Index != INDEX_NONE && Index >= this->Num()) return INDEX_NONE;
|
||||
|
||||
if (Index == INDEX_NONE) Index = this->Num();
|
||||
|
||||
for (; Index != 0; --Index)
|
||||
{
|
||||
if (InvokeResult<bool>(Forward<F>(InPredicate), (*this)[Index - 1]))
|
||||
{
|
||||
return Index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the character contained in the given view, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindFirstOf(TStringView View, size_t Index = 0) const
|
||||
{
|
||||
return Find([View](ElementType Char) { return View.Contains(Char); }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindFirstOf(ElementType Char, size_t Index = 0) const
|
||||
{
|
||||
return Find(Char, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the character contained in the given view, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindLastOf(TStringView View, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
return RFind([View](ElementType Char) { return View.Contains(Char); }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindLastOf(ElementType Char, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
return RFind(Char, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the first absence of the character contained in the given view, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindFirstNotOf(TStringView View, size_t Index = 0) const
|
||||
{
|
||||
return Find([View](ElementType Char) { return !View.Contains(Char); }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the first absence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindFirstNotOf(ElementType Char, size_t Index = 0) const
|
||||
{
|
||||
return Find([Char](ElementType C) { return C != Char; }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the last absence of the character contained in the given view, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindLastNotOf(TStringView View, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
return RFind([View](ElementType Char) { return !View.Contains(Char); }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the last absence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindLastNotOf(ElementType Char, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
return RFind([Char](ElementType C) { return C != Char; }, Index);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Parse a string using a format string to objects.
|
||||
*
|
||||
* @param Fmt - The format string.
|
||||
* @param Args - The objects to parse.
|
||||
*
|
||||
* @return The number of objects successfully parsed.
|
||||
*/
|
||||
template <typename... Ts>
|
||||
size_t Parse(TStringView Fmt, Ts&... Args) const;
|
||||
|
||||
public:
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for TStringView. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(TStringView A) { return GetTypeHash(static_cast<Super>(A)); }
|
||||
|
||||
};
|
||||
|
||||
template <typename I, typename S>
|
||||
TStringView(I, S) -> TStringView<TIteratorElementType<I>>;
|
||||
|
||||
template<typename T, typename Allocator>
|
||||
TStringView(TString<T, Allocator>) -> TStringView<T>;
|
||||
|
||||
using FStringView = TStringView<char>;
|
||||
using FWStringView = TStringView<wchar>;
|
||||
using FU8StringView = TStringView<u8char>;
|
||||
using FU16StringView = TStringView<u16char>;
|
||||
using FU32StringView = TStringView<u32char>;
|
||||
using FUnicodeStringView = TStringView<unicodechar>;
|
||||
|
||||
#define TEXT_VIEW(X) TStringView(TEXT(X))
|
||||
#define WTEXT_VIEW(X) TStringView(WTEXT(X))
|
||||
#define U8TEXT_VIEW(X) TStringView(U8TEXT(X))
|
||||
#define U16TEXT_VIEW(X) TStringView(U16TEXT(X))
|
||||
#define U32TEXT_VIEW(X) TStringView(U32TEXT(X))
|
||||
#define UNICODETEXT_VIEW(X) TStringView(UNICODETEXT(X))
|
||||
|
||||
#define LITERAL_VIEW(T, X) TStringView(LITERAL(T, X))
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
938
Redcraft.Utility/Source/Public/Strings/Char.h
Normal file
938
Redcraft.Utility/Source/Public/Strings/Char.h
Normal file
@@ -0,0 +1,938 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
#include <locale>
|
||||
#include <climits>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
template <typename T>
|
||||
concept CCharType = CSameAs<T, char> || CSameAs<T, wchar> || CSameAs<T, u8char> || CSameAs<T, u16char> || CSameAs<T, u32char>;
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <CCharType>
|
||||
struct TLiteral;
|
||||
|
||||
template <>
|
||||
struct TLiteral<char>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr char Select(const char X, const wchar , const u8char , const u16char , const u32char ) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const char* Select(const char* X, const wchar*, const u8char*, const u16char*, const u32char*) { return X; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TLiteral<wchar>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr wchar Select(const char , const wchar X, const u8char , const u16char , const u32char ) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const wchar* Select(const char*, const wchar* X, const u8char*, const u16char*, const u32char*) { return X; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TLiteral<u8char>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr u8char Select(const char , const wchar , const u8char X, const u16char , const u32char ) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const u8char* Select(const char*, const wchar*, const u8char* X, const u16char*, const u32char*) { return X; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TLiteral<u16char>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr u16char Select(const char , const wchar , const u8char , const u16char X, const u32char ) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const u16char* Select(const char*, const wchar*, const u8char*, const u16char* X, const u32char*) { return X; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TLiteral<u32char>
|
||||
{
|
||||
NODISCARD FORCEINLINE static constexpr u32char Select(const char , const wchar , const u8char , const u16char , const u32char X) { return X; }
|
||||
NODISCARD FORCEINLINE static constexpr const u32char* Select(const char*, const wchar*, const u8char*, const u16char*, const u32char* X) { return X; }
|
||||
};
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/** Templated literal struct to allow selection of string literals based on the character type provided, and not on compiler switches. */
|
||||
#define LITERAL(CharType, StringLiteral) NAMESPACE_REDCRAFT::NAMESPACE_PRIVATE::TLiteral<CharType>::Select(TEXT(StringLiteral), WTEXT(StringLiteral), U8TEXT(StringLiteral), U16TEXT(StringLiteral), U32TEXT(StringLiteral))
|
||||
|
||||
static_assert(CUnsigned<u8char>, "TChar assumes u8char is an unsigned integer");
|
||||
static_assert(CUnsigned<u16char>, "TChar assumes u16char is an unsigned integer");
|
||||
static_assert(CUnsigned<u32char>, "TChar assumes u32char is an unsigned integer");
|
||||
static_assert(CUnsigned<unicodechar>, "TChar assumes unicodechar is an unsigned integer");
|
||||
|
||||
/** Set of utility functions operating on a single character. Implemented based on user-preferred locale and ISO 30112 "i18n". */
|
||||
template <CCharType T>
|
||||
struct TChar
|
||||
{
|
||||
using FCharType = T;
|
||||
|
||||
/** The maximum number of code units required to represent a single character. if unknown, guess 1. */
|
||||
static constexpr size_t MaxCodeUnitLength =
|
||||
CSameAs<FCharType, char> ? MB_LEN_MAX :
|
||||
CSameAs<FCharType, wchar> ?
|
||||
PLATFORM_WINDOWS ? 2 :
|
||||
PLATFORM_LINUX ? 1 : 1 :
|
||||
CSameAs<FCharType, u8char> ? 4 :
|
||||
CSameAs<FCharType, u16char> ? 2 :
|
||||
CSameAs<FCharType, u32char> ? 1 : 1;
|
||||
|
||||
/** Whether the character type is fixed-length. */
|
||||
static constexpr bool bIsFixedLength = MaxCodeUnitLength == 1;
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsValid(FCharType InChar)
|
||||
{
|
||||
if constexpr (TChar::IsASCII() && CSameAs<FCharType, char>)
|
||||
{
|
||||
if (0x00 <= InChar && InChar <= 0x7F) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
if ((InChar & 0b10000000) == 0b00000000) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char> || CSameAs<FCharType, u32char>)
|
||||
{
|
||||
if (InChar >= 0xD800 && InChar <= 0xDBFF) return false;
|
||||
if (InChar >= 0xDC00 && InChar <= 0xDFFF) return false;
|
||||
|
||||
return InChar <= 0x10FFFF;
|
||||
}
|
||||
|
||||
// Windows uses UTF-16 encoding for wchar.
|
||||
else if constexpr (PLATFORM_WINDOWS && (CSameAs<FCharType, wchar>))
|
||||
{
|
||||
return TChar<u16char>::IsValid(static_cast<u16char>(InChar));
|
||||
}
|
||||
|
||||
// Linux uses UTF-32 encoding for wchar.
|
||||
else if constexpr (PLATFORM_LINUX && (CSameAs<FCharType, wchar>))
|
||||
{
|
||||
return TChar<u32char>::IsValid(static_cast<u32char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsNonch(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
if (InChar >= U16TEXT('\uFDD0') && InChar <= U16TEXT('\uFDEF')) return true;
|
||||
|
||||
if (InChar == U16TEXT('\uFFFE')) return true;
|
||||
if (InChar == U16TEXT('\uFFFF')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
if (InChar >= U32TEXT('\uFDD0') && InChar <= U32TEXT('\uFDEF')) return true;
|
||||
|
||||
if ((InChar & 0x0000FFFE) == 0x0000FFFE) return TChar::IsValid(InChar);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Windows uses UTF-16 encoding for wchar.
|
||||
else if constexpr (PLATFORM_WINDOWS && (CSameAs<FCharType, wchar>))
|
||||
{
|
||||
return TChar<u16char>::IsNonch(static_cast<u16char>(InChar));
|
||||
}
|
||||
|
||||
// Linux uses UTF-32 encoding for wchar.
|
||||
else if constexpr (PLATFORM_LINUX && (CSameAs<FCharType, wchar>))
|
||||
{
|
||||
return TChar<u32char>::IsNonch(static_cast<u32char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsASCII(FCharType InChar = LITERAL(FCharType, '\0'))
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char>)
|
||||
{
|
||||
constexpr bool ASCIICompatible = []() -> bool
|
||||
{
|
||||
constexpr char ASCIITable[] =
|
||||
TEXT("\u0000\u0001\u0002\u0003\u0004\u0005\u0006")
|
||||
TEXT("\a\b\t\n\v\f\r")
|
||||
TEXT("\u000E\u000F")
|
||||
TEXT("\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017")
|
||||
TEXT("\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F")
|
||||
TEXT(" !\"#$%&'()*+,-./0123456789:;<=>?")
|
||||
TEXT("@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_")
|
||||
TEXT("`abcdefghijklmnopqrstuvwxyz{|}~\u007F");
|
||||
|
||||
for (size_t Index = 0; Index <= 0x7F; ++Index)
|
||||
{
|
||||
if (ASCIITable[Index] != static_cast<char>(Index)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
();
|
||||
|
||||
return ASCIICompatible && 0x00 <= InChar && InChar <= 0x7F;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, wchar>)
|
||||
{
|
||||
constexpr bool ASCIICompatible = []() -> bool
|
||||
{
|
||||
constexpr wchar ASCIITable[] =
|
||||
WTEXT("\u0000\u0001\u0002\u0003\u0004\u0005\u0006")
|
||||
WTEXT("\a\b\t\n\v\f\r")
|
||||
WTEXT("\u000E\u000F")
|
||||
WTEXT("\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017")
|
||||
WTEXT("\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F")
|
||||
WTEXT(" !\"#$%&'()*+,-./0123456789:;<=>?")
|
||||
WTEXT("@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_")
|
||||
WTEXT("`abcdefghijklmnopqrstuvwxyz{|}~\u007F");
|
||||
|
||||
for (size_t Index = 0; Index <= 0x7F; ++Index)
|
||||
{
|
||||
if (ASCIITable[Index] != static_cast<wchar>(Index)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
();
|
||||
|
||||
return ASCIICompatible && 0x00 <= InChar && InChar <= 0x7F;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char> || CSameAs<FCharType, u16char> || CSameAs<FCharType, u32char> || CSameAs<FCharType, unicodechar>)
|
||||
{
|
||||
return InChar <= 0x7F;
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsAlnum(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isalnum(InChar, Loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TChar::IsAlpha(InChar) || TChar::IsDigit(InChar);
|
||||
}
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsAlpha(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isalpha(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0041>..<U005A>;<U0061>..<U007A>;
|
||||
*/
|
||||
if ((InChar >= U8TEXT('\u0041') && InChar <= U8TEXT('\u005A')) ||
|
||||
(InChar >= U8TEXT('\u0061') && InChar <= U8TEXT('\u007A')))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char> || CSameAs<FCharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= LITERAL(FCharType, '\u007F'), TEXT("TChar::IsAlpha() only supports basic latin block."));
|
||||
|
||||
if (InChar > LITERAL(FCharType, '\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsAlpha(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsLower(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::islower(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0061>..<U007A>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0061') && InChar <= U8TEXT('\u007A')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsLower() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsLower(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsLower() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsLower(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsUpper(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isupper(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0041>..<U005A>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0041') && InChar <= U8TEXT('\u005A')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsUpper() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsUpper(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsUpper() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsUpper(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsDigit(FCharType InChar)
|
||||
{
|
||||
static_assert(TChar::IsASCII());
|
||||
|
||||
/* <U0030>..<U0039>; */
|
||||
return InChar >= LITERAL(FCharType, '0') && InChar <= LITERAL(FCharType, '9');
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsDigit(FCharType InChar, unsigned Base)
|
||||
{
|
||||
checkf(Base >= 2 && Base <= 36, TEXT("Base must be in the range [2, 36]."));
|
||||
|
||||
static_assert(TChar::IsASCII());
|
||||
|
||||
bool bResult = false;
|
||||
|
||||
/* <U0030>..<U0039>; */
|
||||
bResult |= InChar >= LITERAL(FCharType, '0') && InChar < LITERAL(FCharType, '0') + static_cast<signed>(Base);
|
||||
|
||||
/* <U0041>..<U0046>; */
|
||||
bResult |= InChar >= LITERAL(FCharType, 'a') && InChar < LITERAL(FCharType, 'a') + static_cast<signed>(Base) - 10;
|
||||
|
||||
/* <U0061>..<U0066>; */
|
||||
bResult |= InChar >= LITERAL(FCharType, 'A') && InChar < LITERAL(FCharType, 'A') + static_cast<signed>(Base) - 10;
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsCntrl(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::iscntrl(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/* <U0000>..<U001F>;<U007F>; */
|
||||
return (InChar >= U8TEXT('\u0000') && InChar <= U8TEXT('\u001F')) || InChar == U8TEXT('\u007F');
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
/* <U0000>..<U001F>;<U007F>..<U009F>;<U2028>;<U2029>; */
|
||||
return
|
||||
(InChar >= U16TEXT('\u0000') && InChar <= U16TEXT('\u001F')) ||
|
||||
(InChar >= U16TEXT('\u007F') && InChar <= U16TEXT('\u009F')) ||
|
||||
(InChar == U16TEXT('\u2028') || InChar == U16TEXT('\u2029'));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
/* <U0000>..<U001F>;<U007F>..<U009F>;<U2028>;<U2029>; */
|
||||
return
|
||||
(InChar >= U32TEXT('\u0000') && InChar <= U32TEXT('\u001F')) ||
|
||||
(InChar >= U32TEXT('\u007F') && InChar <= U32TEXT('\u009F')) ||
|
||||
(InChar == U32TEXT('\u2028') || InChar == U32TEXT('\u2029'));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsGraph(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isgraph(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0021>..<U007E>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0021') && InChar <= U8TEXT('\u007E')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsGraph() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsGraph(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsGraph() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsGraph(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsSpace(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isspace(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* ISO/IEC 6429
|
||||
* <U0009>..<U000D>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0009') && InChar <= U8TEXT('\u000D')) return true;
|
||||
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0020>;
|
||||
*/
|
||||
if (InChar == U8TEXT('\u0020')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
/*
|
||||
* ISO/IEC 6429
|
||||
* <U0009>..<U000D>;
|
||||
*/
|
||||
if (InChar >= U16TEXT('\u0009') && InChar <= U16TEXT('\u000D')) return true;
|
||||
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0020>;
|
||||
*/
|
||||
if (InChar == U16TEXT('\u0020')) return true;
|
||||
|
||||
/*
|
||||
* OGHAM
|
||||
* <U1680>;
|
||||
*/
|
||||
if (InChar == U16TEXT('\u1680')) return true;
|
||||
|
||||
/*
|
||||
* MONGOL
|
||||
* <U180E>;
|
||||
*/
|
||||
if (InChar == U16TEXT('\u180E')) return true;
|
||||
|
||||
/*
|
||||
* GENERAL PUNCTUATION
|
||||
* <U2000>..<U2006>;<U2008>..<U200A>;<U2028>;<U2029>;<U205F>;
|
||||
*/
|
||||
if ((InChar >= U16TEXT('\u2000') && InChar <= U16TEXT('\u2006')) ||
|
||||
(InChar >= U16TEXT('\u2008') && InChar <= U16TEXT('\u200A')) ||
|
||||
(InChar == U16TEXT('\u2028') || InChar == U16TEXT('\u2029')) ||
|
||||
(InChar == U16TEXT('\u205F')))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* CJK SYMBOLS AND PUNCTUATION, HIRAGANA
|
||||
* <U3000>;
|
||||
*/
|
||||
if (InChar == U16TEXT('\u3000')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
/*
|
||||
* ISO/IEC 6429
|
||||
* <U0009>..<U000D>;
|
||||
*/
|
||||
if (InChar >= U32TEXT('\u0009') && InChar <= U32TEXT('\u000D')) return true;
|
||||
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0020>;
|
||||
*/
|
||||
if (InChar == U32TEXT('\u0020')) return true;
|
||||
|
||||
/*
|
||||
* OGHAM
|
||||
* <U1680>;
|
||||
*/
|
||||
if (InChar == U32TEXT('\u1680')) return true;
|
||||
|
||||
/*
|
||||
* MONGOL
|
||||
* <U180E>;
|
||||
*/
|
||||
if (InChar == U32TEXT('\u180E')) return true;
|
||||
|
||||
/*
|
||||
* GENERAL PUNCTUATION
|
||||
* <U2000>..<U2006>;<U2008>..<U200A>;<U2028>;<U2029>;<U205F>;
|
||||
*/
|
||||
if ((InChar >= U32TEXT('\u2000') && InChar <= U32TEXT('\u2006')) ||
|
||||
(InChar >= U32TEXT('\u2008') && InChar <= U32TEXT('\u200A')) ||
|
||||
(InChar == U32TEXT('\u2028') || InChar == U32TEXT('\u2029')) ||
|
||||
(InChar == U32TEXT('\u205F')))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* CJK SYMBOLS AND PUNCTUATION, HIRAGANA
|
||||
* <U3000>;
|
||||
*/
|
||||
if (InChar == U32TEXT('\u3000')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsBlank(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isblank(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/* <U0009>;<U0020>; */
|
||||
return InChar == U8TEXT('\u0009') || InChar == U8TEXT('\u0020');
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
/* <U0009>;<U0020>;<U1680>;<U180E>;<U2000>..<U2006>;<U2008>..<U200A>;<U205F>;<U3000>; */
|
||||
return
|
||||
(InChar >= U16TEXT('\u2000') && InChar <= U16TEXT('\u2006')) ||
|
||||
(InChar == U16TEXT('\u0009') || InChar == U16TEXT('\u0020')) ||
|
||||
(InChar == U16TEXT('\u1680') || InChar == U16TEXT('\u180E')) ||
|
||||
(InChar == U16TEXT('\u2008') || InChar == U16TEXT('\u200A')) ||
|
||||
(InChar == U16TEXT('\u205F') || InChar == U16TEXT('\u3000'));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
/* <U0009>;<U0020>;<U1680>;<U180E>;<U2000>..<U2006>;<U2008>..<U200A>;<U205F>;<U3000>; */
|
||||
return
|
||||
(InChar >= U32TEXT('\u2000') && InChar <= U32TEXT('\u2006')) ||
|
||||
(InChar == U32TEXT('\u0009') || InChar == U32TEXT('\u0020')) ||
|
||||
(InChar == U32TEXT('\u1680') || InChar == U32TEXT('\u180E')) ||
|
||||
(InChar == U32TEXT('\u2008') || InChar == U32TEXT('\u200A')) ||
|
||||
(InChar == U32TEXT('\u205F') || InChar == U32TEXT('\u3000'));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsPrint(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::isprint(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0020>..<U007E>;
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0020') && InChar <= U8TEXT('\u007E')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsPrint() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsPrint(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsPrint() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsPrint(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr bool IsPunct(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return NAMESPACE_STD::ispunct(InChar, Loc);
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* <U0021>..<U002F>;<U003A>..<U0040>;<U005B>..<U0060>;<U007B>..<U007E>;
|
||||
*/
|
||||
if ((InChar >= U8TEXT('\u0021') && InChar <= U8TEXT('\u002F')) ||
|
||||
(InChar >= U8TEXT('\u003A') && InChar <= U8TEXT('\u0040')) ||
|
||||
(InChar >= U8TEXT('\u005B') && InChar <= U8TEXT('\u0060')) ||
|
||||
(InChar >= U8TEXT('\u007B') && InChar <= U8TEXT('\u007E')))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::IsPunct() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsPunct(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::IsPunct() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return TChar<u8char>::IsPunct(static_cast<u8char>(InChar));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr FCharType ToLower(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return static_cast<FCharType>(NAMESPACE_STD::tolower(InChar, Loc));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* (<U0041>,<U0061>);(<U0042>,<U0062>);(<U0043>,<U0063>);(<U0044>,<U0064>);
|
||||
* (<U0045>,<U0065>);(<U0046>,<U0066>);(<U0047>,<U0067>);(<U0048>,<U0068>);
|
||||
* (<U0049>,<U0069>);(<U004A>,<U006A>);(<U004B>,<U006B>);(<U004C>,<U006C>);
|
||||
* (<U004D>,<U006D>);(<U004E>,<U006E>);(<U004F>,<U006F>);(<U0050>,<U0070>);
|
||||
* (<U0051>,<U0071>);(<U0052>,<U0072>);(<U0053>,<U0073>);(<U0054>,<U0074>);
|
||||
* (<U0055>,<U0075>);(<U0056>,<U0076>);(<U0057>,<U0077>);(<U0058>,<U0078>);
|
||||
* (<U0059>,<U0079>);(<U005A>,<U007A>);
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0041') && InChar <= U8TEXT('\u005A')) return InChar + U8TEXT('\u0020');
|
||||
|
||||
return InChar;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::ToLower() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return static_cast<u16char>(TChar<u8char>::ToLower(static_cast<u8char>(InChar)));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::ToLower() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return static_cast<u16char>(TChar<u8char>::ToLower(static_cast<u8char>(InChar)));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return InChar;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr FCharType ToUpper(FCharType InChar)
|
||||
{
|
||||
if constexpr (CSameAs<FCharType, char> || CSameAs<FCharType, wchar>)
|
||||
{
|
||||
NAMESPACE_STD::locale Loc("");
|
||||
return static_cast<FCharType>(NAMESPACE_STD::toupper(InChar, Loc));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u8char>)
|
||||
{
|
||||
/*
|
||||
* BASIC LATIN
|
||||
* (<U0061>,<U0041>);(<U0062>,<U0042>);(<U0063>,<U0043>);(<U0064>,<U0044>);/
|
||||
* (<U0065>,<U0045>);(<U0066>,<U0046>);(<U0067>,<U0047>);(<U0068>,<U0048>);/
|
||||
* (<U0069>,<U0049>);(<U006A>,<U004A>);(<U006B>,<U004B>);(<U006C>,<U004C>);/
|
||||
* (<U006D>,<U004D>);(<U006E>,<U004E>);(<U006F>,<U004F>);(<U0070>,<U0050>);/
|
||||
* (<U0071>,<U0051>);(<U0072>,<U0052>);(<U0073>,<U0053>);(<U0074>,<U0054>);/
|
||||
* (<U0075>,<U0055>);(<U0076>,<U0056>);(<U0077>,<U0057>);(<U0078>,<U0058>);/
|
||||
* (<U0079>,<U0059>);(<U007A>,<U005A>);
|
||||
*/
|
||||
if (InChar >= U8TEXT('\u0061') && InChar <= U8TEXT('\u007A')) return InChar - U8TEXT('\u0020');
|
||||
|
||||
return InChar;
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u16char>)
|
||||
{
|
||||
checkf(InChar <= U16TEXT('\u007F'), TEXT("TChar::ToUpper() only supports basic latin block."));
|
||||
|
||||
if (InChar > U16TEXT('\u007F')) return false;
|
||||
|
||||
return static_cast<u16char>(TChar<u8char>::ToUpper(static_cast<u8char>(InChar)));
|
||||
}
|
||||
|
||||
else if constexpr (CSameAs<FCharType, u32char>)
|
||||
{
|
||||
checkf(InChar <= U32TEXT('\u007F'), TEXT("TChar::ToUpper() only supports basic latin block."));
|
||||
|
||||
if (InChar > U32TEXT('\u007F')) return false;
|
||||
|
||||
return static_cast<u16char>(TChar<u8char>::ToUpper(static_cast<u8char>(InChar)));
|
||||
}
|
||||
|
||||
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
|
||||
|
||||
return InChar;
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr unsigned ToDigit(FCharType InChar)
|
||||
{
|
||||
static_assert(TChar::IsASCII());
|
||||
|
||||
constexpr uint8 DigitFromChar[] =
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitFromChar) == 256);
|
||||
|
||||
if constexpr (sizeof(FCharType) > 1) if (InChar >> 8) return DigitFromChar[0];
|
||||
|
||||
return DigitFromChar[InChar];
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr unsigned ToDigit(FCharType InChar, bool bLowercase)
|
||||
{
|
||||
static_assert(TChar::IsASCII());
|
||||
|
||||
if (bLowercase)
|
||||
{
|
||||
constexpr uint8 DigitFromChar[] =
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitFromChar) == 256);
|
||||
|
||||
if constexpr (sizeof(FCharType) > 1) if (InChar >> 8) return DigitFromChar[0];
|
||||
|
||||
return DigitFromChar[InChar];
|
||||
}
|
||||
|
||||
constexpr uint8 DigitFromChar[] =
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitFromChar) == 256);
|
||||
|
||||
if constexpr (sizeof(FCharType) > 1) if (InChar >> 8) return DigitFromChar[0];
|
||||
|
||||
return DigitFromChar[InChar];
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr FCharType FromDigit(unsigned InDigit)
|
||||
{
|
||||
checkf(InDigit < 36, TEXT("Digit must be in the range [0, 35]."));
|
||||
|
||||
return LITERAL(FCharType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")[InDigit];
|
||||
}
|
||||
|
||||
NODISCARD FORCEINLINE static constexpr FCharType FromDigit(unsigned InDigit, bool bLowercase)
|
||||
{
|
||||
checkf(InDigit < 36, TEXT("Digit must be in the range [0, 35]."));
|
||||
|
||||
if (bLowercase) return LITERAL(FCharType, "0123456789abcdefghijklmnopqrstuvwxyz")[InDigit];
|
||||
|
||||
return LITERAL(FCharType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")[InDigit];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using FChar = TChar<char>;
|
||||
using FWChar = TChar<wchar>;
|
||||
using FU8Char = TChar<u8char>;
|
||||
using FU16Char = TChar<u16char>;
|
||||
using FU32Char = TChar<u32char>;
|
||||
using FUnicodeChar = TChar<unicodechar>;
|
||||
|
||||
static_assert(FUnicodeChar::bIsFixedLength);
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
487
Redcraft.Utility/Source/Public/Strings/Convert.h
Normal file
487
Redcraft.Utility/Source/Public/Strings/Convert.h
Normal file
@@ -0,0 +1,487 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Numerics/Limits.h"
|
||||
#include "Algorithms/Basic.h"
|
||||
#include "Memory/Allocators.h"
|
||||
#include "Memory/Address.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "Strings/Char.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
#include <charconv>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4146)
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
template <typename R>
|
||||
concept CStringRange = CInputRange<R> && CCharType<TRangeElement<R>>;
|
||||
|
||||
template <typename I>
|
||||
concept CStringIterator = CInputIterator<I> && CCharType<TIteratorElement<I>>;
|
||||
|
||||
NAMESPACE_BEGIN(Algorithms)
|
||||
|
||||
/**
|
||||
* Parses a boolean value from the given string range.
|
||||
* Ignore leading and trailing spaces and case-insensitive.
|
||||
*
|
||||
* - "True" become true.
|
||||
* - "False" become false.
|
||||
*
|
||||
* @param Range - The range of characters to parse.
|
||||
* @param Value - The boolean value to parse.
|
||||
*
|
||||
* @return true if the value is successfully parsed, false otherwise.
|
||||
*/
|
||||
template <CStringRange R>
|
||||
constexpr bool Parse(R&& Range, bool& Value)
|
||||
{
|
||||
using FCharTraits = TChar<TRangeElement<R>>;
|
||||
|
||||
if constexpr (CSizedRange<R&>)
|
||||
{
|
||||
checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range)."));
|
||||
}
|
||||
|
||||
auto Iter = Ranges::Begin(Range);
|
||||
auto Sent = Ranges::End (Range);
|
||||
|
||||
bool Result;
|
||||
|
||||
// Ignore leading spaces.
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter == Sent) return false;
|
||||
|
||||
// Parse the true value.
|
||||
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 't') || *Iter == LITERAL(TRangeElement<R>, 'T')))
|
||||
{
|
||||
++Iter;
|
||||
|
||||
Result = true;
|
||||
|
||||
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'r') || *Iter == LITERAL(TRangeElement<R>, 'R'))) ++Iter; else return false;
|
||||
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'u') || *Iter == LITERAL(TRangeElement<R>, 'U'))) ++Iter; else return false;
|
||||
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'e') || *Iter == LITERAL(TRangeElement<R>, 'E'))) ++Iter; else return false;
|
||||
}
|
||||
|
||||
// Parse the false value.
|
||||
else if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'f') || *Iter == LITERAL(TRangeElement<R>, 'F')))
|
||||
{
|
||||
++Iter;
|
||||
|
||||
Result = false;
|
||||
|
||||
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'a') || *Iter == LITERAL(TRangeElement<R>, 'A'))) ++Iter; else return false;
|
||||
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'l') || *Iter == LITERAL(TRangeElement<R>, 'L'))) ++Iter; else return false;
|
||||
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 's') || *Iter == LITERAL(TRangeElement<R>, 'S'))) ++Iter; else return false;
|
||||
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'e') || *Iter == LITERAL(TRangeElement<R>, 'E'))) ++Iter; else return false;
|
||||
}
|
||||
|
||||
else return false;
|
||||
|
||||
// Ignore trailing spaces.
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter != Sent) return false;
|
||||
|
||||
Value = Result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a boolean value from the given string range.
|
||||
* Ignore leading and trailing spaces and case-insensitive.
|
||||
*
|
||||
* - "True" become true.
|
||||
* - "False" become false.
|
||||
*
|
||||
* @param First - The iterator of the range.
|
||||
* @param Last - The sentinel of the range.
|
||||
* @param Value - The boolean value to parse.
|
||||
*
|
||||
* @return true if the value is successfully parsed, false otherwise.
|
||||
*/
|
||||
template <CStringIterator I, CSentinelFor<I> S>
|
||||
FORCEINLINE constexpr bool Parse(I First, S Last, bool& Value)
|
||||
{
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
|
||||
}
|
||||
|
||||
return Algorithms::Parse(Ranges::View(MoveTemp(First), Last), Value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an integral value from the given string range.
|
||||
* Ignore leading and trailing spaces and case-insensitive.
|
||||
* If the ingeter value is unsigned, the negative sign causes the parsing to fail.
|
||||
* Allow parsing base prefixes: "0x" for hexadecimal, "0b" for binary, and "0" for octal.
|
||||
*
|
||||
* @param Range - The range of characters to parse.
|
||||
* @param Value - The integral value to parse.
|
||||
* @param Base - The base of the number, between [2, 36], or 0 for auto-detect.
|
||||
*
|
||||
* @return true if the value is successfully parsed, false otherwise.
|
||||
*/
|
||||
template <CStringRange R, CIntegral T> requires (!CConst<T> && !CVolatile<T> && !CSameAs<T, bool>)
|
||||
constexpr bool Parse(R&& Range, T& Value, uint Base = 0)
|
||||
{
|
||||
using FCharTraits = TChar<TRangeElement<R>>;
|
||||
|
||||
checkf(Base == 0 || (Base >= 2 && Base <= 36), TEXT("Illegal base. Please check the Base."));
|
||||
|
||||
if constexpr (CSizedRange<R&>)
|
||||
{
|
||||
checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range)."));
|
||||
}
|
||||
|
||||
auto Iter = Ranges::Begin(Range);
|
||||
auto Sent = Ranges::End (Range);
|
||||
|
||||
// Ignore leading spaces.
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter == Sent) return false;
|
||||
|
||||
bool bNegative = false;
|
||||
|
||||
// Parse the negative sign.
|
||||
if constexpr (CSigned<T>)
|
||||
{
|
||||
if (*Iter == LITERAL(TRangeElement<R>, '-'))
|
||||
{
|
||||
bNegative = true;
|
||||
++Iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the positive sign.
|
||||
if (!bNegative && *Iter == LITERAL(TRangeElement<R>, '+')) ++Iter;
|
||||
|
||||
// Auto-detect the base.
|
||||
if (Base == 0)
|
||||
{
|
||||
if (Iter == Sent) return false;
|
||||
|
||||
if (*Iter == LITERAL(TRangeElement<R>, '0'))
|
||||
{
|
||||
++Iter;
|
||||
|
||||
// Return zero if the string has only one zero.
|
||||
if (Iter == Sent || FCharTraits::IsSpace(*Iter))
|
||||
{
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter != Sent) return false;
|
||||
|
||||
Value = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*Iter == LITERAL(TRangeElement<R>, 'x') || *Iter == LITERAL(TRangeElement<R>, 'X'))
|
||||
{
|
||||
Base = 16;
|
||||
++Iter;
|
||||
}
|
||||
|
||||
else if (*Iter == LITERAL(TRangeElement<R>, 'b') || *Iter == LITERAL(TRangeElement<R>, 'B'))
|
||||
{
|
||||
Base = 2;
|
||||
++Iter;
|
||||
}
|
||||
|
||||
else if (FCharTraits::IsDigit(*Iter, 8)) Base = 8;
|
||||
|
||||
else return false;
|
||||
}
|
||||
|
||||
else Base = 10;
|
||||
}
|
||||
|
||||
// Parse the base prefix.
|
||||
else if (Base == 2 || Base == 16)
|
||||
{
|
||||
if (Iter == Sent) return false;
|
||||
|
||||
if (*Iter == LITERAL(TRangeElement<R>, '0'))
|
||||
{
|
||||
++Iter;
|
||||
|
||||
// Return zero if the string has only one zero.
|
||||
if (Iter == Sent || FCharTraits::IsSpace(*Iter))
|
||||
{
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter != Sent) return false;
|
||||
|
||||
Value = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Base == 16 && (*Iter == LITERAL(TRangeElement<R>, 'x') || *Iter == LITERAL(TRangeElement<R>, 'X'))) ++Iter;
|
||||
if (Base == 2 && (*Iter == LITERAL(TRangeElement<R>, 'b') || *Iter == LITERAL(TRangeElement<R>, 'B'))) ++Iter;
|
||||
}
|
||||
}
|
||||
|
||||
if (Iter == Sent) return false;
|
||||
|
||||
check(Base >= 2 && Base <= 36);
|
||||
|
||||
if (!FCharTraits::IsDigit(*Iter, Base)) return false;
|
||||
|
||||
using FUnsignedT = TMakeUnsigned<T>;
|
||||
|
||||
FUnsignedT LastValue = 0;
|
||||
FUnsignedT Unsigned = 0;
|
||||
|
||||
do
|
||||
{
|
||||
uint Digit = FCharTraits::ToDigit(*Iter);
|
||||
|
||||
// Break if the char is not a digit.
|
||||
if (Digit >= Base) break;
|
||||
|
||||
++Iter;
|
||||
|
||||
LastValue = Unsigned;
|
||||
|
||||
Unsigned = LastValue * Base + Digit;
|
||||
|
||||
// Fail if the value is overflowed.
|
||||
if (Unsigned < LastValue) return false;
|
||||
}
|
||||
while (Iter != Sent);
|
||||
|
||||
// Ignore trailing spaces.
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter != Sent) return false;
|
||||
|
||||
if constexpr (CSigned<T>)
|
||||
{
|
||||
// Fail if the value is overflowed.
|
||||
if (!bNegative && Unsigned >= static_cast<FUnsignedT>(TNumericLimits<T>::Max())) return false;
|
||||
if ( bNegative && Unsigned >= static_cast<FUnsignedT>(TNumericLimits<T>::Min())) return false;
|
||||
|
||||
// Reverse if the value is negative.
|
||||
if (bNegative) Unsigned = -Unsigned;
|
||||
}
|
||||
|
||||
Value = Unsigned;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an integral value from the given string range.
|
||||
* Ignore leading and trailing spaces and case-insensitive.
|
||||
* If the ingeter value is unsigned, the negative sign causes the parsing to fail.
|
||||
* Allow parsing base prefixes: "0x" for hexadecimal, "0b" for binary, and "0" for octal.
|
||||
*
|
||||
* @param First - The iterator of the range.
|
||||
* @param Last - The sentinel of the range.
|
||||
* @param Value - The integral value to parse.
|
||||
* @param Base - The base of the number, between [2, 36], or 0 for auto-detect.
|
||||
*
|
||||
* @return true if the value is successfully parsed, false otherwise.
|
||||
*/
|
||||
template <CStringIterator I, CSentinelFor<I> S, CIntegral T> requires (!CConst<T> && !CVolatile<T> && !CSameAs<T, bool>)
|
||||
FORCEINLINE constexpr bool Parse(I First, S Last, T& Value, uint Base = 0)
|
||||
{
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
|
||||
}
|
||||
|
||||
return Algorithms::Parse(Ranges::View(MoveTemp(First), Last), Value, Base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a floating-point value from the given string range.
|
||||
* Ignore leading and trailing spaces and case-insensitive.
|
||||
* Automatically detect formats if multiple formats are allowed.
|
||||
* Allow parsing base prefixes: "0x" for hexadecimal.
|
||||
*
|
||||
* @param Range - The range of characters to parse.
|
||||
* @param Value - The floating-point value to parse.
|
||||
* @param bFixed - Allow parsing fixed-point values.
|
||||
* @param bScientific - Allow parsing scientific notation values.
|
||||
* @param bHex - Allow parsing hex floating-point values.
|
||||
*
|
||||
* @return true if the value is successfully parsed, false otherwise.
|
||||
*/
|
||||
template <CStringRange R, CFloatingPoint T> requires (!CConst<T> && !CVolatile<T>)
|
||||
constexpr bool Parse(R&& Range, T& Value, bool bFixed = true, bool bScientific = true, bool bHex = true)
|
||||
{
|
||||
if (!bFixed && !bScientific && !bHex) return false;
|
||||
|
||||
using FCharTraits = TChar<TRangeElement<R>>;
|
||||
|
||||
if constexpr (CSizedRange<R&>)
|
||||
{
|
||||
checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range)."));
|
||||
}
|
||||
|
||||
auto Iter = Ranges::Begin(Range);
|
||||
auto Sent = Ranges::End (Range);
|
||||
|
||||
// Ignore leading spaces.
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter == Sent) return false;
|
||||
|
||||
bool bNegative = false;
|
||||
|
||||
// Parse the negative sign.
|
||||
if (*Iter == LITERAL(TRangeElement<R>, '-'))
|
||||
{
|
||||
bNegative = true;
|
||||
++Iter;
|
||||
}
|
||||
|
||||
// Parse the positive sign.
|
||||
else if (*Iter == LITERAL(TRangeElement<R>, '+')) ++Iter;
|
||||
|
||||
if (Iter == Sent) return false;
|
||||
|
||||
// Fail if the string has multiple signs.
|
||||
if (*Iter == LITERAL(TRangeElement<R>, '-')) return false;
|
||||
if (*Iter == LITERAL(TRangeElement<R>, '+')) return false;
|
||||
|
||||
NAMESPACE_STD::chars_format Format = NAMESPACE_STD::chars_format::general;
|
||||
|
||||
if ( bFixed && !bScientific) Format = NAMESPACE_STD::chars_format::fixed;
|
||||
else if (!bFixed && bScientific) Format = NAMESPACE_STD::chars_format::scientific;
|
||||
else if (!bFixed && !bScientific) Format = NAMESPACE_STD::chars_format::hex;
|
||||
|
||||
// Auto-detect the hex format.
|
||||
if (bHex)
|
||||
{
|
||||
if (*Iter == LITERAL(TRangeElement<R>, '0'))
|
||||
{
|
||||
++Iter;
|
||||
|
||||
// Return zero if the string has only one zero.
|
||||
if (Iter == Sent || FCharTraits::IsSpace(*Iter))
|
||||
{
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter != Sent) return false;
|
||||
|
||||
Value = static_cast<T>(bNegative ? -0.0 : 0.0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*Iter == LITERAL(TRangeElement<R>, 'x') || *Iter == LITERAL(TRangeElement<R>, 'X'))
|
||||
{
|
||||
Format = NAMESPACE_STD::chars_format::hex;
|
||||
++Iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Iter == Sent) return false;
|
||||
|
||||
T Result;
|
||||
|
||||
// Copy to a buffer if the range is not contiguous.
|
||||
if constexpr (!CContiguousRange<R> || !CSameAs<TRangeElement<R>, char>)
|
||||
{
|
||||
TArray<char, TInlineAllocator<64>> Buffer;
|
||||
|
||||
for (; Iter != Sent; ++Iter)
|
||||
{
|
||||
auto Char = *Iter;
|
||||
|
||||
// Ignore trailing spaces.
|
||||
if (FCharTraits::IsSpace(Char)) break;
|
||||
|
||||
// Assert that floating-point values must be represented by ASCII.
|
||||
if (FCharTraits::IsASCII(Char)) Buffer.PushBack(static_cast<char>(Char));
|
||||
|
||||
else return false;
|
||||
}
|
||||
|
||||
const char* First = Buffer.GetData();
|
||||
const char* Last = Buffer.GetData() + Buffer.Num();
|
||||
|
||||
NAMESPACE_STD::from_chars_result ConvertResult = NAMESPACE_STD::from_chars(First, Last, Result, Format);
|
||||
|
||||
if (ConvertResult.ec == NAMESPACE_STD::errc::result_out_of_range) return false;
|
||||
if (ConvertResult.ec == NAMESPACE_STD::errc::invalid_argument) return false;
|
||||
|
||||
// Assert that the buffer is fully parsed.
|
||||
if (ConvertResult.ptr != Last) return false;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
const char* First = ToAddress(Iter);
|
||||
const char* Last = ToAddress(Iter) + Algorithms::Distance(Iter, Sent);
|
||||
|
||||
NAMESPACE_STD::from_chars_result ConvertResult = NAMESPACE_STD::from_chars(First, Last, Result, Format);
|
||||
|
||||
if (ConvertResult.ec == NAMESPACE_STD::errc::result_out_of_range) return false;
|
||||
if (ConvertResult.ec == NAMESPACE_STD::errc::invalid_argument) return false;
|
||||
|
||||
// Move the iterator to the end of the parsed value.
|
||||
Algorithms::Advance(Iter, ConvertResult.ptr - First);
|
||||
}
|
||||
|
||||
// Ignore trailing spaces.
|
||||
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
|
||||
|
||||
if (Iter != Sent) return false;
|
||||
|
||||
Value = bNegative ? -Result : Result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a floating-point value from the given string range.
|
||||
* Ignore leading and trailing spaces and case-insensitive.
|
||||
* Automatically detect formats if multiple formats are allowed.
|
||||
* Allow parsing base prefixes: "0x" for hexadecimal.
|
||||
*
|
||||
* @param First - The iterator of the range.
|
||||
* @param Last - The sentinel of the range.
|
||||
* @param Value - The floating-point value to parse.
|
||||
* @param bFixed - Allow parsing fixed-point values.
|
||||
* @param bScientific - Allow parsing scientific notation values.
|
||||
* @param bHex - Allow parsing hex floating-point values.
|
||||
*
|
||||
* @return true if the value is successfully parsed, false otherwise.
|
||||
*/
|
||||
template <CStringIterator I, CSentinelFor<I> S, CFloatingPoint T> requires (!CConst<T> && !CVolatile<T>)
|
||||
FORCEINLINE constexpr bool Parse(I First, S Last, T& Value, bool bFixed = true, bool bScientific = true, bool bHex = true)
|
||||
{
|
||||
if constexpr (CSizedSentinelFor<S, I>)
|
||||
{
|
||||
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
|
||||
}
|
||||
|
||||
return Algorithms::Parse(Ranges::View(MoveTemp(First), Last), Value, bFixed, bScientific, bHex);
|
||||
}
|
||||
|
||||
NAMESPACE_END(Algorithms)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
#pragma warning(pop)
|
||||
3183
Redcraft.Utility/Source/Public/Strings/Formatting.h
Normal file
3183
Redcraft.Utility/Source/Public/Strings/Formatting.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1278
Redcraft.Utility/Source/Public/Strings/StringView.h
Normal file
1278
Redcraft.Utility/Source/Public/Strings/StringView.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -244,19 +244,19 @@ public:
|
||||
&& NAMESPACE_PRIVATE::CFAnyPlaceable<T> && CConstructibleFrom<TDecay<T>, T&&>)
|
||||
FORCEINLINE FAny& operator=(T&& InValue)
|
||||
{
|
||||
using DecayedType = TDecay<T>;
|
||||
using FDecayedType = TDecay<T>;
|
||||
|
||||
if constexpr (CAssignableFrom<DecayedType, T&&>)
|
||||
if constexpr (CAssignableFrom<FDecayedType, T&&>)
|
||||
{
|
||||
if (HoldsAlternative<DecayedType>())
|
||||
if (HoldsAlternative<FDecayedType>())
|
||||
{
|
||||
GetValue<DecayedType>() = Forward<T>(InValue);
|
||||
GetValue<FDecayedType>() = Forward<T>(InValue);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
Destroy();
|
||||
EmplaceImpl<DecayedType>(Forward<T>(InValue));
|
||||
EmplaceImpl<FDecayedType>(Forward<T>(InValue));
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -540,32 +540,32 @@ private:
|
||||
template <typename T, typename... Ts>
|
||||
void EmplaceImpl(Ts&&... Args)
|
||||
{
|
||||
using DecayedType = TDecay<T>;
|
||||
using FDecayedType = TDecay<T>;
|
||||
|
||||
TypeInfo = reinterpret_cast<uintptr>(&typeid(DecayedType));
|
||||
TypeInfo = reinterpret_cast<uintptr>(&typeid(FDecayedType));
|
||||
|
||||
if constexpr (CEmpty<DecayedType> && CTrivial<DecayedType>) return; // ERepresentation::Empty
|
||||
if constexpr (CEmpty<FDecayedType> && CTrivial<FDecayedType>) return; // ERepresentation::Empty
|
||||
|
||||
constexpr bool bIsTriviallyStorable = sizeof(DecayedType) <= sizeof(TrivialStorage.Internal) && alignof(DecayedType) <= alignof(FAny) && CTriviallyCopyable<DecayedType>;
|
||||
constexpr bool bIsSmallStorable = sizeof(DecayedType) <= sizeof( SmallStorage.Internal) && alignof(DecayedType) <= alignof(FAny);
|
||||
constexpr bool bIsTriviallyStorable = sizeof(FDecayedType) <= sizeof(TrivialStorage.Internal) && alignof(FDecayedType) <= alignof(FAny) && CTriviallyCopyable<FDecayedType>;
|
||||
constexpr bool bIsSmallStorable = sizeof(FDecayedType) <= sizeof( SmallStorage.Internal) && alignof(FDecayedType) <= alignof(FAny);
|
||||
|
||||
static constexpr const FRTTI SelectedRTTI(InPlaceType<DecayedType>);
|
||||
static constexpr const FRTTI SelectedRTTI(InPlaceType<FDecayedType>);
|
||||
|
||||
if constexpr (bIsTriviallyStorable)
|
||||
{
|
||||
new (&TrivialStorage.Internal) DecayedType(Forward<Ts>(Args)...);
|
||||
new (&TrivialStorage.Internal) FDecayedType(Forward<Ts>(Args)...);
|
||||
TypeInfo |= static_cast<uintptr>(ERepresentation::Trivial);
|
||||
}
|
||||
else if constexpr (bIsSmallStorable)
|
||||
{
|
||||
new (&SmallStorage.Internal) DecayedType(Forward<Ts>(Args)...);
|
||||
new (&SmallStorage.Internal) FDecayedType(Forward<Ts>(Args)...);
|
||||
SmallStorage.RTTI = &SelectedRTTI;
|
||||
TypeInfo |= static_cast<uintptr>(ERepresentation::Small);
|
||||
}
|
||||
else
|
||||
{
|
||||
BigStorage.External = Memory::Malloc(sizeof(DecayedType), alignof(DecayedType));
|
||||
new (BigStorage.External) DecayedType(Forward<Ts>(Args)...);
|
||||
BigStorage.External = Memory::Malloc(sizeof(FDecayedType), alignof(FDecayedType));
|
||||
new (BigStorage.External) FDecayedType(Forward<Ts>(Args)...);
|
||||
BigStorage.RTTI = &SelectedRTTI;
|
||||
TypeInfo |= static_cast<uintptr>(ERepresentation::Big);
|
||||
}
|
||||
|
||||
@@ -68,48 +68,48 @@ struct TAtomicImpl : FSingleton
|
||||
{
|
||||
protected:
|
||||
|
||||
using NativeAtomicType = TConditional<bIsRef, NAMESPACE_STD::atomic_ref<T>, NAMESPACE_STD::atomic<T>>;
|
||||
using FNativeAtomic = TConditional<bIsRef, NAMESPACE_STD::atomic_ref<T>, NAMESPACE_STD::atomic<T>>;
|
||||
|
||||
public:
|
||||
|
||||
using ValueType = T;
|
||||
using FValueType = T;
|
||||
|
||||
/** Indicates that the type is always lock-free */
|
||||
static constexpr bool bIsAlwaysLockFree = NativeAtomicType::is_always_lock_free;
|
||||
static constexpr bool bIsAlwaysLockFree = FNativeAtomic::is_always_lock_free;
|
||||
|
||||
/** Indicates the required alignment of an object to be referenced by TAtomicRef. */
|
||||
static constexpr size_t RequiredAlignment = NAMESPACE_STD::atomic_ref<T>::required_alignment;
|
||||
|
||||
/** Constructs an atomic object. */
|
||||
FORCEINLINE constexpr TAtomicImpl() requires (!bIsRef) : NativeAtomic() { };
|
||||
FORCEINLINE constexpr TAtomicImpl(ValueType Desired) requires (!bIsRef) : NativeAtomic(Desired) { };
|
||||
FORCEINLINE constexpr TAtomicImpl() requires (!bIsRef) : NativeAtomic() { }
|
||||
FORCEINLINE constexpr TAtomicImpl(FValueType Desired) requires (!bIsRef) : NativeAtomic(Desired) { }
|
||||
|
||||
/** Constructs an atomic reference. */
|
||||
FORCEINLINE explicit TAtomicImpl(ValueType& Desired) requires (bIsRef) : NativeAtomic(Desired) { check(Memory::IsAligned(&Desired, RequiredAlignment)); };
|
||||
FORCEINLINE TAtomicImpl(TAtomicImpl& InValue) requires (bIsRef) : NativeAtomic(InValue) { };
|
||||
FORCEINLINE explicit TAtomicImpl(FValueType& Desired) requires (bIsRef) : NativeAtomic(Desired) { check(Memory::IsAligned(&Desired, RequiredAlignment)); }
|
||||
FORCEINLINE TAtomicImpl(TAtomicImpl& InValue) requires (bIsRef) : NativeAtomic(InValue) { }
|
||||
|
||||
/** Stores a value into an atomic object. */
|
||||
FORCEINLINE ValueType operator=(ValueType Desired) { return NativeAtomic = Desired; }
|
||||
FORCEINLINE ValueType operator=(ValueType Desired) volatile requires (bIsAlwaysLockFree) { return NativeAtomic = Desired; }
|
||||
FORCEINLINE FValueType operator=(FValueType Desired) { return NativeAtomic = Desired; }
|
||||
FORCEINLINE FValueType operator=(FValueType Desired) volatile requires (bIsAlwaysLockFree) { return NativeAtomic = Desired; }
|
||||
|
||||
/** Atomically replaces the value of the atomic object with a non-atomic argument. */
|
||||
FORCEINLINE void Store(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.store(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Store(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.store(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Store(FValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.store(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Store(FValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.store(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically obtains the value of the atomic object. */
|
||||
NODISCARD FORCEINLINE ValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
NODISCARD FORCEINLINE ValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile requires (bIsAlwaysLockFree) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
NODISCARD FORCEINLINE FValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
NODISCARD FORCEINLINE FValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile requires (bIsAlwaysLockFree) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Loads a value from an atomic object. */
|
||||
NODISCARD FORCEINLINE operator ValueType() const { return static_cast<ValueType>(NativeAtomic); }
|
||||
NODISCARD FORCEINLINE operator ValueType() const volatile requires (bIsAlwaysLockFree) { return static_cast<ValueType>(NativeAtomic); }
|
||||
NODISCARD FORCEINLINE operator FValueType() const { return static_cast<FValueType>(NativeAtomic); }
|
||||
NODISCARD FORCEINLINE operator FValueType() const volatile requires (bIsAlwaysLockFree) { return static_cast<FValueType>(NativeAtomic); }
|
||||
|
||||
/** Atomically replaces the value of the atomic object and obtains the value held previously. */
|
||||
NODISCARD FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { return NativeAtomic.exchange(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
NODISCARD FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { return NativeAtomic.exchange(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
NODISCARD FORCEINLINE FValueType Exchange(FValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { return NativeAtomic.exchange(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
NODISCARD FORCEINLINE FValueType Exchange(FValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { return NativeAtomic.exchange(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. */
|
||||
NODISCARD FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, bool bIsWeak = false)
|
||||
NODISCARD FORCEINLINE bool CompareExchange(FValueType& Expected, FValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, bool bIsWeak = false)
|
||||
{
|
||||
MEMORY_ORDER_CHECK(Failure, 0x01 | 0x02 | 0x04 | 0x20);
|
||||
if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Success), static_cast<NAMESPACE_STD::memory_order>(Failure));
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
}
|
||||
|
||||
/** Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. */
|
||||
NODISCARD FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, bool bIsWeak = false) volatile requires (bIsAlwaysLockFree)
|
||||
NODISCARD FORCEINLINE bool CompareExchange(FValueType& Expected, FValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, bool bIsWeak = false) volatile requires (bIsAlwaysLockFree)
|
||||
{
|
||||
MEMORY_ORDER_CHECK(Failure, 0x01 | 0x02 | 0x04 | 0x20);
|
||||
if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Success), static_cast<NAMESPACE_STD::memory_order>(Failure));
|
||||
@@ -125,162 +125,162 @@ public:
|
||||
}
|
||||
|
||||
/** Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. */
|
||||
NODISCARD FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false)
|
||||
NODISCARD FORCEINLINE bool CompareExchange(FValueType& Expected, FValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false)
|
||||
{
|
||||
if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Order));
|
||||
else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Order));
|
||||
}
|
||||
|
||||
/** Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. */
|
||||
NODISCARD FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false) volatile requires (bIsAlwaysLockFree)
|
||||
NODISCARD FORCEINLINE bool CompareExchange(FValueType& Expected, FValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false) volatile requires (bIsAlwaysLockFree)
|
||||
{
|
||||
if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Order));
|
||||
else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Order));
|
||||
}
|
||||
|
||||
/** Blocks the thread until notified and the atomic value changes. */
|
||||
FORCEINLINE void Wait(ValueType Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); NativeAtomic.wait(Old, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Wait(ValueType Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); NativeAtomic.wait(Old, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Wait(FValueType Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); NativeAtomic.wait(Old, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Wait(FValueType Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); NativeAtomic.wait(Old, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Notifies at least one or all threads blocked waiting on the atomic object. */
|
||||
FORCEINLINE void Notify(bool bIsAll = false) { if (bIsAll) NativeAtomic.notify_all(); else NativeAtomic.notify_one(); }
|
||||
FORCEINLINE void Notify(bool bIsAll = false) volatile { if (bIsAll) NativeAtomic.notify_all(); else NativeAtomic.notify_one(); }
|
||||
|
||||
/** Atomically executes the 'Func' on the value stored in the atomic object and obtains the value held previously. */
|
||||
template <typename F> requires (CInvocableResult<ValueType, F, ValueType>)
|
||||
FORCEINLINE ValueType FetchFn(F&& Func, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent)
|
||||
template <typename F> requires (CInvocableResult<FValueType, F, FValueType>)
|
||||
FORCEINLINE FValueType FetchFn(F&& Func, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent)
|
||||
{
|
||||
ValueType Temp(Load(EMemoryOrder::Relaxed));
|
||||
FValueType Temp(Load(EMemoryOrder::Relaxed));
|
||||
// We do a weak read here because we require a loop.
|
||||
while (!CompareExchange(Temp, InvokeResult<ValueType>(Forward<F>(Func), Temp), Order, true));
|
||||
while (!CompareExchange(Temp, InvokeResult<FValueType>(Forward<F>(Func), Temp), Order, true));
|
||||
return Temp;
|
||||
}
|
||||
|
||||
/** Atomically executes the 'Func' on the value stored in the atomic object and obtains the value held previously. */
|
||||
template <typename F> requires (CInvocableResult<ValueType, F, ValueType> && bIsAlwaysLockFree)
|
||||
FORCEINLINE ValueType FetchFn(F&& Func, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile
|
||||
template <typename F> requires (CInvocableResult<FValueType, F, FValueType> && bIsAlwaysLockFree)
|
||||
FORCEINLINE FValueType FetchFn(F&& Func, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile
|
||||
{
|
||||
ValueType Temp(Load(EMemoryOrder::Relaxed));
|
||||
FValueType Temp(Load(EMemoryOrder::Relaxed));
|
||||
// We do a weak read here because we require a loop.
|
||||
while (!CompareExchange(Temp, InvokeResult<ValueType>(Forward<F>(Func), Temp), Order, true));
|
||||
while (!CompareExchange(Temp, InvokeResult<FValueType>(Forward<F>(Func), Temp), Order, true));
|
||||
return Temp;
|
||||
}
|
||||
|
||||
/** Atomically adds the argument to the value stored in the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchAdd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> || CFloatingPoint<T>) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchAdd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchAdd(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> || CFloatingPoint<T>) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchAdd(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically adds the argument to the value stored in the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchAdd(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CPointer<T> ) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchAdd(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchAdd(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CPointer<T> ) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchAdd(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically subtracts the argument from the value stored in the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchSub(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> || CFloatingPoint<T>) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchSub(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchSub(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> || CFloatingPoint<T>) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchSub(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically subtracts the argument from the value stored in the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchSub(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CPointer<T> ) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchSub(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchSub(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CPointer<T> ) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchSub(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically multiples the argument from the value stored in the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchMul(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> || CFloatingPoint<T>) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old * InValue; }); }
|
||||
FORCEINLINE ValueType FetchMul(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return FetchFn([InValue](ValueType Old) -> ValueType { return Old * InValue; }); }
|
||||
FORCEINLINE FValueType FetchMul(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> || CFloatingPoint<T>) { return FetchFn([InValue](FValueType Old) -> FValueType { return Old * InValue; }); }
|
||||
FORCEINLINE FValueType FetchMul(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return FetchFn([InValue](FValueType Old) -> FValueType { return Old * InValue; }); }
|
||||
|
||||
/** Atomically divides the argument from the value stored in the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchDiv(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> || CFloatingPoint<T>) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old / InValue; }); }
|
||||
FORCEINLINE ValueType FetchDiv(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return FetchFn([InValue](ValueType Old) -> ValueType { return Old / InValue; }); }
|
||||
FORCEINLINE FValueType FetchDiv(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> || CFloatingPoint<T>) { return FetchFn([InValue](FValueType Old) -> FValueType { return Old / InValue; }); }
|
||||
FORCEINLINE FValueType FetchDiv(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return FetchFn([InValue](FValueType Old) -> FValueType { return Old / InValue; }); }
|
||||
|
||||
/** Atomically models the argument from the value stored in the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchMod(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old % InValue; }); }
|
||||
FORCEINLINE ValueType FetchMod(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old % InValue; }); }
|
||||
FORCEINLINE FValueType FetchMod(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return FetchFn([InValue](FValueType Old) -> FValueType { return Old % InValue; }); }
|
||||
FORCEINLINE FValueType FetchMod(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchFn([InValue](FValueType Old) -> FValueType { return Old % InValue; }); }
|
||||
|
||||
/** Atomically performs bitwise AND between the argument and the value of the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchAnd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return NativeAtomic.fetch_and(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchAnd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_and(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchAnd(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return NativeAtomic.fetch_and(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchAnd(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_and(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically performs bitwise OR between the argument and the value of the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchOr(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return NativeAtomic.fetch_or(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchOr(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_or(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchOr(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return NativeAtomic.fetch_or(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchOr(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_or(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically performs bitwise XOR between the argument and the value of the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchXor(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return NativeAtomic.fetch_xor(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchXor(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_xor(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchXor(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return NativeAtomic.fetch_xor(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE FValueType FetchXor(FValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_xor(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Atomically performs bitwise LSH between the argument and the value of the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchLsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old << InValue; }); }
|
||||
FORCEINLINE ValueType FetchLsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old << InValue; }); }
|
||||
FORCEINLINE FValueType FetchLsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return FetchFn([InValue](FValueType Old) -> FValueType { return Old << InValue; }); }
|
||||
FORCEINLINE FValueType FetchLsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchFn([InValue](FValueType Old) -> FValueType { return Old << InValue; }); }
|
||||
|
||||
/** Atomically performs bitwise RSH between the argument and the value of the atomic object and obtains the value held previously. */
|
||||
FORCEINLINE ValueType FetchRsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old >> InValue; }); }
|
||||
FORCEINLINE ValueType FetchRsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old >> InValue; }); }
|
||||
FORCEINLINE FValueType FetchRsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral<T> ) { return FetchFn([InValue](FValueType Old) -> FValueType { return Old >> InValue; }); }
|
||||
FORCEINLINE FValueType FetchRsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchFn([InValue](FValueType Old) -> FValueType { return Old >> InValue; }); }
|
||||
|
||||
/** Increments the atomic value by one. */
|
||||
FORCEINLINE ValueType operator++() requires ((CIntegral<T> || CPointer<T>) ) { return ++NativeAtomic; }
|
||||
FORCEINLINE ValueType operator++() volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return ++NativeAtomic; }
|
||||
FORCEINLINE FValueType operator++() requires ((CIntegral<T> || CPointer<T>) ) { return ++NativeAtomic; }
|
||||
FORCEINLINE FValueType operator++() volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return ++NativeAtomic; }
|
||||
|
||||
/** Increments the atomic value by one. */
|
||||
FORCEINLINE ValueType operator++(int) requires ((CIntegral<T> || CPointer<T>) ) { return NativeAtomic++; }
|
||||
FORCEINLINE ValueType operator++(int) volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return NativeAtomic++; }
|
||||
FORCEINLINE FValueType operator++(int) requires ((CIntegral<T> || CPointer<T>) ) { return NativeAtomic++; }
|
||||
FORCEINLINE FValueType operator++(int) volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return NativeAtomic++; }
|
||||
|
||||
/** Decrements the atomic value by one. */
|
||||
FORCEINLINE ValueType operator--() requires ((CIntegral<T> || CPointer<T>) ) { return --NativeAtomic; }
|
||||
FORCEINLINE ValueType operator--() volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return --NativeAtomic; }
|
||||
FORCEINLINE FValueType operator--() requires ((CIntegral<T> || CPointer<T>) ) { return --NativeAtomic; }
|
||||
FORCEINLINE FValueType operator--() volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return --NativeAtomic; }
|
||||
|
||||
/** Decrements the atomic value by one. */
|
||||
FORCEINLINE ValueType operator--(int) requires ((CIntegral<T> || CPointer<T>) ) { return NativeAtomic--; }
|
||||
FORCEINLINE ValueType operator--(int) volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return NativeAtomic--; }
|
||||
FORCEINLINE FValueType operator--(int) requires ((CIntegral<T> || CPointer<T>) ) { return NativeAtomic--; }
|
||||
FORCEINLINE FValueType operator--(int) volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return NativeAtomic--; }
|
||||
|
||||
/** Adds with the atomic value. */
|
||||
FORCEINLINE ValueType operator+=(ValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return NativeAtomic += InValue; }
|
||||
FORCEINLINE ValueType operator+=(ValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return NativeAtomic += InValue; }
|
||||
FORCEINLINE FValueType operator+=(FValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return NativeAtomic += InValue; }
|
||||
FORCEINLINE FValueType operator+=(FValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return NativeAtomic += InValue; }
|
||||
|
||||
/** Adds with the atomic value. */
|
||||
FORCEINLINE ValueType operator+=(ptrdiff InValue) requires (CPointer<T> ) { return NativeAtomic += InValue; }
|
||||
FORCEINLINE ValueType operator+=(ptrdiff InValue) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic += InValue; }
|
||||
FORCEINLINE FValueType operator+=(ptrdiff InValue) requires (CPointer<T> ) { return NativeAtomic += InValue; }
|
||||
FORCEINLINE FValueType operator+=(ptrdiff InValue) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic += InValue; }
|
||||
|
||||
/** Subtracts with the atomic value. */
|
||||
FORCEINLINE ValueType operator-=(ValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return NativeAtomic -= InValue; }
|
||||
FORCEINLINE ValueType operator-=(ValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return NativeAtomic -= InValue; }
|
||||
FORCEINLINE FValueType operator-=(FValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return NativeAtomic -= InValue; }
|
||||
FORCEINLINE FValueType operator-=(FValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return NativeAtomic -= InValue; }
|
||||
|
||||
/** Subtracts with the atomic value. */
|
||||
FORCEINLINE ValueType operator-=(ptrdiff InValue) requires (CPointer<T> ) { return NativeAtomic -= InValue; }
|
||||
FORCEINLINE ValueType operator-=(ptrdiff InValue) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic -= InValue; }
|
||||
FORCEINLINE FValueType operator-=(ptrdiff InValue) requires (CPointer<T> ) { return NativeAtomic -= InValue; }
|
||||
FORCEINLINE FValueType operator-=(ptrdiff InValue) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic -= InValue; }
|
||||
|
||||
/** Multiples with the atomic value. */
|
||||
FORCEINLINE ValueType operator*=(ValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return FetchMul(InValue) * InValue; }
|
||||
FORCEINLINE ValueType operator*=(ValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return FetchMul(InValue) * InValue; }
|
||||
FORCEINLINE FValueType operator*=(FValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return FetchMul(InValue) * InValue; }
|
||||
FORCEINLINE FValueType operator*=(FValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return FetchMul(InValue) * InValue; }
|
||||
|
||||
/** Divides with the atomic value. */
|
||||
FORCEINLINE ValueType operator/=(ValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return FetchDiv(InValue) / InValue; }
|
||||
FORCEINLINE ValueType operator/=(ValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return FetchDiv(InValue) / InValue; }
|
||||
FORCEINLINE FValueType operator/=(FValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return FetchDiv(InValue) / InValue; }
|
||||
FORCEINLINE FValueType operator/=(FValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return FetchDiv(InValue) / InValue; }
|
||||
|
||||
/** Models with the atomic value. */
|
||||
FORCEINLINE ValueType operator%=(ValueType InValue) requires (CIntegral<T> ) { return FetchMod(InValue) % InValue; }
|
||||
FORCEINLINE ValueType operator%=(ValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchMod(InValue) % InValue; }
|
||||
FORCEINLINE FValueType operator%=(FValueType InValue) requires (CIntegral<T> ) { return FetchMod(InValue) % InValue; }
|
||||
FORCEINLINE FValueType operator%=(FValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchMod(InValue) % InValue; }
|
||||
|
||||
/** Performs bitwise AND with the atomic value. */
|
||||
FORCEINLINE ValueType operator&=(ValueType InValue) requires (CIntegral<T> ) { return NativeAtomic &= InValue; }
|
||||
FORCEINLINE ValueType operator&=(ValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic &= InValue; }
|
||||
FORCEINLINE FValueType operator&=(FValueType InValue) requires (CIntegral<T> ) { return NativeAtomic &= InValue; }
|
||||
FORCEINLINE FValueType operator&=(FValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic &= InValue; }
|
||||
|
||||
/** Performs bitwise OR with the atomic value. */
|
||||
FORCEINLINE ValueType operator|=(ValueType InValue) requires (CIntegral<T> ) { return NativeAtomic |= InValue; }
|
||||
FORCEINLINE ValueType operator|=(ValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic |= InValue; }
|
||||
FORCEINLINE FValueType operator|=(FValueType InValue) requires (CIntegral<T> ) { return NativeAtomic |= InValue; }
|
||||
FORCEINLINE FValueType operator|=(FValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic |= InValue; }
|
||||
|
||||
/** Performs bitwise XOR with the atomic value. */
|
||||
FORCEINLINE ValueType operator^=(ValueType InValue) requires (CIntegral<T> ) { return NativeAtomic ^= InValue; }
|
||||
FORCEINLINE ValueType operator^=(ValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic ^= InValue; }
|
||||
FORCEINLINE FValueType operator^=(FValueType InValue) requires (CIntegral<T> ) { return NativeAtomic ^= InValue; }
|
||||
FORCEINLINE FValueType operator^=(FValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic ^= InValue; }
|
||||
|
||||
/** Performs bitwise LSH with the atomic value. */
|
||||
FORCEINLINE ValueType operator<<=(size_t InValue) requires (CIntegral<T> ) { return FetchLsh(InValue) << InValue; }
|
||||
FORCEINLINE ValueType operator<<=(size_t InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchLsh(InValue) << InValue; }
|
||||
FORCEINLINE FValueType operator<<=(size_t InValue) requires (CIntegral<T> ) { return FetchLsh(InValue) << InValue; }
|
||||
FORCEINLINE FValueType operator<<=(size_t InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchLsh(InValue) << InValue; }
|
||||
|
||||
/** Performs bitwise RSH with the atomic value. */
|
||||
FORCEINLINE ValueType operator>>=(size_t InValue) requires (CIntegral<T> ) { return FetchRsh(InValue) >> InValue; }
|
||||
FORCEINLINE ValueType operator>>=(size_t InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchRsh(InValue) >> InValue; }
|
||||
FORCEINLINE FValueType operator>>=(size_t InValue) requires (CIntegral<T> ) { return FetchRsh(InValue) >> InValue; }
|
||||
FORCEINLINE FValueType operator>>=(size_t InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchRsh(InValue) >> InValue; }
|
||||
|
||||
protected:
|
||||
|
||||
NativeAtomicType NativeAtomic;
|
||||
FNativeAtomic NativeAtomic;
|
||||
|
||||
};
|
||||
|
||||
@@ -311,7 +311,7 @@ struct FAtomicFlag final : FSingleton
|
||||
public:
|
||||
|
||||
/** Constructs an atomic flag. */
|
||||
FORCEINLINE constexpr FAtomicFlag() : NativeAtomic() { };
|
||||
FORCEINLINE constexpr FAtomicFlag() = default;
|
||||
|
||||
/** Atomically sets flag to false. */
|
||||
FORCEINLINE void Clear(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.clear(static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "TypeTraits/Swappable.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
/** @return The pointer to the container element storage. */
|
||||
template <typename T> requires (requires(T&& Container) { { Container.GetData() } -> CPointer; })
|
||||
FORCEINLINE constexpr auto GetData(T&& Container)
|
||||
{
|
||||
return Container.GetData();
|
||||
}
|
||||
|
||||
/** Overloads the GetData algorithm for arrays. */
|
||||
template <typename T, size_t N> FORCEINLINE constexpr T* GetData( T(& Container)[N]) { return Container; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr T* GetData( T(&& Container)[N]) { return Container; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr const T* GetData(const T(& Container)[N]) { return Container; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr const T* GetData(const T(&& Container)[N]) { return Container; }
|
||||
|
||||
/** Overloads the GetData algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr const T* GetData(initializer_list<T> Container)
|
||||
{
|
||||
return Container.begin();
|
||||
}
|
||||
|
||||
/** @return The number of elements in the container. */
|
||||
template <typename T> requires (requires(T&& Container) { { Container.Num() } -> CConvertibleTo<size_t>; })
|
||||
FORCEINLINE constexpr auto GetNum(T&& Container)
|
||||
{
|
||||
return Container.Num();
|
||||
}
|
||||
|
||||
/** Overloads the GetNum algorithm for arrays. */
|
||||
template <typename T, size_t N> FORCEINLINE constexpr size_t GetNum( T(& )[N]) { return N; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr size_t GetNum( T(&&)[N]) { return N; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr size_t GetNum(const T(& )[N]) { return N; }
|
||||
template <typename T, size_t N> FORCEINLINE constexpr size_t GetNum(const T(&&)[N]) { return N; }
|
||||
|
||||
/** Overloads the GetNum algorithm for initializer_list. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr size_t GetNum(initializer_list<T> Container)
|
||||
{
|
||||
return Container.size();
|
||||
}
|
||||
|
||||
/** Overloads the Swap algorithm for arrays. */
|
||||
template <typename T, size_t N> requires (CSwappable<TRemoveAllExtents<T>>)
|
||||
FORCEINLINE constexpr void Swap(T(&A)[N], T(&B)[N])
|
||||
{
|
||||
for (size_t Index = 0; Index < N; ++Index)
|
||||
{
|
||||
Swap(A[Index], B[Index]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for arrays. */
|
||||
template <typename T, size_t N> requires (CHashable<TRemoveAllExtents<T>>)
|
||||
FORCEINLINE constexpr size_t GetTypeHash(T(&A)[N])
|
||||
{
|
||||
size_t Result = 3516520171;
|
||||
|
||||
for (size_t Index = 0; Index < N; ++Index)
|
||||
{
|
||||
Result = HashCombine(Result, GetTypeHash(A[Index]));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
// Make sure you call this function after you have destroyed the held object using Destroy().
|
||||
template <typename T, typename U>
|
||||
FORCEINLINE constexpr void Emplace(intptr InCallable, U&& Args)
|
||||
FORCEINLINE constexpr void Emplace(uintptr InCallable, U&& Args)
|
||||
{
|
||||
static_assert(CSameAs<TDecay<T>, TDecay<U>>);
|
||||
ValuePtr = reinterpret_cast<uintptr>(AddressOf(Args));
|
||||
@@ -262,29 +262,29 @@ public:
|
||||
{
|
||||
Callable = InCallable;
|
||||
|
||||
using DecayedType = TDecay<T>;
|
||||
using FDecayedType = TDecay<T>;
|
||||
|
||||
static constexpr const FRTTI SelectedRTTI(InPlaceType<DecayedType>);
|
||||
static constexpr const FRTTI SelectedRTTI(InPlaceType<FDecayedType>);
|
||||
RTTI = reinterpret_cast<uintptr>(&SelectedRTTI);
|
||||
|
||||
if constexpr (CEmpty<DecayedType> && CTrivial<DecayedType>) return; // ERepresentation::Empty
|
||||
if constexpr (CEmpty<FDecayedType> && CTrivial<FDecayedType>) return; // ERepresentation::Empty
|
||||
|
||||
constexpr bool bIsTriviallyStorable = sizeof(DecayedType) <= sizeof(InternalStorage) && alignof(DecayedType) <= alignof(TFunctionStorage) && CTriviallyCopyable<DecayedType>;
|
||||
constexpr bool bIsSmallStorable = sizeof(DecayedType) <= sizeof(InternalStorage) && alignof(DecayedType) <= alignof(TFunctionStorage);
|
||||
constexpr bool bIsTriviallyStorable = sizeof(FDecayedType) <= sizeof(InternalStorage) && alignof(FDecayedType) <= alignof(TFunctionStorage) && CTriviallyCopyable<FDecayedType>;
|
||||
constexpr bool bIsSmallStorable = sizeof(FDecayedType) <= sizeof(InternalStorage) && alignof(FDecayedType) <= alignof(TFunctionStorage);
|
||||
|
||||
if constexpr (bIsTriviallyStorable)
|
||||
{
|
||||
new (&InternalStorage) DecayedType(Forward<Ts>(Args)...);
|
||||
new (&InternalStorage) FDecayedType(Forward<Ts>(Args)...);
|
||||
RTTI |= static_cast<uintptr>(ERepresentation::Trivial);
|
||||
}
|
||||
else if constexpr (bIsSmallStorable)
|
||||
{
|
||||
new (&InternalStorage) DecayedType(Forward<Ts>(Args)...);
|
||||
new (&InternalStorage) FDecayedType(Forward<Ts>(Args)...);
|
||||
RTTI |= static_cast<uintptr>(ERepresentation::Small);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExternalStorage = new DecayedType(Forward<Ts>(Args)...);
|
||||
ExternalStorage = new FDecayedType(Forward<Ts>(Args)...);
|
||||
RTTI |= static_cast<uintptr>(ERepresentation::Big);
|
||||
}
|
||||
|
||||
@@ -446,12 +446,12 @@ template <typename Ret, typename... Ts, typename F> struct TIsInvocableSignature
|
||||
template <typename Ret, typename... Ts, typename F> struct TIsInvocableSignature<Ret(Ts...) const&&, F> : TBoolConstant<CInvocableResult<Ret, const F , Ts...>> { };
|
||||
|
||||
template <typename F> struct TFunctionInfo;
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) > { using Fn = Ret(Ts...); using CVRef = int; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) & > { using Fn = Ret(Ts...); using CVRef = int&; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) && > { using Fn = Ret(Ts...); using CVRef = int&&; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) const > { using Fn = Ret(Ts...); using CVRef = const int; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) const& > { using Fn = Ret(Ts...); using CVRef = const int&; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) const&&> { using Fn = Ret(Ts...); using CVRef = const int&&; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) > { using FFn = Ret(Ts...); using FCVRef = int; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) & > { using FFn = Ret(Ts...); using FCVRef = int&; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) && > { using FFn = Ret(Ts...); using FCVRef = int&&; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) const > { using FFn = Ret(Ts...); using FCVRef = const int; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) const& > { using FFn = Ret(Ts...); using FCVRef = const int&; };
|
||||
template <typename Ret, typename... Ts> struct TFunctionInfo<Ret(Ts...) const&&> { using FFn = Ret(Ts...); using FCVRef = const int&&; };
|
||||
|
||||
template <typename F, typename CVRef, bool bIsRef, bool bIsUnique = false> class TFunctionImpl;
|
||||
|
||||
@@ -460,8 +460,8 @@ class TFunctionImpl<Ret(Ts...), CVRef, bIsRef, bIsUnique>
|
||||
{
|
||||
public:
|
||||
|
||||
using ResultType = Ret;
|
||||
using ArgumentType = TTypeSequence<Ts...>;
|
||||
using FResultType = Ret;
|
||||
using FArgumentType = TTypeSequence<Ts...>;
|
||||
|
||||
FORCEINLINE constexpr TFunctionImpl() = default;
|
||||
FORCEINLINE constexpr TFunctionImpl(const TFunctionImpl&) = default;
|
||||
@@ -471,12 +471,12 @@ public:
|
||||
FORCEINLINE constexpr ~TFunctionImpl() = default;
|
||||
|
||||
/** Invokes the stored callable function target with the parameters args. */
|
||||
FORCEINLINE ResultType operator()(Ts... Args) requires (CSameAs<CVRef, int >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE ResultType operator()(Ts... Args) & requires (CSameAs<CVRef, int& >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE ResultType operator()(Ts... Args) && requires (CSameAs<CVRef, int&&>) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE ResultType operator()(Ts... Args) const requires (CSameAs<CVRef, const int >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE ResultType operator()(Ts... Args) const& requires (CSameAs<CVRef, const int& >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE ResultType operator()(Ts... Args) const&& requires (CSameAs<CVRef, const int&&>) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE FResultType operator()(Ts... Args) requires (CSameAs<CVRef, int >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE FResultType operator()(Ts... Args) & requires (CSameAs<CVRef, int& >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE FResultType operator()(Ts... Args) && requires (CSameAs<CVRef, int&&>) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE FResultType operator()(Ts... Args) const requires (CSameAs<CVRef, const int >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE FResultType operator()(Ts... Args) const& requires (CSameAs<CVRef, const int& >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE FResultType operator()(Ts... Args) const&& requires (CSameAs<CVRef, const int&&>) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
|
||||
/** @return false if instance stores a callable function target, true otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(nullptr_t) const& requires (!bIsRef) { return !IsValid(); }
|
||||
@@ -487,14 +487,14 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
using CallableType = ResultType(*)(uintptr, Ts&&...);
|
||||
using FCallableType = FResultType(*)(uintptr, Ts&&...);
|
||||
|
||||
TFunctionStorage<bIsRef, bIsUnique> Storage;
|
||||
|
||||
FORCEINLINE ResultType CallImpl(Ts&&... Args) const
|
||||
FORCEINLINE FResultType CallImpl(Ts&&... Args) const
|
||||
{
|
||||
checkf(Storage.IsValid(), TEXT("Attempting to call an unbound TFunction!"));
|
||||
CallableType Callable = reinterpret_cast<CallableType>(Storage.GetCallable());
|
||||
FCallableType Callable = reinterpret_cast<FCallableType>(Storage.GetCallable());
|
||||
return Callable(Storage.GetValuePtr(), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
@@ -510,26 +510,26 @@ protected:
|
||||
template <typename T, typename... Us>
|
||||
FORCEINLINE constexpr TDecay<T>& Emplace(Us&&... Args)
|
||||
{
|
||||
using DecayedType = TDecay<T>;
|
||||
using FDecayedType = TDecay<T>;
|
||||
|
||||
// This add a l-value reference to a non-reference type, while preserving the r-value reference.
|
||||
using ObjectType = TCopyCVRef<CVRef, DecayedType>;
|
||||
using InvokeType = TConditional<CReference<ObjectType>, ObjectType, ObjectType&>;
|
||||
using FObjectType = TCopyCVRef<CVRef, FDecayedType>;
|
||||
using FInvokeType = TConditional<CReference<FObjectType>, FObjectType, FObjectType&>;
|
||||
|
||||
CallableType Callable = [](uintptr ObjectPtr, Ts&&... Args) -> ResultType
|
||||
FCallableType Callable = [](uintptr ObjectPtr, Ts&&... Args) -> FResultType
|
||||
{
|
||||
return InvokeResult<ResultType>(
|
||||
static_cast<InvokeType>(*reinterpret_cast<DecayedType*>(ObjectPtr)),
|
||||
return InvokeResult<FResultType>(
|
||||
static_cast<FInvokeType>(*reinterpret_cast<FDecayedType*>(ObjectPtr)),
|
||||
Forward<Ts>(Args)...
|
||||
);
|
||||
};
|
||||
|
||||
Storage.template Emplace<DecayedType>(
|
||||
Storage.template Emplace<FDecayedType>(
|
||||
reinterpret_cast<uintptr>(Callable),
|
||||
Forward<Us>(Args)...
|
||||
);
|
||||
|
||||
return *reinterpret_cast<DecayedType*>(Storage.GetValuePtr());
|
||||
return *reinterpret_cast<FDecayedType*>(Storage.GetValuePtr());
|
||||
}
|
||||
|
||||
friend FORCEINLINE constexpr void Swap(TFunctionImpl& A, TFunctionImpl& B) requires (!bIsRef) { Swap(A.Storage, B.Storage); }
|
||||
@@ -552,15 +552,15 @@ NAMESPACE_PRIVATE_END
|
||||
template <CFunction F>
|
||||
class TFunctionRef final
|
||||
: public NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::Fn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::CVRef,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FFn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FCVRef,
|
||||
true>
|
||||
{
|
||||
private:
|
||||
|
||||
using Impl = NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::Fn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::CVRef,
|
||||
using FImpl = NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FFn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FCVRef,
|
||||
true>;
|
||||
|
||||
public:
|
||||
@@ -585,7 +585,7 @@ public:
|
||||
FORCEINLINE constexpr TFunctionRef(T&& InValue)
|
||||
{
|
||||
checkf(NAMESPACE_PRIVATE::FunctionIsBound(InValue), TEXT("Cannot bind a null/unbound callable to a TFunctionRef"));
|
||||
Impl::template Emplace<T>(Forward<T>(InValue));
|
||||
FImpl::template Emplace<T>(Forward<T>(InValue));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -602,21 +602,21 @@ public:
|
||||
template <CFunction F>
|
||||
class TFunction final
|
||||
: public NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::Fn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::CVRef,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FFn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FCVRef,
|
||||
false, false>
|
||||
{
|
||||
private:
|
||||
|
||||
using Impl = NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::Fn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::CVRef,
|
||||
using FImpl = NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FFn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FCVRef,
|
||||
false, false>;
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor. */
|
||||
FORCEINLINE constexpr TFunction(nullptr_t = nullptr) { Impl::Invalidate(); }
|
||||
FORCEINLINE constexpr TFunction(nullptr_t = nullptr) { FImpl::Invalidate(); }
|
||||
|
||||
FORCEINLINE TFunction(const TFunction&) = default;
|
||||
FORCEINLINE TFunction(TFunction&&) = default;
|
||||
@@ -634,8 +634,8 @@ public:
|
||||
&& NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value)
|
||||
FORCEINLINE TFunction(T&& InValue)
|
||||
{
|
||||
if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::Invalidate();
|
||||
else Impl::template Emplace<T>(Forward<T>(InValue));
|
||||
if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) FImpl::Invalidate();
|
||||
else FImpl::template Emplace<T>(Forward<T>(InValue));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -647,7 +647,7 @@ public:
|
||||
&& CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE explicit TFunction(TInPlaceType<T>, Ts&&... Args)
|
||||
{
|
||||
Impl::template Emplace<T>(Forward<Ts>(Args)...);
|
||||
FImpl::template Emplace<T>(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,7 +659,7 @@ public:
|
||||
&& CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE explicit TFunction(TInPlaceType<T>, initializer_list<U> IL, Ts&&... Args)
|
||||
{
|
||||
Impl::template Emplace<T>(IL, Forward<Ts>(Args)...);
|
||||
FImpl::template Emplace<T>(IL, Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Removes any bound callable from the TFunction, restoring it to the default empty state. */
|
||||
@@ -692,8 +692,8 @@ public:
|
||||
&& CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE TDecay<T>& Emplace(Ts&&... Args)
|
||||
{
|
||||
Impl::Destroy();
|
||||
return Impl::template Emplace<T>(Forward<Ts>(Args)...);
|
||||
FImpl::Destroy();
|
||||
return FImpl::template Emplace<T>(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -710,15 +710,15 @@ public:
|
||||
&& CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE TDecay<T>& Emplace(initializer_list<U> IL, Ts&&... Args)
|
||||
{
|
||||
Impl::Destroy();
|
||||
return Impl::template Emplace<T>(IL, Forward<Ts>(Args)...);
|
||||
FImpl::Destroy();
|
||||
return FImpl::template Emplace<T>(IL, Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Removes any bound callable from the TFunction, restoring it to the default empty state. */
|
||||
FORCEINLINE constexpr void Reset() { Impl::Destroy(); Impl::Invalidate(); }
|
||||
FORCEINLINE constexpr void Reset() { FImpl::Destroy(); FImpl::Invalidate(); }
|
||||
|
||||
/** Overloads the Swap algorithm for TFunction. */
|
||||
friend FORCEINLINE constexpr void Swap(TFunction& A, TFunction& B) { Swap(static_cast<Impl&>(A), static_cast<Impl&>(B)); }
|
||||
friend FORCEINLINE constexpr void Swap(TFunction& A, TFunction& B) { Swap(static_cast<FImpl&>(A), static_cast<FImpl&>(B)); }
|
||||
|
||||
};
|
||||
|
||||
@@ -731,21 +731,21 @@ public:
|
||||
template <CFunction F>
|
||||
class TUniqueFunction final
|
||||
: public NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::Fn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::CVRef,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FFn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FCVRef,
|
||||
false, true>
|
||||
{
|
||||
private:
|
||||
|
||||
using Impl = NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::Fn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::CVRef,
|
||||
using FImpl = NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FFn,
|
||||
typename NAMESPACE_PRIVATE::TFunctionInfo<F>::FCVRef,
|
||||
false, true>;
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor. */
|
||||
FORCEINLINE constexpr TUniqueFunction(nullptr_t = nullptr) { Impl::Invalidate(); }
|
||||
FORCEINLINE constexpr TUniqueFunction(nullptr_t = nullptr) { FImpl::Invalidate(); }
|
||||
|
||||
FORCEINLINE TUniqueFunction(const TUniqueFunction&) = delete;
|
||||
FORCEINLINE TUniqueFunction(TUniqueFunction&&) = default;
|
||||
@@ -788,8 +788,8 @@ public:
|
||||
&& NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value)
|
||||
FORCEINLINE TUniqueFunction(T&& InValue)
|
||||
{
|
||||
if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::Invalidate();
|
||||
else Impl::template Emplace<T>(Forward<T>(InValue));
|
||||
if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) FImpl::Invalidate();
|
||||
else FImpl::template Emplace<T>(Forward<T>(InValue));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -800,7 +800,7 @@ public:
|
||||
&& CConstructibleFrom<TDecay<T>, Ts...> && CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE explicit TUniqueFunction(TInPlaceType<T>, Ts&&... Args)
|
||||
{
|
||||
Impl::template Emplace<T>(Forward<Ts>(Args)...);
|
||||
FImpl::template Emplace<T>(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -811,11 +811,11 @@ public:
|
||||
&& CConstructibleFrom<TDecay<T>, initializer_list<U>, Ts...> && CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE explicit TUniqueFunction(TInPlaceType<T>, initializer_list<U> IL, Ts&&... Args)
|
||||
{
|
||||
Impl::template Emplace<T>(IL, Forward<Ts>(Args)...);
|
||||
FImpl::template Emplace<T>(IL, Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Removes any bound callable from the TUniqueFunction, restoring it to the default empty state. */
|
||||
FORCEINLINE constexpr TUniqueFunction& operator=(nullptr_t) { Impl::Destroy(); Impl::Invalidate(); return *this; }
|
||||
FORCEINLINE constexpr TUniqueFunction& operator=(nullptr_t) { FImpl::Destroy(); FImpl::Invalidate(); return *this; }
|
||||
|
||||
template <typename T> requires (NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value
|
||||
&& !CTFunctionRef<TDecay<T>> && !CTFunction<TDecay<T>> && !CTUniqueFunction<TDecay<T>>
|
||||
@@ -841,9 +841,9 @@ public:
|
||||
&& CConstructibleFrom<TDecay<T>, Ts...> && CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE TDecay<T>& Emplace(Ts&&... Args)
|
||||
{
|
||||
Impl::Destroy();
|
||||
using DecayedType = TDecay<T>;
|
||||
return Impl::template Emplace<T>(Forward<Ts>(Args)...);
|
||||
FImpl::Destroy();
|
||||
|
||||
return FImpl::template Emplace<T>(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -859,16 +859,16 @@ public:
|
||||
&& CConstructibleFrom<TDecay<T>, initializer_list<U>, Ts...> && CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE TDecay<T>& Emplace(initializer_list<U> IL, Ts&&... Args)
|
||||
{
|
||||
Impl::Destroy();
|
||||
using DecayedType = TDecay<T>;
|
||||
return Impl::template Emplace<T>(IL, Forward<Ts>(Args)...);
|
||||
FImpl::Destroy();
|
||||
|
||||
return FImpl::template Emplace<T>(IL, Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Removes any bound callable from the TUniqueFunction, restoring it to the default empty state. */
|
||||
FORCEINLINE constexpr void Reset() { Impl::Destroy(); Impl::Invalidate(); }
|
||||
FORCEINLINE constexpr void Reset() { FImpl::Destroy(); FImpl::Invalidate(); }
|
||||
|
||||
/** Overloads the Swap algorithm for TUniqueFunction. */
|
||||
friend FORCEINLINE constexpr void Swap(TUniqueFunction& A, TUniqueFunction& B) { Swap(static_cast<Impl&>(A), static_cast<Impl&>(B)); }
|
||||
friend FORCEINLINE constexpr void Swap(TUniqueFunction& A, TUniqueFunction& B) { Swap(static_cast<FImpl&>(A), static_cast<FImpl&>(B)); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
struct InvokeFunction
|
||||
struct FInvokeFunction
|
||||
{
|
||||
template <typename F, typename... Ts>
|
||||
static auto Invoke(F&& Object, Ts&&... Args)
|
||||
@@ -20,7 +20,7 @@ struct InvokeFunction
|
||||
}
|
||||
};
|
||||
|
||||
struct InvokeMemberFunction
|
||||
struct FInvokeMemberFunction
|
||||
{
|
||||
template <typename F, typename ObjectType, typename... Ts>
|
||||
static auto Invoke(F&& Func, ObjectType&& Object, Ts&&... Args)
|
||||
@@ -37,7 +37,7 @@ struct InvokeMemberFunction
|
||||
}
|
||||
};
|
||||
|
||||
struct InvokeMemberObject
|
||||
struct FInvokeMemberObject
|
||||
{
|
||||
template <typename F, typename ObjectType>
|
||||
static auto Invoke(F&& Func, ObjectType&& Object)
|
||||
@@ -59,34 +59,34 @@ template <typename F,
|
||||
typename Decayed = TDecay<F>,
|
||||
bool IsMemberFunction = CMemberFunctionPointer<Decayed>,
|
||||
bool IsMemberObject = CMemberObjectPointer<Decayed>>
|
||||
struct InvokeMember;
|
||||
struct FInvokeMember;
|
||||
|
||||
template <typename F, typename T, typename Decayed>
|
||||
struct InvokeMember<F, T, Decayed, true, false> : InvokeMemberFunction { };
|
||||
struct FInvokeMember<F, T, Decayed, true, false> : FInvokeMemberFunction { };
|
||||
|
||||
template <typename F, typename T, typename Decayed>
|
||||
struct InvokeMember<F, T, Decayed, false, true> : InvokeMemberObject { };
|
||||
struct FInvokeMember<F, T, Decayed, false, true> : FInvokeMemberObject { };
|
||||
|
||||
template <typename F, typename T, typename Decayed>
|
||||
struct InvokeMember<F, T, Decayed, false, false> : InvokeFunction { };
|
||||
struct FInvokeMember<F, T, Decayed, false, false> : FInvokeFunction { };
|
||||
|
||||
template <typename F, typename... Ts>
|
||||
struct InvokeImpl;
|
||||
struct FInvokeImpl;
|
||||
|
||||
template <typename F>
|
||||
struct InvokeImpl<F> : InvokeFunction { };
|
||||
struct FInvokeImpl<F> : FInvokeFunction { };
|
||||
|
||||
template <typename F, typename T, typename... Ts>
|
||||
struct InvokeImpl<F, T, Ts...> : InvokeMember<F, T> { };
|
||||
struct FInvokeImpl<F, T, Ts...> : FInvokeMember<F, T> { };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/** Invoke the Callable object f with the parameters args. */
|
||||
template <typename F, typename... Ts> requires (CInvocable<F, Ts...>)
|
||||
FORCEINLINE constexpr auto Invoke(F&& Func, Ts&&... Args)
|
||||
-> decltype(NAMESPACE_PRIVATE::InvokeImpl<F, Ts...>::Invoke(Forward<F>(Func), Forward<Ts>(Args)...))
|
||||
-> decltype(NAMESPACE_PRIVATE::FInvokeImpl<F, Ts...>::Invoke(Forward<F>(Func), Forward<Ts>(Args)...))
|
||||
{
|
||||
return NAMESPACE_PRIVATE::InvokeImpl<F, Ts...>::Invoke(Forward<F>(Func), Forward<Ts>(Args)...);
|
||||
return NAMESPACE_PRIVATE::FInvokeImpl<F, Ts...>::Invoke(Forward<F>(Func), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Invoke the Callable object f with the parameters args. */
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
@@ -12,9 +11,8 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
template <typename T, T... Ints>
|
||||
struct TIntegerSequence
|
||||
{
|
||||
using ValueType = T;
|
||||
using FValueType = T;
|
||||
FORCEINLINE static constexpr size_t Num() { return sizeof...(Ints); }
|
||||
FORCEINLINE static constexpr const T* GetData() { return NAMESPACE_REDCRAFT::GetData({ Ints... }); }
|
||||
};
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
@@ -24,7 +22,7 @@ NAMESPACE_PRIVATE_BEGIN
|
||||
template <unsigned N, typename T>
|
||||
struct TMakeIntegerSequenceImpl
|
||||
{
|
||||
using Type = typename __make_integer_seq<TIntegerSequence, T, N>;
|
||||
using FType = typename __make_integer_seq<TIntegerSequence, T, N>;
|
||||
};
|
||||
|
||||
#elif __has_builtin(__make_integer_seq)
|
||||
@@ -32,7 +30,7 @@ struct TMakeIntegerSequenceImpl
|
||||
template <unsigned N, typename T>
|
||||
struct TMakeIntegerSequenceImpl
|
||||
{
|
||||
using Type = typename __make_integer_seq<TIntegerSequence, T, N>;
|
||||
using FType = typename __make_integer_seq<TIntegerSequence, T, N>;
|
||||
};
|
||||
|
||||
#else
|
||||
@@ -40,13 +38,13 @@ struct TMakeIntegerSequenceImpl
|
||||
template <unsigned N, typename T, T... Ints>
|
||||
struct TMakeIntegerSequenceImpl
|
||||
{
|
||||
using Type = typename TMakeIntegerSequenceImpl<N - 1, T, T(N - 1), Ints...>::Type;
|
||||
using FType = typename TMakeIntegerSequenceImpl<N - 1, T, T(N - 1), Ints...>::FType;
|
||||
};
|
||||
|
||||
template <typename T, T... Ints>
|
||||
struct TMakeIntegerSequenceImpl<0, T, Ints...>
|
||||
{
|
||||
using Type = TIntegerSequence<T, Ints...>;
|
||||
using FType = TIntegerSequence<T, Ints...>;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -57,7 +55,7 @@ template <size_t... Ints>
|
||||
using TIndexSequence = TIntegerSequence<size_t, Ints...>;
|
||||
|
||||
template <typename T, T N>
|
||||
using TMakeIntegerSequence = typename NAMESPACE_PRIVATE::TMakeIntegerSequenceImpl<N, T>::Type;
|
||||
using TMakeIntegerSequence = typename NAMESPACE_PRIVATE::TMakeIntegerSequenceImpl<N, T>::FType;
|
||||
|
||||
template <size_t N>
|
||||
using TMakeIndexSequence = TMakeIntegerSequence<size_t, N>;
|
||||
@@ -103,7 +101,7 @@ struct TFrontImpl;
|
||||
template <typename T, typename... Ts>
|
||||
struct TFrontImpl<TTypeSequence<T, Ts...>>
|
||||
{
|
||||
using Type = T;
|
||||
using FType = T;
|
||||
};
|
||||
|
||||
template <typename TSequence>
|
||||
@@ -112,7 +110,7 @@ struct TPopImpl;
|
||||
template <typename T, typename... Ts>
|
||||
struct TPopImpl<TTypeSequence<T, Ts...>>
|
||||
{
|
||||
using Type = TTypeSequence<Ts...>;
|
||||
using FType = TTypeSequence<Ts...>;
|
||||
};
|
||||
|
||||
template <typename T, typename TSequence>
|
||||
@@ -121,19 +119,19 @@ struct TPushImpl;
|
||||
template <typename T, typename... Ts>
|
||||
struct TPushImpl<T, TTypeSequence<Ts...>>
|
||||
{
|
||||
using Type = TTypeSequence<T, Ts...>;
|
||||
using FType = TTypeSequence<T, Ts...>;
|
||||
};
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <CTTypeSequence TSequence>
|
||||
using TFront = typename NAMESPACE_PRIVATE::TFrontImpl<TSequence>::Type;
|
||||
using TFront = typename NAMESPACE_PRIVATE::TFrontImpl<TSequence>::FType;
|
||||
|
||||
template <CTTypeSequence TSequence>
|
||||
using TPop = typename NAMESPACE_PRIVATE::TPopImpl<TSequence>::Type;
|
||||
using TPop = typename NAMESPACE_PRIVATE::TPopImpl<TSequence>::FType;
|
||||
|
||||
template <typename T, typename TSequence>
|
||||
using TPush = typename NAMESPACE_PRIVATE::TPushImpl<T, TSequence>::Type;
|
||||
using TPush = typename NAMESPACE_PRIVATE::TPushImpl<T, TSequence>::FType;
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
@@ -194,19 +192,19 @@ struct TIndexAssert
|
||||
template <size_t I, typename TSequence>
|
||||
struct TTypeImpl
|
||||
{
|
||||
using Type = typename TTypeImpl<I - 1, TPop<TSequence>>::Type;
|
||||
using FType = typename TTypeImpl<I - 1, TPop<TSequence>>::FType;
|
||||
};
|
||||
|
||||
template <typename TSequence>
|
||||
struct TTypeImpl<0, TSequence>
|
||||
{
|
||||
using Type = TFront<TSequence>;
|
||||
using FType = TFront<TSequence>;
|
||||
};
|
||||
|
||||
template <typename TSequence>
|
||||
struct TTypeImpl<INDEX_NONE, TSequence>
|
||||
{
|
||||
using Type = void;
|
||||
using FType = void;
|
||||
};
|
||||
|
||||
template <size_t I, typename TSequence>
|
||||
@@ -214,7 +212,7 @@ struct TTypeAssert
|
||||
{
|
||||
static_assert(I < TSizeImpl<TSequence>::Value, "I is invalid index in type sequence");
|
||||
static constexpr size_t SafeIndex = I < TSizeImpl<TSequence>::Value ? I : INDEX_NONE;
|
||||
using Type = TCopyCV<TSequence, typename TTypeImpl<SafeIndex, TSequence>::Type>;
|
||||
using FType = TCopyCV<TSequence, typename TTypeImpl<SafeIndex, TSequence>::FType>;
|
||||
};
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
@@ -226,29 +224,29 @@ template <typename T, CTTypeSequence TSequence>
|
||||
inline constexpr size_t TIndex = NAMESPACE_PRIVATE::TIndexAssert<T, TSequence>::Value;
|
||||
|
||||
template <size_t I, CTTypeSequence TSequence>
|
||||
using TType = typename NAMESPACE_PRIVATE::TTypeAssert<I, TSequence>::Type;
|
||||
using TType = typename NAMESPACE_PRIVATE::TTypeAssert<I, TSequence>::FType;
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename TSequence>
|
||||
struct TUniqueTypeSequenceImpl
|
||||
{
|
||||
using FrontType = TFront<TSequence>;
|
||||
using NextSequence = TPop<TSequence>;
|
||||
using NextUniqueSequence = typename TUniqueTypeSequenceImpl<NextSequence>::Type;
|
||||
using Type = TConditional<!CExistentType<FrontType, NextSequence>, TPush<FrontType, NextUniqueSequence>, NextUniqueSequence>;
|
||||
using FFrontType = TFront<TSequence>;
|
||||
using FNextSequence = TPop<TSequence>;
|
||||
using FNextUniqueSequence = typename TUniqueTypeSequenceImpl<FNextSequence>::FType;
|
||||
using FType = TConditional<!CExistentType<FFrontType, FNextSequence>, TPush<FFrontType, FNextUniqueSequence>, FNextUniqueSequence>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TUniqueTypeSequenceImpl<TTypeSequence<>>
|
||||
{
|
||||
using Type = TTypeSequence<>;
|
||||
using FType = TTypeSequence<>;
|
||||
};
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <CTTypeSequence TSequence>
|
||||
using TUniqueTypeSequence = typename NAMESPACE_PRIVATE::TUniqueTypeSequenceImpl<TSequence>::Type;
|
||||
using TUniqueTypeSequence = typename NAMESPACE_PRIVATE::TUniqueTypeSequenceImpl<TSequence>::FType;
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ class TOptional<T, false> final
|
||||
{
|
||||
public:
|
||||
|
||||
using ValueType = T;
|
||||
using FValueType = T;
|
||||
|
||||
static_assert(!CReference<ValueType>);
|
||||
static_assert(!CReference<FValueType>);
|
||||
|
||||
/** Constructs an object that does not contain a value. */
|
||||
FORCEINLINE constexpr TOptional() : bIsValid(false) { }
|
||||
@@ -56,8 +56,8 @@ public:
|
||||
FORCEINLINE constexpr TOptional(FInvalid) : TOptional() { }
|
||||
|
||||
/** Constructs an object with initial content an object, direct-initialized from Forward<U>(InValue). */
|
||||
template <typename U = T> requires (CConstructibleFrom<T, U&&>)
|
||||
&& (!CSameAs<TRemoveCVRef<U>, FInPlace>) && (!CSameAs<TOptional, TRemoveCVRef<U>>)
|
||||
template <typename U = T> requires (CConstructibleFrom<T, U&&>
|
||||
&& !CSameAs<TRemoveCVRef<U>, FInPlace> && !CSameAs<TOptional, TRemoveCVRef<U>>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<U&&, T>) TOptional(U&& InValue)
|
||||
: TOptional(InPlace, Forward<U>(InValue))
|
||||
{ }
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
FORCEINLINE constexpr explicit TOptional(FInPlace, Ts&&... Args)
|
||||
: bIsValid(true)
|
||||
{
|
||||
new (&Value) ValueType(Forward<Ts>(Args)...);
|
||||
new (&Value) FValueType(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Constructs an object with initial content an object, direct-non-list-initialized from IL, Forward<Ts>(Args).... */
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
FORCEINLINE constexpr explicit TOptional(FInPlace, initializer_list<W> IL, Ts&&... Args)
|
||||
: bIsValid(true)
|
||||
{
|
||||
new (&Value) ValueType(IL, Forward<Ts>(Args)...);
|
||||
new (&Value) FValueType(IL, Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Copies content of other into a new instance. */
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CCopyConstructible<T> && !CTriviallyCopyConstructible<T>)
|
||||
: bIsValid(InValue.IsValid())
|
||||
{
|
||||
if (InValue.IsValid()) new (&Value) ValueType(InValue.GetValue());
|
||||
if (InValue.IsValid()) new (&Value) FValueType(InValue.GetValue());
|
||||
}
|
||||
|
||||
/** Moves content of other into a new instance. */
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
FORCEINLINE constexpr TOptional(TOptional&& InValue) requires (CMoveConstructible<T> && !CTriviallyMoveConstructible<T>)
|
||||
: bIsValid(InValue.IsValid())
|
||||
{
|
||||
if (InValue.IsValid()) new (&Value) ValueType(MoveTemp(InValue.GetValue()));
|
||||
if (InValue.IsValid()) new (&Value) FValueType(MoveTemp(InValue.GetValue()));
|
||||
}
|
||||
|
||||
/** Converting copy constructor. */
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const U&, T>) TOptional(const TOptional<U>& InValue)
|
||||
: bIsValid(InValue.IsValid())
|
||||
{
|
||||
if (InValue.IsValid()) new (&Value) ValueType(InValue.GetValue());
|
||||
if (InValue.IsValid()) new (&Value) FValueType(InValue.GetValue());
|
||||
}
|
||||
|
||||
/** Converting move constructor. */
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<U&&, T>) TOptional(TOptional<U>&& InValue)
|
||||
: bIsValid(InValue.IsValid())
|
||||
{
|
||||
if (InValue.IsValid()) new (&Value) ValueType(MoveTemp(InValue.GetValue()));
|
||||
if (InValue.IsValid()) new (&Value) FValueType(MoveTemp(InValue.GetValue()));
|
||||
}
|
||||
|
||||
/** Destroys the contained object, if any, as if by a call to Reset(). */
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
if (IsValid()) GetValue() = InValue.GetValue();
|
||||
else
|
||||
{
|
||||
new (&Value) ValueType(InValue.GetValue());
|
||||
new (&Value) FValueType(InValue.GetValue());
|
||||
bIsValid = true;
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ public:
|
||||
if (IsValid()) GetValue() = MoveTemp(InValue.GetValue());
|
||||
else
|
||||
{
|
||||
new (&Value) ValueType(MoveTemp(InValue.GetValue()));
|
||||
new (&Value) FValueType(MoveTemp(InValue.GetValue()));
|
||||
bIsValid = true;
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public:
|
||||
if (IsValid()) GetValue() = InValue.GetValue();
|
||||
else
|
||||
{
|
||||
new (&Value) ValueType(InValue.GetValue());
|
||||
new (&Value) FValueType(InValue.GetValue());
|
||||
bIsValid = true;
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ public:
|
||||
if (IsValid()) GetValue() = MoveTemp(InValue.GetValue());
|
||||
else
|
||||
{
|
||||
new (&Value) ValueType(MoveTemp(InValue.GetValue()));
|
||||
new (&Value) FValueType(MoveTemp(InValue.GetValue()));
|
||||
bIsValid = true;
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ public:
|
||||
if (IsValid()) GetValue() = Forward<U>(InValue);
|
||||
else
|
||||
{
|
||||
new (&Value) ValueType(Forward<U>(InValue));
|
||||
new (&Value) FValueType(Forward<U>(InValue));
|
||||
bIsValid = true;
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ public:
|
||||
{
|
||||
Reset();
|
||||
|
||||
T* Result = new (&Value) ValueType(Forward<Ts>(Args)...);
|
||||
T* Result = new (&Value) FValueType(Forward<Ts>(Args)...);
|
||||
bIsValid = true;
|
||||
|
||||
return *Result;
|
||||
@@ -298,7 +298,7 @@ public:
|
||||
{
|
||||
Reset();
|
||||
|
||||
T* Result = new (&Value) ValueType(IL, Forward<Ts>(Args)...);
|
||||
T* Result = new (&Value) FValueType(IL, Forward<Ts>(Args)...);
|
||||
bIsValid = true;
|
||||
|
||||
return *Result;
|
||||
@@ -335,7 +335,7 @@ public:
|
||||
{
|
||||
bIsValid = false;
|
||||
|
||||
reinterpret_cast<T*>(&Value)->~ValueType();
|
||||
reinterpret_cast<T*>(&Value)->~FValueType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,9 +380,9 @@ class TOptional<T, true> final
|
||||
{
|
||||
public:
|
||||
|
||||
using ValueType = TRemoveReference<T>;
|
||||
using FValueType = TRemoveReference<T>;
|
||||
|
||||
static_assert(!CReference<ValueType>);
|
||||
static_assert(!CReference<FValueType>);
|
||||
|
||||
/** Constructs an object that does not contain a reference. */
|
||||
FORCEINLINE constexpr TOptional() : Ptr(nullptr) { }
|
||||
@@ -408,13 +408,13 @@ public:
|
||||
{ }
|
||||
|
||||
/** Converting constructor. */
|
||||
template <typename U> requires (!CConst<ValueType> && CConstructibleFrom<T, U&> && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable<U, T>)
|
||||
template <typename U> requires (!CConst<FValueType> && CConstructibleFrom<T, U&> && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable<U, T>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<U&, T>) TOptional(TOptional<U, false>& InValue)
|
||||
: Ptr(InValue.IsValid() ? AddressOf(static_cast<T>(InValue.GetValue())) : nullptr)
|
||||
{ }
|
||||
|
||||
/** Converting constructor. */
|
||||
template <typename U> requires (CConst<ValueType> && CConstructibleFrom<T, const U&> && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable<U, T>)
|
||||
template <typename U> requires (CConst<FValueType> && CConstructibleFrom<T, const U&> && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable<U, T>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const U&, T>) TOptional(const TOptional<U, false>& InValue)
|
||||
: Ptr(InValue.IsValid() ? AddressOf(static_cast<T>(InValue.GetValue())) : nullptr)
|
||||
{ }
|
||||
@@ -492,7 +492,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
ValueType* Ptr;
|
||||
FValueType* Ptr;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class TPropagateConst final
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = TPointerTraits<T>::ElementType;
|
||||
using FElementType = TPointerTraits<T>::FElementType;
|
||||
|
||||
/** Constructs an TPropagateConst, default-initializing underlying pointer. */
|
||||
FORCEINLINE constexpr TPropagateConst() = default;
|
||||
@@ -105,26 +105,26 @@ public:
|
||||
NODISCARD FORCEINLINE constexpr decltype(auto) operator<=>(U InPtr) const& { return SynthThreeWayCompare(Ptr, InPtr); }
|
||||
|
||||
/** @return The pointer to the object pointed to by the wrapped pointer. */
|
||||
NODISCARD FORCEINLINE constexpr ElementType* Get() { return TPointerTraits<T>::ToAddress(Ptr); }
|
||||
NODISCARD FORCEINLINE constexpr const ElementType* Get() const { return TPointerTraits<T>::ToAddress(Ptr); }
|
||||
NODISCARD FORCEINLINE constexpr FElementType* Get() { return TPointerTraits<T>::ToAddress(Ptr); }
|
||||
NODISCARD FORCEINLINE constexpr const FElementType* Get() const { return TPointerTraits<T>::ToAddress(Ptr); }
|
||||
|
||||
/** @return true if *this owns an object, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsValid() const { return Get() != nullptr; }
|
||||
NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Get() != nullptr; }
|
||||
|
||||
/** @return The a reference or pointer to the object owned by *this, i.e. Get(). */
|
||||
NODISCARD FORCEINLINE constexpr ElementType& operator*() { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return *Get(); }
|
||||
NODISCARD FORCEINLINE constexpr const ElementType& operator*() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return *Get(); }
|
||||
NODISCARD FORCEINLINE constexpr ElementType* operator->() { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get(); }
|
||||
NODISCARD FORCEINLINE constexpr const ElementType* operator->() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get(); }
|
||||
NODISCARD FORCEINLINE constexpr FElementType& operator*() { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return *Get(); }
|
||||
NODISCARD FORCEINLINE constexpr const FElementType& operator*() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return *Get(); }
|
||||
NODISCARD FORCEINLINE constexpr FElementType* operator->() { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get(); }
|
||||
NODISCARD FORCEINLINE constexpr const FElementType* operator->() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get(); }
|
||||
|
||||
/** @return The element at index, i.e. Get()[Index]. */
|
||||
NODISCARD FORCEINLINE constexpr T& operator[](size_t Index) { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get()[Index]; }
|
||||
NODISCARD FORCEINLINE constexpr const T& operator[](size_t Index) const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get()[Index]; }
|
||||
|
||||
/** @return The pointer to the object pointed to by the wrapped pointer-like object. */
|
||||
NODISCARD FORCEINLINE constexpr operator ElementType*() requires (CConvertibleTo<T, ElementType*>) { return Ptr; }
|
||||
NODISCARD FORCEINLINE constexpr operator const ElementType*() const requires (CConvertibleTo<T, ElementType*>) { return Ptr; }
|
||||
NODISCARD FORCEINLINE constexpr operator FElementType*() requires (CConvertibleTo<T, FElementType*>) { return Ptr; }
|
||||
NODISCARD FORCEINLINE constexpr operator const FElementType*() const requires (CConvertibleTo<T, FElementType*>) { return Ptr; }
|
||||
|
||||
/** @return The reference to the pointer-like object stored. */
|
||||
NODISCARD FORCEINLINE constexpr T& GetUnderlying() { return Ptr; }
|
||||
|
||||
@@ -22,7 +22,7 @@ class TReferenceWrapper final
|
||||
{
|
||||
public:
|
||||
|
||||
using Type = ReferencedType;
|
||||
using FType = ReferencedType;
|
||||
|
||||
/** Constructs a new reference wrapper. */
|
||||
template <typename T = ReferencedType&> requires (CConvertibleTo<T&&, ReferencedType&> && !CSameAs<TReferenceWrapper, TRemoveCVRef<T>>)
|
||||
@@ -94,27 +94,15 @@ FORCEINLINE constexpr TReferenceWrapper<T> Ref(TReferenceWrapper<T> InValue)
|
||||
return Ref(InValue.Get());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr TReferenceWrapper<const T> Ref(const T& InValue)
|
||||
{
|
||||
return TReferenceWrapper<const T>(InValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr TReferenceWrapper<const T> Ref(TReferenceWrapper<T> InValue)
|
||||
{
|
||||
return Ref(InValue.Get());
|
||||
}
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename T> struct TIsTReferenceWrapperImpl : FFalse { };
|
||||
template <typename T> struct TIsTReferenceWrapperImpl<TReferenceWrapper<T>> : FTrue { };
|
||||
|
||||
template <typename T> struct TUnwrapReferenceImpl { using Type = T; };
|
||||
template <typename T> struct TUnwrapReferenceImpl<TReferenceWrapper<T>> { using Type = T&; };
|
||||
template <typename T> struct TUnwrapReferenceImpl { using FType = T; };
|
||||
template <typename T> struct TUnwrapReferenceImpl<TReferenceWrapper<T>> { using FType = T&; };
|
||||
|
||||
template <typename T> struct TUnwrapRefDecayImpl { using Type = typename TUnwrapReferenceImpl<TDecay<T>>::Type; };
|
||||
template <typename T> struct TUnwrapRefDecayImpl { using FType = typename TUnwrapReferenceImpl<TDecay<T>>::FType; };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
@@ -122,10 +110,10 @@ template <typename T>
|
||||
concept CTReferenceWrapper = NAMESPACE_PRIVATE::TIsTReferenceWrapperImpl<TRemoveCV<T>>::Value;
|
||||
|
||||
template <typename T>
|
||||
using TUnwrapReference = typename NAMESPACE_PRIVATE::TUnwrapReferenceImpl<T>::Type;
|
||||
using TUnwrapReference = typename NAMESPACE_PRIVATE::TUnwrapReferenceImpl<T>::FType;
|
||||
|
||||
template <typename T>
|
||||
using TUnwrapRefDecay = typename NAMESPACE_PRIVATE::TUnwrapRefDecayImpl<T>::Type;
|
||||
using TUnwrapRefDecay = typename NAMESPACE_PRIVATE::TUnwrapRefDecayImpl<T>::FType;
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Templates/Noncopyable.h"
|
||||
#include "Templates/Invoke.h"
|
||||
#include "Templates/ReferenceWrapper.h"
|
||||
|
||||
@@ -49,7 +49,7 @@ struct TTupleElementImpl;
|
||||
template <size_t I, typename... Ts>
|
||||
struct TTupleElementImpl<I, TTuple<Ts...>>
|
||||
{
|
||||
using Type = Meta::TType<I, TTypeSequence<Ts...>>;
|
||||
using FType = Meta::TType<I, TTypeSequence<Ts...>>;
|
||||
};
|
||||
|
||||
template <bool bTrue, typename... Ts>
|
||||
@@ -77,12 +77,12 @@ struct TTupleBasicElement
|
||||
{
|
||||
private:
|
||||
|
||||
using ValueType = T;
|
||||
ValueType Value;
|
||||
using FValueType = T;
|
||||
FValueType Value;
|
||||
|
||||
public:
|
||||
|
||||
template <typename Type>
|
||||
template <typename Type> requires (CConstructibleFrom<T, Type&&>)
|
||||
FORCEINLINE constexpr TTupleBasicElement(Type&& Arg)
|
||||
: Value(Forward<Type>(Arg))
|
||||
{ }
|
||||
@@ -110,10 +110,10 @@ public:
|
||||
template <typename T> \
|
||||
struct TTupleBasicElement<T, Index> \
|
||||
{ \
|
||||
using Name##Type = T; \
|
||||
Name##Type Name; \
|
||||
using F##Name##Type = T; \
|
||||
F##Name##Type Name; \
|
||||
\
|
||||
template <typename Type> \
|
||||
template <typename Type> requires (CConstructibleFrom<T, F##Name##Type&&>) \
|
||||
FORCEINLINE constexpr TTupleBasicElement(Type&& Arg) \
|
||||
: Name(Forward<Type>(Arg)) \
|
||||
{ } \
|
||||
@@ -278,28 +278,37 @@ struct TTTupleSynthThreeWayComparable<TTypeSequence<>, TTypeSequence<>> : FTrue
|
||||
template <typename TSequence, typename USequence>
|
||||
concept CTTupleSynthThreeWayComparable = TTTupleSynthThreeWayComparable<TSequence, USequence>::Value;
|
||||
|
||||
template <typename Ret, typename Indices>
|
||||
struct TTupleVisitElementByIndex;
|
||||
|
||||
template <typename Ret, size_t I, size_t... Indices>
|
||||
struct TTupleVisitElementByIndex<Ret, TIndexSequence<I, Indices...>>
|
||||
template <typename Ret, typename F, typename TTupleType>
|
||||
struct TTupleVisitElementByIndex
|
||||
{
|
||||
template <typename F, typename TTupleType>
|
||||
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, TTupleType&& Arg, size_t Index)
|
||||
template <size_t Index>
|
||||
struct TInvokeElement
|
||||
{
|
||||
if (Index == I) return InvokeResult<Ret>(Forward<F>(Func), Forward<TTupleType>(Arg).template GetValue<I>());
|
||||
return TTupleVisitElementByIndex<Ret, TIndexSequence<Indices...>>::Do(Forward<F>(Func), Forward<TTupleType>(Arg), Index);
|
||||
FORCEINLINE static constexpr Ret Do(F&& Func, TTupleType&& Arg)
|
||||
{
|
||||
return InvokeResult<Ret>(Forward<F>(Func), Forward<TTupleType>(Arg).template GetValue<Index>());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Ret>
|
||||
struct TTupleVisitElementByIndex<Ret, TIndexSequence<>>
|
||||
template <typename>
|
||||
struct TInvokeTuple;
|
||||
|
||||
template <size_t... Indices>
|
||||
struct TInvokeTuple<TIndexSequence<Indices...>>
|
||||
{
|
||||
template <typename F, typename TTupleType>
|
||||
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, TTupleType&& Arg, size_t)
|
||||
FORCEINLINE static constexpr Ret Do(F&& Func, TTupleType&& Arg, size_t Index)
|
||||
{
|
||||
checkf(false, "Read access violation. Please check Index.");
|
||||
return InvokeResult<Ret>(Forward<F>(Func), Forward<TTupleType>(Arg).template GetValue<0>());
|
||||
using FInvokeImplType = Ret(*)(F&&, TTupleType&&);
|
||||
|
||||
constexpr FInvokeImplType InvokeImpl[] = { TInvokeElement<Indices>::Do... };
|
||||
|
||||
return InvokeImpl[Index](Forward<F>(Func), Forward<TTupleType>(Arg));
|
||||
}
|
||||
};
|
||||
|
||||
FORCEINLINE static constexpr Ret Do(F&& Func, TTupleType&& Arg, size_t Index)
|
||||
{
|
||||
return TInvokeTuple<TMakeIndexSequence<Arg.Num()>>::Do(Forward<F>(Func), Forward<TTupleType>(Arg), Index);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -315,15 +324,15 @@ template <typename T, CTTuple U>
|
||||
inline constexpr size_t TTupleIndex = NAMESPACE_PRIVATE::TTupleIndexImpl<T, TRemoveCV<U>>::Value;
|
||||
|
||||
template <size_t I, CTTuple U>
|
||||
using TTupleElement = TCopyCV<U, typename NAMESPACE_PRIVATE::TTupleElementImpl<I, TRemoveCV<U>>::Type>;
|
||||
using TTupleElement = TCopyCV<U, typename NAMESPACE_PRIVATE::TTupleElementImpl<I, TRemoveCV<U>>::FType>;
|
||||
|
||||
template <typename... Ts>
|
||||
class TTuple final : public NAMESPACE_PRIVATE::TTupleImpl<TIndexSequenceFor<Ts...>, Ts...>
|
||||
{
|
||||
private:
|
||||
|
||||
using Super = NAMESPACE_PRIVATE::TTupleImpl<TIndexSequenceFor<Ts...>, Ts...>;
|
||||
using Helper = NAMESPACE_PRIVATE::TTupleHelper<TIndexSequenceFor<Ts...>>;
|
||||
using FSuper = NAMESPACE_PRIVATE::TTupleImpl<TIndexSequenceFor<Ts...>, Ts...>;
|
||||
using FHelper = NAMESPACE_PRIVATE::TTupleHelper<TIndexSequenceFor<Ts...>>;
|
||||
|
||||
public:
|
||||
|
||||
@@ -334,7 +343,7 @@ public:
|
||||
template <typename... Us> requires (sizeof...(Ts) >= 1 && sizeof...(Us) == sizeof...(Ts))
|
||||
&& (true && ... && CConstructibleFrom<Ts, Us&&>)
|
||||
FORCEINLINE constexpr explicit (!(true && ... && CConvertibleTo<Us&&, Ts>)) TTuple(Us&&... Args)
|
||||
: Super(NAMESPACE_PRIVATE::ForwardingConstructor, Forward<Us>(Args)...)
|
||||
: FSuper(NAMESPACE_PRIVATE::ForwardingConstructor, Forward<Us>(Args)...)
|
||||
{ }
|
||||
|
||||
/** Converting copy constructor. Initializes each element of the tuple with the corresponding element of other. */
|
||||
@@ -342,7 +351,7 @@ public:
|
||||
&& (true && ... && CConstructibleFrom<Ts, const Us&>)
|
||||
&& NAMESPACE_PRIVATE::TTupleConvertCopy<sizeof...(Ts) != 1, Ts..., Us...>::Value)
|
||||
FORCEINLINE constexpr explicit (!(true && ... && CConvertibleTo<Us&&, Ts>)) TTuple(const TTuple<Us...>& InValue)
|
||||
: Super(NAMESPACE_PRIVATE::OtherTupleConstructor, InValue)
|
||||
: FSuper(NAMESPACE_PRIVATE::OtherTupleConstructor, InValue)
|
||||
{ }
|
||||
|
||||
/** Converting move constructor. Initializes each element of the tuple with the corresponding element of other. */
|
||||
@@ -350,7 +359,7 @@ public:
|
||||
&& (true && ... && CConstructibleFrom<Ts, Us&&>)
|
||||
&& NAMESPACE_PRIVATE::TTupleConvertMove<sizeof...(Ts) != 1, Ts..., Us...>::Value)
|
||||
FORCEINLINE constexpr explicit (!(true && ... && CConvertibleTo<Us&&, Ts>)) TTuple(TTuple<Us...>&& InValue)
|
||||
: Super(NAMESPACE_PRIVATE::OtherTupleConstructor, MoveTemp(InValue))
|
||||
: FSuper(NAMESPACE_PRIVATE::OtherTupleConstructor, MoveTemp(InValue))
|
||||
{ }
|
||||
|
||||
/** Copies/moves content of other into a new instance. */
|
||||
@@ -362,7 +371,7 @@ public:
|
||||
&& (true && ... && CAssignableFrom<Ts&, const Us&>))
|
||||
FORCEINLINE constexpr TTuple& operator=(const TTuple<Us...>& InValue)
|
||||
{
|
||||
Helper::Assign(*this, InValue);
|
||||
FHelper::Assign(*this, InValue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -371,7 +380,7 @@ public:
|
||||
&& (true && ... && CAssignableFrom<Ts&, Us&&>))
|
||||
FORCEINLINE constexpr TTuple& operator=(TTuple<Us...>&& InValue)
|
||||
{
|
||||
Helper::Assign(*this, MoveTemp(InValue));
|
||||
FHelper::Assign(*this, MoveTemp(InValue));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -396,8 +405,8 @@ public:
|
||||
template <typename... Us> requires (sizeof...(Ts) == sizeof...(Us) && NAMESPACE_PRIVATE::CTTupleSynthThreeWayComparable<TTypeSequence<Ts...>, TTypeSequence<Us...>>)
|
||||
NODISCARD friend FORCEINLINE constexpr TCommonComparisonCategory<TSynthThreeWayResult<Ts, Us>...> operator<=>(const TTuple& LHS, const TTuple<Us...>& RHS)
|
||||
{
|
||||
using R = TCommonComparisonCategory<TSynthThreeWayResult<Ts, Us>...>;
|
||||
return NAMESPACE_PRIVATE::TTupleThreeWay<R, TMakeIndexSequence<sizeof...(Ts)>>::Do(LHS, RHS);
|
||||
using FResult = TCommonComparisonCategory<TSynthThreeWayResult<Ts, Us>...>;
|
||||
return NAMESPACE_PRIVATE::TTupleThreeWay<FResult, TMakeIndexSequence<sizeof...(Ts)>>::Do(LHS, RHS);
|
||||
}
|
||||
|
||||
/** Extracts the Ith element from the tuple. I must be an integer value in [0, sizeof...(Ts)). */
|
||||
@@ -421,64 +430,64 @@ public:
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast<const volatile TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple>>(); }
|
||||
|
||||
/** Invoke the callable object 'Func' with a tuple of arguments. */
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) & { return Helper::Apply(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const & { return Helper::Apply(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile& { return Helper::Apply(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const volatile& { return Helper::Apply(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) && { return Helper::Apply(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const && { return Helper::Apply(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile&& { return Helper::Apply(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const volatile&& { return Helper::Apply(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (CInvocable<F&&, Ts& ...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) & { return FHelper::Apply(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F&&, const Ts& ...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const & { return FHelper::Apply(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F&&, volatile Ts& ...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile& { return FHelper::Apply(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F&&, const volatile Ts& ...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const volatile& { return FHelper::Apply(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F&&, Ts&&...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) && { return FHelper::Apply(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
|
||||
template <typename F> requires (CInvocable<F&&, const Ts&&...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const && { return FHelper::Apply(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
|
||||
template <typename F> requires (CInvocable<F&&, volatile Ts&&...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile&& { return FHelper::Apply(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (CInvocable<F&&, const volatile Ts&&...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const volatile&& { return FHelper::Apply(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
|
||||
/** Visits each element in a tuple in parallel and applies it as arguments to the function. */
|
||||
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) & { VisitTuple(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) const & { VisitTuple(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) volatile& { VisitTuple(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) const volatile& { VisitTuple(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) && { VisitTuple(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) const && { VisitTuple(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) volatile&& { VisitTuple(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) const volatile&& { VisitTuple(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F&&, Ts& >) FORCEINLINE constexpr void Visit(F&& Func) & { VisitTuple(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F&&, const Ts& >) FORCEINLINE constexpr void Visit(F&& Func) const & { VisitTuple(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F&&, volatile Ts& >) FORCEINLINE constexpr void Visit(F&& Func) volatile& { VisitTuple(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F&&, const volatile Ts& >) FORCEINLINE constexpr void Visit(F&& Func) const volatile& { VisitTuple(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F&&, Ts&&>) FORCEINLINE constexpr void Visit(F&& Func) && { VisitTuple(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F&&, const Ts&&>) FORCEINLINE constexpr void Visit(F&& Func) const && { VisitTuple(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F&&, volatile Ts&&>) FORCEINLINE constexpr void Visit(F&& Func) volatile&& { VisitTuple(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && CInvocable<F&&, const volatile Ts&&>) FORCEINLINE constexpr void Visit(F&& Func) const volatile&& { VisitTuple(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
|
||||
/** Visits specified element in a tuple and applies it as arguments to the function. */
|
||||
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) & { return static_cast< TTuple& >(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const & { return static_cast<const TTuple& >(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) volatile& { return static_cast< volatile TTuple& >(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const volatile& { return static_cast<const volatile TTuple& >(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) && { return static_cast< TTuple&&>(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const && { return static_cast<const TTuple&&>(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) volatile&& { return static_cast< volatile TTuple&&>(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const volatile&& { return static_cast<const volatile TTuple&&>(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, Ts& >) && CCommonReference<TInvokeResult<F&&, Ts& >...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) & { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast< TTuple& >(*this).Visit<TCommonReference<TInvokeResult<F&&, Ts& >...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, const Ts& >) && CCommonReference<TInvokeResult<F&&, const Ts& >...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const & { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast<const TTuple& >(*this).Visit<TCommonReference<TInvokeResult<F&&, const Ts& >...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, volatile Ts& >) && CCommonReference<TInvokeResult<F&&, volatile Ts& >...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) volatile& { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast< volatile TTuple& >(*this).Visit<TCommonReference<TInvokeResult<F&&, volatile Ts& >...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, const volatile Ts& >) && CCommonReference<TInvokeResult<F&&, const volatile Ts& >...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const volatile& { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast<const volatile TTuple& >(*this).Visit<TCommonReference<TInvokeResult<F&&, const volatile Ts& >...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, Ts&&>) && CCommonReference<TInvokeResult<F&&, Ts&&>...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) && { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast< TTuple&&>(*this).Visit<TCommonReference<TInvokeResult<F&&, Ts&&>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, const Ts&&>) && CCommonReference<TInvokeResult<F&&, const Ts&&>...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const && { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast<const TTuple&&>(*this).Visit<TCommonReference<TInvokeResult<F&&, const Ts&&>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, volatile Ts&&>) && CCommonReference<TInvokeResult<F&&, volatile Ts&&>...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) volatile&& { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast< volatile TTuple&&>(*this).Visit<TCommonReference<TInvokeResult<F&&, volatile Ts&&>...>>(Forward<F>(Func), Index); }
|
||||
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, const volatile Ts&&>) && CCommonReference<TInvokeResult<F&&, const volatile Ts&&>...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const volatile&& { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast<const volatile TTuple&&>(*this).Visit<TCommonReference<TInvokeResult<F&&, const volatile Ts&&>...>>(Forward<F>(Func), Index); }
|
||||
|
||||
/** Visits specified element in a tuple and applies it as arguments to the function. */
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) & { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast< TTuple& >(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const & { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast<const TTuple& >(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) volatile& { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast< volatile TTuple& >(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const volatile& { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast<const volatile TTuple& >(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) && { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast< TTuple&&>(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const && { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast<const TTuple&&>(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) volatile&& { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast< volatile TTuple&&>(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const volatile&& { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, Ts& >) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) & { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, TTuple& >::Do(Forward<F>(Func), static_cast< TTuple& >(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const Ts& >) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const & { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, const TTuple& >::Do(Forward<F>(Func), static_cast<const TTuple& >(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, volatile Ts& >) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) volatile& { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, volatile TTuple& >::Do(Forward<F>(Func), static_cast< volatile TTuple& >(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const volatile Ts& >) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const volatile& { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, const volatile TTuple& >::Do(Forward<F>(Func), static_cast<const volatile TTuple& >(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, Ts&&>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) && { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, TTuple&&>::Do(Forward<F>(Func), static_cast< TTuple&&>(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const Ts&&>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const && { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, const TTuple&&>::Do(Forward<F>(Func), static_cast<const TTuple&&>(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, volatile Ts&&>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) volatile&& { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, volatile TTuple&&>::Do(Forward<F>(Func), static_cast< volatile TTuple&&>(*this), Index); }
|
||||
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const volatile Ts&&>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const volatile&& { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, const volatile TTuple&&>::Do(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this), Index); }
|
||||
|
||||
/** Transform a tuple into another tuple using the given function. */
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) & { return Helper::Transform(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const & { return Helper::Transform(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile& { return Helper::Transform(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile& { return Helper::Transform(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) && { return Helper::Transform(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const && { return Helper::Transform(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile&& { return Helper::Transform(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile&& { return Helper::Transform(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F&&, Ts& > && !CSameAs<void, TInvokeResult<F&&, Ts& >>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) & { return FHelper::Transform(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F&&, const Ts& > && !CSameAs<void, TInvokeResult<F&&, const Ts& >>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const & { return FHelper::Transform(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F&&, volatile Ts& > && !CSameAs<void, TInvokeResult<F&&, volatile Ts& >>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile& { return FHelper::Transform(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F&&, const volatile Ts& > && !CSameAs<void, TInvokeResult<F&&, const volatile Ts& >>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile& { return FHelper::Transform(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F&&, Ts&&> && !CSameAs<void, TInvokeResult<F&&, Ts&&>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) && { return FHelper::Transform(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F&&, const Ts&&> && !CSameAs<void, TInvokeResult<F&&, const Ts&&>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const && { return FHelper::Transform(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F&&, volatile Ts&&> && !CSameAs<void, TInvokeResult<F&&, volatile Ts&&>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile&& { return FHelper::Transform(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F&&, const volatile Ts&&> && !CSameAs<void, TInvokeResult<F&&, const volatile Ts&&>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile&& { return FHelper::Transform(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
|
||||
/** Constructs an object of type T with a tuple as an argument. */
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() & { return Helper::template Construct<T>(static_cast< TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const & { return Helper::template Construct<T>(static_cast<const TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() volatile& { return Helper::template Construct<T>(static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile& { return Helper::template Construct<T>(static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() && { return Helper::template Construct<T>(static_cast< TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const && { return Helper::template Construct<T>(static_cast<const TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() volatile&& { return Helper::template Construct<T>(static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile&& { return Helper::template Construct<T>(static_cast<const volatile TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts& ...>) NODISCARD FORCEINLINE constexpr T Construct() & { return FHelper::template Construct<T>(static_cast< TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, const Ts& ...>) NODISCARD FORCEINLINE constexpr T Construct() const & { return FHelper::template Construct<T>(static_cast<const TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, volatile Ts& ...>) NODISCARD FORCEINLINE constexpr T Construct() volatile& { return FHelper::template Construct<T>(static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, const volatile Ts& ...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile& { return FHelper::template Construct<T>(static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts&&...>) NODISCARD FORCEINLINE constexpr T Construct() && { return FHelper::template Construct<T>(static_cast< TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, const Ts&&...>) NODISCARD FORCEINLINE constexpr T Construct() const && { return FHelper::template Construct<T>(static_cast<const TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, volatile Ts&&...>) NODISCARD FORCEINLINE constexpr T Construct() volatile&& { return FHelper::template Construct<T>(static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, const volatile Ts&&...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile&& { return FHelper::template Construct<T>(static_cast<const volatile TTuple&&>(*this)); }
|
||||
|
||||
/** @return The number of elements in the tuple. */
|
||||
NODISCARD static FORCEINLINE constexpr size_t Num() { return sizeof...(Ts); }
|
||||
@@ -555,13 +564,13 @@ struct TTupleCatResultImpl;
|
||||
template <typename... Ts, typename... TTupleTypes>
|
||||
struct TTupleCatResultImpl<TTuple<Ts...>, TTupleTypes...>
|
||||
{
|
||||
using Type = typename TTupleCatResultImpl<TTupleTypes..., Ts...>::Type;
|
||||
using FType = typename TTupleCatResultImpl<TTupleTypes..., Ts...>::FType;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
struct TTupleCatResultImpl<FTupleEndFlag, Ts...>
|
||||
{
|
||||
using Type = TTuple<Ts...>;
|
||||
using FType = TTuple<Ts...>;
|
||||
};
|
||||
|
||||
template <typename R, typename Indices>
|
||||
@@ -571,14 +580,14 @@ template <typename... RTypes, size_t... Indices>
|
||||
struct TTupleCatMake<TTuple<RTypes...>, TIndexSequence<Indices...>>
|
||||
{
|
||||
template <typename T, typename U>
|
||||
struct ForwardType { using Type = TConditional<CRValueReference<T>, TRemoveReference<U>&&, U>; };
|
||||
struct FForwardType { using FType = TConditional<CRValueReference<T>, TRemoveReference<U>&&, U>; };
|
||||
|
||||
template <typename TTupleType>
|
||||
FORCEINLINE static constexpr TTuple<RTypes...> Do(TTupleType&& InValue)
|
||||
{
|
||||
return TTuple<RTypes...>
|
||||
(
|
||||
static_cast<typename ForwardType<RTypes, decltype(Forward<TTupleType>(InValue).template GetValue<Indices>())>::Type>
|
||||
static_cast<typename FForwardType<RTypes, decltype(Forward<TTupleType>(InValue).template GetValue<Indices>())>::FType>
|
||||
(
|
||||
Forward<TTupleType>(InValue).template GetValue<Indices>()
|
||||
)...
|
||||
@@ -642,15 +651,15 @@ struct TTupleVisitImpl<TIndexSequence<>>
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <typename... TTupleTypes> requires (true && ... && CTTuple<TRemoveCVRef<TTupleTypes>>)
|
||||
using TTupleCatResult = typename NAMESPACE_PRIVATE::TTupleCatResultImpl<TRemoveReference<TTupleTypes>..., NAMESPACE_PRIVATE::FTupleEndFlag>::Type;;
|
||||
using TTupleCatResult = typename NAMESPACE_PRIVATE::TTupleCatResultImpl<TRemoveReference<TTupleTypes>..., NAMESPACE_PRIVATE::FTupleEndFlag>::FType;
|
||||
|
||||
/** Creates a tuple by concatenating any number of tuples. */
|
||||
template <typename... TTupleTypes> requires (true && ... && CTTuple<TRemoveCVRef<TTupleTypes>>)
|
||||
FORCEINLINE constexpr decltype(auto) TupleCat(TTupleTypes&&... Args)
|
||||
{
|
||||
using R = TTupleCatResult<TTupleTypes...>;
|
||||
if constexpr (sizeof...(Args) == 0) return R();
|
||||
else return NAMESPACE_PRIVATE::TTupleCatImpl<R>::Do(Forward<TTupleTypes>(Args)...);
|
||||
using FResult = TTupleCatResult<TTupleTypes...>;
|
||||
if constexpr (sizeof...(Args) == 0) return FResult();
|
||||
else return NAMESPACE_PRIVATE::TTupleCatImpl<FResult>::Do(Forward<TTupleTypes>(Args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,20 +688,22 @@ FORCEINLINE constexpr void VisitTuple(F&& Func, FirstTupleType&& FirstTuple, Tup
|
||||
template <typename... Ts, typename... Us> requires (requires { typename TTuple<TCommonType<Ts, Us>...>; })
|
||||
struct TBasicCommonType<TTuple<Ts...>, TTuple<Us...>>
|
||||
{
|
||||
using Type = TTuple<TCommonType<Ts, Us>...>;
|
||||
using FType = TTuple<TCommonType<Ts, Us>...>;
|
||||
};
|
||||
|
||||
template <typename... Ts, typename... Us, template<typename> typename TQualifiers, template<typename> typename UQualifiers>
|
||||
requires (requires { typename TTuple<TCommonReference<TQualifiers<Ts>, UQualifiers<Us>>...>; })
|
||||
struct TBasicCommonReference<TTuple<Ts...>, TTuple<Us...>, TQualifiers, UQualifiers>
|
||||
{
|
||||
using Type = TTuple<TCommonReference<TQualifiers<Ts>, UQualifiers<Us>>...>;
|
||||
using FType = TTuple<TCommonReference<TQualifiers<Ts>, UQualifiers<Us>>...>;
|
||||
};
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
|
||||
NAMESPACE_STD_BEGIN
|
||||
|
||||
// Support structure binding, should not be directly used.
|
||||
@@ -718,3 +729,5 @@ template <size_t Index, typename ...Ts> FORCEINLINE constexpr decltype(auto) get
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
|
||||
@@ -84,7 +84,7 @@ FORCEINLINE constexpr size_t GetTypeHash(T A)
|
||||
template <typename T> requires (CPointer<T> || CSameAs<T, nullptr_t>)
|
||||
FORCEINLINE constexpr size_t GetTypeHash(T A)
|
||||
{
|
||||
return GetTypeHash(reinterpret_cast<intptr>(A));
|
||||
return GetTypeHash(reinterpret_cast<uintptr>(A));
|
||||
}
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for T::hash_code(). */
|
||||
@@ -94,6 +94,20 @@ FORCEINLINE constexpr size_t GetTypeHash(const T& A)
|
||||
return GetTypeHash(A.hash_code());
|
||||
}
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for arrays. */
|
||||
template <typename T, size_t N> requires (requires(const T& A) { { GetTypeHash(A) } -> CSameAs<size_t>; })
|
||||
FORCEINLINE constexpr size_t GetTypeHash(T(&A)[N])
|
||||
{
|
||||
size_t Result = 3516520171;
|
||||
|
||||
for (size_t Index = 0; Index < N; ++Index)
|
||||
{
|
||||
Result = HashCombine(Result, GetTypeHash(A[Index]));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
concept CHashable = requires(const T& A) { { GetTypeHash(A) } -> CSameAs<size_t>; };
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/CompositeType.h"
|
||||
#include "TypeTraits/Miscellaneous.h"
|
||||
#include "TypeTraits/SupportedOperations.h"
|
||||
|
||||
@@ -24,8 +23,8 @@ void AsConst(const T&& Ref) = delete;
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr TRemoveReference<T>&& MoveTemp(T&& Obj)
|
||||
{
|
||||
using CastType = TRemoveReference<T>;
|
||||
return static_cast<CastType&&>(Obj);
|
||||
using FCastType = TRemoveReference<T>;
|
||||
return static_cast<FCastType&&>(Obj);
|
||||
}
|
||||
|
||||
/** CopyTemp will enforce the creation of an rvalue which can bind to rvalue reference parameters. */
|
||||
@@ -73,6 +72,16 @@ FORCEINLINE constexpr void Swap(T& A, T& B)
|
||||
B = MoveTemp(Temp);
|
||||
}
|
||||
|
||||
/** Overloads the Swap algorithm for arrays. */
|
||||
template <typename T, size_t N> requires (requires(T& A, T& B) { Swap(A, B); })
|
||||
FORCEINLINE constexpr void Swap(T(&A)[N], T(&B)[N])
|
||||
{
|
||||
for (size_t Index = 0; Index != N; ++Index)
|
||||
{
|
||||
Swap(A[Index], B[Index]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Replaces the value of 'A' with 'B' and returns the old value of 'A'. */
|
||||
template <typename T, typename U = T> requires (CMoveConstructible<T> && CAssignableFrom<T&, U>)
|
||||
FORCEINLINE constexpr T Exchange(T& A, U&& B)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Ranges/Utility.h"
|
||||
#include "Templates/Meta.h"
|
||||
#include "Templates/Invoke.h"
|
||||
#include "Templates/Utility.h"
|
||||
@@ -40,30 +41,30 @@ struct TVariantAlternativeImpl;
|
||||
template <size_t I, typename... Ts>
|
||||
struct TVariantAlternativeImpl<I, TVariant<Ts...>>
|
||||
{
|
||||
using Type = Meta::TType<I, TTypeSequence<Ts...>>;
|
||||
using FType = Meta::TType<I, TTypeSequence<Ts...>>;
|
||||
};
|
||||
|
||||
template <typename T, typename TSequence>
|
||||
struct TVariantOverloadType
|
||||
{
|
||||
using FrontType = Meta::TFront<TSequence>;
|
||||
using NextSequence = Meta::TPop<TSequence>;
|
||||
using NextUniqueSequence = typename TVariantOverloadType<T, NextSequence>::Type;
|
||||
using FFrontType = Meta::TFront<TSequence>;
|
||||
using FNextSequence = Meta::TPop<TSequence>;
|
||||
using FNextUniqueSequence = typename TVariantOverloadType<T, FNextSequence>::FType;
|
||||
|
||||
// T_i x[] = { Forward<T>(t) };
|
||||
static constexpr bool bConditional = requires { DeclVal<void(FrontType(&&)[1])>()({ DeclVal<T>() }); };
|
||||
static constexpr bool bConditional = requires { DeclVal<void(FFrontType(&&)[1])>()({ DeclVal<T>() }); };
|
||||
|
||||
using Type = TConditional<bConditional, Meta::TPush<FrontType, NextUniqueSequence>, NextUniqueSequence>;
|
||||
using FType = TConditional<bConditional, Meta::TPush<FFrontType, FNextUniqueSequence>, FNextUniqueSequence>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TVariantOverloadType<T, TTypeSequence<>>
|
||||
{
|
||||
using Type = TTypeSequence<>;
|
||||
using FType = TTypeSequence<>;
|
||||
};
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
using TVariantSelectedType = Meta::TOverloadResolution<T, typename NAMESPACE_PRIVATE::TVariantOverloadType<T, TTypeSequence<Ts...>>::Type>;
|
||||
using TVariantSelectedType = Meta::TOverloadResolution<T, typename NAMESPACE_PRIVATE::TVariantOverloadType<T, TTypeSequence<Ts...>>::FType>;
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
@@ -77,7 +78,7 @@ template <typename T, CTVariant U>
|
||||
inline constexpr size_t TVariantIndex = NAMESPACE_PRIVATE::TVariantIndexImpl<T, TRemoveCV<U>>::Value;
|
||||
|
||||
template <size_t I, CTVariant U>
|
||||
using TVariantAlternative = TCopyCV<U, typename NAMESPACE_PRIVATE::TVariantAlternativeImpl<I, TRemoveCV<U>>::Type>;
|
||||
using TVariantAlternative = TCopyCV<U, typename NAMESPACE_PRIVATE::TVariantAlternativeImpl<I, TRemoveCV<U>>::FType>;
|
||||
|
||||
/**
|
||||
* The class template TVariant represents a type-safe union. An instance of TVariant
|
||||
@@ -139,8 +140,8 @@ public:
|
||||
FORCEINLINE constexpr explicit TVariant(TInPlaceIndex<I>, Us&&... Args)
|
||||
: TypeIndex(I)
|
||||
{
|
||||
using SelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
new (&Value) SelectedType(Forward<Us>(Args)...);
|
||||
using FSelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
new (&Value) FSelectedType(Forward<Us>(Args)...);
|
||||
}
|
||||
|
||||
/** Constructs a variant with the specified alternative T and initializes the contained value with the arguments IL, Forward<Us>(Args).... */
|
||||
@@ -155,8 +156,8 @@ public:
|
||||
FORCEINLINE constexpr explicit TVariant(TInPlaceIndex<I>, initializer_list<T> IL, Us&&... Args)
|
||||
: TypeIndex(I)
|
||||
{
|
||||
using SelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
new (&Value) SelectedType(IL, Forward<Us>(Args)...);
|
||||
using FSelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
new (&Value) FSelectedType(IL, Forward<Us>(Args)...);
|
||||
}
|
||||
|
||||
/** Destroys the contained object, if any, as if by a call to Reset(). */
|
||||
@@ -224,14 +225,14 @@ public:
|
||||
template <typename T> requires (requires { typename NAMESPACE_PRIVATE::TVariantSelectedType<T, Ts...>; })
|
||||
FORCEINLINE constexpr TVariant& operator=(T&& InValue)
|
||||
{
|
||||
using SelectedType = NAMESPACE_PRIVATE::TVariantSelectedType<T, Ts...>;
|
||||
using FSelectedType = NAMESPACE_PRIVATE::TVariantSelectedType<T, Ts...>;
|
||||
|
||||
if (GetIndex() == TVariantIndex<SelectedType, TVariant<Ts...>>) GetValue<SelectedType>() = Forward<T>(InValue);
|
||||
if (GetIndex() == TVariantIndex<FSelectedType, TVariant<Ts...>>) GetValue<FSelectedType>() = Forward<T>(InValue);
|
||||
else
|
||||
{
|
||||
Reset();
|
||||
new (&Value) SelectedType(Forward<T>(InValue));
|
||||
TypeIndex = TVariantIndex<SelectedType, TVariant<Ts...>>;
|
||||
new (&Value) FSelectedType(Forward<T>(InValue));
|
||||
TypeIndex = TVariantIndex<FSelectedType, TVariant<Ts...>>;
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -299,8 +300,8 @@ public:
|
||||
{
|
||||
Reset();
|
||||
|
||||
using SelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
SelectedType* Result = new (&Value) SelectedType(Forward<Us>(Args)...);
|
||||
using FSelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
FSelectedType* Result = new (&Value) FSelectedType(Forward<Us>(Args)...);
|
||||
TypeIndex = I;
|
||||
|
||||
return *Result;
|
||||
@@ -327,8 +328,8 @@ public:
|
||||
{
|
||||
Reset();
|
||||
|
||||
using SelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
SelectedType* Result = new (&Value) SelectedType(IL, Forward<Us>(Args)...);
|
||||
using FSelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
FSelectedType* Result = new (&Value) FSelectedType(IL, Forward<Us>(Args)...);
|
||||
TypeIndex = I;
|
||||
|
||||
return *Result;
|
||||
@@ -432,11 +433,11 @@ private:
|
||||
using FMoveAssignImpl = void(*)(void*, void*);
|
||||
using FDestroyImpl = void(*)(void* );
|
||||
|
||||
static constexpr FCopyConstructImpl CopyConstructImpl[] = { [](void* A, const void* B) { if constexpr (requires(Ts* A, const Ts* B) { Memory::CopyConstruct (A, B); }) Memory::CopyConstruct (reinterpret_cast<Ts*>(A), reinterpret_cast<const Ts*>(B)); else checkf(false, TEXT("The type '%s' is not copy constructible."), typeid(Ts).name()); }... };
|
||||
static constexpr FMoveConstructImpl MoveConstructImpl[] = { [](void* A, void* B) { if constexpr (requires(Ts* A, Ts* B) { Memory::MoveConstruct (A, B); }) Memory::MoveConstruct (reinterpret_cast<Ts*>(A), reinterpret_cast< Ts*>(B)); else checkf(false, TEXT("The type '%s' is not move constructible."), typeid(Ts).name()); }... };
|
||||
static constexpr FCopyAssignImpl CopyAssignImpl[] = { [](void* A, const void* B) { if constexpr (requires(Ts* A, const Ts* B) { Memory::CopyAssign (A, B); }) Memory::CopyAssign (reinterpret_cast<Ts*>(A), reinterpret_cast<const Ts*>(B)); else checkf(false, TEXT("The type '%s' is not copy assignable."), typeid(Ts).name()); }... };
|
||||
static constexpr FMoveAssignImpl MoveAssignImpl[] = { [](void* A, void* B) { if constexpr (requires(Ts* A, Ts* B) { Memory::MoveAssign (A, B); }) Memory::MoveAssign (reinterpret_cast<Ts*>(A), reinterpret_cast< Ts*>(B)); else checkf(false, TEXT("The type '%s' is not move assignable."), typeid(Ts).name()); }... };
|
||||
static constexpr FDestroyImpl DestroyImpl[] = { [](void* A ) { if constexpr (requires(Ts* A ) { Memory::Destruct (A ); }) Memory::Destruct (reinterpret_cast<Ts*>(A) ); else checkf(false, TEXT("The type '%s' is not destructible."), typeid(Ts).name()); }... };
|
||||
static constexpr FCopyConstructImpl CopyConstructImpl[] = { [](void* A, const void* B) { if constexpr (requires(Ts* A, const Ts* B) { Memory::CopyConstruct (A, B); }) Memory::CopyConstruct (static_cast<Ts*>(A), static_cast<const Ts*>(B)); else checkf(false, TEXT("The type '%s' is not copy constructible."), typeid(Ts).name()); }... };
|
||||
static constexpr FMoveConstructImpl MoveConstructImpl[] = { [](void* A, void* B) { if constexpr (requires(Ts* A, Ts* B) { Memory::MoveConstruct (A, B); }) Memory::MoveConstruct (static_cast<Ts*>(A), static_cast< Ts*>(B)); else checkf(false, TEXT("The type '%s' is not move constructible."), typeid(Ts).name()); }... };
|
||||
static constexpr FCopyAssignImpl CopyAssignImpl[] = { [](void* A, const void* B) { if constexpr (requires(Ts* A, const Ts* B) { Memory::CopyAssign (A, B); }) Memory::CopyAssign (static_cast<Ts*>(A), static_cast<const Ts*>(B)); else checkf(false, TEXT("The type '%s' is not copy assignable."), typeid(Ts).name()); }... };
|
||||
static constexpr FMoveAssignImpl MoveAssignImpl[] = { [](void* A, void* B) { if constexpr (requires(Ts* A, Ts* B) { Memory::MoveAssign (A, B); }) Memory::MoveAssign (static_cast<Ts*>(A), static_cast< Ts*>(B)); else checkf(false, TEXT("The type '%s' is not move assignable."), typeid(Ts).name()); }... };
|
||||
static constexpr FDestroyImpl DestroyImpl[] = { [](void* A ) { if constexpr (requires(Ts* A ) { Memory::Destruct (A ); }) Memory::Destruct (static_cast<Ts*>(A) ); else checkf(false, TEXT("The type '%s' is not destructible."), typeid(Ts).name()); }... };
|
||||
|
||||
TAlignedUnion<1, Ts...> Value;
|
||||
uint8 TypeIndex;
|
||||
@@ -448,7 +449,7 @@ NAMESPACE_PRIVATE_BEGIN
|
||||
template <typename F, typename... VariantTypes>
|
||||
struct TVariantVisitImpl
|
||||
{
|
||||
struct GetTotalNum
|
||||
struct FGetTotalNum
|
||||
{
|
||||
FORCEINLINE static constexpr size_t Do()
|
||||
{
|
||||
@@ -464,10 +465,10 @@ struct TVariantVisitImpl
|
||||
}
|
||||
|
||||
return Result;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct EncodeIndices
|
||||
struct FEncodeIndices
|
||||
{
|
||||
FORCEINLINE static constexpr size_t Do(initializer_list<size_t> Indices)
|
||||
{
|
||||
@@ -478,14 +479,14 @@ struct TVariantVisitImpl
|
||||
for (size_t Index = 0; Index < sizeof...(VariantTypes); ++Index)
|
||||
{
|
||||
Result *= VariantNums[Index];
|
||||
Result += GetData(Indices)[Index];
|
||||
Result += Ranges::Begin(Indices)[Index];
|
||||
}
|
||||
|
||||
return Result;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct DecodeExtent
|
||||
struct FDecodeExtent
|
||||
{
|
||||
FORCEINLINE static constexpr size_t Do(size_t EncodedIndex, size_t Extent)
|
||||
{
|
||||
@@ -497,76 +498,76 @@ struct TVariantVisitImpl
|
||||
}
|
||||
|
||||
return EncodedIndex % VariantNums[Extent];
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t EncodedIndex, typename>
|
||||
struct InvokeEncoded;
|
||||
struct TInvokeEncoded;
|
||||
|
||||
template <size_t EncodedIndex, size_t... ExtentIndices>
|
||||
struct InvokeEncoded<EncodedIndex, TIndexSequence<ExtentIndices...>>
|
||||
struct TInvokeEncoded<EncodedIndex, TIndexSequence<ExtentIndices...>>
|
||||
{
|
||||
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
|
||||
{
|
||||
return Invoke(Forward<F>(Func), Forward<VariantTypes>(Variants).template GetValue<DecodeExtent::Do(EncodedIndex, ExtentIndices)>()...);
|
||||
return Invoke(Forward<F>(Func), Forward<VariantTypes>(Variants).template GetValue<FDecodeExtent::Do(EncodedIndex, ExtentIndices)>()...);
|
||||
}
|
||||
|
||||
template <typename Ret>
|
||||
struct Result
|
||||
struct TResult
|
||||
{
|
||||
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
|
||||
{
|
||||
return InvokeResult<Ret>(Forward<F>(Func), Forward<VariantTypes>(Variants).template GetValue<DecodeExtent::Do(EncodedIndex, ExtentIndices)>()...);
|
||||
return InvokeResult<Ret>(Forward<F>(Func), Forward<VariantTypes>(Variants).template GetValue<FDecodeExtent::Do(EncodedIndex, ExtentIndices)>()...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename>
|
||||
struct InvokeVariant;
|
||||
struct TInvokeVariant;
|
||||
|
||||
template <size_t... EncodedIndices>
|
||||
struct InvokeVariant<TIndexSequence<EncodedIndices...>>
|
||||
struct TInvokeVariant<TIndexSequence<EncodedIndices...>>
|
||||
{
|
||||
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
|
||||
{
|
||||
using ExtentIndices = TIndexSequenceFor<VariantTypes...>;
|
||||
using FExtentIndices = TIndexSequenceFor<VariantTypes...>;
|
||||
|
||||
using ResultType = TCommonType<decltype(InvokeEncoded<EncodedIndices, ExtentIndices>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...))...>;
|
||||
using FResultType = TCommonReference<decltype(TInvokeEncoded<EncodedIndices, FExtentIndices>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...))...>;
|
||||
|
||||
using InvokeImplType = ResultType(*)(F&&, VariantTypes&&...);
|
||||
using FInvokeImplType = FResultType(*)(F&&, VariantTypes&&...);
|
||||
|
||||
constexpr InvokeImplType InvokeImpl[] = { InvokeEncoded<EncodedIndices, ExtentIndices>::template Result<ResultType>::Do... };
|
||||
constexpr FInvokeImplType InvokeImpl[] = { TInvokeEncoded<EncodedIndices, FExtentIndices>::template TResult<FResultType>::Do... };
|
||||
|
||||
return InvokeImpl[EncodeIndices::Do({ Variants.GetIndex()... })](Forward<F>(Func), Forward<VariantTypes>(Variants)...);
|
||||
return InvokeImpl[FEncodeIndices::Do({ Variants.GetIndex()... })](Forward<F>(Func), Forward<VariantTypes>(Variants)...);
|
||||
}
|
||||
|
||||
template <typename Ret>
|
||||
struct Result
|
||||
struct TResult
|
||||
{
|
||||
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
|
||||
{
|
||||
using ExtentIndices = TIndexSequenceFor<VariantTypes...>;
|
||||
using FExtentIndices = TIndexSequenceFor<VariantTypes...>;
|
||||
|
||||
using InvokeImplType = Ret(*)(F&&, VariantTypes&&...);
|
||||
using FInvokeImplType = Ret(*)(F&&, VariantTypes&&...);
|
||||
|
||||
constexpr InvokeImplType InvokeImpl[] = { InvokeEncoded<EncodedIndices, ExtentIndices>::template Result<Ret>::Do... };
|
||||
constexpr FInvokeImplType InvokeImpl[] = { TInvokeEncoded<EncodedIndices, FExtentIndices>::template TResult<Ret>::Do... };
|
||||
|
||||
return InvokeImpl[EncodeIndices::Do({ Variants.GetIndex()... })](Forward<F>(Func), Forward<VariantTypes>(Variants)...);
|
||||
return InvokeImpl[FEncodeIndices::Do({ Variants.GetIndex()... })](Forward<F>(Func), Forward<VariantTypes>(Variants)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
|
||||
{
|
||||
return InvokeVariant<TMakeIndexSequence<GetTotalNum::Do()>>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
|
||||
return TInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
|
||||
}
|
||||
|
||||
template <typename Ret>
|
||||
struct Result
|
||||
struct TResult
|
||||
{
|
||||
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
|
||||
{
|
||||
return InvokeVariant<TMakeIndexSequence<GetTotalNum::Do()>>::template Result<Ret>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
|
||||
return TInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::template TResult<Ret>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -588,7 +589,7 @@ template <typename Ret, typename F, typename FirstVariantType, typename... Varia
|
||||
constexpr Ret Visit(F&& Func, FirstVariantType&& FirstVariant, VariantTypes&&... Variants)
|
||||
{
|
||||
checkf((true && ... && Variants.IsValid()), TEXT("It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."));
|
||||
return NAMESPACE_PRIVATE::TVariantVisitImpl<F, FirstVariantType, VariantTypes...>::template Result<Ret>::Do(Forward<F>(Func), Forward<FirstVariantType>(FirstVariant), Forward<VariantTypes>(Variants)...);
|
||||
return NAMESPACE_PRIVATE::TVariantVisitImpl<F, FirstVariantType, VariantTypes...>::template TResult<Ret>::Do(Forward<F>(Func), Forward<FirstVariantType>(FirstVariant), Forward<VariantTypes>(Variants)...);
|
||||
}
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
REDCRAFTUTILITY_API void TestContainers();
|
||||
REDCRAFTUTILITY_API void TestArray();
|
||||
REDCRAFTUTILITY_API void TestStaticArray();
|
||||
REDCRAFTUTILITY_API void TestArrayView();
|
||||
REDCRAFTUTILITY_API void TestBitset();
|
||||
REDCRAFTUTILITY_API void TestStaticBitset();
|
||||
REDCRAFTUTILITY_API void TestList();
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
REDCRAFTUTILITY_API void TestMemory();
|
||||
REDCRAFTUTILITY_API void TestAddress();
|
||||
REDCRAFTUTILITY_API void TestAlignment();
|
||||
REDCRAFTUTILITY_API void TestMemoryBuffer();
|
||||
REDCRAFTUTILITY_API void TestMemoryMalloc();
|
||||
REDCRAFTUTILITY_API void TestMemoryOperator();
|
||||
REDCRAFTUTILITY_API void TestPointerTraits();
|
||||
REDCRAFTUTILITY_API void TestUniquePointer();
|
||||
REDCRAFTUTILITY_API void TestSharedPointer();
|
||||
REDCRAFTUTILITY_API void TestObserverPointer();
|
||||
REDCRAFTUTILITY_API void TestInOutPointer();
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
REDCRAFTUTILITY_API void TestMiscellaneous();
|
||||
REDCRAFTUTILITY_API void TestAssertionMacros();
|
||||
REDCRAFTUTILITY_API void TestCompare();
|
||||
REDCRAFTUTILITY_API void TestVarArgs();
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
REDCRAFTUTILITY_API void TestString();
|
||||
REDCRAFTUTILITY_API void TestChar();
|
||||
REDCRAFTUTILITY_API void TestStringView();
|
||||
REDCRAFTUTILITY_API void TestTemplateString();
|
||||
REDCRAFTUTILITY_API void TestStringConversion();
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
REDCRAFTUTILITY_API void TestTemplates();
|
||||
REDCRAFTUTILITY_API void TestInvoke();
|
||||
REDCRAFTUTILITY_API void TestReferenceWrapper();
|
||||
REDCRAFTUTILITY_API void TestOptional();
|
||||
REDCRAFTUTILITY_API void TestVariant();
|
||||
REDCRAFTUTILITY_API void TestAny();
|
||||
REDCRAFTUTILITY_API void TestTuple();
|
||||
REDCRAFTUTILITY_API void TestFunction();
|
||||
REDCRAFTUTILITY_API void TestAtomic();
|
||||
REDCRAFTUTILITY_API void TestScopeHelper();
|
||||
REDCRAFTUTILITY_API void TestPropagateConst();
|
||||
REDCRAFTUTILITY_API void TestMiscTemplates();
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
26
Redcraft.Utility/Source/Public/Testing/Testing.h
Normal file
26
Redcraft.Utility/Source/Public/Testing/Testing.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
REDCRAFTUTILITY_API void TestTypeTraits();
|
||||
REDCRAFTUTILITY_API void TestTemplates();
|
||||
REDCRAFTUTILITY_API void TestNumeric();
|
||||
REDCRAFTUTILITY_API void TestIterator();
|
||||
REDCRAFTUTILITY_API void TestRange();
|
||||
REDCRAFTUTILITY_API void TestAlgorithms();
|
||||
REDCRAFTUTILITY_API void TestMemory();
|
||||
REDCRAFTUTILITY_API void TestContainers();
|
||||
REDCRAFTUTILITY_API void TestString();
|
||||
REDCRAFTUTILITY_API void TestMiscellaneous();
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
REDCRAFTUTILITY_API void TestTypeTraits();
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
||||
@@ -20,58 +20,58 @@ template <typename T, typename U, template <typename> typename TQualifiers, temp
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
// Template class declaration for the common Type implementation
|
||||
// Template class declaration for the common type implementation
|
||||
template <typename...>
|
||||
struct TCommonTypeImpl;
|
||||
|
||||
// If sizeof...(Ts) is zero, there is no member Type
|
||||
// If sizeof...(Ts) is zero, there is no member FType
|
||||
template <>
|
||||
struct TCommonTypeImpl<> { };
|
||||
|
||||
// If sizeof...(Ts) is one, the member Type names the same Type as TCommonType<T, T> if it exists; otherwise there is no member Type
|
||||
// If sizeof...(Ts) is one, the member FType names the same type as TCommonType<T, T> if it exists; otherwise there is no member FType
|
||||
template <typename T>
|
||||
struct TCommonTypeImpl<T> : TCommonTypeImpl<T, T> { };
|
||||
|
||||
// If sizeof...(Ts) is two
|
||||
|
||||
// If applying TDecay to at least one of T and U produces a different Type, the member Type names the same Type as TCommonType<TDecay<T>, TDecay<U>>, if it exists; if not, there is no member Type
|
||||
// If applying TDecay to at least one of T and U produces a different type, the member FType names the same type as TCommonType<TDecay<T>, TDecay<U>>, if it exists; if not, there is no member FType
|
||||
template <typename T, typename U> concept CDecayed = CSameAs<T, TDecay<T>> && CSameAs<U, TDecay<U>>;
|
||||
template <typename T, typename U> requires (!CDecayed<T, U>)
|
||||
struct TCommonTypeImpl<T, U> : TCommonTypeImpl<TDecay<T>, TDecay<U>> { };
|
||||
|
||||
// Otherwise, if there is a user specialization for TBasicCommonType<T, U>::Type, that specialization is used
|
||||
template <typename T, typename U> concept CBasicCommonType = requires { typename TBasicCommonType<T, U>::Type; };
|
||||
// Otherwise, if there is a user specialization for TBasicCommonType<T, U>::FType, that specialization is used
|
||||
template <typename T, typename U> concept CBasicCommonType = requires { typename TBasicCommonType<T, U>::FType; };
|
||||
template <typename T, typename U> requires (CDecayed<T, U> && CBasicCommonType<T, U>)
|
||||
struct TCommonTypeImpl<T, U> : TBasicCommonType<T, U>
|
||||
{
|
||||
// If such a specialization has a member named type,
|
||||
// it must be a public and unambiguous member that names a cv-unqualified non-reference type to which both T and U are explicitly convertible
|
||||
// Additionally, TBasicCommonType<T, U>::Type and TBasicCommonType<U, T>::Type must denote the same type
|
||||
static_assert(CSameAs<typename TBasicCommonType<T, U>::Type, TDecay<typename TBasicCommonType<T, U>::Type>>, "The basic common type must be a cv-unqualified non-reference type");
|
||||
static_assert(CConstructibleFrom<typename TBasicCommonType<T, U>::Type, T&&> && CConstructibleFrom<typename TBasicCommonType<T, U>::Type, U&&>, "The basic common type must be a type which both T and U are explicitly convertible");
|
||||
static_assert(CSameAs<typename TBasicCommonType<T, U>::Type, typename TBasicCommonType<U, T>::Type>, "TBasicCommonType<T, U>::Type and TBasicCommonType<U, T>::Type must denote the same type");
|
||||
// Additionally, TBasicCommonType<T, U>::FType and TBasicCommonType<U, T>::FType must denote the same type
|
||||
static_assert(CSameAs<typename TBasicCommonType<T, U>::FType, TDecay<typename TBasicCommonType<T, U>::FType>>, "The basic common type must be a cv-unqualified non-reference type");
|
||||
static_assert(CConstructibleFrom<typename TBasicCommonType<T, U>::FType, T&&> && CConstructibleFrom<typename TBasicCommonType<T, U>::FType, U&&>, "The basic common type must be a type which both T and U are explicitly convertible");
|
||||
static_assert(CSameAs<typename TBasicCommonType<T, U>::FType, typename TBasicCommonType<U, T>::FType>, "TBasicCommonType<T, U>::FType and TBasicCommonType<U, T>::FType must denote the same type");
|
||||
};
|
||||
|
||||
// Otherwise, if TDecay<decltype(false ? DeclVal<T>() : DeclVal<U>())> is a valid Type, the member Type denotes that Type
|
||||
// Otherwise, if TDecay<decltype(false ? DeclVal<T>() : DeclVal<U>())> is a valid type, the member FType denotes that type
|
||||
template <typename T, typename U> concept CConditionalType = requires { typename TDecay<decltype(false ? DeclVal<T>() : DeclVal<U>())>; };
|
||||
template <typename T, typename U> requires (CDecayed<T, U> && !CBasicCommonType<T, U> && CConditionalType<T, U>)
|
||||
struct TCommonTypeImpl<T, U> { using Type = TDecay<decltype(false ? DeclVal<T>() : DeclVal<U>())>; };
|
||||
struct TCommonTypeImpl<T, U> { using FType = TDecay<decltype(false ? DeclVal<T>() : DeclVal<U>())>; };
|
||||
|
||||
// Otherwise, if TDecay<decltype(false ? DeclVal<CRT>() : DeclVal<CRU>())> is a valid Type,
|
||||
// where CRT and CRU are const TRemoveReference<T>& and const TRemoveReference<U>& respectively, the member Type denotes that Type
|
||||
// Otherwise, if TDecay<decltype(false ? DeclVal<CRT>() : DeclVal<CRU>())> is a valid type,
|
||||
// where CRT and CRU are const TRemoveReference<T>& and const TRemoveReference<U>& respectively, the member FType denotes that type
|
||||
template <typename T, typename U> concept CConditionalCRefType = requires { typename TDecay<decltype(false ? DeclVal<const TRemoveReference<T>&>() : DeclVal<const TRemoveReference<U>&>())>; };
|
||||
template <typename T, typename U> requires (CDecayed<T, U> && !CBasicCommonType<T, U> && !CConditionalType<T, U> && CConditionalCRefType<T, U>)
|
||||
struct TCommonTypeImpl<T, U> { using Type = TDecay<decltype(false ? DeclVal<const TRemoveReference<T>&>() : DeclVal<const TRemoveReference<U>&>())>; };
|
||||
struct TCommonTypeImpl<T, U> { using FType = TDecay<decltype(false ? DeclVal<const TRemoveReference<T>&>() : DeclVal<const TRemoveReference<U>&>())>; };
|
||||
|
||||
// Otherwise, there is no member Type
|
||||
// Otherwise, there is no member FType
|
||||
|
||||
// If sizeof...(Ts) is greater than two
|
||||
|
||||
// If TCommonType<T, U> exists, the member Type denotes TCommonType<TCommonType<T, U>, R...> if such a Type exists
|
||||
template <typename T, typename U, typename W, typename... Ts> requires (requires { typename TCommonTypeImpl<T, U>::Type; })
|
||||
struct TCommonTypeImpl<T, U, W, Ts...> : TCommonTypeImpl<typename TCommonTypeImpl<T, U>::Type, W, Ts...> { };
|
||||
// If TCommonType<T, U> exists, the member FType denotes TCommonType<TCommonType<T, U>, R...> if such a type exists
|
||||
template <typename T, typename U, typename W, typename... Ts> requires (requires { typename TCommonTypeImpl<T, U>::FType; })
|
||||
struct TCommonTypeImpl<T, U, W, Ts...> : TCommonTypeImpl<typename TCommonTypeImpl<T, U>::FType, W, Ts...> { };
|
||||
|
||||
// In all other cases, there is no member Type
|
||||
// In all other cases, there is no member FType
|
||||
template <typename...>
|
||||
struct TCommonTypeImpl { };
|
||||
|
||||
@@ -83,7 +83,7 @@ NAMESPACE_PRIVATE_BEGIN
|
||||
template <typename...>
|
||||
struct TCommonReferenceImpl;
|
||||
|
||||
// Template class declaration for the simple common reference Type implementation
|
||||
// Template class declaration for the simple common reference type implementation
|
||||
template <typename, typename>
|
||||
struct TSimpleCommonReferenceImpl;
|
||||
|
||||
@@ -91,89 +91,89 @@ struct TSimpleCommonReferenceImpl;
|
||||
template <typename>
|
||||
struct TQualifiersImpl;
|
||||
|
||||
// If sizeof...(T) is zero, there is no member Type
|
||||
// If sizeof...(T) is zero, there is no member FType
|
||||
template <>
|
||||
struct TCommonReferenceImpl<> { };
|
||||
|
||||
// If sizeof...(T) is one, the member Type names the same Type as T
|
||||
// If sizeof...(T) is one, the member FType names the same type as T
|
||||
template <typename T>
|
||||
struct TCommonReferenceImpl<T> { using Type = T; };
|
||||
struct TCommonReferenceImpl<T> { using FType = T; };
|
||||
|
||||
// If sizeof...(Ts) is two
|
||||
|
||||
// If T and U are both reference types, and the simple common reference Type S of T and U exists, then the member Type Type names S
|
||||
template <typename T, typename U> concept CSimpleCommonReference = requires { typename TSimpleCommonReferenceImpl<T, U>::Type; };
|
||||
// If T and U are both reference types, and the simple common reference type S of T and U exists, then the member FType names S
|
||||
template <typename T, typename U> concept CSimpleCommonReference = requires { typename TSimpleCommonReferenceImpl<T, U>::FType; };
|
||||
template <typename T, typename U> requires (CSimpleCommonReference<T, U>)
|
||||
struct TCommonReferenceImpl<T, U> : TSimpleCommonReferenceImpl<T, U> { };
|
||||
|
||||
// Otherwise, if TBasicCommonReference<TRemoveCVRef<T>, TRemoveCVRef<U>, TQ, UQ>::Type exists
|
||||
template <typename T, typename U> struct TBasicCommonReferenceImpl : TBasicCommonReference<TRemoveCVRef<T>, TRemoveCVRef<U>, TQualifiersImpl<T>::template Apply, TQualifiersImpl<U>::template Apply> { };
|
||||
template <typename T, typename U> concept CBasicCommonReference = requires { typename TBasicCommonReferenceImpl<T, U>::Type; };
|
||||
// Otherwise, if TBasicCommonReference<TRemoveCVRef<T>, TRemoveCVRef<U>, TQ, UQ>::FType exists
|
||||
template <typename T, typename U> struct TBasicCommonReferenceImpl : TBasicCommonReference<TRemoveCVRef<T>, TRemoveCVRef<U>, TQualifiersImpl<T>::template FApply, TQualifiersImpl<U>::template FApply> { };
|
||||
template <typename T, typename U> concept CBasicCommonReference = requires { typename TBasicCommonReferenceImpl<T, U>::FType; };
|
||||
template <typename T, typename U> requires (!CSimpleCommonReference<T, U> && CBasicCommonReference<T, U>)
|
||||
struct TCommonReferenceImpl<T, U> : TBasicCommonReferenceImpl<T, U>
|
||||
{
|
||||
// If such a specialization has a member named type,
|
||||
// it must be a public and unambiguous member that names a type to which both TQualifiers<T> and UQualifiers<U> are convertible
|
||||
// Additionally, TBasicCommonReference<T, U, TQualifiers, UQualifiers>::Type and TBasicCommonReference<U, T, UQualifiers, TQualifiers>::Type must denote the same type
|
||||
static_assert(CConvertibleTo<T, typename TBasicCommonReferenceImpl<T, U>::Type> && CConvertibleTo<U, typename TBasicCommonReferenceImpl<T, U>::Type>, "The basic common reference must be a type to which both TQualifiers<T> and UQualifiers<U> are convertible");
|
||||
static_assert(CSameAs<typename TBasicCommonReferenceImpl<T, U>::Type, typename TBasicCommonReferenceImpl<U, T>::Type>, "TBasicCommonReference<T, U, TQualifiers, UQualifiers>::Type and TBasicCommonReference<U, T, UQualifiers, TQualifiers>::Type must denote the same type");
|
||||
// Additionally, TBasicCommonReference<T, U, TQualifiers, UQualifiers>::FType and TBasicCommonReference<U, T, UQualifiers, TQualifiers>::FType must denote the same type
|
||||
static_assert(CConvertibleTo<T, typename TBasicCommonReferenceImpl<T, U>::FType> && CConvertibleTo<U, typename TBasicCommonReferenceImpl<T, U>::FType>, "The basic common reference must be a type to which both TQualifiers<T> and UQualifiers<U> are convertible");
|
||||
static_assert(CSameAs<typename TBasicCommonReferenceImpl<T, U>::FType, typename TBasicCommonReferenceImpl<U, T>::FType>, "TBasicCommonReference<T, U, TQualifiers, UQualifiers>::FType and TBasicCommonReference<U, T, UQualifiers, TQualifiers>::FType must denote the same type");
|
||||
};
|
||||
|
||||
// Where TQ and UQ is a unary alias template such that TQ<U> is U with the addition of T's cv-ref qualifiers, then the member Type Type names that Type
|
||||
template <typename T> struct TQualifiersImpl { template <typename U> using Apply = TCopyCV<T, U>; };
|
||||
template <typename T> struct TQualifiersImpl<T&> { template <typename U> using Apply = TAddLValueReference<TCopyCV<T, U>>; };
|
||||
template <typename T> struct TQualifiersImpl<T&&> { template <typename U> using Apply = TAddRValueReference<TCopyCV<T, U>>; };
|
||||
// Where TQ and UQ is a unary alias template such that TQ<U> is U with the addition of T's cv-ref qualifiers, then the member FType names that type
|
||||
template <typename T> struct TQualifiersImpl { template <typename U> using FApply = TCopyCV<T, U>; };
|
||||
template <typename T> struct TQualifiersImpl<T&> { template <typename U> using FApply = TAddLValueReference<TCopyCV<T, U>>; };
|
||||
template <typename T> struct TQualifiersImpl<T&&> { template <typename U> using FApply = TAddRValueReference<TCopyCV<T, U>>; };
|
||||
|
||||
// Otherwise, if decltype(false ? Val<T>() : Val<U>()), where val is a function template template <typename T> T Val();, is a valid Type, then the member Type Type names that Type
|
||||
// Otherwise, if decltype(false ? Val<T>() : Val<U>()), where val is a function template <typename T> T Val();, is a valid type, then the member FType names that type
|
||||
template <typename T> T Val();
|
||||
template <typename T, typename U> concept CConditionalValType = requires { typename TVoid<decltype(false ? Val<T>() : Val<U>())>; };
|
||||
template <typename T, typename U> requires (!CSimpleCommonReference<T, U> && !CBasicCommonReference<T, U> && CConditionalValType<T, U>)
|
||||
struct TCommonReferenceImpl<T, U> { using Type = decltype(false ? Val<T>() : Val<U>()); };
|
||||
struct TCommonReferenceImpl<T, U> { using FType = decltype(false ? Val<T>() : Val<U>()); };
|
||||
|
||||
// Otherwise, if TCommonType<T, U> is a valid Type, then the member Type Type names that Type
|
||||
// Otherwise, if TCommonType<T, U> is a valid type, then the member FType names that type
|
||||
template <typename T, typename U> concept CCommonTypeImpl = requires { typename TCommonTypeImpl<T, U>; };
|
||||
template <typename T, typename U> requires (!CSimpleCommonReference<T, U> && !CBasicCommonReference<T, U> && !CConditionalValType<T, U> && CCommonTypeImpl<T, U>)
|
||||
struct TCommonReferenceImpl<T, U> : TCommonTypeImpl<T, U> { };
|
||||
|
||||
// Otherwise, there is no member Type
|
||||
// Otherwise, there is no member FType
|
||||
|
||||
// If sizeof...(Ts) is greater than two
|
||||
|
||||
// If TCommonReference<T, U> exists, the member Type denotes TCommonReference<TCommonReference<T, U>, R...> if such a Type exists
|
||||
template <typename T, typename U, typename W, typename... Ts> requires (requires { typename TCommonReferenceImpl<T, U>::Type; })
|
||||
struct TCommonReferenceImpl<T, U, W, Ts...> : TCommonReferenceImpl<typename TCommonReferenceImpl<T, U>::Type, W, Ts...> { };
|
||||
// If TCommonReference<T, U> exists, the member FType denotes TCommonReference<TCommonReference<T, U>, R...> if such a type exists
|
||||
template <typename T, typename U, typename W, typename... Ts> requires (requires { typename TCommonReferenceImpl<T, U>::FType; })
|
||||
struct TCommonReferenceImpl<T, U, W, Ts...> : TCommonReferenceImpl<typename TCommonReferenceImpl<T, U>::FType, W, Ts...> { };
|
||||
|
||||
// In all other cases, there is no member Type
|
||||
// In all other cases, there is no member FType
|
||||
template <typename...>
|
||||
struct TCommonReferenceImpl { };
|
||||
|
||||
// If T is CV1 X & and U is CV2 Y & (i.e., both are lvalue reference types):
|
||||
// their simple common reference Type is decltype(false ? DeclVal<CV12 X &>() : DeclVal<CV12 Y &>()),
|
||||
// where CV12 is the union of CV1 and CV2, if that Type exists and is a reference Type
|
||||
// their simple common reference type is decltype(false ? DeclVal<CV12 X &>() : DeclVal<CV12 Y &>()),
|
||||
// where CV12 is the union of CV1 and CV2, if that type exists and is a reference type
|
||||
template <typename T, typename U> requires (CLValueReference<decltype(false ? DeclVal<TCopyCV<T, U>&>() : DeclVal<TCopyCV<U, T>&>())>)
|
||||
struct TSimpleCommonReferenceImpl<T&, U&> { using Type = decltype(false ? DeclVal<TCopyCV<T, U>&>() : DeclVal<TCopyCV<U, T>&>()); };
|
||||
struct TSimpleCommonReferenceImpl<T&, U&> { using FType = decltype(false ? DeclVal<TCopyCV<T, U>&>() : DeclVal<TCopyCV<U, T>&>()); };
|
||||
|
||||
// If T and U are both rvalue reference types:
|
||||
// if the simple common reference Type of T & and U & exists, then let C denote that Type's corresponding rvalue reference Type
|
||||
// If CConvertibleTo<T, C> and CConvertibleTo<U, C> are both true, then the simple common reference Type of T and U is C
|
||||
template <typename T, typename U> requires (CConvertibleTo<T&&, TRemoveReference<typename TSimpleCommonReferenceImpl<T&, U&>::Type> &&> && CConvertibleTo<U&&, TRemoveReference<typename TSimpleCommonReferenceImpl<T&, U&>::Type> &&>)
|
||||
struct TSimpleCommonReferenceImpl<T&&, U&&> { using Type = TRemoveReference<typename TSimpleCommonReferenceImpl<T&, U&>::Type> &&; };
|
||||
// if the simple common reference type of T & and U & exists, then let C denote that type's corresponding rvalue reference type
|
||||
// If CConvertibleTo<T, C> and CConvertibleTo<U, C> are both true, then the simple common reference type of T and U is C
|
||||
template <typename T, typename U> requires (CConvertibleTo<T&&, TRemoveReference<typename TSimpleCommonReferenceImpl<T&, U&>::FType> &&> && CConvertibleTo<U&&, TRemoveReference<typename TSimpleCommonReferenceImpl<T&, U&>::FType> &&>)
|
||||
struct TSimpleCommonReferenceImpl<T&&, U&&> { using FType = TRemoveReference<typename TSimpleCommonReferenceImpl<T&, U&>::FType> &&; };
|
||||
|
||||
// Otherwise, one of the two types must be an lvalue reference Type A & and the other must be an rvalue reference Type B &&
|
||||
// Let D denote the simple common reference Type of A & and B const &, if any
|
||||
// If D exists and CConvertibleTo<B&&, D> is true, then the simple common reference Type is D
|
||||
template <typename T, typename U> requires (CConvertibleTo<U&&, typename TSimpleCommonReferenceImpl<T&, const U&>::Type>)
|
||||
struct TSimpleCommonReferenceImpl<T&, U&&> { using Type = typename TSimpleCommonReferenceImpl<T&, const U&>::Type; };
|
||||
// Otherwise, one of the two types must be a lvalue reference type A & and the other must be a rvalue reference type B &&
|
||||
// Let D denote the simple common reference type of A & and B const &, if any
|
||||
// If D exists and CConvertibleTo<B&&, D> is true, then the simple common reference type is D
|
||||
template <typename T, typename U> requires (CConvertibleTo<U&&, typename TSimpleCommonReferenceImpl<T&, const U&>::FType>)
|
||||
struct TSimpleCommonReferenceImpl<T&, U&&> { using FType = typename TSimpleCommonReferenceImpl<T&, const U&>::FType; };
|
||||
template <typename T, typename U> struct TSimpleCommonReferenceImpl<T&&, U&> : TSimpleCommonReferenceImpl<U&, T&&> { }; // The order is not important
|
||||
|
||||
// Otherwise, there's no simple common reference Type
|
||||
// Otherwise, there's no simple common reference type
|
||||
template <typename T, typename U>
|
||||
struct TSimpleCommonReferenceImpl { };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <typename... Ts> using TCommonType = typename NAMESPACE_PRIVATE::TCommonTypeImpl<Ts...>::Type;
|
||||
template <typename... Ts> using TCommonReference = typename NAMESPACE_PRIVATE::TCommonReferenceImpl<Ts...>::Type;
|
||||
template <typename... Ts> using TCommonType = typename NAMESPACE_PRIVATE::TCommonTypeImpl<Ts...>::FType;
|
||||
template <typename... Ts> using TCommonReference = typename NAMESPACE_PRIVATE::TCommonReferenceImpl<Ts...>::FType;
|
||||
|
||||
template <typename... Ts>
|
||||
concept CCommonReference =
|
||||
|
||||
@@ -11,35 +11,35 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <typename From, typename To> struct TCopyConstImpl { using Type = To; };
|
||||
template <typename From, typename To> struct TCopyConstImpl<const From, To> { using Type = const To; };
|
||||
template <typename From, typename To> struct TCopyConstImpl { using FType = To; };
|
||||
template <typename From, typename To> struct TCopyConstImpl<const From, To> { using FType = const To; };
|
||||
|
||||
template <typename From, typename To> struct TCopyVolatileImpl { using Type = To; };
|
||||
template <typename From, typename To> struct TCopyVolatileImpl<volatile From, To> { using Type = volatile To; };
|
||||
template <typename From, typename To> struct TCopyVolatileImpl { using FType = To; };
|
||||
template <typename From, typename To> struct TCopyVolatileImpl<volatile From, To> { using FType = volatile To; };
|
||||
|
||||
template <typename From, typename To> struct TCopyCVImpl { using Type = typename TCopyConstImpl<From, typename TCopyVolatileImpl<From, To>::Type>::Type; };
|
||||
template <typename From, typename To> struct TCopyCVImpl { using FType = typename TCopyConstImpl<From, typename TCopyVolatileImpl<From, To>::FType>::FType; };
|
||||
|
||||
template <typename From, typename To> struct TCopyReferenceImpl { using Type = To; };
|
||||
template <typename From, typename To> struct TCopyReferenceImpl<From&, To> { using Type = To&; };
|
||||
template <typename From, typename To> struct TCopyReferenceImpl<From&&, To> { using Type = To&&; };
|
||||
template <typename From, typename To> struct TCopyReferenceImpl { using FType = To; };
|
||||
template <typename From, typename To> struct TCopyReferenceImpl<From&, To> { using FType = To&; };
|
||||
template <typename From, typename To> struct TCopyReferenceImpl<From&&, To> { using FType = To&&; };
|
||||
|
||||
template <typename From, typename To> struct TCopyCVRefImpl { using Type = typename TCopyCVImpl<From, To>::Type; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From, To& > { using Type = typename TCopyCVImpl<From, To>::Type&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From, To&&> { using Type = typename TCopyCVImpl<From, To>::Type&&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&, To > { using Type = typename TCopyCVImpl<From, To>::Type&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&, To& > { using Type = typename TCopyCVImpl<From, To>::Type&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&, To&&> { using Type = typename TCopyCVImpl<From, To>::Type&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&&, To > { using Type = typename TCopyCVImpl<From, To>::Type&&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&&, To& > { using Type = typename TCopyCVImpl<From, To>::Type&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&&, To&&> { using Type = typename TCopyCVImpl<From, To>::Type&&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl { using FType = typename TCopyCVImpl<From, To>::FType; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From, To& > { using FType = typename TCopyCVImpl<From, To>::FType&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From, To&&> { using FType = typename TCopyCVImpl<From, To>::FType&&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&, To > { using FType = typename TCopyCVImpl<From, To>::FType&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&, To& > { using FType = typename TCopyCVImpl<From, To>::FType&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&, To&&> { using FType = typename TCopyCVImpl<From, To>::FType&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&&, To > { using FType = typename TCopyCVImpl<From, To>::FType&&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&&, To& > { using FType = typename TCopyCVImpl<From, To>::FType&; };
|
||||
template <typename From, typename To> struct TCopyCVRefImpl<From&&, To&&> { using FType = typename TCopyCVImpl<From, To>::FType&&; };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <typename From, typename To> using TCopyConst = typename NAMESPACE_PRIVATE::TCopyConstImpl<From, To>::Type;
|
||||
template <typename From, typename To> using TCopyVolatile = typename NAMESPACE_PRIVATE::TCopyVolatileImpl<From, To>::Type;
|
||||
template <typename From, typename To> using TCopyCV = typename NAMESPACE_PRIVATE::TCopyCVImpl<From, To>::Type;
|
||||
template <typename From, typename To> using TCopyReference = typename NAMESPACE_PRIVATE::TCopyReferenceImpl<From, To>::Type;
|
||||
template <typename From, typename To> using TCopyCVRef = typename NAMESPACE_PRIVATE::TCopyCVRefImpl<From, To>::Type;
|
||||
template <typename From, typename To> using TCopyConst = typename NAMESPACE_PRIVATE::TCopyConstImpl <From, To>::FType;
|
||||
template <typename From, typename To> using TCopyVolatile = typename NAMESPACE_PRIVATE::TCopyVolatileImpl <From, To>::FType;
|
||||
template <typename From, typename To> using TCopyCV = typename NAMESPACE_PRIVATE::TCopyCVImpl <From, To>::FType;
|
||||
template <typename From, typename To> using TCopyReference = typename NAMESPACE_PRIVATE::TCopyReferenceImpl<From, To>::FType;
|
||||
template <typename From, typename To> using TCopyCVRef = typename NAMESPACE_PRIVATE::TCopyCVRefImpl <From, To>::FType;
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
|
||||
@@ -6,14 +6,14 @@ NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
template <typename InType, InType InValue>
|
||||
template <typename T, T InValue>
|
||||
struct TConstant
|
||||
{
|
||||
using ValueType = InType;
|
||||
using Type = TConstant;
|
||||
static constexpr ValueType Value = InValue;
|
||||
FORCEINLINE constexpr operator ValueType() const { return Value; }
|
||||
FORCEINLINE constexpr ValueType operator()() const { return Value; }
|
||||
using FValueType = T;
|
||||
using FType = TConstant;
|
||||
static constexpr FValueType Value = InValue;
|
||||
FORCEINLINE constexpr operator FValueType() const { return Value; }
|
||||
FORCEINLINE constexpr FValueType operator()() const { return Value; }
|
||||
};
|
||||
|
||||
template <bool InValue>
|
||||
|
||||
Reference in New Issue
Block a user