feat: add more output functions in console helper.
- add more output functions in console helper. * split output functions by format string and plain string as Format and Write. * add functions which output to stderr. - improve document.
This commit is contained in:
parent
0ef1939e6e
commit
c32806ea03
|
@ -127,10 +127,10 @@ namespace YYCC::ConsoleHelper {
|
|||
return real_return_buffer;
|
||||
}
|
||||
|
||||
static void WinConsoleWrite(const std::string& strl) {
|
||||
static void WinConsoleWrite(const std::string& strl, bool to_stderr) {
|
||||
// Prepare some Win32 variables
|
||||
// fetch stdout handle first
|
||||
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
HANDLE hStdOut = GetStdHandle(to_stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
|
||||
DWORD dwConsoleMode;
|
||||
DWORD dwWrittenNumberOfChars;
|
||||
|
||||
|
@ -198,35 +198,80 @@ namespace YYCC::ConsoleHelper {
|
|||
#endif
|
||||
}
|
||||
|
||||
static void RawWrite(const std::string& strl) {
|
||||
template<bool bNeedFmt, bool bIsErr, bool bHasEOL>
|
||||
static void RawWrite(const char* 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;
|
||||
if constexpr (bNeedFmt) {
|
||||
// treat as format string
|
||||
va_list argcpy;
|
||||
va_copy(argcpy, argptr);
|
||||
strl = YYCC::StringHelper::VPrintf(u8_fmt, argcpy);
|
||||
va_end(argcpy);
|
||||
} else {
|
||||
// treat as plain string
|
||||
strl = u8_fmt;
|
||||
}
|
||||
// Checkout whether add EOL
|
||||
if constexpr (bHasEOL) {
|
||||
strl += "\n";
|
||||
}
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// call Windows specific writer
|
||||
WinConsoleWrite(strl);
|
||||
|
||||
WinConsoleWrite(strl, bIsErr);
|
||||
#else
|
||||
|
||||
// in linux, directly use C function to write.
|
||||
std::fputs(strl.c_str(), stdout);
|
||||
|
||||
std::fputs(strl.c_str(), to_stderr ? stderr : stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Write(const char* u8_fmt, ...) {
|
||||
void Format(const char* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite(YYCC::StringHelper::VPrintf(u8_fmt, argptr));
|
||||
RawWrite<true, false, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void WriteLine(const char* u8_fmt, ...) {
|
||||
void FormatLine(const char* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
std::string cache(YYCC::StringHelper::VPrintf(u8_fmt, argptr));
|
||||
cache += "\n";
|
||||
RawWrite(cache);
|
||||
RawWrite<true, false, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void Write(const char* u8_strl) {
|
||||
RawWrite<false, false, false>(u8_strl, va_list());
|
||||
}
|
||||
|
||||
void WriteLine(const char* u8_strl) {
|
||||
RawWrite<false, false, true>(u8_strl, va_list());
|
||||
}
|
||||
|
||||
void ErrFormat(const char* 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, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, true, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void ErrWrite(const char* u8_strl) {
|
||||
RawWrite<false, true, false>(u8_strl, va_list());
|
||||
}
|
||||
|
||||
void ErrWriteLine(const char* u8_strl) {
|
||||
RawWrite<false, true, true>(u8_strl, va_list());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,64 @@
|
|||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief The namespace providing universal Console visiting functions like C-Sharp Console class.
|
||||
* @details
|
||||
* \par Why this Namespace
|
||||
* Windows console doesn't support UTF8 very well.
|
||||
* The standard input output functions can not work properly on Windows with UTF8.
|
||||
* 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.
|
||||
* \par
|
||||
* 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 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.
|
||||
* \par
|
||||
* All IO functions this namespace provided are UTF8-based.
|
||||
* It also means that input output string should always be UTF8 encoded.
|
||||
*
|
||||
* \par 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.
|
||||
* \par
|
||||
* Due to implementation, if you decide to use this function,
|
||||
* you should give up using any other function to read stdin stream,
|
||||
* such as std::gets() and 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.
|
||||
* \par
|
||||
* 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.
|
||||
*
|
||||
* \par 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.
|
||||
* \par
|
||||
* 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.
|
||||
* \par
|
||||
* 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 std::fprintf(), otherwise the functions with embedded "Write" will
|
||||
* only write plain string like 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.
|
||||
*/
|
||||
namespace YYCC::ConsoleHelper {
|
||||
|
||||
#define YYCC_COLORHDR_BLACK "\033[30m"
|
||||
|
@ -56,54 +114,62 @@ namespace YYCC::ConsoleHelper {
|
|||
|
||||
/**
|
||||
* @brief Universal console read function
|
||||
* @details This function is more like C# Console.ReadLine().
|
||||
* @return
|
||||
* The UTF8 encoded string this function read. EOL is excluded.
|
||||
* Empty string if user just press Enter key or function failed.
|
||||
* @remarks
|
||||
* This function is more like C# Console.ReadLine().
|
||||
* It read user input with UTF8 encoding until reaching EOL.
|
||||
*
|
||||
* This function provide an universal, platform-independent way to read UTF8 string from console,
|
||||
* no matter whether it is redirected.
|
||||
* @return The UTF8 encoded string this function read. EOL is excluded.
|
||||
* @remarks In Windows, this function will try use native Win32 function for reading,
|
||||
* because standard C/C++ function can not handle UTF8 input on Windows normally.
|
||||
* In other platforms, this function will redirect request to std::readline with std::cin,
|
||||
* which is all programmer commonly used method.
|
||||
* It also mean that we assume stdin is encoded by UTF8 on these platforms.
|
||||
*
|
||||
* Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
|
||||
* This action is actually remove all CR chars in result string.
|
||||
*
|
||||
* \par
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* @brief Universal console write function
|
||||
* @details This function is more like C# Console.Write().
|
||||
* It write user given UTF8 string into console.
|
||||
*
|
||||
* This function provide an universal, platform-independent way to write UTF8 string into console,
|
||||
* no matter whether it is redirected.
|
||||
* @param u8_fmt[in] The format string.
|
||||
* If you just want to write a pure string, you should escape formatter chars (%) in this string,
|
||||
* because this function always take this parameter as a format string.
|
||||
* @param ...[in] The arguments to be formatted.
|
||||
* @remarks In Windows, this funcion will use native Win32 function for writing,
|
||||
* because standard C/C++ function can not handle UTF8 output on Windows normally.
|
||||
* In other platforms, this function will redirect request to std::fprintf with stdout,
|
||||
* which is all programmer commonly used method.
|
||||
* It also mean that we assume stdout is encoded by UTF8 on these platforms.
|
||||
* @brief Universal console write function with format feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void Write(const char* u8_fmt, ...);
|
||||
void Format(const char* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console write function with automatic EOL
|
||||
* @details This function is same as Write(const char*, ...),
|
||||
* but it will automatically add EOL in output to break line.
|
||||
* This is commonly used.
|
||||
* @param u8_fmt[in] The format string.
|
||||
* If you just want to write a pure string, you should escape formatter chars (%) in this string,
|
||||
* because this function always take this parameter as a format string.
|
||||
* If you want to output plain string, you need escape format
|
||||
* @param ...[in] The arguments to be formatted.
|
||||
* @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 WriteLine(const char* u8_fmt, ...);
|
||||
void FormatLine(const char* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console write function.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void Write(const char* 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);
|
||||
|
||||
/**
|
||||
* @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, ...);
|
||||
/**
|
||||
* @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, ...);
|
||||
/**
|
||||
* @brief Universal console error write function.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void ErrWrite(const char* 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);
|
||||
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ namespace YYCC::DialogHelper {
|
|||
* @return True if success, otherwise false.
|
||||
* @remarks This is an assist function of CommonFileDialog.
|
||||
*/
|
||||
bool ExtractDisplayName(IShellItem* item, std::string& ret) {
|
||||
static bool ExtractDisplayName(IShellItem* item, std::string& ret) {
|
||||
// fetch display name from IShellItem*
|
||||
LPWSTR _name;
|
||||
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name);
|
||||
|
@ -205,7 +205,7 @@ namespace YYCC::DialogHelper {
|
|||
* @remarks This function is the real underlying function of all dialog functions.
|
||||
*/
|
||||
template<CommonFileDialogType EDialogType>
|
||||
bool CommonFileDialog(const FileDialog& params, std::vector<std::string>& ret) {
|
||||
static bool CommonFileDialog(const FileDialog& params, std::vector<std::string>& ret) {
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
|
||||
// prepare result variable
|
||||
HRESULT hr;
|
||||
|
|
|
@ -8,12 +8,12 @@ namespace YYCC::ExceptionHelper {
|
|||
* @brief Register unhandled exception handler
|
||||
* @details
|
||||
* This function will set an internal function as unhandled exception handler on Windows.
|
||||
* \n
|
||||
*
|
||||
* When unhandled exception raised,
|
||||
* That internal function will output error stacktrace in standard output
|
||||
* and log file (located in temp folder), and also generate a dump file
|
||||
* in temp folder (for convenient debugging of developer when reporting bugs) if it can.
|
||||
* \n
|
||||
*
|
||||
* This function usually is called at the start of program.
|
||||
* @remarks This function is Windows only.
|
||||
*/
|
||||
|
@ -22,10 +22,10 @@ namespace YYCC::ExceptionHelper {
|
|||
* @brief Unregister unhandled exception handler
|
||||
* @details
|
||||
* The reverse operation of Register().
|
||||
* \n
|
||||
*
|
||||
* This function and Register() should always be used as a pair.
|
||||
* You must call this function if you have called Register() before.
|
||||
* \n
|
||||
*
|
||||
* This function usually is called at the end of program.
|
||||
* @remarks This function is Windows only.
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace YYCC::IOHelper {
|
|||
* @details
|
||||
* When printing a pointer with HEX style, we always hope it can be left-padded with some zero for easy reading.
|
||||
* In different architecture, the size of this padding is differnet too so we create this macro.
|
||||
* \n
|
||||
*
|
||||
* In 32-bit environment, it will be "08" meaning left pad zero until 8 number position.
|
||||
* In 64-bit environment, it will be "016" meaning left pad zero until 16 number position.
|
||||
*/
|
||||
|
@ -25,8 +25,8 @@ namespace YYCC::IOHelper {
|
|||
|
||||
/**
|
||||
* @brief The UTF8 version of std::fopen.
|
||||
* @param u8_filepath[in] The UTF8 encoded path to the file to be opened.
|
||||
* @param u8_mode[in] UTF8 encoded mode string of the file to be opened.
|
||||
* @param[in] u8_filepath The UTF8 encoded path to the file to be opened.
|
||||
* @param[in] u8_mode UTF8 encoded mode string of the file to be opened.
|
||||
* @remarks
|
||||
* This function is suit for Windows because std::fopen do not support UTF8 on Windows.
|
||||
* On other platforms, this function will delegate request directly to std::fopen.
|
||||
|
@ -36,7 +36,7 @@ namespace YYCC::IOHelper {
|
|||
|
||||
/**
|
||||
* @brief Build std::filesystem::path from UTF8 string.
|
||||
* @param u8_path[in] UTF8 path string for building this std::filesystem::path.
|
||||
* @param[in] u8_path UTF8 path string for building this std::filesystem::path.
|
||||
* @return std::filesystem::path instance.
|
||||
* @exception std::invalid_argument Fail to parse given UTF8 string (maybe invalid?).
|
||||
* @remarks
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
#include <Windows.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
|
||||
/**
|
||||
* @brief The helper providing assistance to Win32 functions.
|
||||
* @details
|
||||
* This helper is Windows specific.
|
||||
* If current environment is not Windows,
|
||||
* the whole namespace will disappear.
|
||||
*/
|
||||
namespace YYCC::WinFctHelper {
|
||||
|
||||
/**
|
||||
|
@ -15,7 +22,7 @@ namespace YYCC::WinFctHelper {
|
|||
* @details
|
||||
* If your target is EXE, the current module simply is your program self.
|
||||
* However, if your target is DLL, the current module is your DLL, not the EXE loading your DLL.
|
||||
* \n
|
||||
*
|
||||
* This function is frequently used by DLL.
|
||||
* Because some design need the HANDLE of current module, not the host EXE loading your DLL.
|
||||
* For example, you may want to get the name of your built DLL at runtime, then you should pass current module HANDLE, not the HANDLE of EXE.
|
||||
|
@ -32,9 +39,9 @@ namespace YYCC::WinFctHelper {
|
|||
|
||||
/**
|
||||
* @brief Get the file name of given module HANDLE
|
||||
* @param hModule[in]
|
||||
* @param[in] hModule
|
||||
* The HANDLE to the module where we want get file name.
|
||||
* It is same as the HANDLE parameter of GetModuleFileName.
|
||||
* It is same as the HANDLE parameter of \c GetModuleFileName.
|
||||
* @return UTF8 encoded file name of given module. Empty string if failed.
|
||||
*/
|
||||
std::string GetModuleName(HINSTANCE hModule);
|
||||
|
|
|
@ -7,9 +7,9 @@ namespace YYCCTestbench {
|
|||
|
||||
static void Assert(bool condition, const char* description) {
|
||||
if (condition) {
|
||||
Console::WriteLine(YYCC_COLOR_LIGHT_GREEN("OK: %s"), description);
|
||||
Console::FormatLine(YYCC_COLOR_LIGHT_GREEN("OK: %s"), description);
|
||||
} else {
|
||||
Console::WriteLine(YYCC_COLOR_LIGHT_RED("Failed: %s\n"), description);
|
||||
Console::FormatLine(YYCC_COLOR_LIGHT_RED("Failed: %s\n"), description);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
@ -51,13 +51,13 @@ namespace YYCCTestbench {
|
|||
"\xF0\x9F\x8D\xA3 \xE2\x9C\x96 \xF0\x9F\x8D\xBA", // EMOJI
|
||||
};
|
||||
for (const auto* ptr : c_TestStrings) {
|
||||
Console::WriteLine("\t%s", ptr);
|
||||
Console::FormatLine("\t%s", ptr);
|
||||
}
|
||||
|
||||
// UTF8 Input Test
|
||||
Console::WriteLine("UTF8 Input Test:");
|
||||
for (const auto* ptr : c_TestStrings) {
|
||||
Console::WriteLine("\tPlease type: %s", ptr);
|
||||
Console::FormatLine("\tPlease type: %s", ptr);
|
||||
Console::Write("\t> ");
|
||||
|
||||
std::string gotten(Console::ReadLine());
|
||||
|
@ -198,27 +198,27 @@ namespace YYCCTestbench {
|
|||
filters.Add("All Files (*.*)", {"*.*"});
|
||||
params.SetDefaultFileTypeIndex(0u);
|
||||
if (YYCC::DialogHelper::OpenFileDialog(params, ret)) {
|
||||
Console::WriteLine("Open File: %s", ret.c_str());
|
||||
Console::FormatLine("Open File: %s", ret.c_str());
|
||||
}
|
||||
if (YYCC::DialogHelper::OpenMultipleFileDialog(params, rets)) {
|
||||
Console::WriteLine("Open Multiple Files:");
|
||||
for (const auto& item : rets) {
|
||||
Console::WriteLine("\t%s", item.c_str());
|
||||
Console::FormatLine("\t%s", item.c_str());
|
||||
}
|
||||
}
|
||||
if (YYCC::DialogHelper::SaveFileDialog(params, ret)) {
|
||||
Console::WriteLine("Save File: %s", ret.c_str());
|
||||
Console::FormatLine("Save File: %s", ret.c_str());
|
||||
}
|
||||
params.Clear();
|
||||
if (YYCC::DialogHelper::OpenFolderDialog(params, ret)) {
|
||||
Console::WriteLine("Open Folder: %s", ret.c_str());
|
||||
Console::FormatLine("Open Folder: %s", ret.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void WinFctTestbench() {
|
||||
Console::WriteLine("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR, YYCC::WinFctHelper::GetCurrentModule());
|
||||
Console::WriteLine("Temp Directory: %s", YYCC::WinFctHelper::GetTempDirectory().c_str());
|
||||
Console::WriteLine("Current Module Name: %s", YYCC::WinFctHelper::GetModuleName(YYCC::WinFctHelper::GetCurrentModule()).c_str());
|
||||
Console::FormatLine("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR, YYCC::WinFctHelper::GetCurrentModule());
|
||||
Console::FormatLine("Temp Directory: %s", YYCC::WinFctHelper::GetTempDirectory().c_str());
|
||||
Console::FormatLine("Current Module Name: %s", YYCC::WinFctHelper::GetModuleName(YYCC::WinFctHelper::GetCurrentModule()).c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user