fix: change the behavior of printf in string op.
- add compiler hint for checking the arguments of printf. - change the return value of printf. from std::expected to normal value. use C++ exception to indicate error. * the error of printf usually caused by programmer. so it can be found when testing program. * so i use std::logic_error to indicate this and programmer should fix this before releasing program. - change the use of encoding convertion. for those cases that convertion must be safe, we unwrap it directly.
This commit is contained in:
@ -31,6 +31,7 @@ PRIVATE
|
||||
yycc/carton/tabulate.cpp
|
||||
yycc/carton/ironpad.cpp
|
||||
yycc/carton/csconsole.cpp
|
||||
#yycc/carton/clap/option.cpp
|
||||
)
|
||||
target_sources(YYCCommonplace
|
||||
PUBLIC
|
||||
@ -46,6 +47,7 @@ FILES
|
||||
yycc/macro/compiler_detector.hpp
|
||||
yycc/macro/ptr_size_detector.hpp
|
||||
yycc/macro/class_copy_move.hpp
|
||||
yycc/macro/printf_checker.hpp
|
||||
yycc/flag_enum.hpp
|
||||
yycc/string/reinterpret.hpp
|
||||
yycc/string/op.hpp
|
||||
@ -81,6 +83,9 @@ FILES
|
||||
yycc/carton/tabulate.hpp
|
||||
yycc/carton/ironpad.hpp
|
||||
yycc/carton/csconsole.hpp
|
||||
yycc/carton/clap.hpp
|
||||
yycc/carton/clap/types.hpp
|
||||
yycc/carton/clap/option.hpp
|
||||
)
|
||||
# Setup header infomations
|
||||
target_include_directories(YYCCommonplace
|
||||
|
5
src/yycc/carton/clap.hpp
Normal file
5
src/yycc/carton/clap.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace yycc::carton::clap {
|
||||
|
||||
}
|
5
src/yycc/carton/clap/option.cpp
Normal file
5
src/yycc/carton/clap/option.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
#include "option.hpp"
|
||||
|
||||
namespace yycc::carton::clap::option {
|
||||
|
||||
}
|
85
src/yycc/carton/clap/option.hpp
Normal file
85
src/yycc/carton/clap/option.hpp
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#include "types.hpp"
|
||||
#include "../../macro/class_copy_move.hpp"
|
||||
#include "../../string/op.hpp"
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
#define NS_YYCC_CLAP_TYPES ::yycc::carton::clap::types
|
||||
|
||||
namespace yycc::carton::clap::option {
|
||||
|
||||
class Option {
|
||||
public:
|
||||
Option(std::optional<std::u8string_view> short_name,
|
||||
std::optional<std::u8string_view> long_name,
|
||||
std::optional<std::u8string_view> value_hint,
|
||||
const std::u8string& description) :
|
||||
short_name(short_name), long_name(long_name), value_hint(value_hint), description(description) {
|
||||
if (!short_name.has_value() && !long_name.has_value()) {
|
||||
throw std::logic_error("must have at least one name, short or long name");
|
||||
}
|
||||
|
||||
if (short_name.has_value()) {
|
||||
const auto& short_name_value = short_name.value();
|
||||
if (!legal_short_name(short_name_value)) {
|
||||
throw std::logic_error(std::format("invalid short name {}", short_name_value));
|
||||
}
|
||||
}
|
||||
if (long_name.has_value()) {
|
||||
const auto& long_name_value = long_name.value();
|
||||
if (!legal_long_name(long_name_value)) {
|
||||
throw std::logic_error(std::format("invalid long name {}", long_name_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
~Option() {}
|
||||
YYCC_DEFAULT_COPY_MOVE(Option)
|
||||
|
||||
public:
|
||||
std::optional<std::u8string_view> get_short_name() const { return this->short_name; }
|
||||
std::optional<std::u8string_view> get_long_name() const { return this->long_name; }
|
||||
std::optional<std::u8string_view> get_value_hint() const { return this->value_hint; }
|
||||
std::u8string_view get_description() const { return this->description; }
|
||||
|
||||
std::u8string to_showcase_name() {
|
||||
namespace op = ::yycc::string::op;
|
||||
|
||||
if (short_name.has_value()) {
|
||||
if (long_name.has_value()) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
} else {
|
||||
if (long_name.has_value()) {
|
||||
op::printf
|
||||
} else {
|
||||
throw std::runtime_error("both long name and short name are empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static bool legal_short_name(const std::u8string_view& name) {
|
||||
if (name.empty()) return false;
|
||||
if (name.starts_with(NS_YYCC_CLAP_TYPES::DASH)) return false;
|
||||
return true;
|
||||
}
|
||||
static bool legal_long_name(const std::u8string_view& name) {
|
||||
if (name.empty()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<std::u8string> short_name;
|
||||
std::optional<std::u8string> long_name;
|
||||
std::optional<std::u8string> value_hint;
|
||||
std::u8string description;
|
||||
};
|
||||
|
||||
} // namespace yycc::carton::clap::option
|
||||
|
||||
#undef NS_YYCC_CLAP_TYPES
|
19
src/yycc/carton/clap/types.hpp
Normal file
19
src/yycc/carton/clap/types.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <expected>
|
||||
#include <string_view>
|
||||
|
||||
namespace yycc::carton::clap::types {
|
||||
|
||||
enum class ClapError {
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using ClapResult = std::expected<T, ClapError>;
|
||||
|
||||
inline constexpr std::u8string_view DASH = u8"-";
|
||||
inline constexpr std::u8string_view DOUBLE_DASH = u8"--";
|
||||
|
||||
using Token = size_t;
|
||||
|
||||
} // namespace yycc::carton::clap::types
|
@ -108,9 +108,7 @@ namespace yycc::carton::csconsole {
|
||||
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
||||
// console handle, use WriteConsoleW.
|
||||
// convert utf8 string to wide char first
|
||||
auto rv = ENC::to_wchar(strl);
|
||||
if (!rv.has_value()) return;
|
||||
std::wstring wstrl(std::move(rv.value()));
|
||||
std::wstring wstrl = ENC::to_wchar(strl).value();
|
||||
size_t wstrl_size = wstrl.size();
|
||||
// write string with size check
|
||||
if (wstrl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
@ -171,11 +169,8 @@ namespace yycc::carton::csconsole {
|
||||
// treat as format string
|
||||
va_list argcpy;
|
||||
va_copy(argcpy, argptr);
|
||||
auto rv = OP::vprintf(u8_fmt, argcpy);
|
||||
strl = OP::vprintf(u8_fmt, argcpy);
|
||||
va_end(argcpy);
|
||||
// check format result
|
||||
if (!rv.has_value()) return;
|
||||
else strl = std::move(rv.value());
|
||||
} else {
|
||||
// treat as plain string
|
||||
strl = u8_fmt;
|
||||
|
@ -63,11 +63,9 @@ namespace yycc::carton::ironpad {
|
||||
// check singleton
|
||||
// build mutex string first
|
||||
auto mutex_name = OP::printf(u8"Global\\%" PRIu32 ".{61634294-d23c-43f9-8490-b5e09837eede}", GetCurrentProcessId());
|
||||
if (!mutex_name.has_value()) return false;
|
||||
auto w_mutex_name = ENC::to_wchar(mutex_name.value());
|
||||
if (!w_mutex_name.has_value()) return false;
|
||||
auto wmutex_name = ENC::to_wchar(mutex_name).value();
|
||||
// create mutex
|
||||
m_SingletonMutex = CreateMutexW(NULL, FALSE, w_mutex_name.value().c_str());
|
||||
m_SingletonMutex = CreateMutexW(NULL, FALSE, wmutex_name.c_str());
|
||||
DWORD errcode = GetLastError();
|
||||
// check whether be created
|
||||
if (m_SingletonMutex == NULL) return false;
|
||||
@ -303,18 +301,17 @@ namespace yycc::carton::ironpad {
|
||||
DWORD process_id = GetCurrentProcessId();
|
||||
// conbine them as a file name prefix
|
||||
auto u8_filename_prefix = OP::printf(u8"%s.%" PRIu32, u8_process_name.c_str(), process_id);
|
||||
if (!u8_filename_prefix.has_value()) return std::nullopt;
|
||||
// then get file name for log and minidump
|
||||
std::u8string u8_filename;
|
||||
switch (kind) {
|
||||
case FileKind::LogFile:
|
||||
u8_filename = u8_filename_prefix.value() + u8".log";
|
||||
u8_filename = u8_filename_prefix + u8".log";
|
||||
break;
|
||||
case FileKind::CoredumpFile:
|
||||
u8_filename = u8_filename_prefix.value() + u8".dmp";
|
||||
u8_filename = u8_filename_prefix + u8".dmp";
|
||||
break;
|
||||
default:
|
||||
u8_filename = u8_filename_prefix.value();
|
||||
u8_filename = u8_filename_prefix;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -451,11 +448,9 @@ namespace yycc::carton::ironpad {
|
||||
* @details This function will write coredump of given exception into given file path.
|
||||
*/
|
||||
void do_coredump(const std::u8string_view& u8_filename, LPEXCEPTION_POINTERS info) {
|
||||
// convert file encoding
|
||||
// if convertion failed, return
|
||||
auto filename_rv = ENC::to_wchar(u8_filename);
|
||||
if (!filename_rv.has_value()) return;
|
||||
std::wstring filename = filename_rv.value();
|
||||
// convert file encoding.
|
||||
// it must be okey.
|
||||
auto filename = ENC::to_wchar(u8_filename).value();
|
||||
|
||||
// open file and write
|
||||
HANDLE hFile = CreateFileW(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
@ -554,15 +549,11 @@ namespace yycc::carton::ironpad {
|
||||
* @param[in] ... The argument to be formatted.
|
||||
*/
|
||||
void log_format_line(std::FILE* fs, const char8_t* fmt, ...) {
|
||||
// do format first
|
||||
// write to file and console
|
||||
va_list arg;
|
||||
va_start(arg, fmt);
|
||||
auto fmt_rv = OP::vprintf(fmt, arg);
|
||||
log_write_line(fs, OP::vprintf(fmt, arg).c_str());
|
||||
va_end(arg);
|
||||
// write to file and console
|
||||
if (fmt_rv.has_value()) {
|
||||
log_write_line(fs, fmt_rv.value().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -468,11 +468,8 @@ namespace yycc::carton::wcwidth {
|
||||
}
|
||||
|
||||
Result<size_t> wcswidth(const std::u8string_view& rhs) {
|
||||
// Cast encoding
|
||||
auto u32str = ENC::to_utf32(rhs);
|
||||
if (!u32str.has_value()) return std::unexpected(Error::BadEncoding);
|
||||
// Call underlying function
|
||||
return wcswidth(u32str.value());
|
||||
// Cast encoding and call underlying function
|
||||
return wcswidth(ENC::to_utf32(rhs).value());
|
||||
}
|
||||
|
||||
} // namespace yycc::carton::wcwidth
|
||||
|
@ -31,7 +31,6 @@ namespace yycc::carton::wcwidth {
|
||||
|
||||
/// @brief Error occurs in this module
|
||||
enum class Error {
|
||||
BadEncoding, ///< Given
|
||||
BadAnsiEscSeq, ///< Bad char when processing ANSI Escape Sequence
|
||||
BadCsiSeq, ///< Bad char when processing CSI Sequence.
|
||||
};
|
||||
|
28
src/yycc/macro/printf_checker.hpp
Normal file
28
src/yycc/macro/printf_checker.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "compiler_detector.hpp"
|
||||
#include "stl_detector.hpp"
|
||||
|
||||
// YYC MARK:
|
||||
// This code is copied from Qt project.
|
||||
|
||||
#if defined(YYCC_CC_GCC)
|
||||
// GCC has its special attribute
|
||||
#define YYCC_PRINTF_CHECK_ATTR(A, B) __attribute__((format(gnu_printf, (A), (B))))
|
||||
#elif defined(YYCC_CC_CLANG)
|
||||
// Clang use its own attribute
|
||||
#define YYCC_PRINTF_CHECK_ATTR(A, B) __attribute__((format(printf, (A), (B))))
|
||||
#else
|
||||
// Other CC do not support this (like MSVC), skip it
|
||||
#define YYCC_PRINTF_CHECK_ATTR(A, B)
|
||||
#endif
|
||||
|
||||
#if defined(YYCC_STL_MSSTL)
|
||||
// On Microsoft STL, we can use some mechanisms to check it.
|
||||
#include "../windows/import_guard_head.hpp"
|
||||
#include <sal.h>
|
||||
#include "../windows/import_guard_tail.hpp"
|
||||
#define YYCC_PRINTF_CHECK_FMTSTR _Printf_format_string_
|
||||
#else
|
||||
// Other STL do not have this.
|
||||
#define YYCC_PRINTF_CHECK_FMTSTR
|
||||
#endif
|
@ -18,19 +18,9 @@ namespace yycc::patch::fopen {
|
||||
|
||||
std::FILE* fopen(const char8_t* u8_filepath, const char8_t* u8_mode) {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// convert mode and file path to wchar
|
||||
auto wmode = ENC::to_wchar(u8_mode);
|
||||
auto wpath = ENC::to_wchar(u8_filepath);
|
||||
|
||||
// check convertion success
|
||||
if (wmode.has_value() && wpath.has_value()) {
|
||||
// Call MSVCRT specified fopen which support wchar as argument.
|
||||
// Reference: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170
|
||||
return _wfopen(wpath.value().c_str(), wmode.value().c_str());
|
||||
} else {
|
||||
// fail to convert encoding
|
||||
return nullptr;
|
||||
}
|
||||
// Convert encoding first, and call MSVCRT specified fopen which support wchar as argument.
|
||||
// Reference: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170
|
||||
return _wfopen(ENC::to_wchar(u8_filepath).value().c_str(), ENC::to_wchar(u8_mode).value().c_str());
|
||||
#else
|
||||
return std::fopen(REINTERPRET::as_ordinary(u8_filepath), REINTERPRET::as_ordinary(u8_mode));
|
||||
#endif
|
||||
|
@ -25,10 +25,7 @@ namespace yycc::rust::env {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
|
||||
// Convert to wchar
|
||||
auto wname = ENC::to_wchar(name);
|
||||
if (!wname.has_value()) {
|
||||
return std::unexpected(EnvError::BadEncoding);
|
||||
}
|
||||
auto wname = ENC::to_wchar(name).value();
|
||||
|
||||
// Prepare a variable with proper init size for holding value.
|
||||
std::wstring wvalue;
|
||||
@ -42,7 +39,7 @@ namespace yycc::rust::env {
|
||||
// So we forcely use checked add and sub for this bad behavior.
|
||||
auto fct_size = SAFEOP::checked_add<size_t>(wvalue.size(), 1);
|
||||
if (!fct_size.has_value()) return std::unexpected(EnvError::BadArithmetic);
|
||||
auto rv = ::GetEnvironmentVariableW(wname.value().c_str(), wvalue.data(), fct_size.value());
|
||||
auto rv = ::GetEnvironmentVariableW(wname.c_str(), wvalue.data(), fct_size.value());
|
||||
|
||||
// Check the return value
|
||||
if (rv == 0) {
|
||||
@ -95,20 +92,10 @@ namespace yycc::rust::env {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||
|
||||
// Convert to wchar
|
||||
auto wname = ENC::to_wchar(name);
|
||||
auto wvalue = ENC::to_wchar(value);
|
||||
if (!(wname.has_value() && wvalue.has_value())) {
|
||||
return std::unexpected(EnvError::BadEncoding);
|
||||
}
|
||||
|
||||
// Delete variable and check result.
|
||||
auto rv = ::SetEnvironmentVariableW(wname.value().c_str(), wvalue.value().c_str());
|
||||
if (!rv) {
|
||||
return std::unexpected(EnvError::BadCall);
|
||||
}
|
||||
|
||||
return {};
|
||||
// Convert to wchar, set variable, and check result.
|
||||
auto rv = ::SetEnvironmentVariableW(ENC::to_wchar(name).value().c_str(), ENC::to_wchar(value).value().c_str());
|
||||
if (!rv) return std::unexpected(EnvError::BadCall);
|
||||
else return {};
|
||||
#else
|
||||
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html
|
||||
|
||||
@ -129,19 +116,10 @@ namespace yycc::rust::env {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||
|
||||
// Convert to wchar
|
||||
auto wname = ENC::to_wchar(name);
|
||||
if (!wname.has_value()) {
|
||||
return std::unexpected(EnvError::BadEncoding);
|
||||
}
|
||||
|
||||
// Delete variable and check result.
|
||||
auto rv = ::SetEnvironmentVariableW(wname.value().c_str(), NULL);
|
||||
if (!rv) {
|
||||
return std::unexpected(EnvError::BadCall);
|
||||
}
|
||||
|
||||
return {};
|
||||
// Convert to wchar, delete variable, and check result.
|
||||
auto rv = ::SetEnvironmentVariableW(ENC::to_wchar(name).value().c_str(), NULL);
|
||||
if (!rv) return std::unexpected(EnvError::BadCall);
|
||||
else return {};
|
||||
#else
|
||||
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html
|
||||
|
||||
|
@ -1,19 +1,21 @@
|
||||
#include "op.hpp"
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace yycc::string::op {
|
||||
|
||||
#pragma region Printf VPrintf
|
||||
|
||||
/// @brief The concept for all viable char type in printf function family
|
||||
template<typename TChar>
|
||||
requires(sizeof(TChar) == sizeof(char))
|
||||
static FormatResult<std::basic_string<TChar>> generic_printf(const TChar* format, va_list argptr) {
|
||||
concept PrintfSupportedChar = std::is_same_v<TChar, char> || std::is_same_v<TChar, char8_t>;
|
||||
|
||||
template<PrintfSupportedChar TChar>
|
||||
static std::basic_string<TChar> generic_printf(const TChar* format, va_list argptr) {
|
||||
// Prepare result
|
||||
std::basic_string<TChar> rv;
|
||||
|
||||
// Check format
|
||||
if (format == nullptr) return std::unexpected(FormatError::NullFormat);
|
||||
|
||||
// Prepare variable arguments
|
||||
va_list args1;
|
||||
va_copy(args1, argptr);
|
||||
@ -21,12 +23,13 @@ namespace yycc::string::op {
|
||||
va_copy(args2, argptr);
|
||||
|
||||
// The return value is desired char count without NULL terminal.
|
||||
// Minus number means error.
|
||||
// Negative number means error.
|
||||
int count = std::vsnprintf(nullptr, 0, reinterpret_cast<const char*>(format), args1);
|
||||
// Check expected size.
|
||||
if (count < 0) {
|
||||
// Invalid length returned by vsnprintf.
|
||||
return std::unexpected(FormatError::NoDesiredSize);
|
||||
// This may be caused by invalid format string
|
||||
throw std::logic_error("fail to determine the size of formatted string");
|
||||
}
|
||||
va_end(args1);
|
||||
|
||||
@ -39,14 +42,15 @@ namespace yycc::string::op {
|
||||
// Check written size.
|
||||
if (write_result < 0 || write_result > count) {
|
||||
// Invalid write result in vsnprintf.
|
||||
return std::unexpected(FormatError::BadWrittenSize);
|
||||
// Idk why this can happen.
|
||||
throw std::logic_error("the size of written formatted string is not expected");
|
||||
}
|
||||
|
||||
// Return value
|
||||
return rv;
|
||||
}
|
||||
|
||||
FormatResult<std::u8string> printf(const char8_t* format, ...) {
|
||||
std::u8string printf(const char8_t* format, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
auto rv = vprintf(format, argptr);
|
||||
@ -54,11 +58,11 @@ namespace yycc::string::op {
|
||||
return rv;
|
||||
}
|
||||
|
||||
FormatResult<std::u8string> vprintf(const char8_t* format, va_list argptr) {
|
||||
std::u8string vprintf(const char8_t* format, va_list argptr) {
|
||||
return generic_printf(format, argptr);
|
||||
}
|
||||
|
||||
FormatResult<std::string> printf(const char* format, ...) {
|
||||
std::string printf(const char* format, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
auto rv = vprintf(format, argptr);
|
||||
@ -66,7 +70,7 @@ namespace yycc::string::op {
|
||||
return rv;
|
||||
}
|
||||
|
||||
FormatResult<std::string> vprintf(const char* format, va_list argptr) {
|
||||
std::string vprintf(const char* format, va_list argptr) {
|
||||
return generic_printf(format, argptr);
|
||||
}
|
||||
|
||||
|
@ -1,50 +1,41 @@
|
||||
#pragma once
|
||||
#include "../macro/printf_checker.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstdarg>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <expected>
|
||||
|
||||
namespace yycc::string::op {
|
||||
|
||||
enum class FormatError {
|
||||
NullFormat, ///< Given format string is nullptr.
|
||||
NoDesiredSize, ///< Fail to fetch the expected size of result.
|
||||
BadWrittenSize, ///< The written size is different with expected size.
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using FormatResult = std::expected<T, FormatError>;
|
||||
|
||||
/**
|
||||
* @brief Perform an UTF8 string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] ... Argument list of format string.
|
||||
* @return The formatted result, or the fail reason.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
FormatResult<std::u8string> printf(const char8_t* format, ...);
|
||||
std::u8string printf(YYCC_PRINTF_CHECK_FMTSTR const char8_t* format, ...) YYCC_PRINTF_CHECK_ATTR(1, 2);
|
||||
/**
|
||||
* @brief Perform an UTF8 string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] argptr Argument list of format string.
|
||||
* @return The formatted result, or the fail reason.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
FormatResult<std::u8string> vprintf(const char8_t* format, va_list argptr);
|
||||
std::u8string vprintf(const char8_t* format, va_list argptr);
|
||||
/**
|
||||
* @brief Perform an ordinary string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] ... Argument list of format string.
|
||||
* @return The formatted result, or the fail reason.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
FormatResult<std::string> printf(const char* format, ...);
|
||||
std::string printf(YYCC_PRINTF_CHECK_FMTSTR const char* format, ...) YYCC_PRINTF_CHECK_ATTR(1, 2);
|
||||
/**
|
||||
* @brief Perform an ordinary string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] argptr Argument list of format string.
|
||||
* @return The formatted result, or the fail reason.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
FormatResult<std::string> vprintf(const char* format, va_list argptr);
|
||||
std::string vprintf(const char* format, va_list argptr);
|
||||
|
||||
/**
|
||||
* @brief Modify given string with all occurrences of substring \e old replaced by \e new.
|
||||
|
@ -130,21 +130,15 @@ namespace yycc::windows::dialog {
|
||||
// build new Windows oriented string vector
|
||||
for (const auto& item : m_Filters) {
|
||||
// convert name to wchar
|
||||
auto win_name = ENC::to_wchar(item.first);
|
||||
if (!win_name.has_value()) {
|
||||
return std::unexpected(DialogError::BadEncoding);
|
||||
}
|
||||
auto win_name = ENC::to_wchar(item.first).value();
|
||||
|
||||
// join pattern string and convert to wchar
|
||||
const auto& modes = item.second;
|
||||
auto joined_modes = OP::join(modes.begin(), modes.end(), u8";");
|
||||
auto win_modes = ENC::to_wchar(joined_modes);
|
||||
if (!win_modes.has_value()) {
|
||||
return std::unexpected(DialogError::BadEncoding);
|
||||
}
|
||||
auto win_modes = ENC::to_wchar(joined_modes).value();
|
||||
|
||||
// append this pair
|
||||
rv.m_WinFilters.emplace_back(std::make_pair(win_name.value(), win_modes.value()));
|
||||
rv.m_WinFilters.emplace_back(std::make_pair(win_name, win_modes));
|
||||
}
|
||||
|
||||
// update data struct
|
||||
@ -305,28 +299,21 @@ namespace yycc::windows::dialog {
|
||||
|
||||
// build title and init file name
|
||||
if (m_Title.has_value()) {
|
||||
auto win_title = ENC::to_wchar(m_Title.value());
|
||||
if (!win_title.has_value()) return std::unexpected(DialogError::BadEncoding);
|
||||
else rv.m_WinTitle = win_title.value();
|
||||
rv.m_WinTitle = ENC::to_wchar(m_Title.value()).value();
|
||||
} else rv.m_WinTitle = std::nullopt;
|
||||
if (m_InitFileName.has_value()) {
|
||||
auto win_init_file_name = ENC::to_wchar(m_InitFileName.value());
|
||||
if (!win_init_file_name.has_value()) return std::unexpected(DialogError::BadEncoding);
|
||||
else rv.m_WinInitFileName = win_init_file_name.value();
|
||||
rv.m_WinInitFileName = ENC::to_wchar(m_InitFileName.value()).value();
|
||||
} else rv.m_WinInitFileName = std::nullopt;
|
||||
|
||||
// fetch init directory
|
||||
if (m_InitDirectory.has_value()) {
|
||||
// convert to wchar path
|
||||
auto w_init_dir = ENC::to_wchar(m_InitDirectory.value());
|
||||
if (!w_init_dir.has_value()) {
|
||||
return std::unexpected(DialogError::BadEncoding);
|
||||
}
|
||||
auto w_init_dir = ENC::to_wchar(m_InitDirectory.value()).value();
|
||||
|
||||
// fetch IShellItem*
|
||||
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
|
||||
IShellItem* init_directory = NULL;
|
||||
HRESULT hr = SHCreateItemFromParsingName(w_init_dir.value().c_str(), NULL, IID_PPV_ARGS(&init_directory));
|
||||
HRESULT hr = SHCreateItemFromParsingName(w_init_dir.c_str(), NULL, IID_PPV_ARGS(&init_directory));
|
||||
if (FAILED(hr)) return std::unexpected(DialogError::NoSuchDir);
|
||||
|
||||
// assign IShellItem*
|
||||
@ -378,7 +365,7 @@ namespace yycc::windows::dialog {
|
||||
WINCOM::SmartLPWSTR display_name(display_name_ptr);
|
||||
|
||||
// convert result and return
|
||||
return ENC::to_utf8(display_name.get()).transform_error([](auto err) { return DialogError::BadEncoding; });
|
||||
return ENC::to_utf8(display_name.get()).value();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,6 @@ namespace yycc::windows::dialog {
|
||||
|
||||
/// @brief The error occurs in this module.
|
||||
enum class DialogError {
|
||||
BadEncoding, ///< Error occurs when perform encoding convertion.
|
||||
TooManyFilters, ///< The size of file filters list is too large for Windows.
|
||||
IndexOverflow, ///< Default filter index is too large for Windows.
|
||||
EmptyFilters, ///< File filters is empty when picking file.
|
||||
@ -310,7 +309,7 @@ namespace yycc::windows::dialog {
|
||||
*/
|
||||
DialogResult<DialogOutcome<std::u8string>> open_folder(const FileDialog& params);
|
||||
|
||||
}
|
||||
} // namespace yycc::windows::dialog
|
||||
|
||||
#undef NS_YYCC_WINDOWS_COM
|
||||
|
||||
|
@ -21,7 +21,7 @@ namespace yycc::windows::winfct {
|
||||
(LPCWSTR) get_current_module,
|
||||
&hModule);
|
||||
if (rv) return hModule;
|
||||
else return std::unexpected(WinFctError::Backend);
|
||||
else return std::unexpected(WinFctError::Win32);
|
||||
}
|
||||
|
||||
WinFctResult<std::u8string> get_temp_directory() {
|
||||
@ -33,7 +33,7 @@ namespace yycc::windows::winfct {
|
||||
while (true) {
|
||||
if ((expected_size = ::GetTempPathW(static_cast<DWORD>(wpath.size()), wpath.data())) == 0) {
|
||||
// failed, return
|
||||
return std::unexpected(WinFctError::Backend);
|
||||
return std::unexpected(WinFctError::Win32);
|
||||
}
|
||||
|
||||
if (expected_size > static_cast<DWORD>(wpath.size())) {
|
||||
@ -47,7 +47,7 @@ namespace yycc::windows::winfct {
|
||||
}
|
||||
|
||||
// convert to utf8 and return
|
||||
return ENC::to_utf8(wpath).transform_error([](auto err) { return WinFctError::Encoding; });
|
||||
return ENC::to_utf8(wpath).value();
|
||||
}
|
||||
|
||||
WinFctResult<std::u8string> get_module_file_name(HINSTANCE hModule) {
|
||||
@ -58,7 +58,7 @@ namespace yycc::windows::winfct {
|
||||
while (true) {
|
||||
if ((copied_size = ::GetModuleFileNameW(hModule, wpath.data(), static_cast<DWORD>(wpath.size()))) == 0) {
|
||||
// failed, return
|
||||
return std::unexpected(WinFctError::Backend);
|
||||
return std::unexpected(WinFctError::Win32);
|
||||
}
|
||||
|
||||
// check insufficient buffer
|
||||
@ -73,7 +73,7 @@ namespace yycc::windows::winfct {
|
||||
}
|
||||
|
||||
// convert to utf8 and return
|
||||
return ENC::to_utf8(wpath).transform_error([](auto err) { return WinFctError::Encoding; });
|
||||
return ENC::to_utf8(wpath).value();
|
||||
}
|
||||
|
||||
bool is_valid_code_page(UINT code_page) {
|
||||
@ -82,41 +82,32 @@ namespace yycc::windows::winfct {
|
||||
}
|
||||
|
||||
WinFctResult<void> copy_file(const std::u8string_view& lpExistingFileName, const std::u8string_view& lpNewFileName, BOOL bFailIfExists) {
|
||||
auto wExistingFileName = ENC::to_wchar(lpExistingFileName);
|
||||
auto wNewFileName = ENC::to_wchar(lpNewFileName);
|
||||
if (!(wExistingFileName.has_value() && wNewFileName.has_value())) {
|
||||
return std::unexpected(WinFctError::Encoding);
|
||||
}
|
||||
auto wExistingFileName = ENC::to_wchar(lpExistingFileName).value();
|
||||
auto wNewFileName = ENC::to_wchar(lpNewFileName).value();
|
||||
|
||||
if (!::CopyFileW(wExistingFileName.value().c_str(), wNewFileName.value().c_str(), bFailIfExists)) {
|
||||
return std::unexpected(WinFctError::Backend);
|
||||
if (!::CopyFileW(wExistingFileName.c_str(), wNewFileName.c_str(), bFailIfExists)) {
|
||||
return std::unexpected(WinFctError::Win32);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
WinFctResult<void> move_file(const std::u8string_view& lpExistingFileName, const std::u8string_view& lpNewFileName) {
|
||||
auto wExistingFileName = ENC::to_wchar(lpExistingFileName);
|
||||
auto wNewFileName = ENC::to_wchar(lpNewFileName);
|
||||
if (!(wExistingFileName.has_value() && wNewFileName.has_value())) {
|
||||
return std::unexpected(WinFctError::Encoding);
|
||||
}
|
||||
auto wExistingFileName = ENC::to_wchar(lpExistingFileName).value();
|
||||
auto wNewFileName = ENC::to_wchar(lpNewFileName).value();
|
||||
|
||||
if (!::MoveFileW(wExistingFileName.value().c_str(), wNewFileName.value().c_str())) {
|
||||
return std::unexpected(WinFctError::Backend);
|
||||
if (!::MoveFileW(wExistingFileName.c_str(), wNewFileName.c_str())) {
|
||||
return std::unexpected(WinFctError::Win32);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
WinFctResult<void> delete_file(const std::u8string_view& lpFileName) {
|
||||
auto wFileName = ENC::to_wchar(lpFileName);
|
||||
if (!wFileName.has_value()) {
|
||||
return std::unexpected(WinFctError::Encoding);
|
||||
}
|
||||
auto wFileName = ENC::to_wchar(lpFileName).value();
|
||||
|
||||
if (!::DeleteFileW(wFileName.value().c_str())) {
|
||||
return std::unexpected(WinFctError::Backend);
|
||||
if (!::DeleteFileW(wFileName.c_str())) {
|
||||
return std::unexpected(WinFctError::Win32);
|
||||
}
|
||||
|
||||
return {};
|
||||
@ -144,11 +135,11 @@ namespace yycc::windows::winfct {
|
||||
// fetch path
|
||||
LPWSTR raw_known_path;
|
||||
HRESULT hr = SHGetKnownFolderPath(*pId, KF_FLAG_CREATE, NULL, &raw_known_path);
|
||||
if (FAILED(hr)) return std::unexpected(WinFctError::Backend);
|
||||
if (FAILED(hr)) return std::unexpected(WinFctError::Win32);
|
||||
COM::SmartLPWSTR known_path(raw_known_path);
|
||||
|
||||
// convert to utf8 and return
|
||||
return ENC::to_utf8(known_path.get()).transform_error([](auto err) { return WinFctError::Encoding; });
|
||||
return ENC::to_utf8(known_path.get()).value();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -15,8 +15,7 @@ namespace yycc::windows::winfct {
|
||||
|
||||
/// @brief All errors occur in this module.
|
||||
enum class WinFctError {
|
||||
Backend, ///< Error occurs when calling Win32 functions.
|
||||
Encoding, ///< Can not perform encoding convertion.
|
||||
Win32, ///< Error occurs when calling Win32 functions.
|
||||
NoCom, ///< No COM environment.
|
||||
};
|
||||
|
||||
|
@ -39,6 +39,7 @@ PRIVATE
|
||||
yycc/carton/termcolor.cpp
|
||||
yycc/carton/wcwidth.cpp
|
||||
yycc/carton/tabulate.cpp
|
||||
yycc/carton/clap.cpp
|
||||
)
|
||||
target_sources(YYCCTestbench
|
||||
PRIVATE
|
||||
|
@ -10,12 +10,11 @@ namespace yycctest::patch::ptr_pad {
|
||||
|
||||
TEST(PatchPtrPad, Normal) {
|
||||
auto rv = OP::printf(u8"0x%" PRIXPTR_LPAD PRIXPTR, nullptr);
|
||||
EXPECT_TRUE(rv.has_value());
|
||||
|
||||
#if defined(YYCC_PTRSIZE_32)
|
||||
EXPECT_EQ(rv.value(), u8"0x00000000");
|
||||
EXPECT_EQ(rv, u8"0x00000000");
|
||||
#else
|
||||
EXPECT_EQ(rv.value(), u8"0x0000000000000000");
|
||||
EXPECT_EQ(rv, u8"0x0000000000000000");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,16 @@ using namespace std::literals::string_view_literals;
|
||||
namespace yycctest::string::op {
|
||||
|
||||
TEST(StringOp, Printf) {
|
||||
auto rv = OP::printf(u8"%s == %s", u8"Hello World", u8"Hello, world");
|
||||
EXPECT_EQ(rv, u8"Hello World == Hello, world");
|
||||
// UTF8 string
|
||||
{
|
||||
auto rv = OP::printf(u8"%s == %s", u8"Hello World", u8"Hello, world");
|
||||
EXPECT_EQ(rv, u8"Hello World == Hello, world");
|
||||
}
|
||||
// Ordinary string
|
||||
{
|
||||
auto rv = OP::printf("%s == %s", "Hello World", "Hello, world");
|
||||
EXPECT_EQ(rv, "Hello World == Hello, world");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StringOp, Replace) {
|
||||
|
Reference in New Issue
Block a user