feat: basically finish parser helper.
- basically finish parser helper, including TryParse, Parse and ToString. - only Parse has been tested. other test should be filled in future.
This commit is contained in:
parent
d437ecc140
commit
72eb79ce1a
|
@ -61,7 +61,7 @@ namespace YYCC::DialogHelper {
|
||||||
if (modes.empty()) return false;
|
if (modes.empty()) return false;
|
||||||
|
|
||||||
// add into pairs and return
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace YYCC::ExceptionHelper {
|
||||||
*/
|
*/
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup;
|
LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup;
|
||||||
|
|
||||||
#pragma region Exception Handler Detail
|
#pragma region Exception Handler Implementation
|
||||||
|
|
||||||
static HMODULE GetCurrentModule() {
|
static HMODULE GetCurrentModule() {
|
||||||
// Reference: https://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
|
// Reference: https://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
|
||||||
|
|
|
@ -6,19 +6,29 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "WinImportPrefix.hpp"
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "WinImportSuffix.hpp"
|
||||||
|
|
||||||
namespace YYCC::IOHelper {
|
namespace YYCC::IOHelper {
|
||||||
|
|
||||||
FILE* FOpen(const char* u8_filepath, const char* u8_mode) {
|
bool futf8(FILE* fs) {
|
||||||
std::wstring wmode, wpath;
|
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setmode?view=msvc-170
|
||||||
bool suc = YYCC::EncodingHelper::CharToWchar(u8_mode, wmode, CP_UTF8);
|
return _setmode(_fileno(fs), _O_U8TEXT) != -1;
|
||||||
suc = suc && YYCC::EncodingHelper::CharToWchar(u8_filepath, wpath, CP_UTF8);
|
}
|
||||||
|
|
||||||
if (suc) {
|
FILE* fopen(const char* u8_filepath, const char* u8_mode) {
|
||||||
return _wfopen(wpath.c_str(), wmode.c_str());
|
// convert mode and file path to wchar
|
||||||
} else {
|
std::wstring wmode, wpath;
|
||||||
// fallback
|
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_mode, wmode))
|
||||||
return std::fopen(u8_filepath, u8_mode);
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,17 @@
|
||||||
|
|
||||||
namespace YYCC::IOHelper {
|
namespace YYCC::IOHelper {
|
||||||
|
|
||||||
FILE* FOpen(const char* u8_filepath, const char* u8_mode);
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,84 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "YYCCInternal.hpp"
|
||||||
|
|
||||||
|
#include "EncodingHelper.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace YYCC::ParserHelper {
|
namespace YYCC::ParserHelper {
|
||||||
|
|
||||||
bool Parse(const std::string& strl, int& ret, int base = 10);
|
template<class>
|
||||||
int Parse(const std::string& strl, int base = 10);
|
constexpr bool g_AlwaysFalse = false;
|
||||||
|
|
||||||
|
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, 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<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !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<std::is_unsigned_v<_Ty>, 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<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty>, int> = 0>
|
||||||
|
_Ty Parse(const std::string& strl) {
|
||||||
|
_Ty ret;
|
||||||
|
TryParse(strl, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty>, int> = 0>
|
||||||
|
std::string ToString(_Ty num) {
|
||||||
|
return std::to_string(num);
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
std::string ToString<bool>(bool num) {
|
||||||
|
if (num) return std::string("true");
|
||||||
|
else return std::string("false");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "YYCCInternal.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace YYCC::TerminalHelper {
|
||||||
if (!SetConsoleOutputCP(CP_UTF8)) return false;
|
if (!SetConsoleOutputCP(CP_UTF8)) return false;
|
||||||
|
|
||||||
/*_setmode(_fileno(stdout), _O_U8TEXT);*/
|
/*_setmode(_fileno(stdout), _O_U8TEXT);*/
|
||||||
int _ = _setmode(_fileno(fs), _O_U16TEXT);
|
int _ = _setmode(_fileno(fs), _O_U8TEXT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,3 +6,4 @@
|
||||||
#include "EncodingHelper.hpp"
|
#include "EncodingHelper.hpp"
|
||||||
#include "TerminalHelper.hpp"
|
#include "TerminalHelper.hpp"
|
||||||
#include "DialogHelper.hpp"
|
#include "DialogHelper.hpp"
|
||||||
|
#include "ParserHelper.hpp"
|
||||||
|
|
|
@ -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<type_t>(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<bool>(cache));
|
||||||
|
// Assert(ret == "true", "YYCC::StringHelper::ToString<bool>");
|
||||||
|
//}
|
||||||
|
|
||||||
|
#undef TEST_MACRO
|
||||||
|
}
|
||||||
|
|
||||||
static void DialogTestbench() {
|
static void DialogTestbench() {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
std::vector<std::string> rets;
|
std::vector<std::string> rets;
|
||||||
|
@ -87,5 +113,6 @@ namespace Testbench {
|
||||||
int main(int argc, char** args) {
|
int main(int argc, char** args) {
|
||||||
Testbench::TerminalTestbench();
|
Testbench::TerminalTestbench();
|
||||||
Testbench::StringTestbench();
|
Testbench::StringTestbench();
|
||||||
Testbench::DialogTestbench();
|
Testbench::ParserTestbench();
|
||||||
|
//Testbench::DialogTestbench();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user