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

Мне нужна ваша помощь в понимании возможных подходов к использованию/отключению исключений в C++.

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

В настоящее время варианты, о которых я могу думать, следующие:

  1. Компиляция с -fno-exceptions и отказ от большинства стандартных контейнеров (возможно, определение внутренних контейнеров, которые не вызывают исключения, как это предлагается в Стиль кодирования SpiderMonkey)
  2. Просто избегайте бросать и ловить в собственном коде, но по-прежнему используйте стандартные контейнеры, которые могут генерировать исключения. Быть довольным тем фактом, что в случае исключений программа может завершиться без раскручивания стека, и что даже внешние ресурсы, обработанные RAII, могут остаться зависшими. (Похоже, это подход Google C++ в соответствии с ответами на этот ТАК вопрос)
  3. Не использовать исключения, но обернуть все в блок catch all std::exception try, чтобы убедиться, что стек раскручен, а дескрипторы RAII для внешних ресурсов освобождены до завершения программы, как для это правило Cert C++
  4. Как указано выше, но также генерируются исключения, которые в конечном итоге приведут к завершению программы.
  5. Также использование перехваченных исключений и восстановление после исключений.

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

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

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

You 22.02.2019 11:16

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

Gianni 22.02.2019 11:39

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

You 22.02.2019 12:19

Спасибо, я попытался очистить свой заголовок. Кажется, что реальная стоимость исключений оплачивается в тот момент, когда я ловлю исключение. Так действительно ли я плачу за затраты времени/памяти на исключения в вариантах 3/4, где исключения никогда не перехватываются, кроме как для завершения программы? И это возвращает меня к моему последнему вопросу: вариант 5 заставляет меня кодировать с учетом безопасности исключений. Весь мой код должен гарантировать хотя бы базовую безопасность исключений. Это действительно необходимо, если я выберу вариант 3?

Gianni 22.02.2019 13:39

Обратите внимание, что -fno-exceptions не только отключает поддержку исключений, но и делает невозможным для кода -fexceptions связывание ранее созданного кода с правильно переданным кодом исключений над в этом ранее коде сборки (т. е. из обратного вызова, переданного в библиотечную функцию).

rubenvb 28.02.2019 14:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
6
692
1

Ответы 1

Обновлять

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

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

И даже более того, в целом, речь идет об обработке ошибок в целом: исключения предназначены для обработки ошибок, с которыми вы сталкиваетесь в время выполнения, и если все сделано правильно, ошибки вы можете обнаружить ТОЛЬКО во время выполнения. Вы можете избежать исключений, просто решив все проблемы, которые вы можете обнаружить до выполнения, например правильное кодирование и просмотр (время написания кода), используя строгие типы и проверки (шаблоны, время компиляции) и перепроверив их снова (статический анализатор).

Обновление 2

Если вы понимаете, что ошибаетесь в отношении безопасности исключений, я бы сказал: По сути, сначала это зависит от того, когда вы включаете исключения в целом: если они отключены, вы не можете и не должны заботиться о безопасности исключений, поскольку их нет (или если они попытаются появиться, вы все равно прекратите работу/сбой/жесткий сбой). ). Если вы включаете исключения, но не используете их в своем коде, как в случаях 2, 4 и 3, нет проблем, которые вы все равно хотите завершить, поэтому отсутствующий код очистки не имеет значения (и материал в 3. по-прежнему выполняется в любом случае). кейс). Но тогда следует дать понять всем, что вы не хотите их использовать, чтобы они не пытались восстановиться после исключений. И если вы используете их в своем коде, вы должны позаботиться о безопасности исключений таким образом, чтобы, когда возникает исключение, вы очищали себя и после себя, а затем его основной обработчик или будущие изменения кода, когда вы все еще прекратиться или может восстановиться. Не очищать, но по-прежнему использовать исключения не имеет смысла. Тогда вы можете просто придерживаться варианта 1.

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

Рекомендация

Я рекомендую вариант 4. и я скажу вам, почему:

Исключения - это очень неправильно понятый и неправильно используемый шаблон. Они неправильно используются для выполнения программы, как языки Java. Они часто запрещены во встроенном коде или коде безопасности из-за их природы, в которой трудно сказать, какой обработчик его поймает, если он есть, и сколько времени это займет, для чего C++ std просто говорит в основном «магия реализации».

Задний план

Однако в моих рассуждениях ненависть к исключениям в основном является большой проблемой XY: Когда люди жалуются, что их восстановление сломано и трудно сказать, то обычная проблема заключается в том, что вы не видите, что вы не можете или не должны много делать с большинством исключений, для этого они и существуют. Однако такие вещи, как тайм-аут или закрытое соединение tcp, вряд ли являются ненормальными, но многие люди используют для этого исключения, что, конечно, было бы неправильно. Но если вся ваша ОС говорит вам, что нет сетевого адаптера или памяти, что вы можете сделать? Единственное, что вам, вероятно, нужно, это попытаться где-то зарегистрировать причину, и это должно быть сделано за одну попытку/отлов вокруг основного блока.

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

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

Это имеет приятное преимущество: более чистый код, не забывая проверять на наличие ошибок и не снижая производительности, по крайней мере, используя общие реализации C++, такие как gcc и т. д.

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

Подводить итоги

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

  2. Нежизнеспособно ИМХО, худшее из обоих

  3. как уже упоминалось, хорошо, но что вам нужно сделать в ваших статических DTors, что вы даже ненормальное завершение не сделаете?

  4. Мой любимый

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

Что я имею в виду в качестве общего совета: Возбуждение и исключение должны означать, что произошло что-то, что никогда не должно происходить при нормальной работе, но произошло из-за непредвиденной ошибки, такой как аппаратный сбой, отсоединенные кабели, ненайденные общие библиотеки, программная ошибка, которую вы сделали, например, попытка .at() по индексу не в контейнере, о котором вы могли бы знать, а в контейнере - нет. Тогда вполне логично, что генерация исключения должна почти каждый раз приводить к завершению программы/жесткому сбою.

Следствием компиляции с поддержкой исключений является то, что, например, для автономной программы ARM размер вашей программы увеличится на 27 КБ, вероятно, нет для размещенной (Linux/Windows и т. д.) и 2 КБ ОЗУ для автономного случая (см. Обработчик исключений C++ в коре головного мозга gnu m4 с freertos)

Однако вы не платите за производительность за использование исключений при использовании общих компиляторов, таких как clang или gcc, когда ваш код работает нормально, то есть когда ваши ветки/ifs, которые могут вызвать исключение, не запускаются.

В качестве ссылки, т.е. доказательства моих утверждений, я ссылаюсь на ISO/IEC TR 18015:2006 Технический отчет о производительности C++ с этой небольшой выдержкой из 7.2.2.3:

Enable exception handling for real-time critical programs only if exceptions are actually used. A complete analysis must always include the throwing of an exception, and this analysis will always be implementation dependent. On the other hand, the requirement to act within a deterministic time might loosen in the case of an exception (e.g. there is no need to handle any more input from a device when a connection has broken down). An overview of alternatives for exception handling is given in §5.4. But as shown there, all options have their run-time costs, and throwing exceptions might still be the best way to deal with exceptional cases. As long as no exceptions are thrown a long way (i.e. there are only a few nested function calls between the throw-expression and the handler), it might even reduce run-time costs.

Мой вопрос не о том, что является лучшим выбором, а о том, каковы возможные варианты и что эти варианты подразумевают., но +1 за подробности и ссылки! Что вам нужно делать в деструкторах, которые все равно не очистят вашу операционную систему? это собственно и хотел узнать, есть ли что-то, что ОС сама бы не подчистила?
Gianni 28.02.2019 13:37

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

Superlokkus 28.02.2019 13:40

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

Gianni 28.02.2019 13:46

Как насчет сейчас @Gianni

Superlokkus 28.02.2019 14:05

Другие вопросы по теме