Насколько важно, по вашему мнению, безопасность исключений в вашем коде C ++?

19

Каждый раз, когда я решаю сделать свой код безопасным для исключений, я оправдываю это не делать, потому что это отнимает много времени. Рассмотрим этот относительно простой фрагмент:

Level::Entity* entity = new Level::Entity();
entity->id = GetNextId();
entity->AddComponent(new Component::Position(x, y));
entity->AddComponent(new Component::Movement());
entity->AddComponent(new Component::Render());
allEntities.push_back(entity); // std::vector
entityById[entity->id] = entity; // std::map
return entity;

Чтобы реализовать базовую гарантию исключений, я мог бы использовать ограниченный указатель на newвызовы. Это предотвратит утечки памяти, если любой из вызовов вызовет исключение.

Однако, скажем, я хочу реализовать строгую исключительную гарантию. По крайней мере, мне нужно было бы реализовать общий указатель для моих контейнеров (я не использую Boost), nothrow Entity::Swapдля атомного добавления компонентов и какую-то идиому для атомарного добавления к обоим Vectorи Map. Это не только потребует много времени для реализации, но и будет дорогостоящим, поскольку требует гораздо большего копирования, чем небезопасное исключительное решение.

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

Как далеко вы берете это в своих собственных игровых проектах? Обычно допустимо писать небезопасный код исключения для программы, которая может просто аварийно завершить работу при наличии исключения?

Кай
источник
21
Когда в прошлом я работал над проектами на C ++, мы делали вид, что исключений не существует.
Тетрад
2
Лично я люблю исключения и часто на самом деле получаю удовольствие от того, как заставить мой код обеспечивать надежные гарантии. Если вы просто потерпите крах, когда получите исключение, хотя ... возможно, не беспокойтесь об этом.
7
Лично я использую исключения для управления .. хм .. "исключения", а не ошибки. Например, если я знаю, что функция может аварийно завершить работу, я позволю ей аварийно завершить работу, но если я знаю, что функция не может успешно прочитать архив, но есть другой способ, я окружаю его попыткой, поймать. и если это исключение «неспособно прочитать», я просто управляю им иначе, как без чтения архива.
Густаво Масиэль
Что сказал Гтокну, вам нужно знать, какие исключения могут выдавать любые внешние библиотеки.
Питер Олстед
Связанный: gamedev.stackexchange.com/questions/2762/...
Тетрадь

Ответы:

26

Если вы собираетесь использовать исключения (что не означает, что у этого подхода есть свои плюсы и минусы, которые выходят за рамки конкретного вопроса), вы должны правильно написать безопасный код для исключений.

Код, который не использует исключения и явно не является безопасным для исключения, лучше, чем код, который их использует, но наполовину обеспечивает безопасность своих исключений. Если у вас есть последний случай, у вас есть целый класс ошибок и сбоев, которые могут произойти, когда возникает исключение, которое вы, вероятно, вообще не сможете восстановить, что делает одно из преимуществ исключений полностью недействительным. Даже если это относительно редкий класс неудач, это все же возможно.

Это не значит, что если вы используете исключения, весь код должен обеспечивать гарантию строгих исключений - просто каждый фрагмент кода должен обеспечивать какую-то гарантию (нет, базовую, сильную), чтобы при использовании этого кода Вы знаете, что гарантирует вам потребитель. Как правило, чем ниже уровень рассматриваемого компонента, тем сильнее должна быть гарантия.

Если вы никогда не собираетесь предоставлять надежную гарантию или никогда не собираетесь по-настоящему использовать парадигмы обработки исключений и всю дополнительную работу и недостатки, которые это подразумевает, вы также не сможете в полной мере воспользоваться всеми вытекающими из этого преимуществами, и вам может быть легче время просто отказывается от исключений.


источник
12
Одно это стоит +1: «Код, который не использует исключения и явно не является безопасным для исключения, лучше, чем код, который их использует, но наполовину обеспечивает безопасность своих исключений».
Максимус Минимус
14

Мое общее правило: если вам нужно использовать исключения, используйте исключения. Если вам не нужно использовать исключения, не используйте исключения. Если вы не знаете, нужно ли вам использовать исключения, вам не нужно использовать исключения.

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

С другой стороны, когда вы разрабатываете игру, вам лучше следовать мантре: ранний сбой, громкий сбой. Если есть проблема, вы действительно хотите знать об этом как можно скорее; вы никогда не хотите, чтобы ошибки были незаметно «обработаны», потому что это часто скрывает фактическую причину последующих неправильных действий и делает отладку намного более сложной, чем это необходимо.

Тревор Пауэлл
источник
5

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

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

Например, я буду жестко кодировать специальную сетку ERROR в своей игре. Это вращающийся красный круг с белым крестиком на нем и белой рамкой. В зависимости от игры невидимый может быть лучше. Один стартовый экземпляр инициализируется при запуске. Так как он встроен в код, а не использует какие-либо внешние файлы, он не сможет потерпеть неудачу

В случае сбоя обычного вызова loadMesh вместо выдачи какой-либо ошибки и сбоя на рабочий стол он автоматически записывает ошибку в консоль / файл журнала и возвращает жестко запрограммированную сетку ошибок. Я узнал, что Skyrim делает то же самое, когда мод облажался. Есть хорошая вероятность, что игрок даже не увидит сетку ошибок (если только нет какой-то серьезной проблемы, и многие из них похожи на нее).

Также есть запасной шейдер. Тот, который выполняет базовые операции с матрицами вершин, но если по какой-то причине нет однородной матрицы, он все равно должен делать ортогональное представление. У него нет правильного затенения / освещения / материалов (хотя у него есть флаг color_overide, поэтому я могу использовать его с сеткой ошибок).

Единственная возможная проблема заключается в том, что версия с ошибкой меша может каким-то образом навсегда сломать игру. Например, убедитесь, что он не применяет физику, так как, если игрок загружает область, то эта ошибка мешает ее падению на пол, тогда, если они перезагрузят игру на этот раз с правильной работой сетки, если она больше, это может быть встроен в землю. Это не должно быть слишком сложно, так как вы, вероятно, собираетесь хранить свои физические сетки вместе с графическими. Сценарии также могут быть проблемой. С некоторыми вещами вам просто придется тяжело умереть. Не совсем уверен, каким будет использование отступного «уровня» (хотя вы могли бы сделать небольшую комнату со знаком, указывающим, что произошла ошибка, просто убедитесь, что сохранение отключено, поскольку вы не хотите, чтобы игрок застрял в нем).

В случае «нового», для разработки игр может быть лучше взглянуть на какую-то фабрику. Это может дать вам другие преимущества, такие как предварительное распределение объектов до того, как они вам понадобятся, и / или массовое их создание. Это также облегчает создание таких вещей, как глобальный индекс объектов, который вы можете использовать для управления памятью, находя вещи по идентификатору (хотя вы можете делать это и в конструкторах). И, конечно, вы также можете обрабатывать ошибки размещения.

Дэвид С. Бишоп
источник
2

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

Теперь хакер не может использовать фактический сбой, как только сбой происходит, процесс бесполезен и безвреден. Если вы просто читаете из нулевого указателя, это чистый сбой, вы делаете ошибку, и процесс мгновенно умирает.

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

Неисследованное исключение на самом деле не является настоящим сбоем, это просто способ завершить программу. Исключения случаются только потому, что кто-то написал библиотеку, которая специально выдает исключение при определенных обстоятельствах. Как правило, чтобы не попасть в непредвиденную ситуацию, которая может стать открытием для хакера.

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

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

AAAAAAAAAAAA
источник
2

Вам нужно посмотреть на свои исключения и взглянуть на условия, которые могут их вызвать. Чертовски много времени, большой процент того, для чего люди используют исключения, можно избежать с помощью надлежащих проверок во время цикла разработки / отладки сборки / тестирования. Используйте утверждения, передавайте ссылки, всегда инициализируйте локальные переменные при объявлении, memset-zero все выделения, NULL после освобождения и т. Д., И огромное количество теоретических исключений просто исчезает.

Подумайте: если что-то не получается и может быть кандидатом на выдачу исключения - как это повлияет? Насколько это важно? Если вам не удастся выделить память в игре (на вашем первоначальном примере), у вас, как правило, есть большая проблема, чем то, как вы обрабатываете случай сбоя, и обработка случая сбоя с помощью исключения не сделает эту большую проблему. уходи. Хуже - это может на самом деле скрыть тот факт, что большая проблема даже там. Вы хотите, чтобы? Я знаю, что нет. Я знаю, что если мне не удастся выделить память, я хочу ужасно аварийно завершить работу и сделать это как можно ближе к точке сбоя, чтобы я мог взломать отладчик, выяснить, что происходит, и исправить это должным образом.

Максимус Минимус
источник
1

Не уверен, что цитирование кого-либо еще подходит для SE, но я чувствую, что ни один из других ответов не касался этого.

Цитирую Джейсона Грегори из его книги « Архитектура игрового движка» . (БОЛЬШАЯ КНИГА!)

«Структурная обработка исключений (SEH) добавляет много накладных расходов на программу. Каждый кадр стека должен быть дополнен, чтобы содержать дополнительную информацию, необходимую для процесса разматывания стека. Кроме того, разматывание стека обычно происходит очень медленно - порядка двух-трех в разы дороже, чем просто возврат из функции. Кроме того, если хотя бы одна функция в вашей программе (или библиотеке) использует SEH, вся ваша программа должна использовать SEH. Компилятор не может знать, какие функции могут находиться над вами в стеке вызовов, когда Вы бросаете исключение. "

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

KlashnikovKid
источник
2
Структурная обработка исключений - это особый тип обработки исключений, доступный в Windows, который отличается от исключений C ++ в целом.
Kylotan
До, я не могу поверить, что я не знал этого. Спасибо за исправление!
KlashnikovKid
0

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

carddamom
источник