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:
yyc12345 2024-08-26 11:58:20 +08:00
parent 3858b4f3ec
commit c91df3a74f
9 changed files with 222 additions and 168 deletions

View File

@ -15,7 +15,7 @@ Functions located in this helper support the convertion between string and follo
Please note in C++, \c bool is integral type but we list it individually because parser will treat it specially. Please note in C++, \c bool is integral type but we list it individually because parser will treat it specially.
For \c bool type, parser will try doing convertion between it and \c "true" \c "false" string. For \c bool type, parser will try doing convertion between it and \c "true" \c "false" string.
(\b case-sensitive. It means that \c true will only be converted to \c "true" and \c "TRUE" can not be recognised.) (\b case-insensitive. It means that \c true can be converted from \c "true", \c "True" or \c "TRUE".)
\section parser_helper__try_parse Try Parse \section parser_helper__try_parse Try Parse
@ -31,6 +31,7 @@ YYCC::ParserHelper::TryParse<uint32_t>(YYCC_U8("123"), val);
YYCC::ParserHelper::TryParse<uint32_t>(YYCC_U8("7fff"), val, 16); YYCC::ParserHelper::TryParse<uint32_t>(YYCC_U8("7fff"), val, 16);
\endcode \endcode
For floating point type, this function allows caller to specify extra argument providing the format of given number string (\c std::chars_format).
For integral type, this function allows caller to specify extra argument providing the base of given number string. For integral type, this function allows caller to specify extra argument providing the base of given number string.
\section parser_helper__parse Parse \section parser_helper__parse Parse
@ -45,8 +46,8 @@ There is an example:
uint32_t val = YYCC::ParserHelper::Parse<uint32_t>(YYCC_U8("123")); uint32_t val = YYCC::ParserHelper::Parse<uint32_t>(YYCC_U8("123"));
\endcode \endcode
Please note, for integral types, there is no base argument in #Parse. For integral and floating point value,
Please use #TryParse instead. it has same extra argument with #TryParse to provide more number infomation.
Using this function is dangerous if the validation of your input is important. Using this function is dangerous if the validation of your input is important.
In this case, please use #TryParse instead. In this case, please use #TryParse instead.
@ -63,6 +64,11 @@ There is an example:
auto result = YYCC::ParserHelper::ToString<uint32_t>(UINT32_C(114)); auto result = YYCC::ParserHelper::ToString<uint32_t>(UINT32_C(114));
\endcode \endcode
For floating point type, this function allows caller to specify extra arguments
which provides the format (\c std::chars_format) and precision when getting string representation.
For integral type, this function allows caller to specify extra argument
providing the base of number when getting string representation.
\section parser_helper__notes Notes \section parser_helper__notes Notes
All functions within this helper are implementated by standard library functions. All functions within this helper are implementated by standard library functions.

View File

@ -38,8 +38,8 @@ and second overload will return empty string when formatter failed.
YYCC::StringHelper provide 2 functions for programmer do string replacement: YYCC::StringHelper provide 2 functions for programmer do string replacement:
\code \code
void Replace(yycc_u8string&, const yycc_char8_t*, const yycc_char8_t*); void Replace(yycc_u8string&, const yycc_u8string_view&, const yycc_u8string_view&);
yycc_u8string Replace(const yycc_char8_t*, const yycc_char8_t*, const yycc_char8_t*); yycc_u8string Replace(const yycc_u8string_view&, const yycc_u8string_view&, const yycc_u8string_view&);
\endcode \endcode
The first overload will do replacement in given string container directly. The first overload will do replacement in given string container directly.
@ -47,9 +47,9 @@ The second overload will produce a copy of original string and do replacement on
#Replace has special treatments for following scenarios: #Replace has special treatments for following scenarios:
\li If given string is empty or nullptr, the return value will be empty. \li If given string is empty, the return value will be empty.
\li If the character sequence to be replaced is nullptr or empty string, no replacement will happen. \li If the character sequence to be replaced is empty string, no replacement will happen.
\li If the character sequence will be replaced into string is nullptr or empty, it will simply delete found character sequence from given string. \li If the character sequence will be replaced into string is or empty, it will simply delete found character sequence from given string.
\section string_helper__join Join \section string_helper__join Join
@ -95,10 +95,18 @@ auto joined_string = YYCC::StringHelper::Join(
\subsection string_helper__join__specialized Specialized Join Function \subsection string_helper__join__specialized Specialized Join Function
Despite universal join function, Despite universal join function,
YYCC::StringHelper also provide some specialized join functions for commonly used types. YYCC::StringHelper also provide a specialized join functions for standard library container.
Current we support following join function: For example, the code written above can be written in following code by using this specialized overload.
The first two argument is just the begin and end iterator.
However, you must make sure that we can dereference it and then implicitly convert it to yycc_u8string_view.
Otherwise this overload will throw template error.
\li \c std::vector<yycc_u8string>: With an extra option which allow join it with reversed order. \code
std::vector<yycc_u8string> data {
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
};
auto joined_string = YYCC::StringHelper::Join(data.begin(), data.end(), decilmer);
\endcode
\section string_helper__lower_upper Lower Upper \section string_helper__lower_upper Lower Upper
@ -106,11 +114,11 @@ String helper provides Python-like string lower and upper function.
Both lower and upper function have 2 overloads: Both lower and upper function have 2 overloads:
\code \code
yycc_u8string Lower(const yycc_char8_t*); yycc_u8string Lower(const yycc_u8string_view&);
void Lower(yycc_u8string&); void Lower(yycc_u8string&);
\endcode \endcode
First overload accepts a NULL-terminated string as argument and return a \b copy whose content are all the lower case of original string. First overload accepts a string view as argument and return a \b copy whose content are all the lower case of original string.
Second overload accepts a mutable string container as argument and will make all characters stored in it become their lower case. Second overload accepts a mutable string container as argument and will make all characters stored in it become their lower case.
You can choose on of them for your flavor and requirements. You can choose on of them for your flavor and requirements.
Upper also has similar 2 overloads. Upper also has similar 2 overloads.
@ -121,19 +129,19 @@ String helper provides Python-like string split function.
It has 2 types for you: It has 2 types for you:
\code \code
std::vector<yycc_u8string> Split(const yycc_u8string_view&, const yycc_char8_t*); std::vector<yycc_u8string> Split(const yycc_u8string_view&, const yycc_u8string_view&);
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view&, const yycc_char8_t*); std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view&, const yycc_u8string_view&);
\endcode \endcode
All these overloads take a string view as the first argument representing the string need to be split. All these overloads take a string view as the first argument representing the string need to be split.
The second argument is a raw string pointer representing the decilmer for splitting. The second argument is a string view representing the decilmer for splitting.
The only difference between these 2 split function are overt according to their names. The only difference between these 2 split function are overt according to their names.
The first split function will return a list of copied string as its split result. The first split function will return a list of copied string as its split result.
The second split function will return a list of string view as its split result, The second split function will return a list of string view as its split result,
and it will keep valid as long as the life time of your given string view argument. and it will keep valid as long as the life time of your given string view argument.
It also means that the last overload will cost less memory if you don't need the copy of original string. It also means that the last overload will cost less memory if you don't need the copy of original string.
If the source string (the string need to be split) is empty, or the decilmer is \c nullptr or empty, If the source string (the string need to be split) is empty, or the decilmer is empty,
the result will only has 1 item and this item is source string itself. the result will only has 1 item and this item is source string itself.
There is no way that these methods return an empty list, except the code is buggy. There is no way that these methods return an empty list, except the code is buggy.

View File

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

View File

@ -40,7 +40,8 @@ namespace YYCC::DialogHelper {
return false; return false;
// convert pattern and join them // 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; WinFileFilters::WinFilterModes modes;
if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes, modes)) if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes, modes))
return false; return false;

View File

@ -2,6 +2,7 @@
#include "YYCCInternal.hpp" #include "YYCCInternal.hpp"
#include "EncodingHelper.hpp" #include "EncodingHelper.hpp"
#include "StringHelper.hpp"
#include <string> #include <string>
#include <cinttypes> #include <cinttypes>
#include <type_traits> #include <type_traits>
@ -25,14 +26,15 @@ namespace YYCC::ParserHelper {
* @param[out] num * @param[out] num
* The variable receiving result. * The variable receiving result.
* There is no guarantee that the content is not modified when parsing failed. * 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. * @return True if success, otherwise false.
*/ */
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0> 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( auto [ptr, ec] = std::from_chars(
EncodingHelper::ToOrdinary(strl.data()), EncodingHelper::ToOrdinary(strl.data()),
EncodingHelper::ToOrdinary(strl.data() + strl.size()), EncodingHelper::ToOrdinary(strl.data() + strl.size()),
num, std::chars_format::general num, fmt
); );
if (ec == std::errc()) { if (ec == std::errc()) {
// check whether the full string is matched // check whether the full string is matched
@ -50,12 +52,12 @@ namespace YYCC::ParserHelper {
} }
/** /**
* @brief Try parsing given string to integral types. * @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[in] strl The string need to be parsed.
* @param[out] num * @param[out] num
* The variable receiving result. * The variable receiving result.
* There is no guarantee that the content is not modified when parsing failed. * 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. * @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> 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. * @brief Try parsing given string to bool types.
* @tparam _Ty The type derived from bool type. * @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 * @param[out] num
* The variable receiving result. * The variable receiving result.
* There is no guarantee that the content is not modified when parsing failed. * There is no guarantee that the content is not modified when parsing failed.
@ -90,6 +92,10 @@ namespace YYCC::ParserHelper {
*/ */
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0> template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
bool TryParse(const yycc_u8string_view& strl, _Ty& num) { 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; if (strl == YYCC_U8("true")) num = true;
else if (strl == YYCC_U8("false")) num = false; else if (strl == YYCC_U8("false")) num = false;
else return false; else return false;
@ -97,15 +103,47 @@ namespace YYCC::ParserHelper {
} }
/** /**
* @brief Parse given string to arithmetic types. * @brief Parse given string to floating point types.
* @tparam _Ty The type derived from arithmetic type. * @tparam _Ty The type derived from floating point type.
* @param[in] strl The string need to be parsed. * @param[in] strl The string need to be parsed.
* @param[in] fmt The floating point format used when try parsing.
* @return * @return
* The parsing result. * The parsing result.
* There is no guarantee about the content of this return value when parsing failed. * 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. * 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 Parse(const yycc_u8string_view& strl) {
_Ty ret; _Ty ret;
TryParse(strl, ret); TryParse(strl, ret);
@ -115,18 +153,21 @@ namespace YYCC::ParserHelper {
// Reference: https://en.cppreference.com/w/cpp/utility/to_chars // Reference: https://en.cppreference.com/w/cpp/utility/to_chars
/** /**
* @brief Return a string version of given arithmetic value. * @brief Return the string representation of given floating point value.
* @tparam _Ty The type derived from arithmetic type. * @tparam _Ty The type derived from floating point type.
* @param[in] num The value getting string version. * @param[in] num The value need to get string representation.
* @return The string version of given value. * @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> template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
yycc_u8string ToString(_Ty num) { 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; std::array<yycc_char8_t, 64> buffer;
auto [ptr, ec] = std::to_chars( auto [ptr, ec] = std::to_chars(
EncodingHelper::ToOrdinary(buffer.data()), EncodingHelper::ToOrdinary(buffer.data()),
EncodingHelper::ToOrdinary(buffer.data() + buffer.size()), EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),
num num, fmt, precision
); );
if (ec == std::errc()) { if (ec == std::errc()) {
return yycc_u8string(buffer.data(), EncodingHelper::ToUTF8(ptr) - buffer.data()); 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. * @tparam _Ty The type derived from bool type.
* @param[in] num The value getting string version. * @param[in] num The value need to get string representation.
* @return The string version of given value ("true" or "false"). * @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> template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
yycc_u8string ToString(_Ty num) { yycc_u8string ToString(_Ty num) {

View File

@ -81,12 +81,10 @@ namespace YYCC::StringHelper {
#pragma region Replace #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 // Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
// check requirements // 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 // from string should not be empty
yycc_u8string from_strl(_from_strl); yycc_u8string from_strl(_from_strl);
yycc_u8string to_strl(_to_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 // prepare result
yycc_u8string strl; yycc_u8string strl(_strl);
// if given string is not nullptr, assign it and process it. Replace(strl, _from_strl, _to_strl);
if (_strl != nullptr) {
strl = _strl;
Replace(strl, _from_strl, _to_strl);
}
// return value // return value
return strl; return strl;
} }
@ -116,7 +110,7 @@ namespace YYCC::StringHelper {
#pragma region Join #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; yycc_u8string ret;
bool is_first = true; bool is_first = true;
yycc_u8string_view element; yycc_u8string_view element;
@ -126,9 +120,8 @@ namespace YYCC::StringHelper {
// insert decilmer // insert decilmer
if (is_first) is_first = false; if (is_first) is_first = false;
else { else {
// only insert non-nullptr decilmer. // append decilmer.
if (decilmer != nullptr) ret.append(decilmer);
ret.append(decilmer);
} }
// insert element if it is not empty // insert element if it is not empty
@ -139,32 +132,6 @@ namespace YYCC::StringHelper {
return ret; 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 endregion
#pragma region Upper Lower #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) { void Lower(yycc_u8string& strl) {
GeneralStringLowerUpper<true>(strl); GeneralStringLowerUpper<true>(strl);
} }
yycc_u8string Upper(const yycc_char8_t* strl) { yycc_u8string Lower(const yycc_u8string_view& strl) {
// same as Lower, just replace char transform function. yycc_u8string ret(strl);
yycc_u8string ret; Lower(ret);
if (strl == nullptr) return ret;
else ret = strl;
Upper(ret);
return ret; return ret;
} }
@ -208,16 +164,24 @@ namespace YYCC::StringHelper {
GeneralStringLowerUpper<false>(strl); 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 endregion
#pragma region Split #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 // call split view
auto view_result = SplitView(strl, _decilmer); auto view_result = SplitView(strl, _decilmer);
// copy string view result to string // copy string view result to string
std::vector<yycc_u8string> elems; std::vector<yycc_u8string> elems;
elems.reserve(view_result.size());
for (const auto& strl_view : view_result) { for (const auto& strl_view : view_result) {
elems.emplace_back(yycc_u8string(strl_view)); elems.emplace_back(yycc_u8string(strl_view));
} }
@ -225,17 +189,17 @@ namespace YYCC::StringHelper {
return elems; 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: // Reference:
// https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c // https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
// prepare return value // prepare return value
std::vector<yycc_u8string_view> elems; std::vector<yycc_u8string_view> elems;
// if string need to be splitted is empty, return original string (empty item). // if string need to be splitted is empty, return original string (empty string).
// if decilmer is nullptr, or decilmer is zero length, return original string. // if decilmer is empty, return original string.
yycc_u8string decilmer; yycc_u8string decilmer(_decilmer);
if (strl.empty() || _decilmer == nullptr || (decilmer = _decilmer, decilmer.empty())) { if (strl.empty() || decilmer.empty()) {
elems.emplace_back(strl); elems.emplace_back(strl);
return elems; return elems;
} }

View File

@ -54,7 +54,7 @@ namespace YYCC::StringHelper {
* @param[in] _from_strl The \e old string. * @param[in] _from_strl The \e old string.
* @param[in] _to_strl The \e new 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. * @brief Return a copy with all occurrences of substring \e old replaced by \e new.
* @param[in] _strl The string for replacing * @param[in] _strl The string for replacing
@ -62,7 +62,7 @@ namespace YYCC::StringHelper {
* @param[in] _to_strl The \e new string. * @param[in] _to_strl The \e new string.
* @return The result of replacement. * @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. * @brief The data provider of general join function.
@ -85,38 +85,51 @@ namespace YYCC::StringHelper {
* @param[in] decilmer The decilmer used for joining. * @param[in] decilmer The decilmer used for joining.
* @return The result string of 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. * @brief Specialized join function for standard library container.
* @param[in] data The list to be joined. * @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] 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. * @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. * @brief Convert given string to lowercase.
* @param[in,out] strl The string to be lowercase. * @param[in,out] strl The string to be lowercase.
*/ */
void Lower(yycc_u8string& strl); void Lower(yycc_u8string& strl);
/** /**
* @brief Return a copy of the string converted to uppercase. * @brief Return a copy of the string converted to lowercase.
* @param[in] strl The string to be uppercase. * @param[in] strl The string to be lowercase.
* @return The copy of the string converted to uppercase. * @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. * @brief Convert given string to uppercase.
* @param[in,out] strl The string to be uppercase. * @param[in,out] strl The string to be uppercase.
*/ */
void Upper(yycc_u8string& strl); 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. * @brief Split given string with specified decilmer.
@ -125,10 +138,10 @@ namespace YYCC::StringHelper {
* @return * @return
* The split result. * The split result.
* \par * \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. * 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. * @brief Split given string with specified decilmer as string view.
* @param[in] strl The string need to be splitting. * @param[in] strl The string need to be splitting.
@ -137,10 +150,10 @@ namespace YYCC::StringHelper {
* The split result with string view format. * The split result with string view format.
* This will not produce any copy of original string. * This will not produce any copy of original string.
* \par * \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. * the result container will only contain 1 entry which is equal to given string.
* @see Split(const yycc_u8string_view&, const yycc_char8_t*) * @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);
} }

View File

@ -15,12 +15,8 @@ PRIVATE
YYCCommonplace YYCCommonplace
) )
# Setup C++ standard # Setup C++ standard
set_target_properties(YYCCTestbench target_compile_features(YYCCTestbench PUBLIC cxx_std_17)
PROPERTIES set_target_properties(YYCCTestbench PROPERTIES CXX_EXTENSION OFF)
CXX_STANDARD 17
CXX_STANDARD_REQUIRED 17
CXX_EXTENSION OFF
)
# Order Unicode charset for private using # Order Unicode charset for private using
target_compile_definitions(YYCCTestbench target_compile_definitions(YYCCTestbench
PRIVATE PRIVATE

View File

@ -195,17 +195,13 @@ namespace YYCCTestbench {
Assert(test_replace == YYCC_U8("aaddcc"), YYCC_U8("YYCC::StringHelper::Replace")); Assert(test_replace == YYCC_U8("aaddcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("zz"), YYCC_U8("yy")); // no replace test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("zz"), YYCC_U8("yy")); // no replace
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace")); Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8(""), YYCC_U8("zz")); // empty finding test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC::yycc_u8string_view(), YYCC_U8("zz")); // empty finding
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), nullptr, YYCC_U8("zz")); // nullptr finding
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace")); Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaaabbaa"), YYCC_U8("aa"), YYCC_U8("")); // no replaced string test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaaabbaa"), YYCC_U8("aa"), YYCC_U8("")); // no replaced string
Assert(test_replace == YYCC_U8("bb"), YYCC_U8("YYCC::StringHelper::Replace")); Assert(test_replace == YYCC_U8("bb"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaxcc"), YYCC_U8("x"), YYCC_U8("yx")); // nested replacing test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaxcc"), YYCC_U8("x"), YYCC_U8("yx")); // nested replacing
Assert(test_replace == YYCC_U8("aayxcc"), YYCC_U8("YYCC::StringHelper::Replace")); Assert(test_replace == YYCC_U8("aayxcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8(""), YYCC_U8(""), YYCC_U8("xy")); // empty source string test_replace = YYCC::StringHelper::Replace(YYCC::yycc_u8string_view(), YYCC_U8(""), YYCC_U8("xy")); // empty source string
Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(nullptr, YYCC_U8(""), YYCC_U8("xy")); // nullptr source string
Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace")); Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace"));
// Test Upper / Lower // Test Upper / Lower
@ -218,10 +214,8 @@ namespace YYCCTestbench {
std::vector<YYCC::yycc_u8string> test_join_container { std::vector<YYCC::yycc_u8string> test_join_container {
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("") YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
}; };
auto test_join = YYCC::StringHelper::Join(test_join_container, YYCC_U8(", ")); auto test_join = YYCC::StringHelper::Join(test_join_container.begin(), test_join_container.end(), YYCC_U8(", "));
Assert(test_join == YYCC_U8(", 1, 2, "), YYCC_U8("YYCC::StringHelper::Join")); Assert(test_join == YYCC_U8(", 1, 2, "), YYCC_U8("YYCC::StringHelper::Join"));
test_join = YYCC::StringHelper::Join(test_join_container, YYCC_U8(", "), true);
Assert(test_join == YYCC_U8(", 2, 1, "), YYCC_U8("YYCC::StringHelper::Join"));
// Test Split // Test Split
auto test_split = YYCC::StringHelper::Split(YYCC_U8(", 1, 2, "), YYCC_U8(", ")); // normal auto test_split = YYCC::StringHelper::Split(YYCC_U8(", 1, 2, "), YYCC_U8(", ")); // normal
@ -233,7 +227,7 @@ namespace YYCCTestbench {
test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("-")); // no matched decilmer test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("-")); // no matched decilmer
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split")); Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split")); Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split"));
test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("")); // empty decilmer test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC::yycc_u8string_view()); // empty decilmer
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split")); Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split")); Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split"));
test_split = YYCC::StringHelper::Split(YYCC::yycc_u8string_view(), YYCC_U8("")); // empty source string test_split = YYCC::StringHelper::Split(YYCC::yycc_u8string_view(), YYCC_U8("")); // empty source string
@ -245,10 +239,10 @@ namespace YYCCTestbench {
static void ParserTestbench() { static void ParserTestbench() {
// Test success TryParse // Test success TryParse
#define TEST_MACRO(type_t, value, string_value) { \ #define TEST_MACRO(type_t, value, string_value, ...) { \
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \ YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
type_t cache; \ type_t cache; \
Assert(YYCC::ParserHelper::TryParse<type_t>(cache_string, cache) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \ Assert(YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, __VA_ARGS__) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
} }
TEST_MACRO(int8_t, INT8_C(-61), "-61"); TEST_MACRO(int8_t, INT8_C(-61), "-61");
@ -259,33 +253,38 @@ namespace YYCCTestbench {
TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293"); TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293");
TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161"); TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161");
TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807"); TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807");
TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16);
TEST_MACRO(float, 1.0f, "1.0");
TEST_MACRO(double, 1.0, "1.0");
TEST_MACRO(bool, true, "true"); TEST_MACRO(bool, true, "true");
#undef TEST_MACRO #undef TEST_MACRO
// Test failed TryParse // Test failed TryParse
#define TEST_MACRO(type_t, value, string_value) { \ #define TEST_MACRO(type_t, string_value, ...) { \
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \ YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
type_t cache; \ type_t cache; \
Assert(!YYCC::ParserHelper::TryParse<type_t>(cache_string, cache), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \ Assert(!YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, __VA_ARGS__), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
} }
TEST_MACRO(int8_t, INT8_C(-61), "6161"); TEST_MACRO(int8_t, "6161");
TEST_MACRO(uint8_t, UINT8_C(200), "32800"); TEST_MACRO(uint8_t, "32800");
TEST_MACRO(int16_t, INT16_C(6161), "61616161"); TEST_MACRO(int16_t, "61616161");
TEST_MACRO(uint16_t, UINT16_C(32800), "4294967293"); TEST_MACRO(uint16_t, "4294967293");
TEST_MACRO(int32_t, INT32_C(61616161), "616161616161"); TEST_MACRO(int32_t, "616161616161");
TEST_MACRO(uint32_t, UINT32_C(4294967293), "9223372036854775807"); TEST_MACRO(uint32_t, "9223372036854775807");
TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161616161616161"); TEST_MACRO(int64_t, "616161616161616161616161");
TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "92233720368547758079223372036854775807"); TEST_MACRO(uint64_t, "92233720368547758079223372036854775807");
TEST_MACRO(bool, true, "hello, world!"); TEST_MACRO(float, "1e40");
TEST_MACRO(double, "1e114514");
TEST_MACRO(bool, "hello, world!");
#undef TEST_MACRO #undef TEST_MACRO
// Test ToString // Test ToString
#define TEST_MACRO(type_t, value, string_value) { \ #define TEST_MACRO(type_t, value, string_value, ...) { \
type_t cache = value; \ type_t cache = value; \
YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString<type_t>(cache)); \ YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString<type_t>(cache, __VA_ARGS__)); \
Assert(ret == YYCC_U8(string_value), YYCC_U8("YYCC::StringHelper::ToString<" #type_t ">")); \ Assert(ret == YYCC_U8(string_value), YYCC_U8("YYCC::StringHelper::ToString<" #type_t ">")); \
} }
@ -297,10 +296,13 @@ namespace YYCCTestbench {
TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293"); TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293");
TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161"); TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161");
TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807"); TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807");
TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16);
TEST_MACRO(float, 1.0f, "1.0", std::chars_format::fixed, 1);
TEST_MACRO(double, 1.0, "1.0", std::chars_format::fixed, 1);
TEST_MACRO(bool, true, "true"); TEST_MACRO(bool, true, "true");
#undef TEST_MACRO #undef TEST_MACRO
} }
static void DialogTestbench() { static void DialogTestbench() {
@ -416,7 +418,7 @@ namespace YYCCTestbench {
#else #else
YYCC::yycc_u8string decilmer(1u, std::filesystem::path::preferred_separator); YYCC::yycc_u8string decilmer(1u, std::filesystem::path::preferred_separator);
#endif #endif
YYCC::yycc_u8string test_joined_path(YYCC::StringHelper::Join(c_UTF8TestStrTable, decilmer.c_str())); YYCC::yycc_u8string test_joined_path(YYCC::StringHelper::Join(c_UTF8TestStrTable.begin(), c_UTF8TestStrTable.end(), decilmer));
Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::StdPatch::ToStdPath, YYCC::StdPatch::ToUTF8Path")); Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::StdPatch::ToStdPath, YYCC::StdPatch::ToUTF8Path"));
@ -574,8 +576,8 @@ namespace YYCCTestbench {
// init option context // init option context
TestArgParser test; TestArgParser test;
#define PREPARE_DATA(...) char* test_argv[] = { __VA_ARGS__ }; \ #define PREPARE_DATA(...) const char* test_argv[] = { __VA_ARGS__ }; \
auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeof(char*), test_argv); auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeof(char*), const_cast<char**>(test_argv));
// normal test // normal test
{ {