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)
|
cmake_minimum_required(VERSION 3.23)
|
||||||
project(YYCC
|
project(YYCC
|
||||||
VERSION 1.0.0
|
VERSION 1.1.0
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,8 +4,141 @@
|
|||||||
|
|
||||||
YYCC::EncodingHelper namespace include all encoding related functions:
|
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 Windows specific convertion between \c WCHAR, UTF8 string and string encoded by other encoding.
|
||||||
\li The convertion among UTF8, UTF16 and UTF32.
|
\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 intro
|
||||||
|
|
||||||
|
\li \subpage platform_checker
|
||||||
|
|
||||||
\li \subpage library_encoding
|
\li \subpage library_encoding
|
||||||
|
|
||||||
\li \subpage encoding_helper
|
\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.
|
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.
|
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
|
\section library_encoding__utf8_literal UTF8 Literal
|
||||||
|
|
||||||
String literal is a C++ concept.
|
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);
|
yycc_char8_t* mutable_converted = YYCC::EncodingHelper::ToUTF8(mutable_utf8);
|
||||||
\endcode
|
\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:
|
Here is an exmaple:
|
||||||
|
|
||||||
\code
|
\code
|
||||||
const yycc_char8_t* yycc_utf8 = YYCC_U8("I am UTF8 string.");
|
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.
|
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
|
\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
|
\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.
|
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.
|
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.
|
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.
|
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:
|
There is an exmaple:
|
||||||
|
|
||||||
\code
|
\code
|
||||||
std::string native_string("I am UTF8");
|
std::string ordinary_string("I am UTF8");
|
||||||
yycc_u8string yycc_string = YYCC::EncodingHelper::ToUTF8(native_string);
|
yycc_u8string yycc_string = YYCC::EncodingHelper::ToUTF8(ordinary_string);
|
||||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
||||||
\endcode
|
\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.
|
so you can directly pass a \c std::string instance to it.
|
||||||
|
|
||||||
String view will reduce unnecessary memory copy.
|
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.
|
you can use alternative YYCC::EncodingHelper::ToUTF8View.
|
||||||
|
|
||||||
\code
|
\code
|
||||||
std::string native_string("I am UTF8");
|
std::string ordinary_string("I am UTF8");
|
||||||
yycc_u8string_view yycc_string = YYCC::EncodingHelper::ToUTF8View(native_string);
|
yycc_u8string_view yycc_string = YYCC::EncodingHelper::ToUTF8View(ordinary_string);
|
||||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
Comparing with previous one, this example use less memory.
|
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.
|
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.
|
Try to do your own research and figure out how to use them.
|
||||||
It's pretty easy.
|
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.
|
Programmer will not be affected by the automatical rename of \c GetObject, \c GetClassName and etc.
|
||||||
<UL>
|
<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>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>
|
</UL>
|
||||||
</LI>
|
</LI>
|
||||||
<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.
|
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.
|
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.
|
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.
|
The only thing you should pledge is that you must make sure they are presented by pair.
|
||||||
|
|
||||||
This guard is Windows specific.
|
This guard is Windows specific.
|
||||||
It does nothing if you accidently use it in other platforms such as Linux,
|
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,
|
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.
|
if your program need to be run on multiple platforms.
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ MKDIR bin
|
|||||||
CD bin
|
CD bin
|
||||||
MKDIR Win32
|
MKDIR Win32
|
||||||
MKDIR x64
|
MKDIR x64
|
||||||
|
MKDIR documentation
|
||||||
MKDIR install
|
MKDIR install
|
||||||
|
|
||||||
:: Build for Win32
|
:: Build for Win32
|
||||||
@ -32,4 +33,11 @@ cmake --build . --config Release
|
|||||||
cmake --install . --prefix=../install --config Release
|
cmake --install . --prefix=../install --config Release
|
||||||
CD ..
|
CD ..
|
||||||
|
|
||||||
|
:: Build for documentation
|
||||||
|
CD documentation
|
||||||
|
cmake -DYYCC_BUILD_DOC=ON ../..
|
||||||
|
cmake --build . --config Release
|
||||||
|
:: cmake --install . --prefix=../install --config Release
|
||||||
|
CD ..
|
||||||
|
|
||||||
ECHO DONE
|
ECHO DONE
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
#include "EncodingHelper.hpp"
|
||||||
#include "IOHelper.hpp"
|
#include "IOHelper.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace YYCC::ConfigManager {
|
namespace YYCC::ConfigManager {
|
||||||
|
|
||||||
@ -17,7 +18,11 @@ namespace YYCC::ConfigManager {
|
|||||||
m_CfgFilePath = cfg_file_path;
|
m_CfgFilePath = cfg_file_path;
|
||||||
// assign settings
|
// assign settings
|
||||||
for (auto* setting : 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 {
|
namespace YYCC::ConfigManager {
|
||||||
|
|
||||||
template<typename _Ty>
|
template<typename _Ty>
|
||||||
struct Constrain {
|
struct Constraint {
|
||||||
using CheckFct_t = std::function<bool(const _Ty&)>;
|
using CheckFct_t = std::function<bool(const _Ty&)>;
|
||||||
//using CorrectFct_t = std::function<_Ty(const _Ty&)>;
|
//using CorrectFct_t = std::function<_Ty(const _Ty&)>;
|
||||||
CheckFct_t m_CheckFct;
|
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>
|
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)
|
if (min_value > max_value)
|
||||||
throw std::invalid_argument("invalid min max value for NumberRangeConstrain");
|
throw std::invalid_argument("invalid min max value for NumberRangeConstraint");
|
||||||
return Constrain<_Ty> {
|
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) -> bool { return (val <= max_value) && (val >= min_value); }
|
||||||
/*[min_value, max_value](const _Ty& val) -> _Ty { return std::clamp(val, min_value, max_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>
|
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> || std::is_enum_v<_Ty>, int> = 0>
|
||||||
class NumberSetting : public AbstractSetting {
|
class NumberSetting : public AbstractSetting {
|
||||||
public:
|
public:
|
||||||
NumberSetting(const yycc_char8_t* name, _Ty default_value, Constrain<_Ty> constrain = Constrain<_Ty> {}) :
|
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_Constrain(constrain) {}
|
AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constraint(constraint) {}
|
||||||
virtual ~NumberSetting() {}
|
virtual ~NumberSetting() {}
|
||||||
|
|
||||||
_Ty Get() const { return m_Data; }
|
_Ty Get() const { return m_Data; }
|
||||||
bool Set(_Ty new_data) {
|
bool Set(_Ty new_data) {
|
||||||
// validate 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;
|
return false;
|
||||||
// assign data
|
// assign data
|
||||||
m_Data = new_data;
|
m_Data = new_data;
|
||||||
@ -117,7 +117,7 @@ namespace YYCC::ConfigManager {
|
|||||||
return false;
|
return false;
|
||||||
m_Data = *reinterpret_cast<const _Ty*>(GetDataPtr());
|
m_Data = *reinterpret_cast<const _Ty*>(GetDataPtr());
|
||||||
// check data
|
// 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 false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -132,13 +132,13 @@ namespace YYCC::ConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_Ty m_Data, m_DefaultData;
|
_Ty m_Data, m_DefaultData;
|
||||||
Constrain<_Ty> m_Constrain;
|
Constraint<_Ty> m_Constraint;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StringSetting : public AbstractSetting {
|
class StringSetting : public AbstractSetting {
|
||||||
public:
|
public:
|
||||||
StringSetting(const yycc_char8_t* name, const yycc_char8_t* default_value, Constrain<yycc_u8string> constrain = Constrain<yycc_u8string> {}) :
|
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_Constrain(constrain) {
|
AbstractSetting(name), m_Data(), m_DefaultData(), m_Constraint(constraint) {
|
||||||
if (default_value != nullptr) {
|
if (default_value != nullptr) {
|
||||||
m_Data = default_value;
|
m_Data = default_value;
|
||||||
m_DefaultData = default_value;
|
m_DefaultData = default_value;
|
||||||
@ -151,7 +151,7 @@ namespace YYCC::ConfigManager {
|
|||||||
// check data validation
|
// check data validation
|
||||||
if (new_data == nullptr)
|
if (new_data == nullptr)
|
||||||
return false;
|
return false;
|
||||||
if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data))
|
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
||||||
return false;
|
return false;
|
||||||
// assign data
|
// assign data
|
||||||
m_Data = new_data;
|
m_Data = new_data;
|
||||||
@ -173,7 +173,7 @@ namespace YYCC::ConfigManager {
|
|||||||
string_length
|
string_length
|
||||||
);
|
);
|
||||||
// check data
|
// 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 false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ namespace YYCC::ConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
yycc_u8string m_Data, m_DefaultData;
|
yycc_u8string m_Data, m_DefaultData;
|
||||||
Constrain<yycc_u8string> m_Constrain;
|
Constraint<yycc_u8string> m_Constraint;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
@ -226,7 +226,7 @@ namespace YYCC::ConsoleHelper {
|
|||||||
WinConsoleWrite(strl, bIsErr);
|
WinConsoleWrite(strl, bIsErr);
|
||||||
#else
|
#else
|
||||||
// in linux, directly use C function to write.
|
// 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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace YYCC::EncodingHelper {
|
namespace YYCC::EncodingHelper {
|
||||||
|
|
||||||
#pragma region UTF8 Native Convertion
|
#pragma region UTF8 Ordinary Convertion
|
||||||
|
|
||||||
const yycc_char8_t* ToUTF8(const char* src) {
|
const yycc_char8_t* ToUTF8(const char* src) {
|
||||||
return reinterpret_cast<const yycc_char8_t*>(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());
|
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);
|
return reinterpret_cast<const char*>(src);
|
||||||
}
|
}
|
||||||
char* ToNative(yycc_char8_t* src) {
|
char* ToOrdinary(yycc_char8_t* src) {
|
||||||
return reinterpret_cast<char*>(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());
|
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());
|
return std::string_view(reinterpret_cast<const char*>(src.data()), src.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ return ret;
|
|||||||
#pragma region UTF8ToWchar
|
#pragma region UTF8ToWchar
|
||||||
|
|
||||||
bool UTF8ToWchar(const yycc_u8string_view& src, std::wstring& dst) {
|
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);
|
return CharToWchar(adapted_src, dst, CP_UTF8);
|
||||||
}
|
}
|
||||||
bool UTF8ToWchar(const yycc_char8_t* src, std::wstring& dst) {
|
bool UTF8ToWchar(const yycc_char8_t* src, std::wstring& dst) {
|
||||||
|
@ -10,58 +10,25 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The namespace handling encoding issues.
|
* @brief The helper for all encoding aspects.
|
||||||
* @details
|
* @details
|
||||||
* \par Windows Encoding Convertion
|
* For more infomations about how to use the functions provided by this namespace,
|
||||||
* This namespace provides the convertion between wchar_t, UTF8 and code-page-based string:
|
* please see \ref library_encoding and \ref encoding_helper.
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
namespace YYCC::EncodingHelper {
|
namespace YYCC::EncodingHelper {
|
||||||
|
|
||||||
#define _YYCC_U8(strl) 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)))
|
#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);
|
const yycc_char8_t* ToUTF8(const char* src);
|
||||||
yycc_char8_t* ToUTF8(char* src);
|
yycc_char8_t* ToUTF8(char* src);
|
||||||
yycc_u8string ToUTF8(const std::string_view& src);
|
yycc_u8string ToUTF8(const std::string_view& src);
|
||||||
yycc_u8string_view ToUTF8View(const std::string_view& src);
|
yycc_u8string_view ToUTF8View(const std::string_view& src);
|
||||||
|
|
||||||
const char* ToNative(const yycc_char8_t* src);
|
const char* ToOrdinary(const yycc_char8_t* src);
|
||||||
char* ToNative(yycc_char8_t* src);
|
char* ToOrdinary(yycc_char8_t* src);
|
||||||
std::string ToNative(const yycc_u8string_view& src);
|
std::string ToOrdinary(const yycc_u8string_view& src);
|
||||||
std::string_view ToNativeView(const yycc_u8string_view& src);
|
std::string_view ToOrdinaryView(const yycc_u8string_view& src);
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "WinImportPrefix.hpp"
|
#include "WinImportPrefix.hpp"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@ -19,38 +20,153 @@
|
|||||||
|
|
||||||
namespace YYCC::ExceptionHelper {
|
namespace YYCC::ExceptionHelper {
|
||||||
|
|
||||||
|
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 True if the exception handler already registered, otherwise false.
|
* @brief Try to register unhandled exception handler.
|
||||||
* @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;
|
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 True if a exception handler is running, otherwise false.
|
* @brief Try to unregister unhandled exception handler.
|
||||||
* @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;
|
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 The backup of original exception handler.
|
* @brief Check whether handler is registered.
|
||||||
* @details
|
* @return True if it is, otherwise false.
|
||||||
* This variable was set when registering unhandled exception handler.
|
|
||||||
* And will be used when unregistering for restoring.
|
|
||||||
*/
|
*/
|
||||||
static LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup;
|
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
|
#pragma region Exception Handler Implementation
|
||||||
|
|
||||||
@ -122,7 +238,7 @@ namespace YYCC::ExceptionHelper {
|
|||||||
if (fs != nullptr) {
|
if (fs != nullptr) {
|
||||||
va_list arg1;
|
va_list arg1;
|
||||||
va_start(arg1, fmt);
|
va_start(arg1, fmt);
|
||||||
std::vfprintf(fs, EncodingHelper::ToNative(fmt), arg1);
|
std::vfprintf(fs, EncodingHelper::ToOrdinary(fmt), arg1);
|
||||||
std::fputs("\n", fs);
|
std::fputs("\n", fs);
|
||||||
va_end(arg1);
|
va_end(arg1);
|
||||||
}
|
}
|
||||||
@ -145,7 +261,7 @@ namespace YYCC::ExceptionHelper {
|
|||||||
static void UExceptionErrLogWriteLine(std::FILE* fs, const yycc_char8_t* strl) {
|
static void UExceptionErrLogWriteLine(std::FILE* fs, const yycc_char8_t* strl) {
|
||||||
// write to file
|
// write to file
|
||||||
if (fs != nullptr) {
|
if (fs != nullptr) {
|
||||||
std::fputs(EncodingHelper::ToNative(strl), fs);
|
std::fputs(EncodingHelper::ToOrdinary(strl), fs);
|
||||||
std::fputs("\n", fs);
|
std::fputs("\n", fs);
|
||||||
}
|
}
|
||||||
// write to stderr
|
// write to stderr
|
||||||
@ -316,28 +432,24 @@ namespace YYCC::ExceptionHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool UExceptionFetchRecordPath(yycc_u8string& log_path, yycc_u8string& coredump_path) {
|
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".
|
// build two file names like: "error.exe.1234.log" and "error.exe.1234.dmp".
|
||||||
// "module.dll" is the name of current module. "1234" is current process id.
|
// "error.exe" is the name of current process. "1234" is current process id.
|
||||||
// get self module name
|
// get process name
|
||||||
yycc_u8string u8_self_module_name;
|
yycc_u8string u8_process_name;
|
||||||
{
|
{
|
||||||
// get module handle
|
// get full path of process
|
||||||
HMODULE hSelfModule = YYCC::WinFctHelper::GetCurrentModule();
|
yycc_u8string u8_process_path;
|
||||||
if (hSelfModule == nullptr)
|
if (!YYCC::WinFctHelper::GetModuleFileName(NULL, u8_process_path))
|
||||||
return false;
|
|
||||||
// get full path of self module
|
|
||||||
yycc_u8string u8_self_module_path;
|
|
||||||
if (!YYCC::WinFctHelper::GetModuleFileName(hSelfModule, u8_self_module_path))
|
|
||||||
return false;
|
return false;
|
||||||
// extract file name from full path by std::filesystem::path
|
// extract file name from full path by std::filesystem::path
|
||||||
std::filesystem::path self_module_path(FsPathPatch::FromUTF8Path(u8_self_module_path.c_str()));
|
std::filesystem::path process_path(FsPathPatch::FromUTF8Path(u8_process_path.c_str()));
|
||||||
u8_self_module_name = FsPathPatch::ToUTF8Path(self_module_path.filename());
|
u8_process_name = FsPathPatch::ToUTF8Path(process_path.filename());
|
||||||
}
|
}
|
||||||
// then get process id
|
// then get process id
|
||||||
DWORD process_id = GetCurrentProcessId();
|
DWORD process_id = GetCurrentProcessId();
|
||||||
// conbine them as a file name prefix
|
// conbine them as a file name prefix
|
||||||
yycc_u8string u8_filename_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;
|
return false;
|
||||||
// then get file name for log and minidump
|
// then get file name for log and minidump
|
||||||
yycc_u8string u8_log_filename = u8_filename_prefix + YYCC_U8(".log");
|
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) {
|
static LONG WINAPI UExceptionImpl(LPEXCEPTION_POINTERS info) {
|
||||||
// detect loop calling
|
// try to start process current unhandled exception
|
||||||
if (g_IsProcessing) goto end_proc;
|
// to prevent any possible recursive calling.
|
||||||
// start process
|
if (!g_ExceptionRegister.StartProcessing()) goto end_proc;
|
||||||
g_IsProcessing = true;
|
|
||||||
|
|
||||||
// core implementation
|
// core implementation
|
||||||
{
|
{
|
||||||
@ -396,14 +507,15 @@ namespace YYCC::ExceptionHelper {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// end process
|
// stop process
|
||||||
failed:
|
g_ExceptionRegister.StartProcessing();
|
||||||
g_IsProcessing = false;
|
|
||||||
|
end_proc:
|
||||||
// if backup proc can be run, run it
|
// if backup proc can be run, run it
|
||||||
// otherwise directly return.
|
// otherwise directly return.
|
||||||
end_proc:
|
auto prev_proc = g_ExceptionRegister.GetPrevProcHandler();
|
||||||
if (g_ProcBackup != nullptr) {
|
if (prev_proc != nullptr) {
|
||||||
return g_ProcBackup(info);
|
return prev_proc(info);
|
||||||
} else {
|
} else {
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
@ -412,15 +524,11 @@ namespace YYCC::ExceptionHelper {
|
|||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
void Register() {
|
void Register() {
|
||||||
if (g_IsRegistered) return;
|
g_ExceptionRegister.Register();
|
||||||
g_ProcBackup = SetUnhandledExceptionFilter(UExceptionImpl);
|
|
||||||
g_IsRegistered = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unregister() {
|
void Unregister() {
|
||||||
if (!g_IsRegistered) return;
|
g_ExceptionRegister.Unregister();
|
||||||
SetUnhandledExceptionFilter(g_ProcBackup);
|
|
||||||
g_IsRegistered = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace YYCC::FsPathPatch {
|
|||||||
return std::filesystem::path(wpath);
|
return std::filesystem::path(wpath);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
return std::filesystem::path(EncodingHelper::ToNative(u8_path));
|
return std::filesystem::path(EncodingHelper::ToOrdinary(u8_path));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,13 +16,13 @@ namespace YYCC::ParserHelper {
|
|||||||
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
||||||
bool TryParse(const yycc_u8string_view& strl, _Ty& num) {
|
bool TryParse(const yycc_u8string_view& strl, _Ty& num) {
|
||||||
auto [ptr, ec] = std::from_chars(
|
auto [ptr, ec] = std::from_chars(
|
||||||
EncodingHelper::ToNative(strl.data()),
|
EncodingHelper::ToOrdinary(strl.data()),
|
||||||
EncodingHelper::ToNative(strl.data() + strl.size()),
|
EncodingHelper::ToOrdinary(strl.data() + strl.size()),
|
||||||
num, std::chars_format::general
|
num, std::chars_format::general
|
||||||
);
|
);
|
||||||
if (ec == std::errc()) {
|
if (ec == std::errc()) {
|
||||||
// check whether the full string is matched
|
// 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) {
|
} else if (ec == std::errc::invalid_argument) {
|
||||||
// given string is invalid
|
// given string is invalid
|
||||||
return false;
|
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>
|
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) {
|
bool TryParse(const yycc_u8string_view& strl, _Ty& num, int base = 10) {
|
||||||
auto [ptr, ec] = std::from_chars(
|
auto [ptr, ec] = std::from_chars(
|
||||||
EncodingHelper::ToNative(strl.data()),
|
EncodingHelper::ToOrdinary(strl.data()),
|
||||||
EncodingHelper::ToNative(strl.data() + strl.size()),
|
EncodingHelper::ToOrdinary(strl.data() + strl.size()),
|
||||||
num, base
|
num, base
|
||||||
);
|
);
|
||||||
if (ec == std::errc()) {
|
if (ec == std::errc()) {
|
||||||
// check whether the full string is matched
|
// 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) {
|
} else if (ec == std::errc::invalid_argument) {
|
||||||
// given string is invalid
|
// given string is invalid
|
||||||
return false;
|
return false;
|
||||||
@ -76,8 +76,8 @@ namespace YYCC::ParserHelper {
|
|||||||
yycc_u8string ToString(_Ty num) {
|
yycc_u8string ToString(_Ty num) {
|
||||||
std::array<yycc_char8_t, 64> buffer;
|
std::array<yycc_char8_t, 64> buffer;
|
||||||
auto [ptr, ec] = std::to_chars(
|
auto [ptr, ec] = std::to_chars(
|
||||||
EncodingHelper::ToNative(buffer.data()),
|
EncodingHelper::ToOrdinary(buffer.data()),
|
||||||
EncodingHelper::ToNative(buffer.data() + buffer.size()),
|
EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),
|
||||||
num
|
num
|
||||||
);
|
);
|
||||||
if (ec == std::errc()) {
|
if (ec == std::errc()) {
|
||||||
|
@ -25,7 +25,7 @@ namespace YYCC::StringHelper {
|
|||||||
int count = std::vsnprintf(
|
int count = std::vsnprintf(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
EncodingHelper::ToNative(format),
|
EncodingHelper::ToOrdinary(format),
|
||||||
args1
|
args1
|
||||||
);
|
);
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
@ -40,9 +40,9 @@ namespace YYCC::StringHelper {
|
|||||||
// however std::vsnprintf already have a trailing NULL, so we plus 1 for it.
|
// however std::vsnprintf already have a trailing NULL, so we plus 1 for it.
|
||||||
strl.resize(count);
|
strl.resize(count);
|
||||||
int write_result = std::vsnprintf(
|
int write_result = std::vsnprintf(
|
||||||
EncodingHelper::ToNative(strl.data()),
|
EncodingHelper::ToOrdinary(strl.data()),
|
||||||
strl.size() + 1,
|
strl.size() + 1,
|
||||||
EncodingHelper::ToNative(format),
|
EncodingHelper::ToOrdinary(format),
|
||||||
args2
|
args2
|
||||||
);
|
);
|
||||||
va_end(args2);
|
va_end(args2);
|
||||||
|
@ -134,7 +134,8 @@ namespace YYCCTestbench {
|
|||||||
Console::Write(YYCC_U8("\t> "));
|
Console::Write(YYCC_U8("\t> "));
|
||||||
|
|
||||||
YYCC::yycc_u8string gotten(Console::ReadLine());
|
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_FloatSetting(YYCC_U8("float-setting"), 0.0f),
|
||||||
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
|
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
|
||||||
m_BoolSetting(YYCC_U8("bool-setting"), false),
|
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_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
|
||||||
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
|
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
|
||||||
&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting
|
&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting
|
||||||
@ -436,7 +437,7 @@ namespace YYCCTestbench {
|
|||||||
// init cfg manager
|
// init cfg manager
|
||||||
TestConfigManager test;
|
TestConfigManager test;
|
||||||
|
|
||||||
// test constrain works
|
// test constraint works
|
||||||
Assert(!test.m_ClampedFloatSetting.Set(2.0f), YYCC_U8("YYCC::ConfigManager::Constraint"));
|
Assert(!test.m_ClampedFloatSetting.Set(2.0f), YYCC_U8("YYCC::ConfigManager::Constraint"));
|
||||||
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::Constraint"));
|
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::Constraint"));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user