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

Я пытаюсь убедить мою команду привести к использованию исключений в C ++ вместо того, чтобы возвращать bool isSuccessful или перечисление с помощью код ошибки. Однако я не могу противостоять этой критике его.

Рассмотрим эту библиотеку:

class OpenFileException() : public std::runtime_error {
}

void B();
void C();

/** Does blah and blah. */
void B() {
    // The developer of B() either forgot to handle C()'s exception or
    // chooses not to handle it and let it go up the stack.
    C();
};

/** Does blah blah.
 *
 * @raise OpenFileException When we failed to open the file. */
void C() {
    throw new OpenFileException();
};
  1. Рассмотрим разработчика, вызывающего функцию B(). Он проверяет свою документацию и видит, что она не возвращает никаких исключений, поэтому он не пытается ничего поймать. Этот код может привести к сбою программы в процессе производства.

  2. Рассмотрим разработчика, вызывающего функцию C(). Он не проверяет документацию, поэтому не видит никаких исключений. Вызов небезопасен и может привести к сбою в работе программы.

Но если мы проверяем ошибки таким образом:

void old_C(myenum &return_code);

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

Как я могу использовать исключения безопасно, так что есть какой-то контракт?

31 голос | спросил DBedrenko 28 +03002016-10-28T12:15:41+03:00312016bEurope/MoscowFri, 28 Oct 2016 12:15:41 +0300 2016, 12:15:41

5 ответов


48

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

Исключения имеют недостатки; вы должны сделать дело на основе выгодных затрат.
Я нашел эти две статьи полезными: Необходимость исключений и Все неправильно с исключениями. Также в этом блоге есть мнения многих экспертов об исключениях, с акцентом на C ++ . Хотя мнение экспертов, похоже, склоняется к исключениям, это далеко не однозначный консенсус.

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

  

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

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

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

ответил 28 +03002016-10-28T12:35:08+03:00312016bEurope/MoscowFri, 28 Oct 2016 12:35:08 +0300 2016, 12:35:08
26

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


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

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

В моем коде есть только несколько операторов catch - если они вообще есть - вне основного цикла. И это похоже на общий подход в современном C ++.


Исключения и коды возврата не являются взаимоисключающими.

Вы не должны делать это «все или ничего». Исключения должны использоваться для исключительных ситуаций. Такие вещи, как «Файл конфигурации не найден», «Диск полный» или что-то еще, что нельзя обрабатывать локально.

Общие сбои, такие как проверка наличия имени файла, предоставленного пользователем, не являются прецедентом для исключений; Вместо этого используйте возвращаемое значение.

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

Таким образом, нет абсолютного правила. Грубая рекомендация: , если ее можно обрабатывать локально, сделать ее возвратным значением; если вы не можете обработать его локально, выделите исключение.


Статическая проверка исключений не является полезной.

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

Java имеет статическую проверку, но обычно считается неудачным экспериментом, и большинство языков, поскольку, в частности, C #, не имеют такого типа статической проверки. Это хорошее чтение о причинах, по которым C # не имеет его.

По этим причинам C ++ отказался от своего кода throw(exceptionA, exceptionB) в пользу noexcept(true). По умолчанию используется функция throw, поэтому программисты должны ожидать этого, если документация явно не обещает иначе.


Написание кода исключения исключений не имеет ничего общего с написанием обработчиков исключений.

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

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

ответил Sjoerd 28 +03002016-10-28T13:38:47+03:00312016bEurope/MoscowFri, 28 Oct 2016 13:38:47 +0300 2016, 13:38:47
8

Программисты на С ++ не ищут спецификаций исключений. Они ищут гарантии исключений.

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

Или возможно, что определенный фрагмент кода может гарантировать никогда не бросать (т. е. ничего, кроме завершения процесса ОС)?

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

Различные методы программирования на С ++ способствуют гарантиям исключения. RAII (управление ресурсами на основе областей) предоставляет механизм для выполнения кода очистки и обеспечения освобождения ресурсов как в обычных случаях, так и в исключительных случаях. Создание копии данных перед выполнением модификаций объектов позволяет восстановить состояние этого объекта, если операция завершилась неудачно. И так далее.

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

При разработке на C ++ (для использования в производстве) нельзя позволить замаскировать детали. Кроме того, двоичный blob (non-open-source) является ошибкой программистов на C ++. Если мне нужно вызвать двоичный blob, а blob не удастся, тогда обратное проектирование - это то, что сделает следующий программист на C ++.

Ссылка: http://en.cppreference.com/w/cpp /language /exceptions # Exception_safety - см. раздел Безопасность исключений.

У C ++ была неудачная попытка реализовать спецификации исключений. Более поздний анализ на других языках говорит, что спецификации исключений просто не практичны.

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

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

Ссылка: http://www.gotw.ca/publications/mill22.htm

Начиная с C ++ 17, атрибут [[nodiscard]] может использоваться для значений возвращаемых значений (см. https://stackoverflow.com/questions/39327028/может-ас-функция-быть объявлено-то-что-заместитель возврата стоимость не может-быть проигнорирован ).

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

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

ответил rwong 28 +03002016-10-28T16:42:50+03:00312016bEurope/MoscowFri, 28 Oct 2016 16:42:50 +0300 2016, 16:42:50
4
  

Рассмотрим разработчика, вызывающего функцию C (). Он не проверяет   поэтому никаких исключений не обнаружено. Вызов небезопасен

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

ответил DeadMG 29 +03002016-10-29T23:15:52+03:00312016bEurope/MoscowSat, 29 Oct 2016 23:15:52 +0300 2016, 23:15:52
3

Если вы строите критическую систему, подумайте о рекомендациях руководства вашей команды и не используйте исключения. Это правило AV 208 в Стандартные стандарты кодирования на воздушном судне Lockheed Martin's Joint Cike . С другой стороны, MISRA Руководства C ++ содержат очень конкретные правила, касающиеся того, когда исключения могут и не могут быть использованы, если вы создают ПО, совместимые с MISRA.

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

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

ответил Thomas Owens 28 +03002016-10-28T17:46:28+03:00312016bEurope/MoscowFri, 28 Oct 2016 17:46:28 +0300 2016, 17:46:28

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132