1
0

refactor: refactor doc layout

This commit is contained in:
2025-12-25 15:12:29 +08:00
parent 45f32297da
commit 337734d340
18 changed files with 52 additions and 137 deletions

View File

@@ -0,0 +1,200 @@
namespace YYCC::ArgParser {
/**
\page arg_parser Universal Argument Parser
YYCC::ArgParser provides an universal way to parsing command line arguments.
Universal argument parser has similar design with universal config manager,
it is highly recommand that read \ref config_manager chapter first,
because you will have a clear understanding of this namespace after reading universal config manager chapter.
There is an example about how to use universal argument parser.
In following content, we will describe it in detail.
\code{.cpp}
class TestArgParser {
public:
TestArgParser() :
m_IntArgument(YYCC_U8("int"), YYCC_U8_CHAR('i'), YYCC_U8("integral argument"), YYCC_U8("114514")),
m_FloatArgument(nullptr, YYCC_U8_CHAR('f'), nullptr, nullptr, true),
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the test of argument parser."), {
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
&m_BoolArgument, &m_ClampedFloatArgument
}) {}
~TestArgParser() {}
YYCC::ArgParser::NumberArgument<int32_t> m_IntArgument;
YYCC::ArgParser::NumberArgument<float> m_FloatArgument;
YYCC::ArgParser::StringArgument m_StringArgument;
YYCC::ArgParser::SwitchArgument m_BoolArgument;
YYCC::ArgParser::NumberArgument<float> m_ClampedFloatArgument;
YYCC::ArgParser::OptionContext m_OptionContext;
};
// Initialize argument parser.
TestArgParser test;
// Get argument list for parsing from standard C main function.
auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv);
// Start parsing
test.Parse(al);
// Get captured string argument
if (test.m_StringArgument.IsCaptured())
auto val = test.m_StringArgument.Get();
\endcode
These code can resolve following command line:
\code{.sh}
exec -i 114514 -f 2.0 --string fuck -b --clamped-float 0.5
\endcode
For convenience, we define following terms used in this article.
\li Every items in command line: Argument.
\li \c -i, \c --clamped-float: \b Switch / \b Option. the argument starts with dash or double dash.
\li \c 114514: \b Value. the value of switch.
\section arg_parser__argument Argument
Argument is the leaf of argument parser.
It has the same position as setting in universal config manager.
\subsection arg_parser__argument__presets Argument Presets
Like setting in universal config manager,
we also provide various common used argument presets.
Current'y we support following argument presets:
\li NumberArgument: The argument storing arithmetic type (except \c bool) inside. Such as <TT>-i 114514</TT> in example.
\li StringArgument: The argument storing string inside. Such as <TT>--string fuck</TT> in example.
\li SwitchArgument: The argument storing nothing. It is just a simple switch. Such as <TT>-b</TT> in example.
When constructing these argument,
you need provide one from long name or short name, or both of them.
Short name is the argument starting with dash and long name starts with double dash.
You don't need add dash or double dash prefix when providing these names.
Please note only ASCII characters, which can be displayed on screen, can be used in these names.
Optionally, you can provide description when constructing,
which will tell user how this switch does and more infomation about this switch.
And, you can add an example to tell user which value is valid.
Next, you can specify an argument to be optional.
Optional argument can be absent in command line.
Oppositely, non-optional argument must be presented in command line,
otherwise parser will return false to indicate an error.
For checking whether an optional argument is specified,
please call AbstractArgument::IsCaptured().
Last, you can optionally assign a constraint to it,
to help argument limit its value.
However SwitchArgument must be optional argument.
Because it is true if user specify it explicit it,
and will be false if user do not give this flag.
SwitchArgument doesn't have constraint features,
because it doesn't store any value inside.
Thus no need to limit this.
\subsection arg_parser__argument__custom Custom Argument
In most cases, the combination use of argument presets and constraints is enough.
However, if you still are urge to create your personal argument,
please inherit AbstractArgument and implement essential class functions.
For the class functions you need to implement,
please refer to our argument presets.
\section arg_parser__argument_list Argument List
Argument list is a struct used by parser for parsing.
It is a higher wrapper of a simple list containing argument items.
We provide 2 ways to get argument list.
\li ArgumentList::CreateFromStd: Create argument list from standard C main function parameters.
\li ArgumentList::CreateFromWin32: Create argument list from Win32 functions in Windows.
You should use this function in Windows instead of ArgumentList::CreateFromStd.
Because the command line passed in standard C main function has encoding issue in Windows.
Use this function you will fetch correct argument list especially command including non-ASCII characters.
Please note the first argument in given command line will be stripped.
Because in most cases it point to the executable self,
and should not be seen as the part of argument list.
\section arg_parser__option_context Option Context
Please note any unknow argument will let the parser return false.
This is different with other argument parsers.
In other common argument parsers,
they will collect all unknow argument as positional argument,
or just simply ignore them.
OptionContext also will not add \c -h or \c --help switch automatically.
This is also differnent with other parsers.
You should manually add it.
However, OptionContext provide a universal help print function, OptionContext::Help.
You can directly call it to output help text if you needed (fail to parse or user order help).
\section arg_parser__limitation Limitation
This universal argument parser is a tiny parser.
It only just fulfill my personal requirements.
So it only accepts limited command line syntax.
In following content I will tell you some syntaxes which this parser \b not accept.
\subsection arg_parser__limitation__flag_combination Flag Combination
\code{.sh}
exec -l -s -h
exec -lsh
\endcode
Parser accept first line but not accept the second line.
You must write these flags independently.
\subsection arg_parser__limitation__equal_symbol Equal Symbol
\code{.sh}
exec --value 114514
exec --value=114514
exec --value:114514
\endcode
Parser only accept first line command.
You can not use equal symbol or any other symbol to assign value for specified argument.
You must write value after the argument immediately please.
\subsection arg_parser__limitation__variable_argument Variable Argument
\code{.sh}
exec -DSOME_VARABLE=SOME_VALUE
exec -D SOME_VARIABLE=SOME_VALUE
\endcode
Parser only accept second line.
However you nned to write a custom argument or constraint to holding this value.
\subsection arg_parser__limitation__switch_dependency Switch Dependency
\code{.sh}
exec --action-a --action-b
\endcode
For command line written above,
if you hope \c --action-a and \c --action-b is exclusive,
or \c --action-b only be valid if \c --action-a specified,
you should manually implement this.
Parser don't have such features to process this switch dependency.
The thing you need to do is set these switches are \b not optional.
And after parser do a success parsing,
manually calling AbstractArgument::IsCaptured to fetch whether corresponding switches are captured,
then do your personal dependency check.
*/
}

View File

@@ -0,0 +1,151 @@
namespace YYCC::ConfigManager {
/**
\page config_manager Universal Config Manager
YYCC::ConfigManager give programmer an universal way to manage its program settings.
There is an example about how to use universal config manager.
In following content, we will describe it in detail.
\code
class TestConfigManager {
public:
enum class TestEnum : int8_t {
Test1, Test2, Test3
};
TestConfigManager() :
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
m_FloatSetting(YYCC_U8("float-setting"), 0.0f, YYCC::Constraints::GetNumberRangeConstraint<float>(-1.0f, 1.0f)),
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
&m_IntSetting, &m_StringSetting, &m_FloatSetting, &m_EnumSetting
})
{}
~TestConfigManager() {}
YYCC::ConfigManager::NumberSetting<int32_t> m_IntSetting;
YYCC::ConfigManager::StringSetting m_StringSetting;
YYCC::ConfigManager::NumberSetting<float> m_FloatSetting;
YYCC::ConfigManager::NumberSetting<TestEnum> m_EnumSetting;
YYCC::ConfigManager::CoreManager m_CoreManager;
};
// Initialize config manager
TestConfigManager test;
// Load settings.
test.m_CoreManager.Load()
// Get string setting value.
auto val = test.m_StringSetting.Get();
\endcode
\section config_manager__setting Setting
Setting can be seen as the leaf of the config tree.
Each setting describe a single configuration entry.
\subsection config_manager__setting__presets Setting Presets
We currently provide 2 setting preset classes which you can directly use.
\li NumberSetting: The setting storing a number inside.
It is a template class. Support all arithmetic and enum types (integral, floating point, bool, enum).
\li StringSetting: The setting storing a string inside.
When constructing these settings,
you need to provide its unique name which will be used when saving to file or reading from file.
Also you need to provide a default value for it.
It will be used when fail to read file or initializing itself.
Optionally, you also can provide a constraint to setting.
Constraint is the struct instructing library to limit value in specified range.
It usually is used for making sure the setting stored value is valid.
See \ref constraints chapters to know how we provide constraints.
\subsection config_manager__setting__custom Custom Setting
In most cases, the combination use of setting presets and constraints is enough.
However, if you still are urge to create your personal setting,
please inherit AbstractSetting and implement essential class functions.
For the class functions you need to implement,
please refer to our setting presets, NumberSetting and StringSetting.
\section config_manager__core_manager Core Manager
CoreManager manage a collection of settings.
And have responsibility to reading and writing config file.
We highly suggest that you create a personal config manager class like example does.
Then put essential settings and core manager inside it.
Please note you must place core manager after all settings.
Because the order of C++ initializing its class member is the order you declared them.
The constructor of core manager need the pointer to all it managed settings,
so it must be initialized after initializing all settings.
When initializing core manager, you need assign config file path first.
Then you need specify a version number.
Version number is important.
It will be used when reading config file and only can be increased if needed (version can not downgrade).
The last argument is an initializer list which contain the \b pointer to all settings this manager managed.
When executing YYCC::ConfigManager::CoreManager::Load to load configs, it will perform following steps one by one:
<UL>
<LI>
Open given config file.
<UL>
<LI>
If given file is not existing, loading function will simply return and all configs will be reset to its default value.
</LI>
<LI>
Success to open file, go to next step.
</LI>
</UL>
</LI>
<LI>
Fetch version number from file.
<UL>
<LI>
If fail to read version number from file, loading function will simply return and all configs will be reset to its default value.
</LI>
<LI>
If the version of config file is higher than your specified version number when constructing this class,
core manager will assume you are trying to read a config file created by a higher version program,
and will reject reading and use default value for all settings.
</LI>
<LI>
If the version of config file is lower than your specified version number,
core manager will try to read config file and do proper migration (set default value for configs which do not existing) if possible.
</LI>
<LI>
If the version of config file is equal than your specified version number,
core manager will read config file normally.
</LI>
</UL>
</LI>
<LI>
Read config file body.
<UL>
<LI>
If any IO error occurs when reading, loading function will simply return.
All read config will keep their read value and all configs which has not been read will keep their default value.
</LI>
<LI>
If some config can not parse binary data to its type,
this config will be skipped and core manager will process next config.
This config will keep its default value.
</LI>
</UL>
</LI>
</UL>
All of these scenarios can be found by the return value of loading function.
The return type of loading function, ConfigLoadResult is a flag enum.
You can find whether loading process happend specified issue by using bitwise operation on it.
*/
}

View File

@@ -0,0 +1,181 @@
namespace YYCC::ConsoleHelper {
/**
\page console_helper Console Helper
This helper provide console related stuff.
This helper includes 2 parts.
First part is console color.
It was constituted by a bunch of macros.
The second part is universal console IO function because Windows is lacking in UTF8 console IO.
All of these parts will be introduced in following content.
\section console_helper__color Console Color
YYCC::ConsoleHelper provide a bunch of macros which can allow you output colorful text in terminal.
Supported color is limited in 16 colors,
because these color is implemented by ASCII Escape Code: https://en.wikipedia.org/wiki/ANSI_escape_code .
So if your terminal do not support this, such as default Windows terminal, or teletypewriter,
you will see some unrecognised characters surrounding with your output.
That's ASCII Escape Code.
\subsection console_helper__color__enable_win_color Enable Color in Windows Console
As we introduced in above,
you may know Windows console does not support ASCII Escape Code color in default.
However #EnableColorfulConsole can fix this issue.
#EnableColorfulConsole will forcely enable ASCII Escape Code support in Windows console if possible.
Thus you can write colorful text in Windows console freely.
We suggest you to call this function at the beginning of program.
Considering most Linux console supports ASCII Escape Code very well,
this function does nothing in non-Windows platform.
So it is not essential that brack this function calling with Windows-only \c \#if.
\subsection console_helper__color__common Common Usage
For common scenarios, you can use macro like this:
\code
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("Light Red Text")));
YYCC::ConsoleHelper::WriteLine(YYCC_U8("I am " YYCC_COLOR_LIGHT_RED("Light Red")));
\endcode
In first line, it will make <TT>"Light Red Text"</TT> to be shown in light red color.
And for second line, it will make <TT>"Light Red"</TT> to be shown in light red color,
but <TT>"I am "</TT> will keep default console font color.
You also may notice this macro is used with YYCC_U8 macro.
Because #WriteLine only accept UTF8 argument.
So please note if you use console color macro with YYCC_U8,
please make YYCC_U8 always is located the outside.
Otherwise, YYCC_U8 will fail to make the whole become UTF8 stirng as we introduced in \ref library_encoding.
Because console color macro is implemented by string literal concatenation internally.
YYCC_COLOR_LIGHT_RED is a member in YYCC_COLOR macro family.
YYCC_COLOR macro family has 16 members for 16 different colors:
\li YYCC_COLOR_BLACK
\li YYCC_COLOR_RED
\li YYCC_COLOR_GREEN
\li YYCC_COLOR_YELLOW
\li YYCC_COLOR_BLUE
\li YYCC_COLOR_MAGENTA
\li YYCC_COLOR_CYAN
\li YYCC_COLOR_WHITE
\li YYCC_COLOR_LIGHT_BLACK
\li YYCC_COLOR_LIGHT_RED
\li YYCC_COLOR_LIGHT_GREEN
\li YYCC_COLOR_LIGHT_YELLOW
\li YYCC_COLOR_LIGHT_BLUE
\li YYCC_COLOR_LIGHT_MAGENTA
\li YYCC_COLOR_LIGHT_CYAN
\li YYCC_COLOR_LIGHT_WHITE
\subsection console_helper__color__embedded Embedded Usgae
In some cases, you want change console at some time point and reset it in another time point.
You can use color macros like following example:
\code
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLORHDR_LIGHT_BLUE));
// Write as much as you liked
YYCC::ConsoleHelper::WriteLine(YYCC_U8("some string"));
YYCC::ConsoleHelper::WriteLine(YYCC_U8("another string"));
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLORTAIL));
\endcode
At first line, we output YYCC_COLORHDR_LIGHT_BLUE which is in YYCC_COLORHDR macro family.
It is colorful text ASCII Escape Code head.
It will make all following output become light blue color,
until the last line we output YYCC_COLORTAIL to reset console color to original color.
Same as YYCC_COLOR macro family,
YYCC_COLORHDR macro family also has 16 members for 16 different colors:
\li YYCC_COLORHDR_BLACK
\li YYCC_COLORHDR_RED
\li YYCC_COLORHDR_GREEN
\li YYCC_COLORHDR_YELLOW
\li YYCC_COLORHDR_BLUE
\li YYCC_COLORHDR_MAGENTA
\li YYCC_COLORHDR_CYAN
\li YYCC_COLORHDR_WHITE
\li YYCC_COLORHDR_LIGHT_BLACK
\li YYCC_COLORHDR_LIGHT_RED
\li YYCC_COLORHDR_LIGHT_GREEN
\li YYCC_COLORHDR_LIGHT_YELLOW
\li YYCC_COLORHDR_LIGHT_BLUE
\li YYCC_COLORHDR_LIGHT_MAGENTA
\li YYCC_COLORHDR_LIGHT_CYAN
\li YYCC_COLORHDR_LIGHT_WHITE
However YYCC_COLORTAIL is YYCC_COLORTAIL.
There is no other variant for different colors.
Because all tail of colorful ASCII Escape Code is same.
\section console_helper__universal_io Universal IO Function
\subsection console_helper__universal_io__why Why?
Windows console doesn't support UTF8 very well.
The standard input output functions can not work properly with UTF8 on Windows.
So we create this namespace and provide various console-related functions
to patch Windows console and let it more like the console in other platforms.
The function provided in this function can be called in any platforms.
In Windows, the implementation will use Windows native function,
and in other platform, the implementation will redirect request to standard C function like \c std::fputs and etc.
So the programmer do not need to be worried about which function should they use,
and don't need to use macro to use different IO function in different platforms.
It is just enough that fully use the functions provided in this namespace.
All IO functions this namespace provided are UTF8-based.
It also means that input output string should always be UTF8 encoded.
\subsection console_helper__universal_io__input Input Functions
Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
This action actually is removing all CR chars in result string.
This behavior affect nothing in most cases but it still is possible break something in some special case.
Due to implementation, if you decide to use this function,
you should give up using any other function to read stdin stream,
such as \c std::gets() and \c std::cin.
Because this function may read chars which is more than needed.
These extra chars will be stored in this function and can be used next calling.
But these chars can not be visited by stdin again.
This behavior may cause bug.
So if you decide using this function, stick on it and do not change.
Due to implementation, this function do not support hot switch of stdin.
It means that stdin can be redirected before first calling of this function,
but it should not be redirected during program running.
The reason is the same one introduced above.
\subsection console_helper__universal_io__output Output Functions
In current implementation, EOL will not be converted automatically to CRLF.
This is different with other stream read functions provided in this namespace.
Comparing with other stream read functions provided in this namespace,
stream write function support hot switch of stdout and stderr.
Because they do not have internal buffer storing something.
In this namespace, there are various stream write function.
There is a list telling you how to choose one from them for using:
\li Functions with leading "Err" will write data into stderr,
otherwise they will write data into stdout.
\li Functions with embedded "Format" are output functions with format feature
like \c std::fprintf(), otherwise the functions with embedded "Write" will
only write plain string like \c std::fputs().
\li Functions with trailing "Line" will write extra EOL to break current line.
This is commonly used, otherwise functions will only write the text provided by arguments,
without adding something.
*/
}

View File

@@ -0,0 +1,148 @@
namespace YYCC::EncodingHelper {
/**
\page encoding_helper Encoding Helper
YYCC::EncodingHelper namespace include all encoding related functions:
\li The convertion between ordinary string and UTF8 string which has been introduced in chapter \ref library_encoding.
\li Windows specific convertion between \c WCHAR, UTF8 string and string encoded by other encoding.
\li The convertion among UTF8, UTF16 and UTF32.
\section encoding_helper__ordinary_utf8_conv Ordinary & UTF8 Convertion
These convertion functions have been introduced in previous page.
See \ref library_encoding for more infomation.
YYCC supports following convertions:
\li #ToUTF8: Convert ordinary string to UTF8 string.
\li #ToUTF8View: Same as ToUTF8, but return string view instead.
\li #ToOrdinary: Convert UTF8 string to ordinary string.
\li #ToOrdinaryView: Same as ToOrdinary, but return string view instead.
\section encoding_helper__win_conv Windows Specific Convertion
During Windows programming, the convertion between Microsoft specified \c wchar_t and \c char is an essential operation.
Because Windows has 2 different function system, the functions ended with A and the functions ended with W.
(Microsoft specified \c wchar_t is \c 2 bytes long. It's different with Linux defined common 4 bytes long).
Thus YYCC provides these convertion functions in Windows to help programmer have better programming experience.
These functions are Windows specific, so they will be invisible in other platforms.
Please use them carefully (make sure that you are using them only in Windows environment).
YYCC supports following convertions:
\li #WcharToChar: Convert \c wchar_t string to code page specified string.
\li #CharToWchar: The reversed convertion of WcharToChar.
\li #CharToChar: Convert string between 2 different code pages. It's a shortcut of calling CharToWchar and WcharToChar successively.
\li #WcharToUTF8: Convert \c wchar_t string to UTF8 string.
\li #UTF8ToWchar: The reversed convertion of WcharToUTF8.
\li #CharToUTF8: Convert code page specified string to UTF8 string.
\li #UTF8ToChar: The reversed convertion of CharToUTF8.
Code Page is a Windows concept.
If you don't understand it, please view corresponding Microsoft documentation.
\section encoding_helper__utf_conv UTF8 UTF16 UTF32 Convertion
The convertion between UTF8, UTF16 and UTF32 is not common but essential.
These convertions can be achieved by standard library functions and classes.
(they are actually done by standard library functions in our implementation)
But we provided functions are easy to use and have clear interface.
These functions are different with the functions introduced above.
They can be used in any platform, not confined in Windows platforms.
YYCC supports following convertions:
\li #UTF8ToUTF16: Convert UTF8 string to UTF16 string.
\li #UTF16ToUTF8: The reversed convertion of UTF8ToUTF16.
\li #UTF8ToUTF32: Convert UTF8 string to UTF32 string.
\li #UTF32ToUTF8: The reversed convertion of UTF8ToUTF32.
\section encoding_helper__overloads Function Overloads
Every encoding convertion functions (except the convertion between UTF8 and ordinary string) have 4 different overloads for different scenarios.
Take #WcharToChar for example.
There are following 4 overloads:
\code
bool WcharToChar(const std::wstring_view& src, std::string& dst, UINT code_page);
bool WcharToChar(const wchar_t* src, std::string& dst, UINT code_page);
std::string WcharToChar(const std::wstring_view& src, UINT code_page);
std::string WcharToChar(const wchar_t* src, UINT code_page);
\endcode
\subsection encoding_helper__overloads_destination Destination String
According to the return value, these 4 overload can be divided into 2 types.
The first type returns bool. The second type returns \c std::string instance.
For the first type, it always return bool to indicate whether the convertion is success.
Due to this, the function must require an argument for holding the result string.
So you can see the functions belonging to this type always require a reference to \c std::string in argument.
Oppositely, the second directly returns result by return value.
It doesn't care the success of convertion and will return empty string if convertion failed.
Programmer can more naturally use it because the retuen value itself is the result.
There is no need to declare a variable before calling convertion function for holding result.
All in all, the first type overload should be used in strict scope.
The success of convertion will massively affect the behavior of your following code.
For example, the convertion code is delivered to some system function and it should not be empty and etc.
The second type overload usually is used in lossen scenarios.
For exmaple, this overload usually is used in console output because it usually doesn't matter.
There is no risk even if the convertion failed (just output a blank string).
For the first type, please note that there is \b NO guarantee that the argument holding return value is not changed.
Even the convertion is failed, the argument holding return value may still be changed by function itself.
In this case, the type of result is \c std::string because this is function required.
In other functions, such as #WcharToUTF8, the type of result can be \c yycc_u8string or etc.
So please note the type of result is decided by convertion function itself, not only \c std::string.
\subsection encoding_helper__overloads__source Source String
According to the way providing source string,
these 4 overload also can be divided into 2 types.
The first type take a reference to constant \c std::wstring_view.
The second type take a pointer to constant \c wchar_t.
For first type, it will take the whole string for convertion, including \b embedded NUL terminal.
Please note we use string view as argument.
It is compatible with corresponding raw string pointer and string container.
So it is safe to directly pass \c std::wstring for this function.
For second type, it will assume that you passed argument is a NUL terminated string and send it for convertion.
The result is clear.
If you want to process string with \b embedded NUL terminal, please choose first type overload.
Otherwise the second type overload is enough.
Same as destination string, the type of source is also decided by the convertion function itself.
For exmaple, the type of source in #UTF8ToWchar is \c yycc_u8string_view and \c yycc_char8_t,
not \c std::wstring and \c wchar_t.
\subsection encoding_helper__overloads__extra Extra Argument
There is an extra argument called \c code_page for #WcharToChar.
It indicates the code page of destination string,
because this function will convert \c wchar_t string to the string with specified code page encoding.
Some convertion functions have extra argument like this,
because they need more infomations to decide what they need to do.
Some convertion functions don't have extra argument.
For exmaple, the convertion between \c wchar_t string and UTF8 string.
Because both source string and destination string are concrete.
There is no need to provide any more infomations.
\subsection encoding_helper__overloads__conclusion Conclusion
Mixing 2 types of source string and 2 types of destination string,
we have 4 different overload as we illustrated before.
Programmer can use them freely according to your requirements.
And don't forget to provide extra argument if function required.
*/
}

View File

@@ -0,0 +1,110 @@
namespace YYCC::ExceptionHelper {
/**
\page exception_helper Unhandled Exception Handler
Most Linux users are familiar with core dump.
However core dump is a tough work on Windows especially most Windows users are naive for getting core dump.
So it is essential to make an easy-to-visit core dump Feature for Windows program.
YYCC provides this feature in YYCC::ExceptionHelper.
You may know Google also has a similar and universal project called Crashpad used by Google Chrome.
That's right. But it is too heavy.
I just want to implement a tiny but worked core dump feature on Windows.
This module is Windows specific.
It will be invisible on other platforms.
\section exception_helper__usage Usage
\subsection exception_helper__usage__code Register Code
In most scenarios, programmer only need call #Register when program started or module loaded.
And call #Unregister when program exited or module unloaded.
All details are hidden by these 2 feature.
Programmer do not need worried about the implementation of unhandled exception handler.
Optionally, you can provide a function pointer during calling #Register as a callback.
The prototype of this function pointer is #ExceptionCallback.
This callback will be called if any unhandled exception happened.
It provides 2 pathes to log file and core dump file respectively.
So that you can use an explicit way, e.g. \c MessageBox, to tell user exception happened and where are the log files,
especially in GUI application because the default output stream, \c stderr, is invisible in GUI application.
However, please note the pathes provided by callback may be empty.
In this case, it means that handler fail to create corresponding log files.
Also, if you trying to register unhandled exception handler on the same process in different module with different callback,
only the callback provided in first success registering will be called when unhandled exception happened,
due to \ref exception_helper__notes__singleton design.
\subsection exception_helper__usage__location Location
When unhandled exception occurs,
unhandled exception handler will try to record error log and core dump in following path:
\li Error Log: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.log</TT>
\li Core Dump: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.dmp</TT>
The italic characters <I>program.exe</I> and <I>pid</I> will be replaced by program name and process ID respectively at runtime.
Directory <TT>\%LOCALAPPDATA\%\\CrashDumps</TT> also is Windows used crash dump directory.
So you may see some other core dumps done by Windows in it.
\subsection exception_helper__usage__last_remedy Last Remedy
If unhandled exception handler occurs error, these stuff may not be generated correctly.
The end user may not find them and send them to you.
There is a last remedy for this scenario.
Unhandled exception handler will still output error log in \c stderr no matter whether error log or core dump is created.
So end user always can fetch error log from console.
You only need to instruct end user open command prompt, launch application, reproduce error and get the output error log in console.
In this case, you can not get core dump. But you can get error log.
It is not good for debugging but it is better than nothing.
Also please note the last remedy may still have a little bit possibility to occurs error and output nothing,
especially the error occurs in back trace function.
There is no guaranteen that unhandled exception handler must generate error log and core dump.
\section exception_helper__notes Notes
\subsection exception_helper__notes__thread_safe Thread Safe
All exposed functions in YYCC::ExceptionHelper are thread safe.
The implementation uses \c std:mutex to ensure this.
\subsection exception_helper__notes__singleton Singleton Handler
YYCC::ExceptionHelper also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
For example, you have an executable program A.exe, and 2 dynamic libraries B.dll and C.dll.
A.exe and B.dll use YYCC unhandled exception handler feature but C.dll not.
A.exe will load B.dll and C.dll at runtime.
Although both A.exe and B.dll call #Register,
when unhandled exception occurs, there is only one error report output,
which may be generated by A.exe or B.dll accoridng to their order of loading.
The core purpose of this is making sure the program will not output too many error report for the same unhandled exception,
no matter how many modules calling #Register are loaded.
Only one error report is enough.
More precisely, we use \c CreateMutexW to create an unique mutex in Windows global scope,
to make sure #Register only run once in the same process.
It is very like the implementation of singleton application.
\subsection exception_helper__notes__recursive_calling Recursive Calling
The implementation of unhandled exception handler may also will throw exception.
This will cause infinite recursive calling.
YYCC::ExceptionHelper has internal mechanism to prevent this bad case.
If this really happened, the handler will quit silent and will not cause any issue.
Programmer don't need to worry about this.
\subsection exception_helper__notes__user_callback The Timing of User Callback
The timing of calling user callback is the tail of unhandled exception handler.
It means that all log and coredump have been written if possible before calling callback.
Because user callback may still raise exception.
We want all essential log files has been written before calling it,
so that at least we can visit them on disk or console.
*/
}