Compare commits
13 Commits
v1.0.0
...
06e75924f1
Author | SHA1 | Date | |
---|---|---|---|
06e75924f1 | |||
e374575852 | |||
e2a582e7d2 | |||
588946583c | |||
e1823d4b8e | |||
23b4da95ce | |||
e5b6e8c6c3 | |||
ccb729c718 | |||
44dbbb1c99 | |||
91ba0c22d6 | |||
73ef8af56c | |||
61ad1ff3ce | |||
c15b57d055 |
5
doc/src/config_manager.dox
Normal file
5
doc/src/config_manager.dox
Normal file
@ -0,0 +1,5 @@
|
||||
/**
|
||||
|
||||
\page config_manager Universal Config Manager
|
||||
|
||||
*/
|
78
doc/src/dialog_helper.dox
Normal file
78
doc/src/dialog_helper.dox
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
|
||||
\page dialog_helper Dialog Helper
|
||||
|
||||
Picking files and folders is an important and essential operation under Windows.
|
||||
However the functions picking files and folders are so complex.
|
||||
This helper provides universal dialog picker by simple classes and functions.
|
||||
In following contents we will tell you how to call them.
|
||||
|
||||
This helper is Windows specific.
|
||||
It will be totally invisible if you are in other platforms.
|
||||
|
||||
\section dialog_helper__file_dialog Configure File Dialog
|
||||
|
||||
The first thing is that we should initialize YYCC::DialogHelper::FileDialog,
|
||||
and configure it according to your requirements.
|
||||
|
||||
This class is the data struct representing all aspects of file dialog.
|
||||
It also one of the arguments in final dialog function.
|
||||
|
||||
\code
|
||||
YYCC::DialogHelper::FileDialog params;
|
||||
params.SetOwner(owner_getter());
|
||||
params.SetTitle(YYCC_U8("My File Picker"));
|
||||
params.SetInitFileName(YYCC_U8("test.txt"));
|
||||
params.SetInitDirectory(initial_directory_getter());
|
||||
\endcode
|
||||
|
||||
\subsection dialog_helper__file_dialog__owner Owner
|
||||
|
||||
YYCC::DialogHelper::FileDialog::SetOwner will set owner of this dialog.
|
||||
It accepts a Microsoft defined \c HWND as argument which should be familiar with Windows programmer.
|
||||
If you pass \c NULL to it or skip calling this function, it indicate that there is no owner of this dialog.
|
||||
<I>
|
||||
I don't what whill happend if there is no owner for it.
|
||||
But it would be better to have an owner if possible.
|
||||
</I>
|
||||
|
||||
\subsection dialog_helper__file_dialog__title Title
|
||||
|
||||
YYCC::DialogHelper::FileDialog::SetTitle will set dialog title of this dialog.
|
||||
If you pass \c nullptr or skip calling it,
|
||||
the title of dialog will be filled by system and the function type you calling.
|
||||
For example, the title will be "Open..." if you call open file function,
|
||||
and will be "Save As..." if you call save file function.
|
||||
At the same time, the language of this title filled by system is system UI dependent.
|
||||
It means that you do not need to do any extra I18N work for it.
|
||||
So I suggest you do not set title except you really want to modify title.
|
||||
|
||||
\subsection dialog_helper__file_dialog__init_file_name Initial File Name
|
||||
|
||||
YYCC::DialogHelper::FileDialog::SetInitFileName will set the initial file name presented in dialog file name input box.
|
||||
If you pass \c nullptr or skip calling it, the text in dialog file name input box will be empty.
|
||||
|
||||
User can modify the name presented in input box later.
|
||||
But if you assign this value, the dialog will lose the ability that remember the previous name user input in previous calling.
|
||||
In normal case, dialog will try remembering the file name user input in dialog, and represent it in the next calling.
|
||||
However, if you specify this field, the dialog will always presented your specified value in every calling.
|
||||
|
||||
\subsection dialog_helper__file_dialog__init_directory Initial Directory
|
||||
|
||||
YYCC::DialogHelper::FileDialog::SetInitDirectory will set the initial directory (startup directory) when opening dialog.
|
||||
|
||||
In following cases, initial directory will fall back to system behavior:
|
||||
|
||||
\li Pass \c nullptr to this function.
|
||||
\li Skip calling this function.
|
||||
\li Given directory path is invalid.
|
||||
|
||||
The system default behavior of initial directory is similar with initial file name.
|
||||
The dialog will try remembering the last directory you just entering, and will back into it in the next calling.
|
||||
The directory we meeting in the first launch is system defined.
|
||||
|
||||
\section dialog_helper__file_filters Configure File Filters
|
||||
|
||||
\section dialog_helper__result Create Dialog and Get Result
|
||||
|
||||
*/
|
11
doc/src/encoding_helper.dox
Normal file
11
doc/src/encoding_helper.dox
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
|
||||
\page encoding_helper Encoding Helper
|
||||
|
||||
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 Windows specific convertion between \c WCHAR, UTF8 string and string encoded by other encoding.
|
||||
\li The convertion among UTF8, UTF16 and UTF32.
|
||||
|
||||
*/
|
@ -1,10 +1,55 @@
|
||||
/**
|
||||
|
||||
\mainpage YYCCommonplace Library Manual
|
||||
\mainpage YYCCommonplace Programming Manual
|
||||
|
||||
This manual is organized into the following chapters and appendices:
|
||||
<TABLE CELLPADDING="8" CELLSPACING="0" SUMMARY="TITLE BAR" WIDTH="100%" BORDER="0">
|
||||
<TR>
|
||||
<TD><CENTER>
|
||||
\image html yycc_icon.png
|
||||
</CENTER></TD>
|
||||
<TD><CENTER>
|
||||
<B>YYCCommonplace Programming Manual</B>
|
||||
|
||||
\subpage intro
|
||||
Copyright 2024 by yyc12345.
|
||||
</CENTER></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<TABLE CELLPADDING="8" CELLSPACING="0" SUMMARY="TITLE BAR" WIDTH="100%" BORDER="0">
|
||||
<TR>
|
||||
<TD>
|
||||
This software and manual are provided under the terms of the MIT License.
|
||||
</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<TABLE CELLPADDING="8" CELLSPACING="0" SUMMARY="Table of Contents" WIDTH="100%" BORDER="0">
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
|
||||
<B>General Features</B>
|
||||
|
||||
\li \subpage intro
|
||||
|
||||
\li \subpage library_encoding
|
||||
|
||||
\li \subpage encoding_helper
|
||||
|
||||
\li \subpage string_helper
|
||||
|
||||
<B>Advanced Features</B>
|
||||
|
||||
\li \subpage config_manager
|
||||
|
||||
</TD>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
|
||||
<B>Windows Specific Features</B>
|
||||
|
||||
\li \subpage win_import
|
||||
|
||||
\li \subpage dialog_helper
|
||||
|
||||
</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
*/
|
||||
|
@ -2,10 +2,75 @@
|
||||
|
||||
\page intro Introduction to YYCCommonplace
|
||||
|
||||
work in progress
|
||||
YYCCommonplace, or YYC Commonplace (abbr. YYCC), is a static library providing various useful C++ functions when programming with standard library or Windows environment.
|
||||
|
||||
\section work in progress
|
||||
Actually YYCC provides the functions which I frequently used in my personal projects.
|
||||
Thus I do not need copy these functions from one project to another project.
|
||||
I can write them once and use them everywhere.
|
||||
It's also good for bug fix.
|
||||
If I found bug in these code, I only need to fix it in this project.
|
||||
Otherwise I need to fix them one by one in each project because they share the same code.
|
||||
|
||||
work in progress
|
||||
\section intro__why Why YYCCommonplace
|
||||
|
||||
\subsection intro__why__windows Windows Issues
|
||||
|
||||
I frequently program on Windows environment because the software I programming for, Virtools, is Windows-only software.
|
||||
During programming, I found Windows is super lack in UTF8 supports.
|
||||
Programmer loves UTF8, because it can handle all charcaters over the world in one encoding and is still compatible with C-Style string.
|
||||
However, Windows use a weird way to achieve internationalization, 2 different function trailing, A and W for legacy code and modern code respectively.
|
||||
The worst things is that the char type W trailing function used, \c WCHAR, is defined as 2 bytes long, not 4 bytes long as Linux does (\c wchar_t).
|
||||
It mean that one emoji charcater will be torn into 2 \c WCHAR on Windows because emoji code unit is higher than the manimum value of \c WCHAR.
|
||||
|
||||
Also, there are various issues which should not be presented.
|
||||
For example, Microsoft invents various \e safe standard library functions to prevent possible overflow issues raised by \c std::fgets and etc.
|
||||
also, MSVC may throw weird error when you using some specific standard library functions.
|
||||
You need to define some weird macro to disable this shitty behavior.
|
||||
|
||||
There are various non-standard issue you may faced on Windows programming.
|
||||
All in all, programming on Windows is a tough work.
|
||||
This is one of the reasons why I create this library.
|
||||
I create much wrappers for these weird Windows functions.
|
||||
Thus I can have a similar Linux C++ programming experience on Windows.
|
||||
|
||||
\subsection intro__why__std Standard Library Issues
|
||||
|
||||
The eccentric decision of standard commission also is the reason why I create this library.
|
||||
|
||||
\li C++ standard commission loves to bring one feature with N concepts and P assistant classes.
|
||||
\li C++ standard commission seems doesn't want to bring any features the programmer urgent needed.
|
||||
\li C++ standard commission loves delete programmer loved convenient functions and classes.
|
||||
\li etc...
|
||||
|
||||
There is not a proper way to \e format a string in C++ until C++ 20 (\c std::format).
|
||||
String related functions, such as split, lower, higher, replace, now still not be included in standard library.
|
||||
Programmer loved, easy to used UTF8 procession functions and classes was deprecate now and will be removed in future.
|
||||
|
||||
That's why I create this library.
|
||||
I bring these function in this library.
|
||||
Not industrial level, but easy to use and have enough performance in my project.
|
||||
|
||||
\subsection intro__why__boost Boost Issues
|
||||
|
||||
Bosst is a powerful C++ library. But the shortcoming is overt. It's tooooo big.
|
||||
This drawback will be more obvious considering the bad dependency mechanism of C++.
|
||||
Although the most of Boost sub-library is header-only, but some library still need to link with Boost.
|
||||
It order you download the whole Boost library and extract it in your hard disk.
|
||||
Cost more than half hours, 5+ GB disk space and the life time of your disk.
|
||||
|
||||
The functions belonging to Boost is industrial level.
|
||||
But what I want is not industrial level functions.
|
||||
I only need a function which can barely finish my work. That's enough.
|
||||
I don't need extreme performance. I just want my code works.
|
||||
|
||||
So I create this library, bring some Boost functions with ordinary but not bad implementation.
|
||||
|
||||
\section intro__usage Library Usage
|
||||
|
||||
Before using this library, I suggest you read this manual fully to have a full overview of this library.
|
||||
Otherwise you may make mistake during using this library.
|
||||
I suggest you read this manual from top to bottom in the left tree panel, one by one.
|
||||
|
||||
This library is a static library.
|
||||
|
||||
*/
|
||||
|
191
doc/src/library_encoding.dox
Normal file
191
doc/src/library_encoding.dox
Normal file
@ -0,0 +1,191 @@
|
||||
/**
|
||||
|
||||
\page library_encoding Library Encoding
|
||||
|
||||
Before using this library, you should know the encoding strategy of this library first.
|
||||
In short words, this library use UTF8 encoding everywhere except some special cases,
|
||||
for example, function explicitly order the encoding of input parameters.
|
||||
|
||||
In following content of this article, you will know the details about how we use UTF8 in this library.
|
||||
|
||||
\section library_encoding__utf8_type UTF8 Type
|
||||
|
||||
YYCC uses custom UTF8 char type, string container and string view all over the library, from parameters to return value.
|
||||
Following content will introduce how we define them.
|
||||
|
||||
\subsection library_encoding__utf8_type__char_type Char Type
|
||||
|
||||
YYCC library has its own UTF8 char type, \c yycc_char8_t.
|
||||
This is how we define it:
|
||||
|
||||
\code
|
||||
#if defined(__cpp_char8_t)
|
||||
using yycc_char8_t = char8_t;
|
||||
#else
|
||||
using yycc_char8_t = unsigned char;
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
If your environment (higher or equal to C++ 20) supports \c char8_t provided by standard library, \c yycc_char8_t is just an alias to \c char8_t,
|
||||
otherwise (lower than C++ 20, e.g. C++ 17), \c yycc_char8_t will be defined as \c unsigned \c char like C++ 20 does (this can be seen as a polyfill).
|
||||
|
||||
This means that if you already have used \c char8_t provided by standard library,
|
||||
you do not need to do any extra modification before using this library.
|
||||
Because all types are compatible.
|
||||
|
||||
\subsection library_encoding__utf8_type__container_type String Container and View
|
||||
|
||||
We define string container and string view like this:
|
||||
|
||||
\code
|
||||
using yycc_u8string = std::basic_string<yycc_char8_t>;
|
||||
using yycc_u8string_view = std::basic_string_view<yycc_char8_t>;
|
||||
\endcode
|
||||
|
||||
The real code written in library may be slightly different with this but they have same meanings.
|
||||
|
||||
In \c char8_t environment, they are just the alias to \c std::u8string and \c std::u8string_view respectively.
|
||||
So if you have already used them, no need to any modification for your code before using this library.
|
||||
|
||||
\subsection library_encoding__utf8_type__why Why?
|
||||
|
||||
You may curious why I create a new UTF8 char type, rather than using standard library UTF8 char type directly. There are 2 reasons.
|
||||
|
||||
First, It was too late that I notice I can use standard library UTF8 char type.
|
||||
My UTF8 char type has been used in library everywhere and its tough to fully replace them into standard library UTF8 char type.
|
||||
|
||||
Second, UTF8 related content of standard library is \e volatile.
|
||||
I notice standard library change UTF8 related functions frequently and its API are not stable.
|
||||
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__utf8_literal UTF8 Literal
|
||||
|
||||
String literal is a C++ concept.
|
||||
If you are not familar with it, please browse related article first, such as CppReference.
|
||||
|
||||
\subsection library_encoding__utf8_literal__single Single Literal
|
||||
|
||||
In short words, YYCC allow you declare an UTF8 literal like this:
|
||||
|
||||
\code
|
||||
YYCC_U8("This is UTF8 literal.")
|
||||
\endcode
|
||||
|
||||
YYCC_U8 is macro.
|
||||
You don't need add extra \c u8 prefix in string given to the macro.
|
||||
This macro will do this automatically.
|
||||
|
||||
In detail, this macro do a \c reinterpret_cast to change the type of given argument to \c const \c yycc_char8_t* forcely.
|
||||
This ensure that declared UTF8 literal is compatible with YYCC UTF8 types.
|
||||
|
||||
\subsection library_encoding__utf8_literal__concatenation Literal Concatenation
|
||||
|
||||
YYCC_U8 macro also works for string literal concatenation:
|
||||
|
||||
\code
|
||||
YYCC_U8("Error code: " PRIu32 ". Please contact me.");
|
||||
\endcode
|
||||
|
||||
According to C++ standard for string literal concatenation,
|
||||
<I>"If one of the strings has an encoding prefix and the other does not, the one that does not will be considered to have the same encoding prefix as the other."</I>
|
||||
At the same time, YYCC_U8 macro will automatically add \c u8 prefix for the first component of this string literal concatenation.
|
||||
So the whole string will be UTF8 literal.
|
||||
It also order you should \b not add any prefix for other components of this string literal concatenation.
|
||||
|
||||
\subsection library_encoding__utf8_literal__why Why?
|
||||
|
||||
You may know that C++ standard allows programmer declare an UTF8 literal explicitly by writing code like this:
|
||||
|
||||
\code
|
||||
u8"foo bar"
|
||||
\endcode
|
||||
|
||||
This is okey. But it may incompatible with YYCC UTF8 char type.
|
||||
According to C++ standard, this UTF8 literal syntax will only return \c const \c char8_t* if your C++ standard higher or equal to C++ 20,
|
||||
otherwise it will return \c const \c char*.
|
||||
This behavior cause that you can not assign this UTF8 literal to \c yycc_u8string if you are in the environment which do not support \c char8_t,
|
||||
because their types are different.
|
||||
Thereas you can not use the functions provided by this library because they are all use YYCC defined UTF8 char type.
|
||||
|
||||
\section library_encoding__utf8_pointer UTF8 String Pointer
|
||||
|
||||
String pointer means the raw pointer pointing to a string, such as \c const \c char*, \c char*, \c char32_t* and etc.
|
||||
|
||||
Many legacy code assume \c char* is encoded with UTF8 (the exception is Windows). But \c char* is incompatible with \c yycc_char8_t.
|
||||
YYCC provides YYCC::EncodingHelper::ToUTF8 to resolve this issue. There is an exmaple:
|
||||
|
||||
\code
|
||||
const char* absolutely_is_utf8 = "I confirm this is encoded with UTF8.";
|
||||
const yycc_char8_t* converted = YYCC::EncodingHelper::ToUTF8(absolutely_is_utf8);
|
||||
|
||||
char* mutable_utf8 = const_cast<char*>(absolutely_is_utf8); // This is not safe. Just for example.
|
||||
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 also has ability that convert YYCC UTF8 char type to native char type by YYCC::EncodingHelper::ToNative.
|
||||
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);
|
||||
|
||||
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);
|
||||
\endcode
|
||||
|
||||
Same as YYCC::EncodingHelper::ToUTF8, YYCC::EncodingHelper::ToNative also has 2 overloads to handle const 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?
|
||||
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.
|
||||
There is an exmaple:
|
||||
|
||||
\code
|
||||
std::string native_string("I am UTF8");
|
||||
yycc_u8string yycc_string = YYCC::EncodingHelper::ToUTF8(native_string);
|
||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
||||
\endcode
|
||||
|
||||
Actually, YYCC::EncodingHelper::ToUTF8 accepts a reference to \c std::string_view as argument.
|
||||
However, there is a implicit convertion from \c std::string to \c std::string_view,
|
||||
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,
|
||||
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);
|
||||
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.
|
||||
Try to do your own research and figure out how to use them.
|
||||
It's pretty easy.
|
||||
|
||||
\section library_encoding__windows Warnings to Windows Programmer
|
||||
|
||||
Due to the legacy of MSVC, the encoding of \c char* may not be UTF8 in most cases.
|
||||
If you run the convertion code introduced in this article with the string which is not encoded with UTF8, it may cause undefined behavior.
|
||||
|
||||
To enable UTF8 mode of MSVC, please deliver \c /utf-8 switch to MSVC.
|
||||
Thus you can use the functions introduced in this article safely.
|
||||
Otherwise, you must guarteen that the argument you provided to these functions is encoded by UTF8 manually.
|
||||
|
||||
Linux user do not need care this.
|
||||
Because almost Linux distro use UTF8 in default.
|
||||
|
||||
*/
|
43
doc/src/string_helper.dox
Normal file
43
doc/src/string_helper.dox
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
|
||||
\page string_helper String Helper
|
||||
|
||||
|
||||
\section string_helper_lower_upper Lower Upper
|
||||
|
||||
String helper provides Python-like string lower and upper function.
|
||||
Both lower and upper function have 2 overloads:
|
||||
|
||||
\code
|
||||
yycc_u8string Lower(const yycc_char8_t*);
|
||||
void Lower(yycc_u8string&);
|
||||
\endcode
|
||||
|
||||
First overload accepts a NULL-terminated string as argument and return a \b copy whose content are all the lower case of original string.
|
||||
Second overload accepts a mutable string container as argument and will make all characters stored in it become their lower case.
|
||||
You can choose on of them for your flavor and requirements.
|
||||
Upper also has similar 2 overloads.
|
||||
|
||||
\section string_helper_split Split
|
||||
|
||||
String helper provides Python-like string split function.
|
||||
It has 2 types for you:
|
||||
|
||||
\code
|
||||
std::vector<yycc_u8string> Split(const yycc_u8string_view&, const yycc_char8_t*);
|
||||
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view&, const yycc_char8_t*);
|
||||
\endcode
|
||||
|
||||
All these overloads take a string view as the first argument for the string need to be split.
|
||||
The second argument is a raw string pointer representing the decilmer for splitting.
|
||||
The only difference between these 2 split function are overt according to their names.
|
||||
The first split function will return a list of copied string as its split result.
|
||||
The second split function will return a list of string view as its split result,
|
||||
and it will keep valid as long as the life time of your given string view argument.
|
||||
It also means that the last type will cost less memory if you don't need the copy of original string.
|
||||
|
||||
If the source string (the string need to be split) is empty, or the decilmer is \c nullptr or empty,
|
||||
the result will only has 1 item and this item is source string itself.
|
||||
There is no way that this method return an empty list, except the code is buggy.
|
||||
|
||||
*/
|
69
doc/src/win_import.dox
Normal file
69
doc/src/win_import.dox
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
|
||||
\page win_import Windows Import Guard
|
||||
|
||||
Windows is shitty for the programmer who is familiar with UNIX programming.
|
||||
Due to legacy reason, Windows defines various things which are not compatible with UNIX or standard C++ programming.
|
||||
|
||||
\section win_import__usage Usage
|
||||
|
||||
YYCC has a way to solve the issue introduced above.
|
||||
|
||||
\code
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#include <WinImportPrefix.hpp>
|
||||
#include <Windows.h>
|
||||
#include "other_header_depend_on_windows.h"
|
||||
#include <WinImportSuffix.hpp>
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
The including of WinImportPrefix.hpp and WinImportSuffix.hpp is a pair.
|
||||
They just like a guard bracket the include operation of Windows related headers,
|
||||
to keep all Windows shitty contents will not be leaked outside.
|
||||
|
||||
This guard can solve following issues:
|
||||
|
||||
<UL>
|
||||
<LI>
|
||||
Programmer can not use \c std::max and \c std::min normally.
|
||||
<UL>
|
||||
<LI>Windows defines \c MAX and \c MIN as macros for personal use. This is why this happend.</LI>
|
||||
<LI>Guard defines some special macros to tell Windows do not create these 2 macros.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
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>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
Compiler throw annoy warnings and errors when using specific standard library functions.
|
||||
<UL>
|
||||
<LI>MSVC will throw warnings and errors when you are using Microsoft so-called \e depracted or \e unsafe standard library functions.</LI>
|
||||
<LI>YYCCInternal.hpp, which has been included by this pair, defines some macros to purge these warnings and errors out.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
\section win_import__notes Notes
|
||||
|
||||
If you have other header files which are strongly depend on Windows header,
|
||||
you should put them into this bracket at the same time like example did.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
*/
|
BIN
doc/src/yycc_icon.png
Normal file
BIN
doc/src/yycc_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
@ -5,6 +5,7 @@ target_sources(YYCCommonplace
|
||||
PRIVATE
|
||||
# Sources
|
||||
COMHelper.cpp
|
||||
ConfigManager.cpp
|
||||
ConsoleHelper.cpp
|
||||
DialogHelper.cpp
|
||||
EncodingHelper.cpp
|
||||
@ -21,6 +22,7 @@ FILES
|
||||
# Headers
|
||||
# Common headers
|
||||
COMHelper.hpp
|
||||
ConfigManager.hpp
|
||||
ConsoleHelper.hpp
|
||||
DialogHelper.hpp
|
||||
EncodingHelper.hpp
|
||||
|
148
src/ConfigManager.cpp
Normal file
148
src/ConfigManager.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
#include "ConfigManager.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "IOHelper.hpp"
|
||||
|
||||
namespace YYCC::ConfigManager {
|
||||
|
||||
#pragma region Core Manager
|
||||
|
||||
CoreManager::CoreManager(
|
||||
const yycc_char8_t* cfg_file_path,
|
||||
uint64_t version_identifier,
|
||||
std::initializer_list<AbstractSetting*> settings) :
|
||||
m_CfgFilePath(), m_VersionIdentifier(version_identifier), m_Settings() {
|
||||
// assign cfg path
|
||||
if (cfg_file_path != nullptr)
|
||||
m_CfgFilePath = cfg_file_path;
|
||||
// assign settings
|
||||
for (auto* setting : settings) {
|
||||
m_Settings.try_emplace(setting->GetName(), setting);
|
||||
}
|
||||
}
|
||||
|
||||
bool CoreManager::Load() {
|
||||
// reset all settings first
|
||||
Reset();
|
||||
|
||||
// get file handle
|
||||
auto fs = this->GetFileHandle(YYCC_U8("rb"));
|
||||
if (fs.get() == nullptr) {
|
||||
// if we fail to get, it means that we do not have corresponding cfg file.
|
||||
// all settings should be reset to default value.
|
||||
return true;
|
||||
}
|
||||
|
||||
// fetch version info
|
||||
uint64_t version_info;
|
||||
if (std::fread(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
||||
return false;
|
||||
// check version
|
||||
// if read version is greater than we expected,
|
||||
// it means that this cfg file is created by the program higer than this.
|
||||
// we should not read anything from it.
|
||||
// however, for compaitibility reason, we allow read old cfg data.
|
||||
if (version_info > m_VersionIdentifier)
|
||||
return true;
|
||||
|
||||
// fetch setting item from file
|
||||
yycc_u8string name_cache;
|
||||
while (true) {
|
||||
// try fetch setting name
|
||||
// fetch name length
|
||||
size_t name_length;
|
||||
if (std::fread(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length)) {
|
||||
// we also check whether reach EOF at there.
|
||||
if (std::feof(fs.get())) break;
|
||||
else return false;
|
||||
}
|
||||
// fetch name body
|
||||
name_cache.resize(name_length);
|
||||
if (std::fread(name_cache.data(), 1u, name_length, fs.get()) != name_length)
|
||||
return false;
|
||||
|
||||
// get setting data length
|
||||
size_t data_length;
|
||||
if (std::fread(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
||||
return false;
|
||||
|
||||
// get matched setting first
|
||||
const auto& found = m_Settings.find(name_cache);
|
||||
if (found != m_Settings.end()) {
|
||||
// found. read data for it
|
||||
found->second->ResizeData(data_length);
|
||||
if (std::fread(found->second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
||||
return false;
|
||||
// call user defined load function
|
||||
// if fail to parse, reset to default value
|
||||
if (!found->second->UserLoad())
|
||||
found->second->UserReset();
|
||||
} else {
|
||||
// fail to find. skip this unknown setting
|
||||
if (fseek(fs.get(), static_cast<long>(data_length), SEEK_CUR) != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreManager::Save() {
|
||||
// get file handle
|
||||
auto fs = this->GetFileHandle(YYCC_U8("wb"));
|
||||
// if we fail to get, return false.
|
||||
if (fs == nullptr) return false;
|
||||
|
||||
// write config data
|
||||
uint64_t version_info = m_VersionIdentifier;
|
||||
if (std::fwrite(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
||||
return false;
|
||||
|
||||
// iterate all data for writing
|
||||
for (const auto& pair : m_Settings) {
|
||||
// do user defined save
|
||||
// if failed, skip this setting
|
||||
if (!pair.second->UserSave())
|
||||
continue;
|
||||
|
||||
// write setting name
|
||||
// write name length
|
||||
size_t name_length = pair.first.size();
|
||||
if (std::fwrite(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length))
|
||||
return false;
|
||||
// write name body
|
||||
if (std::fwrite(pair.first.c_str(), 1u, name_length, fs.get()) != name_length)
|
||||
return false;
|
||||
|
||||
// write setting daat
|
||||
// write data length
|
||||
size_t data_length = pair.second->GetDataSize();
|
||||
if (std::fwrite(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
||||
return false;
|
||||
// write data body
|
||||
if (std::fwrite(pair.second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
||||
return false;
|
||||
}
|
||||
|
||||
// all settings done, return true
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreManager::Reset() {
|
||||
for (const auto& pair : m_Settings) {
|
||||
pair.second->UserReset();
|
||||
}
|
||||
}
|
||||
|
||||
CoreManager::FileHandleGuard_t CoreManager::GetFileHandle(const yycc_char8_t* mode) const {
|
||||
return CoreManager::FileHandleGuard_t(
|
||||
IOHelper::UTF8FOpen(this->m_CfgFilePath.c_str(), mode),
|
||||
[](FILE* fs) -> void {
|
||||
if (fs != nullptr) std::fclose(fs);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
203
src/ConfigManager.hpp
Normal file
203
src/ConfigManager.hpp
Normal file
@ -0,0 +1,203 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace YYCC::ConfigManager {
|
||||
|
||||
template<typename _Ty>
|
||||
struct Constrain {
|
||||
using CheckFct_t = std::function<bool(const _Ty&)>;
|
||||
//using CorrectFct_t = std::function<_Ty(const _Ty&)>;
|
||||
CheckFct_t m_CheckFct;
|
||||
//CorrectFct_t m_CorrectFct;
|
||||
|
||||
bool IsValid() const {
|
||||
return m_CheckFct != nullptr/* && m_CorrectFct != nullptr*/;
|
||||
}
|
||||
};
|
||||
|
||||
namespace ConstrainPresets {
|
||||
|
||||
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) {
|
||||
if (min_value > max_value)
|
||||
throw std::invalid_argument("invalid min max value for NumberRangeConstrain");
|
||||
return Constrain<_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); }*/
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AbstractSetting {
|
||||
friend class CoreManager;
|
||||
public:
|
||||
AbstractSetting(const yycc_char8_t* name) : m_Name(), m_RawData() {
|
||||
if (name != nullptr) m_Name = name;
|
||||
}
|
||||
virtual ~AbstractSetting() {}
|
||||
|
||||
// Name interface
|
||||
public:
|
||||
const yycc_u8string& GetName() const { return m_Name; }
|
||||
private:
|
||||
yycc_u8string m_Name;
|
||||
|
||||
// User Implementations
|
||||
protected:
|
||||
virtual bool UserLoad() = 0;
|
||||
virtual bool UserSave() = 0;
|
||||
virtual void UserReset() = 0;
|
||||
|
||||
// Buffer related functions
|
||||
protected:
|
||||
void ResizeData(size_t new_size) { m_RawData.resize(new_size); }
|
||||
const void* GetDataPtr() const { return m_RawData.data(); }
|
||||
void* GetDataPtr() { return m_RawData.data(); }
|
||||
size_t GetDataSize() const { return m_RawData.size(); }
|
||||
private:
|
||||
std::vector<uint8_t> m_RawData;
|
||||
};
|
||||
|
||||
class CoreManager {
|
||||
public:
|
||||
CoreManager(
|
||||
const yycc_char8_t* cfg_file_path,
|
||||
uint64_t version_identifier,
|
||||
std::initializer_list<AbstractSetting*> settings);
|
||||
~CoreManager() {}
|
||||
|
||||
// Core functions
|
||||
public:
|
||||
bool Load();
|
||||
bool Save();
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
using FileHandleGuard_t = std::unique_ptr<FILE, std::function<void(FILE*)>>;
|
||||
FileHandleGuard_t GetFileHandle(const yycc_char8_t* mode) const;
|
||||
|
||||
yycc_u8string m_CfgFilePath;
|
||||
uint64_t m_VersionIdentifier;
|
||||
std::map<yycc_u8string, AbstractSetting*> m_Settings;
|
||||
};
|
||||
|
||||
#pragma region Setting Presets
|
||||
|
||||
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) {}
|
||||
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))
|
||||
return false;
|
||||
// assign data
|
||||
m_Data = new_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool UserLoad() override {
|
||||
// read data
|
||||
if (sizeof(m_Data) != GetDataSize())
|
||||
return false;
|
||||
m_Data = *reinterpret_cast<const _Ty*>(GetDataPtr());
|
||||
// check data
|
||||
if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
virtual bool UserSave() override {
|
||||
// write data
|
||||
ResizeData(sizeof(m_Data));
|
||||
*reinterpret_cast<_Ty*>(GetDataPtr()) = m_Data;
|
||||
return true;
|
||||
}
|
||||
virtual void UserReset() override {
|
||||
m_Data = m_DefaultData;
|
||||
}
|
||||
|
||||
_Ty m_Data, m_DefaultData;
|
||||
Constrain<_Ty> m_Constrain;
|
||||
};
|
||||
|
||||
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) {
|
||||
if (default_value != nullptr) {
|
||||
m_Data = default_value;
|
||||
m_DefaultData = default_value;
|
||||
}
|
||||
}
|
||||
virtual ~StringSetting() {}
|
||||
|
||||
const yycc_u8string& Get() const { return m_Data; }
|
||||
bool Set(const yycc_char8_t* new_data) {
|
||||
// check data validation
|
||||
if (new_data == nullptr)
|
||||
return false;
|
||||
if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data))
|
||||
return false;
|
||||
// assign data
|
||||
m_Data = new_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool UserLoad() override {
|
||||
// read string length
|
||||
size_t string_length;
|
||||
if (GetDataSize() < sizeof(string_length))
|
||||
return false;
|
||||
string_length = *reinterpret_cast<const size_t*>(GetDataPtr());
|
||||
// read string body
|
||||
if (GetDataSize() != sizeof(string_length) + string_length)
|
||||
return false;
|
||||
m_Data.assign(
|
||||
reinterpret_cast<const yycc_char8_t*>(static_cast<const uint8_t*>(GetDataPtr()) + sizeof(string_length)),
|
||||
string_length
|
||||
);
|
||||
// check data
|
||||
if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
virtual bool UserSave() override {
|
||||
// allocate result buffer
|
||||
size_t string_length = m_Data.size();
|
||||
ResizeData(sizeof(string_length) + string_length);
|
||||
// get pointer
|
||||
uint8_t* ptr = static_cast<uint8_t*>(GetDataPtr());
|
||||
// assign string length
|
||||
*reinterpret_cast<size_t*>(ptr) = string_length;
|
||||
// assign string body
|
||||
std::memcpy(ptr + sizeof(string_length), m_Data.data(), string_length);
|
||||
return true;
|
||||
}
|
||||
virtual void UserReset() override {
|
||||
m_Data = m_DefaultData;
|
||||
}
|
||||
|
||||
yycc_u8string m_Data, m_DefaultData;
|
||||
Constrain<yycc_u8string> m_Constrain;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
@ -54,7 +54,7 @@ namespace YYCC::ConsoleHelper {
|
||||
*/
|
||||
|
||||
template<bool _bIsConsole>
|
||||
static std::string WinConsoleRead(HANDLE hStdIn) {
|
||||
static yycc_u8string WinConsoleRead(HANDLE hStdIn) {
|
||||
using _TChar = std::conditional_t<_bIsConsole, wchar_t, char>;
|
||||
|
||||
// Prepare an internal buffer because the read data may not be fully used.
|
||||
@ -113,21 +113,21 @@ namespace YYCC::ConsoleHelper {
|
||||
}
|
||||
|
||||
// post-process for return value
|
||||
std::string real_return_buffer;
|
||||
yycc_u8string real_return_buffer;
|
||||
if constexpr (_bIsConsole) {
|
||||
// console mode need convert wchar to utf8
|
||||
YYCC::EncodingHelper::WcharToUTF8(return_buffer.c_str(), real_return_buffer);
|
||||
YYCC::EncodingHelper::WcharToUTF8(return_buffer, real_return_buffer);
|
||||
} else {
|
||||
// non-console just copt the result
|
||||
real_return_buffer = return_buffer;
|
||||
real_return_buffer = EncodingHelper::ToUTF8(return_buffer);
|
||||
}
|
||||
// every mode need delete \r words
|
||||
YYCC::StringHelper::Replace(real_return_buffer, "\r", "");
|
||||
YYCC::StringHelper::Replace(real_return_buffer, YYCC_U8("\r"), YYCC_U8(""));
|
||||
// return value
|
||||
return real_return_buffer;
|
||||
}
|
||||
|
||||
static void WinConsoleWrite(const std::string& strl, bool to_stderr) {
|
||||
static void WinConsoleWrite(const yycc_u8string& strl, bool to_stderr) {
|
||||
// Prepare some Win32 variables
|
||||
// fetch stdout handle first
|
||||
HANDLE hStdOut = GetStdHandle(to_stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
|
||||
@ -139,7 +139,7 @@ namespace YYCC::ConsoleHelper {
|
||||
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
||||
// console handle, use WriteConsoleW.
|
||||
// convert utf8 string to wide char first
|
||||
std::wstring wstrl(YYCC::EncodingHelper::UTF8ToWchar(strl.c_str()));
|
||||
std::wstring wstrl(YYCC::EncodingHelper::UTF8ToWchar(strl));
|
||||
size_t wstrl_size = wstrl.size();
|
||||
// write string with size check
|
||||
if (wstrl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
@ -149,7 +149,7 @@ namespace YYCC::ConsoleHelper {
|
||||
// anything else, use WriteFile instead.
|
||||
// WriteFile do not need extra convertion, because it is direct writing.
|
||||
// check whether string length is overflow
|
||||
size_t strl_size = strl.size() * sizeof(std::string::value_type);
|
||||
size_t strl_size = strl.size() * sizeof(yycc_u8string::value_type);
|
||||
// write string with size check
|
||||
if (strl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
WriteFile(hStdOut, strl.c_str(), static_cast<DWORD>(strl_size), &dwWrittenNumberOfChars, NULL);
|
||||
@ -176,7 +176,7 @@ namespace YYCC::ConsoleHelper {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string ReadLine() {
|
||||
yycc_u8string ReadLine() {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// get stdin mode
|
||||
@ -188,24 +188,24 @@ namespace YYCC::ConsoleHelper {
|
||||
} else {
|
||||
return WinConsoleRead<false>(hStdIn);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
// in linux, directly use C++ function to fetch.
|
||||
std::string cmd;
|
||||
if (std::getline(std::cin, cmd).fail()) cmd.clear();
|
||||
return cmd;
|
||||
return EncodingHelper::ToUTF8(cmd);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
template<bool bNeedFmt, bool bIsErr, bool bHasEOL>
|
||||
static void RawWrite(const char* u8_fmt, va_list argptr) {
|
||||
static void RawWrite(const yycc_char8_t* u8_fmt, va_list argptr) {
|
||||
// Buiild string need to be written first
|
||||
// If no format string or plain string for writing, return.
|
||||
if (u8_fmt == nullptr) return;
|
||||
// Build or simply copy string
|
||||
std::string strl;
|
||||
yycc_u8string strl;
|
||||
if constexpr (bNeedFmt) {
|
||||
// treat as format string
|
||||
va_list argcpy;
|
||||
@ -218,62 +218,62 @@ namespace YYCC::ConsoleHelper {
|
||||
}
|
||||
// Checkout whether add EOL
|
||||
if constexpr (bHasEOL) {
|
||||
strl += "\n";
|
||||
strl += YYCC_U8("\n");
|
||||
}
|
||||
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
// call Windows specific writer
|
||||
WinConsoleWrite(strl, bIsErr);
|
||||
#else
|
||||
// in linux, directly use C function to write.
|
||||
std::fputs(strl.c_str(), bIsErr ? stderr : stdout);
|
||||
std::fputs(EncodingHelper::ToNative(strl.c_str()), bIsErr ? stderr : stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Format(const char* u8_fmt, ...) {
|
||||
void Format(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, false, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void FormatLine(const char* u8_fmt, ...) {
|
||||
void FormatLine(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, false, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void Write(const char* u8_strl) {
|
||||
void Write(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, false, false>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void WriteLine(const char* u8_strl) {
|
||||
void WriteLine(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, false, true>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void ErrFormat(const char* u8_fmt, ...) {
|
||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, true, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void ErrFormatLine(const char* u8_fmt, ...) {
|
||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, true, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void ErrWrite(const char* u8_strl) {
|
||||
void ErrWrite(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, true, false>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void ErrWriteLine(const char* u8_strl) {
|
||||
void ErrWriteLine(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, true, true>(u8_strl, empty);
|
||||
}
|
||||
|
@ -124,52 +124,52 @@ namespace YYCC::ConsoleHelper {
|
||||
* This function also can be used as ordering user press Enter key by
|
||||
* simply calling this function and ignoring its return value.
|
||||
*/
|
||||
std::string ReadLine();
|
||||
yycc_u8string ReadLine();
|
||||
|
||||
/**
|
||||
* @brief Universal console write function with format feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void Format(const char* u8_fmt, ...);
|
||||
void Format(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console write function with format and auto EOL feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void FormatLine(const char* u8_fmt, ...);
|
||||
void FormatLine(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console write function.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void Write(const char* u8_strl);
|
||||
void Write(const yycc_char8_t* u8_strl);
|
||||
/**
|
||||
* @brief Universal console write function with auto EOL feature.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void WriteLine(const char* u8_strl);
|
||||
void WriteLine(const yycc_char8_t* u8_strl);
|
||||
|
||||
/**
|
||||
* @brief Universal console error write function with format and feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void ErrFormat(const char* u8_fmt, ...);
|
||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console error write function with format and auto EOL feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void ErrFormatLine(const char* u8_fmt, ...);
|
||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console error write function.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void ErrWrite(const char* u8_strl);
|
||||
void ErrWrite(const yycc_char8_t* u8_strl);
|
||||
/**
|
||||
* @brief Universal console error write function with auto EOL feature.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void ErrWriteLine(const char* u8_strl);
|
||||
void ErrWriteLine(const yycc_char8_t* u8_strl);
|
||||
|
||||
}
|
||||
|
@ -8,16 +8,16 @@ namespace YYCC::DialogHelper {
|
||||
|
||||
#pragma region FileFilters
|
||||
|
||||
bool FileFilters::Add(const char* filter_name, std::initializer_list<const char*> il) {
|
||||
bool FileFilters::Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il) {
|
||||
// assign filter name
|
||||
if (filter_name == nullptr) return false;
|
||||
FilterName name(filter_name);
|
||||
|
||||
// assign filter patterns
|
||||
FilterModes modes;
|
||||
for (const char* pattern : il) {
|
||||
for (const yycc_char8_t* pattern : il) {
|
||||
if (pattern != nullptr)
|
||||
modes.emplace_back(std::string(pattern));
|
||||
modes.emplace_back(yycc_u8string(pattern));
|
||||
}
|
||||
|
||||
// check filter patterns
|
||||
@ -36,13 +36,13 @@ namespace YYCC::DialogHelper {
|
||||
for (const auto& it : m_Filters) {
|
||||
// convert name to wchar
|
||||
WinFileFilters::WinFilterName name;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(it.first.c_str(), name))
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(it.first, name))
|
||||
return false;
|
||||
|
||||
// convert pattern and join them
|
||||
std::string joined_modes(YYCC::StringHelper::Join(it.second, ";"));
|
||||
yycc_u8string joined_modes(YYCC::StringHelper::Join(it.second, YYCC_U8(";")));
|
||||
WinFileFilters::WinFilterModes modes;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes.c_str(), modes))
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes, modes))
|
||||
return false;
|
||||
|
||||
// append new pair
|
||||
@ -94,12 +94,12 @@ namespace YYCC::DialogHelper {
|
||||
|
||||
// build title and init file name
|
||||
if (m_HasTitle) {
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_Title.c_str(), win_result.m_WinTitle))
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_Title, win_result.m_WinTitle))
|
||||
return false;
|
||||
win_result.m_HasTitle = true;
|
||||
}
|
||||
if (m_HasInitFileName) {
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitFileName.c_str(), win_result.m_WinInitFileName))
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitFileName, win_result.m_WinInitFileName))
|
||||
return false;
|
||||
win_result.m_HasInitFileName = true;
|
||||
}
|
||||
@ -108,7 +108,7 @@ namespace YYCC::DialogHelper {
|
||||
if (m_HasInitDirectory) {
|
||||
// convert to wpath
|
||||
std::wstring w_init_directory;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitDirectory.c_str(), w_init_directory))
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitDirectory, w_init_directory))
|
||||
return false;
|
||||
|
||||
// fetch IShellItem*
|
||||
@ -143,7 +143,7 @@ namespace YYCC::DialogHelper {
|
||||
* @return True if success, otherwise false.
|
||||
* @remarks This is an assist function of CommonFileDialog.
|
||||
*/
|
||||
static bool ExtractDisplayName(IShellItem* item, std::string& ret) {
|
||||
static bool ExtractDisplayName(IShellItem* item, yycc_u8string& ret) {
|
||||
// fetch display name from IShellItem*
|
||||
LPWSTR _name;
|
||||
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name);
|
||||
@ -168,7 +168,7 @@ namespace YYCC::DialogHelper {
|
||||
* @remarks This function is the real underlying function of all dialog functions.
|
||||
*/
|
||||
template<CommonFileDialogType EDialogType>
|
||||
static bool CommonFileDialog(const FileDialog& params, std::vector<std::string>& ret) {
|
||||
static bool CommonFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
|
||||
// prepare result variable
|
||||
HRESULT hr;
|
||||
@ -289,7 +289,7 @@ namespace YYCC::DialogHelper {
|
||||
COMHelper::SmartIShellItem result_item(_item);
|
||||
|
||||
// extract display name
|
||||
std::string result_name;
|
||||
yycc_u8string result_name;
|
||||
if (!ExtractDisplayName(result_item.get(), result_name))
|
||||
return false;
|
||||
|
||||
@ -326,7 +326,7 @@ namespace YYCC::DialogHelper {
|
||||
COMHelper::SmartIShellItem result_item(_item);
|
||||
|
||||
// extract display name
|
||||
std::string result_name;
|
||||
yycc_u8string result_name;
|
||||
if (!ExtractDisplayName(result_item.get(), result_name))
|
||||
return false;
|
||||
|
||||
@ -347,24 +347,24 @@ namespace YYCC::DialogHelper {
|
||||
|
||||
#pragma region Wrapper Functions
|
||||
|
||||
bool OpenFileDialog(const FileDialog& params, std::string& ret) {
|
||||
std::vector<std::string> cache;
|
||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFile>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
}
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<std::string>& ret) {
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
||||
return CommonFileDialog<CommonFileDialogType::OpenMultipleFiles>(params, ret);
|
||||
}
|
||||
bool SaveFileDialog(const FileDialog& params, std::string& ret) {
|
||||
std::vector<std::string> cache;
|
||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::SaveFile>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
}
|
||||
|
||||
bool OpenFolderDialog(const FileDialog& params, std::string& ret) {
|
||||
std::vector<std::string> cache;
|
||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFolder>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
|
@ -58,14 +58,14 @@ namespace YYCC::DialogHelper {
|
||||
* @brief Add a filter pair in file types list.
|
||||
* @param filter_name[in] The friendly name of the filter.
|
||||
* @param il[in] A C++ initialize list.
|
||||
* Every entries must be `const char*` represent a single filter pattern.
|
||||
* Every entries must be `const yycc_char8_t*` represent a single filter pattern.
|
||||
* The list at least should have one valid pattern.
|
||||
* This function will not validate these filter patterns, so please write them carefully.
|
||||
* @return True if added success, otherwise false.
|
||||
* @remarks This function allow you register multiple filter patterns for single friendly name.
|
||||
* For example: `Add("Microsoft Word (*.doc; *.docx)", {"*.doc", "*.docx"})`
|
||||
*/
|
||||
bool Add(const char* filter_name, std::initializer_list<const char*> il);
|
||||
bool Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il);
|
||||
/**
|
||||
* @brief Clear filter pairs for following re-use.
|
||||
*/
|
||||
@ -85,8 +85,8 @@ namespace YYCC::DialogHelper {
|
||||
bool Generate(WinFileFilters& win_result) const;
|
||||
|
||||
protected:
|
||||
using FilterModes = std::vector<std::string>;
|
||||
using FilterName = std::string;
|
||||
using FilterModes = std::vector<yycc_u8string>;
|
||||
using FilterName = yycc_u8string;
|
||||
using FilterPair = std::pair<FilterName, FilterModes>;
|
||||
|
||||
std::vector<FilterPair> m_Filters;
|
||||
@ -159,7 +159,7 @@ namespace YYCC::DialogHelper {
|
||||
m_HasTitle(false), m_HasInitFileName(false), m_HasInitDirectory(false) {}
|
||||
|
||||
void SetOwner(HWND owner) { m_Owner = owner; }
|
||||
void SetTitle(const char* title) {
|
||||
void SetTitle(const yycc_char8_t* title) {
|
||||
if (m_HasTitle = title != nullptr)
|
||||
m_Title = title;
|
||||
}
|
||||
@ -167,11 +167,11 @@ namespace YYCC::DialogHelper {
|
||||
return m_FileTypes;
|
||||
}
|
||||
void SetDefaultFileTypeIndex(size_t idx) { m_DefaultFileTypeIndex = idx; }
|
||||
void SetInitFileName(const char* init_filename) {
|
||||
void SetInitFileName(const yycc_char8_t* init_filename) {
|
||||
if (m_HasInitFileName = init_filename != nullptr)
|
||||
m_InitFileName = init_filename;
|
||||
}
|
||||
void SetInitDirectory(const char* init_dir) {
|
||||
void SetInitDirectory(const yycc_char8_t* init_dir) {
|
||||
if (m_HasInitDirectory = init_dir != nullptr)
|
||||
m_InitDirectory = init_dir;
|
||||
}
|
||||
@ -200,7 +200,7 @@ namespace YYCC::DialogHelper {
|
||||
protected:
|
||||
HWND m_Owner;
|
||||
bool m_HasTitle, m_HasInitFileName, m_HasInitDirectory;
|
||||
std::string m_Title, m_InitFileName, m_InitDirectory;
|
||||
yycc_u8string m_Title, m_InitFileName, m_InitDirectory;
|
||||
FileFilters m_FileTypes;
|
||||
/**
|
||||
* @brief The default selected file type in dialog
|
||||
@ -210,11 +210,11 @@ namespace YYCC::DialogHelper {
|
||||
size_t m_DefaultFileTypeIndex;
|
||||
};
|
||||
|
||||
bool OpenFileDialog(const FileDialog& params, std::string& ret);
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<std::string>& ret);
|
||||
bool SaveFileDialog(const FileDialog& params, std::string& ret);
|
||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret);
|
||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
|
||||
bool OpenFolderDialog(const FileDialog& params, std::string& ret);
|
||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
|
||||
}
|
||||
|
||||
|
@ -4,88 +4,224 @@
|
||||
|
||||
namespace YYCC::EncodingHelper {
|
||||
|
||||
#pragma region UTF8 Native Convertion
|
||||
|
||||
const yycc_char8_t* ToUTF8(const char* src) {
|
||||
return reinterpret_cast<const yycc_char8_t*>(src);
|
||||
}
|
||||
yycc_char8_t* ToUTF8(char* src) {
|
||||
return reinterpret_cast<yycc_char8_t*>(src);
|
||||
}
|
||||
yycc_u8string ToUTF8(const std::string_view& src) {
|
||||
return yycc_u8string(reinterpret_cast<const yycc_char8_t*>(src.data()), src.size());
|
||||
}
|
||||
yycc_u8string_view ToUTF8View(const std::string_view& src) {
|
||||
return yycc_u8string_view(reinterpret_cast<const yycc_char8_t*>(src.data()), src.size());
|
||||
}
|
||||
|
||||
const char* ToNative(const yycc_char8_t* src) {
|
||||
return reinterpret_cast<const char*>(src);
|
||||
}
|
||||
char* ToNative(yycc_char8_t* src) {
|
||||
return reinterpret_cast<char*>(src);
|
||||
}
|
||||
std::string ToNative(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) {
|
||||
return std::string_view(reinterpret_cast<const char*>(src.data()), src.size());
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
/* Define some assistant macros for easy writing. */
|
||||
|
||||
#define CONVFCT_TYPE2(fct_name, src_char_type, dst_char_type, ...) if (src == nullptr) return false; \
|
||||
std::basic_string_view<src_char_type> cache(src); \
|
||||
return fct_name(cache, dst, ##__VA_ARGS__);
|
||||
|
||||
#define CONVFCT_TYPE3(fct_name, src_char_type, dst_char_type, ...) std::basic_string<dst_char_type> ret; \
|
||||
if (!fct_name(src, ret, ##__VA_ARGS__)) ret.clear(); \
|
||||
return ret;
|
||||
|
||||
#define CONVFCT_TYPE4(fct_name, src_char_type, dst_char_type, ...) std::basic_string<dst_char_type> ret; \
|
||||
if (!fct_name(src, ret, ##__VA_ARGS__)) ret.clear(); \
|
||||
return ret;
|
||||
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
bool WcharToChar(const wchar_t* src, std::string& dest, UINT codepage) {
|
||||
int count, write_result;
|
||||
#pragma region WcharToChar
|
||||
|
||||
bool WcharToChar(const std::wstring_view& src, std::string& dst, UINT code_page) {
|
||||
// if src is empty, direct output
|
||||
if (src.empty()) {
|
||||
dst.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
//converter to CHAR
|
||||
count = WideCharToMultiByte(codepage, 0, reinterpret_cast<LPCWCH>(src), -1, NULL, 0, NULL, NULL);
|
||||
if (count <= 0) return false;
|
||||
// init WideCharToMultiByte used variables
|
||||
// setup src pointer
|
||||
LPCWCH lpWideCharStr = reinterpret_cast<LPCWCH>(src.data());
|
||||
// check whether source string is too large.
|
||||
size_t cSrcSize = src.size();
|
||||
if (cSrcSize > std::numeric_limits<int>::max()) return false;
|
||||
int cchWideChar = static_cast<int>(src.size());
|
||||
|
||||
dest.resize(count - 1);
|
||||
write_result = WideCharToMultiByte(codepage, 0, reinterpret_cast<LPCWCH>(src), -1, reinterpret_cast<LPSTR>(dest.data()), count, NULL, NULL);
|
||||
// do convertion
|
||||
// do a dry-run first to fetch desired size.
|
||||
int desired_size = WideCharToMultiByte(code_page, 0, lpWideCharStr, cchWideChar, NULL, 0, NULL, NULL);
|
||||
if (desired_size <= 0) return false;
|
||||
// resize dest for receiving result
|
||||
dst.resize(static_cast<size_t>(desired_size));
|
||||
// do real convertion
|
||||
int write_result = WideCharToMultiByte(code_page, 0, lpWideCharStr, cchWideChar, reinterpret_cast<LPSTR>(dst.data()), desired_size, NULL, NULL);
|
||||
if (write_result <= 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool WcharToUTF8(const wchar_t* src, std::string& dest) {
|
||||
return WcharToChar(src, dest, CP_UTF8);
|
||||
bool WcharToChar(const wchar_t* src, std::string& dst, UINT code_page) {
|
||||
CONVFCT_TYPE2(WcharToChar, wchar_t, char, code_page);
|
||||
}
|
||||
std::string WcharToChar(const wchar_t* src, UINT codepage) {
|
||||
std::string ret;
|
||||
if (!WcharToChar(src, ret, codepage)) ret.clear();
|
||||
return ret;
|
||||
std::string WcharToChar(const std::wstring_view& src, UINT code_page) {
|
||||
CONVFCT_TYPE3(WcharToChar, wchar_t, char, code_page);
|
||||
}
|
||||
std::string WcharToUTF8(const wchar_t* src) {
|
||||
return WcharToChar(src, CP_UTF8);
|
||||
std::string WcharToChar(const wchar_t* src, UINT code_page) {
|
||||
CONVFCT_TYPE4(WcharToChar, wchar_t, char, code_page);
|
||||
}
|
||||
|
||||
bool CharToWchar(const char* src, std::wstring& dest, UINT codepage) {
|
||||
int wcount, write_result;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CharToWchar
|
||||
|
||||
// convert to WCHAR
|
||||
wcount = MultiByteToWideChar(codepage, 0, reinterpret_cast<LPCCH>(src), -1, NULL, 0);
|
||||
if (wcount <= 0) return false;
|
||||
bool CharToWchar(const std::string_view& src, std::wstring& dst, UINT code_page) {
|
||||
// if src is empty, direct output
|
||||
if (src.empty()) {
|
||||
dst.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
dest.resize(wcount - 1);
|
||||
write_result = MultiByteToWideChar(codepage, 0, reinterpret_cast<LPCCH>(src), -1, reinterpret_cast<LPWSTR>(dest.data()), wcount);
|
||||
// init WideCharToMultiByte used variables
|
||||
// setup src pointer
|
||||
LPCCH lpMultiByteStr = reinterpret_cast<LPCCH>(src.data());
|
||||
// check whether source string is too large.
|
||||
size_t cSrcSize = src.size();
|
||||
if (cSrcSize > std::numeric_limits<int>::max()) return false;
|
||||
int cbMultiByte = static_cast<int>(src.size());
|
||||
|
||||
// do convertion
|
||||
// do a dry-run first to fetch desired size.
|
||||
int desired_size = MultiByteToWideChar(code_page, 0, lpMultiByteStr, cbMultiByte, NULL, 0);
|
||||
if (desired_size <= 0) return false;
|
||||
// resize dest for receiving result
|
||||
dst.resize(static_cast<size_t>(desired_size));
|
||||
// do real convertion
|
||||
int write_result = MultiByteToWideChar(code_page, 0, lpMultiByteStr, cbMultiByte, reinterpret_cast<LPWSTR>(dst.data()), desired_size);
|
||||
if (write_result <= 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool UTF8ToWchar(const char* src, std::wstring& dest) {
|
||||
return CharToWchar(src, dest, CP_UTF8);
|
||||
bool CharToWchar(const char* src, std::wstring& dst, UINT code_page) {
|
||||
CONVFCT_TYPE2(CharToWchar, char, wchar_t, code_page);
|
||||
}
|
||||
std::wstring CharToWchar(const char* src, UINT codepage) {
|
||||
std::wstring ret;
|
||||
if (!CharToWchar(src, ret, codepage)) ret.clear();
|
||||
return ret;
|
||||
std::wstring CharToWchar(const std::string_view& src, UINT code_page) {
|
||||
CONVFCT_TYPE3(CharToWchar, char, wchar_t, code_page);
|
||||
}
|
||||
std::wstring UTF8ToWchar(const char* src) {
|
||||
return CharToWchar(src, CP_UTF8);
|
||||
std::wstring CharToWchar(const char* src, UINT code_page) {
|
||||
CONVFCT_TYPE4(CharToWchar, char, wchar_t, code_page);
|
||||
}
|
||||
|
||||
bool CharToChar(const char* src, std::string& dest, UINT src_codepage, UINT dest_codepage) {
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CharToChar
|
||||
|
||||
bool CharToChar(const std::string_view& src, std::string& dst, UINT src_code_page, UINT dst_code_page) {
|
||||
std::wstring intermediary;
|
||||
if (!CharToWchar(src, intermediary, src_codepage)) return false;
|
||||
if (!WcharToChar(intermediary.c_str(), dest, dest_codepage)) return false;
|
||||
if (!CharToWchar(src, intermediary, src_code_page)) return false;
|
||||
if (!WcharToChar(intermediary, dst, dst_code_page)) return false;
|
||||
return true;
|
||||
}
|
||||
std::string CharToChar(const char* src, UINT src_codepage, UINT dest_codepage) {
|
||||
std::string ret;
|
||||
if (!CharToChar(src, ret, src_codepage, dest_codepage)) ret.clear();
|
||||
return ret;
|
||||
bool CharToChar(const char* src, std::string& dst, UINT src_code_page, UINT dst_code_page) {
|
||||
CONVFCT_TYPE2(CharToChar, char, char, src_code_page, dst_code_page);
|
||||
}
|
||||
std::string CharToChar(const std::string_view& src, UINT src_code_page, UINT dst_code_page) {
|
||||
CONVFCT_TYPE3(CharToChar, char, char, src_code_page, dst_code_page);
|
||||
}
|
||||
std::string CharToChar(const char* src, UINT src_code_page, UINT dst_code_page) {
|
||||
CONVFCT_TYPE4(CharToChar, char, char, src_code_page, dst_code_page);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region WcharToUTF8
|
||||
|
||||
bool WcharToUTF8(const std::wstring_view& src, yycc_u8string& dst) {
|
||||
std::string adapted_dst;
|
||||
bool ret = WcharToChar(src, adapted_dst, CP_UTF8);
|
||||
if (ret) dst = ToUTF8(adapted_dst);
|
||||
return ret;
|
||||
}
|
||||
bool WcharToUTF8(const wchar_t* src, yycc_u8string& dst) {
|
||||
CONVFCT_TYPE2(WcharToUTF8, wchar_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string WcharToUTF8(const std::wstring_view& src) {
|
||||
CONVFCT_TYPE3(WcharToUTF8, wchar_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string WcharToUTF8(const wchar_t* src) {
|
||||
CONVFCT_TYPE4(WcharToUTF8, wchar_t, yycc_char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8ToWchar
|
||||
|
||||
bool UTF8ToWchar(const yycc_u8string_view& src, std::wstring& dst) {
|
||||
std::string_view adapted_src(ToNativeView(src));
|
||||
return CharToWchar(adapted_src, dst, CP_UTF8);
|
||||
}
|
||||
bool UTF8ToWchar(const yycc_char8_t* src, std::wstring& dst) {
|
||||
CONVFCT_TYPE2(UTF8ToWchar, yycc_char8_t, wchar_t);
|
||||
}
|
||||
std::wstring UTF8ToWchar(const yycc_u8string_view& src) {
|
||||
CONVFCT_TYPE3(UTF8ToWchar, yycc_char8_t, wchar_t);
|
||||
}
|
||||
std::wstring UTF8ToWchar(const yycc_char8_t* src) {
|
||||
CONVFCT_TYPE4(UTF8ToWchar, yycc_char8_t, wchar_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#pragma region UTF8 UTF16 UTF32 Help Funcs
|
||||
|
||||
/*
|
||||
According to the documentation introduced in CppReference.
|
||||
The standard library is guaranteed to provide several specific specializations of \c std::codecvt.
|
||||
The UTF8 char type in UTF8 related specializations of \c std::codecvt is different.
|
||||
It is also independend from we defined \c yycc_char8_t.
|
||||
So it is essential define a type which can correctly trigger specific specializations of \c std::codecv in there.
|
||||
*/
|
||||
#if defined(__cpp_char8_t)
|
||||
using CodecvtUTF8Char_t = char8_t;
|
||||
#else
|
||||
using CodecvtUTF8Char_t = char;
|
||||
#endif
|
||||
|
||||
template<typename _TChar, std::enable_if_t<std::is_same_v<_TChar, char16_t> || std::is_same_v<_TChar, char32_t>, int> = 0>
|
||||
using CodecvtFacet_t = std::codecvt<_TChar, CodecvtUTF8Char_t, std::mbstate_t>;
|
||||
|
||||
template<typename _TChar, std::enable_if_t<std::is_same_v<_TChar, char16_t> || std::is_same_v<_TChar, char32_t>, int> = 0>
|
||||
static bool UTF8ToUTFOther(const char* _src, std::basic_string<_TChar>& dest) {
|
||||
static bool UTF8ToUTFOther(const yycc_u8string_view& src, std::basic_string<_TChar>& dst) {
|
||||
// Reference:
|
||||
// https://zh.cppreference.com/w/cpp/locale/codecvt/in
|
||||
|
||||
// init src string
|
||||
if (_src == nullptr) return false;
|
||||
std::string src(_src);
|
||||
// if src is empty, return directly
|
||||
if (src.empty()) {
|
||||
dst.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// init locale and get codecvt facet
|
||||
// same reason in UTFOtherToUTF8 to keeping reference to locale
|
||||
@ -94,12 +230,12 @@ namespace YYCC::EncodingHelper {
|
||||
|
||||
// convertion preparation
|
||||
std::mbstate_t mb{};
|
||||
dest.resize(src.size());
|
||||
const CodecvtUTF8Char_t* intern_from = reinterpret_cast<const CodecvtUTF8Char_t*>(src.c_str()),
|
||||
*intern_from_end = reinterpret_cast<const CodecvtUTF8Char_t*>(src.c_str() + src.size()),
|
||||
dst.resize(src.size());
|
||||
const CodecvtUTF8Char_t* intern_from = reinterpret_cast<const CodecvtUTF8Char_t*>(src.data()),
|
||||
*intern_from_end = reinterpret_cast<const CodecvtUTF8Char_t*>(src.data() + src.size()),
|
||||
*intern_from_next = nullptr;
|
||||
_TChar* extern_to = dest.data(),
|
||||
*extern_to_end = dest.data() + dest.size(),
|
||||
_TChar* extern_to = dst.data(),
|
||||
*extern_to_end = dst.data() + dst.size(),
|
||||
*extern_to_next = nullptr;
|
||||
// do convertion
|
||||
auto result = this_codecvt.in(
|
||||
@ -112,35 +248,20 @@ namespace YYCC::EncodingHelper {
|
||||
if (result != CodecvtFacet_t<_TChar>::ok)
|
||||
return false;
|
||||
// resize result and return
|
||||
dest.resize(extern_to_next - dest.data());
|
||||
dst.resize(extern_to_next - dst.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UTF8ToUTF16(const char* src, std::u16string& dest) {
|
||||
return UTF8ToUTFOther<char16_t>(src, dest);
|
||||
}
|
||||
std::u16string UTF8ToUTF16(const char* src) {
|
||||
std::u16string ret;
|
||||
if (!UTF8ToUTF16(src, ret)) ret.clear();
|
||||
return ret;
|
||||
}
|
||||
bool UTF8ToUTF32(const char* src, std::u32string& dest) {
|
||||
return UTF8ToUTFOther<char32_t>(src, dest);
|
||||
}
|
||||
std::u32string UTF8ToUTF32(const char* src) {
|
||||
std::u32string ret;
|
||||
if (!UTF8ToUTF32(src, ret)) ret.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename _TChar, std::enable_if_t<std::is_same_v<_TChar, char16_t> || std::is_same_v<_TChar, char32_t>, int> = 0>
|
||||
static bool UTFOtherToUTF8(const _TChar* _src, std::string& dest) {
|
||||
static bool UTFOtherToUTF8(const std::basic_string_view<_TChar>& src, yycc_u8string& dst) {
|
||||
// Reference:
|
||||
// https://zh.cppreference.com/w/cpp/locale/codecvt/out
|
||||
|
||||
// initialize src string
|
||||
if (_src == nullptr) return false;
|
||||
std::basic_string<_TChar> src(_src);
|
||||
// if src is empty, return directly
|
||||
if (src.empty()) {
|
||||
dst.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// init locale and get codecvt facet
|
||||
// the reference to locale must be preserved until convertion done.
|
||||
@ -150,12 +271,12 @@ namespace YYCC::EncodingHelper {
|
||||
|
||||
// do convertion preparation
|
||||
std::mbstate_t mb{};
|
||||
dest.resize(src.size() * this_codecvt.max_length());
|
||||
const _TChar* intern_from = src.c_str(),
|
||||
*intern_from_end = src.c_str() + src.size(),
|
||||
dst.resize(src.size() * this_codecvt.max_length());
|
||||
const _TChar* intern_from = src.data(),
|
||||
*intern_from_end = src.data() + src.size(),
|
||||
*intern_from_next = nullptr;
|
||||
CodecvtUTF8Char_t* extern_to = reinterpret_cast<CodecvtUTF8Char_t*>(dest.data()),
|
||||
*extern_to_end = reinterpret_cast<CodecvtUTF8Char_t*>(dest.data() + dest.size()),
|
||||
CodecvtUTF8Char_t* extern_to = reinterpret_cast<CodecvtUTF8Char_t*>(dst.data()),
|
||||
*extern_to_end = reinterpret_cast<CodecvtUTF8Char_t*>(dst.data() + dst.size()),
|
||||
*extern_to_next = nullptr;
|
||||
// do convertion
|
||||
auto result = this_codecvt.out(
|
||||
@ -168,26 +289,83 @@ namespace YYCC::EncodingHelper {
|
||||
if (result != CodecvtFacet_t<_TChar>::ok)
|
||||
return false;
|
||||
// resize result and retuen
|
||||
dest.resize(extern_to_next - reinterpret_cast<CodecvtUTF8Char_t*>(dest.data()));
|
||||
dst.resize(extern_to_next - reinterpret_cast<CodecvtUTF8Char_t*>(dst.data()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UTF16ToUTF8(const char16_t* src, std::string& dest) {
|
||||
return UTFOtherToUTF8<char16_t>(src, dest);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8ToUTF16
|
||||
|
||||
bool UTF8ToUTF16(const yycc_u8string_view& src, std::u16string& dst) {
|
||||
return UTF8ToUTFOther<char16_t>(src, dst);
|
||||
}
|
||||
std::string UTF16ToUTF8(const char16_t* src) {
|
||||
std::string ret;
|
||||
if (!UTF16ToUTF8(src, ret)) ret.clear();
|
||||
return ret;
|
||||
bool UTF8ToUTF16(const yycc_char8_t* src, std::u16string& dst) {
|
||||
CONVFCT_TYPE2(UTF8ToUTF16, yycc_char8_t, char16_t);
|
||||
}
|
||||
bool UTF32ToUTF8(const char32_t* src, std::string& dest) {
|
||||
return UTFOtherToUTF8<char32_t>(src, dest);
|
||||
std::u16string UTF8ToUTF16(const yycc_u8string_view& src) {
|
||||
CONVFCT_TYPE3(UTF8ToUTF16, yycc_char8_t, char16_t);
|
||||
}
|
||||
std::string UTF32ToUTF8(const char32_t* src) {
|
||||
std::string ret;
|
||||
if (!UTF32ToUTF8(src, ret)) ret.clear();
|
||||
return ret;
|
||||
std::u16string UTF8ToUTF16(const yycc_char8_t* src) {
|
||||
CONVFCT_TYPE4(UTF8ToUTF16, yycc_char8_t, char16_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF16ToUTF8
|
||||
|
||||
bool UTF16ToUTF8(const std::u16string_view& src, yycc_u8string& dst) {
|
||||
return UTFOtherToUTF8<char16_t>(src, dst);
|
||||
}
|
||||
bool UTF16ToUTF8(const char16_t* src, yycc_u8string& dst) {
|
||||
CONVFCT_TYPE2(UTF16ToUTF8, char16_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string UTF16ToUTF8(const std::u16string_view& src) {
|
||||
CONVFCT_TYPE3(UTF16ToUTF8, char16_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string UTF16ToUTF8(const char16_t* src) {
|
||||
CONVFCT_TYPE4(UTF16ToUTF8, char16_t, yycc_char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8ToUTF32
|
||||
|
||||
bool UTF8ToUTF32(const yycc_u8string_view& src, std::u32string& dst) {
|
||||
return UTF8ToUTFOther<char32_t>(src, dst);
|
||||
}
|
||||
bool UTF8ToUTF32(const yycc_char8_t* src, std::u32string& dst) {
|
||||
CONVFCT_TYPE2(UTF8ToUTF32, yycc_char8_t, char32_t);
|
||||
}
|
||||
std::u32string UTF8ToUTF32(const yycc_u8string_view& src) {
|
||||
CONVFCT_TYPE3(UTF8ToUTF32, yycc_char8_t, char32_t);
|
||||
}
|
||||
std::u32string UTF8ToUTF32(const yycc_char8_t* src) {
|
||||
CONVFCT_TYPE4(UTF8ToUTF32, yycc_char8_t, char32_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF32ToUTF8
|
||||
|
||||
bool UTF32ToUTF8(const std::u32string_view& src, yycc_u8string& dst) {
|
||||
return UTFOtherToUTF8<char32_t>(src, dst);
|
||||
}
|
||||
bool UTF32ToUTF8(const char32_t* src, yycc_u8string& dst) {
|
||||
CONVFCT_TYPE2(UTF32ToUTF8, char32_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string UTF32ToUTF8(const std::u32string_view& src) {
|
||||
CONVFCT_TYPE3(UTF32ToUTF8, char32_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string UTF32ToUTF8(const char32_t* src) {
|
||||
CONVFCT_TYPE4(UTF32ToUTF8, char32_t, yycc_char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#undef CONVFCT_TYPE2
|
||||
#undef CONVFCT_TYPE3
|
||||
#undef CONVFCT_TYPE4
|
||||
|
||||
}
|
||||
|
||||
|
@ -21,20 +21,20 @@
|
||||
* \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,
|
||||
* 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.
|
||||
@ -46,35 +46,72 @@
|
||||
* 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 {
|
||||
|
||||
#define _YYCC_U8(strl) u8 ## strl
|
||||
#define YYCC_U8(strl) (reinterpret_cast<const ::YYCC::yycc_char8_t*>(_YYCC_U8(strl)))
|
||||
|
||||
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);
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
bool WcharToChar(const wchar_t* src, std::string& dest, UINT codepage);
|
||||
bool WcharToUTF8(const wchar_t* src, std::string& dest);
|
||||
std::string WcharToChar(const wchar_t* src, UINT codepage);
|
||||
std::string WcharToUTF8(const wchar_t* src);
|
||||
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);
|
||||
|
||||
bool CharToWchar(const char* src, std::wstring& dest, UINT codepage);
|
||||
bool UTF8ToWchar(const char* src, std::wstring& dest);
|
||||
std::wstring CharToWchar(const char* src, UINT codepage);
|
||||
std::wstring UTF8ToWchar(const char* src);
|
||||
bool CharToWchar(const std::string_view& src, std::wstring& dst, UINT code_page);
|
||||
bool CharToWchar(const char* src, std::wstring& dst, UINT code_page);
|
||||
std::wstring CharToWchar(const std::string_view& src, UINT code_page);
|
||||
std::wstring CharToWchar(const char* src, UINT code_page);
|
||||
|
||||
bool CharToChar(const char* src, std::string& dest, UINT src_codepage, UINT dest_codepage);
|
||||
std::string CharToChar(const char* src, UINT src_codepage, UINT dest_codepage);
|
||||
bool CharToChar(const std::string_view& src, std::string& dst, UINT src_code_page, UINT dst_code_page);
|
||||
bool CharToChar(const char* src, std::string& dst, UINT src_code_page, UINT dst_code_page);
|
||||
std::string CharToChar(const std::string_view& src, UINT src_code_page, UINT dst_code_page);
|
||||
std::string CharToChar(const char* src, UINT src_code_page, UINT dst_code_page);
|
||||
|
||||
|
||||
bool WcharToUTF8(const std::wstring_view& src, yycc_u8string& dst);
|
||||
bool WcharToUTF8(const wchar_t* src, yycc_u8string& dst);
|
||||
yycc_u8string WcharToUTF8(const std::wstring_view& src);
|
||||
yycc_u8string WcharToUTF8(const wchar_t* src);
|
||||
|
||||
bool UTF8ToWchar(const yycc_u8string_view& src, std::wstring& dst);
|
||||
bool UTF8ToWchar(const yycc_char8_t* src, std::wstring& dst);
|
||||
std::wstring UTF8ToWchar(const yycc_u8string_view& src);
|
||||
std::wstring UTF8ToWchar(const yycc_char8_t* src);
|
||||
|
||||
#endif
|
||||
|
||||
bool UTF8ToUTF16(const char* src, std::u16string& dest);
|
||||
std::u16string UTF8ToUTF16(const char* src);
|
||||
bool UTF8ToUTF32(const char* src, std::u32string& dest);
|
||||
std::u32string UTF8ToUTF32(const char* src);
|
||||
bool UTF8ToUTF16(const yycc_u8string_view& src, std::u16string& dst);
|
||||
bool UTF8ToUTF16(const yycc_char8_t* src, std::u16string& dst);
|
||||
std::u16string UTF8ToUTF16(const yycc_u8string_view& src);
|
||||
std::u16string UTF8ToUTF16(const yycc_char8_t* src);
|
||||
|
||||
bool UTF16ToUTF8(const char16_t* src, std::string& dest);
|
||||
std::string UTF16ToUTF8(const char16_t* src);
|
||||
bool UTF32ToUTF8(const char32_t* src, std::string& dest);
|
||||
std::string UTF32ToUTF8(const char32_t* src);
|
||||
bool UTF16ToUTF8(const std::u16string_view& src, yycc_u8string& dst);
|
||||
bool UTF16ToUTF8(const char16_t* src, yycc_u8string& dst);
|
||||
yycc_u8string UTF16ToUTF8(const std::u16string_view& src);
|
||||
yycc_u8string UTF16ToUTF8(const char16_t* src);
|
||||
|
||||
|
||||
bool UTF8ToUTF32(const yycc_u8string_view& src, std::u32string& dst);
|
||||
bool UTF8ToUTF32(const yycc_char8_t* src, std::u32string& dst);
|
||||
std::u32string UTF8ToUTF32(const yycc_u8string_view& src);
|
||||
std::u32string UTF8ToUTF32(const yycc_char8_t* src);
|
||||
|
||||
bool UTF32ToUTF8(const std::u32string_view& src, yycc_u8string& dst);
|
||||
bool UTF32ToUTF8(const char32_t* src, yycc_u8string& dst);
|
||||
yycc_u8string UTF32ToUTF8(const std::u32string_view& src);
|
||||
yycc_u8string UTF32ToUTF8(const char32_t* src);
|
||||
|
||||
}
|
||||
|
@ -59,50 +59,50 @@ namespace YYCC::ExceptionHelper {
|
||||
* @param[in] code Exception code
|
||||
* @return The const string pointer to corresponding exception explanation string.
|
||||
*/
|
||||
static const char* UExceptionGetCodeName(DWORD code) {
|
||||
static const yycc_char8_t* UExceptionGetCodeName(DWORD code) {
|
||||
switch (code) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
return "access violation";
|
||||
return YYCC_U8("access violation");
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
return "array index out of bound";
|
||||
return YYCC_U8("array index out of bound");
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
return "breakpoint reached";
|
||||
return YYCC_U8("breakpoint reached");
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
return "misaligned data access";
|
||||
return YYCC_U8("misaligned data access");
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
return "operand had denormal value";
|
||||
return YYCC_U8("operand had denormal value");
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
return "floating-point division by zero";
|
||||
return YYCC_U8("floating-point division by zero");
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
return "no decimal fraction representation for value";
|
||||
return YYCC_U8("no decimal fraction representation for value");
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
return "invalid floating-point operation";
|
||||
return YYCC_U8("invalid floating-point operation");
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
return "floating-point overflow";
|
||||
return YYCC_U8("floating-point overflow");
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
return "floating-point stack corruption";
|
||||
return YYCC_U8("floating-point stack corruption");
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
return "floating-point underflow";
|
||||
return YYCC_U8("floating-point underflow");
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
return "illegal instruction";
|
||||
return YYCC_U8("illegal instruction");
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
return "inaccessible page";
|
||||
return YYCC_U8("inaccessible page");
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
return "integer division by zero";
|
||||
return YYCC_U8("integer division by zero");
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
return "integer overflow";
|
||||
return YYCC_U8("integer overflow");
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
return "documentation says this should never happen";
|
||||
return YYCC_U8("documentation says this should never happen");
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
return "can't continue after a noncontinuable exception";
|
||||
return YYCC_U8("can't continue after a noncontinuable exception");
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
return "attempted to execute a privileged instruction";
|
||||
return YYCC_U8("attempted to execute a privileged instruction");
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
return "one instruction has been executed";
|
||||
return YYCC_U8("one instruction has been executed");
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
return "stack overflow";
|
||||
return YYCC_U8("stack overflow");
|
||||
default:
|
||||
return "unknown exception";
|
||||
return YYCC_U8("unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,12 +117,12 @@ namespace YYCC::ExceptionHelper {
|
||||
* @param[in] fmt The format string.
|
||||
* @param[in] ... The argument to be formatted.
|
||||
*/
|
||||
static void UExceptionErrLogFormatLine(std::FILE* fs, const char* fmt, ...) {
|
||||
static void UExceptionErrLogFormatLine(std::FILE* fs, const yycc_char8_t* fmt, ...) {
|
||||
// write to file
|
||||
if (fs != nullptr) {
|
||||
va_list arg1;
|
||||
va_start(arg1, fmt);
|
||||
std::vfprintf(fs, fmt, arg1);
|
||||
std::vfprintf(fs, EncodingHelper::ToNative(fmt), arg1);
|
||||
std::fputs("\n", fs);
|
||||
va_end(arg1);
|
||||
}
|
||||
@ -142,10 +142,10 @@ namespace YYCC::ExceptionHelper {
|
||||
* If it is nullptr, function will skip writing for file stream.
|
||||
* @param[in] strl The string to be written.
|
||||
*/
|
||||
static void UExceptionErrLogWriteLine(std::FILE* fs, const char* strl) {
|
||||
static void UExceptionErrLogWriteLine(std::FILE* fs, const yycc_char8_t* strl) {
|
||||
// write to file
|
||||
if (fs != nullptr) {
|
||||
std::fputs(strl, fs);
|
||||
std::fputs(EncodingHelper::ToNative(strl), fs);
|
||||
std::fputs("\n", fs);
|
||||
}
|
||||
// write to stderr
|
||||
@ -163,7 +163,7 @@ namespace YYCC::ExceptionHelper {
|
||||
// init symbol
|
||||
if (!SymInitialize(process, 0, TRUE)) {
|
||||
// fail to init. return
|
||||
UExceptionErrLogWriteLine(fs, "Fail to initialize symbol handle for process!");
|
||||
UExceptionErrLogWriteLine(fs, YYCC_U8("Fail to initialize symbol handle for process!"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -215,13 +215,13 @@ namespace YYCC::ExceptionHelper {
|
||||
// depth breaker
|
||||
--maxdepth;
|
||||
if (maxdepth < 0) {
|
||||
UExceptionErrLogWriteLine(fs, "..."); // indicate there are some frames not listed
|
||||
UExceptionErrLogWriteLine(fs, YYCC_U8("...")); // indicate there are some frames not listed
|
||||
break;
|
||||
}
|
||||
|
||||
// get module name
|
||||
const char* module_name = "<unknown module>";
|
||||
std::string module_name_raw;
|
||||
const yycc_char8_t* module_name = YYCC_U8("<unknown module>");
|
||||
yycc_u8string module_name_raw;
|
||||
DWORD64 module_base;
|
||||
if (module_base = SymGetModuleBase64(process, frame.AddrPC.Offset)) {
|
||||
if (WinFctHelper::GetModuleFileName((HINSTANCE)module_base, module_name_raw)) {
|
||||
@ -230,18 +230,18 @@ namespace YYCC::ExceptionHelper {
|
||||
}
|
||||
|
||||
// get source file and line
|
||||
const char* source_file = "<unknown source>";
|
||||
const yycc_char8_t* source_file = YYCC_U8("<unknown source>");
|
||||
DWORD64 source_file_line = 0;
|
||||
DWORD dwDisplacement;
|
||||
IMAGEHLP_LINE64 winline;
|
||||
winline.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &dwDisplacement, &winline)) {
|
||||
source_file = winline.FileName;
|
||||
source_file = EncodingHelper::ToUTF8(winline.FileName); // TODO: check whether there is UNICODE file name.
|
||||
source_file_line = winline.LineNumber;
|
||||
}
|
||||
|
||||
// write to file
|
||||
UExceptionErrLogFormatLine(fs, "0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "[%s+0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "]\t%s#L%" PRIu64,
|
||||
UExceptionErrLogFormatLine(fs, YYCC_U8("0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "[%s+0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "]\t%s#L%" PRIu64),
|
||||
frame.AddrPC.Offset, // memory adress
|
||||
module_name, frame.AddrPC.Offset - module_base, // module name + relative address
|
||||
source_file, source_file_line // source file + source line
|
||||
@ -255,16 +255,16 @@ namespace YYCC::ExceptionHelper {
|
||||
SymCleanup(process);
|
||||
}
|
||||
|
||||
static void UExceptionErrorLog(const std::string& u8_filename, LPEXCEPTION_POINTERS info) {
|
||||
static void UExceptionErrorLog(const yycc_u8string& u8_filename, LPEXCEPTION_POINTERS info) {
|
||||
// open file stream if we have file name
|
||||
std::FILE* fs = nullptr;
|
||||
if (!u8_filename.empty()) {
|
||||
fs = IOHelper::UTF8FOpen(u8_filename.c_str(), "wb");
|
||||
fs = IOHelper::UTF8FOpen(u8_filename.c_str(), YYCC_U8("wb"));
|
||||
}
|
||||
|
||||
// record exception type first
|
||||
PEXCEPTION_RECORD rec = info->ExceptionRecord;
|
||||
UExceptionErrLogFormatLine(fs, "Unhandled exception occured at 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR ": %s (%" PRIu32 ").",
|
||||
UExceptionErrLogFormatLine(fs, YYCC_U8("Unhandled exception occured at 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR ": %s (%" PRIu32 ")."),
|
||||
rec->ExceptionAddress,
|
||||
UExceptionGetCodeName(rec->ExceptionCode),
|
||||
rec->ExceptionCode
|
||||
@ -273,10 +273,10 @@ namespace YYCC::ExceptionHelper {
|
||||
// special proc for 2 exceptions
|
||||
if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || rec->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
|
||||
if (rec->NumberParameters >= 2) {
|
||||
const char* op =
|
||||
rec->ExceptionInformation[0] == 0 ? "read" :
|
||||
rec->ExceptionInformation[0] == 1 ? "written" : "executed";
|
||||
UExceptionErrLogFormatLine(fs, "The data at memory address 0x%" PRI_XPTR_LEFT_PADDING PRIxPTR " could not be %s.",
|
||||
const yycc_char8_t* op =
|
||||
rec->ExceptionInformation[0] == 0 ? YYCC_U8("read") :
|
||||
rec->ExceptionInformation[0] == 1 ? YYCC_U8("written") : YYCC_U8("executed");
|
||||
UExceptionErrLogFormatLine(fs, YYCC_U8("The data at memory address 0x%" PRI_XPTR_LEFT_PADDING PRIxPTR " could not be %s."),
|
||||
rec->ExceptionInformation[1], op);
|
||||
}
|
||||
}
|
||||
@ -290,12 +290,12 @@ namespace YYCC::ExceptionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static void UExceptionCoreDump(const std::string& u8_filename, LPEXCEPTION_POINTERS info) {
|
||||
static void UExceptionCoreDump(const yycc_u8string& u8_filename, LPEXCEPTION_POINTERS info) {
|
||||
// convert file encoding
|
||||
std::wstring filename;
|
||||
if (u8_filename.empty())
|
||||
return; // if no given file name, return
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_filename.c_str(), filename))
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_filename, filename))
|
||||
return; // if convertion failed, return
|
||||
|
||||
// open file and write
|
||||
@ -315,18 +315,18 @@ namespace YYCC::ExceptionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static bool UExceptionFetchRecordPath(std::string& log_path, std::string& 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".
|
||||
// "module.dll" is the name of current module. "1234" is current process id.
|
||||
// get self module name
|
||||
std::string u8_self_module_name;
|
||||
yycc_u8string u8_self_module_name;
|
||||
{
|
||||
// get module handle
|
||||
HMODULE hSelfModule = YYCC::WinFctHelper::GetCurrentModule();
|
||||
if (hSelfModule == nullptr)
|
||||
return false;
|
||||
// get full path of self module
|
||||
std::string u8_self_module_path;
|
||||
yycc_u8string u8_self_module_path;
|
||||
if (!YYCC::WinFctHelper::GetModuleFileName(hSelfModule, u8_self_module_path))
|
||||
return false;
|
||||
// extract file name from full path by std::filesystem::path
|
||||
@ -336,22 +336,22 @@ namespace YYCC::ExceptionHelper {
|
||||
// then get process id
|
||||
DWORD process_id = GetCurrentProcessId();
|
||||
// conbine them as a file name prefix
|
||||
std::string u8_filename_prefix;
|
||||
if (!YYCC::StringHelper::Printf(u8_filename_prefix, "%s.%" PRIu32, u8_self_module_name.c_str(), process_id))
|
||||
yycc_u8string u8_filename_prefix;
|
||||
if (!YYCC::StringHelper::Printf(u8_filename_prefix, YYCC_U8("%s.%" PRIu32), u8_self_module_name.c_str(), process_id))
|
||||
return false;
|
||||
// then get file name for log and minidump
|
||||
std::string u8_log_filename = u8_filename_prefix + ".log";
|
||||
std::string u8_coredump_filename = u8_filename_prefix + ".dmp";
|
||||
yycc_u8string u8_log_filename = u8_filename_prefix + YYCC_U8(".log");
|
||||
yycc_u8string u8_coredump_filename = u8_filename_prefix + YYCC_U8(".dmp");
|
||||
|
||||
// fetch crash report path
|
||||
// get local appdata folder
|
||||
std::string u8_localappdata_path;
|
||||
yycc_u8string u8_localappdata_path;
|
||||
if (!WinFctHelper::GetLocalAppData(u8_localappdata_path))
|
||||
return false;
|
||||
// convert to std::filesystem::path
|
||||
std::filesystem::path crash_report_path(FsPathPatch::FromUTF8Path(u8_localappdata_path.c_str()));
|
||||
// slash into crash report folder
|
||||
crash_report_path /= FsPathPatch::FromUTF8Path("CrashDumps");
|
||||
crash_report_path /= FsPathPatch::FromUTF8Path(YYCC_U8("CrashDumps"));
|
||||
// use create function to make sure it is existing
|
||||
std::filesystem::create_directories(crash_report_path);
|
||||
|
||||
@ -375,18 +375,18 @@ namespace YYCC::ExceptionHelper {
|
||||
// core implementation
|
||||
{
|
||||
// fetch error report path first
|
||||
std::string log_path, coredump_path;
|
||||
yycc_u8string log_path, coredump_path;
|
||||
if (!UExceptionFetchRecordPath(log_path, coredump_path)) {
|
||||
// fail to fetch path, clear them.
|
||||
// we still can handle crash without them
|
||||
log_path.clear();
|
||||
coredump_path.clear();
|
||||
// and tell user we can not output file
|
||||
ConsoleHelper::ErrWriteLine("Crash occurs, but we can not create crash log and coredump!");
|
||||
ConsoleHelper::ErrWriteLine(YYCC_U8("Crash occurs, but we can not create crash log and coredump!"));
|
||||
} else {
|
||||
// okey. output file path to tell user the path where you can find.
|
||||
ConsoleHelper::ErrFormatLine("Crash Log: %s", log_path.c_str());
|
||||
ConsoleHelper::ErrFormatLine("Crash Coredump: %s", coredump_path.c_str());
|
||||
ConsoleHelper::ErrFormatLine(YYCC_U8("Crash Log: %s"), log_path.c_str());
|
||||
ConsoleHelper::ErrFormatLine(YYCC_U8("Crash Coredump: %s"), coredump_path.c_str());
|
||||
}
|
||||
|
||||
// write crash log
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
namespace YYCC::FsPathPatch {
|
||||
|
||||
std::filesystem::path FromUTF8Path(const char* u8_path) {
|
||||
std::filesystem::path FromUTF8Path(const yycc_char8_t* u8_path) {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// convert path to wchar
|
||||
@ -14,19 +14,19 @@ namespace YYCC::FsPathPatch {
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_path, wpath))
|
||||
throw std::invalid_argument("Fail to convert given UTF8 string.");
|
||||
|
||||
// call microsoft specified fopen which support wchar as argument.
|
||||
// return path with wchar_t ctor
|
||||
return std::filesystem::path(wpath);
|
||||
|
||||
|
||||
#else
|
||||
return std::filesystem::path(u8_path);
|
||||
return std::filesystem::path(EncodingHelper::ToNative(u8_path));
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string ToUTF8Path(const std::filesystem::path& path) {
|
||||
yycc_u8string ToUTF8Path(const std::filesystem::path& path) {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// get and convert to utf8
|
||||
std::string u8_path;
|
||||
yycc_u8string u8_path;
|
||||
if (!YYCC::EncodingHelper::WcharToUTF8(path.c_str(), u8_path))
|
||||
throw std::invalid_argument("Fail to convert to UTF8 string.");
|
||||
|
||||
@ -34,7 +34,7 @@ namespace YYCC::FsPathPatch {
|
||||
return u8_path;
|
||||
|
||||
#else
|
||||
return path.string();
|
||||
return EncodingHelper::ToUTF8(path.string());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace YYCC::FsPathPatch {
|
||||
* @return std::filesystem::path instance.
|
||||
* @exception std::invalid_argument Fail to parse given UTF8 string (maybe invalid?).
|
||||
*/
|
||||
std::filesystem::path FromUTF8Path(const char* u8_path);
|
||||
std::filesystem::path FromUTF8Path(const yycc_char8_t* u8_path);
|
||||
|
||||
/**
|
||||
* @brief Returns the UTF8 representation of the pathname
|
||||
@ -36,6 +36,6 @@ namespace YYCC::FsPathPatch {
|
||||
* @return UTF8 encoded string representing given path.
|
||||
* @exception std::invalid_argument Fail to parse to UTF8 string.
|
||||
*/
|
||||
std::string ToUTF8Path(const std::filesystem::path& path);
|
||||
yycc_u8string ToUTF8Path(const std::filesystem::path& path);
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
namespace YYCC::IOHelper {
|
||||
|
||||
FILE* UTF8FOpen(const char* u8_filepath, const char* u8_mode) {
|
||||
FILE* UTF8FOpen(const yycc_char8_t* u8_filepath, const yycc_char8_t* u8_mode) {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// convert mode and file path to wchar
|
||||
|
@ -32,6 +32,6 @@ namespace YYCC::IOHelper {
|
||||
* On other platforms, this function will delegate request directly to std::fopen.
|
||||
* @return FILE* of the file to be opened, or nullptr if failed.
|
||||
*/
|
||||
FILE* UTF8FOpen(const char* u8_filepath, const char* u8_mode);
|
||||
FILE* UTF8FOpen(const yycc_char8_t* u8_filepath, const yycc_char8_t* u8_mode);
|
||||
|
||||
}
|
||||
|
@ -14,11 +14,15 @@ namespace YYCC::ParserHelper {
|
||||
// Reference: https://zh.cppreference.com/w/cpp/utility/from_chars
|
||||
|
||||
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
||||
bool TryParse(const std::string& strl, _Ty& num) {
|
||||
auto [ptr, ec] = std::from_chars(strl.c_str(), strl.c_str() + strl.size(), num, std::chars_format::general);
|
||||
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()),
|
||||
num, std::chars_format::general
|
||||
);
|
||||
if (ec == std::errc()) {
|
||||
// check whether the full string is matched
|
||||
return ptr == strl.c_str() + strl.size();
|
||||
return ptr == EncodingHelper::ToNative(strl.data() + strl.size());
|
||||
} else if (ec == std::errc::invalid_argument) {
|
||||
// given string is invalid
|
||||
return false;
|
||||
@ -31,11 +35,15 @@ 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 std::string& strl, _Ty& num, int base = 10) {
|
||||
auto [ptr, ec] = std::from_chars(strl.c_str(), strl.c_str() + strl.size(), num, base);
|
||||
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()),
|
||||
num, base
|
||||
);
|
||||
if (ec == std::errc()) {
|
||||
// check whether the full string is matched
|
||||
return ptr == strl.c_str() + strl.size();
|
||||
return ptr == EncodingHelper::ToNative(strl.data() + strl.size());
|
||||
} else if (ec == std::errc::invalid_argument) {
|
||||
// given string is invalid
|
||||
return false;
|
||||
@ -48,15 +56,15 @@ namespace YYCC::ParserHelper {
|
||||
}
|
||||
}
|
||||
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
||||
bool TryParse(const std::string& strl, _Ty& num) {
|
||||
if (strl == "true") num = true;
|
||||
else if (strl == "false") num = false;
|
||||
bool TryParse(const yycc_u8string_view& strl, _Ty& num) {
|
||||
if (strl == YYCC_U8("true")) num = true;
|
||||
else if (strl == YYCC_U8("false")) num = false;
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty>, int> = 0>
|
||||
_Ty Parse(const std::string& strl) {
|
||||
_Ty Parse(const yycc_u8string_view& strl) {
|
||||
_Ty ret;
|
||||
TryParse(strl, ret);
|
||||
return ret;
|
||||
@ -65,11 +73,15 @@ namespace YYCC::ParserHelper {
|
||||
// Reference: https://en.cppreference.com/w/cpp/utility/to_chars
|
||||
|
||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||
std::string ToString(_Ty num) {
|
||||
std::array<char, 64> buffer;
|
||||
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), num);
|
||||
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()),
|
||||
num
|
||||
);
|
||||
if (ec == std::errc()) {
|
||||
return std::string(buffer.data(), ptr - buffer.data());
|
||||
return yycc_u8string(buffer.data(), EncodingHelper::ToUTF8(ptr) - buffer.data());
|
||||
} else if (ec == std::errc::value_too_large) {
|
||||
// too short buffer
|
||||
// this should not happend
|
||||
@ -80,9 +92,9 @@ namespace YYCC::ParserHelper {
|
||||
}
|
||||
}
|
||||
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
||||
std::string ToString(_Ty num) {
|
||||
if (num) return std::string("true");
|
||||
else return std::string("false");
|
||||
yycc_u8string ToString(_Ty num) {
|
||||
if (num) return yycc_u8string(YYCC_U8("true"));
|
||||
else return yycc_u8string(YYCC_U8("false"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
#include "StringHelper.hpp"
|
||||
#include "EncodingHelper.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace YYCC::StringHelper {
|
||||
|
||||
bool Printf(std::string& strl, const char* format, ...) {
|
||||
#pragma region Printf VPrintf
|
||||
|
||||
bool Printf(yycc_u8string& strl, const yycc_char8_t* format, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
bool ret = VPrintf(strl, format, argptr);
|
||||
@ -11,7 +14,7 @@ namespace YYCC::StringHelper {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VPrintf(std::string& strl, const char* format, va_list argptr) {
|
||||
bool VPrintf(yycc_u8string& strl, const yycc_char8_t* format, va_list argptr) {
|
||||
va_list args1;
|
||||
va_copy(args1, argptr);
|
||||
va_list args2;
|
||||
@ -19,7 +22,12 @@ namespace YYCC::StringHelper {
|
||||
|
||||
// the return value is desired char count without NULL terminal.
|
||||
// minus number means error
|
||||
int count = std::vsnprintf(nullptr, 0, format, args1);
|
||||
int count = std::vsnprintf(
|
||||
nullptr,
|
||||
0,
|
||||
EncodingHelper::ToNative(format),
|
||||
args1
|
||||
);
|
||||
if (count < 0) {
|
||||
// invalid length returned by vsnprintf.
|
||||
return false;
|
||||
@ -31,7 +39,12 @@ namespace YYCC::StringHelper {
|
||||
// because std::vsnprintf only can write "buf_size - 1" chars with a trailing NULL.
|
||||
// however std::vsnprintf already have a trailing NULL, so we plus 1 for it.
|
||||
strl.resize(count);
|
||||
int write_result = std::vsnprintf(strl.data(), strl.size() + 1, format, args2);
|
||||
int write_result = std::vsnprintf(
|
||||
EncodingHelper::ToNative(strl.data()),
|
||||
strl.size() + 1,
|
||||
EncodingHelper::ToNative(format),
|
||||
args2
|
||||
);
|
||||
va_end(args2);
|
||||
|
||||
if (write_result < 0 || write_result > count) {
|
||||
@ -42,9 +55,8 @@ namespace YYCC::StringHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::string Printf(const char* format, ...) {
|
||||
std::string ret;
|
||||
yycc_u8string Printf(const yycc_char8_t* format, ...) {
|
||||
yycc_u8string ret;
|
||||
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
@ -54,8 +66,8 @@ namespace YYCC::StringHelper {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string VPrintf(const char* format, va_list argptr) {
|
||||
std::string ret;
|
||||
yycc_u8string VPrintf(const yycc_char8_t* format, va_list argptr) {
|
||||
yycc_u8string ret;
|
||||
|
||||
va_list argcpy;
|
||||
va_copy(argcpy, argptr);
|
||||
@ -65,28 +77,32 @@ namespace YYCC::StringHelper {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Replace(std::string& strl, const char* _from_strl, const char* _to_strl) {
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Replace
|
||||
|
||||
void Replace(yycc_u8string& strl, const yycc_char8_t* _from_strl, const yycc_char8_t* _to_strl) {
|
||||
// Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
|
||||
|
||||
// check requirements
|
||||
// from string and to string should not be nullptr.
|
||||
if (_from_strl == nullptr || _to_strl == nullptr) return;
|
||||
// from string should not be empty
|
||||
std::string from_strl(_from_strl);
|
||||
std::string to_strl(_to_strl);
|
||||
yycc_u8string from_strl(_from_strl);
|
||||
yycc_u8string to_strl(_to_strl);
|
||||
if (from_strl.empty()) return;
|
||||
|
||||
// start replace one by one
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = strl.find(from_strl, start_pos)) != std::string::npos) {
|
||||
while ((start_pos = strl.find(from_strl, start_pos)) != yycc_u8string::npos) {
|
||||
strl.replace(start_pos, from_strl.size(), to_strl);
|
||||
start_pos += to_strl.size(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
std::string Replace(const char* _strl, const char* _from_strl, const char* _to_strl) {
|
||||
yycc_u8string Replace(const yycc_char8_t* _strl, const yycc_char8_t* _from_strl, const yycc_char8_t* _to_strl) {
|
||||
// prepare result
|
||||
std::string strl;
|
||||
yycc_u8string strl;
|
||||
// if given string is not nullptr, assign it and process it.
|
||||
if (_strl != nullptr) {
|
||||
strl = _strl;
|
||||
@ -96,13 +112,17 @@ namespace YYCC::StringHelper {
|
||||
return strl;
|
||||
}
|
||||
|
||||
std::string Join(JoinDataProvider fct_data, const char* decilmer) {
|
||||
std::string ret;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Join
|
||||
|
||||
yycc_u8string Join(JoinDataProvider fct_data, const yycc_char8_t* decilmer) {
|
||||
yycc_u8string ret;
|
||||
bool is_first = true;
|
||||
const char* element;
|
||||
yycc_u8string_view element;
|
||||
|
||||
// fetch element
|
||||
while ((element = fct_data()) != nullptr) {
|
||||
while (fct_data(element)) {
|
||||
// insert decilmer
|
||||
if (is_first) is_first = false;
|
||||
else {
|
||||
@ -111,42 +131,46 @@ namespace YYCC::StringHelper {
|
||||
ret.append(decilmer);
|
||||
}
|
||||
|
||||
// insert element
|
||||
ret.append(element);
|
||||
// insert element if it is not empty
|
||||
if (!element.empty())
|
||||
ret.append(element);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Join(const std::vector<std::string>& data, const char* decilmer, bool reversed) {
|
||||
yycc_u8string Join(const std::vector<yycc_u8string>& data, const yycc_char8_t* decilmer, bool reversed) {
|
||||
if (reversed) {
|
||||
auto iter = data.crbegin();
|
||||
auto stop = data.crend();
|
||||
return Join([&iter, &stop]() -> const char* {
|
||||
// if we reach tail, return nullptr
|
||||
if (iter == stop) return nullptr;
|
||||
return Join([&iter, &stop](yycc_u8string_view& view) -> bool {
|
||||
// if we reach tail, return false
|
||||
if (iter == stop) return false;
|
||||
// otherwise fetch data, inc iterator and return.
|
||||
const char* ret = iter->c_str();
|
||||
view = *iter;
|
||||
++iter;
|
||||
return ret;
|
||||
return true;
|
||||
}, decilmer);
|
||||
} else {
|
||||
auto iter = data.cbegin();
|
||||
auto stop = data.cend();
|
||||
return Join([&iter, &stop]() -> const char* {
|
||||
return Join([&iter, &stop](yycc_u8string_view& view) -> bool {
|
||||
// if we reach tail, return nullptr
|
||||
if (iter == stop) return nullptr;
|
||||
if (iter == stop) return false;
|
||||
// otherwise fetch data, inc iterator and return.
|
||||
const char* ret = iter->c_str();
|
||||
view = *iter;
|
||||
++iter;
|
||||
return ret;
|
||||
return true;
|
||||
}, decilmer);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Upper Lower
|
||||
|
||||
template<bool bIsToLower>
|
||||
void GeneralStringLowerUpper(std::string& strl) {
|
||||
void GeneralStringLowerUpper(yycc_u8string& strl) {
|
||||
// References:
|
||||
// https://en.cppreference.com/w/cpp/algorithm/transform
|
||||
// https://en.cppreference.com/w/cpp/string/byte/tolower
|
||||
@ -159,59 +183,77 @@ namespace YYCC::StringHelper {
|
||||
);
|
||||
}
|
||||
|
||||
std::string Lower(const char* strl) {
|
||||
std::string ret;
|
||||
yycc_u8string Lower(const yycc_char8_t* strl) {
|
||||
yycc_u8string ret;
|
||||
if (strl == nullptr) return ret;
|
||||
else ret = strl;
|
||||
Lower(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Lower(std::string& strl) {
|
||||
void Lower(yycc_u8string& strl) {
|
||||
GeneralStringLowerUpper<true>(strl);
|
||||
}
|
||||
|
||||
std::string Upper(const char* strl) {
|
||||
yycc_u8string Upper(const yycc_char8_t* strl) {
|
||||
// same as Lower, just replace char transform function.
|
||||
std::string ret;
|
||||
yycc_u8string ret;
|
||||
if (strl == nullptr) return ret;
|
||||
else ret = strl;
|
||||
Upper(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Upper(std::string& strl) {
|
||||
void Upper(yycc_u8string& strl) {
|
||||
GeneralStringLowerUpper<false>(strl);
|
||||
}
|
||||
|
||||
std::vector<std::string> Split(const char* _strl, const char* _decilmer) {
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Split
|
||||
|
||||
std::vector<yycc_u8string> Split(const yycc_u8string_view& strl, const yycc_char8_t* _decilmer) {
|
||||
// call split view
|
||||
auto view_result = SplitView(strl, _decilmer);
|
||||
|
||||
// copy string view result to string
|
||||
std::vector<yycc_u8string> elems;
|
||||
for (const auto& strl_view : view_result) {
|
||||
elems.emplace_back(yycc_u8string(strl_view));
|
||||
}
|
||||
// return copied result
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view& strl, const yycc_char8_t* _decilmer) {
|
||||
// Reference:
|
||||
// https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
|
||||
|
||||
// prepare return value
|
||||
std::vector<std::string> elems;
|
||||
std::vector<yycc_u8string_view> elems;
|
||||
|
||||
// if the string need to be splitted is nullptr, return empty result.
|
||||
if (_strl == nullptr) return elems;
|
||||
std::string strl(_strl);
|
||||
// if decilmer is nullptr, or decilmer is zero length, return original string
|
||||
std::string decilmer;
|
||||
if (_decilmer == nullptr || (decilmer = _decilmer, decilmer.empty())) {
|
||||
elems.push_back(strl);
|
||||
// if string need to be splitted is empty, return original string (empty item).
|
||||
// if decilmer is nullptr, or decilmer is zero length, return original string.
|
||||
yycc_u8string decilmer;
|
||||
if (strl.empty() || _decilmer == nullptr || (decilmer = _decilmer, decilmer.empty())) {
|
||||
elems.emplace_back(strl);
|
||||
return elems;
|
||||
}
|
||||
|
||||
// start spliting
|
||||
std::size_t previous = 0, current;
|
||||
while ((current = strl.find(decilmer.c_str(), previous)) != std::string::npos) {
|
||||
elems.push_back(strl.substr(previous, current - previous));
|
||||
while ((current = strl.find(decilmer.c_str(), previous)) != yycc_u8string::npos) {
|
||||
elems.emplace_back(strl.substr(previous, current - previous));
|
||||
previous = current + decilmer.size();
|
||||
}
|
||||
// try insert last part but prevent possible out of range exception
|
||||
if (previous <= strl.size()) {
|
||||
elems.push_back(strl.substr(previous));
|
||||
elems.emplace_back(strl.substr(previous));
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
||||
|
@ -8,21 +8,22 @@
|
||||
|
||||
namespace YYCC::StringHelper {
|
||||
|
||||
bool Printf(std::string& strl, const char* format, ...);
|
||||
bool VPrintf(std::string& strl, const char* format, va_list argptr);
|
||||
bool Printf(yycc_u8string& strl, const yycc_char8_t* format, ...);
|
||||
bool VPrintf(yycc_u8string& strl, const yycc_char8_t* format, va_list argptr);
|
||||
yycc_u8string Printf(const yycc_char8_t* format, ...);
|
||||
yycc_u8string VPrintf(const yycc_char8_t* format, va_list argptr);
|
||||
|
||||
std::string Printf(const char* format, ...);
|
||||
std::string VPrintf(const char* format, va_list argptr);
|
||||
|
||||
void Replace(std::string& strl, const char* _from_strl, const char* _to_strl);
|
||||
std::string Replace(const char* _strl, const char* _from_strl, const char* _to_strl);
|
||||
void Replace(yycc_u8string& strl, const yycc_char8_t* _from_strl, const yycc_char8_t* _to_strl);
|
||||
yycc_u8string Replace(const yycc_char8_t* _strl, const yycc_char8_t* _from_strl, const yycc_char8_t* _to_strl);
|
||||
|
||||
/**
|
||||
* @brief The data provider of general Join function.
|
||||
* This function pointer return non-null string pointer to represent a element of joined series.
|
||||
* otherwise return nullptr to terminate the joining process.
|
||||
* For the implementation of this function:
|
||||
* Function return true to continue join. otherwise return false to terminate join.
|
||||
* The argument assigned in the calling returning false is not included.
|
||||
* During calling, implementation should assign the string view to the string need to be joined in given argument.
|
||||
*/
|
||||
using JoinDataProvider = std::function<const char* ()>;
|
||||
using JoinDataProvider = std::function<bool(yycc_u8string_view&)>;
|
||||
/**
|
||||
* @brief General Join function.
|
||||
* @details This function use function pointer as a general data provider interface,
|
||||
@ -31,7 +32,7 @@ namespace YYCC::StringHelper {
|
||||
* @param decilmer[in] The decilmer.
|
||||
* @return A std::string instance which containing the join result.
|
||||
*/
|
||||
std::string Join(JoinDataProvider fct_data, const char* decilmer);
|
||||
yycc_u8string Join(JoinDataProvider fct_data, const yycc_char8_t* decilmer);
|
||||
/**
|
||||
* @brief Specialized Join function for common used container.
|
||||
* @param data
|
||||
@ -39,26 +40,16 @@ namespace YYCC::StringHelper {
|
||||
* @param reversed
|
||||
* @return
|
||||
*/
|
||||
std::string Join(const std::vector<std::string>& data, const char* decilmer, bool reversed = false);
|
||||
yycc_u8string Join(const std::vector<yycc_u8string>& data, const yycc_char8_t* decilmer, bool reversed = false);
|
||||
|
||||
/**
|
||||
* @brief Transform string to lower.
|
||||
* @param strl
|
||||
* @return
|
||||
*/
|
||||
std::string Lower(const char* strl);
|
||||
void Lower(std::string& strl);
|
||||
/**
|
||||
* @brief Transform string to upper.
|
||||
* @param strl
|
||||
* @return
|
||||
*/
|
||||
std::string Upper(const char* strl);
|
||||
void Upper(std::string& strl);
|
||||
yycc_u8string Lower(const yycc_char8_t* strl);
|
||||
void Lower(yycc_u8string& strl);
|
||||
yycc_u8string Upper(const yycc_char8_t* strl);
|
||||
void Upper(yycc_u8string& strl);
|
||||
|
||||
/**
|
||||
* @brief General Split function.
|
||||
* @param _strl[in] The string need to be splitting.
|
||||
* @param strl[in] The string need to be splitting.
|
||||
* If this is nullptr, the result will be empty.
|
||||
* @param _decilmer[in] The decilmer for splitting.
|
||||
* If decilmer is nullptr or zero length, the result will only have 1 element which is original string.
|
||||
@ -67,5 +58,7 @@ namespace YYCC::StringHelper {
|
||||
* It can works in most toy cases but not suit for high performance scenario.
|
||||
* Also, this function will produce a copy of original string because it is not zero copy.
|
||||
*/
|
||||
std::vector<std::string> Split(const char* _strl, const char* _decilmer);
|
||||
std::vector<yycc_u8string> Split(const yycc_u8string_view& strl, const yycc_char8_t* _decilmer);
|
||||
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view& strl, const yycc_char8_t* _decilmer);
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ namespace YYCC::WinFctHelper {
|
||||
return hModule;
|
||||
}
|
||||
|
||||
bool GetTempDirectory(std::string& ret) {
|
||||
bool GetTempDirectory(yycc_u8string& ret) {
|
||||
// create wchar buffer for receiving the temp path.
|
||||
std::wstring wpath(MAX_PATH + 1u, L'\0');
|
||||
DWORD expected_size;
|
||||
@ -41,10 +41,10 @@ namespace YYCC::WinFctHelper {
|
||||
// resize result
|
||||
wpath.resize(expected_size);
|
||||
// convert to utf8 and return
|
||||
return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str(), ret);
|
||||
return YYCC::EncodingHelper::WcharToUTF8(wpath, ret);
|
||||
}
|
||||
|
||||
bool GetModuleFileName(HINSTANCE hModule, std::string& ret) {
|
||||
bool GetModuleFileName(HINSTANCE hModule, yycc_u8string& ret) {
|
||||
// create wchar buffer for receiving the temp path.
|
||||
std::wstring wpath(MAX_PATH + 1u, L'\0');
|
||||
DWORD copied_size;
|
||||
@ -68,10 +68,13 @@ namespace YYCC::WinFctHelper {
|
||||
// resize result
|
||||
wpath.resize(copied_size);
|
||||
// convert to utf8 and return
|
||||
return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str(), ret);
|
||||
return YYCC::EncodingHelper::WcharToUTF8(wpath, ret);
|
||||
}
|
||||
|
||||
bool GetLocalAppData(std::string& ret) {
|
||||
bool GetLocalAppData(yycc_u8string& ret) {
|
||||
// check whether com initialized
|
||||
if (!COMHelper::IsInitialized()) return false;
|
||||
|
||||
// fetch path
|
||||
LPWSTR _known_path;
|
||||
HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &_known_path);
|
||||
|
@ -36,7 +36,7 @@ namespace YYCC::WinFctHelper {
|
||||
* The variable receiving UTF8 encoded path to Windows temp folder.
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
bool GetTempDirectory(std::string& ret);
|
||||
bool GetTempDirectory(yycc_u8string& ret);
|
||||
|
||||
/**
|
||||
* @brief Get the file name of given module HANDLE
|
||||
@ -47,7 +47,7 @@ namespace YYCC::WinFctHelper {
|
||||
* The variable receiving UTF8 encoded file name of given module.
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
bool GetModuleFileName(HINSTANCE hModule, std::string& ret);
|
||||
bool GetModuleFileName(HINSTANCE hModule, yycc_u8string& ret);
|
||||
|
||||
/**
|
||||
* @brief Get the path to LOCALAPPDATA.
|
||||
@ -56,7 +56,7 @@ namespace YYCC::WinFctHelper {
|
||||
* The variable receiving UTF8 encoded path to LOCALAPPDATA.
|
||||
* @return
|
||||
*/
|
||||
bool GetLocalAppData(std::string& ret);
|
||||
bool GetLocalAppData(yycc_u8string& ret);
|
||||
|
||||
}
|
||||
|
||||
|
@ -24,14 +24,20 @@
|
||||
|
||||
#endif
|
||||
|
||||
//// Decide the char type we used
|
||||
//#include <string>
|
||||
//namespace YYCC {
|
||||
//#if defined(__cpp_char8_t)
|
||||
// using u8char = char8_t;
|
||||
// using u8string = std::std::string
|
||||
//#else
|
||||
// using u8char = char;
|
||||
// using u8string = std::string;
|
||||
//#endif
|
||||
//}
|
||||
// Define the UTF8 char type we used.
|
||||
// And do a polyfill if no embedded char8_t type.
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
namespace YYCC {
|
||||
#if defined(__cpp_char8_t)
|
||||
using yycc_char8_t = char8_t;
|
||||
using yycc_u8string = std::u8string;
|
||||
using yycc_u8string_view = std::u8string_view;
|
||||
#else
|
||||
using yycc_char8_t = unsigned char;
|
||||
using yycc_u8string = std::basic_string<yycc_char8_t>;
|
||||
using yycc_u8string_view = std::basic_string_view<yycc_char8_t>;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
@ -2,13 +2,15 @@
|
||||
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include "StringHelper.hpp"
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
#include "ConsoleHelper.hpp"
|
||||
#include "COMHelper.hpp"
|
||||
#include "DialogHelper.hpp"
|
||||
#include "ParserHelper.hpp"
|
||||
#include "ExceptionHelper.hpp"
|
||||
#include "IOHelper.hpp"
|
||||
#include "WinFctHelper.hpp"
|
||||
#include "FsPathPatch.hpp"
|
||||
#include "ExceptionHelper.hpp"
|
||||
|
||||
#include "ConfigManager.hpp"
|
||||
|
@ -23,12 +23,12 @@ namespace YYCCTestbench {
|
||||
#define TEST_UNICODE_STR_EMOJI "\U0001F363 \u2716 \U0001F37A" // sushi x beer mug
|
||||
|
||||
#define CONCAT(prefix, strl) prefix ## strl
|
||||
#define CPP_U8_LITERAL(strl) reinterpret_cast<const char*>(CONCAT(u8, strl))
|
||||
#define CPP_U8_LITERAL(strl) YYCC_U8(strl)
|
||||
#define CPP_U16_LITERAL(strl) CONCAT(u, strl)
|
||||
#define CPP_U32_LITERAL(strl) CONCAT(U, strl)
|
||||
#define CPP_WSTR_LITERAL(strl) CONCAT(L, strl)
|
||||
|
||||
static std::vector<std::string> c_UTF8TestStrTable {
|
||||
static std::vector<YYCC::yycc_u8string> c_UTF8TestStrTable {
|
||||
CPP_U8_LITERAL(TEST_UNICODE_STR_JAPAN),
|
||||
CPP_U8_LITERAL(TEST_UNICODE_STR_CHINA),
|
||||
CPP_U8_LITERAL(TEST_UNICODE_STR_KOREA),
|
||||
@ -93,11 +93,11 @@ namespace YYCCTestbench {
|
||||
|
||||
#pragma endregion
|
||||
|
||||
static void Assert(bool condition, const char* description) {
|
||||
static void Assert(bool condition, const YYCC::yycc_char8_t* description) {
|
||||
if (condition) {
|
||||
Console::FormatLine(YYCC_COLOR_LIGHT_GREEN("OK: %s"), description);
|
||||
Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("OK: %s")), description);
|
||||
} else {
|
||||
Console::FormatLine(YYCC_COLOR_LIGHT_RED("Failed: %s\n"), description);
|
||||
Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("Failed: %s\n")), description);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
@ -105,9 +105,9 @@ namespace YYCCTestbench {
|
||||
static void ConsoleTestbench() {
|
||||
// Color Test
|
||||
Console::EnableColorfulConsole();
|
||||
Console::WriteLine("Color Test:");
|
||||
Console::WriteLine(YYCC_U8("Color Test:"));
|
||||
|
||||
#define TEST_MACRO(col) Console::WriteLine("\t" YYCC_COLOR_ ## col ("\u2588\u2588") YYCC_COLOR_LIGHT_ ## col("\u2588\u2588") " " #col " / LIGHT " #col );
|
||||
#define TEST_MACRO(col) Console::WriteLine(YYCC_U8("\t" YYCC_COLOR_ ## col ("\u2588\u2588") YYCC_COLOR_LIGHT_ ## col("\u2588\u2588") " " #col " / LIGHT " #col ));
|
||||
// U+2588 is full block
|
||||
|
||||
TEST_MACRO(BLACK);
|
||||
@ -122,19 +122,19 @@ namespace YYCCTestbench {
|
||||
#undef TEST_MACRO
|
||||
|
||||
// UTF8 Output Test
|
||||
Console::WriteLine("UTF8 Output Test:");
|
||||
Console::WriteLine(YYCC_U8("UTF8 Output Test:"));
|
||||
for (const auto& strl : c_UTF8TestStrTable) {
|
||||
Console::FormatLine("\t%s", strl.c_str());
|
||||
Console::FormatLine(YYCC_U8("\t%s"), strl.c_str());
|
||||
}
|
||||
|
||||
// UTF8 Input Test
|
||||
Console::WriteLine("UTF8 Input Test:");
|
||||
Console::WriteLine(YYCC_U8("UTF8 Input Test:"));
|
||||
for (const auto& strl : c_UTF8TestStrTable) {
|
||||
Console::FormatLine("\tPlease type: %s", strl.c_str());
|
||||
Console::Write("\t> ");
|
||||
Console::FormatLine(YYCC_U8("\tPlease type: %s"), strl.c_str());
|
||||
Console::Write(YYCC_U8("\t> "));
|
||||
|
||||
std::string gotten(Console::ReadLine());
|
||||
Assert(gotten == strl, YYCC::StringHelper::Printf("Got: %s", gotten.c_str()).c_str());
|
||||
YYCC::yycc_u8string gotten(Console::ReadLine());
|
||||
Assert(gotten == strl, YYCC::StringHelper::Printf(YYCC_U8("Got: %s"), gotten.c_str()).c_str());
|
||||
}
|
||||
|
||||
}
|
||||
@ -151,16 +151,16 @@ namespace YYCCTestbench {
|
||||
const auto& u32str = c_UTF32TestStrTable[i];
|
||||
|
||||
// create cache variables
|
||||
std::string u8cache;
|
||||
YYCC::yycc_u8string u8cache;
|
||||
std::u16string u16cache;
|
||||
std::u32string u32cache;
|
||||
|
||||
// do convertion check
|
||||
Assert(YYCC::EncodingHelper::UTF8ToUTF16(u8str.c_str(), u16cache) && u16cache == u16str, "YYCC::EncodingHelper::UTF8ToUTF16");
|
||||
Assert(YYCC::EncodingHelper::UTF8ToUTF32(u8str.c_str(), u32cache) && u32cache == u32str, "YYCC::EncodingHelper::UTF8ToUTF32");
|
||||
Assert(YYCC::EncodingHelper::UTF8ToUTF16(u8str, u16cache) && u16cache == u16str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF16"));
|
||||
Assert(YYCC::EncodingHelper::UTF8ToUTF32(u8str, u32cache) && u32cache == u32str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF32"));
|
||||
|
||||
Assert(YYCC::EncodingHelper::UTF16ToUTF8(u16str.c_str(), u8cache) && u8cache == u8str, "YYCC::EncodingHelper::UTF16ToUTF8");
|
||||
Assert(YYCC::EncodingHelper::UTF32ToUTF8(u32str.c_str(), u8cache) && u8cache == u8str, "YYCC::EncodingHelper::UTF32ToUTF8");
|
||||
Assert(YYCC::EncodingHelper::UTF16ToUTF8(u16str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF16ToUTF8"));
|
||||
Assert(YYCC::EncodingHelper::UTF32ToUTF8(u32str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF32ToUTF8"));
|
||||
}
|
||||
|
||||
// check wstring convertion on windows
|
||||
@ -171,12 +171,12 @@ namespace YYCCTestbench {
|
||||
const auto& wstr = c_WStrTestStrTable[i];
|
||||
|
||||
// create cache variables
|
||||
std::string u8cache;
|
||||
YYCC::yycc_u8string u8cache;
|
||||
std::wstring wcache;
|
||||
|
||||
// do convertion check
|
||||
Assert(YYCC::EncodingHelper::UTF8ToWchar(u8str.c_str(), wcache) && wcache == wstr, "YYCC::EncodingHelper::UTF8ToWchar");
|
||||
Assert(YYCC::EncodingHelper::WcharToUTF8(wstr.c_str(), u8cache) && u8cache == u8str, "YYCC::EncodingHelper::WcharToUTF8");
|
||||
Assert(YYCC::EncodingHelper::UTF8ToWchar(u8str.c_str(), wcache) && wcache == wstr, YYCC_U8("YYCC::EncodingHelper::UTF8ToWchar"));
|
||||
Assert(YYCC::EncodingHelper::WcharToUTF8(wstr.c_str(), u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::WcharToUTF8"));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -184,55 +184,58 @@ namespace YYCCTestbench {
|
||||
|
||||
static void StringTestbench() {
|
||||
// Test Printf
|
||||
auto test_printf = YYCC::StringHelper::Printf("%s == %s", "Hello World", "Hello, world");
|
||||
Assert(test_printf == "Hello World == Hello, world", "YYCC::StringHelper::Printf");
|
||||
auto test_printf = YYCC::StringHelper::Printf(YYCC_U8("%s == %s"), YYCC_U8("Hello World"), YYCC_U8("Hello, world"));
|
||||
Assert(test_printf == YYCC_U8("Hello World == Hello, world"), YYCC_U8("YYCC::StringHelper::Printf"));
|
||||
|
||||
// Test Replace
|
||||
auto test_replace = YYCC::StringHelper::Replace("aabbcc", "bb", "dd"); // normal case
|
||||
Assert(test_replace == "aaddcc", "YYCC::StringHelper::Replace");
|
||||
test_replace = YYCC::StringHelper::Replace("aabbcc", "zz", "yy"); // no replace
|
||||
Assert(test_replace == "aabbcc", "YYCC::StringHelper::Replace");
|
||||
test_replace = YYCC::StringHelper::Replace("aabbcc", "", "zz"); // empty finding
|
||||
Assert(test_replace == "aabbcc", "YYCC::StringHelper::Replace");
|
||||
test_replace = YYCC::StringHelper::Replace("aabbcc", nullptr, "zz"); // nullptr finding
|
||||
Assert(test_replace == "aabbcc", "YYCC::StringHelper::Replace");
|
||||
test_replace = YYCC::StringHelper::Replace("aaaabbaa", "aa", ""); // no replaced string
|
||||
Assert(test_replace == "bb", "YYCC::StringHelper::Replace");
|
||||
test_replace = YYCC::StringHelper::Replace("aaxcc", "x", "yx"); // nested replacing
|
||||
Assert(test_replace == "aayxcc", "YYCC::StringHelper::Replace");
|
||||
test_replace = YYCC::StringHelper::Replace("", "", "xy"); // empty source string
|
||||
Assert(test_replace == "", "YYCC::StringHelper::Replace");
|
||||
test_replace = YYCC::StringHelper::Replace(nullptr, "", "xy"); // nullptr source string
|
||||
Assert(test_replace == "", "YYCC::StringHelper::Replace");
|
||||
auto test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("bb"), YYCC_U8("dd")); // normal case
|
||||
Assert(test_replace == YYCC_U8("aaddcc"), YYCC_U8("YYCC::StringHelper::Replace"));
|
||||
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("zz"), YYCC_U8("yy")); // no replace
|
||||
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
|
||||
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8(""), YYCC_U8("zz")); // empty finding
|
||||
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
|
||||
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), nullptr, YYCC_U8("zz")); // nullptr finding
|
||||
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
|
||||
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaaabbaa"), YYCC_U8("aa"), YYCC_U8("")); // no replaced string
|
||||
Assert(test_replace == YYCC_U8("bb"), YYCC_U8("YYCC::StringHelper::Replace"));
|
||||
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaxcc"), YYCC_U8("x"), YYCC_U8("yx")); // nested replacing
|
||||
Assert(test_replace == YYCC_U8("aayxcc"), YYCC_U8("YYCC::StringHelper::Replace"));
|
||||
test_replace = YYCC::StringHelper::Replace(YYCC_U8(""), YYCC_U8(""), YYCC_U8("xy")); // empty source string
|
||||
Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace"));
|
||||
test_replace = YYCC::StringHelper::Replace(nullptr, YYCC_U8(""), YYCC_U8("xy")); // nullptr source string
|
||||
Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace"));
|
||||
|
||||
// Test Upper / Lower
|
||||
auto test_lower = YYCC::StringHelper::Lower("LOWER");
|
||||
Assert(test_lower == "lower", "YYCC::StringHelper::Lower");
|
||||
auto test_upper = YYCC::StringHelper::Upper("upper");
|
||||
Assert(test_upper == "UPPER", "YYCC::StringHelper::Upper");
|
||||
auto test_lower = YYCC::StringHelper::Lower(YYCC_U8("LOWER"));
|
||||
Assert(test_lower == YYCC_U8("lower"), YYCC_U8("YYCC::StringHelper::Lower"));
|
||||
auto test_upper = YYCC::StringHelper::Upper(YYCC_U8("upper"));
|
||||
Assert(test_upper == YYCC_U8("UPPER"), YYCC_U8("YYCC::StringHelper::Upper"));
|
||||
|
||||
// Test Join
|
||||
std::vector<std::string> test_join_container {
|
||||
"", "1", "2", ""
|
||||
std::vector<YYCC::yycc_u8string> test_join_container {
|
||||
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
|
||||
};
|
||||
auto test_join = YYCC::StringHelper::Join(test_join_container, ", ");
|
||||
Assert(test_join == ", 1, 2, ", "YYCC::StringHelper::Join");
|
||||
test_join = YYCC::StringHelper::Join(test_join_container, ", ", true);
|
||||
Assert(test_join == ", 2, 1, ", "YYCC::StringHelper::Join");
|
||||
auto test_join = YYCC::StringHelper::Join(test_join_container, YYCC_U8(", "));
|
||||
Assert(test_join == YYCC_U8(", 1, 2, "), YYCC_U8("YYCC::StringHelper::Join"));
|
||||
test_join = YYCC::StringHelper::Join(test_join_container, YYCC_U8(", "), true);
|
||||
Assert(test_join == YYCC_U8(", 2, 1, "), YYCC_U8("YYCC::StringHelper::Join"));
|
||||
|
||||
// Test Split
|
||||
auto test_split = YYCC::StringHelper::Split(", 1, 2, ", ", ");
|
||||
Assert(test_split.size() == 4u, "YYCC::StringHelper::Split");
|
||||
Assert(test_split[0] == "", "YYCC::StringHelper::Split");
|
||||
Assert(test_split[1] == "1", "YYCC::StringHelper::Split");
|
||||
Assert(test_split[2] == "2", "YYCC::StringHelper::Split");
|
||||
Assert(test_split[3] == "", "YYCC::StringHelper::Split");
|
||||
test_split = YYCC::StringHelper::Split("test", "-");
|
||||
Assert(test_split.size() == 1u, "YYCC::StringHelper::Split");
|
||||
Assert(test_split[0] == "test", "YYCC::StringHelper::Split");
|
||||
test_split = YYCC::StringHelper::Split("test", "");
|
||||
Assert(test_split.size() == 1u, "YYCC::StringHelper::Split");
|
||||
Assert(test_split[0] == "test", "YYCC::StringHelper::Split");
|
||||
auto test_split = YYCC::StringHelper::Split(YYCC_U8(", 1, 2, "), YYCC_U8(", ")); // normal
|
||||
Assert(test_split.size() == 4u, YYCC_U8("YYCC::StringHelper::Split"));
|
||||
Assert(test_split[0] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split"));
|
||||
Assert(test_split[1] == YYCC_U8("1"), YYCC_U8("YYCC::StringHelper::Split"));
|
||||
Assert(test_split[2] == YYCC_U8("2"), YYCC_U8("YYCC::StringHelper::Split"));
|
||||
Assert(test_split[3] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split"));
|
||||
test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("-")); // no matched decilmer
|
||||
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
|
||||
Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split"));
|
||||
test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("")); // empty decilmer
|
||||
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
|
||||
Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split"));
|
||||
test_split = YYCC::StringHelper::Split(YYCC::yycc_u8string_view(), YYCC_U8("")); // empty source string
|
||||
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
|
||||
Assert(test_split[0].empty(), YYCC_U8("YYCC::StringHelper::Split"));
|
||||
|
||||
}
|
||||
|
||||
@ -240,9 +243,9 @@ namespace YYCCTestbench {
|
||||
|
||||
// Test success TryParse
|
||||
#define TEST_MACRO(type_t, value, string_value) { \
|
||||
std::string cache_string(string_value); \
|
||||
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
|
||||
type_t cache; \
|
||||
Assert(YYCC::ParserHelper::TryParse<type_t>(cache_string, cache) && cache == value, "YYCC::StringHelper::TryParse<" #type_t ">"); \
|
||||
Assert(YYCC::ParserHelper::TryParse<type_t>(cache_string, cache) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
|
||||
}
|
||||
|
||||
TEST_MACRO(int8_t, INT8_C(-61), "-61");
|
||||
@ -259,9 +262,9 @@ namespace YYCCTestbench {
|
||||
|
||||
// Test failed TryParse
|
||||
#define TEST_MACRO(type_t, value, string_value) { \
|
||||
std::string cache_string(string_value); \
|
||||
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
|
||||
type_t cache; \
|
||||
Assert(!YYCC::ParserHelper::TryParse<type_t>(cache_string, cache), "YYCC::StringHelper::TryParse<" #type_t ">"); \
|
||||
Assert(!YYCC::ParserHelper::TryParse<type_t>(cache_string, cache), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
|
||||
}
|
||||
|
||||
TEST_MACRO(int8_t, INT8_C(-61), "6161");
|
||||
@ -279,8 +282,8 @@ namespace YYCCTestbench {
|
||||
// Test ToString
|
||||
#define TEST_MACRO(type_t, value, string_value) { \
|
||||
type_t cache = value; \
|
||||
std::string ret(YYCC::ParserHelper::ToString<type_t>(cache)); \
|
||||
Assert(ret == string_value, "YYCC::StringHelper::ToString<" #type_t ">"); \
|
||||
YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString<type_t>(cache)); \
|
||||
Assert(ret == YYCC_U8(string_value), YYCC_U8("YYCC::StringHelper::ToString<" #type_t ">")); \
|
||||
}
|
||||
|
||||
TEST_MACRO(int8_t, INT8_C(-61), "-61");
|
||||
@ -292,11 +295,7 @@ namespace YYCCTestbench {
|
||||
TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161");
|
||||
TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807");
|
||||
TEST_MACRO(bool, true, "true");
|
||||
//{
|
||||
// bool cache = true;
|
||||
// std::string ret(YYCC::ParserHelper::ToString<bool>(cache));
|
||||
// Assert(ret == "true", "YYCC::StringHelper::ToString<bool>");
|
||||
//}
|
||||
|
||||
|
||||
#undef TEST_MACRO
|
||||
}
|
||||
@ -304,32 +303,32 @@ namespace YYCCTestbench {
|
||||
static void DialogTestbench() {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
std::string ret;
|
||||
std::vector<std::string> rets;
|
||||
YYCC::yycc_u8string ret;
|
||||
std::vector<YYCC::yycc_u8string> rets;
|
||||
|
||||
YYCC::DialogHelper::FileDialog params;
|
||||
auto& filters = params.ConfigreFileTypes();
|
||||
filters.Add("Microsoft Word (*.docx; *.doc)", { "*.docx", "*.doc" });
|
||||
filters.Add("Microsoft Excel (*.xlsx; *.xls)", { "*.xlsx", "*.xls" });
|
||||
filters.Add("Microsoft PowerPoint (*.pptx; *.ppt)", { "*.pptx", "*.ppt" });
|
||||
filters.Add("Text File (*.txt)", { "*.txt" });
|
||||
filters.Add("All Files (*.*)", { "*.*" });
|
||||
filters.Add(YYCC_U8("Microsoft Word (*.docx; *.doc)"), { YYCC_U8("*.docx"), YYCC_U8("*.doc") });
|
||||
filters.Add(YYCC_U8("Microsoft Excel (*.xlsx; *.xls)"), { YYCC_U8("*.xlsx"), YYCC_U8("*.xls") });
|
||||
filters.Add(YYCC_U8("Microsoft PowerPoint (*.pptx; *.ppt)"), { YYCC_U8("*.pptx"), YYCC_U8("*.ppt") });
|
||||
filters.Add(YYCC_U8("Text File (*.txt)"), { YYCC_U8("*.txt") });
|
||||
filters.Add(YYCC_U8("All Files (*.*)"), { YYCC_U8("*.*") });
|
||||
params.SetDefaultFileTypeIndex(0u);
|
||||
if (YYCC::DialogHelper::OpenFileDialog(params, ret)) {
|
||||
Console::FormatLine("Open File: %s", ret.c_str());
|
||||
Console::FormatLine(YYCC_U8("Open File: %s"), ret.c_str());
|
||||
}
|
||||
if (YYCC::DialogHelper::OpenMultipleFileDialog(params, rets)) {
|
||||
Console::WriteLine("Open Multiple Files:");
|
||||
Console::WriteLine(YYCC_U8("Open Multiple Files:"));
|
||||
for (const auto& item : rets) {
|
||||
Console::FormatLine("\t%s", item.c_str());
|
||||
Console::FormatLine(YYCC_U8("\t%s"), item.c_str());
|
||||
}
|
||||
}
|
||||
if (YYCC::DialogHelper::SaveFileDialog(params, ret)) {
|
||||
Console::FormatLine("Save File: %s", ret.c_str());
|
||||
Console::FormatLine(YYCC_U8("Save File: %s"), ret.c_str());
|
||||
}
|
||||
params.Clear();
|
||||
if (YYCC::DialogHelper::OpenFolderDialog(params, ret)) {
|
||||
Console::FormatLine("Open Folder: %s", ret.c_str());
|
||||
Console::FormatLine(YYCC_U8("Open Folder: %s"), ret.c_str());
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -353,20 +352,20 @@ namespace YYCCTestbench {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
HMODULE test_current_module;
|
||||
Assert((test_current_module = YYCC::WinFctHelper::GetCurrentModule()) != nullptr, "YYCC::WinFctHelper::GetCurrentModule");
|
||||
Console::FormatLine("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR, test_current_module);
|
||||
Assert((test_current_module = YYCC::WinFctHelper::GetCurrentModule()) != nullptr, YYCC_U8("YYCC::WinFctHelper::GetCurrentModule"));
|
||||
Console::FormatLine(YYCC_U8("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR), test_current_module);
|
||||
|
||||
std::string test_temp;
|
||||
Assert(YYCC::WinFctHelper::GetTempDirectory(test_temp), "YYCC::WinFctHelper::GetTempDirectory");
|
||||
Console::FormatLine("Temp Directory: %s", test_temp.c_str());
|
||||
YYCC::yycc_u8string test_temp;
|
||||
Assert(YYCC::WinFctHelper::GetTempDirectory(test_temp), YYCC_U8("YYCC::WinFctHelper::GetTempDirectory"));
|
||||
Console::FormatLine(YYCC_U8("Temp Directory: %s"), test_temp.c_str());
|
||||
|
||||
std::string test_module_name;
|
||||
Assert(YYCC::WinFctHelper::GetModuleFileName(YYCC::WinFctHelper::GetCurrentModule(), test_module_name), "YYCC::WinFctHelper::GetModuleFileName");
|
||||
Console::FormatLine("Current Module File Name: %s", test_module_name.c_str());
|
||||
YYCC::yycc_u8string test_module_name;
|
||||
Assert(YYCC::WinFctHelper::GetModuleFileName(YYCC::WinFctHelper::GetCurrentModule(), test_module_name), YYCC_U8("YYCC::WinFctHelper::GetModuleFileName"));
|
||||
Console::FormatLine(YYCC_U8("Current Module File Name: %s"), test_module_name.c_str());
|
||||
|
||||
std::string test_localappdata_path;
|
||||
Assert(YYCC::WinFctHelper::GetLocalAppData(test_localappdata_path), "YYCC::WinFctHelper::GetLocalAppData");
|
||||
Console::FormatLine("Local AppData: %s", test_localappdata_path.c_str());
|
||||
YYCC::yycc_u8string test_localappdata_path;
|
||||
Assert(YYCC::WinFctHelper::GetLocalAppData(test_localappdata_path), YYCC_U8("YYCC::WinFctHelper::GetLocalAppData"));
|
||||
Console::FormatLine(YYCC_U8("Local AppData: %s"), test_localappdata_path.c_str());
|
||||
|
||||
#endif
|
||||
}
|
||||
@ -377,29 +376,126 @@ namespace YYCCTestbench {
|
||||
for (const auto& strl : c_UTF8TestStrTable) {
|
||||
test_path /= YYCC::FsPathPatch::FromUTF8Path(strl.c_str());
|
||||
}
|
||||
std::string test_slashed_path(YYCC::FsPathPatch::ToUTF8Path(test_path));
|
||||
YYCC::yycc_u8string test_slashed_path(YYCC::FsPathPatch::ToUTF8Path(test_path));
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
std::wstring wdecilmer(1u, std::filesystem::path::preferred_separator);
|
||||
std::string decilmer(YYCC::EncodingHelper::WcharToUTF8(wdecilmer.c_str()));
|
||||
YYCC::yycc_u8string decilmer(YYCC::EncodingHelper::WcharToUTF8(wdecilmer.c_str()));
|
||||
#else
|
||||
std::string decilmer(1u, std::filesystem::path::preferred_separator);
|
||||
YYCC::yycc_u8string decilmer(1u, std::filesystem::path::preferred_separator);
|
||||
#endif
|
||||
std::string test_joined_path(YYCC::StringHelper::Join(c_UTF8TestStrTable, decilmer.c_str()));
|
||||
YYCC::yycc_u8string test_joined_path(YYCC::StringHelper::Join(c_UTF8TestStrTable, decilmer.c_str()));
|
||||
|
||||
Assert(test_slashed_path == test_joined_path, "YYCC::FsPathPatch");
|
||||
Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::FsPathPatch"));
|
||||
|
||||
}
|
||||
|
||||
class TestConfigManager {
|
||||
public:
|
||||
enum class TestEnum : int8_t {
|
||||
Test1, Test2, Test3
|
||||
};
|
||||
|
||||
TestConfigManager() :
|
||||
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
|
||||
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_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
|
||||
})
|
||||
{}
|
||||
~TestConfigManager() {}
|
||||
|
||||
void PrintSettings() {
|
||||
Console::WriteLine(YYCC_U8("Config Manager Settings:"));
|
||||
|
||||
Console::FormatLine(YYCC_U8("\tint-setting: %" PRIi32), m_IntSetting.Get());
|
||||
Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_FloatSetting.Get());
|
||||
Console::FormatLine(YYCC_U8("\tstring-setting: %s"), m_StringSetting.Get().c_str());
|
||||
|
||||
Console::FormatLine(YYCC_U8("\tbool-setting: %s"), m_BoolSetting.Get() ? YYCC_U8("true") : YYCC_U8("false"));
|
||||
Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_ClampedFloatSetting.Get());
|
||||
Console::FormatLine(YYCC_U8("\tenum-setting: %" PRIi8), static_cast<std::underlying_type_t<TestEnum>>(m_EnumSetting.Get()));
|
||||
}
|
||||
|
||||
YYCC::ConfigManager::NumberSetting<int32_t> m_IntSetting;
|
||||
YYCC::ConfigManager::NumberSetting<float> m_FloatSetting;
|
||||
YYCC::ConfigManager::StringSetting m_StringSetting;
|
||||
|
||||
YYCC::ConfigManager::NumberSetting<bool> m_BoolSetting;
|
||||
YYCC::ConfigManager::NumberSetting<float> m_ClampedFloatSetting;
|
||||
YYCC::ConfigManager::NumberSetting<TestEnum> m_EnumSetting;
|
||||
|
||||
YYCC::ConfigManager::CoreManager m_CoreManager;
|
||||
};
|
||||
|
||||
static void ConfigManagerTestbench() {
|
||||
// init cfg manager
|
||||
TestConfigManager test;
|
||||
|
||||
// test constrain 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"));
|
||||
|
||||
// test modify settings
|
||||
#define TEST_MACRO(member_name, set_val) { \
|
||||
Assert(test.member_name.Set(set_val), YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \
|
||||
Assert(test.member_name.Get() == set_val, YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \
|
||||
}
|
||||
|
||||
TEST_MACRO(m_IntSetting, INT32_C(114));
|
||||
TEST_MACRO(m_FloatSetting, 2.0f);
|
||||
TEST_MACRO(m_StringSetting, YYCC_U8("fuck"));
|
||||
TEST_MACRO(m_BoolSetting, true);
|
||||
TEST_MACRO(m_ClampedFloatSetting, 0.5f);
|
||||
TEST_MACRO(m_EnumSetting, TestConfigManager::TestEnum::Test2);
|
||||
|
||||
#undef TEST_MACRO
|
||||
|
||||
// test save
|
||||
test.PrintSettings();
|
||||
Assert(test.m_CoreManager.Save(), YYCC_U8("YYCC::ConfigManager::CoreManager::Save"));
|
||||
|
||||
// test reset
|
||||
test.m_CoreManager.Reset();
|
||||
test.PrintSettings();
|
||||
Assert(test.m_IntSetting.Get() == INT32_C(0), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||
Assert(test.m_FloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||
Assert(test.m_StringSetting.Get() == YYCC_U8(""), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||
Assert(test.m_BoolSetting.Get() == false, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||
Assert(test.m_EnumSetting.Get() == TestConfigManager::TestEnum::Test1, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||
|
||||
// test load
|
||||
Assert(test.m_CoreManager.Load(), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
test.PrintSettings();
|
||||
Assert(test.m_IntSetting.Get() == INT32_C(114), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
Assert(test.m_FloatSetting.Get() == 2.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
Assert(test.m_StringSetting.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
Assert(test.m_BoolSetting.Get() == true, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
Assert(test.m_ClampedFloatSetting.Get() == 0.5f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
Assert(test.m_EnumSetting.Get() == TestConfigManager::TestEnum::Test2, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** args) {
|
||||
//YYCCTestbench::ConsoleTestbench();
|
||||
// common testbench
|
||||
// normal
|
||||
YYCCTestbench::EncodingTestbench();
|
||||
YYCCTestbench::StringTestbench();
|
||||
YYCCTestbench::ParserTestbench();
|
||||
YYCCTestbench::DialogTestbench();
|
||||
YYCCTestbench::ExceptionTestbench();
|
||||
YYCCTestbench::WinFctTestbench();
|
||||
YYCCTestbench::FsPathPatch();
|
||||
// advanced
|
||||
YYCCTestbench::ConfigManagerTestbench();
|
||||
|
||||
// testbench which may terminal app or ordering input
|
||||
YYCCTestbench::ConsoleTestbench();
|
||||
YYCCTestbench::DialogTestbench();
|
||||
YYCCTestbench::ExceptionTestbench();
|
||||
}
|
||||
|
Reference in New Issue
Block a user