diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 869d99f..f727923 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,6 +65,7 @@ FILES yycc/patch/path.hpp yycc/patch/contains.hpp yycc/patch/starts_ends_with.hpp + yycc/patch/expected.hpp yycc/encoding/utf.hpp yycc/encoding/windows.hpp yycc/encoding/iconv.hpp diff --git a/src/yycc/encoding/utf.cpp b/src/yycc/encoding/utf.cpp index 06f7804..fc8d80b 100644 --- a/src/yycc/encoding/utf.cpp +++ b/src/yycc/encoding/utf.cpp @@ -1,8 +1,8 @@ #include "utf.hpp" -#include "../macro/feature_probe.hpp" #include #define NS_YYCC_STRING ::yycc::string +#define NS_YYCC_PATCH_EXPECTED ::yycc::patch::expected namespace yycc::encoding::utf { @@ -125,14 +125,13 @@ namespace yycc::encoding::utf { #pragma region Help Macros #define CONVFN_TYPE1(fct_name, src_char_type, dst_char_type) \ + namespace expected = NS_YYCC_PATCH_EXPECTED; \ auto rv = priv_##fct_name(src); \ - if (const auto* ptr = std::get_if>(&rv)) { \ - dst = std::move(*ptr); \ + if (expected::is_value(rv)) { \ + dst = std::move(expected::get_value(rv)); \ return true; \ - } else if (const auto* ptr = std::get_if(&rv)) { \ - return false; \ } else { \ - throw std::runtime_error("unreachable code"); \ + return false; \ } #define CONVFN_TYPE2(fct_name, src_char_type, dst_char_type) \ diff --git a/src/yycc/encoding/utf.hpp b/src/yycc/encoding/utf.hpp index 892919a..ea9b2a6 100644 --- a/src/yycc/encoding/utf.hpp +++ b/src/yycc/encoding/utf.hpp @@ -1,9 +1,9 @@ #pragma once -#include -#include -#include +#include "../string.hpp" +#include "../patch/expected.hpp" #define NS_YYCC_STRING ::yycc::string +#define NS_YYCC_PATCH_EXPECTED ::yycc::patch::expected namespace yycc::encoding::utf { @@ -11,8 +11,8 @@ namespace yycc::encoding::utf { struct ConvError {}; /// @private - template, int> = 0> - using ConvResult = std::variant; + template + using ConvResult = NS_YYCC_PATCH_EXPECTED::Expected; // UTF8 -> UTF16 @@ -40,4 +40,5 @@ namespace yycc::encoding::utf { } +#undef NS_YYCC_PATCH_EXPECTED #undef NS_YYCC_STRING diff --git a/src/yycc/encoding/windows.cpp b/src/yycc/encoding/windows.cpp index e69de29..350c313 100644 --- a/src/yycc/encoding/windows.cpp +++ b/src/yycc/encoding/windows.cpp @@ -0,0 +1,252 @@ +#include "windows.hpp" + +#if YYCC_OS == YYCC_OS_WINDOWS + +#include "../string/reinterpret.hpp" +#include +#include + +#include "../windows/import_guard_head.hpp" +#include "../windows/import_guard_tail.hpp" +#include + +#define NS_YYCC_STRING ::yycc::string +#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret +#define NS_YYCC_PATCH_EXPECTED ::yycc::patch::expected + +namespace yycc::encoding::windows { + +#pragma region Help Macros + +#define CONVFN_TYPE1(fct_name, src_char_type, dst_char_type, ...) \ + namespace expected = NS_YYCC_PATCH_EXPECTED; \ + auto rv = priv_##fct_name(src, ##__VA_ARGS__); \ + if (expected::is_value(rv)) { \ + dst = std::move(expected::get_value(rv)); \ + return true; \ + } else { \ + return false; \ + } + +#define CONVFN_TYPE2(fct_name, src_char_type, dst_char_type, ...) \ + std::basic_string rv; \ + if (fct_name(src, rv, ##__VA_ARGS__)) return rv; \ + else throw std::runtime_error("fail to convert string in Win32 function"); + +#pragma endregion + +#pragma region WChar -> Char + + ConvResult priv_to_char(const std::wstring_view& src, CodePage code_page) { + // prepare result + std::string dst; + + // if src is empty, direct output + if (src.empty()) { + return dst; + } + + // init WideCharToMultiByte used variables + // setup src pointer + LPCWCH lpWideCharStr = reinterpret_cast(src.data()); + // check whether source string is too large. + size_t cSrcSize = src.size(); + if (cSrcSize > std::numeric_limits::max()) return ConvError::TooLargeLength; + int cchWideChar = static_cast(src.size()); + + // do convertion + // do a dry-run first to fetch desired size. + int desired_size + = WideCharToMultiByte(code_page, 0, lpWideCharStr, cchWideChar, NULL, 0, NULL, NULL); + if (desired_size <= 0) return ConvError::NoDesiredSize; + // resize dest for receiving result + dst.resize(static_cast(desired_size)); + // do real convertion + int write_result = WideCharToMultiByte(code_page, + 0, + lpWideCharStr, + cchWideChar, + reinterpret_cast(dst.data()), + desired_size, + NULL, + NULL); + if (write_result <= 0) return ConvError::BadWrittenSize; + + return dst; + } + + bool to_char(const std::wstring_view& src, std::string& dst, CodePage code_page) { + CONVFN_TYPE1(to_char, wchar_t, char, code_page); + } + + std::string to_char(const std::wstring_view& src, CodePage code_page) { + CONVFN_TYPE2(to_char, wchar_t, char, code_page); + } + +#pragma endregion + +#pragma region Char -> WChar + + ConvResult priv_to_wchar(const std::string_view& src, CodePage code_page) { + // prepare result + std::wstring dst; + + // if src is empty, direct output + if (src.empty()) { + return dst; + } + + // init WideCharToMultiByte used variables + // setup src pointer + LPCCH lpMultiByteStr = reinterpret_cast(src.data()); + // check whether source string is too large. + size_t cSrcSize = src.size(); + if (cSrcSize > std::numeric_limits::max()) return ConvError::TooLargeLength; + int cbMultiByte = static_cast(src.size()); + + // do convertion + // do a dry-run first to fetch desired size. + int desired_size = MultiByteToWideChar(code_page, 0, lpMultiByteStr, cbMultiByte, NULL, 0); + if (desired_size <= 0) return ConvError::NoDesiredSize; + // resize dest for receiving result + dst.resize(static_cast(desired_size)); + // do real convertion + int write_result = MultiByteToWideChar(code_page, + 0, + lpMultiByteStr, + cbMultiByte, + reinterpret_cast(dst.data()), + desired_size); + if (write_result <= 0) return ConvError::BadWrittenSize; + + return dst; + } + + bool to_wchar(const std::string_view& src, std::wstring& dst, CodePage code_page) { + CONVFN_TYPE1(to_wchar, char, wchar_t, code_page); + } + + std::wstring to_wchar(const std::string_view& src, CodePage code_page) { + CONVFN_TYPE2(to_wchar, char, wchar_t, code_page); + } + +#pragma endregion + +#pragma region Char -> Char + + ConvResult priv_to_char(const std::string_view& src, + CodePage src_code_page, + CodePage dst_code_page) { + namespace expected = NS_YYCC_PATCH_EXPECTED; + + // Perform first convertion + auto first_rv = priv_to_wchar(src, src_code_page); + if (expected::is_error(first_rv)) { + return expected::get_error(first_rv); + } + // Perform second convertion + auto second_rv = to_char(std::get(first_rv), dst_code_page); + return second_rv; + } + + bool to_char(const std::string_view& src, + std::string& dst, + CodePage src_code_page, + CodePage dst_code_page) { + CONVFN_TYPE1(to_char, char, char, src_code_page, dst_code_page); + } + + std::string to_char(const std::string_view& src, + CodePage src_code_page, + CodePage dst_code_page) { + CONVFN_TYPE2(to_char, char, char, src_code_page, dst_code_page); + } + +#pragma endregion + +#pragma region WChar -> UTF8 + + ConvResult priv_to_utf8(const std::wstring_view& src) { + namespace expected = NS_YYCC_PATCH_EXPECTED; + + auto rv = priv_to_char(src, CP_UTF8); + if (expected::is_value(rv)) { + return NS_YYCC_STRING_REINTERPRET::as_utf8(expected::get_value(rv)); + } else { + return expected::get_error(rv); + } + } + + bool to_utf8(const std::wstring_view& src, NS_YYCC_STRING::u8string& dst) { + CONVFN_TYPE1(to_utf8, wchar_t, NS_YYCC_STRING::u8char); + } + + NS_YYCC_STRING::u8string to_utf8(const std::wstring_view& src) { + CONVFN_TYPE2(to_utf8, wchar_t, NS_YYCC_STRING::u8char); + } + +#pragma endregion + +#pragma region UTF8 -> WChar + + ConvResult priv_to_wchar(const NS_YYCC_STRING::u8string_view& src) { + return priv_to_wchar(NS_YYCC_STRING_REINTERPRET::as_ordinary_view(src), CP_UTF8); + } + + bool to_wchar(const NS_YYCC_STRING::u8string_view& src, std::wstring& dst) { + CONVFN_TYPE1(to_wchar, NS_YYCC_STRING::u8char, wchar_t); + } + + std::wstring to_wchar(const NS_YYCC_STRING::u8string_view& src) { + CONVFN_TYPE2(to_wchar, NS_YYCC_STRING::u8char, wchar_t); + } + +#pragma endregion + +#pragma region Char -> UTF8 + + ConvResult priv_to_utf8(const std::string_view& src, + CodePage code_page) { + namespace expected = NS_YYCC_PATCH_EXPECTED; + + auto rv = priv_to_char(src, code_page, CP_UTF8); + if (expected::is_value(rv)) { + return NS_YYCC_STRING_REINTERPRET::as_utf8(expected::get_value(rv)); + } else { + return expected::get_error(rv); + } + } + + bool to_utf8(const std::string_view& src, NS_YYCC_STRING::u8string& dst, CodePage code_page) { + CONVFN_TYPE1(to_utf8, char, NS_YYCC_STRING::u8char, code_page); + } + + NS_YYCC_STRING::u8string to_utf8(const std::string_view& src, CodePage code_page) { + CONVFN_TYPE2(to_utf8, char, NS_YYCC_STRING::u8char, code_page); + } + +#pragma endregion + +#pragma region UTF8 -> Char + ConvResult priv_to_char(const NS_YYCC_STRING::u8string_view& src, + CodePage code_page) { + return priv_to_char(NS_YYCC_STRING_REINTERPRET::as_ordinary_view(src), CP_UTF8, code_page); + } + + bool to_char(const NS_YYCC_STRING::u8string_view& src, std::string& dst, CodePage code_page) { + CONVFN_TYPE1(to_char, NS_YYCC_STRING::u8char, char, code_page); + } + + std::string to_char(const NS_YYCC_STRING::u8string_view& src, CodePage code_page) { + CONVFN_TYPE2(to_char, NS_YYCC_STRING::u8char, char, code_page); + } + +#pragma endregion + +#undef CONVFN_TYPE1 +#undef CONVFN_TYPE2 +#undef CONVFCT_TYPE4 + +} // namespace yycc::encoding::windows + +#endif diff --git a/src/yycc/encoding/windows.hpp b/src/yycc/encoding/windows.hpp index e69de29..5b744ba 100644 --- a/src/yycc/encoding/windows.hpp +++ b/src/yycc/encoding/windows.hpp @@ -0,0 +1,80 @@ +#pragma once +#include "../macro/os_detector.hpp" +#include "../string.hpp" +#include "../patch/expected.hpp" +#include + +#define NS_YYCC_STRING ::yycc::string +#define NS_YYCC_PATCH_EXPECTED ::yycc::patch::expected + +namespace yycc::encoding::windows { + +#if YYCC_OS == YYCC_OS_WINDOWS + + using CodePage = uint32_t; + + /// @private + enum class ConvError { + TooLargeLength, ///< The length of given string is too large exceeding the maximum capacity of Win32 function. + NoDesiredSize, ///< Can not compute the desired size of result string. + BadWrittenSize, ///< The size of written data is not matched with expected size. + }; + + /// @private + template + using ConvResult = NS_YYCC_PATCH_EXPECTED::Expected; + + // WChar -> Char + ConvResult priv_to_char(const std::wstring_view& src, CodePage code_page); + bool to_char(const std::wstring_view& src, std::string& dst, CodePage code_page); + std::string to_char(const std::wstring_view& src, CodePage code_page); + + // Char -> WChar + ConvResult priv_to_wchar(const std::string_view& src, CodePage code_page); + bool to_wchar(const std::string_view& src, std::wstring& dst, CodePage code_page); + std::wstring to_wchar(const std::string_view& src, CodePage code_page); + + // YYC MARK: + // Following functions are basically the alias of above functions. + + // Char -> Char + ConvResult priv_to_char(const std::string_view& src, + CodePage src_code_page, + CodePage dst_code_page); + bool to_char(const std::string_view& src, + std::string& dst, + CodePage src_code_page, + CodePage dst_code_page); + std::string to_char(const std::string_view& src, CodePage src_code_page, CodePage dst_code_page); + + // YYC MARK: + // Following functions are basically the specialized UTF8 functions. + + // WChar -> UTF8 + ConvResult priv_to_utf8(const std::wstring_view& src); + bool to_utf8(const std::wstring_view& src, NS_YYCC_STRING::u8string& dst); + NS_YYCC_STRING::u8string to_utf8(const std::wstring_view& src); + + // UTF8 -> WChar + ConvResult priv_to_wchar(const NS_YYCC_STRING::u8string_view& src); + bool to_wchar(const NS_YYCC_STRING::u8string_view& src, std::wstring& dst); + std::wstring to_wchar(const NS_YYCC_STRING::u8string_view& src); + + // Char -> UTF8 + ConvResult priv_to_utf8(const std::string_view& src, + CodePage code_page); + bool to_utf8(const std::string_view& src, NS_YYCC_STRING::u8string& dst, CodePage code_page); + NS_YYCC_STRING::u8string to_utf8(const std::string_view& src, CodePage code_page); + + // UTF8 -> Char + ConvResult priv_to_char(const NS_YYCC_STRING::u8string_view& src, + CodePage code_page); + bool to_char(const NS_YYCC_STRING::u8string_view& src, std::string& dst, CodePage code_page); + std::string to_char(const NS_YYCC_STRING::u8string_view& src, CodePage code_page); + +#endif + +} // namespace yycc::encoding::windows + +#undef NS_YYCC_PATCH_EXPECTED +#undef NS_YYCC_STRING diff --git a/src/yycc/num/parse.hpp b/src/yycc/num/parse.hpp index 5c0fb91..07a0f43 100644 --- a/src/yycc/num/parse.hpp +++ b/src/yycc/num/parse.hpp @@ -1,15 +1,15 @@ #pragma once +#include "../patch/expected.hpp" #include "../string.hpp" #include "../string/op.hpp" #include "../string/reinterpret.hpp" #include #include -#include -#include #define NS_YYCC_STRING ::yycc::string #define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret #define NS_YYCC_STRING_OP ::yycc::string::op +#define NS_YYCC_PATCH_EXPECTED ::yycc::patch::expected /** * @brief Provides string parsing utilities for converting strings to numeric and boolean values. @@ -30,8 +30,8 @@ namespace yycc::num::parse { /// @private /// @brief The return value of internal parse function which ape `std::expected`. - template, int> = 0> - using ParseResult = std::variant; + template + using ParseResult = NS_YYCC_PATCH_EXPECTED::Expected; /** * @private @@ -132,15 +132,14 @@ namespace yycc::num::parse { bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num, std::chars_format fmt = std::chars_format::general) { + namespace expected = NS_YYCC_PATCH_EXPECTED; + auto rv = priv_parse(strl, fmt); - if (const auto* ptr = std::get_if(&rv)) { - num = *ptr; + if (expected::is_value(rv)) { + num = expected::get_value(rv); return true; - } else if (const auto* ptr = std::get_if(&rv)) { - return false; } else { - // Unreachable - throw std::runtime_error("unreachable code."); + return false; } } /** @@ -155,15 +154,14 @@ namespace yycc::num::parse { */ template && !std::is_same_v, int> = 0> bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num, int base = 10) { + namespace expected = NS_YYCC_PATCH_EXPECTED; + auto rv = priv_parse(strl, base); - if (const auto* ptr = std::get_if(&rv)) { - num = *ptr; + if (expected::is_value(rv)) { + num = expected::get_value(rv); return true; - } else if (const auto* ptr = std::get_if(&rv)) { - return false; } else { - // Unreachable - throw std::runtime_error("unreachable code."); + return false; } } /** @@ -177,15 +175,14 @@ namespace yycc::num::parse { */ template, int> = 0> bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num) { + namespace expected = NS_YYCC_PATCH_EXPECTED; + auto rv = priv_parse(strl); - if (const auto* ptr = std::get_if(&rv)) { - num = *ptr; + if (expected::is_value(rv)) { + num = expected::get_value(rv); return true; - } else if (const auto* ptr = std::get_if(&rv)) { - return false; } else { - // Unreachable - throw std::runtime_error("unreachable code."); + return false; } } @@ -241,8 +238,9 @@ namespace yycc::num::parse { else throw std::invalid_argument("can not parse given string"); } -} // namespace yycc::string::parse +} // namespace yycc::num::parse +#undef NS_YYCC_PATCH_EXPECTED #undef NS_YYCC_STRING_OP #undef NS_YYCC_STRING_REINTERPRET #undef NS_YYCC_STRING diff --git a/src/yycc/patch/expected.hpp b/src/yycc/patch/expected.hpp new file mode 100644 index 0000000..2ba8126 --- /dev/null +++ b/src/yycc/patch/expected.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include + +/** + * @brief A stupid polyfill for std::expected, + * + * For those C++ standard which do not support std::expected, + * we provide this namespace with a pretty bad but at least working std::expected pplyfill. + * + * The polyfill was done by std::variant. + * + * This namespace is used by this project because this framework must support C++ 17. + */ +namespace yycc::patch::expected { + + template, int> = 0> + using Expected = std::variant; + + template + bool is_value(const Expected& exp) { + return exp.index() == 0u; + } + + template + bool is_error(const Expected& exp) { + return exp.index() == 1u; + } + + template + const TValue& get_value(const Expected& exp) { + return std::get<0>(exp); + } + + template + const TError& get_error(const Expected& exp) { + return std::get<1>(exp); + } + +} diff --git a/src/yycc/rust/num/parse.hpp b/src/yycc/rust/num/parse.hpp index 6f2b65d..48dc718 100644 --- a/src/yycc/rust/num/parse.hpp +++ b/src/yycc/rust/num/parse.hpp @@ -1,12 +1,12 @@ #pragma once #include "../../macro/feature_probe.hpp" #include "../../num/parse.hpp" -#include "../panic.hpp" #include "../result.hpp" #define NS_YYCC_STRING ::yycc::string #define NS_YYCC_NUM_PARSE ::yycc::num::parse #define NS_YYCC_RUST_RESULT ::yycc::rust::result +#define NS_YYCC_PATCH_EXPECTED ::yycc::patch::expected /** * @namespace yycc::rust::parse @@ -37,15 +37,14 @@ namespace yycc::rust::num::parse { template, int> = 0> Result parse(const NS_YYCC_STRING::u8string_view& strl, std::chars_format fmt = std::chars_format::general) { - auto rv = NS_YYCC_NUM_PARSE::priv_parse(strl, fmt); + namespace expected = NS_YYCC_PATCH_EXPECTED; + namespace result = NS_YYCC_RUST_RESULT; - if (const auto* ptr = std::get_if(&rv)) { - return NS_YYCC_RUST_RESULT::Ok>(*ptr); - } else if (const auto* ptr = std::get_if(&rv)) { - return NS_YYCC_RUST_RESULT::Err>(*ptr); + auto rv = NS_YYCC_NUM_PARSE::priv_parse(strl, fmt); + if (expected::is_value(rv)) { + return result::Ok>(expected::get_value(rv)); } else { - // Unreachable - RS_PANIC("unreachable code."); + return result::Err>(expected::get_error(rv)); } } @@ -58,15 +57,14 @@ namespace yycc::rust::num::parse { */ template && !std::is_same_v, int> = 0> Result parse(const NS_YYCC_STRING::u8string_view& strl, int base = 10) { - auto rv = NS_YYCC_NUM_PARSE::priv_parse(strl, base); + namespace expected = NS_YYCC_PATCH_EXPECTED; + namespace result = NS_YYCC_RUST_RESULT; - if (const auto* ptr = std::get_if(&rv)) { - return NS_YYCC_RUST_RESULT::Ok>(*ptr); - } else if (const auto* ptr = std::get_if(&rv)) { - return NS_YYCC_RUST_RESULT::Err>(*ptr); + auto rv = NS_YYCC_NUM_PARSE::priv_parse(strl, base); + if (expected::is_value(rv)) { + return result::Ok>(expected::get_value(rv)); } else { - // Unreachable - RS_PANIC("unreachable code."); + return result::Err>(expected::get_error(rv)); } } @@ -78,15 +76,14 @@ namespace yycc::rust::num::parse { */ template, int> = 0> Result parse(const NS_YYCC_STRING::u8string_view& strl) { - auto rv = NS_YYCC_NUM_PARSE::priv_parse(strl); + namespace expected = NS_YYCC_PATCH_EXPECTED; + namespace result = NS_YYCC_RUST_RESULT; - if (const auto* ptr = std::get_if(&rv)) { - return NS_YYCC_RUST_RESULT::Ok>(*ptr); - } else if (const auto* ptr = std::get_if(&rv)) { - return NS_YYCC_RUST_RESULT::Err>(*ptr); + auto rv = NS_YYCC_NUM_PARSE::priv_parse(strl); + if (expected::is_value(rv)) { + return result::Ok>(expected::get_value(rv)); } else { - // Unreachable - RS_PANIC("unreachable code."); + return result::Err>(expected::get_error(rv)); } } @@ -94,6 +91,7 @@ namespace yycc::rust::num::parse { } +#undef NS_YYCC_PATCH_EXPECTED #undef NS_YYCC_RUST_RESULT #undef NS_YYCC_NUM_PARSE #undef NS_YYCC_STRING