mirror of https://github.com/orefkov/simstr.git
Мелкие правки в тестах и документации.
This commit is contained in:
parent
0cc4fad6e6
commit
a4eb796220
|
|
@ -43,6 +43,7 @@
|
|||
для строк — по меньшей мере может приводить к уменьшению производительности, а также нарушает принцип «не плати за то,
|
||||
чем не пользуешься». Почему же «строки строкам рознь» и почему нам мало одного типа для строки, рассмотрим как раз далее.
|
||||
|
||||
### Ресурсы
|
||||
И следующий вопрос, возникающий со строками — это владение ресурсами.
|
||||
Практически каждый крупный фреймворк решал эту задачу самостоятельно, изобретая свои велосипеды.
|
||||
У нас есть `std::string`, в QT у нас `QString`, в MFC - `CString`, в ATL - `CAtlString`, свои строки есть в Folly,
|
||||
|
|
@ -50,10 +51,12 @@
|
|||
|
||||
Многие из этих реализаций в аспекте управления ресурсами для улучшения производительности использовали подход
|
||||
**COW** – “Copy On Write”. При этом объект строки ссылался на некий разделяемый между несколькими объектами буфер с символами
|
||||
строки и счётчиком ссылок на этот буфер, что позволяло быстро создавать копию строки.
|
||||
строки и счётчиком ссылок на этот буфер, что позволяло быстро создавать копию строки, а реально копировать символы только
|
||||
при её модификации.
|
||||
|
||||
Но все они совпадали в одном — строка всегда предполагалась мутабельной, то есть что мы можем модифицировать символы в буфере строки.
|
||||
|
||||
### Мутабельность / иммутабельность
|
||||
Из-за этого подход **COW** умер к С++11: при каждой операции, могущей модифицировать символы строки приходилось проверять,
|
||||
не ссылаемся ли мы на разделяемый буфер и если да, то копировать символы в другой буфер.
|
||||
В многопоточной же среде потом ещё и проверять, не надо ли теперь освобождать старый буфер, и естественно всё это обмазавшись
|
||||
|
|
@ -65,7 +68,8 @@
|
|||
“Small String Optimization”, когда объект строки содержит внутри себя небольшой буфер и символы коротких строк
|
||||
располагаются прямо в нём.
|
||||
Но это уже зависит от реализации: в одних библиотеках помещают в объект строки до 15 байт, в некоторых до 23.
|
||||
Однако эта оптимизация тоже палка о двух концах, и может в различных реализациях сделать строку "non trivial moveable".
|
||||
Однако эта оптимизация тоже палка о двух концах, и может в различных реализациях усложнить перемещение строки - если она хранит
|
||||
указатель на свой внутренний буфер, его придётся корректировать.
|
||||
|
||||
А без COW мутабельность строк приводит к тому, что любая инициализация объекта строки приводит к копированию байтов.
|
||||
Посмотрим такой код:
|
||||
|
|
@ -108,6 +112,7 @@
|
|||
скопировать его в `std::string`. А раз нужен `std::string`, то и параметром функции оптимальнее cделать `const std::string&`
|
||||
и далее по цепочке, все параметры вновь станут `const std::string&`.
|
||||
|
||||
### Конкатенация строк
|
||||
Далее, после инициализации строки, самая частая мутабельная операция с ними, скорее всего конкатенация строк, либо в виде просто
|
||||
сложения строк, либо добавления строки к строке. И именно она легко может вызывать как неоптимальную производительность при неграмотном
|
||||
использовании, так и оверхед по памяти, даже при грамотном использовании.
|
||||
|
|
@ -241,7 +246,7 @@
|
|||
#### Класс sstring (shared string).
|
||||
(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)); }
|
||||
};
|
||||
```
|
||||
|
||||
Таким образом, операция сложения нескольких строковых выражений создает объект, также являющийся строковым выражением,
|
||||
который рекурсивно хранит ссылки на слагаемые части, каждая из которых знает свой размер и умеет размещать себя
|
||||
в буфере результата. А чтобы получить готовый результат сложения, это выражение надо "материализовать", то есть
|
||||
присвоить любому владеющему стровому объекту, который выделит всю память под результат за один раз и запустит
|
||||
размещение символов в этой памяти.
|
||||
Таким образом, операция сложения строковых выражений создает объект, также являющийся строковым выражением,
|
||||
к которому также может быть применена следующая операция сложения, и который рекурсивно хранит ссылки на слагаемые части,
|
||||
каждая из которых знает свой размер и умеет размещать себя в буфере результата. И так далее, к каждому получаемому
|
||||
строковому выражению можно снова применить `operator +`, формируя цепочку из нескольких строковых выражений,
|
||||
и в итоге "материализовать" последний получившийся объект, который сначала посчитает размер всей общей памяти для
|
||||
конечного результата, а затем разместит вложенные подвыражения в один буфер.
|
||||
|
||||
Все строковые типы библиотеки сами являются строковыми выражениями, то есть могут служить слагаемыми в конкатенациях
|
||||
строковых выражений.
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ Windows и Linux (в WSL), с использованием компилятор
|
|||
## Примеры использования
|
||||
Пока отдельных примеров использования не подготовлено, можно посмотреть тексты [тестов](tests/test_str.cpp),
|
||||
[бенчмарков](bench/bench_str.cpp), и [утилиты подготовки html](bench/process_result.cpp) из результатов бенчмарков.
|
||||
Также simstr используется в моём проекте [v8sqlite](https://github.com/orefkov/v8sqlite)
|
||||
|
||||
## Сгенерированная документация
|
||||
[Находится здесь](https://snegopat.ru/simstr/docs/)
|
||||
|
|
|
|||
|
|
@ -764,9 +764,9 @@ TEST(SimStr, CreateSstringLong) {
|
|||
}
|
||||
|
||||
TEST(SimStr, CreateSstringStrExpr) {
|
||||
EXPECT_EQ(stringa{eea + "test" + 101 + e_c(3, 'a')}, "test101aaa");
|
||||
EXPECT_EQ(stringu{eeu + u"test" + 1234 + e_c(3, u'a')}, u"test1234aaa");
|
||||
EXPECT_EQ(stringw{eew + L"test" + 12345 + e_c(3, L'a')}, L"test12345aaa");
|
||||
EXPECT_EQ(stringa{"test"_ss + 101 + e_c(3, 'a')}, "test101aaa");
|
||||
EXPECT_EQ(stringu{u"test"_ss + 1234 + e_c(3, u'a')}, u"test1234aaa");
|
||||
EXPECT_EQ(stringw{L"test"_ss + 12345 + e_c(3, L'a')}, L"test12345aaa");
|
||||
}
|
||||
|
||||
TEST(SimStr, CreateSstringReplace) {
|
||||
|
|
@ -788,8 +788,8 @@ TEST(SimStr, AssignSstring) {
|
|||
EXPECT_EQ(test = test, "test");
|
||||
EXPECT_EQ(test = "next", "next");
|
||||
EXPECT_EQ(test = ssa{"other"}, "other");
|
||||
EXPECT_EQ(test = stringa{eea + "trtr" + 10}, "trtr10");
|
||||
EXPECT_EQ(test = eea + "trtr" + 20, "trtr20");
|
||||
EXPECT_EQ(test = stringa{"trtr"_ss + 10}, "trtr10");
|
||||
EXPECT_EQ(test = "trtr"_ss + 20, "trtr20");
|
||||
EXPECT_EQ(test = test(2), "tr20");
|
||||
EXPECT_EQ(test = lstringa<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"_ss + 10), "testing10");
|
||||
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"} += eea + "ing" + 10, "testing10");
|
||||
EXPECT_EQ(lstringa<20>{"test"} += "ing"_ss + 10, "testing10");
|
||||
}
|
||||
|
||||
TEST(SimStr, LStrChange) {
|
||||
|
|
@ -1287,7 +1287,7 @@ TEST(SimStr, LStrFormat) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(SimStr, LStrFormatExpressions) {
|
||||
TEST(SimStr, LStrJoinAndExpressions) {
|
||||
lstringa<100> buffer;
|
||||
buffer = e_join<true>(std::vector<ssa>{}, "<>");
|
||||
EXPECT_EQ(buffer, "");
|
||||
|
|
@ -1524,7 +1524,7 @@ struct bbbb {
|
|||
res = "my_str: "_ss + field + '\n';
|
||||
auto strs = get_strings();
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue