Compare commits

...

31 Commits

Author SHA1 Message Date
6cc31f660c style(miscellaneous): add comments to the file system operation functions 2025-01-24 18:27:34 +08:00
2d79e18b25 fix(strings): fix access out-of-bounds when converting zero-length strings to null-terminated strings 2025-01-24 18:20:06 +08:00
1d5a61f308 feat(miscellaneous): add file system operation functions 2025-01-24 18:17:50 +08:00
38e1c3f8b7 feat(miscellaneous): add byte and text file access functions 2025-01-22 21:44:40 +08:00
8d3fa1ac98 feat(miscellaneous): add console operation functions 2025-01-20 12:04:57 +08:00
88e330336a refactor(strings): suppress compilation warnings from formatting tools 2025-01-20 12:04:16 +08:00
eca0d90d98 feat(miscellaneous): add enum class bitwise operation macro tools 2025-01-17 16:30:16 +08:00
d8adf47d10 feat(strings): add formatter for TStringView and TString 2025-01-15 19:33:13 +08:00
8a834a9c05 perf(strings): optimize formatting algorithm function 2025-01-15 17:04:05 +08:00
35f0ba71ab refactor(strings): replace and remove deprecated formatting tools 2025-01-14 22:28:52 +08:00
db7a40cb30 fix(strings): fix unexpected infinite recursion in TChar 2025-01-14 22:21:18 +08:00
6a70f0c501 fix(strings): fix treating boolean type as integral arguments 2025-01-14 22:19:12 +08:00
5629e31d49 fix(strings): fix string literal encoding types in formatting 2025-01-14 22:15:06 +08:00
c16da1af53 feat(strings): add formatter for pointer or member pointer types 2025-01-14 21:53:52 +08:00
88aba14620 fix(strings): fix dynamic formatting arguments not working 2025-01-14 17:53:16 +08:00
23963e0389 refactor(strings): refactor zero padding to make it more flexible 2025-01-14 17:39:19 +08:00
0c6f33762a fix(strings): fix incorrect treating 'A' as decimal number 2025-01-14 16:55:06 +08:00
9024957ff2 feat(strings): add formatter for floating-point types 2025-01-14 16:47:39 +08:00
68cd2c310a fix(strings): fix fill character cannot be defaulted 2025-01-07 22:09:53 +08:00
594acf219a feat(strings): add formatter for integral like types 2025-01-07 21:57:04 +08:00
e498b39d3d refactor(strings): refactor format string rules to make it more flexible 2025-01-04 21:51:35 +08:00
9dd5e74788 feat(strings): add a more general string formatting framework 2025-01-03 01:15:16 +08:00
c596882c32 feat(strings): add TChar::IsValid() support for char type 2025-01-03 01:14:09 +08:00
0e7ee5cde2 fix(templates): fix TTuple requires and implementations to avoid compilation failures 2025-01-03 01:12:39 +08:00
39d12bd7e4 refactor(templates): make TVariant conform to the style for general type identifiers 2025-01-03 01:10:04 +08:00
319d1cc8cb fix(ranges): fix TRangeView to be able to hold output iterator 2025-01-03 01:07:15 +08:00
fc87332845 fix(iterators): fix insert iterators to be assignable 2025-01-03 01:05:35 +08:00
22952237df fix(templates): fix the return value type of TTuple::Visit 2024-12-28 09:36:20 +08:00
f817afe6df fix(algorithms): fix compilation error on GCC for concept instantiation 2024-12-25 18:17:04 +08:00
a3cebf03ec refactor(strings): refactor and simplify string parsing functions 2024-12-25 18:15:46 +08:00
262ce1bb67 feat(ranges): add Ranges::AppendTo() function 2024-12-24 13:15:34 +08:00
19 changed files with 6226 additions and 3247 deletions

View 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

View 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)

View File

@ -2,9 +2,10 @@
#include "Strings/Char.h"
#include "Memory/Memory.h"
#include "Strings/String.h"
#include "Numerics/Numerics.h"
#include "Strings/String.h"
#include "Strings/StringView.h"
#include "Strings/Convert.h"
#include "Miscellaneous/AssertionMacros.h"
NAMESPACE_REDCRAFT_BEGIN
@ -196,10 +197,11 @@ void TestStringView()
{
always_check( LITERAL_VIEW(T, "012345678900").IsASCII());
always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").IsASCII());
always_check( LITERAL_VIEW(T, "012345678900").IsInteger());
always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").IsInteger());
always_check(!LITERAL_VIEW(T, "0123456789AB").IsInteger());
always_check( LITERAL_VIEW(T, "0123456789AB").IsInteger(16));
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));
}
};
@ -211,7 +213,7 @@ void TestStringView()
Test(InPlaceType<unicodechar>);
}
void TestTemplateString()
void TestString()
{
auto Test = []<typename T>(TInPlaceType<T>)
{
@ -448,38 +450,17 @@ 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.14) == LITERAL(T, "#3.140000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +TNumericLimits<float>::Infinity()) == LITERAL(T, "#Infinity#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -TNumericLimits<float>::Infinity()) == LITERAL(T, "#-Infinity#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +TNumericLimits<float>::QuietNaN()) == LITERAL(T, "#NaN#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -TNumericLimits<float>::QuietNaN()) == LITERAL(T, "#-NaN#"));
auto CheckParseArithmetic = []<typename U>(TStringView<T> View, U Result)
{
U Object;
if constexpr (CSameAs<U, bool>) always_check(View.Parse(LITERAL(T, "{0:}"), Object) == 1);
else if constexpr (CIntegral<U>) always_check(View.Parse(LITERAL(T, "{0:+#I}"), Object) == 1);
else if constexpr (CFloatingPoint<U>) always_check(View.Parse(LITERAL(T, "{0:+#G}"), 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>)
{
@ -554,6 +535,68 @@ void TestStringConversion()
CheckParseFloat(InPlaceType<float>);
CheckParseFloat(InPlaceType<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>);
Test(InPlaceType<wchar>);
Test(InPlaceType<u8char>);
Test(InPlaceType<u16char>);
Test(InPlaceType<u32char>);
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"));
@ -579,37 +622,6 @@ void TestStringConversion()
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" ));
}
{
always_check( LITERAL_VIEW(T, "True" ).ToBool());
always_check(!LITERAL_VIEW(T, "False" ).ToBool());
always_check( LITERAL_VIEW(T, "1" ).ToBool());
always_check(!LITERAL_VIEW(T, "0" ).ToBool());
always_check(!LITERAL_VIEW(T, "random").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, "Invalid").ToInt() == 0 );
always_check(LITERAL_VIEW(T, "999999999999999999999999999999").ToInt() == 0);
always_check(LITERAL_VIEW(T, "-999999999999999999999999999999").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);
always_check(Math::IsNaN(LITERAL_VIEW(T, "1e+308").ToFloat()));
always_check(Math::IsNaN(LITERAL_VIEW(T, "-1e+308").ToFloat()));
always_check(Math::IsNaN(LITERAL_VIEW(T, "1e-308").ToFloat()));
always_check(Math::IsNaN(LITERAL_VIEW(T, "-1e-308").ToFloat()));
}
};
Test(InPlaceType<char>);
@ -626,8 +638,9 @@ void TestString()
{
NAMESPACE_PRIVATE::TestChar();
NAMESPACE_PRIVATE::TestStringView();
NAMESPACE_PRIVATE::TestTemplateString();
NAMESPACE_PRIVATE::TestStringConversion();
NAMESPACE_PRIVATE::TestString();
NAMESPACE_PRIVATE::TestConvert();
NAMESPACE_PRIVATE::TestFormatting();
}
NAMESPACE_END(Testing)

View File

@ -1001,13 +1001,13 @@ 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)
{
if constexpr (CSameAs<T&&, int32&>) always_check(A == 2);
else if constexpr (CSameAs<T&&, char&>) always_check(A == 'B');
if constexpr (CSameAs<T&&, int32&>) always_check(A == 2 );
else if constexpr (CSameAs<T&&, char &>) always_check(A == 'B');
else always_check_no_entry();
}
);

View File

@ -1209,7 +1209,7 @@ template <CForwardRange R,
CRegularInvocable<TRangeReference<R>> Proj =
decltype([]<typename T>(T&& A) -> T&& { return Forward<T>(A); }),
CEquivalenceRelation<TInvokeResult<Proj, TRangeReference<R>>, TInvokeResult<Proj, TRangeReference<R>>> Pred =
TConditional<CWeaklyEqualityComparable<TInvokeResult<Proj, TRangeReference<R>>>,
TConditional<CWeaklyEqualityComparable<TInvokeResult<Proj, TRangeReference<R>>, TInvokeResult<Proj, TRangeReference<R>>>,
decltype([]<typename LHS, typename RHS>(const LHS& A, const RHS& B) { return A == B; }), void>>
requires (CBorrowedRange<R>)
NODISCARD constexpr TRangeIterator<R> FindAdjacent(R&& Range, Pred Predicate = { }, Proj Projection = { })
@ -1251,7 +1251,7 @@ template <CForwardIterator I, CSentinelFor<I> S,
CRegularInvocable<TIteratorReference<I>> Proj =
decltype([]<typename T>(T&& A) -> T&& { return Forward<T>(A); }),
CEquivalenceRelation<TInvokeResult<Proj, TIteratorReference<I>>, TInvokeResult<Proj, TIteratorReference<I>>> Pred =
TConditional<CWeaklyEqualityComparable<TInvokeResult<Proj, TIteratorReference<I>>>,
TConditional<CWeaklyEqualityComparable<TInvokeResult<Proj, TIteratorReference<I>>, TInvokeResult<Proj, TIteratorReference<I>>>,
decltype([]<typename LHS, typename RHS>(const LHS& A, const RHS& B) { return A == B; }), void>>
NODISCARD FORCEINLINE constexpr I FindAdjacent(I First, S Last, Pred Predicate = { }, Proj Projection = { })
{

View File

@ -6,6 +6,7 @@
#include "Templates/Utility.h"
#include "Templates/Noncopyable.h"
#include "Templates/Invoke.h"
#include "Memory/Address.h"
#include "Miscellaneous/AssertionMacros.h"
NAMESPACE_REDCRAFT_BEGIN
@ -124,27 +125,77 @@ private:
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([&Container]<typename T>(T&& A) { Container.PushFront(Forward<T>(A)); });
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([&Container]<typename T>(T&& A) { Container.PushBack(Forward<T>(A)); });
return NAMESPACE_PRIVATE::TInsertIterator(NAMESPACE_PRIVATE::FBackInserter(Container));
}
/** Creates an iterator adapter inserted in the container. */
template <typename C>
NODISCARD FORCEINLINE constexpr auto MakeInserter(C& Container, const typename C::FConstIterator& InIter)
template <typename C, typename I>
NODISCARD FORCEINLINE constexpr auto MakeInserter(C& Container, I&& InIter)
{
return NAMESPACE_PRIVATE::TInsertIterator([&Container, Iter = InIter]<typename T>(T&& A) mutable { Iter = Container.Insert(Iter, Forward<T>(A)); ++Iter; });
return NAMESPACE_PRIVATE::TInsertIterator(NAMESPACE_PRIVATE::FInserter(Container, Forward<I>(InIter)));
}
NAMESPACE_MODULE_END(Utility)

View 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

View 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

View 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

View File

@ -32,25 +32,50 @@ 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 Ref>
template <typename C, typename T>
concept CAppendableContainer =
requires (C& Container, Ref&& Reference)
requires (C& Container, T&& Object)
{
requires
(
requires { Container.EmplaceBack (Forward<Ref>(Reference)); } ||
requires { Container.PushBack (Forward<Ref>(Reference)); } ||
requires { Container.Emplace(Container.End(), Forward<Ref>(Reference)); } ||
requires { Container.Insert (Container.End(), Forward<Ref>(Reference)); }
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)
@ -78,25 +103,7 @@ NODISCARD FORCEINLINE constexpr auto To(R&& Range, Ts&&... Args)
for (TRangeReference<R> Element : Range)
{
if constexpr (requires { Result.EmplaceBack(DeclVal<TRangeReference<R>>()); })
{
Result.EmplaceBack(Forward<TRangeReference<R>>(Element));
}
else if constexpr (requires { Result.PushBack(DeclVal<TRangeReference<R>>()); })
{
Result.PushBack(Forward<TRangeReference<R>>(Element));
}
else if constexpr (requires { Result.Emplace(Result.End(), DeclVal<TRangeReference<R>>()); })
{
Result.Emplace(Result.End(), Forward<TRangeReference<R>>(Element));
}
else /* if constexpr (requires { Result.Insert(Result.End(), DeclVal<TRangeReference<R>>()); }) */
{
Result.Insert(Result.End(), Forward<TRangeReference<R>>(Element));
}
Ranges::AppendTo(Result, Forward<TRangeReference<R>>(Element));
}
return Result;

View File

@ -112,8 +112,6 @@ class TRangeView : public IBasicViewInterface<TRangeView<I, S>>
{
public:
using FElementType = TIteratorElement<I>;
FORCEINLINE constexpr TRangeView() requires (CDefaultConstructible<I>) = default;
FORCEINLINE constexpr TRangeView(I InFirst, S InLast) : First(MoveTemp(InFirst)), Last(InLast) { }

View File

@ -85,7 +85,14 @@ struct TChar
NODISCARD FORCEINLINE static constexpr bool IsValid(FCharType InChar)
{
if constexpr (CSameAs<FCharType, u8char>)
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;
@ -103,13 +110,13 @@ struct TChar
// Windows uses UTF-16 encoding for wchar.
else if constexpr (PLATFORM_WINDOWS && (CSameAs<FCharType, wchar>))
{
return TChar::IsValid(static_cast<u16char>(InChar));
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::IsValid(static_cast<u32char>(InChar));
return TChar<u32char>::IsValid(static_cast<u32char>(InChar));
}
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");
@ -146,13 +153,13 @@ struct TChar
// Windows uses UTF-16 encoding for wchar.
else if constexpr (PLATFORM_WINDOWS && (CSameAs<FCharType, wchar>))
{
return TChar::IsNonch(static_cast<u16char>(InChar));
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::IsNonch(static_cast<u32char>(InChar));
return TChar<u32char>::IsNonch(static_cast<u32char>(InChar));
}
else static_assert(sizeof(FCharType) == -1, "Unsupported character type");

File diff suppressed because it is too large Load Diff

View 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)

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,14 @@
#include "Containers/Array.h"
#include "Containers/ArrayView.h"
#include "Iterators/Utility.h"
#include "Iterators/BasicIterator.h"
#include "Iterators/Sentinel.h"
#include "Iterators/BasicIterator.h"
#include "Iterators/InsertIterator.h"
#include "Ranges/Utility.h"
#include "Ranges/Factory.h"
#include "Strings/Char.h"
#include "Strings/StringView.h"
#include "Strings/Formatting.h"
#include "Miscellaneous/AssertionMacros.h"
#include <locale>
@ -1122,10 +1124,7 @@ public:
/** @return The non-modifiable standard C character string version of the string. */
NODISCARD FORCEINLINE auto operator*() &&
{
if (this->Back() != LITERAL(T, '\0'))
{
this->PushBack(LITERAL(T, '\0'));
}
if (!EndsWith(LITERAL(T, '\0'))) this->PushBack(LITERAL(T, '\0'));
return AsConst(*this).GetData();
}
@ -1144,22 +1143,64 @@ public:
return TStringView<FElementType>(*this).IsASCII();
}
/** @return true if the string can be fully represented as a boolean value, false otherwise. */
/** @return true if the string can be converted to a boolean value, false otherwise. */
NODISCARD FORCEINLINE bool IsBoolean() const
{
return TStringView<FElementType>(*this).IsBoolean();
}
/** @return true if the string can be fully represented as an integer value, false otherwise. */
NODISCARD FORCEINLINE bool IsInteger(unsigned Base = 10, bool bSigned = true) const
/** @return true if the string can be converted to an integer value, false otherwise. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && CSameAs<TRemoveCVRef<U>, U>)
NODISCARD FORCEINLINE bool IsInteger(uint Base = 0) const
{
return TStringView<FElementType>(*this).IsInteger(Base, bSigned);
return TStringView<FElementType>(*this).template IsInteger<U>(Base);
}
/** @return true if the string can be fully represented as a floating-point value, false otherwise. */
NODISCARD FORCEINLINE bool IsFloatingPoint(bool bFixed = true, bool bScientific = true, bool bSigned = true) const
/** @return true if the string can be converted to a floating-point value, false otherwise. */
template <CFloatingPoint U = float> requires (!CSameAs<U, bool> && CSameAs<TRemoveCVRef<U>, U>)
NODISCARD FORCEINLINE bool IsFloatingPoint(bool bFixed = true, bool bScientific = true, bool bHex = true) const
{
return TStringView<FElementType>(*this).IsFloatingPoint(bFixed, bScientific, bSigned);
return TStringView<FElementType>(*this).template IsFloatingPoint<U>(bFixed, bScientific, bHex);
}
/** Converts the string into a boolean value. */
NODISCARD FORCEINLINE constexpr bool ToBool() const
{
return TStringView<FElementType>(*this).ToBool();
}
/** Converts the string into an integer value. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr U ToInt(uint Base = 0) const
{
return TStringView<FElementType>(*this).template ToInt<U>(Base);
}
/** Converts the string into a floating-point value. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr U ToFloat(bool bFixed = true, bool bScientific = true, bool bHex = true) const
{
return TStringView<FElementType>(*this).template ToFloat<U>(bFixed, bScientific, bHex);
}
/** Parse the string into a boolean value. */
NODISCARD FORCEINLINE constexpr bool Parse(bool& Value)
{
return TStringView<FElementType>(*this).Parse(Value);
}
/** Parse the string into an integer value. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, uint Base = 0)
{
return TStringView<FElementType>(*this).Parse(Value, Base);
}
/** Parse the string into a floating-point value. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, bool bFixed = true, bool bScientific = true, bool bHex = true)
{
return TStringView<FElementType>(*this).Parse(Value, bFixed, bScientific, bHex);
}
public:
@ -1189,7 +1230,7 @@ public:
* @return The string containing the integer value.
*/
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD static FORCEINLINE TString FromInt(U Value, unsigned Base = 10)
NODISCARD static FORCEINLINE TString FromInt(U Value, uint Base = 10)
{
checkf(Base >= 2 && Base <= 36, TEXT("Illegal base. Please check the base."));
@ -1247,7 +1288,7 @@ public:
* @return
*/
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD static FORCEINLINE TString FromFloat(U Value, bool bFixed, bool bScientific, unsigned Precision)
NODISCARD static FORCEINLINE TString FromFloat(U Value, bool bFixed, bool bScientific, uint Precision)
{
TString Result;
@ -1257,121 +1298,83 @@ public:
}
/** Converts a boolean value into a string and appends it to the string. */
void AppendBool(bool Value);
FORCEINLINE void AppendBool(bool Value)
{
auto Inserter = Ranges::View(MakeBackInserter(*this), UnreachableSentinel);
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0}"), Value);
}
/** Converts an integer value into a string and appends it to the string. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
void AppendInt(U Value, unsigned Base = 10);
FORCEINLINE void AppendInt(U Value, uint Base = 10)
{
auto Inserter = Ranges::View(MakeBackInserter(*this), UnreachableSentinel);
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:_{1}I}"), Value, Base);
}
/** Converts a floating-point value into a string and appends it to the string. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
void AppendFloat(U Value);
FORCEINLINE void AppendFloat(U Value)
{
auto Inserter = Ranges::View(MakeBackInserter(*this), UnreachableSentinel);
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0}"), Value);
}
/** Converts a floating-point value into a string and appends it to the string. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
void AppendFloat(U Value, bool bFixed, bool bScientific);
FORCEINLINE void AppendFloat(U Value, bool bFixed, bool bScientific)
{
auto Inserter = Ranges::View(MakeBackInserter(*this), UnreachableSentinel);
if (bFixed && bScientific)
{
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:G}"), Value);
}
else if (bFixed)
{
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:F}"), Value);
}
else if (bScientific)
{
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:E}"), Value);
}
else
{
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:A}"), Value);
}
}
/** Converts a floating-point value into a string and appends it to the string. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
void AppendFloat(U Value, bool bFixed, bool bScientific, unsigned Precision);
/**
* Converts a string into a boolean value.
*
* - "True" and non-zero integers become true.
* - "False" and unparsable values become false.
*
* @return The boolean value.
*/
NODISCARD FORCEINLINE bool ToBool() const
FORCEINLINE void AppendFloat(U Value, bool bFixed, bool bScientific, uint Precision)
{
return TStringView<FElementType>(*this).ToBool();
}
auto Inserter = Ranges::View(MakeBackInserter(*this), UnreachableSentinel);
/**
* Converts a string into an integer value.
*
* - "0x" or "0X" prefixes are not recognized if base is 16.
* - Only the minus sign is recognized (not the plus sign), and only for signed integer types of value.
* - Leading whitespace is not ignored.
*
* Ensure that the entire string can be parsed if IsNumeric(Base, false, true, false) is true.
*
* @param Base - The base of the number, between [2, 36].
*
* @return The integer value.
*/
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE U ToInt(unsigned Base = 10) const
{
checkf(Base >= 2 && Base <= 36, TEXT("Illegal base. Please check the base."));
if (bFixed && bScientific)
{
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:.{1}G}"), Value, Precision);
}
return TStringView<FElementType>(*this).template ToInt<U>(Base);
}
else if (bFixed)
{
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:.{1}F}"), Value, Precision);
}
/**
* Converts a string into a floating-point value.
*
* - "0x" or "0X" prefixes are not recognized if base is 16.
* - The plus sign is not recognized outside the exponent (only the minus sign is permitted at the beginning).
* - Leading whitespace is not ignored.
*
* Ensure that the entire string can be parsed if bFixed and IsNumeric(10, false) is true.
* Parsers hex floating-point values if bFixed and bScientific are false.
*
* @param bFixed - The fixed-point format.
* @param bScientific - The scientific notation.
*
* @return The floating-point value.
*/
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE U ToFloat(bool bFixed = true, bool bScientific = false) const
{
return TStringView<FElementType>(*this).template ToFloat<U>(bFixed, bScientific);
}
else if (bScientific)
{
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:.{1}E}"), Value, Precision);
}
/** Converts a string into a boolean value and remove the parsed substring. */
NODISCARD FORCEINLINE bool ToBoolAndTrim()
{
TStringView<FElementType> View = *this;
bool Result = View.ToBoolAndTrim();
size_t TrimNum = this->Num() - View.Num();
if (TrimNum > 0) Erase(0, TrimNum);
return Result;
}
/** Converts a string into an integer value and remove the parsed substring. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE U ToIntAndTrim(unsigned Base = 10)
{
TStringView<FElementType> View = *this;
U Result = View.template ToIntAndTrim<U>(Base);
size_t TrimNum = this->Num() - View.Num();
if (TrimNum > 0) Erase(0, TrimNum);
return Result;
}
/** Converts a string into a floating-point value and remove the parsed substring. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE U ToFloatAndTrim(bool bFixed = true, bool bScientific = true)
{
TStringView<FElementType> View = *this;
U Result = View.template ToFloatAndTrim<U>(bFixed, bScientific);
size_t TrimNum = this->Num() - View.Num();
if (TrimNum > 0) Erase(0, TrimNum);
return Result;
else
{
Algorithms::Format(Inserter, LITERAL_VIEW(FElementType, "{0:.{1}A}"), Value, Precision);
}
}
public:
@ -1396,35 +1399,11 @@ public:
/** Format some objects using a format string and append to the string. */
template <typename... Ts>
void AppendFormat(TStringView<FElementType> Fmt, const Ts&... Args);
/**
* 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>
FORCEINLINE size_t Parse(TStringView<FElementType> Fmt, Ts&... Args) const
FORCEINLINE void AppendFormat(TStringView<FElementType> Fmt, const Ts&... Args)
{
return TStringView(*this).Parse(Fmt, Args...);
}
auto Inserter = Ranges::View(MakeBackInserter(*this), UnreachableSentinel);
/** Parse a string using a format string to objects and remove the parsed substring. */
template <typename... Ts>
FORCEINLINE size_t ParseAndTrim(TStringView<FElementType> Fmt, Ts&... Args)
{
TStringView<FElementType> View = *this;
size_t Result = View.ParseAndTrim(Fmt, Args...);
size_t TrimNum = this->Num() - View.Num();
if (TrimNum > 0) Erase(0, TrimNum);
return Result;
Algorithms::Format(Inserter, Fmt, Args...);
}
public:
@ -1460,10 +1439,68 @@ using FU32String = TString<u32char>;
using FUnicodeString = TString<unicodechar>;
template <CCharType T> template <typename Allocator> constexpr TStringView<T>::TStringView(const TString<FElementType, Allocator>& InString)
: TStringView(InString.GetData(), InString.Num()) { }
: TStringView(InString.GetData(), InString.Num())
{ }
/**
* A formatter for TString.
*
* The syntax of format specifications is:
*
* [Fill And Align] [Width] [Precision] [Type] [!] [?]
*
* 1. The fill and align part:
*
* [Fill Character] <Align Option>
*
* i. Fill Character: The character is used to fill width of the object. It is optional and cannot be '{' or '}'.
* It should be representable as a single unicode otherwise it is undefined behavior.
*
* ii. Align Option: The character is used to indicate the direction of alignment.
*
* - '<': Align the formatted argument to the left of the available space
* by inserting n fill characters after the formatted argument.
* This is default option.
* - '^': Align the formatted argument to the center of the available space
* by inserting n fill characters around the formatted argument.
* If cannot absolute centering, offset to the left.
* - '>': Align the formatted argument ro the right of the available space
* by inserting n fill characters before the formatted argument.
*
* 2. The width part:
*
* - 'N': The number is used to specify the minimum field width of the object.
* N should be an unsigned non-zero decimal number.
* - '{N}': Dynamically determine the minimum field width of the object.
* N should be a valid index of the format integral argument.
* N is optional, and the default value is automatic indexing.
*
* 3. The precision part:
*
* - '.N': The number is used to specify the maximum field width of the object.
* N should be an unsigned non-zero decimal number.
* - '.{N}': Dynamically determine the maximum field width of the object.
* N should be a valid index of the format integral argument.
* N is optional, and the default value is automatic indexing.
*
* 4. The type indicator part:
*
* - none: Indicates the as-is formatting.
* - 'S': Indicates the as-is formatting.
* - 's': Indicates lowercase formatting.
*
* 5. The case indicators part:
*
* - '!': Indicates capitalize the entire string.
*
* 6. The escape indicators part:
*
* - '?': Indicates the escape formatting.
*
*/
template <CCharType T, typename Allocator>
class TFormatter<TString<T, Allocator>, T> : public TFormatter<TStringView<T>, T> { };
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END
#include "Strings/Conversion.h.inl"

View File

@ -12,6 +12,8 @@
#include "Iterators/BasicIterator.h"
#include "Iterators/Sentinel.h"
#include "Strings/Char.h"
#include "Strings/Convert.h"
#include "Strings/Formatting.h"
#include "Miscellaneous/AssertionMacros.h"
#include <cstring>
@ -491,7 +493,7 @@ public:
/** @return The non-modifiable standard C character string version of the string view. */
NODISCARD FORCEINLINE auto operator*() const
{
if (this->Back() == LITERAL(FElementType, '\0') || Contains(LITERAL(FElementType, '\0')))
if (EndsWith(LITERAL(FElementType, '\0')) || Contains(LITERAL(FElementType, '\0')))
{
return NAMESPACE_PRIVATE::TCStringFromTStringView<FElementType>(this->GetData(), false);
}
@ -529,133 +531,83 @@ public:
return true;
}
/** @return true if the string can be fully represented as a boolean value, false otherwise. */
/** @return true if the string can be converted to a boolean value, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsBoolean() const
{
TStringView View = *this;
bool Temp;
Ignore = View.ToBoolAndTrim();
return View.IsEmpty();
return Algorithms::Parse(*this, Temp);
}
/** @return true if the string can be fully represented as an integer value, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsInteger(unsigned Base = 10, bool bSigned = true) const
/** @return true if the string can be converted to an integer value, false otherwise. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && CSameAs<TRemoveCVRef<U>, U>)
NODISCARD FORCEINLINE constexpr bool IsInteger(uint Base = 0) const
{
TStringView View = *this;
U Temp;
if (View.StartsWith(LITERAL(FElementType, '-')))
{
if (bSigned) View.RemovePrefix(1);
else return false;
}
Ignore = View.ToIntAndTrim(Base);
return View.IsEmpty();
return Algorithms::Parse(*this, Temp, Base);
}
/** @return true if the string can be fully represented as a floating-point value, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsFloatingPoint(bool bFixed = true, bool bScientific = true, bool bSigned = true) const
/** @return true if the string can be converted to a floating-point value, false otherwise. */
template <CFloatingPoint U = float> requires (!CSameAs<U, bool> && CSameAs<TRemoveCVRef<U>, U>)
NODISCARD FORCEINLINE constexpr bool IsFloatingPoint(bool bFixed = true, bool bScientific = true, bool bHex = true) const
{
TStringView View = *this;
U Temp;
if (View.StartsWith(LITERAL(FElementType, '-')))
{
if (bSigned) View.RemovePrefix(1);
else return false;
}
Ignore = View.ToFloatAndTrim(bFixed, bScientific);
return View.IsEmpty();
return Algorithms::Parse(*this, Temp, bFixed, bScientific, bHex);
}
public:
/**
* Converts a string into a boolean value.
*
* - "True" and non-zero integers become true.
* - "False" and unparsable values become false.
*
* @return The boolean value.
*/
NODISCARD constexpr bool ToBool() const
/** Converts the string into a boolean value. */
NODISCARD FORCEINLINE constexpr bool ToBool() const
{
return TStringView(*this).ToBoolAndTrim();
bool Result;
verifyf(Algorithms::Parse(*this, Result), TEXT("Illegal conversion. Please check the IsBoolean()."));
return Result;
}
/**
* Converts a string into an integer value.
*
* - "0x" or "0X" prefixes are not recognized if base is 16.
* - Only the minus sign is recognized (not the plus sign), and only for signed integer types of value.
* - Leading whitespace is not ignored.
*
* Ensure that the entire string can be parsed if IsNumeric(Base, false, true, false) is true.
*
* @param Base - The base of the number, between [2, 36].
*
* @return The integer value.
*/
/** Converts the string into an integer value. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD constexpr U ToInt(unsigned Base = 10) const
NODISCARD FORCEINLINE constexpr U ToInt(uint Base = 0) const
{
return TStringView(*this).ToIntAndTrim<U>(Base);
U Result;
verifyf(Algorithms::Parse(*this, Result, Base), TEXT("Illegal conversion. Please check the IsInteger()."));
return Result;
}
/**
* Converts a string into a floating-point value.
*
* - "0x" or "0X" prefixes are not recognized if base is 16.
* - The plus sign is not recognized outside the exponent (only the minus sign is permitted at the beginning).
* - Leading whitespace is not ignored.
*
* Ensure that the entire string can be parsed if bFixed and IsNumeric(10, false) is true.
* Parsers hex floating-point values if bFixed and bScientific are false.
*
* @param bFixed - The fixed-point format.
* @param bScientific - The scientific notation.
*
* @return The floating-point value.
*/
/** Converts the string into a floating-point value. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD constexpr U ToFloat(bool bFixed = true, bool bScientific = true) const
NODISCARD FORCEINLINE constexpr U ToFloat(bool bFixed = true, bool bScientific = true, bool bHex = true) const
{
return TStringView(*this).ToFloatAndTrim<U>(bFixed, bScientific);
U Result;
verifyf(Algorithms::Parse(*this, Result, bFixed, bScientific, bHex), TEXT("Illegal conversion. Please check the IsFloatingPoint()."));
return Result;
}
/** Converts a string into a boolean value and remove the parsed substring. */
NODISCARD constexpr bool ToBoolAndTrim();
/** Parse the string into a boolean value. */
NODISCARD FORCEINLINE constexpr bool Parse(bool& Value)
{
return Algorithms::Parse(*this, Value);
}
/** Converts a string into an integer value and remove the parsed substring. */
/** Parse the string into an integer value. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD constexpr U ToIntAndTrim(unsigned Base = 10);
/** Converts a string into a floating-point value and remove the parsed substring. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD constexpr U ToFloatAndTrim(bool bFixed = true, bool bScientific = true);
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
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, uint Base = 0)
{
return TStringView(*this).ParseAndTrim(Fmt, Args...);
return Algorithms::Parse(*this, Value, Base);
}
/** Parse a string using a format string to objects and remove the parsed substring. */
template <typename... Ts>
size_t ParseAndTrim(TStringView Fmt, Ts&... Args);
/** Parse the string into a floating-point value. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, bool bFixed = true, bool bScientific = true, bool bHex = true)
{
return Algorithms::Parse(*this, Value, bFixed, bScientific, bHex);
}
public:
@ -693,6 +645,634 @@ using FUnicodeStringView = TStringView<unicodechar>;
// ReSharper restore CppInconsistentNaming
/**
* A formatter for TStringView.
*
* The syntax of format specifications is:
*
* [Fill And Align] [Width] [Precision] [Type] [!] [?]
*
* 1. The fill and align part:
*
* [Fill Character] <Align Option>
*
* i. Fill Character: The character is used to fill width of the object. It is optional and cannot be '{' or '}'.
* It should be representable as a single unicode otherwise it is undefined behavior.
*
* ii. Align Option: The character is used to indicate the direction of alignment.
*
* - '<': Align the formatted argument to the left of the available space
* by inserting n fill characters after the formatted argument.
* This is default option.
* - '^': Align the formatted argument to the center of the available space
* by inserting n fill characters around the formatted argument.
* If cannot absolute centering, offset to the left.
* - '>': Align the formatted argument ro the right of the available space
* by inserting n fill characters before the formatted argument.
*
* 2. The width part:
*
* - 'N': The number is used to specify the minimum field width of the object.
* N should be an unsigned non-zero decimal number.
* - '{N}': Dynamically determine the minimum field width of the object.
* N should be a valid index of the format integral argument.
* N is optional, and the default value is automatic indexing.
*
* 3. The precision part:
*
* - '.N': The number is used to specify the maximum field width of the object.
* N should be an unsigned non-zero decimal number.
* - '.{N}': Dynamically determine the maximum field width of the object.
* N should be a valid index of the format integral argument.
* N is optional, and the default value is automatic indexing.
*
* 4. The type indicator part:
*
* - none: Indicates the as-is formatting.
* - 'S': Indicates the as-is formatting.
* - 's': Indicates lowercase formatting.
*
* 5. The case indicators part:
*
* - '!': Indicates capitalize the entire string.
*
* 6. The escape indicators part:
*
* - '?': Indicates the escape formatting.
*
*/
template <CCharType T>
class TFormatter<TStringView<T>, T>
{
private:
using FCharType = T;
using FCharTraits = TChar<FCharType>;
using FFillCharacter = TStaticArray<FCharType, FCharTraits::MaxCodeUnitLength>;
public:
template <CFormatStringContext<FCharType> CTX>
constexpr TRangeIterator<CTX> Parse(CTX& Context)
{
auto Iter = Ranges::Begin(Context);
auto Sent = Ranges::End (Context);
// Set the default values.
{
FillUnitLength = 1;
FillCharacter[0] = LITERAL(FCharType, ' ');
AlignOption = LITERAL(FCharType, '<');
MinFieldWidth = 0;
MaxFieldWidth = -1;
bDynamicMin = false;
bDynamicMax = false;
bLowercase = false;
bUppercase = false;
bEscape = false;
}
// If the format description string is empty.
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
FCharType Char = *Iter; ++Iter;
// Try to parse the fill and align part.
// This code assumes that the format string does not contain multi-unit characters, except for fill character.
// If the fill character is multi-unit.
if (!FCharTraits::IsValid(Char))
{
FillUnitLength = 1;
FillCharacter[0] = Char;
while (true)
{
if (Iter == Sent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
Char = *Iter; ++Iter;
// If the fill character ends.
if (FillUnitLength == FCharTraits::MaxCodeUnitLength || FCharTraits::IsValid(Char)) break;
FillCharacter[FillUnitLength++] = Char;
}
if (Char != LITERAL(FCharType, '<') && Char != LITERAL(FCharType, '^') && Char != LITERAL(FCharType, '>')) UNLIKELY
{
checkf(false, TEXT("Illegal format string. The fill character is not representable as a single unicode."));
return Iter;
}
AlignOption = Char;
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
// If the fill character is single-unit.
else do
{
if (Iter == Sent) break;
// If the fill character is specified.
if (*Iter == LITERAL(FCharType, '<') || *Iter == LITERAL(FCharType, '^') || *Iter == LITERAL(FCharType, '>'))
{
FillUnitLength = 1;
FillCharacter[0] = Char;
Char = *Iter; ++Iter;
}
// If the fill character is not specified and the align option is not specified.
else if (Char != LITERAL(FCharType, '<') && Char != LITERAL(FCharType, '^') && Char != LITERAL(FCharType, '>')) break;
AlignOption = Char;
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
while (false);
// Try to parse the width part.
{
if (Char == LITERAL(FCharType, '{'))
{
bDynamicMin = true;
MinFieldWidth = INDEX_NONE;
if (Iter == Sent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
Char = *Iter; ++Iter;
}
if ((bDynamicMin || Char != LITERAL(FCharType, '0')) && FCharTraits::IsDigit(Char))
{
MinFieldWidth = FCharTraits::ToDigit(Char);
while (true)
{
if (Iter == Sent)
{
checkf(!bDynamicMin, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
if (!bDynamicMin && *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
const uint Digit = FCharTraits::ToDigit(Char);
if (Digit >= 10) break;
MinFieldWidth = MinFieldWidth * 10 + Digit;
}
}
if (bDynamicMin)
{
if (Char != LITERAL(FCharType, '}')) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
do
{
// Try to automatic indexing.
if (MinFieldWidth == INDEX_NONE)
{
MinFieldWidth = Context.GetNextIndex();
if (MinFieldWidth == INDEX_NONE) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the field width."));
}
else break;
}
// Try to manual indexing.
else if (!Context.CheckIndex(MinFieldWidth)) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the field width."));
}
else break;
bDynamicMin = false;
MinFieldWidth = 0;
}
while (false);
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
}
// Try to parse the precision part.
if (Char == LITERAL(FCharType, '.'))
{
if (Iter == Sent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing precision in format string."));
return Iter;
}
Char = *Iter; ++Iter;
if (Char == LITERAL(FCharType, '{'))
{
bDynamicMax = true;
MaxFieldWidth = INDEX_NONE;
if (Iter == Sent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
Char = *Iter; ++Iter;
}
if ((bDynamicMax || Char != LITERAL(FCharType, '0')) && FCharTraits::IsDigit(Char))
{
MaxFieldWidth = FCharTraits::ToDigit(Char);
while (true)
{
if (Iter == Sent)
{
checkf(!bDynamicMax, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
if (!bDynamicMax && *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
const uint Digit = FCharTraits::ToDigit(Char);
if (Digit >= 10) break;
MaxFieldWidth = MaxFieldWidth * 10 + Digit;
}
}
else if (!bDynamicMax)
{
checkf(false, TEXT("Illegal format string. Missing precision in format string."));
return Iter;
}
if (bDynamicMax)
{
if (Char != LITERAL(FCharType, '}')) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
do
{
// Try to automatic indexing.
if (MaxFieldWidth == INDEX_NONE)
{
MaxFieldWidth = Context.GetNextIndex();
if (MaxFieldWidth == INDEX_NONE) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the precision."));
}
else break;
}
// Try to manual indexing.
else if (!Context.CheckIndex(MaxFieldWidth)) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the precision."));
}
else break;
bDynamicMax = false;
MaxFieldWidth = -1;
}
while (false);
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
}
// Try to parse the type indicators part.
switch (Char)
{
case LITERAL(FCharType, 's'): bLowercase = true; break;
default: { }
}
switch (Char)
{
case LITERAL(FCharType, 'S'):
case LITERAL(FCharType, 's'): if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter; Char = *Iter; ++Iter; break;
default: { }
}
// Try to parse the case indicators part.
if (Char == LITERAL(FCharType, '!'))
{
bUppercase = true;
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
// Try to parse the escape indicators part.
if (Char == LITERAL(FCharType, '?'))
{
bEscape = true;
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
template <CFormatObjectContext<FCharType> CTX>
constexpr TRangeIterator<CTX> Format(TStringView<FCharType> Object, CTX& Context) const
{
auto Iter = Ranges::Begin(Context);
auto Sent = Ranges::End (Context);
size_t MinDynamicField = MinFieldWidth;
size_t MaxDynamicField = MaxFieldWidth;
// Visit the dynamic width argument.
if (bDynamicMin)
{
MinDynamicField = Context.Visit([]<typename U>(U&& Value) -> size_t
{
using FDecayU = TRemoveCVRef<U>;
if constexpr (CIntegral<FDecayU> && !CSameAs<FDecayU, bool>)
{
checkf(Value > 0, TEXT("Illegal format argument. The dynamic width argument must be a unsigned non-zero number."));
return Math::Max(Value, 1);
}
else
{
checkf(false, TEXT("Illegal format argument. The dynamic width argument must be an integral."));
return 0;
}
}
, MinFieldWidth);
}
// Visit the dynamic precision argument.
if (bDynamicMax)
{
MaxDynamicField = Context.Visit([]<typename U>(U&& Value) -> size_t
{
using FDecayU = TRemoveCVRef<U>;
if constexpr (CIntegral<FDecayU> && !CSameAs<FDecayU, bool>)
{
checkf(Value > 0, TEXT("Illegal format argument. The dynamic precision argument must be a unsigned non-zero number."));
return Math::Max(Value, 1);
}
else
{
checkf(false, TEXT("Illegal format argument. The dynamic precision argument must be an integral."));
return 0;
}
}
, MaxFieldWidth);
}
size_t LeftPadding = 0;
size_t RightPadding = 0;
// Estimate the field width.
if (MinDynamicField != 0)
{
// If escape formatting is enabled, add quotes characters.
size_t FieldWidth = bEscape ? 2 : 0;
for (auto ObjectIter = Object.Begin(); ObjectIter != Object.End(); ++ObjectIter)
{
if (bEscape)
{
switch (const FCharType Char = *ObjectIter)
{
case LITERAL(FCharType, '\"'):
case LITERAL(FCharType, '\\'):
case LITERAL(FCharType, '\a'):
case LITERAL(FCharType, '\b'):
case LITERAL(FCharType, '\f'):
case LITERAL(FCharType, '\n'):
case LITERAL(FCharType, '\r'):
case LITERAL(FCharType, '\t'):
case LITERAL(FCharType, '\v'): FieldWidth += 2; break;
default:
{
// Use '\x00' format for other non-printable characters.
if (!FCharTraits::IsASCII(Char) || !FCharTraits::IsPrint(Char))
{
FieldWidth += 2 + sizeof(FCharType) * 2;
}
else ++FieldWidth;
}
}
}
else ++FieldWidth;
}
const size_t PaddingWidth = MinDynamicField - Math::Min(FieldWidth, MinDynamicField, MaxDynamicField);
switch (AlignOption)
{
default:
case LITERAL(FCharType, '<'): RightPadding = PaddingWidth; break;
case LITERAL(FCharType, '>'): LeftPadding = PaddingWidth; break;
case LITERAL(FCharType, '^'):
LeftPadding = Math::DivAndFloor(PaddingWidth, 2);
RightPadding = PaddingWidth - LeftPadding;
}
}
// Write the left padding.
for (size_t Index = 0; Index != LeftPadding; ++Index)
{
for (size_t Jndex = 0; Jndex != FillUnitLength; ++Jndex)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = FillCharacter[Jndex];
}
}
// Write the left quote.
if (bEscape)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = LITERAL(FCharType, '\"');
}
auto ObjectIter = Object.Begin();
bool bComplete = false;
// Write the object, include escaped quotes in the counter.
for (size_t Index = bEscape ? 1 : 0; Index != MaxDynamicField; ++Index)
{
if (ObjectIter == Object.End())
{
bComplete = true;
break;
}
FCharType Char = *ObjectIter++;
if (Iter == Sent) UNLIKELY return Iter;
// Convert the character case.
if (bLowercase) Char = FCharTraits::ToLower(Char);
if (bUppercase) Char = FCharTraits::ToUpper(Char);
if (bEscape)
{
switch (Char)
{
case LITERAL(FCharType, '\"'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, '\"'); break;
case LITERAL(FCharType, '\\'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, '\\'); break;
case LITERAL(FCharType, '\a'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'a'); break;
case LITERAL(FCharType, '\b'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'b'); break;
case LITERAL(FCharType, '\f'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'f'); break;
case LITERAL(FCharType, '\n'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'n'); break;
case LITERAL(FCharType, '\r'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'r'); break;
case LITERAL(FCharType, '\t'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 't'); break;
case LITERAL(FCharType, '\v'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'v'); break;
default:
{
// Use '\x00' format for other non-printable characters.
if (!FCharTraits::IsASCII(Char) || !FCharTraits::IsPrint(Char))
{
*Iter++ = LITERAL(FCharType, '\\');
*Iter++ = LITERAL(FCharType, 'x' );
using FUnsignedT = TMakeUnsigned<FCharType>;
constexpr size_t DigitNum = sizeof(FCharType) * 2;
FUnsignedT IntValue = static_cast<FUnsignedT>(Char);
TStaticArray<FCharType, DigitNum> Buffer;
for (size_t Jndex = 0; Jndex != DigitNum; ++Jndex)
{
Buffer[DigitNum - Jndex - 1] = FCharTraits::FromDigit(IntValue & 0xF);
IntValue >>= 4;
}
check(IntValue == 0);
for (size_t Jndex = 0; Jndex != DigitNum; ++Jndex)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = Buffer[Jndex];
}
}
else *Iter++ = Char;
}
}
}
else *Iter++ = Char;
}
// Write the right quote, if the field width is enough.
if (bEscape && bComplete)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = LITERAL(FCharType, '\"');
}
// Write the right padding.
for (size_t Index = 0; Index != RightPadding; ++Index)
{
for (size_t Jndex = 0; Jndex != FillUnitLength; ++Jndex)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = FillCharacter[Jndex];
}
}
return Iter;
}
private:
size_t FillUnitLength = 1;
FFillCharacter FillCharacter = { LITERAL(FCharType, ' ') };
FCharType AlignOption = LITERAL(FCharType, '<');
size_t MinFieldWidth = 0;
size_t MaxFieldWidth = -1;
bool bDynamicMin = false;
bool bDynamicMax = false;
bool bLowercase = false;
bool bUppercase = false;
bool bEscape = false;
};
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END

View File

@ -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 F, typename TTupleType>
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, TTupleType&& Arg, size_t)
template <typename>
struct TInvokeTuple;
template <size_t... Indices>
struct TInvokeTuple<TIndexSequence<Indices...>>
{
checkf(false, "Read access violation. Please check Index.");
return InvokeResult<Ret>(Forward<F>(Func), Forward<TTupleType>(Arg).template GetValue<0>());
FORCEINLINE static constexpr Ret Do(F&& Func, TTupleType&& Arg, size_t Index)
{
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);
}
};
@ -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 FHelper::Apply(Forward<F>(Func), static_cast< TTuple& >(*this)); }
template <typename F> requires (CInvocable<F, 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, 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, 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, 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, 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, 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)); }
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 FHelper::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 FHelper::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 FHelper::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 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, Ts> && !CSameAs<void, TInvokeResult<F, 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, Ts> && !CSameAs<void, TInvokeResult<F, 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, Ts> && !CSameAs<void, TInvokeResult<F, 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)); }
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 FHelper::template Construct<T>(static_cast< TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const & { return FHelper::template Construct<T>(static_cast<const TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() volatile& { return FHelper::template Construct<T>(static_cast< volatile TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, 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, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const && { return FHelper::template Construct<T>(static_cast<const TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() volatile&& { return FHelper::template Construct<T>(static_cast< volatile TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, 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)); }
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); }

View File

@ -433,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;
@ -502,10 +502,10 @@ struct TVariantVisitImpl
};
template <size_t EncodedIndex, typename>
struct FInvokeEncoded;
struct TInvokeEncoded;
template <size_t EncodedIndex, size_t... ExtentIndices>
struct FInvokeEncoded<EncodedIndex, TIndexSequence<ExtentIndices...>>
struct TInvokeEncoded<EncodedIndex, TIndexSequence<ExtentIndices...>>
{
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
{
@ -513,7 +513,7 @@ struct TVariantVisitImpl
}
template <typename Ret>
struct FResult
struct TResult
{
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
{
@ -523,26 +523,26 @@ struct TVariantVisitImpl
};
template <typename>
struct FInvokeVariant;
struct TInvokeVariant;
template <size_t... EncodedIndices>
struct FInvokeVariant<TIndexSequence<EncodedIndices...>>
struct TInvokeVariant<TIndexSequence<EncodedIndices...>>
{
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
{
using FExtentIndices = TIndexSequenceFor<VariantTypes...>;
using FResultType = TCommonType<decltype(FInvokeEncoded<EncodedIndices, FExtentIndices>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...))...>;
using FResultType = TCommonReference<decltype(TInvokeEncoded<EncodedIndices, FExtentIndices>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...))...>;
using FInvokeImplType = FResultType(*)(F&&, VariantTypes&&...);
constexpr FInvokeImplType InvokeImpl[] = { FInvokeEncoded<EncodedIndices, FExtentIndices>::template FResult<FResultType>::Do... };
constexpr FInvokeImplType InvokeImpl[] = { TInvokeEncoded<EncodedIndices, FExtentIndices>::template TResult<FResultType>::Do... };
return InvokeImpl[FEncodeIndices::Do({ Variants.GetIndex()... })](Forward<F>(Func), Forward<VariantTypes>(Variants)...);
}
template <typename Ret>
struct FResult
struct TResult
{
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
{
@ -550,7 +550,7 @@ struct TVariantVisitImpl
using FInvokeImplType = Ret(*)(F&&, VariantTypes&&...);
constexpr FInvokeImplType InvokeImpl[] = { FInvokeEncoded<EncodedIndices, FExtentIndices>::template FResult<Ret>::Do... };
constexpr FInvokeImplType InvokeImpl[] = { TInvokeEncoded<EncodedIndices, FExtentIndices>::template TResult<Ret>::Do... };
return InvokeImpl[FEncodeIndices::Do({ Variants.GetIndex()... })](Forward<F>(Func), Forward<VariantTypes>(Variants)...);
}
@ -559,15 +559,15 @@ struct TVariantVisitImpl
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
{
return FInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
return TInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
}
template <typename Ret>
struct FResult
struct TResult
{
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
{
return FInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::template FResult<Ret>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
return TInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::template TResult<Ret>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
}
};
};
@ -589,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 FResult<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)