diff --git a/src/DialogHelper.cpp b/src/DialogHelper.cpp index 79f3969..7c37bc0 100644 --- a/src/DialogHelper.cpp +++ b/src/DialogHelper.cpp @@ -61,7 +61,7 @@ namespace YYCC::DialogHelper { if (modes.empty()) return false; // add into pairs and return - m_Filters.emplace_back(std::make_pair(name, modes)); + m_Filters.emplace_back(std::make_pair(std::move(name), std::move(modes))); return true; } diff --git a/src/ExceptionHelper.cpp b/src/ExceptionHelper.cpp index b5f279f..e92ff7f 100644 --- a/src/ExceptionHelper.cpp +++ b/src/ExceptionHelper.cpp @@ -28,7 +28,7 @@ namespace YYCC::ExceptionHelper { */ LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup; -#pragma region Exception Handler Detail +#pragma region Exception Handler Implementation static HMODULE GetCurrentModule() { // Reference: https://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code diff --git a/src/IOHelper.cpp b/src/IOHelper.cpp index 0fae29d..d6fd32d 100644 --- a/src/IOHelper.cpp +++ b/src/IOHelper.cpp @@ -6,19 +6,29 @@ #include #include +#include "WinImportPrefix.hpp" +#include +#include +#include +#include "WinImportSuffix.hpp" + namespace YYCC::IOHelper { - FILE* FOpen(const char* u8_filepath, const char* u8_mode) { - std::wstring wmode, wpath; - bool suc = YYCC::EncodingHelper::CharToWchar(u8_mode, wmode, CP_UTF8); - suc = suc && YYCC::EncodingHelper::CharToWchar(u8_filepath, wpath, CP_UTF8); + bool futf8(FILE* fs) { + // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setmode?view=msvc-170 + return _setmode(_fileno(fs), _O_U8TEXT) != -1; + } - if (suc) { - return _wfopen(wpath.c_str(), wmode.c_str()); - } else { - // fallback - return std::fopen(u8_filepath, u8_mode); - } + FILE* fopen(const char* u8_filepath, const char* u8_mode) { + // convert mode and file path to wchar + std::wstring wmode, wpath; + if (!YYCC::EncodingHelper::UTF8ToWchar(u8_mode, wmode)) + return nullptr; + if (!YYCC::EncodingHelper::UTF8ToWchar(u8_filepath, wpath)) + return nullptr; + + // call microsoft specified fopen which support wchar as argument. + return _wfopen(wpath.c_str(), wmode.c_str()); } } diff --git a/src/IOHelper.hpp b/src/IOHelper.hpp index d1803f5..888caca 100644 --- a/src/IOHelper.hpp +++ b/src/IOHelper.hpp @@ -5,8 +5,18 @@ #include namespace YYCC::IOHelper { + + /** + * @brief Set given FILE* as UTF8 mode. + * @param fs[in] The FILE* need to be set as UTF8 mode. + * @return True if success, otherwise false. + * @warning Once this function success, you can not use any narrow char function on this FILE*, + * such as std::fputs, std::fprintf and etc. You only can use wide char function on it, + * or use the functions provided in this namespace by providing UTF8 string as their argument. + */ + bool futf8(FILE* fs); - FILE* FOpen(const char* u8_filepath, const char* u8_mode); + FILE* fopen(const char* u8_filepath, const char* u8_mode); } diff --git a/src/ParserHelper.hpp b/src/ParserHelper.hpp index 74f4349..e9aff72 100644 --- a/src/ParserHelper.hpp +++ b/src/ParserHelper.hpp @@ -1,10 +1,84 @@ #pragma once +#include "YYCCInternal.hpp" + +#include "EncodingHelper.hpp" #include #include +#include +#include +#include namespace YYCC::ParserHelper { - bool Parse(const std::string& strl, int& ret, int base = 10); - int Parse(const std::string& strl, int base = 10); + template + constexpr bool g_AlwaysFalse = false; + + template, int> = 0> + bool TryParse(const std::string& strl, _Ty& num) { + try { + // float types + if constexpr (std::is_same_v<_Ty, float>) { + num = std::stof(strl, nullptr); + } else if constexpr (std::is_same_v<_Ty, double>) { + num = std::stod(strl, nullptr); + } else if constexpr (std::is_same_v<_Ty, long double>) { + num = std::stold(strl, nullptr); + } else { + static_assert(g_AlwaysFalse<_Ty>, "Invalid float type."); + } + return true; + } catch (const std::invalid_argument&) { + return false; + } catch (const std::out_of_range&) { + return false; + } + } + template && !std::is_same_v<_Ty, bool>, int> = 0> + bool TryParse(const std::string& strl, _Ty& num, int base = 10) { + try { + // integer type + // decide integer type + using container_t = std::conditional_t, unsigned long long, long long>; + // parse it from string according to whether integer type is signed. + container_t cache; + if constexpr (std::is_unsigned_v<_Ty>) { + cache = std::stoull(strl, nullptr, base); + } else { + cache = std::stoll(strl, nullptr, base); + } + // check its range + if (cache < std::numeric_limits<_Ty>::min() || cache > std::numeric_limits<_Ty>::max()) + return false; + num = static_cast<_Ty>(cache); + return true; + } catch (const std::invalid_argument&) { + return false; + } catch (const std::out_of_range&) { + return false; + } + } + bool TryParse(const std::string& strl, bool& num) { + if (strl == "true") num = true; + else if (strl == "false") num = false; + else return false; + return true; + } + + template, int> = 0> + _Ty Parse(const std::string& strl) { + _Ty ret; + TryParse(strl, ret); + return ret; + } + + template, int> = 0> + std::string ToString(_Ty num) { + return std::to_string(num); + } + template<> + std::string ToString(bool num) { + if (num) return std::string("true"); + else return std::string("false"); + } } diff --git a/src/StringHelper.hpp b/src/StringHelper.hpp index 32726c8..caebb91 100644 --- a/src/StringHelper.hpp +++ b/src/StringHelper.hpp @@ -1,4 +1,6 @@ #pragma once +#include "YYCCInternal.hpp" + #include #include #include diff --git a/src/TerminalHelper.cpp b/src/TerminalHelper.cpp index c4d4536..3b3a757 100644 --- a/src/TerminalHelper.cpp +++ b/src/TerminalHelper.cpp @@ -31,7 +31,7 @@ namespace YYCC::TerminalHelper { if (!SetConsoleOutputCP(CP_UTF8)) return false; /*_setmode(_fileno(stdout), _O_U8TEXT);*/ - int _ = _setmode(_fileno(fs), _O_U16TEXT); + int _ = _setmode(_fileno(fs), _O_U8TEXT); return true; } diff --git a/src/YYCCommonplace.hpp b/src/YYCCommonplace.hpp index 4e2b8f7..21180fa 100644 --- a/src/YYCCommonplace.hpp +++ b/src/YYCCommonplace.hpp @@ -6,3 +6,4 @@ #include "EncodingHelper.hpp" #include "TerminalHelper.hpp" #include "DialogHelper.hpp" +#include "ParserHelper.hpp" diff --git a/testbench/main.cpp b/testbench/main.cpp index 7cebd92..e0d7e8b 100644 --- a/testbench/main.cpp +++ b/testbench/main.cpp @@ -52,6 +52,32 @@ namespace Testbench { } + static void ParserTestbench() { + +#define TEST_MACRO(type_t, value, string_value) { \ + type_t cache = value; \ + std::string ret(YYCC::ParserHelper::ToString(cache)); \ + Assert(ret == string_value, "YYCC::StringHelper::ToString<" #type_t ">"); \ +} + + TEST_MACRO(int8_t, INT8_C(-61), "-61"); + TEST_MACRO(uint8_t, UINT8_C(200), "200"); + TEST_MACRO(int16_t, INT16_C(6161), "6161"); + TEST_MACRO(uint16_t, UINT16_C(32800), "32800"); + TEST_MACRO(int32_t, INT32_C(61616161), "61616161"); + TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293"); + TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161"); + TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807"); + TEST_MACRO(bool, true, "true"); + //{ + // bool cache = true; + // std::string ret(YYCC::ParserHelper::ToString(cache)); + // Assert(ret == "true", "YYCC::StringHelper::ToString"); + //} + +#undef TEST_MACRO + } + static void DialogTestbench() { std::string ret; std::vector rets; @@ -87,5 +113,6 @@ namespace Testbench { int main(int argc, char** args) { Testbench::TerminalTestbench(); Testbench::StringTestbench(); - Testbench::DialogTestbench(); + Testbench::ParserTestbench(); + //Testbench::DialogTestbench(); }