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"));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user