Мелкие правки в тестах и документации.

This commit is contained in:
Aleksandr Orefkov 2025-08-08 12:15:24 +03:00
parent 0cc4fad6e6
commit a4eb796220
3 changed files with 26 additions and 19 deletions

View File

@ -43,6 +43,7 @@
для строк — по меньшей мере может приводить к уменьшению производительности, а также нарушает принцип «не плати за то, для строк — по меньшей мере может приводить к уменьшению производительности, а также нарушает принцип «не плати за то,
чем не пользуешься». Почему же «строки строкам рознь» и почему нам мало одного типа для строки, рассмотрим как раз далее. чем не пользуешься». Почему же «строки строкам рознь» и почему нам мало одного типа для строки, рассмотрим как раз далее.
### Ресурсы
И следующий вопрос, возникающий со строками — это владение ресурсами. И следующий вопрос, возникающий со строками — это владение ресурсами.
Практически каждый крупный фреймворк решал эту задачу самостоятельно, изобретая свои велосипеды. Практически каждый крупный фреймворк решал эту задачу самостоятельно, изобретая свои велосипеды.
У нас есть `std::string`, в QT у нас `QString`, в MFC - `CString`, в ATL - `CAtlString`, свои строки есть в Folly, У нас есть `std::string`, в QT у нас `QString`, в MFC - `CString`, в ATL - `CAtlString`, свои строки есть в Folly,
@ -50,10 +51,12 @@
Многие из этих реализаций в аспекте управления ресурсами для улучшения производительности использовали подход Многие из этих реализаций в аспекте управления ресурсами для улучшения производительности использовали подход
**COW** “Copy On Write”. При этом объект строки ссылался на некий разделяемый между несколькими объектами буфер с символами **COW** “Copy On Write”. При этом объект строки ссылался на некий разделяемый между несколькими объектами буфер с символами
строки и счётчиком ссылок на этот буфер, что позволяло быстро создавать копию строки. строки и счётчиком ссылок на этот буфер, что позволяло быстро создавать копию строки, а реально копировать символы только
при её модификации.
Но все они совпадали в одном — строка всегда предполагалась мутабельной, то есть что мы можем модифицировать символы в буфере строки. Но все они совпадали в одном — строка всегда предполагалась мутабельной, то есть что мы можем модифицировать символы в буфере строки.
### Мутабельность / иммутабельность
Из-за этого подход **COW** умер к С++11: при каждой операции, могущей модифицировать символы строки приходилось проверять, Из-за этого подход **COW** умер к С++11: при каждой операции, могущей модифицировать символы строки приходилось проверять,
не ссылаемся ли мы на разделяемый буфер и если да, то копировать символы в другой буфер. не ссылаемся ли мы на разделяемый буфер и если да, то копировать символы в другой буфер.
В многопоточной же среде потом ещё и проверять, не надо ли теперь освобождать старый буфер, и естественно всё это обмазавшись В многопоточной же среде потом ещё и проверять, не надо ли теперь освобождать старый буфер, и естественно всё это обмазавшись
@ -65,7 +68,8 @@
“Small String Optimization”, когда объект строки содержит внутри себя небольшой буфер и символы коротких строк “Small String Optimization”, когда объект строки содержит внутри себя небольшой буфер и символы коротких строк
располагаются прямо в нём. располагаются прямо в нём.
Но это уже зависит от реализации: в одних библиотеках помещают в объект строки до 15 байт, в некоторых до 23. Но это уже зависит от реализации: в одних библиотеках помещают в объект строки до 15 байт, в некоторых до 23.
Однако эта оптимизация тоже палка о двух концах, и может в различных реализациях сделать строку "non trivial moveable". Однако эта оптимизация тоже палка о двух концах, и может в различных реализациях усложнить перемещение строки - если она хранит
указатель на свой внутренний буфер, его придётся корректировать.
А без COW мутабельность строк приводит к тому, что любая инициализация объекта строки приводит к копированию байтов. А без COW мутабельность строк приводит к тому, что любая инициализация объекта строки приводит к копированию байтов.
Посмотрим такой код: Посмотрим такой код:
@ -108,6 +112,7 @@
скопировать его в `std::string`. А раз нужен `std::string`, то и параметром функции оптимальнее cделать `const std::string&` скопировать его в `std::string`. А раз нужен `std::string`, то и параметром функции оптимальнее cделать `const std::string&`
и далее по цепочке, все параметры вновь станут `const std::string&`. и далее по цепочке, все параметры вновь станут `const std::string&`.
### Конкатенация строк
Далее, после инициализации строки, самая частая мутабельная операция с ними, скорее всего конкатенация строк, либо в виде просто Далее, после инициализации строки, самая частая мутабельная операция с ними, скорее всего конкатенация строк, либо в виде просто
сложения строк, либо добавления строки к строке. И именно она легко может вызывать как неоптимальную производительность при неграмотном сложения строк, либо добавления строки к строке. И именно она легко может вызывать как неоптимальную производительность при неграмотном
использовании, так и оверхед по памяти, даже при грамотном использовании. использовании, так и оверхед по памяти, даже при грамотном использовании.
@ -241,7 +246,7 @@
#### Класс sstring (shared string). #### Класс sstring (shared string).
(simstr::sstring) (simstr::sstring)
Класс, могущий хранить иммутабельную строку. Класс, умеющий хранить иммутабельную строку.
То есть ему можно присвоить некую строку только целиком, модифицировать символы строки нельзя. То есть ему можно присвоить некую строку только целиком, модифицировать символы строки нельзя.
Владеет строкой, управляет памятью для символов строки. Владеет строкой, управляет памятью для символов строки.
@ -397,7 +402,8 @@
} }
``` ```
В этом примере вы наверняка заметили, как конкатенируются строки и задались вопросом — как же при двух сложениях длина всего результата считалась всего один раз, чтобы выделить необходимое место сразу, без промежуточных буферов? В этом примере вы наверняка заметили, как конкатенируются строки и задались вопросом — как же при двух сложениях считалась
длина всего результата, чтобы выделить необходимое место сразу за один раз, без промежуточных буферов?
Ответ на этот вопрос: Ответ на этот вопрос:
@ -433,12 +439,12 @@
    constexpr symb_type* place(symb_type* p) const noexcept { return b.place(a.place(p)); }     constexpr symb_type* place(symb_type* p) const noexcept { return b.place(a.place(p)); }
}; };
``` ```
Таким образом, операция сложения строковых выражений создает объект, также являющийся строковым выражением,
Таким образом, операция сложения нескольких строковых выражений создает объект, также являющийся строковым выражением, к которому также может быть применена следующая операция сложения, и который рекурсивно хранит ссылки на слагаемые части,
который рекурсивно хранит ссылки на слагаемые части, каждая из которых знает свой размер и умеет размещать себя каждая из которых знает свой размер и умеет размещать себя в буфере результата. И так далее, к каждому получаемому
в буфере результата. А чтобы получить готовый результат сложения, это выражение надо "материализовать", то есть строковому выражению можно снова применить `operator +`, формируя цепочку из нескольких строковых выражений,
присвоить любому владеющему стровому объекту, который выделит всю память под результат за один раз и запустит и в итоге "материализовать" последний получившийся объект, который сначала посчитает размер всей общей памяти для
размещение символов в этой памяти. конечного результата, а затем разместит вложенные подвыражения в один буфер.
Все строковые типы библиотеки сами являются строковыми выражениями, то есть могут служить слагаемыми в конкатенациях Все строковые типы библиотеки сами являются строковыми выражениями, то есть могут служить слагаемыми в конкатенациях
строковых выражений. строковых выражений.

View File

@ -84,6 +84,7 @@ Windows и Linux (в WSL), с использованием компилятор
## Примеры использования ## Примеры использования
Пока отдельных примеров использования не подготовлено, можно посмотреть тексты [тестов](tests/test_str.cpp), Пока отдельных примеров использования не подготовлено, можно посмотреть тексты [тестов](tests/test_str.cpp),
[бенчмарков](bench/bench_str.cpp), и [утилиты подготовки html](bench/process_result.cpp) из результатов бенчмарков. [бенчмарков](bench/bench_str.cpp), и [утилиты подготовки html](bench/process_result.cpp) из результатов бенчмарков.
Также simstr используется в моём проекте [v8sqlite](https://github.com/orefkov/v8sqlite)
## Сгенерированная документация ## Сгенерированная документация
[Находится здесь](https://snegopat.ru/simstr/docs/) [Находится здесь](https://snegopat.ru/simstr/docs/)

View File

@ -764,9 +764,9 @@ TEST(SimStr, CreateSstringLong) {
} }
TEST(SimStr, CreateSstringStrExpr) { TEST(SimStr, CreateSstringStrExpr) {
EXPECT_EQ(stringa{eea + "test" + 101 + e_c(3, 'a')}, "test101aaa"); EXPECT_EQ(stringa{"test"_ss + 101 + e_c(3, 'a')}, "test101aaa");
EXPECT_EQ(stringu{eeu + u"test" + 1234 + e_c(3, u'a')}, u"test1234aaa"); EXPECT_EQ(stringu{u"test"_ss + 1234 + e_c(3, u'a')}, u"test1234aaa");
EXPECT_EQ(stringw{eew + L"test" + 12345 + e_c(3, L'a')}, L"test12345aaa"); EXPECT_EQ(stringw{L"test"_ss + 12345 + e_c(3, L'a')}, L"test12345aaa");
} }
TEST(SimStr, CreateSstringReplace) { TEST(SimStr, CreateSstringReplace) {
@ -788,8 +788,8 @@ TEST(SimStr, AssignSstring) {
EXPECT_EQ(test = test, "test"); EXPECT_EQ(test = test, "test");
EXPECT_EQ(test = "next", "next"); EXPECT_EQ(test = "next", "next");
EXPECT_EQ(test = ssa{"other"}, "other"); EXPECT_EQ(test = ssa{"other"}, "other");
EXPECT_EQ(test = stringa{eea + "trtr" + 10}, "trtr10"); EXPECT_EQ(test = stringa{"trtr"_ss + 10}, "trtr10");
EXPECT_EQ(test = eea + "trtr" + 20, "trtr20"); EXPECT_EQ(test = "trtr"_ss + 20, "trtr20");
EXPECT_EQ(test = test(2), "tr20"); EXPECT_EQ(test = test(2), "tr20");
EXPECT_EQ(test = lstringa<10>{"func"}, "func"); EXPECT_EQ(test = lstringa<10>{"func"}, "func");
EXPECT_EQ(test = lstringsa<10>{"func"}, "func"); EXPECT_EQ(test = lstringsa<10>{"func"}, "func");
@ -1025,9 +1025,9 @@ TEST(SimStr, LStrAppend) {
EXPECT_EQ(lstringa<20>{"test"}.append("ing"), "testing"); EXPECT_EQ(lstringa<20>{"test"}.append("ing"), "testing");
EXPECT_EQ(lstringa<20>{"test"}.append("ing"_ss + 10), "testing10"); EXPECT_EQ(lstringa<20>{"test"}.append("ing"_ss + 10), "testing10");
EXPECT_EQ(lstringa<20>{"test"}.append_in(3, "ing"), "tesing"); EXPECT_EQ(lstringa<20>{"test"}.append_in(3, "ing"), "tesing");
EXPECT_EQ(lstringa<20>{"test"}.append_in(3, eea + "ing" + 10), "tesing10"); EXPECT_EQ(lstringa<20>{"test"}.append_in(3, "ing"_ss + 10), "tesing10");
EXPECT_EQ(lstringa<20>{"test"} += "ing", "testing"); EXPECT_EQ(lstringa<20>{"test"} += "ing", "testing");
EXPECT_EQ(lstringa<20>{"test"} += eea + "ing" + 10, "testing10"); EXPECT_EQ(lstringa<20>{"test"} += "ing"_ss + 10, "testing10");
} }
TEST(SimStr, LStrChange) { TEST(SimStr, LStrChange) {
@ -1287,7 +1287,7 @@ TEST(SimStr, LStrFormat) {
} }
} }
TEST(SimStr, LStrFormatExpressions) { TEST(SimStr, LStrJoinAndExpressions) {
lstringa<100> buffer; lstringa<100> buffer;
buffer = e_join<true>(std::vector<ssa>{}, "<>"); buffer = e_join<true>(std::vector<ssa>{}, "<>");
EXPECT_EQ(buffer, ""); EXPECT_EQ(buffer, "");
@ -1524,7 +1524,7 @@ struct bbbb {
res = "my_str: "_ss + field + '\n'; res = "my_str: "_ss + field + '\n';
auto strs = get_strings(); auto strs = get_strings();
for (const auto& s : strs) { for (const auto& s : strs) {
res += e_repl(s, "f", "--") + e_choice(&s != &strs.back(), "\n"_ss, eea); res += e_repl(s, "f", "--") + e_if(&s != &strs.back(), "\n");
} }
} }