Доработана работа с constexpr. Дополнена документация.

This commit is contained in:
Aleksandr Orefkov 2026-01-04 12:20:36 +03:00
parent 3a86c9c221
commit de7461dcd5
8 changed files with 598 additions and 424 deletions

View File

@ -5,7 +5,7 @@ include(FetchContent)
project(
simstr
VERSION 1.3.0
VERSION 1.3.1
DESCRIPTION "Yet another modern C++ string library"
HOMEPAGE_URL "https://github.com/orefkov/simstr"
LANGUAGES CXX

View File

@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.3.0
PROJECT_NUMBER = 1.3.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a

View File

@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.3.0
PROJECT_NUMBER = 1.3.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* ver. 1.3.0
* ver. 1.3.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* База для строковых конкатенаций через выражения времени компиляции
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -49,10 +49,10 @@ using u32s = char32_t;
using uu8s = std::make_unsigned<u8s>::type;
template<typename K>
inline constexpr bool is_one_of_char_v = std::is_same_v<K, u8s> || std::is_same_v<K, wchar_t> || std::is_same_v<K, u16s> || std::is_same_v<K, u32s>;
constexpr bool is_one_of_char_v = std::is_same_v<K, u8s> || std::is_same_v<K, wchar_t> || std::is_same_v<K, u16s> || std::is_same_v<K, u32s>;
template<typename K>
inline constexpr bool is_one_of_std_char_v = std::is_same_v<K, u8s> || std::is_same_v<K, wchar_t> || std::is_same_v<K, wchar_type>;
constexpr bool is_one_of_std_char_v = std::is_same_v<K, u8s> || std::is_same_v<K, wchar_t> || std::is_same_v<K, wchar_type>;
template<typename From>
requires (is_one_of_std_char_v<From>)
@ -143,7 +143,7 @@ template<typename K, size_t N>
class const_lit_to_array {
template<size_t Idx>
size_t find(K s) const {
constexpr size_t find(K s) const {
if constexpr (Idx < N) {
return s == symbols_[Idx] ? Idx : find<Idx + 1>(s);
}
@ -151,7 +151,7 @@ class const_lit_to_array {
}
template<size_t Idx>
bool exist(K s) const {
constexpr bool exist(K s) const {
if constexpr (Idx < N) {
return s == symbols_[Idx] || exist<Idx + 1>(s);
}
@ -412,8 +412,8 @@ struct strexprjoin {
* expressions, which are then materialized into the final result in one call.
*/
template<StrExpr A, StrExprForType<typename A::symb_type> B>
inline auto operator+(const A& a, const B& b) {
return strexprjoin<A, B>{a, b};
constexpr strexprjoin<A, B> operator+(const A& a, const B& b) {
return {a, b};
}
/*!
@ -566,8 +566,8 @@ struct expr_char {
* ```
*/
template<typename K, StrExprForType<K> A>
constexpr inline auto operator+(const A& a, K s) {
return strexprjoin_c<A, expr_char<K>>{a, s};
constexpr strexprjoin_c<A, expr_char<K>> operator+(const A& a, K s) {
return {a, s};
}
/*!
@ -581,8 +581,8 @@ constexpr inline auto operator+(const A& a, K s) {
* @return string expression for a single character string.
*/
template<typename K>
constexpr inline auto e_char(K s) {
return expr_char<K>{s};
constexpr expr_char<K> e_char(K s) {
return {s};
}
template<typename K, size_t N>
@ -652,8 +652,8 @@ struct expr_literal {
* All these methods work and give the same result. Which one to use is a matter of taste.
*/
template<typename T, size_t N = const_lit<T>::Count>
constexpr inline auto e_t(T&& s) {
return expr_literal<typename const_lit<T>::symb_type, static_cast<size_t>(N - 1)>{s};
constexpr expr_literal<typename const_lit<T>::symb_type, static_cast<size_t>(N - 1)> e_t(T&& s) {
return {s};
}
template<bool first, typename K, size_t N, typename A>
@ -692,8 +692,8 @@ struct expr_literal_join {
* @return A string expression concatenating the operands.
*/
template<StrExpr A, typename K = typename A::symb_type, typename T, size_t N = const_lit_for<K, T>::Count>
constexpr inline auto operator+(const A& a, T&& s) {
return expr_literal_join<false, K, (N - 1), A>{s, a};
constexpr expr_literal_join<false, K, (N - 1), A> operator+(const A& a, T&& s) {
return {s, a};
}
/*!
@ -704,8 +704,8 @@ constexpr inline auto operator+(const A& a, T&& s) {
* @return A string expression concatenating the operands.
*/
template<StrExpr A, typename K = typename A::symb_type, typename T, size_t N = const_lit_for<K, T>::Count>
constexpr inline auto operator+(T&& s, const A& a) {
return expr_literal_join<true, K, (N - 1), A>{s, a};
constexpr expr_literal_join<true, K, (N - 1), A> operator+(T&& s, const A& a) {
return {s, a};
}
/*!
@ -748,8 +748,8 @@ struct expr_spaces {
* ```
*/
template<size_t N>
constexpr inline auto e_spca() {
return expr_spaces<u8s, N>();
constexpr expr_spaces<u8s, N> e_spca() {
return {};
}
/*!
@ -766,8 +766,8 @@ constexpr inline auto e_spca() {
* ```
*/
template<size_t N>
constexpr inline auto e_spcw() {
return expr_spaces<uws, N>();
constexpr expr_spaces<uws, N> e_spcw() {
return {};
}
/*!
@ -810,8 +810,8 @@ struct expr_pad {
* @return a string expression that generates a string of l characters k.
*/
template<typename K>
constexpr inline auto e_c(size_t l, K s) {
return expr_pad<K>{ l, s };
constexpr expr_pad<K> e_c(size_t l, K s) {
return { l, s };
}
template<typename K, size_t N>
@ -861,8 +861,8 @@ struct expr_repeat_expr {
* @return a string expression generating a string of l strings s
*/
template<typename T, typename K = const_lit<T>::symb_type, size_t M = const_lit<T>::Count> requires (M > 0)
constexpr inline auto e_repeat(T&& s, size_t l) {
return expr_repeat_lit<K, M - 1>{ l, s };
constexpr expr_repeat_lit<K, M - 1> e_repeat(T&& s, size_t l) {
return { l, s };
}
/*!
@ -879,8 +879,8 @@ constexpr inline auto e_repeat(T&& s, size_t l) {
* @return a string expression generating a string of l string expressions s
*/
template<StrExpr A>
constexpr inline auto e_repeat(const A& s, size_t l) {
return expr_repeat_expr<A>{ l, s };
constexpr expr_repeat_expr<A> e_repeat(const A& s, size_t l) {
return { l, s };
}
/*!
@ -1103,8 +1103,8 @@ struct expr_choice_two_lit {
* which is not optimal and will reduce performance. (This is checked in the "Build Full Func Name" benchmark)
*/
template<StrExpr A, StrExprForType<typename A::symb_type> B>
inline constexpr auto e_choice(bool c, const A& a, const B& b) {
return expr_choice<A, B>{a, b, c};
constexpr expr_choice<A, B> e_choice(bool c, const A& a, const B& b) {
return {a, b, c};
}
/*!
@ -1113,8 +1113,8 @@ inline constexpr auto e_choice(bool c, const A& a, const B& b) {
* @en @brief Overload e_choice when the third argument is a string literal.
*/
template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
inline constexpr auto e_choice(bool c, const A& a, T&& str) {
return expr_choice_one_lit<A, N - 1, true>{str, a, c};
constexpr expr_choice_one_lit<A, N - 1, true> e_choice(bool c, const A& a, T&& str) {
return {str, a, c};
}
/*!
@ -1123,8 +1123,8 @@ inline constexpr auto e_choice(bool c, const A& a, T&& str) {
* @en @brief Overload e_choice when the second argument is a string literal.
*/
template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
inline constexpr auto e_choice(bool c, T&& str, const A& a) {
return expr_choice_one_lit<A, N - 1, false>{str, a, c};
constexpr expr_choice_one_lit<A, N - 1, false> e_choice(bool c, T&& str, const A& a) {
return {str, a, c};
}
/*!
* @ingroup StrExprs
@ -1132,8 +1132,8 @@ inline constexpr auto e_choice(bool c, T&& str, const A& a) {
* @en @brief Overload e_choice when the second and third arguments are string literals.
*/
template<typename T, typename L, size_t N = const_lit<T>::Count, size_t M = const_lit_for<typename const_lit<T>::symb_type, L>::Count>
inline constexpr auto e_choice(bool c, T&& str_a, L&& str_b) {
return expr_choice_two_lit<typename const_lit<T>::symb_type, N -1, M - 1>{str_a, str_b, c};
constexpr expr_choice_two_lit<typename const_lit<T>::symb_type, N -1, M - 1> e_choice(bool c, T&& str_a, L&& str_b) {
return {str_a, str_b, c};
}
/*!
@ -1179,8 +1179,8 @@ inline constexpr auto e_choice(bool c, T&& str_a, L&& str_b) {
* which is not optimal and will reduce performance. (This example is tested in the "Build Full Func Name" benchmark)
*/
template<StrExpr A>
inline constexpr auto e_if(bool c, const A& a) {
return expr_if<A>{a, c};
constexpr expr_if<A> e_if(bool c, const A& a) {
return {a, c};
}
/*!
* @ingroup StrExprs
@ -1188,7 +1188,7 @@ inline constexpr auto e_if(bool c, const A& a) {
* @en @brief Overload e_if when the second argument is a string literal.
*/
template<typename T, size_t N = const_lit<T>::Count>
inline constexpr auto e_if(bool c, T&& str) {
constexpr auto e_if(bool c, T&& str) {
const typename const_lit<T>::symb_type empty[1] = {0};
return expr_choice_two_lit<typename const_lit<T>::symb_type, N - 1, 0>{str, empty, c};
}
@ -1225,8 +1225,8 @@ struct expr_stdstr {
* @en @brief Addition operator for char string expression and std::string.
*/
template<StrExprForType<u8s> A>
auto operator+(const A& a, const std::string& s) {
return strexprjoin_c<A, expr_stdstr<u8s, std::string>, true>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<u8s, std::string>, true> operator+(const A& a, const std::string& s) {
return {a, s};
}
/*!
@ -1235,8 +1235,8 @@ auto operator+(const A& a, const std::string& s) {
* @en @brief Addition operator for std::string and char string expression.
*/
template<StrExprForType<u8s> A>
auto operator+(const std::string& s, const A& a) {
return strexprjoin_c<A, expr_stdstr<u8s, std::string>, false>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<u8s, std::string>, false> operator+(const std::string& s, const A& a) {
return {a, s};
}
/*!
@ -1245,8 +1245,8 @@ auto operator+(const std::string& s, const A& a) {
* @en @brief Addition operator for char string expression and std::string_view.
*/
template<StrExprForType<u8s> A>
auto operator+(const A& a, const std::string_view& s) {
return strexprjoin_c<A, expr_stdstr<u8s, std::string_view>, true>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<u8s, std::string_view>, true> operator+(const A& a, const std::string_view& s) {
return {a, s};
}
/*!
@ -1255,8 +1255,8 @@ auto operator+(const A& a, const std::string_view& s) {
* @en @brief Addition operator for std::string_view and char string expression.
*/
template<StrExprForType<u8s> A>
auto operator+(const std::string_view& s, const A& a) {
return strexprjoin_c<A, expr_stdstr<u8s, std::string_view>, false>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<u8s, std::string_view>, false> operator+(const std::string_view& s, const A& a) {
return {a, s};
}
/*!
@ -1265,8 +1265,8 @@ auto operator+(const std::string_view& s, const A& a) {
* @en @brief Addition operator for wchar_t string expression and std::wstring.
*/
template<StrExprForType<uws> A>
auto operator+(const A& a, const std::wstring& s) {
return strexprjoin_c<A, expr_stdstr<uws, std::wstring>, true>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<uws, std::wstring>, true> operator+(const A& a, const std::wstring& s) {
return {a, s};
}
/*!
@ -1275,8 +1275,8 @@ auto operator+(const A& a, const std::wstring& s) {
* @en @brief Addition operator for std::wstring and wchar_t string expression.
*/
template<StrExprForType<uws> A>
auto operator+(const std::wstring& s, const A& a) {
return strexprjoin_c<A, expr_stdstr<uws, std::wstring>, false>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<uws, std::wstring>, false> operator+(const std::wstring& s, const A& a) {
return {a, s};
}
/*!
@ -1285,8 +1285,8 @@ auto operator+(const std::wstring& s, const A& a) {
* @en @brief Addition operator for wchar_t string expression and std::wstring_view.
*/
template<StrExprForType<uws> A>
auto operator+(const A& a, const std::wstring_view& s) {
return strexprjoin_c<A, expr_stdstr<uws, std::wstring_view>, true>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<uws, std::wstring_view>, true> operator+(const A& a, const std::wstring_view& s) {
return {a, s};
}
/*!
@ -1295,8 +1295,8 @@ auto operator+(const A& a, const std::wstring_view& s) {
* @en @brief Addition operator for std::wstring_view and wchar_t string expression.
*/
template<StrExprForType<uws> A>
auto operator+(const std::wstring_view& s, const A& a) {
return strexprjoin_c<A, expr_stdstr<uws, std::wstring_view>, false>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<uws, std::wstring_view>, false> operator+(const std::wstring_view& s, const A& a) {
return {a, s};
}
/*!
@ -1307,8 +1307,8 @@ auto operator+(const std::wstring_view& s, const A& a) {
* char32_t, depending on the compiler) and std::wstring.
*/
template<StrExprForType<wchar_type> A>
auto operator+(const A& a, const std::wstring& s) {
return strexprjoin_c<A, expr_stdstr<wchar_type, std::wstring>, true>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<wchar_type, std::wstring>, true> operator+(const A& a, const std::wstring& s) {
return {a, s};
}
/*!
@ -1319,8 +1319,8 @@ auto operator+(const A& a, const std::wstring& s) {
* (char16_t or char32_t, depending on the compiler).
*/
template<StrExprForType<wchar_type> A>
auto operator+(const std::wstring& s, const A& a) {
return strexprjoin_c<A, expr_stdstr<wchar_type, std::wstring>, false>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<wchar_type, std::wstring>, false> operator+(const std::wstring& s, const A& a) {
return {a, s};
}
/*!
@ -1331,8 +1331,8 @@ auto operator+(const std::wstring& s, const A& a) {
* char32_t, depending on the compiler) and std::wstring_view.
*/
template<StrExprForType<wchar_type> A>
auto operator+(const A& a, const std::wstring_view& s) {
return strexprjoin_c<A, expr_stdstr<wchar_type, std::wstring_view>, true>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<wchar_type, std::wstring_view>, true> operator+(const A& a, const std::wstring_view& s) {
return {a, s};
}
/*!
@ -1343,8 +1343,8 @@ auto operator+(const A& a, const std::wstring_view& s) {
* (char16_t or char32_t, depending on the compiler).
*/
template<StrExprForType<wchar_type> A>
auto operator+(const std::wstring_view& s, const A& a) {
return strexprjoin_c<A, expr_stdstr<wchar_type, std::wstring_view>, false>{a, s};
constexpr strexprjoin_c<A, expr_stdstr<wchar_type, std::wstring_view>, false> operator+(const std::wstring_view& s, const A& a) {
return {a, s};
}
}// namespace simstr

View File

@ -1,7 +1,7 @@
# simstr - String object and function library
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Version 1.3.0.
Version 1.3.1.
<span class="obfuscator"><a href="readme_ru.md">On Russian | По-русски</a></span>
@ -71,6 +71,8 @@ then the simstr approach will also be clear to you.
you can simply include the files in your project. Building also requires [simdutf](https://github.com/simdutf/simdutf) (when using CMake
it is downloaded automatically).
The library is included in [vcpkg](https://vcpkg.io), use as `orefkov-simstr`.
`simstr` requires a compiler of standard no lower than C++20 to work - concepts and std::format are used.
The work was tested under Windows on MSVC-19 and Clang-19, under Linux - on GCC-13 and Clang-21.
The work in WASM was also tested, built in Emscripten 4.0.6, Clang-21.

View File

@ -1,7 +1,7 @@
# simstr - библиотека строковых объектов и функций
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Версия 1.3.0.
Версия 1.3.1.
<span class="obfuscator"><a href="readme.md">On English | По-английски</a></span>
@ -71,10 +71,13 @@
можно просто включить файлы в свой проект. Для сборки также требуется [simdutf](https://github.com/simdutf/simdutf) (при использовании CMake
скачивается автоматически).
Библиотека включена в [vcpkg](https://vcpkg.io), подключается как `orefkov-simstr`.
Для работы `simstr` требуется компилятор стандарта не ниже С++20 - используются концепты и std::format.
Работа проверялась под Windows на MSVC-19 и Clang-19, под Linux - на GCC-13 и Clang-21.
Также проверялась работа в WASM, сборка в Emscripten 4.0.6, Clang-21.
## Удобная отладка
Вместе с библиотекой поставляются два файла, делающие просмотр simstr строковых объектов в отладчиках
более удобным.\

View File

@ -522,42 +522,42 @@ TEST(SimStr, IsAscii) {
}
TEST(SimStr, ChangeCase) {
EXPECT_EQ(ssa{"TEST"}.uppered_only_ascii<stringa>(), "TEST");
EXPECT_EQ(ssa{"Test"}.uppered_only_ascii<stringa>(), "TEST");
EXPECT_EQ(ssa{"TEST"}.upperred_only_ascii<stringa>(), "TEST");
EXPECT_EQ(ssa{"Test"}.upperred_only_ascii<stringa>(), "TEST");
EXPECT_EQ(ssa{"test"}.lowered_only_ascii<stringa>(), "test");
EXPECT_EQ(ssa{"Test"}.lowered_only_ascii<stringa>(), "test");
EXPECT_EQ(ssa{"TestПрОвЕрКа"}.uppered_only_ascii<stringa>(), "TESTПрОвЕрКа");
EXPECT_EQ(ssa{"TestПрОвЕрКа"}.upperred_only_ascii<stringa>(), "TESTПрОвЕрКа");
EXPECT_EQ(ssa{"TestПрОвЕрКа"}.lowered_only_ascii<stringa>(), "testПрОвЕрКа");
EXPECT_EQ(ssa{"TestПрОвЕрКа"}.uppered<stringa>(), "TESTПРОВЕРКА");
EXPECT_EQ(ssa{"TestПрОвЕрКа"}.upperred<stringa>(), "TESTПРОВЕРКА");
EXPECT_EQ(ssa{"tesTПрОвЕрКа"}.lowered<stringa>(), "testпроверка");
EXPECT_EQ(ssa{"TEST"}.uppered<stringa>(), "TEST");
EXPECT_EQ(ssa{"TEST"}.upperred<stringa>(), "TEST");
EXPECT_EQ(ssa{"test"}.lowered<stringa>(), "test");
EXPECT_EQ(ssa{"TESTПРОВЕРКА"}.uppered<stringa>(), "TESTПРОВЕРКА");
EXPECT_EQ(ssa{"TESTПРОВЕРКА"}.upperred<stringa>(), "TESTПРОВЕРКА");
EXPECT_EQ(ssa{"testпроверка"}.lowered<stringa>(), "testпроверка");
EXPECT_EQ(ssa{"tesTİiȺⱥȾⱦẞßΩФывAsd"}.lowered<stringa>(), "testiiⱥⱥⱦⱦßßωфывasd");
EXPECT_EQ(ssa{"testⱢɱⱮɽⱤWas"}.uppered<stringa>(), "TESTⱢⱮⱮⱤⱤWAS");
EXPECT_EQ(ssa{"testⱢɱⱮɽⱤWas"}.upperred<stringa>(), "TESTⱢⱮⱮⱤⱤWAS");
EXPECT_EQ(ssu{u"TEST"}.uppered_only_ascii<stringu>(), u"TEST");
EXPECT_EQ(ssu{u"TEST"}.upperred_only_ascii<stringu>(), u"TEST");
EXPECT_EQ(ssu{u"test"}.lowered_only_ascii<stringu>(), u"test");
EXPECT_EQ(ssu{u"Test"}.uppered_only_ascii<stringu>(), u"TEST");
EXPECT_EQ(ssu{u"Test"}.upperred_only_ascii<stringu>(), u"TEST");
EXPECT_EQ(ssu{u"Test"}.lowered_only_ascii<stringu>(), u"test");
EXPECT_EQ(ssu{u"TestПрОвЕрКа"}.uppered_only_ascii<stringu>(), u"TESTПрОвЕрКа");
EXPECT_EQ(ssu{u"TestПрОвЕрКа"}.upperred_only_ascii<stringu>(), u"TESTПрОвЕрКа");
EXPECT_EQ(ssu{u"TestПрОвЕрКа"}.lowered_only_ascii<stringu>(), u"testПрОвЕрКа");
EXPECT_EQ(ssu{u"TestПрОвЕрКа"}.uppered<stringu>(), u"TESTПРОВЕРКА");
EXPECT_EQ(ssu{u"TestПрОвЕрКа"}.upperred<stringu>(), u"TESTПРОВЕРКА");
EXPECT_EQ(ssu{u"tesTПрОвЕрКа"}.lowered<stringu>(), u"testпроверка");
EXPECT_EQ(ssu{u"tesTİiȺⱥȾⱦẞßΩФывAsd"}.lowered<stringu>(), u"testiiⱥⱥⱦⱦßßωфывasd");
EXPECT_EQ(ssu{u"testⱢɱⱮɽⱤWas"}.uppered<stringu>(), u"TESTⱢⱮⱮⱤⱤWAS");
EXPECT_EQ(ssu{u"testⱢɱⱮɽⱤWas"}.upperred<stringu>(), u"TESTⱢⱮⱮⱤⱤWAS");
EXPECT_EQ(ssw{L"TEST"}.uppered_only_ascii<stringw>(), L"TEST");
EXPECT_EQ(ssw{L"TEST"}.upperred_only_ascii<stringw>(), L"TEST");
EXPECT_EQ(ssw{L"test"}.lowered_only_ascii<stringw>(), L"test");
EXPECT_EQ(ssw{L"Test"}.uppered_only_ascii<stringw>(), L"TEST");
EXPECT_EQ(ssw{L"Test"}.upperred_only_ascii<stringw>(), L"TEST");
EXPECT_EQ(ssw{L"Test"}.lowered_only_ascii<stringw>(), L"test");
EXPECT_EQ(ssw{L"TestПрОвЕрКа"}.uppered_only_ascii<stringw>(), L"TESTПрОвЕрКа");
EXPECT_EQ(ssw{L"TestПрОвЕрКа"}.upperred_only_ascii<stringw>(), L"TESTПрОвЕрКа");
EXPECT_EQ(ssw{L"TestПрОвЕрКа"}.lowered_only_ascii<stringw>(), L"testПрОвЕрКа");
EXPECT_EQ(ssw{L"TestПрОвЕрКа"}.uppered<stringw>(), L"TESTПРОВЕРКА");
EXPECT_EQ(ssw{L"TestПрОвЕрКа"}.upperred<stringw>(), L"TESTПРОВЕРКА");
EXPECT_EQ(ssw{L"tesTПрОвЕрКа"}.lowered<stringw>(), L"testпроверка");
EXPECT_EQ(ssw{L"tesTİiȺⱥȾⱦẞßΩФывAsd"}.lowered<stringw>(), L"testiiⱥⱥⱦⱦßßωфывasd");
EXPECT_EQ(ssw{L"testⱢɱⱮɽⱤWas"}.uppered<stringw>(), L"TESTⱢⱮⱮⱤⱤWAS");
EXPECT_EQ(ssw{L"testⱢɱⱮɽⱤWas"}.upperred<stringw>(), L"TESTⱢⱮⱮⱤⱤWAS");
}
TEST(SimStr, Replace) {
@ -1775,4 +1775,13 @@ TEST(SimStr, ExprRepeat) {
EXPECT_EQ(lstringa<40>{e_repeat("aa"_ss + t + "_", 3)}, "aa1_aa1_aa1_");
}
TEST(SimStr, Constexpr) {
constexpr ssa tt = " asd "_ss.trimmed();
static_assert(tt == "asd");
constexpr stringa aa{"asd"};
static_assert(aa == "asd");
static_assert(aa.length() == 3);
constexpr stringa bb = "";
}
} // namespace simstr::tests