Чи існують випадки реального життя для C ++ без винятку? [зачинено]


40

В Коли використовувати C над C ++ та C ++ над C? є заявка wrt. для виключення розміру коду / C ++:

Джері відповідає (серед інших пунктів):

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

на що я запитав, чому це було, на що Джеррі відповів:

головне, що C ++ включає обробку виключень, що (принаймні зазвичай) додає певного мінімуму до розміру, що виконується. Більшість компіляторів дозволить вам відключити обробку виключень, але коли ви зробите результат, це не зовсім C ++. (...)

що я не дуже сумніваюся на технічному рівні реального світу.


Тому мені цікаво (суто цікаво) почути приклади реального світу, коли проект вибрав C ++ як мову, а потім вирішив відключити винятки. (Не просто просто "не використовувати" винятки в коді користувача, але і відключити їх у компіляторі, щоб ви не могли кидати або ловити винятки.) Чому проект вирішив це зробити (все ще використовуючи C ++, а не C, але ні винятки) - які / були (технічні) причини?


Додаток: Для тих, хто бажає детальніше розглянути свої відповіді, було б непогано докладно деталізувати, як поводиться із наслідками відсутності виключень:

  • Колекції STL ( vector, ...) не працюють належним чином (про помилку виділення не можна повідомити)
  • new не вмію кидати
  • Конструктори не можуть вийти з ладу

1
JSF C ++. Струми та винятки не поєднуються.
Кодер

1
Деякі просвітливі відомості про проблеми із винятками (та C ++ загалом) 250bpm.com/blog:10
Ambroz Bizjak

1
@AmbrozBizjak - Я цитую DeadMG з коментаря до публікації, на яку ви посилаєтесь: цитата: "Мені здається, ви не розумієте винятків". Я згоден. Автор чітко заплутався у поводженні з винятками, дивлячись на приклади, які він дає у цій публікації.
Мартін Ба

Відповіді:


35

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


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


Є якісь технічні причини, хороші чи погані, що б там не було, це не моя порада, але причини, які я чув:

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

Я думаю, що винятки можуть бути корисними навіть в іграх, але це правда, що в консольних іграх це не дуже корисно.


Оновлення:

Я додаю тут ще один дивний приклад: LLVM / CLang не використовують виняток, ані RTTI з наступних причин :

Намагаючись зменшити розмір коду та виконуваного файлу, LLVM не використовує RTTI (наприклад, динамічний_cast <>) або винятки. Ці дві мовні функції порушують загальний C ++ принцип "ви платите лише за те, що використовуєте", викликаючи виконувану функцію, навіть якщо винятки ніколи не використовуються в базі коду або якщо RTTI ніколи не використовується для класу. Через це ми їх вимикаємо глобально в коді.

Однак, LLVM широко використовує ручну прокручену форму RTTI, яка використовує шаблони, такі як isa <>, cast <> та dyn_cast <>. Ця форма RTTI є опцією та може бути додана до будь-якого класу. Він також значно ефективніший, ніж динамічний_cast <>.

CLang добре відомий своєю швидкістю компіляціїrr та явними помилками, але також і одним рідкісним компілятором, який має дійсно простий у виконанні код.


9
(3): вам потрібна стратегія управління помилками, незважаючи ні на що, якщо ви збираєтеся йти разом з (2). Виникнуть проблеми, і ваше програмне забезпечення консолі повинно вийти з ладу і витончено. Мені не очевидно, що уникнення виключень цілком полегшує це.
Девід Торнлі

6
Усі чудові приклади чітко контрольованих середовищ виконання, коли помилки обробляються належним чином і немає "виняткових" подій.
Патрік Хьюз

1
@DavidThornley Тому що ви припускаєте, що система консолі управлятиме деякими помилками, але це не буде. Добре, можливо, це буде на PS3 або XBox, але це не дозволить пройти тести розробника консолі. У всякому разі, прошивка більшості сучасних консолей не така гнучка, як ви могли подумати. Консольна гра повинна мати доступ до майже всього обладнання консолі, тож ви можете думати про гру, яка працює на консолі, майже як якщо б це була також "ОС" консолі ... тим більше, що ми маємо потужні консолі, тим менше це правда. Тож я згоден з вами, що це здається дивним для деяких випадків.
Клаїм

2
Це хороша відповідь, я хотів би також додати, що навіть якщо виняток заграв і представив користувачеві помилку, що він або вона повинен робити з цим за допомогою D-pad? Єдине реальне рішення для кінцевого користувача - це перезапуск.
анонс

2
Я вилучив приклад безглуздості, оскільки хтось із їхньої команди сказав, що НЕ ВИКЛЮЧАЄ, це не означає "відсутність винятку", але "не виняток із правил". Дивіться permalink.gmane.org/gmane.comp.lib.boost.devel/231377
Klaim

9

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

Ось мої основні причини відключення:

Бінарна сумісність

Перетинання меж мови та перекладу не є загальновизначеним або визначеним. Якщо ви хочете гарантувати, що програма працює в межах визначеної поведінки, вам потрібно буде зробити карантинний виняток у точках виходу модуля.

Виконаний розмір

Ось бінарні розміри безкоштовної програми для винятку, яку я написав, створеної без та з урахуванням винятків:

Без винятку:

  • виконуваний + залежності: 330
  • остаточний знятий виконуваний файл (збірка версії): 37

За винятком:

  • виконуваний + залежності: 380
  • остаточний знімається виконуваний файл (версія версії): 44

Нагадування: Це колекція бібліотек та програм, що містять нульові кидки / вилучення. Прапор компілятора робить включити виключення в стандартній бібліотеці C ++. Тому вартість в реальному світі становить більше 19%, що бачиться на цьому прикладі.

Укладач: яблуко gcc4.2 + llvm. Розміри в МБ.

Швидкість

Незважаючи на термін "виключення з нульовою вартістю", вони все ще додають накладні витрати, навіть коли ніколи нічого не кидає. У вищенаведеному випадку це критична для продуктивності програма (обробка сигналів, генерація, презентація, перетворення, з великими наборами даних / сигналами тощо). Винятки не є необхідною особливістю в цій конструкції, тоді як продуктивність дуже важлива.

Правильність програми

Це здається дивним причин ... Якщо кидання не є варіантом, ви повинні написати відносно суворі, правильні, добре перевірені програми, щоб гарантувати, що програма працює правильно, і щоб клієнти правильно використовували інтерфейси (якщо ви даєте мені поганий аргумент чи ви робите це не перевіряйте код помилки, тоді ви заслуговуєте на UB). Результат? Якість впровадження значно покращується, а проблеми швидко виправляються.

Простота

Реалізації з обробкою винятків часто не оновлюються. Вони також додають великої складності, оскільки реалізація може мати безліч багатьох безлічі вихідних послідовностей. Простіше читати та підтримувати дуже складні програми, коли вони використовують невеликий набір чітко визначених, типізованих стратегій виходу, які підключаються до клієнта та керуються ними. В інших випадках реалізації можуть з часом реалізувати більше кидків або їх залежності можуть ввести їх. Клієнти не можуть легко або належним чином захиститися від усіх цих виходів. Я пишу та оновлюю багато бібліотек, спостерігається часта еволюція та вдосконалення. Спроба зберегти, що все синхронізується з послідовностями виходу виключень (у великій кодовій базі), не буде корисним часом, і, швидше за все, додасть багато шуму та суворості. Через підвищену правильність програми та більше тестів,

Історія / існуючий код

У деяких випадках вони ніколи не були представлені з історичних причин. Існуюча база коду не використовувала їх, зміна програм може зайняти людино-роки і зробити це справді некрасивим, оскільки вони перекриваються конвенціями та реалізаціями.

Недоліки

Звичайно, є і недоліки, найбільші - це незмінність (включаючи двійкові) з іншими бібліотеками, і той факт, що вам доведеться реалізувати хорошу кількість програм, щоб відповідати цій моделі.


+1 відмінна інформація! (хоча я не згоден з абзацом простоти)
Мартін Бат

Було б цікаво, якщо у вас є лише конструктори без відмов і як ви обробляєте розподіл (- пошкодження).
Мартін Ба

@Martin 1) Не погоджуючись цілком нормально. Я розумію, що більшість розробників не згодні з відключенням винятків з кількох причин. Для простоти це поєднується з коректністю програми. Проблеми / недійсні стани просто не мають права їздити далеко. Це означає, що вони перевіряють несправності та виходять витончено. Пост jheriko перегукується з цим.
Джастін

1
@Martin 2b) розподіл трохи складніший. По-перше, кількість виділень купи зменшується в кількості і збільшується в розмірах - для початку порівняно мало купових виділень. По-друге, асигнування проходять через спеціальні розподільники. Якщо виділення спочатку не передбачено для алокатора (наприклад, mallocповернення 0), алокатор вводить атрибут, while (1)який має контекстний перемикач, після чого проводиться інша спроба розподілу. У цій кодовій базі раніше було те, що нове через алокатор може повернути 0, але новіша реалізація працює добре. (продовження)
Джастін

2
так, для користувальницьких інтерфейсів розподільника є сумісна оболонка для stl. технічно програма не буде припиняти випуск; він введе контекстні комутатори (наприклад, дозволить іншій нитці працювати, яка, сподіваємось, звільнить деяку пам’ять), повторіть і ввійдіть, якщо це не вдасться - назавжди. одиничні тести можуть працювати протягом днів без проблем. Єдиною реальною загрозою є величезні асигнування, багато з яких будуть спіймані перед тим, як вимагати. знову ж таки, рівень відмов системи в цей момент також знаходиться на радарі; існує ймовірність того, що вони не зможуть перед базовою реалізацією в такому сценарії.
Джастін

7

Google не схвалює винятків у Посібнику зі стилів C ++ , здебільшого з історичних причин:

З їхнього боку, переваги використання винятків перевищують витрати, особливо в нових проектах. Однак для існуючого коду введення винятків має значення для всіх залежних кодів. Якщо винятки можуть поширюватися за межі нового проекту, також стає проблематичним інтегрувати новий проект у існуючий код, не обмежений винятком. Оскільки більшість існуючих кодів C ++ в Google не готові боротися з винятками, прийняти новий код, який генерує винятки, порівняно важко.

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

Наші поради проти використання винятків базуються не на філософських чи моральних засадах, а на практичних. Оскільки ми хотіли б користуватися нашими проектами з відкритим кодом в Google, і це важко зробити, якщо ці проекти використовують винятки, нам також слід порадити щодо виключень у проектах Google з відкритим кодом. Напевно, все було б інакше, якби нам довелося робити це знову з нуля.

Існує виняток із цього правила (не призначене каламбур) для коду Windows.

(Наголос редактора)


5

Qt майже ніколи не використовує винятку. Помилки в Qt позначаються кодами помилок та сигналами. Офіційно зазначена причина:

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

Чому QT використовує так мало винятків?

Сьогодні одна поширена критика Qt полягає в тому, що безпека його винятків не є повною.


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

4

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


1
Це не стосується сучасних версій Symbian. Symbian TRAP та листя реалізуються як справжні винятки C ++ , але EPOC32 та старіші версії Symbian покладаються на інші засоби (setjmp / longjmp, якщо я правильно пам'ятаю).
otto

1
@OttoHarju: Це я мав на увазі під «принаймні не безпосередньо».
Кіт Томпсон

3

Я / ніколи / не використовую винятки. Для цього є низка причин, але дві основні причини полягають у тому, що я ніколи не потребував їх для створення надійного коду і що вони знижують продуктивність під час виконання.

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

З самими винятками немає нічого серйозного неправильного / якщо ви не дбаєте про продуктивність роботи / і не встигаєте зробити щось належним чином - вони є мовною особливістю для обробки помилок і хорошою заміною правильного механізму обробки помилок. Однак майже завжди є / кращі / способи вирішення помилок - як, наприклад, за власною логікою (важко до цього розібратися - це майже здоровий глузд). Якщо це не ваша помилка, але походить з бібліотеки (наприклад, стандартної бібліотеки), тоді ви повинні поважати вибір винятків або мати певний збій, але я завжди сумніваюся в цьому. Я ніколи не бачив ситуації, коли винятки насправді були найкращим рішенням.

Твердження є більш налагоджуваними, коди помилок мають меншу вагу ... між ними, якщо їх правильно використовувати, ви легше читати, налагоджувати та підтримувати код, який швидше. Його виграє в усьому ...


1
Ні. Винятки легше зловживати, але крім цього винятки працюють добре. Принаймні так просто міркувати, за умови, що всі ваші функції безпечні для винятків (а це, як правило, лише питання використання RTTI для всіх ресурсів, що ви все одно повинні робити). Правильне виконання кодів помилок може бути вкрай стомлюючим і може призвести до поховання вашої програми в купі коду обробки помилок; у цьому випадку винятки є кращими. Пам'ятайте, що мої справи не відрізняються від вас, це не є переконливим свідченням того, що мені не байдуже робити все правильно.
Девід Торнлі

... що стосується того, чому RTTI поганий, це зовсім інша історія - вбудований RTTI у кожному компіляторі, який я бачив, є надзвичайно вигідним - якщо вам навіть потрібен RTTI. Однак все це стосується контексту - такі речі, як RTTI та винятки, можуть бути чудовими для RAD та корпоративних програм - вони не мають місця у світі високопродуктивних (наприклад, ігор) обчислень. Я ніколи не бачив двигуна виробничих ігор, використовуючи будь-яку ціль, де їх можна було вимкнути ...
jheriko

2
Вибачте - RAII - це те, що я мав на увазі. Якщо ви не використовуєте це, то не лише винятки будуть зіпсовані. (Однак динамічні ролі дуже добре працюють у моїй роботі, і мені доводиться перейматися продуктивністю.)
Девід Торнлі

Цікава тема: Я думаю, що винятки дозволяють більш коротко обробляти помилки з кодуванням, але коди помилок є більш надійними, оскільки вони дозволяють обробляти помилки на локальному рівні (ви можете перевірити код повернення негайно у абонента, тоді як виняток може перекинути стек викликів, перш ніж вони будуть спіймані а іноді їх не ловлять, що призводить до аварії. Якщо ви не використовуєте улов (...)).
Джорджіо

3

У стандартному кодуванні стандарту C ++ Joint Strike Fighter від Bjarne et. ін., винятки заборонені через жорсткі вимоги реактивних винищувачів.

JSF ++ призначений для роботи в режимі реального часу та критично важливих для безпеки програм (програмне забезпечення для управління польотами). Якщо обчислення триватиме занадто довго, хтось може загинути. З цієї причини ми маємо гарантувати час відгуку, і ми не можемо - при поточному рівні підтримки інструментів - робити це за винятки. У цьому контексті навіть заборонено розміщення безкоштовних магазинів! Власне, рекомендації JSF ++ щодо поводження з помилками імітують використання винятків в очікуванні дня, коли у нас є інструменти, щоб робити все правильно, тобто використовувати винятки.

Цитується з C ++ FAQ Bjarne .

Пам'ятайте, що C ++, ймовірно, працює з найрізноманітнішим програмним забезпеченням усіх мов ...


JSF ++ також забороняє використовувати "new" (крім розміщення new) та "delete". Яка одна відповідь на запитання "як ти
поводишся

@armb: Так. Див. "У цьому контексті навіть заборонено розміщення безкоштовних магазинів!" вище.
Макке

2

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

Особисто я бачив, як винятки зловживають, коли команді кажуть "кинути виняток", коли трапляється щось не так страшне. Звичайно, ви бачите помилку тут - виняток було закинуто в код, перш ніж хтось зрозумів, що з ним робити. У цьому проекті було кілька аварій, оскільки ці глибокі кидки періодично відступали.


1

Можливо, в цьому плані варто згадати вбудований C ++ . Вбудований 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.