Всё хорошо, но формул для вычисления средних очень много. Вот у тебя, Хабра читатель, есть машина? Она показывает среднюю скорость, средний расход? А как она это делает, никогда не задумывался?
strict aliasing, -Wstrict-aliasing=3 вам в помощь, правда при -O2 и выше.
object lifetime violations (ваш std::launder тут спасает, но ваши примеры плохи), тут санитайзеры в помощь.
type-punning (классика UB, особенно через union или reinterpret_cast между несовместимыми структурами)
alignment issues (на x86 пофиг, но я на продакшене словил от более нового gcc movdqu вместо movdqa, хотя они стоят одинаковое количество тактов) -Wcast-align в помощь
struct Widget {
const int id;
std::string name;
};
alignas(Widget) std::byte buf[sizeof(Widget)];
auto *p = new (buf) Widget{1, "old"};
p->~Widget();
new (buf) Widget{2, "new"};
std::cout << p->id << '\n'; // UB: p указывает на покойника
меняем на
...
p->~Widget();
auto *q = new (buf) Widget{2, "new"};
std::cout << q->id << '\n'; // И здесь всё хорошо
Это простой "use after destruction" или "stale pointer", пока ничего необычного.
Тема оптимизации C++ шаблонов не раскрыта. Необходимо было рассказать, как в библиотеках типа STL, Boost, ... с этим борются, а там есть несколько способов.
Как реализованы разные hash-map. Статическая таблица вызовов функций, инициализируется для каждого типа.
struct RawTableOps {
size_t (*hash)(const void* key);
...
class RawTable {
private:
const RawTableOps* ops_;
...
template<class K, class V,
class H = std::hash<K>,
class E = std::equal_to<K>>
class HashMap : private RawTable {
public:
HashMap() : RawTable(&ops) {}
...
Как реализованы разные вектора. Все тяжёлые алгоритмы работают в базовом классе, приходится гонять через void* и size_t всё, и шаблонная обёртка преобразует к нужному типу.
class _Vec_common {
protected:
...
template<class T, class Alloc = std::allocator<T>>
class tiny_vector : private _Vec_common {
...
private:
T* data_{}; size_t size_{}, cap_{};
Про использование std::function добавлю. В C++26 появился std::function_ref (P0792) - не владеет callable-объектом, а держит reference-wrapper + тривиальный function-pointer thunk. Его размер - два указателя, ни аллокаций, ни type-erasure-state.
В коде ниже не может произойти RVO, потому что возвращаемый тип не соответствует тому, что мы возвращаем. Нам нужно построить std::optional объект, и тут вызовется конструктор перемещения, что не так плохо, но не RVO. В версией с const действительно конструктор перемещения не может быть использован, и будет конструктор копирования.
Видел такое. Типичный "фикс" - это sleep запихнуть. В дебаге всё работает, потому что в этом месте какой-то print в Log есть, и вся синхронизация тредов работает хорошо.
А теперь признайтесь - сколько у вас в коде sleep таких зарыто?
Напомнило, на одном серьёзном проекте надо было тестировать ещё и пользовательскую документацию. QA-девочка с задачей справлялись сильно лучше мальчиков, потому что они реально выполняли команды буквы в букву "cd ..." и такого каталога нет ;-) а мальчик даже не будет такое проверять, всё же норм.
С таким ручным layout вы легко можете нарваться на операцию чтения/записи по невыровненному адресу. А если вы ешё руками начнёте делать reinterpret_cast, то проблемы type punning, strict aliasing, ...
Хорошо что вы про std::bitcast упомянули, но статья была бы намного интереснее, если рассказали про std::launder, std::start_lifetime_as, etc. Иначе тема не раскрыта.
А каким тут боком тэг Си++ тут стоит? Нам, плюсовикам, немного чуждый ваш мир фронт-энда. Хотя, прочитав статью, нашёл нечто похожее, что делали в играл, которые на С/С++, чтобы грузить уровень не целиком, ожидая полминуты, а по ходу пьесы.
А как мне асинхронищину на Boost::ASIO переписать на этот новый "stdexec"? Мне никто не предлагает pool/epoll/uring/.... Наверное, пул-тредов для файловых операций, будет интересно, но сетевые вещи как?
Причём здесь C++, C#?
Как происходит автодополнение, через clangd? Я вот такой плагин знаю https://vimawesome.com/plugin/vim-clangd
Ещё хочется хочется в реалтайм видеть сообщения от Clang-Tidy. Говорят надо использовать плагин ALE (Asynchronous Lint Engine).
Спасибо за статью. Было бы ещё здорово сравнить с
std::pmr
контейнерами cmonotonic_buffer_resource
.a + (b - a) /2
тоже переполняется, еслиb > a
.Если вам по классике, тогда вот так
auto avg = (a & b) + ((a ^ b) >> 1);
Всё хорошо, но формул для вычисления средних очень много.
Вот у тебя, Хабра читатель, есть машина? Она показывает среднюю скорость, средний расход? А как она это делает, никогда не задумывался?
Вы главное забыли. Статья должна была быть про
strict aliasing, -Wstrict-aliasing=3 вам в помощь, правда при -O2 и выше.
object lifetime violations (ваш std::launder тут спасает, но ваши примеры плохи), тут санитайзеры в помощь.
type-punning (классика UB, особенно через union или reinterpret_cast между несовместимыми структурами)
alignment issues (на x86 пофиг, но я на продакшене словил от более нового gcc movdqu вместо movdqa, хотя они стоят одинаковое количество тактов) -Wcast-align в помощь
Ваш код
меняем на
Это простой "use after destruction" или "stale pointer", пока ничего необычного.
Меня такое на интервью спросили кстати.
Тема оптимизации C++ шаблонов не раскрыта. Необходимо было рассказать, как в библиотеках типа STL, Boost, ... с этим борются, а там есть несколько способов.
Как реализованы разные hash-map. Статическая таблица вызовов функций, инициализируется для каждого типа.
Как реализованы разные вектора. Все тяжёлые алгоритмы работают в базовом классе, приходится гонять через void* и size_t всё, и шаблонная обёртка преобразует к нужному типу.
Про использование
std::function
добавлю. В C++26 появилсяstd::function_ref
(P0792) - не владеет callable-объектом, а держит reference-wrapper + тривиальный function-pointer thunk. Его размер - два указателя, ни аллокаций, ни type-erasure-state.В коде ниже не может произойти RVO, потому что возвращаемый тип не соответствует тому, что мы возвращаем. Нам нужно построить std::optional объект, и тут вызовется конструктор перемещения, что не так плохо, но не RVO. В версией с const действительно конструктор перемещения не может быть использован, и будет конструктор копирования.
Чтобы в вашем примере случился RVO надо написать сразу вот так
P.S. В этом, кстати проблема современных std::optional, std:expected и им подобных.
Таки причём тут C++? Опять хештеги путаете?
Задача на собеседовании - что выдаст на экран эта программа ;-)
Но это, конечно, обфускация и ненормативное программирование.
Видел такое. Типичный "фикс" - это sleep запихнуть. В дебаге всё работает, потому что в этом месте какой-то print в Log есть, и вся синхронизация тредов работает хорошо.
А теперь признайтесь - сколько у вас в коде sleep таких зарыто?
Напомнило, на одном серьёзном проекте надо было тестировать ещё и пользовательскую документацию. QA-девочка с задачей справлялись сильно лучше мальчиков, потому что они реально выполняли команды буквы в букву "cd ..." и такого каталога нет ;-) а мальчик даже не будет такое проверять, всё же норм.
С таким ручным layout вы легко можете нарваться на операцию чтения/записи по невыровненному адресу. А если вы ешё руками начнёте делать reinterpret_cast, то проблемы type punning, strict aliasing, ...
Хорошо что вы про std::bitcast упомянули, но статья была бы намного интереснее, если рассказали про std::launder, std::start_lifetime_as, etc. Иначе тема не раскрыта.
А каким тут боком тэг Си++ тут стоит?
Нам, плюсовикам, немного чуждый ваш мир фронт-энда. Хотя, прочитав статью, нашёл нечто похожее, что делали в играл, которые на С/С++, чтобы грузить уровень не целиком, ожидая полминуты, а по ходу пьесы.
А как мне асинхронищину на Boost::ASIO переписать на этот новый "stdexec"? Мне никто не предлагает pool/epoll/uring/.... Наверное, пул-тредов для файловых операций, будет интересно, но сетевые вещи как?
Стандарт тупо ещё не умеет работать с юникодом, с файлами в разных кодировках. Потом уже будет сеть и всё остальное.
Тема C++17 не раскрыта. Пост про трудовые будни геймдева, и про плюсы тут только, что долго собирается. Пишите на голом Си.