refactor: update C++ from 17 to 23

This commit is contained in:
2025-07-25 09:35:26 +08:00
parent f014e54604
commit 4f0b3d19d1
23 changed files with 335 additions and 1139 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
# -------------------- Output --------------------
out/
src/YYCC/YYCCVersion.hpp
src/yycc/version.hpp
CMakeSettings.json
# -------------------- VSCode --------------------

View File

@ -4,11 +4,15 @@ project(YYCC
LANGUAGES CXX
)
# Setup C++ standard
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Provide options
option(YYCC_BUILD_TESTBENCH "Build testbench of YYCCommonplace." OFF)
option(YYCC_BUILD_DOC "Build document of YYCCommonplace." OFF)
option(YYCC_ENFORCE_ICONV "Enforce iconv support for this library (e.g. in MSYS2 environment)." OFF)
option(YYCC_DEBUG_UE_FILTER "YYCC developer used switch for testing Windows unhandled exception filter. Should not set to ON!!!" OFF)
# Setup install path from CMake provided install path for convenient use.
include(GNUInstallDirs)

View File

@ -14,24 +14,10 @@ PRIVATE
yycc/string/reinterpret.cpp
yycc/string/op.cpp
yycc/rust/panic.cpp
yycc/patch/path.cpp
yycc/encoding/stlcvt.cpp
yycc/encoding/windows.cpp
yycc/encoding/iconv.cpp
yycc/encoding/pycodec.cpp
# YYCC/COMHelper.cpp
# YYCC/ArgParser.cpp
# YYCC/ConfigManager.cpp
# YYCC/ConsoleHelper.cpp
# YYCC/DialogHelper.cpp
# YYCC/EncodingHelper.cpp
# YYCC/ExceptionHelper.cpp
# YYCC/StdPatch.cpp
# YYCC/IOHelper.cpp
# YYCC/StringHelper.cpp
# YYCC/WinFctHelper.cpp
# # Natvis (only for MSVC)
# $<$<CXX_COMPILER_ID:MSVC>:YYCC.natvis>
)
target_sources(YYCCommonplace
PUBLIC
@ -40,63 +26,29 @@ FILES
# Headers
yycc.hpp
yycc/version.hpp
yycc/primitive.hpp
yycc/prelude/core.hpp
yycc/prelude/rust.hpp
yycc/macro/version_cmp.hpp
yycc/macro/feature_probe.hpp
yycc/macro/os_detector.hpp
yycc/macro/endian_detector.hpp
yycc/macro/compiler_detector.hpp
yycc/macro/class_copy_move.hpp
yycc/string.hpp
yycc/string/reinterpret.hpp
yycc/string/op.hpp
yycc/num/parse.hpp
yycc/num/stringify.hpp
yycc/rust/primitive.hpp
yycc/rust/panic.hpp
yycc/rust/option.hpp
yycc/rust/result.hpp
yycc/rust/num/parse.hpp
yycc/rust/num/stringify.hpp
yycc/windows/unsafe_suppressor.hpp
yycc/windows/import_guard_head.hpp
yycc/windows/import_guard_tail.hpp
yycc/constraint.hpp
yycc/constraint/builder.hpp
yycc/patch/path.hpp
yycc/patch/contains.hpp
yycc/patch/starts_ends_with.hpp
yycc/patch/expected.hpp
yycc/encoding/stlcvt.hpp
yycc/encoding/windows.hpp
yycc/encoding/iconv.hpp
yycc/encoding/pycodec.hpp
# # Headers
# # Common headers
# YYCC/Constraints.hpp
# YYCC/COMHelper.hpp
# YYCC/ArgParser.hpp
# YYCC/ConfigManager.hpp
# YYCC/ConsoleHelper.hpp
# YYCC/DialogHelper.hpp
# YYCC/EncodingHelper.hpp
# YYCC/EnumHelper.hpp
# YYCC/ExceptionHelper.hpp
# YYCC/StdPatch.hpp
# YYCC/IOHelper.hpp
# YYCC/ParserHelper.hpp
# YYCC/StringHelper.hpp
# YYCC/WinFctHelper.hpp
# # Windows including guard pair
# YYCC/WinImportPrefix.hpp
# YYCC/WinImportSuffix.hpp
# # Internal
# YYCC/YYCCVersion.hpp
# YYCC/YYCCInternal.hpp
# # Exposed
# YYCCommonplace.hpp
)
# Setup header infomations
target_include_directories(YYCCommonplace
@ -116,14 +68,9 @@ target_link_libraries(YYCCommonplace
PRIVATE
$<$<BOOL:${WIN32}>:DbgHelp.lib>
)
# Setup C++ standard
target_compile_features(YYCCommonplace PUBLIC cxx_std_17)
set_target_properties(YYCCommonplace PROPERTIES CXX_EXTENSION OFF)
# Setup macros
target_compile_definitions(YYCCommonplace
PUBLIC
# Debug macro. And it should populate to child projects
$<$<BOOL:${YYCC_DEBUG_UE_FILTER}>:YYCC_DEBUG_UE_FILTER>
# Iconv environment macro
$<$<BOOL:${YYCC_ENFORCE_ICONV}>:YYCC_FEAT_ICONV>
# OS macro
@ -136,14 +83,21 @@ PUBLIC
# Endian macro
$<$<STREQUAL:${CMAKE_CXX_BYTE_ORDER},LITTLE_ENDIAN>:YYCC_ENDIAN_LITTLE>
$<$<STREQUAL:${CMAKE_CXX_BYTE_ORDER},BIG_ENDIAN>:YYCC_ENDIAN_BIG>
PRIVATE
# Unicode charset for private using
# Use Unicode charset on MSVC
$<$<CXX_COMPILER_ID:MSVC>:UNICODE>
$<$<CXX_COMPILER_ID:MSVC>:_UNICODE>
# Fix MSVC shit
$<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS>
$<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_DEPRECATE>
$<$<CXX_COMPILER_ID:MSVC>:_CRT_NONSTDC_NO_WARNINGS>
$<$<CXX_COMPILER_ID:MSVC>:_CRT_NONSTDC_NO_DEPRECATE>
# Fix Windows header file shit
$<$<BOOL:${WIN32}>:WIN32_LEAN_AND_MEAN>
$<$<BOOL:${WIN32}>:NOMINMAX>
)
target_compile_options(YYCCommonplace
# Order build as UTF-8 in MSVC
PRIVATE
PUBLIC
# Order build as UTF-8 in MSVC
$<$<CXX_COMPILER_ID:MSVC>:/utf-8>
)

View File

@ -4,11 +4,13 @@
#include "yycc/version.hpp"
#include "yycc/macro/version_cmp.hpp"
// Operating System Identifier Macros
// Detect essential macros
// Operating System macros
#include "yycc/macro/os_detector.hpp"
// Windows Shitty Behavior Disable Macros
#include "yycc/windows/unsafe_suppressor.hpp"
// Compiler macros
#include "yycc/macro/compiler_detector.hpp"
// Endian macros
#include "yycc/macro/endian_detector.hpp"
// Batch Class Move / Copy Function Macros
#include "yycc/macro/class_copy_move.hpp"

View File

@ -1,10 +1,7 @@
#pragma once
#include "../constraint.hpp"
#include "../string.hpp"
#include <set>
#define NS_YYCC_STRING ::yycc::string
/// @brief The namespace containing convenient function building common used Constraint instance.
namespace yycc::constraint::builder {
@ -15,18 +12,12 @@ namespace yycc::constraint::builder {
* @param[in] max_value The maximum value of range (inclusive).
* @return The generated constraint instance which can be directly applied.
*/
template<typename T,
std::enable_if_t<std::is_arithmetic_v<T> && !std::is_same_v<T, bool>, int> = 0>
template<typename T, std::enable_if_t<std::is_arithmetic_v<T> && !std::is_same_v<T, bool>, int> = 0>
Constraint<T> min_max_constraint(T min_value, T max_value) {
if (min_value > max_value)
throw std::invalid_argument("the max value must be equal or greater than min value");
if (min_value > max_value) throw std::invalid_argument("the max value must be equal or greater than min value");
auto fn_check = [min_value, max_value](const T& val) -> bool {
return (val <= max_value) && (val >= min_value);
};
auto fn_clamp = [min_value, max_value](const T& val) -> T {
return std::clamp(val, min_value, max_value);
};
auto fn_check = [min_value, max_value](const T& val) -> bool { return (val <= max_value) && (val >= min_value); };
auto fn_clamp = [min_value, max_value](const T& val) -> T { return std::clamp(val, min_value, max_value); };
return Constraint<T>(std::move(fn_check), std::move(fn_clamp));
}
@ -39,16 +30,13 @@ namespace yycc::constraint::builder {
*/
template<typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
Constraint<T> enum_constraint(const std::initializer_list<T>& il, size_t default_index = 0u) {
if (default_index >= il.size())
throw std::invalid_argument("the default index must be a valid index in given list");
if (default_index >= il.size()) throw std::invalid_argument("the default index must be a valid index in given list");
T default_entry = il.begin()[default_index];
std::set<T> entries(il);
// TODO: modify it as `contain` once we finish patch namespace.
auto fn_check = [entries](const T& val) -> bool {
return entries.find(val) != entries.end();
};
auto fn_check = [entries](const T& val) -> bool { return entries.find(val) != entries.end(); };
auto fn_clamp = [entries, default_entry](const T& val) -> T {
if (entries.find(val) != entries.end()) {
return val;
@ -65,30 +53,21 @@ namespace yycc::constraint::builder {
* @param[in] default_index The index of default value in given list.
* @return The generated constraint instance which can be directly applied.
*/
inline Constraint<NS_YYCC_STRING::u8string> strenum_constraint(
const std::initializer_list<NS_YYCC_STRING::u8string_view>& il, size_t default_index = 0u) {
if (default_index >= il.size())
throw std::invalid_argument("the default index must be a valid index in given list");
inline Constraint<std::u8string> strenum_constraint(const std::initializer_list<std::u8string_view>& il, size_t default_index = 0u) {
if (default_index >= il.size()) throw std::invalid_argument("the default index must be a valid index in given list");
NS_YYCC_STRING::u8string default_entry = NS_YYCC_STRING::u8string(il.begin()[default_index]);
std::set<NS_YYCC_STRING::u8string> entries;
std::u8string default_entry = std::u8string(il.begin()[default_index]);
std::set<std::u8string> entries;
for (const auto& i : il) {
entries.emplace(i);
}
// TODO: modify it as `contain` once we finish patch namespace.
auto fn_check = [entries](const NS_YYCC_STRING::u8string& val) -> bool {
return entries.find(val) != entries.end();
auto fn_check = [entries](const std::u8string& val) -> bool { return entries.contains(val); };
auto fn_clamp = [entries, default_entry](const std::u8string& val) -> std::u8string {
if (entries.contains(val)) return val;
else return default_entry;
};
auto fn_clamp = [entries, default_entry](
const NS_YYCC_STRING::u8string& val) -> NS_YYCC_STRING::u8string {
if (entries.find(val) != entries.end()) {
return val;
} else {
return default_entry;
}
};
return Constraint<NS_YYCC_STRING::u8string>(std::move(fn_check), fn_clamp);
return Constraint<std::u8string>(std::move(fn_check), fn_clamp);
}
} // namespace yycc::constraint::builder

View File

@ -1,54 +0,0 @@
#pragma once
// Hint for C++ feature detection:
// __cplusplus macro need special compiler switch enabled when compiling.
// So we use _MSVC_LANG check it instead.
// ===== C++ Version =====
// Detect C++ 20
#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
#define YYCC_CPPFEAT_GE_CPP20
#endif
// Detect C++ 23
#if __cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L)
#define YYCC_CPPFEAT_GE_CPP23
#endif
// ===== C++ Features =====
// Check whether there is support of UTF8 string system.
#if defined(__cpp_char8_t) || defined(YYCC_CPPFEAT_GE_CPP20)
#define YYCC_CPPFEAT_UTF8
#endif
// Check whether there is support of `contains` for `set` and `map` including their varients.
#if defined(YYCC_CPPFEAT_GE_CPP20)
#define YYCC_CPPFEAT_CONTAINS
#endif
// Check whether there is support of `starts_with` and `ends_with` for `basic_string`.
#if defined(__cpp_lib_starts_ends_with) || defined(YYCC_CPPFEAT_GE_CPP20)
#define YYCC_CPPFEAT_STARTS_ENDS_WITH
#endif
// Check whether there is support of `std::expected`.
#if defined(__cpp_lib_expected) || defined(YYCC_CPPFEAT_GE_CPP23)
#define YYCC_CPPFEAT_EXPECTED
#endif
// Check whether there is support of `std::format`.
#if defined(YYCC_CPPFEAT_GE_CPP20)
#define YYCC_CPPFEAT_FORMAT
#endif
// Check whether there is support of `__VA_OPT__`
#if defined(YYCC_CPPFEAT_GE_CPP20)
#define YYCC_CPPFEAT_VA_OPT
#endif
// Check whether there is support of `std::stacktrace` and its formatter.
#if (defined(__cpp_lib_starts_ends_with) && defined(__cpp_lib_formatters)) || defined(YYCC_CPPFEAT_GE_CPP23)
#define YYCC_CPPFEAT_STACKTRACE
#endif

View File

@ -1,15 +1,14 @@
#pragma once
#include "../patch/expected.hpp"
#include "../string.hpp"
#include "../string/op.hpp"
#include "../string/reinterpret.hpp"
#include <string_view>
#include <type_traits>
#include <charconv>
#include <stdexcept>
#include <expected>
#define NS_YYCC_STRING ::yycc::string
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
#define NS_YYCC_STRING_OP ::yycc::string::op
#define NS_YYCC_PATCH_EXPECTED ::yycc::patch::expected
/**
* @brief Provides string parsing utilities for converting strings to numeric and boolean values.
@ -20,18 +19,16 @@
*/
namespace yycc::num::parse {
/// @private
/// @brief The error kind when parsing string into number.
enum class ParseError {
PartiallyParsed, ///< Only a part of given string was parsed. The whole string may be invalid.
InvalidString, ///< Given string is a invalid number string.
OutOfRange, ///< Given string is valid but its value out of the range of given number type.
OutOfRange, ///< Given string is valid but its value out of the range of given number type.
};
/// @private
/// @brief The return value of internal parse function which ape `std::expected`.
template<typename T>
using ParseResult = NS_YYCC_PATCH_EXPECTED::Expected<T, ParseError>;
using ParseResult = std::expected<T, ParseError>;
/**
* @private
@ -41,8 +38,9 @@ namespace yycc::num::parse {
* @param fmt The floating point format to use
* @return ParseResult<T> containing either the parsed value or a ParseError
*/
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl, std::chars_format fmt) {
template<typename T>
requires(std::is_floating_point_v<T>)
ParseResult<T> parse(const std::u8string_view& strl, std::chars_format fmt) {
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
T rv;
@ -54,13 +52,13 @@ namespace yycc::num::parse {
// Parse completely.
// But we need to check whether the whole string was parsed.
if (ptr == tail) return rv;
else return ParseError::PartiallyParsed;
else return std::unexpected(ParseError::PartiallyParsed);
} else if (ec == std::errc::invalid_argument) {
// Given string is invalid
return ParseError::InvalidString;
return std::unexpected(ParseError::InvalidString);
} else if (ec == std::errc::result_out_of_range) {
// Given string is out of range
return ParseError::OutOfRange;
return std::unexpected(ParseError::OutOfRange);
} else {
// Unreachable
throw std::runtime_error("invalid ec.");
@ -75,8 +73,9 @@ namespace yycc::num::parse {
* @param base Numeric base (2-36)
* @return ParseResult<T> containing either the parsed value or a ParseError
*/
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl, int base) {
template<typename T>
requires(std::is_integral_v<T> && !std::is_same_v<T, bool>)
ParseResult<T> parse(const std::u8string_view& strl, int base) {
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
T rv;
@ -88,13 +87,13 @@ namespace yycc::num::parse {
// Parse completely.
// But we need to check whether the whole string was parsed.
if (ptr == tail) return rv;
else return ParseError::PartiallyParsed;
else return std::unexpected(ParseError::PartiallyParsed);
} else if (ec == std::errc::invalid_argument) {
// Given string is invalid
return ParseError::InvalidString;
return std::unexpected(ParseError::InvalidString);
} else if (ec == std::errc::result_out_of_range) {
// Given string is out of range
return ParseError::OutOfRange;
return std::unexpected(ParseError::OutOfRange);
} else {
// Unreachable
throw std::runtime_error("invalid ec.");
@ -108,139 +107,18 @@ namespace yycc::num::parse {
* @param strl The UTF-8 string view to parse ("true" or "false", case insensitive)
* @return ParseResult<bool> containing either the parsed value or a ParseError
*/
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl) {
template<typename T>
requires(std::is_same_v<T, bool>)
ParseResult<T> parse(const std::u8string_view& strl) {
// Get lower case
auto lower_case = NS_YYCC_STRING_OP::to_lower(strl);
// Compare result
if (lower_case == YYCC_U8("true")) return true;
else if (lower_case == YYCC_U8("false")) return false;
if (lower_case == u8"true") return true;
else if (lower_case == u8"false") return false;
else return ParseError::InvalidString;
}
/**
* @brief Try parsing given string to floating point types.
* @tparam T The type derived from floating point type.
* @param[in] strl The string need to be parsed.
* @param[out] num
* The variable receiving result.
* There is no guarantee that the content is not modified when parsing failed.
* @param[in] fmt The floating point format used when try parsing.
* @return True if success, otherwise false.
*/
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
bool try_parse(const NS_YYCC_STRING::u8string_view& strl,
T& num,
std::chars_format fmt = std::chars_format::general) {
namespace expected = NS_YYCC_PATCH_EXPECTED;
auto rv = priv_parse<T>(strl, fmt);
if (expected::is_value(rv)) {
num = expected::get_value(rv);
return true;
} else {
return false;
}
}
/**
* @brief Try parsing given string to integral types.
* @tparam T The type derived from integral type except bool type.
* @param[in] strl The string need to be parsed.
* @param[out] num
* The variable receiving result.
* There is no guarantee that the content is not modified when parsing failed.
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
* @return True if success, otherwise false.
*/
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num, int base = 10) {
namespace expected = NS_YYCC_PATCH_EXPECTED;
auto rv = priv_parse<T>(strl, base);
if (expected::is_value(rv)) {
num = expected::get_value(rv);
return true;
} else {
return false;
}
}
/**
* @brief Try parsing given string to bool types.
* @tparam T The type derived from bool type.
* @param[in] strl The string need to be parsed ("true" or "false", case insensitive).
* @param[out] num
* The variable receiving result.
* There is no guarantee that the content is not modified when parsing failed.
* @return True if success, otherwise false.
*/
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num) {
namespace expected = NS_YYCC_PATCH_EXPECTED;
auto rv = priv_parse<T>(strl);
if (expected::is_value(rv)) {
num = expected::get_value(rv);
return true;
} else {
return false;
}
}
/**
* @brief Parse given string to floating point types.
* @tparam T The type derived from floating point type.
* @param[in] strl The string need to be parsed.
* @param[in] fmt The floating point format used when try parsing.
* @return
* The parsing result.
* There is no guarantee about the content of this return value when parsing failed.
* It may be any possible value but usually is its default value.
* @exception std::invalid_argument Can not parse given string.
*/
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
T parse(const NS_YYCC_STRING::u8string_view& strl,
std::chars_format fmt = std::chars_format::general) {
T rv;
if (try_parse(strl, rv, fmt)) return rv;
else throw std::invalid_argument("can not parse given string");
}
/**
* @brief Parse given string to integral type types.
* @tparam T The type derived from integral type except bool type.
* @param[in] strl The string need to be parsed.
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
* @return
* The parsing result.
* There is no guarantee about the content of this return value when parsing failed.
* It may be any possible value but usually is its default value.
* @exception std::invalid_argument Can not parse given string.
*/
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
T parse(const NS_YYCC_STRING::u8string_view& strl, int base = 10) {
T rv;
if (try_parse(strl, rv, base)) return rv;
else throw std::invalid_argument("can not parse given string");
}
/**
* @brief Parse given string to bool types.
* @tparam T The type derived from bool type.
* @param[in] strl The string need to be parsed ("true" or "false", case insensitive).
* @return
* The parsing result.
* There is no guarantee about the content of this return value when parsing failed.
* It may be any possible value but usually is its default value.
* @exception std::invalid_argument Can not parse given string.
*/
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
T parse(const NS_YYCC_STRING::u8string_view& strl) {
T rv;
if (try_parse(strl, rv)) return rv;
else throw std::invalid_argument("can not parse given string");
}
} // namespace yycc::num::parse
#undef NS_YYCC_PATCH_EXPECTED
#undef NS_YYCC_STRING_OP
#undef NS_YYCC_STRING_REINTERPRET
#undef NS_YYCC_STRING

View File

@ -1,12 +1,11 @@
#pragma once
#include "../string.hpp"
#include "../string/reinterpret.hpp"
#include <string>
#include <array>
#include <type_traits>
#include <charconv>
#include <stdexcept>
#include <type_traits>
#define NS_YYCC_STRING ::yycc::string
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
/**
@ -25,7 +24,7 @@ namespace yycc::num::stringify {
inline constexpr size_t STRINGIFY_BUFFER_SIZE = 64u;
/// @private
/// @brief Type alias for the buffer used in string conversion.
using StringifyBuffer = std::array<NS_YYCC_STRING::u8char, STRINGIFY_BUFFER_SIZE>;
using StringifyBuffer = std::array<char8_t, STRINGIFY_BUFFER_SIZE>;
/**
* @brief Return the string representation of given floating point value.
@ -35,10 +34,9 @@ namespace yycc::num::stringify {
* @param[in] precision The floating point precision used when getting string representation.
* @return The string representation of given value.
*/
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
NS_YYCC_STRING::u8string stringify(T num,
std::chars_format fmt = std::chars_format::general,
int precision = 6) {
template<typename T>
requires(std::is_floating_point_v<T>)
std::u8string stringify(T num, std::chars_format fmt = std::chars_format::general, int precision = 6) {
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
StringifyBuffer buffer;
@ -48,8 +46,7 @@ namespace yycc::num::stringify {
fmt,
precision);
if (ec == std::errc()) {
return NS_YYCC_STRING::u8string(buffer.data(),
reinterpret::as_utf8(ptr) - buffer.data());
return std::u8string(buffer.data(), reinterpret::as_utf8(ptr) - buffer.data());
} else if (ec == std::errc::value_too_large) {
// Too short buffer. This should not happen.
throw std::out_of_range("stringify() buffer is not sufficient.");
@ -65,18 +62,18 @@ namespace yycc::num::stringify {
* @param[in] base Integer base used when getting string representation: a value between 2 and 36 (inclusive).
* @return The string representation of given value.
*/
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
NS_YYCC_STRING::u8string stringify(T num, int base = 10) {
template<typename T>
requires(std::is_integral_v<T> && !std::is_same_v<T, bool>)
std::u8string stringify(T num, int base = 10) {
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
StringifyBuffer buffer;
auto [ptr, ec] = std::to_chars(reinterpret::as_ordinary(buffer.data()),
reinterpret::as_ordinary(buffer.data() + buffer.size()),
num,
base);
if (ec == std::errc()) {
return NS_YYCC_STRING::u8string(buffer.data(),
reinterpret::as_utf8(ptr) - buffer.data());
return std::u8string(buffer.data(), reinterpret::as_utf8(ptr) - buffer.data());
} else if (ec == std::errc::value_too_large) {
// Too short buffer. This should not happen.
throw std::out_of_range("stringify() buffer is not sufficient.");
@ -91,13 +88,13 @@ namespace yycc::num::stringify {
* @param[in] num The value need to get string representation.
* @return The string representation of given value ("true" or "false").
*/
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
NS_YYCC_STRING::u8string stringify(T num) {
if (num) return NS_YYCC_STRING::u8string(YYCC_U8("true"));
else return NS_YYCC_STRING::u8string(YYCC_U8("false"));
template<typename T>
requires(std::is_same_v<T, bool>)
std::u8string stringify(T num) {
if (num) return std::u8string(u8"true");
else return std::u8string(u8"false");
}
} // namespace yycc::string::stringify
} // namespace yycc::num::stringify
#undef NS_YYCC_STRING_REINTERPRET
#undef NS_YYCC_STRING

View File

@ -1,45 +0,0 @@
#pragma once
#include "../macro/feature_probe.hpp"
namespace yycc::patch::contains {
/**
* @brief Checks if there is an element with key equivalent to key in the container.
* @details
* The polyfill to \c contains() function of unordered and ordered associative container.
* Because this function only present after C++ 20.
* This function will use our custom polyfill if the version of C++ standard you are using lower than C++ 20.
* Otherwise it will fallback to vanilla standard library function.
* @tparam TContainer
* The type of container. This container must have \c find() and \c end() member functions.
* @tparam TKey
* The type of key of container.
* If the container is a set, this type is the type of item in set.
* If the container is a map, this type is the key type of map.
* @param[in] container The reference to container to find.
* @param[in] key Key value of the element to search for
* @return True if there is such an element, otherwise false.
* @remarks
* This template function do not have constraint check.
* If container type has \c find() and \c end() member functions, this template function will be created without any error.
* However, this function should be used for standard library associative container according to its original purpose.
* It means that the type of container usually and should be one of following types:
* \li \c std::set
* \li \c std::multiset
* \li \c std::map
* \li \c std::multimap
* \li \c std::unordered_set
* \li \c std::unordered_multiset
* \li \c std::unordered_map
* \li \c std::unordered_multimap
*/
template<class TContainer, class TKey>
bool contains(const TContainer& container, const TKey& key) {
#if defined(YYCC_CPPFEAT_CONTAINS)
return container.contains(key);
#else
return container.find(key) != container.end();
#endif
}
} // namespace yycc::patch::container

View File

@ -1,40 +0,0 @@
#pragma once
#include <type_traits>
#include <variant>
/**
* @brief A stupid polyfill for std::expected,
*
* For those C++ standard which do not support std::expected,
* we provide this namespace with a pretty bad but at least working std::expected pplyfill.
*
* The polyfill was done by std::variant.
*
* This namespace is used by this project because this framework must support C++ 17.
*/
namespace yycc::patch::expected {
template<typename TValue, typename TError, std::enable_if_t<!std::is_same_v<TValue, TError>, int> = 0>
using Expected = std::variant<TValue, TError>;
template<typename TValue, typename TError>
bool is_value(const Expected<TValue, TError>& exp) {
return exp.index() == 0u;
}
template<typename TValue, typename TError>
bool is_error(const Expected<TValue, TError>& exp) {
return exp.index() == 1u;
}
template<typename TValue, typename TError>
const TValue& get_value(const Expected<TValue, TError>& exp) {
return std::get<0>(exp);
}
template<typename TValue, typename TError>
const TError& get_error(const Expected<TValue, TError>& exp) {
return std::get<1>(exp);
}
}

View File

@ -1,48 +0,0 @@
#include "path.hpp"
#include "../macro/os_detector.hpp"
#include <stdexcept>
#define NS_YYCC_STRING ::yycc::string
namespace yycc::patch::path {
// TODO: Fix this after finish encoding parts.
// TODO: Add native implementation if C++ support it.
// So we need add feature test macro at the same time.
std::filesystem::path to_std_path(const NS_YYCC_STRING::u8string_view& u8_path) {
// #if defined(YYCC_OS_WINDOWS)
// // convert path to wchar
// std::wstring wpath;
// if (!YYCC::EncodingHelper::UTF8ToWchar(u8_path, wpath))
// throw std::invalid_argument("Fail to convert given UTF8 string.");
// // return path with wchar_t ctor
// return std::filesystem::path(wpath);
// #else
// std::string cache = YYCC::EncodingHelper::ToOrdinary(u8_path);
// return std::filesystem::path(cache.c_str());
// #endif
return std::filesystem::path();
}
NS_YYCC_STRING::u8string to_u8string(const std::filesystem::path& path) {
// #if defined(YYCC_OS_WINDOWS)
// // get and convert to utf8
// NS_YYCC_STRING::u8string u8_path;
// if (!YYCC::EncodingHelper::WcharToUTF8(path.c_str(), u8_path))
// throw std::invalid_argument("Fail to convert to UTF8 string.");
// // return utf8 path
// return u8_path;
// #else
// return EncodingHelper::ToUTF8(path.string());
// #endif
return NS_YYCC_STRING::u8string();
}
}

View File

@ -1,32 +0,0 @@
#pragma once
#include "../string.hpp"
#include <filesystem>
#define NS_YYCC_STRING ::yycc::string
/**
* @brief \c Standard library related patches for UTF8 compatibility and the limitation of C++ standard version.
* @details
* See also \ref std_patch.
*/
namespace yycc::patch::path {
/**
* @brief Constructs \c std::filesystem::path from UTF8 path.
* @param[in] u8_path UTF8 path string for building.
* @return \c std::filesystem::path instance.
* @exception std::invalid_argument Fail to parse given UTF8 string (maybe invalid?).
*/
std::filesystem::path to_std_path(const NS_YYCC_STRING::u8string_view& u8_path);
/**
* @brief Returns the UTF8 representation of given \c std::filesystem::path.
* @param[in] path The \c std::filesystem::path instance converting to UTF8 path.
* @return The UTF8 representation of given \c std::filesystem::path.
* @exception std::invalid_argument Fail to convert to UTF8 string.
*/
NS_YYCC_STRING::u8string to_u8string(const std::filesystem::path& path);
} // namespace yycc::patch::path
#undef NS_YYCC_STRING

View File

@ -1,203 +0,0 @@
#pragma once
#include "../macro/feature_probe.hpp"
#include <string>
#include <string_view>
namespace yycc::patch::starts_ends_with {
// Reference:
// https://en.cppreference.com/w/cpp/string/basic_string_view/starts_with
// https://en.cppreference.com/w/cpp/string/basic_string_view/ends_with
// https://en.cppreference.com/w/cpp/string/basic_string/starts_with
// https://en.cppreference.com/w/cpp/string/basic_string/ends_with
#pragma region For String View
/**
* @brief Checks if the string view begins with the given prefix
* @param[in] that The string view to find.
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
* @return True if the string view begins with the provided prefix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool starts_with(const std::basic_string_view<CharT, Traits>& that,
std::basic_string_view<CharT, Traits> sv) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.starts_with(sv);
#else
return std::basic_string_view<CharT, Traits>(that.data(), std::min(that.size(), sv.size()))
== sv;
#endif
}
/**
* @brief Checks if the string view begins with the given prefix
* @param[in] that The string view to find.
* @param[in] ch A single character.
* @return True if the string view begins with the provided prefix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool starts_with(const std::basic_string_view<CharT, Traits>& that, CharT ch) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.starts_with(ch);
#else
return !that.empty() && Traits::eq(that.front(), ch);
#endif
}
/**
* @brief Checks if the string view begins with the given prefix
* @param[in] that The string view to find.
* @param[in] s A null-terminated character string.
* @return True if the string view begins with the provided prefix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool starts_with(const std::basic_string_view<CharT, Traits>& that, const CharT* s) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.starts_with(s);
#else
return starts_with(that, std::basic_string_view(s));
#endif
}
/**
* @brief Checks if the string view ends with the given suffix
* @param[in] that The string view to find.
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
* @return True if the string view ends with the provided suffix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool ends_with(const std::basic_string_view<CharT, Traits>& that,
std::basic_string_view<CharT, Traits> sv) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.ends_with(sv);
#else
return that.size() >= sv.size()
&& that.compare(that.size() - sv.size(),
std::basic_string_view<CharT, Traits>::npos,
sv)
== 0;
#endif
}
/**
* @brief Checks if the string view ends with the given suffix
* @param[in] that The string view to find.
* @param[in] ch A single character.
* @return True if the string view ends with the provided suffix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool ends_with(const std::basic_string_view<CharT, Traits>& that, CharT ch) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.ends_with(ch);
#else
return !that.empty() && Traits::eq(that.back(), ch);
#endif
}
/**
* @brief Checks if the string view ends with the given suffix
* @param[in] that The string view to find.
* @param[in] s A null-terminated character string.
* @return True if the string view ends with the provided suffix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool ends_with(const std::basic_string_view<CharT, Traits>& that, const CharT* s) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.ends_with(s);
#else
return ends_with(that, std::basic_string_view(s));
#endif
}
#pragma endregion
#pragma region For String
/**
* @brief Checks if the string begins with the given prefix
* @param[in] that The string to find.
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
* @return True if the string view begins with the provided prefix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool starts_with(const std::basic_string<CharT, Traits>& that,
std::basic_string_view<CharT, Traits> sv) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.starts_with(sv);
#else
return starts_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), sv);
#endif
}
/**
* @brief Checks if the string begins with the given prefix
* @param[in] that The string to find.
* @param[in] ch A single character.
* @return True if the string view begins with the provided prefix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool starts_with(const std::basic_string<CharT, Traits>& that, CharT ch) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.starts_with(ch);
#else
return starts_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), ch);
#endif
}
/**
* @brief Checks if the string begins with the given prefix
* @param[in] that The string to find.
* @param[in] s A null-terminated character string.
* @return True if the string view begins with the provided prefix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool starts_with(const std::basic_string<CharT, Traits>& that, const CharT* s) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.starts_with(s);
#else
return starts_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), s);
#endif
}
/**
* @brief Checks if the string ends with the given suffix
* @param[in] that The string to find.
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
* @return True if the string view ends with the provided suffix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool ends_with(const std::basic_string<CharT, Traits>& that,
std::basic_string_view<CharT, Traits> sv) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.ends_with(sv);
#else
return ends_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), sv);
#endif
}
/**
* @brief Checks if the string ends with the given suffix
* @param[in] that The string to find.
* @param[in] ch A single character.
* @return True if the string view ends with the provided suffix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool ends_with(const std::basic_string<CharT, Traits>& that, CharT ch) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.ends_with(ch);
#else
return ends_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), ch);
#endif
}
/**
* @brief Checks if the string ends with the given suffix
* @param[in] that The string to find.
* @param[in] s A null-terminated character string.
* @return True if the string view ends with the provided suffix, false otherwise.
*/
template<class CharT, class Traits = std::char_traits<CharT>>
bool ends_with(const std::basic_string<CharT, Traits>& that, const CharT* s) noexcept {
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
return that.ends_with(s);
#else
return ends_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), s);
#endif
}
#pragma endregion
} // namespace yycc::patch::starts_ends_with

View File

@ -1,9 +1,9 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include "../string.hpp"
#include <string_view>
namespace yycc::rust::primitive {
namespace yycc::primitive {
// `bool` is keyword so should not declare it anymore.
// `char` is keyword so should not declare it anymore.
@ -23,6 +23,6 @@ namespace yycc::rust::primitive {
using f32 = float;
using f64 = double;
using str = ::yycc::string::u8string_view;
using str = std::u8string_view;
}

View File

@ -1,97 +0,0 @@
#pragma once
#include "../../macro/feature_probe.hpp"
#include "../../num/parse.hpp"
#include "../result.hpp"
#define NS_YYCC_STRING ::yycc::string
#define NS_YYCC_NUM_PARSE ::yycc::num::parse
#define NS_YYCC_RUST_RESULT ::yycc::rust::result
#define NS_YYCC_PATCH_EXPECTED ::yycc::patch::expected
/**
* @namespace yycc::rust::parse
* @brief Provides Rust-inspired parsing utilities for converting strings to various types.
* @details
* This namespace contains template functions for parsing strings into different types
* (floating-point, integral, boolean) with Rust-like Result error handling.
*/
namespace yycc::rust::num::parse {
#if defined(YYCC_CPPFEAT_EXPECTED)
/// @brief The error type of parsing.
using Error = NS_YYCC_NUM_PARSE::ParseError;
/// @brief The result type of parsing.
/// @tparam T The expected value type in result.
template<typename T>
using Result = NS_YYCC_RUST_RESULT::Result<T, Error>;
/**
* @brief Parses a string into a floating-point value.
* @tparam T Floating-point type to parse into (float, double, etc.)
* @param strl String view to parse
* @param fmt Formatting flags for parsing (default: general)
* @return Result<T> containing either the parsed value or an error
*/
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
Result<T> parse(const NS_YYCC_STRING::u8string_view& strl,
std::chars_format fmt = std::chars_format::general) {
namespace expected = NS_YYCC_PATCH_EXPECTED;
namespace result = NS_YYCC_RUST_RESULT;
auto rv = NS_YYCC_NUM_PARSE::priv_parse<T>(strl, fmt);
if (expected::is_value(rv)) {
return result::Ok<Result<T>>(expected::get_value(rv));
} else {
return result::Err<Result<T>>(expected::get_error(rv));
}
}
/**
* @brief Parses a string into an integral value (excluding bool).
* @tparam T Integral type to parse into (int, long, etc.)
* @param strl String view to parse
* @param base Numeric base for parsing (default: 10)
* @return Result<T> containing either the parsed value or an error
*/
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
Result<T> parse(const NS_YYCC_STRING::u8string_view& strl, int base = 10) {
namespace expected = NS_YYCC_PATCH_EXPECTED;
namespace result = NS_YYCC_RUST_RESULT;
auto rv = NS_YYCC_NUM_PARSE::priv_parse<T>(strl, base);
if (expected::is_value(rv)) {
return result::Ok<Result<T>>(expected::get_value(rv));
} else {
return result::Err<Result<T>>(expected::get_error(rv));
}
}
/**
* @brief Parses a string into a boolean value.
* @tparam T Must be bool type
* @param strl String view to parse
* @return Result<bool> containing either the parsed value or an error
*/
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
Result<T> parse(const NS_YYCC_STRING::u8string_view& strl) {
namespace expected = NS_YYCC_PATCH_EXPECTED;
namespace result = NS_YYCC_RUST_RESULT;
auto rv = NS_YYCC_NUM_PARSE::priv_parse<T>(strl);
if (expected::is_value(rv)) {
return result::Ok<Result<T>>(expected::get_value(rv));
} else {
return result::Err<Result<T>>(expected::get_error(rv));
}
}
#endif
}
#undef NS_YYCC_PATCH_EXPECTED
#undef NS_YYCC_RUST_RESULT
#undef NS_YYCC_NUM_PARSE
#undef NS_YYCC_STRING

View File

@ -1,10 +0,0 @@
#pragma once
#include "../../num/stringify.hpp"
namespace yycc::rust::num::stringify {
// There is no modification for legacy "stringify" functions like "parse".
// So we simply expose all functions into this namespace.
using namespace ::yycc::num::stringify;
} // namespace yycc::rust::stringify

View File

@ -1,47 +0,0 @@
#pragma once
// Define the UTF8 char type we used.
// And do a polyfill if no embedded char8_t type.
#include "macro/feature_probe.hpp"
#include <string>
#include <string_view>
namespace yycc::string {
/**
\typedef u8char_t
\brief YYCC UTF8 char type.
\details
This char type is an alias to \c char8_t if your current C++ standard support it.
Otherwise it is defined as <TT>unsigned char</TT> as C++ 20 stdandard does.
*/
/**
\typedef u8string
\brief YYCC UTF8 string container type.
\details
This type is defined as \c std::basic_string<yycc_char8_t>.
It is equal to \c std::u8string if your current C++ standard support it.
*/
/**
\typedef u8string_view
\brief YYCC UTF8 string view type.
\details
This type is defined as \c std::basic_string_view<yycc_char8_t>.
It is equal to \c std::u8string_view if your current C++ standard support it.
*/
#if defined(YYCC_CPPFEAT_UTF8)
using u8char = char8_t;
using u8string = std::u8string;
using u8string_view = std::u8string_view;
#else
using u8char = unsigned char;
using u8string = std::basic_string<u8char>;
using u8string_view = std::basic_string_view<u8char>;
#endif
#define _YYCC_U8(strl) u8 ## strl ///< The assistant macro for YYCC_U8.
#define YYCC_U8(strl) (reinterpret_cast<const ::yycc::string::u8char*>(_YYCC_U8(strl))) ///< The macro for creating UTF8 string literal. See \ref library_encoding.
#define YYCC_U8_CHAR(chr) (static_cast<::yycc::string::u8char>(chr)) ///< The macro for casting ordinary char type into YYCC UTF8 char type.
} // namespace yycc::string

View File

@ -1,196 +1,180 @@
#include "op.hpp"
#include <algorithm>
#include "reinterpret.hpp"
#define NS_YYCC_STRING ::yycc::string
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
namespace yycc::string::op {
#pragma region Printf VPrintf
bool printf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, ...) {
va_list argptr;
va_start(argptr, format);
bool ret = vprintf(strl, format, argptr);
va_end(argptr);
return ret;
}
template<typename TChar>
requires(sizeof(TChar) == sizeof(char))
static FormatResult<std::basic_string<TChar>> generic_printf(const TChar* format, va_list argptr) {
// Prepare result
std::basic_string<TChar> rv;
bool vprintf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, va_list argptr) {
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
// Check format
if (format == nullptr) return std::unexpected(FormatError::NullFormat);
va_list args1;
va_copy(args1, argptr);
va_list args2;
va_copy(args2, argptr);
// Prepare variable arguments
va_list args1;
va_copy(args1, argptr);
va_list args2;
va_copy(args2, argptr);
// the return value is desired char count without NULL terminal.
// minus number means error
int count = std::vsnprintf(
nullptr,
0,
reinterpret::as_ordinary(format),
args1
);
if (count < 0) {
// invalid length returned by vsnprintf.
return false;
}
va_end(args1);
// The return value is desired char count without NULL terminal.
// Minus 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::NoExpSize);
}
va_end(args1);
// resize std::string to desired count.
// and pass its length + 1 to std::vsnprintf,
// because std::vsnprintf only can write "buf_size - 1" chars with a trailing NULL.
// however std::vsnprintf already have a trailing NULL, so we plus 1 for it.
strl.resize(count);
int write_result = std::vsnprintf(
reinterpret::as_ordinary(strl.data()),
strl.size() + 1,
reinterpret::as_ordinary(format),
args2
);
va_end(args2);
// Resize std::string to desired count, and pass its length + 1 to std::vsnprintf,
// Because std::vsnprintf only can write "buf_size - 1" chars with a trailing NULL.
// However std::vsnprintf already have a trailing NULL, so we plus 1 for it.
rv.resize(count);
int write_result = std::vsnprintf(reinterpret_cast<char*>(rv.data()), rv.size() + 1, reinterpret_cast<const char*>(format), args2);
va_end(args2);
// Check written size.
if (write_result < 0 || write_result > count) {
// Invalid write result in vsnprintf.
return std::unexpected(FormatError::BadWrittenSize);
}
if (write_result < 0 || write_result > count) {
// invalid write result in vsnprintf.
return false;
}
// Return value
return rv;
}
return true;
}
FormatResult<std::u8string> printf(const char8_t* format, ...) {
va_list argptr;
va_start(argptr, format);
auto rv = vprintf(format, argptr);
va_end(argptr);
return rv;
}
NS_YYCC_STRING::u8string printf(const NS_YYCC_STRING::u8char* format, ...) {
NS_YYCC_STRING::u8string ret;
FormatResult<std::u8string> vprintf(const char8_t* format, va_list argptr) {
return generic_printf(format, argptr);
}
va_list argptr;
va_start(argptr, format);
vprintf(ret, format, argptr);
va_end(argptr);
FormatResult<std::string> printf(const char* format, ...) {
va_list argptr;
va_start(argptr, format);
auto rv = vprintf(format, argptr);
va_end(argptr);
return rv;
}
return ret;
}
NS_YYCC_STRING::u8string vprintf(const NS_YYCC_STRING::u8char* format, va_list argptr) {
NS_YYCC_STRING::u8string ret;
va_list argcpy;
va_copy(argcpy, argptr);
vprintf(ret, format, argcpy);
va_end(argcpy);
return ret;
}
FormatResult<std::string> vprintf(const char* format, va_list argptr) {
return generic_printf(format, argptr);
}
#pragma endregion
#pragma region Replace
void replace(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl) {
// Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
void replace(std::u8string& strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl) {
// Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
// check requirements
// from string should not be empty
NS_YYCC_STRING::u8string from_strl(_from_strl);
NS_YYCC_STRING::u8string to_strl(_to_strl);
if (from_strl.empty()) return;
// check requirements
// from string should not be empty
std::u8string from_strl(_from_strl);
std::u8string to_strl(_to_strl);
if (from_strl.empty()) return;
// start replace one by one
size_t start_pos = 0;
while ((start_pos = strl.find(from_strl, start_pos)) != NS_YYCC_STRING::u8string::npos) {
strl.replace(start_pos, from_strl.size(), to_strl);
start_pos += to_strl.size(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}
// start replace one by one
size_t start_pos = 0;
while ((start_pos = strl.find(from_strl, start_pos)) != std::u8string::npos) {
strl.replace(start_pos, from_strl.size(), to_strl);
start_pos += to_strl.size(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}
NS_YYCC_STRING::u8string replace(const NS_YYCC_STRING::u8string_view& _strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl) {
// prepare result
NS_YYCC_STRING::u8string strl(_strl);
replace(strl, _from_strl, _to_strl);
// return value
return strl;
}
std::u8string replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl) {
// prepare result
std::u8string strl(_strl);
replace(strl, _from_strl, _to_strl);
// return value
return strl;
}
#pragma endregion
#pragma region Join
NS_YYCC_STRING::u8string join(JoinDataProvider fct_data, const NS_YYCC_STRING::u8string_view& delimiter) {
NS_YYCC_STRING::u8string ret;
bool is_first = true;
NS_YYCC_STRING::u8string_view element;
std::u8string join(JoinDataProvider fct_data, const std::u8string_view& delimiter) {
std::u8string ret;
bool is_first = true;
std::u8string_view element;
// fetch element
while (fct_data(element)) {
// insert delimiter
if (is_first) is_first = false;
else {
// append delimiter.
ret.append(delimiter);
}
// fetch element
while (fct_data(element)) {
// insert delimiter
if (is_first) is_first = false;
else {
// append delimiter.
ret.append(delimiter);
}
// insert element if it is not empty
if (!element.empty())
ret.append(element);
}
// insert element if it is not empty
if (!element.empty()) ret.append(element);
}
return ret;
}
return ret;
}
#pragma endregion
#pragma region Upper Lower
template<bool BIsToLower>
static void generic_lower_upper(NS_YYCC_STRING::u8string& strl) {
// References:
// https://en.cppreference.com/w/cpp/algorithm/transform
// https://en.cppreference.com/w/cpp/string/byte/tolower
std::transform(
strl.cbegin(), strl.cend(), strl.begin(),
[](unsigned char c) -> char {
if constexpr (BIsToLower) return std::tolower(c);
else return std::toupper(c);
}
);
}
template<bool BIsToLower>
static void generic_lower_upper(std::u8string& strl) {
// References:
// https://en.cppreference.com/w/cpp/algorithm/transform
// https://en.cppreference.com/w/cpp/string/byte/tolower
std::transform(strl.cbegin(), strl.cend(), strl.begin(), [](unsigned char c) -> char {
if constexpr (BIsToLower) return std::tolower(c);
else return std::toupper(c);
});
}
void lower(NS_YYCC_STRING::u8string& strl) {
generic_lower_upper<true>(strl);
}
void lower(std::u8string& strl) {
generic_lower_upper<true>(strl);
}
NS_YYCC_STRING::u8string to_lower(const NS_YYCC_STRING::u8string_view& strl) {
NS_YYCC_STRING::u8string ret(strl);
lower(ret);
return ret;
}
std::u8string to_lower(const std::u8string_view& strl) {
std::u8string ret(strl);
lower(ret);
return ret;
}
void upper(NS_YYCC_STRING::u8string& strl) {
generic_lower_upper<false>(strl);
}
void upper(std::u8string& strl) {
generic_lower_upper<false>(strl);
}
NS_YYCC_STRING::u8string to_upper(const NS_YYCC_STRING::u8string_view& strl) {
// same as Lower, just replace char transform function.
NS_YYCC_STRING::u8string ret(strl);
upper(ret);
return ret;
}
std::u8string to_upper(const std::u8string_view& strl) {
// same as Lower, just replace char transform function.
std::u8string ret(strl);
upper(ret);
return ret;
}
#pragma endregion
#pragma region Split
std::vector<NS_YYCC_STRING::u8string_view> split(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _delimiter) {
std::vector<std::u8string_view> split(const std::u8string_view& strl, const std::u8string_view& _delimiter) {
// Reference:
// https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
// prepare return value
std::vector<NS_YYCC_STRING::u8string_view> elems;
std::vector<std::u8string_view> elems;
// if string need to be splitted is empty, return original string (empty string).
// if delimiter is empty, return original string.
NS_YYCC_STRING::u8string delimiter(_delimiter);
std::u8string delimiter(_delimiter);
if (strl.empty() || delimiter.empty()) {
elems.emplace_back(strl);
return elems;
@ -198,7 +182,7 @@ namespace yycc::string::op {
// start spliting
std::size_t previous = 0, current;
while ((current = strl.find(delimiter.c_str(), previous)) != NS_YYCC_STRING::u8string::npos) {
while ((current = strl.find(delimiter.c_str(), previous)) != std::u8string::npos) {
elems.emplace_back(strl.substr(previous, current - previous));
previous = current + delimiter.size();
}
@ -209,20 +193,20 @@ namespace yycc::string::op {
return elems;
}
std::vector<NS_YYCC_STRING::u8string> split_owned(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _delimiter) {
// call split view
std::vector<std::u8string> split_owned(const std::u8string_view& strl, const std::u8string_view& _delimiter) {
// call split view
auto view_result = split(strl, _delimiter);
// copy string view result to string
std::vector<NS_YYCC_STRING::u8string> elems;
elems.reserve(view_result.size());
for (const auto& strl_view : view_result) {
elems.emplace_back(NS_YYCC_STRING::u8string(strl_view));
}
// return copied result
return elems;
}
// copy string view result to string
std::vector<std::u8string> elems;
elems.reserve(view_result.size());
for (const auto& strl_view : view_result) {
elems.emplace_back(std::u8string(strl_view));
}
// return copied result
return elems;
}
#pragma endregion
}
} // namespace yycc::string::op

View File

@ -1,65 +1,68 @@
#pragma once
#include <string>
#include <string_view>
#include <cstdarg>
#include <functional>
#include <vector>
#include "../string.hpp"
#define NS_YYCC_STRING ::yycc::string
#include <expected>
namespace yycc::string::op {
/**
* @brief Perform a string formatting operation.
* @param[out] strl
* The string container receiving the result.
* There is no guarantee that the content is not modified when function failed.
* @param[in] format The format string.
* @param[in] ... Argument list of format string.
* @return True if success, otherwise false.
*/
bool printf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, ...);
/**
* @brief Perform a string formatting operation.
* @param[out] strl
* The string container receiving the result.
* There is no guarantee that the content is not modified when function failed.
* @param[in] format The format string.
* @param[in] argptr Argument list of format string.
* @return True if success, otherwise false.
*/
bool vprintf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, va_list argptr);
/**
* @brief Perform a string formatting operation.
* @param[in] format The format string.
* @param[in] ... Argument list of format string.
* @return The formatting result. Empty string if error happened.
*/
NS_YYCC_STRING::u8string printf(const NS_YYCC_STRING::u8char* format, ...);
/**
* @brief Perform a string formatting operation.
* @param[in] format The format string.
* @param[in] argptr Argument list of format string.
* @return The formatting result. Empty string if error happened.
*/
NS_YYCC_STRING::u8string vprintf(const NS_YYCC_STRING::u8char* format, va_list argptr);
enum class FormatError {
NullFormat, ///< Given format string is nullptr.
NoExpSize, ///< 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.
*/
FormatResult<std::u8string> printf(const char8_t* format, ...);
/**
* @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.
*/
FormatResult<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.
*/
FormatResult<std::string> printf(const char* format, ...);
/**
* @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.
*/
FormatResult<std::string> vprintf(const char* format, va_list argptr);
/**
* @brief Modify given string with all occurrences of substring \e old replaced by \e new.
* @param[in,out] strl The string for replacing
* @param[in] _from_strl The \e old string.
* @param[in] _to_strl The \e new string.
*/
void replace(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl);
/**
void replace(std::u8string& strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl);
/**
* @brief Return a copy with all occurrences of substring \e old replaced by \e new.
* @param[in] _strl The string for replacing
* @param[in] _from_strl The \e old string.
* @param[in] _to_strl The \e new string.
* @return The result of replacement.
*/
NS_YYCC_STRING::u8string replace(const NS_YYCC_STRING::u8string_view& _strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl);
std::u8string replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl);
/**
/**
* @brief The data provider of general join function.
* @details
* For programmer using lambda to implement this function pointer:
@ -68,8 +71,8 @@ namespace yycc::string::op {
* \li Function return true to continue joining. otherwise return false to stop joining.
* The argument content assigned in the calling returning false is not included in join process.
*/
using JoinDataProvider = std::function<bool(NS_YYCC_STRING::u8string_view&)>;
/**
using JoinDataProvider = std::function<bool(std::u8string_view&)>;
/**
* @brief Universal join function.
* @details
* This function use function pointer as a general data provider interface,
@ -80,51 +83,56 @@ namespace yycc::string::op {
* @param[in] delimiter The delimiter used for joining.
* @return The result string of joining.
*/
NS_YYCC_STRING::u8string join(JoinDataProvider fct_data, const NS_YYCC_STRING::u8string_view& delimiter);
/**
std::u8string join(JoinDataProvider fct_data, const std::u8string_view& delimiter);
/**
* @brief Specialized join function for standard library container.
* @tparam InputIt
* Must meet the requirements of LegacyInputIterator.
* It also can be dereferenced and then implicitly converted to NS_YYCC_STRING::u8string_view.
* It also can be dereferenced and then implicitly converted to std::u8string_view.
* @param[in] first The beginning of the range of elements to join.
* @param[in] last The terminal of the range of elements to join (exclusive).
* @param[in] delimiter The delimiter used for joining.
* @return The result string of joining.
*/
template<class InputIt>
NS_YYCC_STRING::u8string join(InputIt first, InputIt last, const NS_YYCC_STRING::u8string_view& delimiter) {
return join([&first, &last](NS_YYCC_STRING::u8string_view& view) -> bool {
// if we reach tail, return false to stop join process
if (first == last) return false;
// otherwise fetch data, inc iterator and return.
view = *first;
++first;
return true;
}, delimiter);
}
template<class InputIt>
std::u8string join(InputIt first, InputIt last, const std::u8string_view& delimiter) {
return join(
[&first, &last](std::u8string_view& view) -> bool {
// if we reach tail, return false to stop join process
if (first == last) return false;
// otherwise fetch data, inc iterator and return.
view = *first;
++first;
return true;
},
delimiter);
}
/**
/**
* @brief Convert given string to lowercase.
* @param[in,out] strl The string to be lowercase.
*/
void lower(NS_YYCC_STRING::u8string& strl);
/**
void lower(std::u8string& strl);
/**
* @brief Return a copy of the string converted to lowercase.
* @param[in] strl The string to be lowercase.
* @return The copy of the string converted to lowercase.
*/
NS_YYCC_STRING::u8string to_lower(const NS_YYCC_STRING::u8string_view& strl);
/**
std::u8string to_lower(const std::u8string_view& strl);
/**
* @brief Convert given string to uppercase.
* @param[in,out] strl The string to be uppercase.
*/
void upper(NS_YYCC_STRING::u8string& strl);
/**
void upper(std::u8string& strl);
/**
* @brief Return a copy of the string converted to uppercase.
* @param[in] strl The string to be uppercase.
* @return The copy of the string converted to uppercase.
*/
NS_YYCC_STRING::u8string to_upper(const NS_YYCC_STRING::u8string_view& strl);
std::u8string to_upper(const std::u8string_view& strl);
// TODO:
// Add strip, lstrip and rstrip functions.
/**
* @brief Split given string with specified delimiter as string view.
@ -136,10 +144,10 @@ namespace yycc::string::op {
* \par
* If given string or delimiter are empty,
* the result container will only contain 1 entry which is equal to given string.
* @see Split(const NS_YYCC_STRING::u8string_view&, const NS_YYCC_STRING::u8char*)
* @see Split(const std::u8string_view&, const char8_t*)
*/
std::vector<NS_YYCC_STRING::u8string_view> split(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _delimiter);
/**
std::vector<std::u8string_view> split(const std::u8string_view& strl, const std::u8string_view& _delimiter);
/**
* @brief Split given string with specified delimiter.
* @param[in] strl The string need to be splitting.
* @param[in] _delimiter The delimiter for splitting.
@ -149,9 +157,10 @@ namespace yycc::string::op {
* If given string or delimiter are empty,
* the result container will only contain 1 entry which is equal to given string.
*/
std::vector<NS_YYCC_STRING::u8string> split_owned(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _delimiter);
// undefined lazy_split(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _delimiter);
std::vector<std::u8string> split_owned(const std::u8string_view& strl, const std::u8string_view& _delimiter);
}
// TODO:
// Add lazy_split(const std::u8string_view& strl, const std::u8string_view& _delimiter);
// Once we add it, we need redirect all split function into it.
#undef NS_YYCC_STRING
} // namespace yycc::string::op

View File

@ -1,33 +1,31 @@
#include "reinterpret.hpp"
#define NS_YYCC_STRING ::yycc::string
namespace yycc::string::reinterpret {
const NS_YYCC_STRING::u8char* as_utf8(const char* src) {
return reinterpret_cast<const NS_YYCC_STRING::u8char*>(src);
}
NS_YYCC_STRING::u8char* as_utf8(char* src) {
return reinterpret_cast<NS_YYCC_STRING::u8char*>(src);
}
NS_YYCC_STRING::u8string as_utf8(const std::string_view& src) {
return NS_YYCC_STRING::u8string(reinterpret_cast<const NS_YYCC_STRING::u8char*>(src.data()), src.size());
}
NS_YYCC_STRING::u8string_view as_utf8_view(const std::string_view& src) {
return NS_YYCC_STRING::u8string_view(reinterpret_cast<const NS_YYCC_STRING::u8char*>(src.data()), src.size());
}
const char8_t* as_utf8(const char* src) {
return reinterpret_cast<const char8_t*>(src);
}
char8_t* as_utf8(char* src) {
return reinterpret_cast<char8_t*>(src);
}
std::u8string as_utf8(const std::string_view& src) {
return std::u8string(reinterpret_cast<const char8_t*>(src.data()), src.size());
}
std::u8string_view as_utf8_view(const std::string_view& src) {
return std::u8string_view(reinterpret_cast<const char8_t*>(src.data()), src.size());
}
const char* as_ordinary(const NS_YYCC_STRING::u8char* src) {
return reinterpret_cast<const char*>(src);
}
char* as_ordinary(NS_YYCC_STRING::u8char* src) {
return reinterpret_cast<char*>(src);
}
std::string as_ordinary(const NS_YYCC_STRING::u8string_view& src) {
return std::string(reinterpret_cast<const char*>(src.data()), src.size());
}
std::string_view as_ordinary_view(const NS_YYCC_STRING::u8string_view& src) {
return std::string_view(reinterpret_cast<const char*>(src.data()), src.size());
}
const char* as_ordinary(const char8_t* src) {
return reinterpret_cast<const char*>(src);
}
char* as_ordinary(char8_t* src) {
return reinterpret_cast<char*>(src);
}
std::string as_ordinary(const std::u8string_view& src) {
return std::string(reinterpret_cast<const char*>(src.data()), src.size());
}
std::string_view as_ordinary_view(const std::u8string_view& src) {
return std::string_view(reinterpret_cast<const char*>(src.data()), src.size());
}
}
} // namespace yycc::string::reinterpret

View File

@ -1,7 +1,6 @@
#pragma once
#include "../string.hpp"
#define NS_YYCC_STRING ::yycc::string
#include <string>
#include <string_view>
/**
* @brief Provides utilities for reinterpretation between UTF-8 and ordinary string types.
@ -17,51 +16,49 @@ namespace yycc::string::reinterpret {
* @param src Source ordinary string
* @return Pointer to UTF-8 encoded string
*/
const NS_YYCC_STRING::u8char* as_utf8(const char* src);
const char8_t* as_utf8(const char* src);
/**
* @brief Reinterpret ordinary C-string as an UTF-8 string (non-const version).
* @param src Source ordinary string
* @return Pointer to UTF-8 encoded string
*/
NS_YYCC_STRING::u8char* as_utf8(char* src);
char8_t* as_utf8(char* src);
/**
* @brief Reinterpret ordinary string view to copied UTF-8 string.
* @param src Source ordinary string view
* @return UTF-8 encoded string
*/
NS_YYCC_STRING::u8string as_utf8(const std::string_view& src);
std::u8string as_utf8(const std::string_view& src);
/**
* @brief Reinterpret ordinary string view to UTF-8 string view.
* @param src Source ordinary string view
* @return UTF-8 encoded string view
*/
NS_YYCC_STRING::u8string_view as_utf8_view(const std::string_view& src);
std::u8string_view as_utf8_view(const std::string_view& src);
/**
* @brief Reinterpret UTF-8 C-string to ordinary string (const version).
* @param src Source UTF-8 string
* @return Pointer to ordinary string
*/
const char* as_ordinary(const NS_YYCC_STRING::u8char* src);
const char* as_ordinary(const char8_t* src);
/**
* @brief Reinterpret UTF-8 C-string to ordinary string (non-const version).
* @param src Source UTF-8 string
* @return Pointer to ordinary string
*/
char* as_ordinary(NS_YYCC_STRING::u8char* src);
char* as_ordinary(char8_t* src);
/**
* @brief Reinterpret UTF-8 string view to ordinary string.
* @param src Source UTF-8 string view
* @return Ordinary string
*/
std::string as_ordinary(const NS_YYCC_STRING::u8string_view& src);
std::string as_ordinary(const std::u8string_view& src);
/**
* @brief Reinterpret UTF-8 string view to ordinary string view
* @param src Source UTF-8 string view
* @return Ordinary string view
*/
std::string_view as_ordinary_view(const NS_YYCC_STRING::u8string_view& src);
std::string_view as_ordinary_view(const std::u8string_view& src);
}
#undef NS_YYCC_STRING

View File

@ -1,16 +0,0 @@
#pragma once
#include "../macro/os_detector.hpp"
// If we are in Windows,
// we need add 2 macros to disable Windows shitty warnings and errors of
// depracted functions and not secure functions.
#if defined(YYCC_OS_WINDOWS)
#if !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#if !defined(_CRT_NONSTDC_NO_DEPRECATE)
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#endif

View File

@ -34,20 +34,6 @@ PRIVATE
YYCCommonplace
GTest::gtest_main
)
# Setup C++ standard
target_compile_features(YYCCTestbench PUBLIC cxx_std_17)
set_target_properties(YYCCTestbench PROPERTIES CXX_EXTENSION OFF)
# Order Unicode charset for private using
target_compile_definitions(YYCCTestbench
PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:UNICODE>
$<$<CXX_COMPILER_ID:MSVC>:_UNICODE>
)
# Order build as UTF-8 in MSVC
target_compile_options(YYCCTestbench
PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/utf-8>
)
# Discover all test
include(GoogleTest)