Должен ли я добавить обработку исключений в существующую базу кода? - PullRequest
4 голосов
/ 01 ноября 2011

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

Я работаю над SDK, который управляет ч / б картами в среде Windows.

SDK состоит из более чем 100 DLL, которые взаимодействуют друг с другом. Наша существующая кодовая база, вероятно, содержит 100 000 (если не 1 000 000) строк кода. Наши модули также многопоточные.

Мы связываемся с соответствующей библиотекой, чтобы мы использовали nothrow new (lic.lib вместо licp.lib).

Большая часть кода не имеет обработки исключений. Код написан с учетом этого.

int *p = new int[size];
if (p == NULL)
{
   // handle this case...
   // most probably return an error code
}

char *q = new char[size];
if (q == NULL)
{
    delete[] p;
   // handle this case...
   // most probably return an error code
}

Мы также используем технику RAII. Например, у нас в стеке создан объект, который автоматически ожидает и освобождает критическую секцию.

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

Код обычно проверяет деление на 0 или проверяет NULL-указатели перед разыменованием. Но все равно бывает, что такой случай случится. Поскольку деление на ноль или разыменование NULL-указателя не вызывает исключение, я задаюсь вопросом, насколько полезно пройти через 100 000 строк кода и добавить обработку исключений, которая изменит рабочий процесс и может вызвать утечки памяти, если не обработать должным образом. Я экспериментировал с SEH, но не думаю, что имеет смысл начинать использовать SEH, и это специфично для Microsoft, не так ли?

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

Кроме того, если бы я добавил обработку исключений, как бы я поступил? Измените все модули сразу или начните снизу вверх (то есть, если модуль A вызывает модуль B, который вызывает модуль C, я бы изменил C, затем B, затем A, поскольку мы выпускаем наше программное обеспечение довольно часто, и у нас, вероятно, будет только время изменить C до следующего выпуска).

Спасибо!

Ответы [ 3 ]

3 голосов
/ 01 ноября 2011

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

Вы не говорите, что именно подразумеваете под «обработкой исключений», поэтомуЯ начну с чего-то фундаментального: стандартный C ++ (вы пометили вопрос как c ++ ) требует чтобы вы написали код, который «обрабатывает исключения», для всех приложений, кроме тривиальных, в противном случае для вашего кода неисправен .Различным частям стандартной библиотеки C ++ разрешено генерировать исключения, включая new, который использует ваш пример кода.Поэтому ваш код уже может иметь вероятность того, что в него могут быть сгенерированы исключения, которые он должен «обработать».Что происходит в этом случае?По сути, вы должны написать « код безопасного исключения ».

  • Ошибка программы в утечке ресурсов перед лицом исключений.Вы используете RAII, поэтому вы должны быть в порядке.
  • Любой объект может войти в несогласованное состояние после выброса исключения.Обеспечение того, что может быть намного сложнее.

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

1 голос
/ 01 ноября 2011

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

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

0 голосов
/ 02 января 2018

Я согласен с Raedwald в том, что если вы используете C ++ без очень осторожного стандарта кодирования, чтобы избежать EH (например: использовать nothrow new, избегать стандартных контейнеров и т. Д.), Что, как я полагаю, унаследованный код не сделал, тогда кодуже сломан и может протекать и делать спорадические вещи уже перед лицом исключений, с которыми он может уже встретиться, например bad_alloc или bad_cast, если вы полагаетесь на dynamic_cast.

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

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

Теперь главная причина, по которой я здесь остановился и воскресил этот старый поток (извинения!), заключается в следующем комментарии:

Код обычно проверяет деление на 0 или проверяет NULL-указатели перед разыменованием.Но все равно бывает, что такой случай случится.Поскольку деление на ноль или разыменование нулевого указателя не вызывает исключение [...]

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

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

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

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

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