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/tabulate.cpp
|
||||||
yycc/carton/ironpad.cpp
|
yycc/carton/ironpad.cpp
|
||||||
yycc/carton/csconsole.cpp
|
yycc/carton/csconsole.cpp
|
||||||
|
#yycc/carton/clap/option.cpp
|
||||||
)
|
)
|
||||||
target_sources(YYCCommonplace
|
target_sources(YYCCommonplace
|
||||||
PUBLIC
|
PUBLIC
|
||||||
@ -46,6 +47,7 @@ FILES
|
|||||||
yycc/macro/compiler_detector.hpp
|
yycc/macro/compiler_detector.hpp
|
||||||
yycc/macro/ptr_size_detector.hpp
|
yycc/macro/ptr_size_detector.hpp
|
||||||
yycc/macro/class_copy_move.hpp
|
yycc/macro/class_copy_move.hpp
|
||||||
|
yycc/macro/printf_checker.hpp
|
||||||
yycc/flag_enum.hpp
|
yycc/flag_enum.hpp
|
||||||
yycc/string/reinterpret.hpp
|
yycc/string/reinterpret.hpp
|
||||||
yycc/string/op.hpp
|
yycc/string/op.hpp
|
||||||
@ -81,6 +83,9 @@ FILES
|
|||||||
yycc/carton/tabulate.hpp
|
yycc/carton/tabulate.hpp
|
||||||
yycc/carton/ironpad.hpp
|
yycc/carton/ironpad.hpp
|
||||||
yycc/carton/csconsole.hpp
|
yycc/carton/csconsole.hpp
|
||||||
|
yycc/carton/clap.hpp
|
||||||
|
yycc/carton/clap/types.hpp
|
||||||
|
yycc/carton/clap/option.hpp
|
||||||
)
|
)
|
||||||
# Setup header infomations
|
# Setup header infomations
|
||||||
target_include_directories(YYCCommonplace
|
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)) {
|
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
||||||
// console handle, use WriteConsoleW.
|
// console handle, use WriteConsoleW.
|
||||||
// convert utf8 string to wide char first
|
// convert utf8 string to wide char first
|
||||||
auto rv = ENC::to_wchar(strl);
|
std::wstring wstrl = ENC::to_wchar(strl).value();
|
||||||
if (!rv.has_value()) return;
|
|
||||||
std::wstring wstrl(std::move(rv.value()));
|
|
||||||
size_t wstrl_size = wstrl.size();
|
size_t wstrl_size = wstrl.size();
|
||||||
// write string with size check
|
// write string with size check
|
||||||
if (wstrl_size <= std::numeric_limits<DWORD>::max()) {
|
if (wstrl_size <= std::numeric_limits<DWORD>::max()) {
|
||||||
@ -171,11 +169,8 @@ namespace yycc::carton::csconsole {
|
|||||||
// treat as format string
|
// treat as format string
|
||||||
va_list argcpy;
|
va_list argcpy;
|
||||||
va_copy(argcpy, argptr);
|
va_copy(argcpy, argptr);
|
||||||
auto rv = OP::vprintf(u8_fmt, argcpy);
|
strl = OP::vprintf(u8_fmt, argcpy);
|
||||||
va_end(argcpy);
|
va_end(argcpy);
|
||||||
// check format result
|
|
||||||
if (!rv.has_value()) return;
|
|
||||||
else strl = std::move(rv.value());
|
|
||||||
} else {
|
} else {
|
||||||
// treat as plain string
|
// treat as plain string
|
||||||
strl = u8_fmt;
|
strl = u8_fmt;
|
||||||
|
@ -63,11 +63,9 @@ namespace yycc::carton::ironpad {
|
|||||||
// check singleton
|
// check singleton
|
||||||
// build mutex string first
|
// build mutex string first
|
||||||
auto mutex_name = OP::printf(u8"Global\\%" PRIu32 ".{61634294-d23c-43f9-8490-b5e09837eede}", GetCurrentProcessId());
|
auto mutex_name = OP::printf(u8"Global\\%" PRIu32 ".{61634294-d23c-43f9-8490-b5e09837eede}", GetCurrentProcessId());
|
||||||
if (!mutex_name.has_value()) return false;
|
auto wmutex_name = ENC::to_wchar(mutex_name).value();
|
||||||
auto w_mutex_name = ENC::to_wchar(mutex_name.value());
|
|
||||||
if (!w_mutex_name.has_value()) return false;
|
|
||||||
// create mutex
|
// 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();
|
DWORD errcode = GetLastError();
|
||||||
// check whether be created
|
// check whether be created
|
||||||
if (m_SingletonMutex == NULL) return false;
|
if (m_SingletonMutex == NULL) return false;
|
||||||
@ -303,18 +301,17 @@ namespace yycc::carton::ironpad {
|
|||||||
DWORD process_id = GetCurrentProcessId();
|
DWORD process_id = GetCurrentProcessId();
|
||||||
// conbine them as a file name prefix
|
// conbine them as a file name prefix
|
||||||
auto u8_filename_prefix = OP::printf(u8"%s.%" PRIu32, u8_process_name.c_str(), process_id);
|
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
|
// then get file name for log and minidump
|
||||||
std::u8string u8_filename;
|
std::u8string u8_filename;
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case FileKind::LogFile:
|
case FileKind::LogFile:
|
||||||
u8_filename = u8_filename_prefix.value() + u8".log";
|
u8_filename = u8_filename_prefix + u8".log";
|
||||||
break;
|
break;
|
||||||
case FileKind::CoredumpFile:
|
case FileKind::CoredumpFile:
|
||||||
u8_filename = u8_filename_prefix.value() + u8".dmp";
|
u8_filename = u8_filename_prefix + u8".dmp";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
u8_filename = u8_filename_prefix.value();
|
u8_filename = u8_filename_prefix;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,11 +448,9 @@ namespace yycc::carton::ironpad {
|
|||||||
* @details This function will write coredump of given exception into given file path.
|
* @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) {
|
void do_coredump(const std::u8string_view& u8_filename, LPEXCEPTION_POINTERS info) {
|
||||||
// convert file encoding
|
// convert file encoding.
|
||||||
// if convertion failed, return
|
// it must be okey.
|
||||||
auto filename_rv = ENC::to_wchar(u8_filename);
|
auto filename = ENC::to_wchar(u8_filename).value();
|
||||||
if (!filename_rv.has_value()) return;
|
|
||||||
std::wstring filename = filename_rv.value();
|
|
||||||
|
|
||||||
// open file and write
|
// open file and write
|
||||||
HANDLE hFile = CreateFileW(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
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.
|
* @param[in] ... The argument to be formatted.
|
||||||
*/
|
*/
|
||||||
void log_format_line(std::FILE* fs, const char8_t* fmt, ...) {
|
void log_format_line(std::FILE* fs, const char8_t* fmt, ...) {
|
||||||
// do format first
|
// write to file and console
|
||||||
va_list arg;
|
va_list arg;
|
||||||
va_start(arg, fmt);
|
va_start(arg, fmt);
|
||||||
auto fmt_rv = OP::vprintf(fmt, arg);
|
log_write_line(fs, OP::vprintf(fmt, arg).c_str());
|
||||||
va_end(arg);
|
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) {
|
Result<size_t> wcswidth(const std::u8string_view& rhs) {
|
||||||
// Cast encoding
|
// Cast encoding and call underlying function
|
||||||
auto u32str = ENC::to_utf32(rhs);
|
return wcswidth(ENC::to_utf32(rhs).value());
|
||||||
if (!u32str.has_value()) return std::unexpected(Error::BadEncoding);
|
|
||||||
// Call underlying function
|
|
||||||
return wcswidth(u32str.value());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace yycc::carton::wcwidth
|
} // namespace yycc::carton::wcwidth
|
||||||
|
@ -31,7 +31,6 @@ namespace yycc::carton::wcwidth {
|
|||||||
|
|
||||||
/// @brief Error occurs in this module
|
/// @brief Error occurs in this module
|
||||||
enum class Error {
|
enum class Error {
|
||||||
BadEncoding, ///< Given
|
|
||||||
BadAnsiEscSeq, ///< Bad char when processing ANSI Escape Sequence
|
BadAnsiEscSeq, ///< Bad char when processing ANSI Escape Sequence
|
||||||
BadCsiSeq, ///< Bad char when processing CSI 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) {
|
std::FILE* fopen(const char8_t* u8_filepath, const char8_t* u8_mode) {
|
||||||
#if defined(YYCC_OS_WINDOWS)
|
#if defined(YYCC_OS_WINDOWS)
|
||||||
// convert mode and file path to wchar
|
// Convert encoding first, and call MSVCRT specified fopen which support wchar as argument.
|
||||||
auto wmode = ENC::to_wchar(u8_mode);
|
// Reference: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170
|
||||||
auto wpath = ENC::to_wchar(u8_filepath);
|
return _wfopen(ENC::to_wchar(u8_filepath).value().c_str(), ENC::to_wchar(u8_mode).value().c_str());
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
return std::fopen(REINTERPRET::as_ordinary(u8_filepath), REINTERPRET::as_ordinary(u8_mode));
|
return std::fopen(REINTERPRET::as_ordinary(u8_filepath), REINTERPRET::as_ordinary(u8_mode));
|
||||||
#endif
|
#endif
|
||||||
|
@ -25,10 +25,7 @@ namespace yycc::rust::env {
|
|||||||
#if defined(YYCC_OS_WINDOWS)
|
#if defined(YYCC_OS_WINDOWS)
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
|
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
|
||||||
// Convert to wchar
|
// Convert to wchar
|
||||||
auto wname = ENC::to_wchar(name);
|
auto wname = ENC::to_wchar(name).value();
|
||||||
if (!wname.has_value()) {
|
|
||||||
return std::unexpected(EnvError::BadEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare a variable with proper init size for holding value.
|
// Prepare a variable with proper init size for holding value.
|
||||||
std::wstring wvalue;
|
std::wstring wvalue;
|
||||||
@ -42,7 +39,7 @@ namespace yycc::rust::env {
|
|||||||
// So we forcely use checked add and sub for this bad behavior.
|
// So we forcely use checked add and sub for this bad behavior.
|
||||||
auto fct_size = SAFEOP::checked_add<size_t>(wvalue.size(), 1);
|
auto fct_size = SAFEOP::checked_add<size_t>(wvalue.size(), 1);
|
||||||
if (!fct_size.has_value()) return std::unexpected(EnvError::BadArithmetic);
|
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
|
// Check the return value
|
||||||
if (rv == 0) {
|
if (rv == 0) {
|
||||||
@ -95,20 +92,10 @@ namespace yycc::rust::env {
|
|||||||
#if defined(YYCC_OS_WINDOWS)
|
#if defined(YYCC_OS_WINDOWS)
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||||
|
|
||||||
// Convert to wchar
|
// Convert to wchar, set variable, and check result.
|
||||||
auto wname = ENC::to_wchar(name);
|
auto rv = ::SetEnvironmentVariableW(ENC::to_wchar(name).value().c_str(), ENC::to_wchar(value).value().c_str());
|
||||||
auto wvalue = ENC::to_wchar(value);
|
if (!rv) return std::unexpected(EnvError::BadCall);
|
||||||
if (!(wname.has_value() && wvalue.has_value())) {
|
else return {};
|
||||||
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 {};
|
|
||||||
#else
|
#else
|
||||||
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html
|
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html
|
||||||
|
|
||||||
@ -129,19 +116,10 @@ namespace yycc::rust::env {
|
|||||||
#if defined(YYCC_OS_WINDOWS)
|
#if defined(YYCC_OS_WINDOWS)
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||||
|
|
||||||
// Convert to wchar
|
// Convert to wchar, delete variable, and check result.
|
||||||
auto wname = ENC::to_wchar(name);
|
auto rv = ::SetEnvironmentVariableW(ENC::to_wchar(name).value().c_str(), NULL);
|
||||||
if (!wname.has_value()) {
|
if (!rv) return std::unexpected(EnvError::BadCall);
|
||||||
return std::unexpected(EnvError::BadEncoding);
|
else return {};
|
||||||
}
|
|
||||||
|
|
||||||
// Delete variable and check result.
|
|
||||||
auto rv = ::SetEnvironmentVariableW(wname.value().c_str(), NULL);
|
|
||||||
if (!rv) {
|
|
||||||
return std::unexpected(EnvError::BadCall);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
#else
|
#else
|
||||||
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html
|
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html
|
||||||
|
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
#include "op.hpp"
|
#include "op.hpp"
|
||||||
|
#include <type_traits>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace yycc::string::op {
|
namespace yycc::string::op {
|
||||||
|
|
||||||
#pragma region Printf VPrintf
|
#pragma region Printf VPrintf
|
||||||
|
|
||||||
|
/// @brief The concept for all viable char type in printf function family
|
||||||
template<typename TChar>
|
template<typename TChar>
|
||||||
requires(sizeof(TChar) == sizeof(char))
|
concept PrintfSupportedChar = std::is_same_v<TChar, char> || std::is_same_v<TChar, char8_t>;
|
||||||
static FormatResult<std::basic_string<TChar>> generic_printf(const TChar* format, va_list argptr) {
|
|
||||||
|
template<PrintfSupportedChar TChar>
|
||||||
|
static std::basic_string<TChar> generic_printf(const TChar* format, va_list argptr) {
|
||||||
// Prepare result
|
// Prepare result
|
||||||
std::basic_string<TChar> rv;
|
std::basic_string<TChar> rv;
|
||||||
|
|
||||||
// Check format
|
|
||||||
if (format == nullptr) return std::unexpected(FormatError::NullFormat);
|
|
||||||
|
|
||||||
// Prepare variable arguments
|
// Prepare variable arguments
|
||||||
va_list args1;
|
va_list args1;
|
||||||
va_copy(args1, argptr);
|
va_copy(args1, argptr);
|
||||||
@ -21,12 +23,13 @@ namespace yycc::string::op {
|
|||||||
va_copy(args2, argptr);
|
va_copy(args2, argptr);
|
||||||
|
|
||||||
// The return value is desired char count without NULL terminal.
|
// 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);
|
int count = std::vsnprintf(nullptr, 0, reinterpret_cast<const char*>(format), args1);
|
||||||
// Check expected size.
|
// Check expected size.
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
// Invalid length returned by vsnprintf.
|
// 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);
|
va_end(args1);
|
||||||
|
|
||||||
@ -39,14 +42,15 @@ namespace yycc::string::op {
|
|||||||
// Check written size.
|
// Check written size.
|
||||||
if (write_result < 0 || write_result > count) {
|
if (write_result < 0 || write_result > count) {
|
||||||
// Invalid write result in vsnprintf.
|
// 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 value
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatResult<std::u8string> printf(const char8_t* format, ...) {
|
std::u8string printf(const char8_t* format, ...) {
|
||||||
va_list argptr;
|
va_list argptr;
|
||||||
va_start(argptr, format);
|
va_start(argptr, format);
|
||||||
auto rv = vprintf(format, argptr);
|
auto rv = vprintf(format, argptr);
|
||||||
@ -54,11 +58,11 @@ namespace yycc::string::op {
|
|||||||
return rv;
|
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);
|
return generic_printf(format, argptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatResult<std::string> printf(const char* format, ...) {
|
std::string printf(const char* format, ...) {
|
||||||
va_list argptr;
|
va_list argptr;
|
||||||
va_start(argptr, format);
|
va_start(argptr, format);
|
||||||
auto rv = vprintf(format, argptr);
|
auto rv = vprintf(format, argptr);
|
||||||
@ -66,7 +70,7 @@ namespace yycc::string::op {
|
|||||||
return rv;
|
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);
|
return generic_printf(format, argptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,50 +1,41 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "../macro/printf_checker.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <expected>
|
|
||||||
|
|
||||||
namespace yycc::string::op {
|
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.
|
* @brief Perform an UTF8 string formatting operation.
|
||||||
* @param[in] format The format string.
|
* @param[in] format The format string.
|
||||||
* @param[in] ... Argument list of 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.
|
* @brief Perform an UTF8 string formatting operation.
|
||||||
* @param[in] format The format string.
|
* @param[in] format The format string.
|
||||||
* @param[in] argptr Argument list of 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.
|
* @brief Perform an ordinary string formatting operation.
|
||||||
* @param[in] format The format string.
|
* @param[in] format The format string.
|
||||||
* @param[in] ... Argument list of 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.
|
* @brief Perform an ordinary string formatting operation.
|
||||||
* @param[in] format The format string.
|
* @param[in] format The format string.
|
||||||
* @param[in] argptr Argument list of 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.
|
* @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
|
// build new Windows oriented string vector
|
||||||
for (const auto& item : m_Filters) {
|
for (const auto& item : m_Filters) {
|
||||||
// convert name to wchar
|
// convert name to wchar
|
||||||
auto win_name = ENC::to_wchar(item.first);
|
auto win_name = ENC::to_wchar(item.first).value();
|
||||||
if (!win_name.has_value()) {
|
|
||||||
return std::unexpected(DialogError::BadEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// join pattern string and convert to wchar
|
// join pattern string and convert to wchar
|
||||||
const auto& modes = item.second;
|
const auto& modes = item.second;
|
||||||
auto joined_modes = OP::join(modes.begin(), modes.end(), u8";");
|
auto joined_modes = OP::join(modes.begin(), modes.end(), u8";");
|
||||||
auto win_modes = ENC::to_wchar(joined_modes);
|
auto win_modes = ENC::to_wchar(joined_modes).value();
|
||||||
if (!win_modes.has_value()) {
|
|
||||||
return std::unexpected(DialogError::BadEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// append this pair
|
// 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
|
// update data struct
|
||||||
@ -305,28 +299,21 @@ namespace yycc::windows::dialog {
|
|||||||
|
|
||||||
// build title and init file name
|
// build title and init file name
|
||||||
if (m_Title.has_value()) {
|
if (m_Title.has_value()) {
|
||||||
auto win_title = ENC::to_wchar(m_Title.value());
|
rv.m_WinTitle = ENC::to_wchar(m_Title.value()).value();
|
||||||
if (!win_title.has_value()) return std::unexpected(DialogError::BadEncoding);
|
|
||||||
else rv.m_WinTitle = win_title.value();
|
|
||||||
} else rv.m_WinTitle = std::nullopt;
|
} else rv.m_WinTitle = std::nullopt;
|
||||||
if (m_InitFileName.has_value()) {
|
if (m_InitFileName.has_value()) {
|
||||||
auto win_init_file_name = ENC::to_wchar(m_InitFileName.value());
|
rv.m_WinInitFileName = ENC::to_wchar(m_InitFileName.value()).value();
|
||||||
if (!win_init_file_name.has_value()) return std::unexpected(DialogError::BadEncoding);
|
|
||||||
else rv.m_WinInitFileName = win_init_file_name.value();
|
|
||||||
} else rv.m_WinInitFileName = std::nullopt;
|
} else rv.m_WinInitFileName = std::nullopt;
|
||||||
|
|
||||||
// fetch init directory
|
// fetch init directory
|
||||||
if (m_InitDirectory.has_value()) {
|
if (m_InitDirectory.has_value()) {
|
||||||
// convert to wchar path
|
// convert to wchar path
|
||||||
auto w_init_dir = ENC::to_wchar(m_InitDirectory.value());
|
auto w_init_dir = ENC::to_wchar(m_InitDirectory.value()).value();
|
||||||
if (!w_init_dir.has_value()) {
|
|
||||||
return std::unexpected(DialogError::BadEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch IShellItem*
|
// fetch IShellItem*
|
||||||
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
|
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
|
||||||
IShellItem* init_directory = NULL;
|
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);
|
if (FAILED(hr)) return std::unexpected(DialogError::NoSuchDir);
|
||||||
|
|
||||||
// assign IShellItem*
|
// assign IShellItem*
|
||||||
@ -378,7 +365,7 @@ namespace yycc::windows::dialog {
|
|||||||
WINCOM::SmartLPWSTR display_name(display_name_ptr);
|
WINCOM::SmartLPWSTR display_name(display_name_ptr);
|
||||||
|
|
||||||
// convert result and return
|
// 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.
|
/// @brief The error occurs in this module.
|
||||||
enum class DialogError {
|
enum class DialogError {
|
||||||
BadEncoding, ///< Error occurs when perform encoding convertion.
|
|
||||||
TooManyFilters, ///< The size of file filters list is too large for Windows.
|
TooManyFilters, ///< The size of file filters list is too large for Windows.
|
||||||
IndexOverflow, ///< Default filter index is too large for Windows.
|
IndexOverflow, ///< Default filter index is too large for Windows.
|
||||||
EmptyFilters, ///< File filters is empty when picking file.
|
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);
|
DialogResult<DialogOutcome<std::u8string>> open_folder(const FileDialog& params);
|
||||||
|
|
||||||
}
|
} // namespace yycc::windows::dialog
|
||||||
|
|
||||||
#undef NS_YYCC_WINDOWS_COM
|
#undef NS_YYCC_WINDOWS_COM
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ namespace yycc::windows::winfct {
|
|||||||
(LPCWSTR) get_current_module,
|
(LPCWSTR) get_current_module,
|
||||||
&hModule);
|
&hModule);
|
||||||
if (rv) return hModule;
|
if (rv) return hModule;
|
||||||
else return std::unexpected(WinFctError::Backend);
|
else return std::unexpected(WinFctError::Win32);
|
||||||
}
|
}
|
||||||
|
|
||||||
WinFctResult<std::u8string> get_temp_directory() {
|
WinFctResult<std::u8string> get_temp_directory() {
|
||||||
@ -33,7 +33,7 @@ namespace yycc::windows::winfct {
|
|||||||
while (true) {
|
while (true) {
|
||||||
if ((expected_size = ::GetTempPathW(static_cast<DWORD>(wpath.size()), wpath.data())) == 0) {
|
if ((expected_size = ::GetTempPathW(static_cast<DWORD>(wpath.size()), wpath.data())) == 0) {
|
||||||
// failed, return
|
// failed, return
|
||||||
return std::unexpected(WinFctError::Backend);
|
return std::unexpected(WinFctError::Win32);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected_size > static_cast<DWORD>(wpath.size())) {
|
if (expected_size > static_cast<DWORD>(wpath.size())) {
|
||||||
@ -47,7 +47,7 @@ namespace yycc::windows::winfct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convert to utf8 and return
|
// 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) {
|
WinFctResult<std::u8string> get_module_file_name(HINSTANCE hModule) {
|
||||||
@ -58,7 +58,7 @@ namespace yycc::windows::winfct {
|
|||||||
while (true) {
|
while (true) {
|
||||||
if ((copied_size = ::GetModuleFileNameW(hModule, wpath.data(), static_cast<DWORD>(wpath.size()))) == 0) {
|
if ((copied_size = ::GetModuleFileNameW(hModule, wpath.data(), static_cast<DWORD>(wpath.size()))) == 0) {
|
||||||
// failed, return
|
// failed, return
|
||||||
return std::unexpected(WinFctError::Backend);
|
return std::unexpected(WinFctError::Win32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check insufficient buffer
|
// check insufficient buffer
|
||||||
@ -73,7 +73,7 @@ namespace yycc::windows::winfct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convert to utf8 and return
|
// 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) {
|
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) {
|
WinFctResult<void> copy_file(const std::u8string_view& lpExistingFileName, const std::u8string_view& lpNewFileName, BOOL bFailIfExists) {
|
||||||
auto wExistingFileName = ENC::to_wchar(lpExistingFileName);
|
auto wExistingFileName = ENC::to_wchar(lpExistingFileName).value();
|
||||||
auto wNewFileName = ENC::to_wchar(lpNewFileName);
|
auto wNewFileName = ENC::to_wchar(lpNewFileName).value();
|
||||||
if (!(wExistingFileName.has_value() && wNewFileName.has_value())) {
|
|
||||||
return std::unexpected(WinFctError::Encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!::CopyFileW(wExistingFileName.value().c_str(), wNewFileName.value().c_str(), bFailIfExists)) {
|
if (!::CopyFileW(wExistingFileName.c_str(), wNewFileName.c_str(), bFailIfExists)) {
|
||||||
return std::unexpected(WinFctError::Backend);
|
return std::unexpected(WinFctError::Win32);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
WinFctResult<void> move_file(const std::u8string_view& lpExistingFileName, const std::u8string_view& lpNewFileName) {
|
WinFctResult<void> move_file(const std::u8string_view& lpExistingFileName, const std::u8string_view& lpNewFileName) {
|
||||||
auto wExistingFileName = ENC::to_wchar(lpExistingFileName);
|
auto wExistingFileName = ENC::to_wchar(lpExistingFileName).value();
|
||||||
auto wNewFileName = ENC::to_wchar(lpNewFileName);
|
auto wNewFileName = ENC::to_wchar(lpNewFileName).value();
|
||||||
if (!(wExistingFileName.has_value() && wNewFileName.has_value())) {
|
|
||||||
return std::unexpected(WinFctError::Encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!::MoveFileW(wExistingFileName.value().c_str(), wNewFileName.value().c_str())) {
|
if (!::MoveFileW(wExistingFileName.c_str(), wNewFileName.c_str())) {
|
||||||
return std::unexpected(WinFctError::Backend);
|
return std::unexpected(WinFctError::Win32);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
WinFctResult<void> delete_file(const std::u8string_view& lpFileName) {
|
WinFctResult<void> delete_file(const std::u8string_view& lpFileName) {
|
||||||
auto wFileName = ENC::to_wchar(lpFileName);
|
auto wFileName = ENC::to_wchar(lpFileName).value();
|
||||||
if (!wFileName.has_value()) {
|
|
||||||
return std::unexpected(WinFctError::Encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!::DeleteFileW(wFileName.value().c_str())) {
|
if (!::DeleteFileW(wFileName.c_str())) {
|
||||||
return std::unexpected(WinFctError::Backend);
|
return std::unexpected(WinFctError::Win32);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@ -144,11 +135,11 @@ namespace yycc::windows::winfct {
|
|||||||
// fetch path
|
// fetch path
|
||||||
LPWSTR raw_known_path;
|
LPWSTR raw_known_path;
|
||||||
HRESULT hr = SHGetKnownFolderPath(*pId, KF_FLAG_CREATE, NULL, &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);
|
COM::SmartLPWSTR known_path(raw_known_path);
|
||||||
|
|
||||||
// convert to utf8 and return
|
// 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
|
#endif
|
||||||
|
@ -15,8 +15,7 @@ namespace yycc::windows::winfct {
|
|||||||
|
|
||||||
/// @brief All errors occur in this module.
|
/// @brief All errors occur in this module.
|
||||||
enum class WinFctError {
|
enum class WinFctError {
|
||||||
Backend, ///< Error occurs when calling Win32 functions.
|
Win32, ///< Error occurs when calling Win32 functions.
|
||||||
Encoding, ///< Can not perform encoding convertion.
|
|
||||||
NoCom, ///< No COM environment.
|
NoCom, ///< No COM environment.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ PRIVATE
|
|||||||
yycc/carton/termcolor.cpp
|
yycc/carton/termcolor.cpp
|
||||||
yycc/carton/wcwidth.cpp
|
yycc/carton/wcwidth.cpp
|
||||||
yycc/carton/tabulate.cpp
|
yycc/carton/tabulate.cpp
|
||||||
|
yycc/carton/clap.cpp
|
||||||
)
|
)
|
||||||
target_sources(YYCCTestbench
|
target_sources(YYCCTestbench
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
@ -10,12 +10,11 @@ namespace yycctest::patch::ptr_pad {
|
|||||||
|
|
||||||
TEST(PatchPtrPad, Normal) {
|
TEST(PatchPtrPad, Normal) {
|
||||||
auto rv = OP::printf(u8"0x%" PRIXPTR_LPAD PRIXPTR, nullptr);
|
auto rv = OP::printf(u8"0x%" PRIXPTR_LPAD PRIXPTR, nullptr);
|
||||||
EXPECT_TRUE(rv.has_value());
|
|
||||||
|
|
||||||
#if defined(YYCC_PTRSIZE_32)
|
#if defined(YYCC_PTRSIZE_32)
|
||||||
EXPECT_EQ(rv.value(), u8"0x00000000");
|
EXPECT_EQ(rv, u8"0x00000000");
|
||||||
#else
|
#else
|
||||||
EXPECT_EQ(rv.value(), u8"0x0000000000000000");
|
EXPECT_EQ(rv, u8"0x0000000000000000");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,16 @@ using namespace std::literals::string_view_literals;
|
|||||||
namespace yycctest::string::op {
|
namespace yycctest::string::op {
|
||||||
|
|
||||||
TEST(StringOp, Printf) {
|
TEST(StringOp, Printf) {
|
||||||
auto rv = OP::printf(u8"%s == %s", u8"Hello World", u8"Hello, world");
|
// UTF8 string
|
||||||
EXPECT_EQ(rv, u8"Hello World == Hello, world");
|
{
|
||||||
|
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) {
|
TEST(StringOp, Replace) {
|
||||||
|
Reference in New Issue
Block a user