Существуют ли реальные случаи для C ++ без исключений? [закрыто]

40

В Когда использовать C над C ++ и C ++ над C? есть утверждение относительно к размеру кода / C ++ исключения:

Джерри отвечает (среди прочего):

(...) Сложно создать действительно крошечные исполняемые файлы на C ++. Для действительно небольших систем вы все равно редко пишете много кода, а дополнительный (...)

на что я спросил, почему это будет, на что Джерри ответил:

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

в котором я не очень сомневаюсь в техническом уровне реального мира.


Поэтому мне интересно (чисто из любопытства) услышать из реальных примеров, когда проект выбрал C ++ в качестве языка, а затем отключил исключения. (Не просто «не использовать» исключения в пользовательском коде, но отключить их в компиляторе, чтобы вы не могли генерировать или перехватывать исключения.) Почему проект решил сделать это (все еще используя C ++, а не C, но нет исключения) - каковы / были (технические) причины?


Приложение: Для тех, кто желает уточнить свои ответы, было бы неплохо подробно рассказать о том, как обрабатываются последствия исключений:

  • Коллекции STL ( vector, ...) не работают должным образом (об ошибке выделения нельзя сообщить)
  • new не могу бросить
  • Конструкторы не могут потерпеть неудачу
Мартин Ба
источник
1
JSF C ++. Джеты и исключения не смешиваются.
Кодер
1
Немного поучительной информации о проблемах с исключениями (и C ++ в целом) 250bpm.com/blog:4
Амброз Бижак
1
@AmbrozBizjak - я процитирую DeadMG из комментария к сообщению, на которое вы ссылаетесь: quote: «Мне кажется, что вы не понимаете исключения». Я согласен. Автор явно сделал беспорядок обработки исключений, глядя на примеры, которые он приводит в этом посте.
Мартин Ба

Ответы:

35

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


Кроме того, другим примером является аппаратный SDK Arduino, использующий без исключения gcc, активированный в C ++, и другие вещи, такие как отсутствие STL .


Есть некоторые технические причины, хорошие или плохие, что угодно, это не мой совет, а причины, которые я услышал:

  1. Большинство консолей - это действительно встроенные системы с ограниченным объемом памяти и временем обработки. Возможно, это не будет так справедливо в будущих консолях, но нынешние все еще ограничены по сравнению с ПК. Некоторые портативные консоли труднее программировать, чем любые смартфоны, например NDS. Функция исключения добавляет памяти и немного затрат на скорость, даже если вы ее не используете. Вы можете проверить себя, это правда даже на ПК.
  2. Видеоигры на консоли не могут вылететь. Они должны быть проверены таким образом, чтобы избежать любого сбоя или любого тупика, любого шоу-стопа. Вот почему производители консолей просят тщательно проверить игры перед публикацией. Это также означает, что управление исключениями увеличивает стоимость, которая не очень полезна в случае консольной игры. Например, в смартфоне это лучше, потому что могут быть какие-то способы восстановления или добавления некоторого кода, чтобы отправить вам электронное сообщение о проблеме. Закрытые платформы, как и большинство консолей, не позволяют этого. Таким образом, система исключений на самом деле не нужна. Вы просто «должны заставить его работать правильно». ;)
  3. Управление исключениями, когда вы не допускаете ошибок и сбоев, означает, что вам нужно реализовать стратегию управления ошибками. Такая система может быть достаточно сложной, чтобы заставить кого-то работать много времени, чтобы сделать ее полезной. Разработчики игр не могут позволить себе роскошь работать с функциями, которые считаются полезными при сбоях ...
  4. Компилятор не позволяет это (пока). Да, это случилось.

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


Обновить:

Я добавляю еще один удивительный пример: LLVM / CLang не использует исключения и RTTI по ​​следующим причинам :

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

Тем не менее, LLVM широко использует ручную форму RTTI, в которой используются такие шаблоны, как isa <>, cast <> и dyn_cast <>. Эта форма RTTI является опциональной и может быть добавлена ​​к любому классу. Это также существенно более эффективно, чем dynamic_cast <>.

CLang хорошо известен своей скоростью компиляции и явными ошибками, а также одним редким компилятором, который имеет действительно простой для понимания код.

Klaim
источник
9
(3): вам нужна стратегия управления ошибками, несмотря ни на что, если вы собираетесь согласиться с (2). Проблемы будут возникать, и программное обеспечение вашей консоли должно работать мягко и изящно. Для меня не очевидно, что полное исключение делает это проще.
Дэвид Торнли
6
Все отличные примеры жестко контролируемых сред выполнения, в которых ошибки обрабатываются должным образом и отсутствуют «исключительные» события.
Патрик Хьюз
1
@DavidThornley Потому что вы предполагаете, что система консоли будет обрабатывать какие-то ошибки, но это не так. Ну, может быть, это будет на PS3 или XBox, но это не позволит пройти тесты сборщика консоли. В любом случае, прошивка большинства современных консолей не так гибка, как вы думаете. Консольная игра должна иметь доступ почти ко всем аппаратным средствам консоли, поэтому вы можете думать об игре, работающей на консоли, почти так же, как если бы это была также «ОС» консоли ... чем больше у нас мощных консолей, тем не менее это правда. Поэтому я согласен с вами, что в некоторых случаях это кажется странным.
Klaim
2
Это хороший ответ, я хотел бы также добавить, что, даже если возникло исключение и выдало пользователю ошибку, что он или она должны делать с D-pad? Единственное реальное решение для конечного пользователя - это перезагрузка.
Anon
2
Я удалил глупый пример, потому что кто-то из его команды сказал на повышение «НЕТ ИСКЛЮЧЕНИЯ» не означает «не использовать исключение», но «не исключение из правил». См permalink.gmane.org/gmane.comp.lib.boost.devel/231377
KLAIM
9

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

Вот мои основные причины их отключения:

Двоичная совместимость

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

Размер исполняемого файла

Вот бинарные размеры написанной мной бесплатной программы без исключений:

Без исключений:

  • исполняемый файл + зависимости: 330
  • окончательно очищенный исполняемый файл (сборка выпуска): 37

За исключением:

  • исполняемый файл + зависимости: 380
  • окончательно очищенный исполняемый файл (сборка выпуска): 44

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

Компилятор: яблоко gcc4.2 + llvm. Размеры в МБ.

скорость

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

Корректность программы

Похоже на странную причину ... Если выбрасывание не вариант, вы должны написать относительно строгие, правильные, хорошо протестированные программы, чтобы гарантировать, что ваша программа работает правильно, и что клиенты используют интерфейсы правильно (если вы даете мне неверный аргумент или делаете Не проверяйте код ошибки, тогда вы заслуживаете UB). Результат? Качество реализации значительно улучшается, и проблемы быстро решаются.

Простота

Реализации обработки исключений не часто обновляются. Они также добавляют большую сложность, потому что реализация может иметь много много выходных последовательностей. Проще читать и поддерживать очень сложные программы, когда они используют небольшой набор четко определенных, типизированных стратегий выхода, которые вырабатываются клиентом и обрабатываются им. В других случаях реализации могут со временем реализовывать больше бросков, либо их зависимости могут вводить их. Клиенты не могут легко или надлежащим образом защитить от всех этих выходов. Я пишу и обновляю много библиотек, часто происходит развитие и улучшение. Попытка сохранить это в синхронизации с выходными последовательностями исключений (в большой кодовой базе) не будет хорошим расходом времени и, вероятно, добавит много шума и бесполезности. Из-за повышенной корректности программы и большего количества тестов,

История / Существующий Код

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

Downsides

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

джастин
источник
+1 отличная информация! (хотя я не согласен с пунктом «Простота»)
Мартин Ба
Было бы интересно, если бы у вас были только конструкторы без сбоев и то, как вы обрабатываете распределение (- fail).
Мартин Ба
@ Мартин 1) Несогласие вполне нормально. Я понимаю, что большинство разработчиков не согласны с отключением исключений по нескольким причинам. Для простоты это согласуется с корректностью программы. Проблемы / недействительные состояния просто не позволяют путешествовать далеко. Это означает, что они проверяют на сбои и выходят изящно. пост jheriko повторяет это.
Джастин
1
@Martin 2b) распределение немного сложнее. Во-первых, количество выделений кучи уменьшается в количестве и увеличивается в размере - для начала относительно немного выделений кучи. Во-вторых, выделения проходят через пользовательские распределители. Если выделение изначально не предусмотрено для распределителя (например, mallocвозвращается 0), распределитель вводит a, while (1)который имеет переключение контекста, после чего следует другая попытка выделения. В этой кодовой базе раньше новое через распределитель могло возвращать 0, но более новая реализация работала нормально. (продолжение)
Джастин
2
да, есть оболочка, совместимая с stl, через пользовательские интерфейсы распределителя. технически, программа не будет прервана в выпуске; он вводит переключатели контекста (например, позволяет другому потоку работать, что, мы надеемся, освободит часть памяти), повторять попытки и регистрировать, если это не удается - навсегда модульные тесты могут работать без проблем в течение нескольких дней. единственная реальная угроза - огромные ассигнования, многие из которых будут пойманы до запроса. опять же, частота отказов системы также находится на радаре в этой точке; существует вероятность того, что они потерпят неудачу до реализации ядра в таком сценарии.
Justin
7

Google не одобряет исключения в своем Руководстве по стилю C ++ , в основном по историческим причинам:

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

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

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

Из этого правила есть исключение (без каламбура) для кода Windows.

(Акцент редактора)

SuperElectric
источник
5

Qt почти никогда не использует исключения. Ошибки в Qt обозначены кодами ошибок и сигналами. Официально заявленная причина:

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

Почему QT использует так мало исключений?

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

user16764
источник
1
Спасибо. Хорошая информация, хотя мне интересно, есть ли в большинстве баз кода QT исключения, включенные в компиляторе ...
Martin Ba
4

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

Кит Томпсон
источник
1
Это не относится к современным версиям Symbian. TRAP и листы Symbian реализованы как реальные исключения C ++ , но EPOC32 и более ранние версии Symbian полагались на другие средства (setjmp / longjmp, если я правильно помню).
Отто
1
@ OttoHarju: Это то, что я имел в виду, по крайней мере, не напрямую.
Кит Томпсон
3

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

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

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

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

jheriko
источник
1
Нет. Легче неправильно использовать исключения, но кроме этого исключения работают хорошо. Об этом, по крайней мере, так же легко думать, при условии, что все ваши функции безопасны для исключений (и это обычно всего лишь вопрос использования RTTI для всех ресурсов, что вы должны делать в любом случае). Правильное выполнение кодов ошибок может быть чрезвычайно утомительным и может привести к тому, что ваша программа окажется в куче кода обработки ошибок; в этом случае исключения лучше. Помните, что мои поступки не так, как вы, не являются убедительным доказательством того, что мне все равно, что делать правильно.
Дэвид Торнли
... что касается того, почему RTTI плох, это совсем другая история - встроенный RTTI в каждом компиляторе, который я видел, тривиально победим - если вам даже нужен RTTI. Однако все зависит от контекста - такие вещи, как RTTI и исключения могут быть полезными для RAD и корпоративных приложений - им нет места в мире высокопроизводительных (например, игр) вычислений. Я никогда не видел, чтобы игровой движок использовался ни на одной из целей, где их можно было бы отключить ...
jheriko
2
Извините - RAII это то, что я имел в виду. Если вы не используете это, тогда не только исключения будут испорчены. (Тем не менее, динамическое приведение работает очень хорошо в моей работе, и я должен быть обеспокоен производительностью.)
Дэвид Торнли
Интересная тема: я думаю, что исключения допускают более сжатое кодирование обработки ошибок, но коды ошибок более устойчивы, потому что они позволяют обрабатывать ошибки локально (вы можете проверить код возврата непосредственно в вызывающей программе, в то время как исключение может всплыть в стеке вызовов, прежде чем они будут перехвачены и иногда они не перехватываются, что приводит к сбою. Если вы не используете catch (...)).
Джорджио
3

В стандарте кодирования Joint Strike Fighter C ++, разработанном Bjarne et. al. исключения запрещены из-за жестких требований истребителей в реальном времени.

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

Цитируется из C ++ FAQ Бьярне .

Помните, C ++, вероятно, работает с самым разнообразным программным обеспечением из всех языков ...

Маке
источник
JSF ++ также запрещает использование «new» (кроме размещения new) и «delete». Который является одним из ответов на вопрос «как вы справляетесь с новыми неудачами без исключений?»
armb
@armb: да. Смотрите «В этом контексте даже бесплатное размещение в магазине запрещено!» над.
Мак
2

Это важно в дизайне библиотеки C ++. Часто с C-интерфейсом довольно неприятно выбрасывать клиенту исключение из вашей сторонней библиотеки. Суть в том, что если ваша библиотека генерирует сбой, вы исключаете набор клиентов, которые бы ее использовали, учитывая, что она имеет гарантию отсутствия бросков (по любой причине, которую имеет клиент для ограничения на исключения).

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

скоро
источник
1

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

Я должен добавить, что, хотя EC ++ произвел небольшой всплеск, когда он был новым, они, похоже, в основном пошли довольно тихо. Я не уверен, что люди потеряли интерес, или их первая попытка была настолько совершенной, что никто не видел причин возиться с этим в течение десятилетия или около того.<closed captioning for the humor impaired>Yeah, right!</closed captioning>

Джерри Гроб
источник
Просматривая вопросы и ответы - caravan.net/ec2plus/question.html - я бы сказал, что он в значительной степени мертв.
Мартин Ба
1

Хорошим примером является программирование в режиме ядра.

Вы можете использовать C ++ для более чистого кода, но без исключений. Для них нет библиотеки времени выполнения, и при обработке исключений используется слишком много стековой памяти, которая в ядре очень ограничена (я экспериментировал с исключениями C ++ в ядре Windows NT, а выброс и исключение исключений съедает половину доступного стека - очень легко получить переполнение стека и разбить всю систему).

Как правило, вы определяете свои собственные newи deleteоператоры. Также я нашел довольно удобные placement newссылки на a и c ++ 11. Нельзя использовать STL или другие библиотеки пользовательского режима C ++, которые используют исключения.

Сергий
источник
0

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


источник