Pull to refresh

Comments 37

Обратите внимание, что цикл for не выполняет никакой работы, но изза вынесенной за пределы цикла переменной i у неё валидное значение 0. Выполняется апдейт только первого звука в массиве.

Разве? Цикл выполняет работу: неэффективно меняет i с 0 на size.
Так что последующее внецикловое sounds[i] даёт sounds[size] (UB), а не sounds[0].

Да, спасибо что поправили, так выглядит. Помню дебажил этот блок, он работал только в релизе, компилятор выкидывал оба цикла и оставлял только вызов первого элемента в итоге. Поправил описание

  int i, size = sounds.size();
  ...
  for(i = 0; i < size; ++i); {
    sounds[i].pre_update(dt);
  }

а почему этот цикл выкидывается?

Мы с коллегами так и не поняли, попытки поворошить код в этом месте или изменить флаги приводили к нормальной работе. Компилятор clang 7.0.19 для плойки. Но конкретно такой код работал с ошибками

хм, а разве он выкидывается? sounds[i].pre_update(dt); исполнится же один раз.

Потому что цикл здесь - это вот эта часть: for(i = 0; i < size; ++i);, а компилятор видит, что никаких сайд эффектов у неё нет. Может быть заменен на простое присваивание в i.

я так понял что выкидывается и "мнимое" тело цикла, которое после ";". Оно то выполнится один раз

Ну я так понял, что нет - выкидывался именно цикл, а то что после ; - это уже не цикл, апдейт одного-то звука выполнялся и он проигрывался.

Да, забавно, тест на внимательность не прошел. Интересно статик анализаторы на такое ругнутся ведь наверное...

Как ни странно, но лучше всего с ним бы справился банальный автоформаттер типа clang-format, который на более-менее крупном проекте ИМХО обязательно должен быть.

Автоформатер и в этой ошибке бы помог:

if ( (enemy =! HumanType::Friend)

стало бы как то так:

if ( (enemy = !HumanType::Friend)

было бы проще увидеть.

Может быть заменен на простое присваивание в i.

Тогда опять же, было бы sounds[size], потому что i == size, а автор утверждает, что выходило sounds[0]. Как оно может выходить, я так и не понял. Для этого надо, чтобы for(i = 0 осталось, а ++i) было выкинуто. Потому что без цикла i вообще не инициализировано.

Ну когда i == size, то там уже идет выход за границы этого sounds, а это UB, и кто его знает, во что превратит компилятор этот UB в конкретном случае.

Майкрософт сначала просто морозились и реджектили билды, потом прислали комплайнс на забытые сенситив данные. Какие блин такие забытые?

Было же нормально, потом русский пошёл вразнос.

Большинство вышеперечисленных багов - это код, написанный на C в файлах .cpp. Почти все остальные - из-за выключенных предупреждений компиляторов.

Ну что поделать, люди так пишут. Если код решает поставленную задачу, заказчику обычно до лампочки на чем и как он написан, хоть на бейсике, se la vi. А вот когда он попадает к вам уже встаёт вопрос, поправить в рамках того что сделано или рисковать переписывать полждвижка с непонятными перспективах.

В геймдеве вообще принято тесты писать? Или девочки из отдела тестирования, протестирую?

Или геймеры на релизе?

Ну а как вы думаете? на последнем проекте было за 8к тестов, но они покрфвали от силы треть кода движка и игры.

void init(std::vector<TextureDX12> &a) { memset(a.data(), 0, sizeof(TextureDX12) * a.size());}

В этом куске кода даже C++11 нельзя использовать? Почему бы здесь не использовать for (auto& v... или std::fill?

Видимо, потому что класс TextureDX12 до виртуальности был оберткой над умным указателем. Хотя код не выглядит безопасным, потому что shared_ptr - не pod. И дизайн такой, что после случайного повторного вызова init память потечет. Если естественное начальное состояние nullptr, можно было в класс добавить инициализацию.

void Particle::CreateVerties(size_t p){ std::shared_ptr<Vertex> ss(new Point[p * 3]); // each p is 3 vertex ....}

Так тут each Point состоит из 3 Vertex или наоборот - each Vertex состоит из трёх Points?

Если каждый Vertex содержит 3 точки, разве недостаточно написать так?

auto ss = std::make_shared<Vertex[]>(p);

В C++ ключевое слово volatile не обеспечивает атомарность

Интересно, что до сих пор некоторые упорно с этим спорят.

Но что означает [volatile] в квадратных скобках?

Коллега ставил и убирал в этом месте volatile, и так закрывал баг. Несколько раз

Меня просто удивило, что слово volatile в квадратных скобках написано. Оно же без скобок пишется. Это же не [[nodiscard]] какой-нибудь.

Вероятно это связано с реализацией спинлоков и других примитивов, люди смотрят на код который там написан. Видят что везде используется волатайл переменные и как-то связывают это с мультитредом.

Ну, зачем как раз понятно.

Всегда было странно ставить отдельный барьер для компилятора, и отдельный - для процессора, гораздо лучше когда первый автоматически подразумевает второй. Другое дело, что нестандартные языковые расширения в 21м веке уже не кажутся чем-то хорошим.

странно ставить отдельный барьер для компилятора, и отдельный - для процессора, гораздо лучше когда первый автоматически подразумевает второй.

С одной стороны логично и удобно, да, а с другой:

нестандартные языковые расширения в 21м веке уже не кажутся чем-то хорошим.

Вот это ^ всё и портит. Сам видел виндовый код с volatile в качестве атомиков, про который авторы говорили: "А чо такова, работает жы!" А тот факт, что это ms specific и непортабельно по определению, как-то от них ускользает, потому что уже привыкли так писать. Тут плохо ещё и то, что этот конкретный ms specific включён по умолчанию для x86 и x86_64, и все начинают думать, что всё всегда будет работать.

Ну и в плюсах начиная с С++11 есть атомики, в которых можно ещё и задавать различные memory order.

bool ComponentVec::add(Component c) {

А здесь не лучше ли передавать Component по const-ссылке?

К задачке не относится, но расскажу почему там так. По бест практис конечно лучше передавать ссылку, но в конкретных случаях иногда жертвуют перфом, чтобы избавиться от необходимости синхронизации этого кода. Сам вектор компонентов потокобезопасный должен быть, и чтобы не городить лишних мьютексов передавали копию компонента, компонент в этом случае выступает аналогом shared_ptr, который хранит в себе нужные данные не опасаясь что какието из них повиснут. Както так, код не мой, просто баг затронул АИшку и пришлось разбираться

Согласен, что не всегда const-ссылка - это хорошо. Да и вообще, узкие по производительности места нужно проверять профилировщиком, т.к. иногда самая медленная часть кода - вообще не та, которая на глаз таковой может показаться.

        pair<vec, vec> * plane_egdes = alloca (16 * sizeof(vec) * 2);

Тут же ещё одна ошибка, потому что (теоретически) могут быть байты выравнивания внутри pair.

Наверное, кому-то так удобнее писать, но лучше бы не использовать using namespace std, особенно в заголовочных файлах.

А ошибка здесь в том, что поиск в мапе компонентов производитя ТРИ раза в худшем случае, первый на find(c.id()), второй на insert() если компоненты нет. И третий в операторе доступа по индексу.

На самом деле ошибка здесь в использовании дерева вместо хеш-таблицы.

По 0х13, ENSURE(secretKeyPwa[0] == 0); всё ещё неправильно. Ваш memset всё так же может быть выброшен по as-if-rule. Используйте memset_s, для него не нужны никакие костыли, тем более не гарантированные стандартом.

Вы можете увидеть, что код с memset и без компилируется одинаково, если компилятор может доказать, что ваш массив с секретом не используется после memset.

Sign up to leave a comment.

Articles