Мне нужна ваша помощь в понимании возможных подходов к использованию/отключению исключений в C++.
Мой вопрос не о том, что является лучшим выбором, а о том, каковы возможные варианты и что эти варианты подразумевают.
В настоящее время варианты, о которых я могу думать, следующие:
Я хотел бы знать, правильно ли я понимаю варианты, и что я могу упустить или понять неправильно.
Я также хотел бы знать, имеют ли смысл ограничения на предоставление базовой безопасности исключений для вариантов 2-4 (где исключения всегда в конечном итоге приводят к завершению программы) или как можно ослабить/ограничить требование безопасности исключений для конкретных случаев (например, обработка внешних ресурсов). , файлы).
если фактические накладные расходы и затраты пространства/времени оплачиваются, как только что-то выбрасывает, но мы в любом случае просто пытаемся безопасно завершить программу всякий раз, когда что-то выбрасывает, ограничения накладных расходов и затрат пространства/времени не кажутся слишком актуальными. Варианты 3 и 4 предназначены только для того, чтобы убедиться, что происходит раскручивание стека, а внешние ресурсы, которые будут освобождены деструкторами RAII, не остаются висящими. Это вообще не имеет смысла?
Что ж, если вы уже оплачиваете стоимость исключений, вы можете также выбрать вариант 5 и фактически выполнять обработку ошибок, где это уместно, это моя точка зрения. Независимо от того, делаете ли вы это один раз на верхнем уровне (варианты 3/4) или там, где это действительно целесообразно (вариант 5), это не проблема «регулирования использования исключений», это общая проблема стратегии обработки ошибок.
Спасибо, я попытался очистить свой заголовок. Кажется, что реальная стоимость исключений оплачивается в тот момент, когда я ловлю исключение. Так действительно ли я плачу за затраты времени/памяти на исключения в вариантах 3/4, где исключения никогда не перехватываются, кроме как для завершения программы? И это возвращает меня к моему последнему вопросу: вариант 5 заставляет меня кодировать с учетом безопасности исключений. Весь мой код должен гарантировать хотя бы базовую безопасность исключений. Это действительно необходимо, если я выберу вариант 3?
Обратите внимание, что -fno-exceptions не только отключает поддержку исключений, но и делает невозможным для кода -fexceptions связывание ранее созданного кода с правильно переданным кодом исключений над в этом ранее коде сборки (т. е. из обратного вызова, переданного в библиотечную функцию).





Строго говоря, если вы хотите отключить исключения, верным способом будет только компиляция без поддержки исключений, поэтому вариант 1. Потому что, когда вы хотите отключить исключения, вы также не должны использовать или даже обрабатывать их. Вызов исключения немедленно прекратит работу или приведет к серьезному сбою в большинстве реализаций, и только благодаря этому вы избежите накладных расходов, таких как пространство или накладные расходы на производительность, даже если они малы (см. Ниже).
Однако, если вы просто хотите знать, какие парадигмы об использовании исключений существуют, ваши варианты почти верны, с общей идеей, которую вы не упомянули, о сохранении исключительных исключений и выполнении действий, которые вероятны или даже просто могут быть вызваны при запуске программы .
И даже более того, в целом, речь идет об обработке ошибок в целом: исключения предназначены для обработки ошибок, с которыми вы сталкиваетесь в время выполнения, и если все сделано правильно, ошибки вы можете обнаружить ТОЛЬКО во время выполнения. Вы можете избежать исключений, просто решив все проблемы, которые вы можете обнаружить до выполнения, например правильное кодирование и просмотр (время написания кода), используя строгие типы и проверки (шаблоны, время компиляции) и перепроверив их снова (статический анализатор).
Если вы понимаете, что ошибаетесь в отношении безопасности исключений, я бы сказал: По сути, сначала это зависит от того, когда вы включаете исключения в целом: если они отключены, вы не можете и не должны заботиться о безопасности исключений, поскольку их нет (или если они попытаются появиться, вы все равно прекратите работу/сбой/жесткий сбой). ). Если вы включаете исключения, но не используете их в своем коде, как в случаях 2, 4 и 3, нет проблем, которые вы все равно хотите завершить, поэтому отсутствующий код очистки не имеет значения (и материал в 3. по-прежнему выполняется в любом случае). кейс). Но тогда следует дать понять всем, что вы не хотите их использовать, чтобы они не пытались восстановиться после исключений. И если вы используете их в своем коде, вы должны позаботиться о безопасности исключений таким образом, чтобы, когда возникает исключение, вы очищали себя и после себя, а затем его основной обработчик или будущие изменения кода, когда вы все еще прекратиться или может восстановиться. Не очищать, но по-прежнему использовать исключения не имеет смысла. Тогда вы можете просто придерживаться варианта 1.
Это должно быть, насколько мне известно, исчерпывающим. Для получения дополнительной информации см. ниже.
Я рекомендую вариант 4. и я скажу вам, почему:
Исключения - это очень неправильно понятый и неправильно используемый шаблон. Они неправильно используются для выполнения программы, как языки Java. Они часто запрещены во встроенном коде или коде безопасности из-за их природы, в которой трудно сказать, какой обработчик его поймает, если он есть, и сколько времени это займет, для чего C++ std просто говорит в основном «магия реализации».
Однако в моих рассуждениях ненависть к исключениям в основном является большой проблемой XY: Когда люди жалуются, что их восстановление сломано и трудно сказать, то обычная проблема заключается в том, что вы не видите, что вы не можете или не должны много делать с большинством исключений, для этого они и существуют. Однако такие вещи, как тайм-аут или закрытое соединение tcp, вряд ли являются ненормальными, но многие люди используют для этого исключения, что, конечно, было бы неправильно. Но если вся ваша ОС говорит вам, что нет сетевого адаптера или памяти, что вы можете сделать? Единственное, что вам, вероятно, нужно, это попытаться где-то зарегистрировать причину, и это должно быть сделано за одну попытку/отлов вокруг основного блока.
То же самое и с программами безопасности/реального времени: например, когда не хватает памяти, когда это происходит, вы в любом случае ****, тогда стратегия состоит в том, чтобы делать это в небезопасное время инициализации, в котором исключений нет. проблема тоже.
При использовании контейнеров с бросающими членами это аналогичный сценарий, что вы сможете сделать, если вместо этого получите их код ошибки? Не так много, вот почему вы должны убедиться во время кода, что нет причин для ошибок, например убедиться, что элемент действительно там, или зарезервировать емкость для ваших векторов.
Это имеет приятное преимущество: более чистый код, не забывая проверять на наличие ошибок и не снижая производительности, по крайней мере, используя общие реализации C++, такие как gcc и т. д.
Причина 3. спорна, вы тоже могли бы сделать это, на мой взгляд, но тогда мой вопрос: что вам нужно делать в деструкторах, которые все равно не очистили бы вашу операционную систему? ОС будет очищать память, сокеты и т. д. Если у вас есть автономный сценарий, вопрос остается, когда бы он ни отличался: вы планируете остановиться, например, из-за того, что ваш UART сломался, что бы вы хотели сделать перед остановкой? А почему нельзя сделать это в блоке catch перед повторным броском?
Имеет проблему с тем, что не используется какой-либо бросающий код, и по-прежнему остается проблема, как обрабатывать редкие коды ошибок. (Вот почему так много программистов на C до сих пор используют goto или длинные переходы)
Нежизнеспособно ИМХО, худшее из обоих
как уже упоминалось, хорошо, но что вам нужно сделать в ваших статических DTors, что вы даже ненормальное завершение не сделаете?
Мой любимый
Только если у вас действительно есть редкие заболевания, от которых вы действительно можете вылечиться.
Что я имею в виду в качестве общего совета: Возбуждение и исключение должны означать, что произошло что-то, что никогда не должно происходить при нормальной работе, но произошло из-за непредвиденной ошибки, такой как аппаратный сбой, отсоединенные кабели, ненайденные общие библиотеки, программная ошибка, которую вы сделали, например, попытка .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.
Вы имеете в виду возможные варианты минимизации обработки каждого исключения? И по последствиям, какие накладные расходы у них есть?
Я хотел знать, полон ли мой список из 5 возможных стратегий обработки исключений, отсутствует ли какая-то другая стратегия или некоторые из них следует удалить. Под последствиями я подразумеваю, например, должен ли я заботиться о предоставлении базовой безопасности исключений, когда я выбираю стратегию 2-4, или чем я рискую, выбирая стратегию 2 (вы частично ответили на это).
Как насчет сейчас @Gianni
Как правило, причина для отключения исключений заключается в том, что они вводят ненулевые накладные расходы и имеют непредсказуемые затраты пространства/времени во время выполнения, что неприемлемо во многих ситуациях. Эти проблемы появляются, как только что-либо в вашем коде выдает исключение, поэтому я не вижу смысла в вариантах 3 и 4. При этом этот вопрос является слишком общий, и я бы проголосовал за его закрытие, если бы меня не остановила награда.