Compare commits
	
		
			7 Commits
		
	
	
		
			06e75924f1
			...
			4f1e2447d0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4f1e2447d0 | |||
| 3075ec583d | |||
| 2e28dd4c48 | |||
| a1699f13db | |||
| 65b81f5cfa | |||
| 1c5a85bbb2 | |||
| 1f04e23092 | 
| @ -1,6 +1,6 @@ | ||||
| cmake_minimum_required(VERSION 3.23) | ||||
| project(YYCC | ||||
|     VERSION 1.0.0 | ||||
|     VERSION 1.1.0 | ||||
|     LANGUAGES CXX | ||||
| ) | ||||
|  | ||||
|  | ||||
| @ -4,8 +4,141 @@ | ||||
|  | ||||
| YYCC::EncodingHelper namespace include all encoding related functions: | ||||
|  | ||||
| \li The convertion between native string and UTF8 string which has been introduced in chapter \ref library_encoding. | ||||
| \li The convertion between ordinary string and UTF8 string which has been introduced in chapter \ref library_encoding. | ||||
| \li Windows specific convertion between \c WCHAR, UTF8 string and string encoded by other encoding. | ||||
| \li The convertion among UTF8, UTF16 and UTF32. | ||||
|  | ||||
| \section encoding_helper__ordinary_utf8_conv Ordinary & UTF8 Convertion | ||||
|  | ||||
| These convertion functions have been introduced in previous page. | ||||
| See \ref library_encoding for more infomation. | ||||
|  | ||||
| YYCC supports following convertions: | ||||
|  | ||||
| \li YYCC::EncodingHelper::ToUTF8: Convert ordinary string to UTF8 string. | ||||
| \li YYCC::EncodingHelper::ToUTF8View: Same as ToUTF8, but return string view instead. | ||||
| \li YYCC::EncodingHelper::ToOrdinary: Convert UTF8 string to ordinary string. | ||||
| \li YYCC::EncodingHelper::ToOrdinaryView: Same as ToOrdinary, but return string view instead. | ||||
|  | ||||
| \section encoding_helper__win_conv Windows Specific Convertion | ||||
|  | ||||
| During Windows programming, the convertion between Microsoft specified \c wchar_t and \c char is an essential operation. | ||||
| Because Windows has 2 different function system, the functions ended with A and the functions ended with W. | ||||
| (Microsoft specified \c wchar_t is \c 2 bytes long. It's different with Linux defined common 4 bytes long). | ||||
| Thus YYCC provides these convertion functions in Windows to help programmer have better programming experience. | ||||
|  | ||||
| These functions are Windows specific, so they will be invisible in other platforms. | ||||
| Please use them carefully (make sure that you are using them only in Windows environment). | ||||
|  | ||||
| YYCC supports following convertions: | ||||
|  | ||||
| \li YYCC::EncodingHelper::WcharToChar: Convert \c wchar_t string to code page specified string. | ||||
| \li YYCC::EncodingHelper::CharToWchar: The reversed convertion of WcharToChar. | ||||
| \li YYCC::EncodingHelper::CharToChar: Convert string between 2 different code pages. It's a shortcut of calling CharToWchar and WcharToChar successively. | ||||
| \li YYCC::EncodingHelper::WcharToUTF8: Convert \c wchar_t string to UTF8 string. | ||||
| \li YYCC::EncodingHelper::UTF8ToWchar: The reversed convertion of WcharToUTF8. | ||||
|  | ||||
| Code Page is a Windows concept. | ||||
| If you don't understand it, please view corresponding Microsoft documentation. | ||||
|  | ||||
| \section encoding_helper__utf_conv UTF8 UTF16 UTF32 Convertion | ||||
|  | ||||
| The convertion between UTF8, UTF16 and UTF32 is not common but essential. | ||||
| These convertions can be achieved by standard library functions and classes. | ||||
| (they are actually done by standard library functions in our implementation) | ||||
| But we provided functions are easy to use and have clear interface. | ||||
|  | ||||
| These functions are different with the functions introduced above. | ||||
| They can be used in any platform, not confined in Windows platforms. | ||||
|  | ||||
| YYCC supports following convertions: | ||||
|  | ||||
| \li YYCC::EncodingHelper::UTF8ToUTF16: Convert UTF8 string to UTF16 string. | ||||
| \li YYCC::EncodingHelper::UTF16ToUTF8: The reversed convertion of UTF8ToUTF16. | ||||
| \li YYCC::EncodingHelper::UTF8ToUTF32: Convert UTF8 string to UTF32 string. | ||||
| \li YYCC::EncodingHelper::UTF32ToUTF8: The reversed convertion of UTF8ToUTF32. | ||||
|  | ||||
| \section encoding_helper__overloads Function Overloads | ||||
|  | ||||
| Every encoding convertion functions (except the convertion between UTF8 and ordinary string) have 4 different overloads for different scenarios. | ||||
| Take YYCC::EncodingHelper::WcharToChar for example. | ||||
| There are following 4 overloads: | ||||
|  | ||||
| \code | ||||
| bool WcharToChar(const std::wstring_view& src, std::string& dst, UINT code_page); | ||||
| bool WcharToChar(const wchar_t* src, std::string& dst, UINT code_page); | ||||
| std::string WcharToChar(const std::wstring_view& src, UINT code_page); | ||||
| std::string WcharToChar(const wchar_t* src, UINT code_page); | ||||
| \endcode | ||||
|  | ||||
| \subsection encoding_helper__overloads_destination Destination String | ||||
|  | ||||
| According to the return value, these 4 overload can be divided into 2 types. | ||||
| The first type returns bool. The second type returns \c std::string instance. | ||||
|  | ||||
| For the first type, it always return bool to indicate whether the convertion is success. | ||||
| Due to this, the function must require an argument for holding the result string. | ||||
| So you can see the functions belonging to this type always require a reference to \c std::string in argument. | ||||
|  | ||||
| Oppositely, the second directly returns result by return value. | ||||
| It doesn't care the success of convertion and will return empty string if convertion failed. | ||||
| Programmer can more naturally use it because the retuen value itself is the result. | ||||
| There is no need to declare a variable before calling convertion function for holding result. | ||||
|  | ||||
| All in all, the first type overload should be used in strict scope. | ||||
| The success of convertion will massively affect the behavior of your following code. | ||||
| For example, the convertion code is delivered to some system function and it should not be empty and etc. | ||||
| The second type overload usually is used in lossen scenarios. | ||||
| For exmaple, this overload usually is used in console output because it usually doesn't matter. | ||||
| There is no risk even if the convertion failed (just output a blank string). | ||||
|  | ||||
| For the first type, please note that there is \b NO guarantee that the argument holding return value is not changed. | ||||
| Even the convertion is failed, the argument holding return value may still be changed by function itself. | ||||
|  | ||||
| In this case, the type of result is \c std::string because this is function required. | ||||
| In other functions, such as YYCC::EncodingHelper::WcharToUTF8, the type of result can be \c yycc_u8string or etc. | ||||
| So please note the type of result is decided by convertion function itself, not only \c std::string. | ||||
|  | ||||
| \subsection encoding_helper__overloads__source Source String | ||||
|  | ||||
| According to the way providing source string, | ||||
| these 4 overload also can be divided into 2 types. | ||||
| The first type take a reference to constant \c std::wstring_view. | ||||
| The second type take a pointer to constant wchar_t. | ||||
|  | ||||
| For first type, it will take the whole string for convertion, including \b embedded NUL terminal. | ||||
| Please note we use string view as argument. | ||||
| It is compatible with corresponding raw string pointer and string container. | ||||
| So it is safe to directly pass \c std::wstring for this function. | ||||
|  | ||||
| For second type, it will assume that you passed argument is a NUL terminated string and send it for convertion. | ||||
|  | ||||
| The result is clear. | ||||
| If you want to process string with \b embedded NUL terminal, please choose first type overload. | ||||
| Otherwise the second type overload is enough. | ||||
|  | ||||
| Same as destination string, the type of source is also decided by the convertion function itself. | ||||
| For exmaple, the type of source in YYCC::EncodingHelper::UTF8ToWchar is \c yycc_u8string_view and \c yycc_char8_t,  | ||||
| not \c std::wstring and \c wchar_t. | ||||
|  | ||||
| \subsection encoding_helper__overloads__extra Extra Argument | ||||
|  | ||||
| There is an extra argument called \c code_page for YYCC::EncodingHelper::WcharToChar. | ||||
| It indicates the code page of destination string, | ||||
| because this function will convert \c wchar_t string to the string with specified code page encoding. | ||||
|  | ||||
| Some convertion functions have extra argument like this, | ||||
| because they need more infomations to decide what they need to do. | ||||
| Some convertion functions don't have extra argument. | ||||
| For exmaple, the convertion between \c wchar_t string and UTF8 string. | ||||
| Because both source string and destination string are concrete. | ||||
| There is no need to provide any more infomations. | ||||
|  | ||||
| \subsection encoding_helper__overloads__conclusion Conclusion | ||||
|  | ||||
| Mixing 2 types of source string and 2 types of destination string, | ||||
| we have 4 different overload as we illustrated before. | ||||
| Programmer can use them freely according to your requirements. | ||||
| And don't forget to provide extra argument if function required. | ||||
|  | ||||
| */ | ||||
| @ -29,6 +29,8 @@ | ||||
|  | ||||
|     \li \subpage intro | ||||
|  | ||||
|     \li \subpage platform_checker | ||||
|  | ||||
|     \li \subpage library_encoding | ||||
|  | ||||
|     \li \subpage encoding_helper | ||||
|  | ||||
| @ -59,6 +59,23 @@ I notice standard library change UTF8 related functions frequently and its API a | ||||
| For example, standard library brings \c std::codecvt_utf8 in C++ 11, deprecate it in C++ 17 and even remove it in C++ 26. | ||||
| That's unacceptable! So I create my own UTF8 type to avoid the scenario that standard library remove \c char8_t in future. | ||||
|  | ||||
| \section library_encoding__concept Concepts | ||||
|  | ||||
| In following content, you may be face with 2 words: ordinary string and UTF8 string. | ||||
|  | ||||
| UTF8 string, as its name, is the string encoded with UTF8. | ||||
| The char type of it must is \c yycc_char8_t. | ||||
| (equivalent to \c char8_t after C++ 20.) | ||||
|  | ||||
| Ordinary string means the plain, native string. | ||||
| The result of C++ string literal without any prefix \c "foo bar" is a rdinary string. | ||||
| The char type of it is \c char. | ||||
| Its encoding depends on compiler and environment. | ||||
| (UTF8 in Linux, or system code page in Windows if UTF8 switch was not enabled in MSVC.) | ||||
|  | ||||
| For more infomation, please browse CppReference: | ||||
| https://en.cppreference.com/w/cpp/language/string_literal | ||||
|  | ||||
| \section library_encoding__utf8_literal UTF8 Literal | ||||
|  | ||||
| String literal is a C++ concept. | ||||
| @ -123,35 +140,35 @@ char* mutable_utf8 = const_cast<char*>(absolutely_is_utf8); // This is not safe. | ||||
| yycc_char8_t* mutable_converted = YYCC::EncodingHelper::ToUTF8(mutable_utf8); | ||||
| \endcode | ||||
|  | ||||
| YYCC::EncodingHelper::ToUTF8 has 2 overloads which can handle const and mutable stirng pointer convertion respectively. | ||||
| YYCC::EncodingHelper::ToUTF8 has 2 overloads which can handle constant and mutable stirng pointer convertion respectively. | ||||
|  | ||||
| YYCC also has ability that convert YYCC UTF8 char type to native char type by YYCC::EncodingHelper::ToNative. | ||||
| YYCC also has ability that convert YYCC UTF8 char type to ordinary char type by YYCC::EncodingHelper::ToOrdinary. | ||||
| Here is an exmaple: | ||||
|  | ||||
| \code | ||||
| const yycc_char8_t* yycc_utf8 = YYCC_U8("I am UTF8 string."); | ||||
| const char* converted = YYCC::EncodingHelper::ToNative(yycc_utf8); | ||||
| const char* converted = YYCC::EncodingHelper::ToOrdinary(yycc_utf8); | ||||
|  | ||||
| yycc_char8_t* mutable_yycc_utf8 = const_cast<char*>(yycc_utf8); // Not safe. Also just for example. | ||||
| char* mutable_converted = YYCC::EncodingHelper::ToNative(mutable_yycc_utf8); | ||||
| char* mutable_converted = YYCC::EncodingHelper::ToOrdinary(mutable_yycc_utf8); | ||||
| \endcode | ||||
|  | ||||
| Same as YYCC::EncodingHelper::ToUTF8, YYCC::EncodingHelper::ToNative also has 2 overloads to handle const and mutable string pointer. | ||||
| Same as YYCC::EncodingHelper::ToUTF8, YYCC::EncodingHelper::ToOrdinary also has 2 overloads to handle constant and mutable string pointer. | ||||
|  | ||||
| \section library_encoding__utf8_container UTF8 String Container | ||||
|  | ||||
| String container usually means the standard library string container, such as \c std::string, \c std::wstring, \c std::u32string and etc. | ||||
|  | ||||
| In many personal project, programmer may use \c std::string everywhere because \c std::u8string may not be presented when writing peoject. | ||||
| How to do convertion between native string container and YYCC UTF8 string container? | ||||
| How to do convertion between ordinary string container and YYCC UTF8 string container? | ||||
| It is definitely illegal that directly do force convertion. Because they may have different class layout. | ||||
| Calm down and I will tell you how to do correct convertion. | ||||
| YYCC provides YYCC::EncodingHelper::ToUTF8 to convert native string container to YYCC UTF8 string container. | ||||
| YYCC provides YYCC::EncodingHelper::ToUTF8 to convert ordinary string container to YYCC UTF8 string container. | ||||
| There is an exmaple: | ||||
|  | ||||
| \code | ||||
| std::string native_string("I am UTF8"); | ||||
| yycc_u8string yycc_string = YYCC::EncodingHelper::ToUTF8(native_string); | ||||
| std::string ordinary_string("I am UTF8"); | ||||
| yycc_u8string yycc_string = YYCC::EncodingHelper::ToUTF8(ordinary_string); | ||||
| auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string); | ||||
| \endcode | ||||
|  | ||||
| @ -160,19 +177,19 @@ However, there is a implicit convertion from \c std::string to \c std::string_vi | ||||
| so you can directly pass a \c std::string instance to it. | ||||
|  | ||||
| String view will reduce unnecessary memory copy. | ||||
| If you just want to pass native string container to function, and this function accepts \c yycc_u8string_view as its argument, | ||||
| If you just want to pass ordinary string container to function, and this function accepts \c yycc_u8string_view as its argument, | ||||
| you can use alternative YYCC::EncodingHelper::ToUTF8View. | ||||
|  | ||||
| \code | ||||
| std::string native_string("I am UTF8"); | ||||
| yycc_u8string_view yycc_string = YYCC::EncodingHelper::ToUTF8View(native_string); | ||||
| std::string ordinary_string("I am UTF8"); | ||||
| yycc_u8string_view yycc_string = YYCC::EncodingHelper::ToUTF8View(ordinary_string); | ||||
| auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string); | ||||
| \endcode | ||||
|  | ||||
| Comparing with previous one, this example use less memory. | ||||
| The reduced memory is the content of \c yycc_string because string view is a view, not the copy of original string. | ||||
|  | ||||
| Same as UTF8 string pointer, we also have YYCC::EncodingHelper::ToNative and YYCC::EncodingHelper::ToNativeView do correspondant reverse convertion. | ||||
| Same as UTF8 string pointer, we also have YYCC::EncodingHelper::ToOrdinary and YYCC::EncodingHelper::ToOrdinaryView do correspondant reverse convertion. | ||||
| Try to do your own research and figure out how to use them. | ||||
| It's pretty easy. | ||||
|  | ||||
|  | ||||
							
								
								
									
										35
									
								
								doc/src/platform_checker.dox
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								doc/src/platform_checker.dox
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| /** | ||||
|  | ||||
| \page platform_checker Platform Checker | ||||
|  | ||||
| In many cross platform applications,  | ||||
| programmer usually write code adapted to different platforms in one source file  | ||||
| and enable them respectively by macros representing the target platform. | ||||
| As a cross platform library, | ||||
| YYCC also has this feature and you can utilize it if you don't have other ways to so the same things. | ||||
|  | ||||
| \section platform_checker__values Values | ||||
|  | ||||
| YYCC always define a macro called \c YYCC_OS to indicate the system of target platform. | ||||
| In implementation, it will check following list from top to bottom to set matched value for it. | ||||
|  | ||||
| \li \c YYCC_OS_WINDOWS: Windows environment. It is done by checking whether environment define \c _WIN32 macro. | ||||
| \li \c YYCC_OS_LINUX: In current implementation, this means target platform is \b NOT Windows. | ||||
|  | ||||
| \section platform_checker__usage Usage | ||||
|  | ||||
| Now you know any possible value of \c YYCC_OS. | ||||
| The next step is how to use it to enable specified code in specific target platform. | ||||
| We take Windows platform for example. | ||||
| Assume \c blabla() function is Windows specific. | ||||
| We have following example code: | ||||
|  | ||||
| \code | ||||
| #if YYCC_OS == YYCC_OS_WINDOWS | ||||
| blabla(); | ||||
| #endif | ||||
| \endcode | ||||
|  | ||||
| It's enough and simple that use \c \#if to bracket the Windows specified code. | ||||
|  | ||||
| */ | ||||
| @ -36,7 +36,7 @@ This guard can solve following issues: | ||||
|     Programmer will not be affected by the automatical rename of \c GetObject, \c GetClassName and etc. | ||||
|     <UL> | ||||
|         <LI>These are all macros for Windows personal use to automatically redirect calling to A function and W function by compiling environment.</LI> | ||||
|         <LI>Guard \c #undef these annoy macros.</LI> | ||||
|         <LI>Guard \c \#undef these annoy macros.</LI> | ||||
|     </UL> | ||||
|     </LI> | ||||
|     <LI> | ||||
| @ -56,13 +56,13 @@ Because this guard operate some Windows macros as we introduced above. | ||||
| The headers depending on Windows may throw error if you put them outside of this pair. | ||||
|  | ||||
| Please note WinImportPrefix.hpp and WinImportSuffix.hpp can be included multiple times. | ||||
| Because they do not have the proprocessor command like <I>#pragma once</I> or etc to make sure they only can be included once. | ||||
| Because they do not have the proprocessor command like <I>\#pragma once</I> or etc to make sure they only can be included once. | ||||
| That's by design. Because we actually may use this pair multiple times. | ||||
| The only thing you should pledge is that you must make sure they are presented by pair. | ||||
|  | ||||
| This guard is Windows specific. | ||||
| It does nothing if you accidently use it in other platforms such as Linux, | ||||
| because the headers use \c #if to check environment out and will do nothing in non-Windows environment. | ||||
| because the headers use \c \#if to check environment out and will do nothing in non-Windows environment. | ||||
| However, we still highly recommend you use this pair with platform checker bracket like example does, | ||||
| if your program need to be run on multiple platforms. | ||||
|  | ||||
|  | ||||
| @ -12,6 +12,7 @@ MKDIR bin | ||||
| CD bin | ||||
| MKDIR Win32 | ||||
| MKDIR x64 | ||||
| MKDIR documentation | ||||
| MKDIR install | ||||
|  | ||||
| :: Build for Win32 | ||||
| @ -32,4 +33,11 @@ cmake --build . --config Release | ||||
| cmake --install . --prefix=../install --config Release | ||||
| CD .. | ||||
|  | ||||
| :: Build for documentation | ||||
| CD documentation | ||||
| cmake -DYYCC_BUILD_DOC=ON ../.. | ||||
| cmake --build . --config Release | ||||
| :: cmake --install . --prefix=../install --config Release | ||||
| CD .. | ||||
|  | ||||
| ECHO DONE | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "EncodingHelper.hpp" | ||||
| #include "IOHelper.hpp" | ||||
| #include <stdexcept> | ||||
|  | ||||
| namespace YYCC::ConfigManager { | ||||
|  | ||||
| @ -17,7 +18,11 @@ namespace YYCC::ConfigManager { | ||||
| 			m_CfgFilePath = cfg_file_path; | ||||
| 		// assign settings | ||||
| 		for (auto* setting : settings) { | ||||
| 			m_Settings.try_emplace(setting->GetName(), setting); | ||||
| 			auto result = m_Settings.try_emplace(setting->GetName(), setting); | ||||
| 			if (!result.second) { | ||||
| 				// if not inserted because duplicated, raise exception | ||||
| 				throw std::invalid_argument("Duplicated setting name"); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
| namespace YYCC::ConfigManager { | ||||
|  | ||||
| 	template<typename _Ty> | ||||
| 	struct Constrain { | ||||
| 	struct Constraint { | ||||
| 		using CheckFct_t = std::function<bool(const _Ty&)>; | ||||
| 		//using CorrectFct_t = std::function<_Ty(const _Ty&)>; | ||||
| 		CheckFct_t m_CheckFct; | ||||
| @ -24,13 +24,13 @@ namespace YYCC::ConfigManager { | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	namespace ConstrainPresets { | ||||
| 	namespace ConstraintPresets { | ||||
|  | ||||
| 		template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_enum_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0> | ||||
| 		Constrain<_Ty> GetNumberRangeConstrain(_Ty min_value, _Ty max_value) { | ||||
| 		Constraint<_Ty> GetNumberRangeConstraint(_Ty min_value, _Ty max_value) { | ||||
| 			if (min_value > max_value) | ||||
| 				throw std::invalid_argument("invalid min max value for NumberRangeConstrain"); | ||||
| 			return Constrain<_Ty> { | ||||
| 				throw std::invalid_argument("invalid min max value for NumberRangeConstraint"); | ||||
| 			return Constraint<_Ty> { | ||||
| 				[min_value, max_value](const _Ty& val) -> bool { return (val <= max_value) && (val >= min_value); } | ||||
| 					/*[min_value, max_value](const _Ty& val) -> _Ty { return std::clamp(val, min_value, max_value); }*/ | ||||
| 			}; | ||||
| @ -96,14 +96,14 @@ namespace YYCC::ConfigManager { | ||||
| 	template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> || std::is_enum_v<_Ty>, int> = 0> | ||||
| 	class NumberSetting : public AbstractSetting { | ||||
| 	public: | ||||
| 		NumberSetting(const yycc_char8_t* name, _Ty default_value, Constrain<_Ty> constrain = Constrain<_Ty> {}) : | ||||
| 			AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constrain(constrain) {} | ||||
| 		NumberSetting(const yycc_char8_t* name, _Ty default_value, Constraint<_Ty> constraint = Constraint<_Ty> {}) : | ||||
| 			AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constraint(constraint) {} | ||||
| 		virtual ~NumberSetting() {} | ||||
|  | ||||
| 		_Ty Get() const { return m_Data; } | ||||
| 		bool Set(_Ty new_data) {  | ||||
| 			// validate data | ||||
| 			if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(new_data)) | ||||
| 			if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(new_data)) | ||||
| 				return false; | ||||
| 			// assign data | ||||
| 			m_Data = new_data; | ||||
| @ -117,7 +117,7 @@ namespace YYCC::ConfigManager { | ||||
| 				return false; | ||||
| 			m_Data = *reinterpret_cast<const _Ty*>(GetDataPtr()); | ||||
| 			// check data | ||||
| 			if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data)) | ||||
| 			if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data)) | ||||
| 				return false; | ||||
| 			return true; | ||||
| 		} | ||||
| @ -132,13 +132,13 @@ namespace YYCC::ConfigManager { | ||||
| 		} | ||||
|  | ||||
| 		_Ty m_Data, m_DefaultData; | ||||
| 		Constrain<_Ty> m_Constrain; | ||||
| 		Constraint<_Ty> m_Constraint; | ||||
| 	}; | ||||
|  | ||||
| 	class StringSetting : public AbstractSetting { | ||||
| 	public: | ||||
| 		StringSetting(const yycc_char8_t* name, const yycc_char8_t* default_value, Constrain<yycc_u8string> constrain = Constrain<yycc_u8string> {}) : | ||||
| 			AbstractSetting(name), m_Data(), m_DefaultData(), m_Constrain(constrain) { | ||||
| 		StringSetting(const yycc_char8_t* name, const yycc_char8_t* default_value, Constraint<yycc_u8string> constraint = Constraint<yycc_u8string> {}) : | ||||
| 			AbstractSetting(name), m_Data(), m_DefaultData(), m_Constraint(constraint) { | ||||
| 			if (default_value != nullptr) { | ||||
| 				m_Data = default_value; | ||||
| 				m_DefaultData = default_value; | ||||
| @ -151,7 +151,7 @@ namespace YYCC::ConfigManager { | ||||
| 			// check data validation | ||||
| 			if (new_data == nullptr)  | ||||
| 				return false; | ||||
| 			if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data)) | ||||
| 			if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data)) | ||||
| 				return false; | ||||
| 			// assign data | ||||
| 			m_Data = new_data; | ||||
| @ -173,7 +173,7 @@ namespace YYCC::ConfigManager { | ||||
| 				string_length | ||||
| 			); | ||||
| 			// check data | ||||
| 			if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data)) | ||||
| 			if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data)) | ||||
| 				return false; | ||||
| 			return true; | ||||
| 		} | ||||
| @ -194,7 +194,7 @@ namespace YYCC::ConfigManager { | ||||
| 		} | ||||
|  | ||||
| 		yycc_u8string m_Data, m_DefaultData; | ||||
| 		Constrain<yycc_u8string> m_Constrain; | ||||
| 		Constraint<yycc_u8string> m_Constraint; | ||||
| 	}; | ||||
|  | ||||
| #pragma endregion | ||||
|  | ||||
| @ -226,7 +226,7 @@ namespace YYCC::ConsoleHelper { | ||||
| 		WinConsoleWrite(strl, bIsErr); | ||||
| #else | ||||
| 		// in linux, directly use C function to write. | ||||
| 		std::fputs(EncodingHelper::ToNative(strl.c_str()), bIsErr ? stderr : stdout); | ||||
| 		std::fputs(EncodingHelper::ToOrdinary(strl.c_str()), bIsErr ? stderr : stdout); | ||||
| #endif | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
|  | ||||
| namespace YYCC::EncodingHelper { | ||||
|  | ||||
| #pragma region UTF8 Native Convertion | ||||
| #pragma region UTF8 Ordinary Convertion | ||||
| 	 | ||||
| 	const yycc_char8_t* ToUTF8(const char* src) { | ||||
| 		return reinterpret_cast<const yycc_char8_t*>(src); | ||||
| @ -19,16 +19,16 @@ namespace YYCC::EncodingHelper { | ||||
| 		return yycc_u8string_view(reinterpret_cast<const yycc_char8_t*>(src.data()), src.size()); | ||||
| 	} | ||||
| 	 | ||||
| 	const char* ToNative(const yycc_char8_t* src) { | ||||
| 	const char* ToOrdinary(const yycc_char8_t* src) { | ||||
| 		return reinterpret_cast<const char*>(src); | ||||
| 	} | ||||
| 	char* ToNative(yycc_char8_t* src) { | ||||
| 	char* ToOrdinary(yycc_char8_t* src) { | ||||
| 		return reinterpret_cast<char*>(src); | ||||
| 	} | ||||
| 	std::string ToNative(const yycc_u8string_view& src) { | ||||
| 	std::string ToOrdinary(const yycc_u8string_view& src) { | ||||
| 		return std::string(reinterpret_cast<const char*>(src.data()), src.size()); | ||||
| 	} | ||||
| 	std::string_view ToNativeView(const yycc_u8string_view& src) { | ||||
| 	std::string_view ToOrdinaryView(const yycc_u8string_view& src) { | ||||
| 		return std::string_view(reinterpret_cast<const char*>(src.data()), src.size()); | ||||
| 	} | ||||
|  | ||||
| @ -176,7 +176,7 @@ return ret; | ||||
| #pragma region UTF8ToWchar | ||||
| 	 | ||||
| 	bool UTF8ToWchar(const yycc_u8string_view& src, std::wstring& dst) { | ||||
| 		std::string_view adapted_src(ToNativeView(src)); | ||||
| 		std::string_view adapted_src(ToOrdinaryView(src)); | ||||
| 		return CharToWchar(adapted_src, dst, CP_UTF8); | ||||
| 	} | ||||
| 	bool UTF8ToWchar(const yycc_char8_t* src, std::wstring& dst) { | ||||
|  | ||||
| @ -10,58 +10,25 @@ | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * @brief The namespace handling encoding issues. | ||||
|  * @brief The helper for all encoding aspects. | ||||
|  * @details | ||||
|  * \par Windows Encoding Convertion | ||||
|  * This namespace provides the convertion between wchar_t, UTF8 and code-page-based string: | ||||
|  * The function name has following format: \c AAAToBBB. | ||||
|  * AAA is the source string and BBB is target string. | ||||
|  * AAA and BBB has following possible value: | ||||
|  * \li \c Char: Code-page-based string. Usually it will add a code page parameter for function to get the code page of this string. For code page, please see Microsoft document. | ||||
|  * \li \c UTF8: UTF8 string. | ||||
|  * \li \c Wchar: wchar_t string. | ||||
|  * \par | ||||
|  * For example: \c WcharToUTF8 will perform the convertion from wchar_t to UTF8, | ||||
|  * and \c CharToChar will perform the convertion between 2 code-page-based string and caller can specify individual code page for these 2 string. | ||||
|  * \par | ||||
|  * These functions are Windows specific and are unavailable on other platforms. | ||||
|  * Becasue Windows use wchar_t string as its function arguments for globalization, and this library use UTF8 everywhere. | ||||
|  * So it should have a bidirectional way to do convertion between wchar_t string and UTF8 string. | ||||
|  * | ||||
|  * \par UTF32, UTF16 and UTF8 Convertion | ||||
|  * This namespace also provide the convertion among UTF32, UTF16 and UTF8. | ||||
|  * These convertion functions are suit for all platforms, not Windows oriented. | ||||
|  * \par | ||||
|  * Due to implementation, this library assume all non-Windows system use UTF8 as their C locale. | ||||
|  * Otherwise these functions will produce wrong result. | ||||
|  * | ||||
|  * \par Function Parameters | ||||
|  * We provide these encoding convertion functions with following 2 types: | ||||
|  * \li Function returns \c bool and its parameter order source string pointer and a corresponding \c std::basic_string container for receiving result. | ||||
|  * \li Function returns corresponding \c std::basic_string result, and its parameter only order source string pointer. | ||||
|  * \par | ||||
|  * For these 2 declarations, both of them will not throw any exception and do not accept nullptr as source string. | ||||
|  * The only difference is that the way to indicate convertion error. | ||||
|  * \par | ||||
|  * First declaration will return false to indicate there is an error when doing convertion. Please note that the content of string container passing in may still be changed! | ||||
|  * Last declaration will return empty string to indicate error. Please note if you pass empty string in, they still will output empty string but it doesn't mean an error. | ||||
|  * So last declaration is used in the scenario that we don't care whether the convertion success did. For example, output something to console. | ||||
|  * | ||||
|  * For more infomations about how to use the functions provided by this namespace, | ||||
|  * please see \ref library_encoding and \ref encoding_helper. | ||||
| */ | ||||
| namespace YYCC::EncodingHelper { | ||||
|  | ||||
| #define _YYCC_U8(strl) u8 ## strl | ||||
| #define YYCC_U8(strl) (reinterpret_cast<const ::YYCC::yycc_char8_t*>(_YYCC_U8(strl))) | ||||
| #define _YYCC_U8(strl) u8 ## strl ///< The assistant macro for YYCC_U8. | ||||
| #define YYCC_U8(strl) (reinterpret_cast<const ::YYCC::yycc_char8_t*>(_YYCC_U8(strl))) ///< The macro for creating UTF8 string literal. See \ref library_encoding. | ||||
|  | ||||
| 	const yycc_char8_t* ToUTF8(const char* src); | ||||
| 	yycc_char8_t* ToUTF8(char* src); | ||||
| 	yycc_u8string ToUTF8(const std::string_view& src); | ||||
| 	yycc_u8string_view ToUTF8View(const std::string_view& src); | ||||
|  | ||||
| 	const char* ToNative(const yycc_char8_t* src); | ||||
| 	char* ToNative(yycc_char8_t* src); | ||||
| 	std::string ToNative(const yycc_u8string_view& src); | ||||
| 	std::string_view ToNativeView(const yycc_u8string_view& src); | ||||
| 	const char* ToOrdinary(const yycc_char8_t* src); | ||||
| 	char* ToOrdinary(yycc_char8_t* src); | ||||
| 	std::string ToOrdinary(const yycc_u8string_view& src); | ||||
| 	std::string_view ToOrdinaryView(const yycc_u8string_view& src); | ||||
|  | ||||
| #if YYCC_OS == YYCC_OS_WINDOWS | ||||
|  | ||||
|  | ||||
| @ -11,6 +11,7 @@ | ||||
| #include <cstdarg> | ||||
| #include <cstdio> | ||||
| #include <cinttypes> | ||||
| #include <mutex> | ||||
|  | ||||
| #include "WinImportPrefix.hpp" | ||||
| #include <Windows.h> | ||||
| @ -19,38 +20,153 @@ | ||||
|  | ||||
| namespace YYCC::ExceptionHelper { | ||||
|  | ||||
| 	/** | ||||
| 	 * @brief True if the exception handler already registered, otherwise false. | ||||
| 	 * @details | ||||
| 	 * This variable is designed to prevent multiple register operation | ||||
| 	 * because unhandled exception handler should only be registered once. | ||||
| 	 * \n | ||||
| 	 * Register function should check whether this variable is false before registering, | ||||
| 	 * and set this variable to true after registing. | ||||
| 	 * Unregister as well as should do the same check. | ||||
| 	*/ | ||||
| 	static bool g_IsRegistered = false; | ||||
| 	/** | ||||
| 	 * @brief True if a exception handler is running, otherwise false. | ||||
| 	 * @details | ||||
| 	 * This variable is served for blocking possible infinity recursive exception handling. | ||||
| 	 * \n | ||||
| 	 * When entering unhandled exception handler, we must check whether this variable is true. | ||||
| 	 * If it is true, it mean that there is another unhandled exception handler running. | ||||
| 	 * Then we should exit immediately. | ||||
| 	 * Otherwise, this variable should be set to true indicating we are processing unhandled exception. | ||||
| 	 * After processing exception, at the end of unhandled exception handler, | ||||
| 	 * we should restore this value to false. | ||||
| 	 * | ||||
| 	*/ | ||||
| 	static bool g_IsProcessing = false; | ||||
| 	/** | ||||
| 	 * @brief The backup of original exception handler. | ||||
| 	 * @details | ||||
| 	 * This variable was set when registering unhandled exception handler. | ||||
| 	 * And will be used when unregistering for restoring. | ||||
| 	*/ | ||||
| 	static LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup; | ||||
| 	static LONG WINAPI UExceptionImpl(LPEXCEPTION_POINTERS); | ||||
| 	class ExceptionRegister { | ||||
| 	public: | ||||
| 		ExceptionRegister() : | ||||
| 			m_CoreMutex(), | ||||
| 			m_IsRegistered(false), m_IsProcessing(false), m_PrevProcHandler(nullptr), | ||||
| 			m_SingletonMutex(NULL) {} | ||||
| 		~ExceptionRegister() { | ||||
| 			Unregister(); | ||||
| 		} | ||||
|  | ||||
| 	public: | ||||
| 		/** | ||||
| 		 * @brief Try to register unhandled exception handler. | ||||
| 		*/ | ||||
| 		void Register() { | ||||
| 			std::lock_guard<std::mutex> locker(m_CoreMutex); | ||||
| 			// if we have registered, return | ||||
| 			if (m_IsRegistered) return; | ||||
|  | ||||
| 			// check singleton | ||||
| 			// build mutex string first | ||||
| 			yycc_u8string mutex_name; | ||||
| 			if (!StringHelper::Printf(mutex_name, YYCC_U8("Global\\%" PRIu32 ".{61634294-d23c-43f9-8490-b5e09837eede}"), GetCurrentProcessId())) | ||||
| 				return; | ||||
| 			std::wstring mutex_wname; | ||||
| 			if (!EncodingHelper::UTF8ToWchar(mutex_name, mutex_wname)) | ||||
| 				return; | ||||
| 			// create mutex | ||||
| 			m_SingletonMutex = CreateMutexW(NULL, FALSE, mutex_wname.c_str()); | ||||
| 			DWORD errcode = GetLastError(); | ||||
| 			// check whether be created | ||||
| 			if (m_SingletonMutex == NULL) | ||||
| 				return; | ||||
| 			if (errcode == ERROR_ALREADY_EXISTS) { | ||||
| 				CloseHandle(m_SingletonMutex); | ||||
| 				m_SingletonMutex = NULL; | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			// okey, we can register it. | ||||
| 			// backup old handler | ||||
| 			m_PrevProcHandler = SetUnhandledExceptionFilter(UExceptionImpl); | ||||
| 			// mark registered | ||||
| 			m_IsRegistered = true; | ||||
| 		} | ||||
| 		/** | ||||
| 		 * @brief Try to unregister unhandled exception handler. | ||||
| 		*/ | ||||
| 		void Unregister() { | ||||
| 			std::lock_guard<std::mutex> locker(m_CoreMutex); | ||||
| 			// if we are not registered, skip | ||||
| 			if (!m_IsRegistered) return; | ||||
|  | ||||
| 			// unregister handler | ||||
| 			// restore old handler | ||||
| 			SetUnhandledExceptionFilter(m_PrevProcHandler); | ||||
| 			m_PrevProcHandler = nullptr; | ||||
|  | ||||
| 			// release singleton handler | ||||
| 			if (m_SingletonMutex != NULL) { | ||||
| 				CloseHandle(m_SingletonMutex); | ||||
| 				m_SingletonMutex = NULL; | ||||
| 			} | ||||
|  | ||||
| 			// mark unregistered | ||||
| 			m_IsRegistered = false; | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 	public: | ||||
| 		/** | ||||
| 		 * @brief Check whether handler is registered. | ||||
| 		 * @return True if it is, otherwise false. | ||||
| 		*/ | ||||
| 		bool IsRegistered() const { | ||||
| 			std::lock_guard<std::mutex> locker(m_CoreMutex); | ||||
| 			return m_IsRegistered; | ||||
| 		} | ||||
| 		/** | ||||
| 		 * @brief Check whether we are processing unhandled exception. | ||||
| 		 * @return True if it is, otherwise false. | ||||
| 		*/ | ||||
| 		bool IsProcessing() const { | ||||
| 			std::lock_guard<std::mutex> locker(m_CoreMutex); | ||||
| 			return m_IsProcessing; | ||||
| 		} | ||||
| 		/** | ||||
| 		 * @brief Get the old unhandled exception handler before registering. | ||||
| 		 * @return The fucntion pointer to old unhandled exception handler. May be nullptr. | ||||
| 		*/ | ||||
| 		LPTOP_LEVEL_EXCEPTION_FILTER GetPrevProcHandler() const { | ||||
| 			std::lock_guard<std::mutex> locker(m_CoreMutex); | ||||
| 			return m_PrevProcHandler; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @brief Try to start process unhandled exception. | ||||
| 		 * @return True if you can start to process. | ||||
| 		 * False means there is already a process running. You should not process it now. | ||||
| 		*/ | ||||
| 		bool StartProcessing() { | ||||
| 			std::lock_guard<std::mutex> locker(m_CoreMutex); | ||||
| 			if (m_IsProcessing) return false; | ||||
| 			else { | ||||
| 				m_IsProcessing = true; | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 		/** | ||||
| 		 * @brief Mark current process of unhandled exception has done. | ||||
| 		 * @details This should only be called when StartProcessing() return true. | ||||
| 		*/ | ||||
| 		void StopProcessing() { | ||||
| 			std::lock_guard<std::mutex> locker(m_CoreMutex); | ||||
| 			m_IsProcessing = false; | ||||
| 		} | ||||
|  | ||||
| 	private: | ||||
| 		/** | ||||
| 		 * @brief The core mutex for keeping this class is in synchronized. | ||||
| 		*/ | ||||
| 		mutable std::mutex m_CoreMutex; | ||||
|  | ||||
| 		/** | ||||
| 		 * @brief Whether we have registered unhandled exception handler. | ||||
| 		 * True if it is, otherwise false. | ||||
| 		*/ | ||||
| 		bool m_IsRegistered; | ||||
| 		/** | ||||
| 		 * @brief Whether we are processing unhandled exception. | ||||
| 		 * True if it is, otherwise false. | ||||
| 		*/ | ||||
| 		bool m_IsProcessing; | ||||
| 		/** | ||||
| 		 * @brief The backup of old unhandled exception handler. | ||||
| 		*/ | ||||
| 		LPTOP_LEVEL_EXCEPTION_FILTER m_PrevProcHandler; | ||||
| 		/** | ||||
| 		 * @brief The Windows mutex handle for singleton implementation. | ||||
| 		 * Because we may have many DLLs using YYCC in the same process. | ||||
| 		 * But the unhandled exception handler only need to be registered once. | ||||
| 		*/ | ||||
| 		HANDLE m_SingletonMutex; | ||||
| 	}; | ||||
|  | ||||
| 	static ExceptionRegister g_ExceptionRegister; | ||||
|  | ||||
| #pragma region Exception Handler Implementation | ||||
|  | ||||
| @ -122,7 +238,7 @@ namespace YYCC::ExceptionHelper { | ||||
| 		if (fs != nullptr) { | ||||
| 			va_list arg1; | ||||
| 			va_start(arg1, fmt); | ||||
| 			std::vfprintf(fs, EncodingHelper::ToNative(fmt), arg1); | ||||
| 			std::vfprintf(fs, EncodingHelper::ToOrdinary(fmt), arg1); | ||||
| 			std::fputs("\n", fs); | ||||
| 			va_end(arg1); | ||||
| 		} | ||||
| @ -145,7 +261,7 @@ namespace YYCC::ExceptionHelper { | ||||
| 	static void UExceptionErrLogWriteLine(std::FILE* fs, const yycc_char8_t* strl) { | ||||
| 		// write to file | ||||
| 		if (fs != nullptr) { | ||||
| 			std::fputs(EncodingHelper::ToNative(strl), fs); | ||||
| 			std::fputs(EncodingHelper::ToOrdinary(strl), fs); | ||||
| 			std::fputs("\n", fs); | ||||
| 		} | ||||
| 		// write to stderr | ||||
| @ -316,28 +432,24 @@ namespace YYCC::ExceptionHelper { | ||||
| 	} | ||||
|  | ||||
| 	static bool UExceptionFetchRecordPath(yycc_u8string& log_path, yycc_u8string& coredump_path) { | ||||
| 		// build two file names like: "module.dll.1234.log" and "module.dll.1234.dmp". | ||||
| 		// "module.dll" is the name of current module. "1234" is current process id. | ||||
| 		// get self module name | ||||
| 		yycc_u8string u8_self_module_name; | ||||
| 		// build two file names like: "error.exe.1234.log" and "error.exe.1234.dmp". | ||||
| 		// "error.exe" is the name of current process. "1234" is current process id. | ||||
| 		// get process name | ||||
| 		yycc_u8string u8_process_name; | ||||
| 		{ | ||||
| 			// get module handle | ||||
| 			HMODULE hSelfModule = YYCC::WinFctHelper::GetCurrentModule(); | ||||
| 			if (hSelfModule == nullptr) | ||||
| 				return false; | ||||
| 			// get full path of self module | ||||
| 			yycc_u8string u8_self_module_path; | ||||
| 			if (!YYCC::WinFctHelper::GetModuleFileName(hSelfModule, u8_self_module_path)) | ||||
| 			// get full path of process | ||||
| 			yycc_u8string u8_process_path; | ||||
| 			if (!YYCC::WinFctHelper::GetModuleFileName(NULL, u8_process_path)) | ||||
| 				return false; | ||||
| 			// extract file name from full path by std::filesystem::path | ||||
| 			std::filesystem::path self_module_path(FsPathPatch::FromUTF8Path(u8_self_module_path.c_str())); | ||||
| 			u8_self_module_name = FsPathPatch::ToUTF8Path(self_module_path.filename()); | ||||
| 			std::filesystem::path process_path(FsPathPatch::FromUTF8Path(u8_process_path.c_str())); | ||||
| 			u8_process_name = FsPathPatch::ToUTF8Path(process_path.filename()); | ||||
| 		} | ||||
| 		// then get process id | ||||
| 		DWORD process_id = GetCurrentProcessId(); | ||||
| 		// conbine them as a file name prefix | ||||
| 		yycc_u8string u8_filename_prefix; | ||||
| 		if (!YYCC::StringHelper::Printf(u8_filename_prefix, YYCC_U8("%s.%" PRIu32), u8_self_module_name.c_str(), process_id)) | ||||
| 		if (!YYCC::StringHelper::Printf(u8_filename_prefix, YYCC_U8("%s.%" PRIu32), u8_process_name.c_str(), process_id)) | ||||
| 			return false; | ||||
| 		// then get file name for log and minidump | ||||
| 		yycc_u8string u8_log_filename = u8_filename_prefix + YYCC_U8(".log"); | ||||
| @ -367,10 +479,9 @@ namespace YYCC::ExceptionHelper { | ||||
| 	} | ||||
|  | ||||
| 	static LONG WINAPI UExceptionImpl(LPEXCEPTION_POINTERS info) { | ||||
| 		// detect loop calling | ||||
| 		if (g_IsProcessing) goto end_proc; | ||||
| 		// start process | ||||
| 		g_IsProcessing = true; | ||||
| 		// try to start process current unhandled exception | ||||
| 		// to prevent any possible recursive calling. | ||||
| 		if (!g_ExceptionRegister.StartProcessing()) goto end_proc; | ||||
|  | ||||
| 		// core implementation | ||||
| 		{ | ||||
| @ -396,14 +507,15 @@ namespace YYCC::ExceptionHelper { | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// end process | ||||
| 	failed: | ||||
| 		g_IsProcessing = false; | ||||
| 		// stop process | ||||
| 		g_ExceptionRegister.StartProcessing(); | ||||
|  | ||||
| 	end_proc: | ||||
| 		// if backup proc can be run, run it | ||||
| 		// otherwise directly return. | ||||
| 	end_proc: | ||||
| 		if (g_ProcBackup != nullptr) { | ||||
| 			return g_ProcBackup(info); | ||||
| 		auto prev_proc = g_ExceptionRegister.GetPrevProcHandler(); | ||||
| 		if (prev_proc != nullptr) { | ||||
| 			return prev_proc(info); | ||||
| 		} else { | ||||
| 			return EXCEPTION_CONTINUE_SEARCH; | ||||
| 		} | ||||
| @ -412,15 +524,11 @@ namespace YYCC::ExceptionHelper { | ||||
| #pragma endregion | ||||
|  | ||||
| 	void Register() { | ||||
| 		if (g_IsRegistered) return; | ||||
| 		g_ProcBackup = SetUnhandledExceptionFilter(UExceptionImpl); | ||||
| 		g_IsRegistered = true; | ||||
| 		g_ExceptionRegister.Register(); | ||||
| 	} | ||||
|  | ||||
| 	void Unregister() { | ||||
| 		if (!g_IsRegistered) return; | ||||
| 		SetUnhandledExceptionFilter(g_ProcBackup); | ||||
| 		g_IsRegistered = false; | ||||
| 		g_ExceptionRegister.Unregister(); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -18,7 +18,7 @@ namespace YYCC::FsPathPatch { | ||||
| 		return std::filesystem::path(wpath); | ||||
| 		 | ||||
| #else | ||||
| 		return std::filesystem::path(EncodingHelper::ToNative(u8_path)); | ||||
| 		return std::filesystem::path(EncodingHelper::ToOrdinary(u8_path)); | ||||
| #endif | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -16,13 +16,13 @@ namespace YYCC::ParserHelper { | ||||
| 	template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0> | ||||
| 	bool TryParse(const yycc_u8string_view& strl, _Ty& num) { | ||||
| 		auto [ptr, ec] = std::from_chars( | ||||
| 			EncodingHelper::ToNative(strl.data()),  | ||||
| 			EncodingHelper::ToNative(strl.data() + strl.size()),  | ||||
| 			EncodingHelper::ToOrdinary(strl.data()),  | ||||
| 			EncodingHelper::ToOrdinary(strl.data() + strl.size()),  | ||||
| 			num, std::chars_format::general | ||||
| 		); | ||||
| 		if (ec == std::errc()) { | ||||
| 			// check whether the full string is matched | ||||
| 			return ptr == EncodingHelper::ToNative(strl.data() + strl.size()); | ||||
| 			return ptr == EncodingHelper::ToOrdinary(strl.data() + strl.size()); | ||||
| 		} else if (ec == std::errc::invalid_argument) { | ||||
| 			// given string is invalid | ||||
| 			return false; | ||||
| @ -37,13 +37,13 @@ namespace YYCC::ParserHelper { | ||||
| 	template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0> | ||||
| 	bool TryParse(const yycc_u8string_view& strl, _Ty& num, int base = 10) { | ||||
| 		auto [ptr, ec] = std::from_chars( | ||||
| 			EncodingHelper::ToNative(strl.data()),  | ||||
| 			EncodingHelper::ToNative(strl.data() + strl.size()),  | ||||
| 			EncodingHelper::ToOrdinary(strl.data()),  | ||||
| 			EncodingHelper::ToOrdinary(strl.data() + strl.size()),  | ||||
| 			num, base | ||||
| 		); | ||||
| 		if (ec == std::errc()) { | ||||
| 			// check whether the full string is matched | ||||
| 			return ptr == EncodingHelper::ToNative(strl.data() + strl.size()); | ||||
| 			return ptr == EncodingHelper::ToOrdinary(strl.data() + strl.size()); | ||||
| 		} else if (ec == std::errc::invalid_argument) { | ||||
| 			// given string is invalid | ||||
| 			return false; | ||||
| @ -76,8 +76,8 @@ namespace YYCC::ParserHelper { | ||||
| 	yycc_u8string ToString(_Ty num) { | ||||
| 		std::array<yycc_char8_t, 64> buffer; | ||||
| 		auto [ptr, ec] = std::to_chars( | ||||
| 			EncodingHelper::ToNative(buffer.data()),  | ||||
| 			EncodingHelper::ToNative(buffer.data() + buffer.size()),  | ||||
| 			EncodingHelper::ToOrdinary(buffer.data()),  | ||||
| 			EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),  | ||||
| 			num | ||||
| 		); | ||||
| 		if (ec == std::errc()) { | ||||
|  | ||||
| @ -25,7 +25,7 @@ namespace YYCC::StringHelper { | ||||
| 		int count = std::vsnprintf( | ||||
| 			nullptr,  | ||||
| 			0,  | ||||
| 			EncodingHelper::ToNative(format),  | ||||
| 			EncodingHelper::ToOrdinary(format),  | ||||
| 			args1 | ||||
| 		); | ||||
| 		if (count < 0) { | ||||
| @ -40,9 +40,9 @@ namespace YYCC::StringHelper { | ||||
| 		// however std::vsnprintf already have a trailing NULL, so we plus 1 for it. | ||||
| 		strl.resize(count); | ||||
| 		int write_result = std::vsnprintf( | ||||
| 			EncodingHelper::ToNative(strl.data()), | ||||
| 			EncodingHelper::ToOrdinary(strl.data()), | ||||
| 			strl.size() + 1,  | ||||
| 			EncodingHelper::ToNative(format),  | ||||
| 			EncodingHelper::ToOrdinary(format),  | ||||
| 			args2 | ||||
| 		); | ||||
| 		va_end(args2); | ||||
|  | ||||
| @ -134,7 +134,8 @@ namespace YYCCTestbench { | ||||
| 			Console::Write(YYCC_U8("\t> ")); | ||||
|  | ||||
| 			YYCC::yycc_u8string gotten(Console::ReadLine()); | ||||
| 			Assert(gotten == strl, YYCC::StringHelper::Printf(YYCC_U8("Got: %s"), gotten.c_str()).c_str()); | ||||
| 			if (gotten == strl) Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("\tMatched! Got: %s")), gotten.c_str()); | ||||
| 			else Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("\tNOT Matched! Got: %s")), gotten.c_str()); | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| @ -401,7 +402,7 @@ namespace YYCCTestbench { | ||||
| 			m_FloatSetting(YYCC_U8("float-setting"), 0.0f), | ||||
| 			m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")), | ||||
| 			m_BoolSetting(YYCC_U8("bool-setting"), false), | ||||
| 			m_ClampedFloatSetting(YYCC_U8("clamped-float-setting"), 0.0f, YYCC::ConfigManager::ConstrainPresets::GetNumberRangeConstrain<float>(-1.0f, 1.0f)), | ||||
| 			m_ClampedFloatSetting(YYCC_U8("clamped-float-setting"), 0.0f, YYCC::ConfigManager::ConstraintPresets::GetNumberRangeConstraint<float>(-1.0f, 1.0f)), | ||||
| 			m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1), | ||||
| 			m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), { | ||||
| 				&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting | ||||
| @ -436,7 +437,7 @@ namespace YYCCTestbench { | ||||
| 		// init cfg manager | ||||
| 		TestConfigManager test; | ||||
|  | ||||
| 		// test constrain works | ||||
| 		// test constraint works | ||||
| 		Assert(!test.m_ClampedFloatSetting.Set(2.0f), YYCC_U8("YYCC::ConfigManager::Constraint")); | ||||
| 		Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::Constraint")); | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user