fix: fix issues.

- add chars format argument for floating point overload of ParserHelper::TryParse.
- add overload for ParserHelper::Parse to match with ParserHelper::TryParse.
- fix the issue that we can not specify c++ standard in command line when configuring project.
- update documentation for changes.
- change following function's argument from const yycc_char8_t* to const yycc_u8string_view&.
	- StringHelper::Split, StringHelper::SplitView
	- StringHelper::Lower, StringHelper::Upper
	- StringHelper::Join
	- StringHelper::Replace
- use iterator type, not std::vector<yycc_u8string> for specialized StringHelper::Join to have more wide usage.
This commit is contained in:
2024-08-26 11:58:20 +08:00
parent 3858b4f3ec
commit c91df3a74f
9 changed files with 222 additions and 168 deletions

View File

@ -56,12 +56,9 @@ PRIVATE
$<$<BOOL:${WIN32}>:DbgHelp.lib>
)
# Setup C++ standard
set_target_properties(YYCCommonplace
PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED 17
CXX_EXTENSION OFF
)
target_compile_features(YYCCommonplace PUBLIC cxx_std_17)
set_target_properties(YYCCommonplace PROPERTIES CXX_EXTENSION OFF)
# Setup macros
target_compile_definitions(YYCCommonplace
# Debug macro should populate to child projects
PUBLIC

View File

@ -40,7 +40,8 @@ namespace YYCC::DialogHelper {
return false;
// convert pattern and join them
yycc_u8string joined_modes(YYCC::StringHelper::Join(it.second, YYCC_U8(";")));
const auto& filter_modes = it.second;
yycc_u8string joined_modes(YYCC::StringHelper::Join(filter_modes.begin(), filter_modes.end(), YYCC_U8(";")));
WinFileFilters::WinFilterModes modes;
if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes, modes))
return false;

View File

@ -2,6 +2,7 @@
#include "YYCCInternal.hpp"
#include "EncodingHelper.hpp"
#include "StringHelper.hpp"
#include <string>
#include <cinttypes>
#include <type_traits>
@ -17,7 +18,7 @@
namespace YYCC::ParserHelper {
// Reference: https://zh.cppreference.com/w/cpp/utility/from_chars
/**
* @brief Try parsing given string to floating point types.
* @tparam _Ty The type derived from floating point type.
@ -25,14 +26,15 @@ namespace YYCC::ParserHelper {
* @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 _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
bool TryParse(const yycc_u8string_view& strl, _Ty& num) {
bool TryParse(const yycc_u8string_view& strl, _Ty& num, std::chars_format fmt = std::chars_format::general) {
auto [ptr, ec] = std::from_chars(
EncodingHelper::ToOrdinary(strl.data()),
EncodingHelper::ToOrdinary(strl.data() + strl.size()),
num, std::chars_format::general
num, fmt
);
if (ec == std::errc()) {
// check whether the full string is matched
@ -50,12 +52,12 @@ namespace YYCC::ParserHelper {
}
/**
* @brief Try parsing given string to integral types.
* @tparam _Ty The type derived from integral type.
* @tparam _Ty 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).
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
* @return True if success, otherwise false.
*/
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
@ -82,7 +84,7 @@ namespace YYCC::ParserHelper {
/**
* @brief Try parsing given string to bool types.
* @tparam _Ty The type derived from bool type.
* @param[in] strl The string need to be parsed ("true" or "false").
* @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.
@ -90,22 +92,58 @@ namespace YYCC::ParserHelper {
*/
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
bool TryParse(const yycc_u8string_view& strl, _Ty& num) {
// get lower case
yycc_u8string lower_case(strl);
YYCC::StringHelper::Lower(lower_case);
// compare result
if (strl == YYCC_U8("true")) num = true;
else if (strl == YYCC_U8("false")) num = false;
else return false;
return true;
}
/**
* @brief Parse given string to arithmetic types.
* @tparam _Ty The type derived from arithmetic type.
* @brief Parse given string to floating point types.
* @tparam _Ty 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.
*/
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty>, int> = 0>
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
_Ty Parse(const yycc_u8string_view& strl, std::chars_format fmt = std::chars_format::general) {
_Ty ret;
TryParse(strl, ret, fmt);
return ret;
}
/**
* @brief Parse given string to integral type types.
* @tparam _Ty 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.
*/
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
_Ty Parse(const yycc_u8string_view& strl, int base = 10) {
_Ty ret;
TryParse(strl, ret, base);
return ret;
}
/**
* @brief Parse given string to bool types.
* @tparam _Ty 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.
*/
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
_Ty Parse(const yycc_u8string_view& strl) {
_Ty ret;
TryParse(strl, ret);
@ -115,18 +153,21 @@ namespace YYCC::ParserHelper {
// Reference: https://en.cppreference.com/w/cpp/utility/to_chars
/**
* @brief Return a string version of given arithmetic value.
* @tparam _Ty The type derived from arithmetic type.
* @param[in] num The value getting string version.
* @return The string version of given value.
* @brief Return the string representation of given floating point value.
* @tparam _Ty The type derived from floating point type.
* @param[in] num The value need to get string representation.
* @param[in] fmt The floating point format used when getting string representation.
* @param[in] precision The floating point precision used when getting string representation.
* @return The string representation of given value.
*/
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
yycc_u8string ToString(_Ty num) {
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
yycc_u8string ToString(_Ty num, std::chars_format fmt = std::chars_format::general, int precision = 6) {
// default precision = 6 is gotten from: https://en.cppreference.com/w/c/io/fprintf
std::array<yycc_char8_t, 64> buffer;
auto [ptr, ec] = std::to_chars(
EncodingHelper::ToOrdinary(buffer.data()),
EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),
num
num, fmt, precision
);
if (ec == std::errc()) {
return yycc_u8string(buffer.data(), EncodingHelper::ToUTF8(ptr) - buffer.data());
@ -140,10 +181,36 @@ namespace YYCC::ParserHelper {
}
}
/**
* @brief Return a string version of given bool value.
* @brief Return the string representation of given integral value.
* @tparam _Ty The type derived from integral type except bool type.
* @param[in] num The value need to get string representation.
* @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 _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
yycc_u8string ToString(_Ty num, int base = 10) {
std::array<yycc_char8_t, 64> buffer;
auto [ptr, ec] = std::to_chars(
EncodingHelper::ToOrdinary(buffer.data()),
EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),
num, base
);
if (ec == std::errc()) {
return yycc_u8string(buffer.data(), EncodingHelper::ToUTF8(ptr) - buffer.data());
} else if (ec == std::errc::value_too_large) {
// too short buffer
// this should not happened
throw std::out_of_range("ToString() buffer is not sufficient.");
} else {
// unreachable
throw std::runtime_error("unreachable code.");
}
}
/**
* @brief Return the string representation of given bool value.
* @tparam _Ty The type derived from bool type.
* @param[in] num The value getting string version.
* @return The string version of given value ("true" or "false").
* @param[in] num The value need to get string representation.
* @return The string representation of given value ("true" or "false").
*/
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
yycc_u8string ToString(_Ty num) {

View File

@ -23,9 +23,9 @@ namespace YYCC::StringHelper {
// the return value is desired char count without NULL terminal.
// minus number means error
int count = std::vsnprintf(
nullptr,
0,
EncodingHelper::ToOrdinary(format),
nullptr,
0,
EncodingHelper::ToOrdinary(format),
args1
);
if (count < 0) {
@ -41,8 +41,8 @@ namespace YYCC::StringHelper {
strl.resize(count);
int write_result = std::vsnprintf(
EncodingHelper::ToOrdinary(strl.data()),
strl.size() + 1,
EncodingHelper::ToOrdinary(format),
strl.size() + 1,
EncodingHelper::ToOrdinary(format),
args2
);
va_end(args2);
@ -81,12 +81,10 @@ namespace YYCC::StringHelper {
#pragma region Replace
void Replace(yycc_u8string& strl, const yycc_char8_t* _from_strl, const yycc_char8_t* _to_strl) {
void Replace(yycc_u8string& strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl) {
// Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
// check requirements
// from string and to string should not be nullptr.
if (_from_strl == nullptr || _to_strl == nullptr) return;
// from string should not be empty
yycc_u8string from_strl(_from_strl);
yycc_u8string to_strl(_to_strl);
@ -100,14 +98,10 @@ namespace YYCC::StringHelper {
}
}
yycc_u8string Replace(const yycc_char8_t* _strl, const yycc_char8_t* _from_strl, const yycc_char8_t* _to_strl) {
yycc_u8string Replace(const yycc_u8string_view& _strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl) {
// prepare result
yycc_u8string strl;
// if given string is not nullptr, assign it and process it.
if (_strl != nullptr) {
strl = _strl;
Replace(strl, _from_strl, _to_strl);
}
yycc_u8string strl(_strl);
Replace(strl, _from_strl, _to_strl);
// return value
return strl;
}
@ -116,7 +110,7 @@ namespace YYCC::StringHelper {
#pragma region Join
yycc_u8string Join(JoinDataProvider fct_data, const yycc_char8_t* decilmer) {
yycc_u8string Join(JoinDataProvider fct_data, const yycc_u8string_view& decilmer) {
yycc_u8string ret;
bool is_first = true;
yycc_u8string_view element;
@ -126,9 +120,8 @@ namespace YYCC::StringHelper {
// insert decilmer
if (is_first) is_first = false;
else {
// only insert non-nullptr decilmer.
if (decilmer != nullptr)
ret.append(decilmer);
// append decilmer.
ret.append(decilmer);
}
// insert element if it is not empty
@ -139,32 +132,6 @@ namespace YYCC::StringHelper {
return ret;
}
yycc_u8string Join(const std::vector<yycc_u8string>& data, const yycc_char8_t* decilmer, bool reversed) {
if (reversed) {
auto iter = data.crbegin();
auto stop = data.crend();
return Join([&iter, &stop](yycc_u8string_view& view) -> bool {
// if we reach tail, return false
if (iter == stop) return false;
// otherwise fetch data, inc iterator and return.
view = *iter;
++iter;
return true;
}, decilmer);
} else {
auto iter = data.cbegin();
auto stop = data.cend();
return Join([&iter, &stop](yycc_u8string_view& view) -> bool {
// if we reach tail, return nullptr
if (iter == stop) return false;
// otherwise fetch data, inc iterator and return.
view = *iter;
++iter;
return true;
}, decilmer);
}
}
#pragma endregion
#pragma region Upper Lower
@ -183,24 +150,13 @@ namespace YYCC::StringHelper {
);
}
yycc_u8string Lower(const yycc_char8_t* strl) {
yycc_u8string ret;
if (strl == nullptr) return ret;
else ret = strl;
Lower(ret);
return ret;
}
void Lower(yycc_u8string& strl) {
GeneralStringLowerUpper<true>(strl);
}
yycc_u8string Upper(const yycc_char8_t* strl) {
// same as Lower, just replace char transform function.
yycc_u8string ret;
if (strl == nullptr) return ret;
else ret = strl;
Upper(ret);
yycc_u8string Lower(const yycc_u8string_view& strl) {
yycc_u8string ret(strl);
Lower(ret);
return ret;
}
@ -208,16 +164,24 @@ namespace YYCC::StringHelper {
GeneralStringLowerUpper<false>(strl);
}
yycc_u8string Upper(const yycc_u8string_view& strl) {
// same as Lower, just replace char transform function.
yycc_u8string ret(strl);
Upper(ret);
return ret;
}
#pragma endregion
#pragma region Split
std::vector<yycc_u8string> Split(const yycc_u8string_view& strl, const yycc_char8_t* _decilmer) {
std::vector<yycc_u8string> Split(const yycc_u8string_view& strl, const yycc_u8string_view& _decilmer) {
// call split view
auto view_result = SplitView(strl, _decilmer);
// copy string view result to string
std::vector<yycc_u8string> elems;
elems.reserve(view_result.size());
for (const auto& strl_view : view_result) {
elems.emplace_back(yycc_u8string(strl_view));
}
@ -225,17 +189,17 @@ namespace YYCC::StringHelper {
return elems;
}
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view& strl, const yycc_char8_t* _decilmer) {
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view& strl, const yycc_u8string_view& _decilmer) {
// Reference:
// https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
// prepare return value
std::vector<yycc_u8string_view> elems;
// if string need to be splitted is empty, return original string (empty item).
// if decilmer is nullptr, or decilmer is zero length, return original string.
yycc_u8string decilmer;
if (strl.empty() || _decilmer == nullptr || (decilmer = _decilmer, decilmer.empty())) {
// if string need to be splitted is empty, return original string (empty string).
// if decilmer is empty, return original string.
yycc_u8string decilmer(_decilmer);
if (strl.empty() || decilmer.empty()) {
elems.emplace_back(strl);
return elems;
}

View File

@ -54,7 +54,7 @@ namespace YYCC::StringHelper {
* @param[in] _from_strl The \e old string.
* @param[in] _to_strl The \e new string.
*/
void Replace(yycc_u8string& strl, const yycc_char8_t* _from_strl, const yycc_char8_t* _to_strl);
void Replace(yycc_u8string& strl, const yycc_u8string_view& _from_strl, const yycc_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
@ -62,7 +62,7 @@ namespace YYCC::StringHelper {
* @param[in] _to_strl The \e new string.
* @return The result of replacement.
*/
yycc_u8string Replace(const yycc_char8_t* _strl, const yycc_char8_t* _from_strl, const yycc_char8_t* _to_strl);
yycc_u8string Replace(const yycc_u8string_view& _strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl);
/**
* @brief The data provider of general join function.
@ -85,38 +85,51 @@ namespace YYCC::StringHelper {
* @param[in] decilmer The decilmer used for joining.
* @return The result string of joining.
*/
yycc_u8string Join(JoinDataProvider fct_data, const yycc_char8_t* decilmer);
yycc_u8string Join(JoinDataProvider fct_data, const yycc_u8string_view& decilmer);
/**
* @brief Specialized join function for \c std::vector.
* @param[in] data The list to be joined.
* @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 yycc_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] decilmer The decilmer used for joining.
* @param[in] reversed True if this list should be joined in reversed order.
* @return The result string of joining.
*/
yycc_u8string Join(const std::vector<yycc_u8string>& data, const yycc_char8_t* decilmer, bool reversed = false);
template<class InputIt>
yycc_u8string Join(InputIt first, InputIt last, const yycc_u8string_view& decilmer) {
return Join([&first, &last](yycc_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;
}, decilmer);
}
/**
* @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.
*/
yycc_u8string Lower(const yycc_char8_t* strl);
/**
* @brief Convert given string to lowercase.
* @param[in,out] strl The string to be lowercase.
*/
void Lower(yycc_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.
* @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.
*/
yycc_u8string Upper(const yycc_char8_t* strl);
yycc_u8string Lower(const yycc_u8string_view& strl);
/**
* @brief Convert given string to uppercase.
* @param[in,out] strl The string to be uppercase.
*/
void Upper(yycc_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.
*/
yycc_u8string Upper(const yycc_u8string_view& strl);
/**
* @brief Split given string with specified decilmer.
@ -125,10 +138,10 @@ namespace YYCC::StringHelper {
* @return
* The split result.
* \par
* If given string is empty, or decilmer is nullptr or empty,
* If given string or decilmer are empty,
* the result container will only contain 1 entry which is equal to given string.
*/
std::vector<yycc_u8string> Split(const yycc_u8string_view& strl, const yycc_char8_t* _decilmer);
std::vector<yycc_u8string> Split(const yycc_u8string_view& strl, const yycc_u8string_view& _decilmer);
/**
* @brief Split given string with specified decilmer as string view.
* @param[in] strl The string need to be splitting.
@ -137,10 +150,10 @@ namespace YYCC::StringHelper {
* The split result with string view format.
* This will not produce any copy of original string.
* \par
* If given string is empty, or decilmer is nullptr or empty,
* If given string or decilmer are empty,
* the result container will only contain 1 entry which is equal to given string.
* @see Split(const yycc_u8string_view&, const yycc_char8_t*)
*/
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view& strl, const yycc_char8_t* _decilmer);
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view& strl, const yycc_u8string_view& _decilmer);
}