Чи повинна програма C ++ охоплювати всі винятки та не допускати, щоб винятки перекинулися на межі main ()?


29

Колись мені порадили, що програма C ++ повинна врешті-решт охопити всі винятки. В той час міркування полягали в тому, що програми, які дозволяють виняткам вибухати поза межами main()дивного стану зомбі. Мені це було сказано кілька років тому, і за ретроспективою я вважаю, що спостережуване явище було пов’язане з тривалим генеруванням надзвичайно великих відвалів ядра від проекту, про який йдеться.

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

(Для всіх, хто цікавиться, чому це не було більш очевидним у той час: Проект генерував велику кількість результатів у декількох файлах з декількох процесів, які фактично затіняли будь-яке aborted (core dumped)повідомлення, і в цьому конкретному випадку після смертельної експертизи основних відвалів не було Важлива методика налагодження, тому основні демпінги не задумувались над проблемами. Проблеми з програмою зазвичай не залежали від стану, накопиченого за багато подій протягом тривалої програми, а, скоріше, від початкових вкладів у програму короткотривалої роботи (< 1 годину), тому практичніше було просто перезапустити програму з тими ж входами з налагодження або в налагоджувачі, щоб отримати більше інформації.)

Наразі я не впевнений, чи є якась основна перевага чи недолік вилучення винятків виключно з метою запобігання вилученням виключень main().

Невелика перевага, яку я можу подумати за те, що дозволяє виняткам перекинутись в минуле, main()- це те, що це призводить до того, що результат std::exception::what()буде надруковано на термінал (принаймні, з gcc, складеними програмами в Linux). З іншого боку, цього доцільно досягти, замість того, щоб набрати всі винятки, отримані з std::exceptionта надрукувати результат, std::exception::what()і якщо бажано надрукувати повідомлення за винятком, який не випливає з std::exceptionцього, воно повинно бути спіймане перед від'їздом main(), щоб надрукувати повідомлення.

Скромний недолік, який я можу подумати за те, що дозволяє виняткам перекинутись в минуле, main()- це те, що можуть утворюватися небажані основні звалища. Для процесу, що використовує великий об'єм пам'яті, це може бути неприємністю, а для контролю поведінки ядра демпінгу з програми потрібні спеціальні виклики ОС. З іншого боку, якщо бажаний дамп і вихід ядра, то це може бути досягнуто в будь-який час шляхом виклику, std::abort()а вихід без основного дампа може бути досягнутий у будь-який час шляхом виклику std::exit().

Анекдотично, я не думаю, що я ніколи не бачив what(): ...повідомлення за замовчуванням, надруковане широко розповсюдженою програмою після збоїв.

Які, якщо такі є, є сильними аргументами за чи проти того, щоб дозволити виняткам C ++ перевершити минуле main()?

Редагувати: На цьому сайті є багато загальних питань, що стосуються обміну винятками. Моє запитання стосується виключно винятків C ++, з якими не вдається обробитись, і це стало до кінця main()- можливо, повідомлення про помилку можна надрукувати, але це негайно показати помилку зупинки.




@gnat Моє запитання дещо конкретніше. Йдеться про винятки C ++, які не можна обробляти, а не обробляти винятки на будь-якій мові програмування ні за яких обставин.
Праксеоліт

Для багатопотокових програм все може бути складнішим або екзотичнішим (винятки з wrt)
Василь Старинкевич

1
Хлопці з програмного забезпечення для Ariane 5, безумовно, хочуть, щоб вони потрапили на всі винятки під час першого запуску ...
Eric Towers

Відповіді:


25

Однією з проблем, що дозволяє виключати виключення, є головна, що програма завершиться викликом, до std::terminateякого слід зателефонувати за поведінкою std::abort. Це лише визначена реалізація, якщо розмотка стека виконується перед викликом, terminateщоб програма могла закінчитися без виклику жодного деструктора! Якщо у вас є ресурс, який справді потребував відновлення за допомогою виклику деструктора, ви перебуваєте в солінні ...


12
Якщо у вас є якийсь ресурс, який дійсно потребував відновлення викликом деструктора, ви знаходитесь у маринуванні ... => Враховуючи, що (на жаль) C ++ є досить схильним до аварій, і це не згадуючи, що ОС може вирішити вбити Ваша програма в будь-який час (наприклад, вбивця OOM в Unix), ваша програма (та її оточення) повинні бути налаштовані на підтримку збоїв.
Матьє М.

@MatthieuM. Ви хочете сказати, що деструктори не є хорошим місцем для очищення ресурсів, яке має відбутися навіть під час аварії?
Праксеоліт

6
@Praxeolitic: Я кажу, що не всі збої розкручують стек, наприклад, std::abortце не так, і, таким чином, невдалі твердження також не є. Варто також зазначити, що на сучасних ОС сама ОС очистить багато ресурсів (пам'ять, ручки файлів, ...), які прив’язані до ідентифікатора процесу. Нарешті, я також натякав на "Захист в глибині": не можна сподіватися, що всі інші процеси захищені від помилок і завжди вивільнять отримані ними ресурси (сеанси, добре закінчуючи запис файлів, ...), які ви повинні планувати це ...
Матьє М.

3
@Praxeolitic Ні, ваше програмне забезпечення не повинно пошкоджувати дані під час відключення живлення. І якщо ви можете цим керувати, ви також можете керувати тим, щоб не пошкоджувати дані після необробленого винятку.
користувач253751

1
@Praxeolitic: Насправді це існує. Ви захочете послухати WM_POWERBROADCASTповідомлення. Це працює лише в тому випадку, якщо ваш комп’ютер працює від акумулятора (якщо ви використовуєте ноутбук або ДБЖ).
Брайан

28

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

Для програми, яка не призначена для тривалого використання або поширення широко, може бути прийнятним повідомлення про несподівані помилки будь-яким способом, в якому ОС вирішить це зробити (наприклад, показуючи діалогове вікно про помилку в обличчі в Windows ).

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


Ви впевнені, що виняток C ++ за замовчуванням, який залишає, main() має багато спільного з ОС? ОС може нічого не знати про C ++. Я здогадувався, що це визначається кодом, який компілятор вставляє десь у програму.
Праксеоліт

5
@Praxeolitic: Більше того, що ОС встановлює конвенції, а компілятор генерує код для виконання цих умов, коли програма несподівано припиняється. Суть полягає в тому, що слід уникати несподіваного припинення програми, коли це можливо.
Барт ван Інґен Шенау

Ви лише втрачаєте контроль у межах std C ++. Має бути доступний специфічний для реалізації спосіб вирішення таких «збоїв». C ++ зазвичай не працює у вакуумі.
Мартін Ба

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

11

TL; DR : Що говорить специфікація?


Технічний об'їзд ...

Коли буде викинуто виняток, і жоден обробник не готовий до цього:

  • це реалізація визначена, чи є стек розмотаним чи ні
  • std::terminate називається, що за замовчуванням перериває
  • залежно від налаштувань оточуючого середовища, аборти можуть або не можуть залишити звіт про аварію

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


Чи потрібно виловлювати всі винятки чи ні, врешті-решт, питання конкретизації:

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

Для будь-якої виробничої програми це слід вказати, і ви повинні слідувати специфікації (і, можливо, стверджувати, що це буде змінено).

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


1
Це. Визначальні фактори wrt. В основному виходять за межі С ++.
Мартін Ба

4

Виняток, який ви потрапляєте, дає вам можливість надрукувати приємне повідомлення про помилку або навіть спробувати відновитись після помилки (можливо, просто повторно запустивши програму).

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

Так що це компроміс.


4

Щойно ви знаєте, що вам доведеться робити аборт, продовжуйте і дзвоніть std::terminateвже, щоб зменшити подальшу шкоду.

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

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

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


1

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

Ви не хочете робити збої на публіці (хоча це справді неминуче - програми виходять з ладу, і справді математично перевірена програма, що не дає збоїв, не про що ми говоримо тут). Подумайте про Білла Гейтса BSODing посеред демонстрації - погано, правда? Тим не менш, ми можемо спробувати з'ясувати, чому ми вчинили аварію, а не знову збій таким же чином.

Я ввімкнув функцію Windows Error Reporting, яка створює локальні демпінг-збої за необробленими винятками. Цікаво, чи є у вас символьні файли, пов'язані з вашою (збійною) збіркою. Досить цікаво, оскільки я налаштував цей інструмент, хочу більше вийти з ладу - тому що я вчуся з кожної аварії. Тоді я можу виправляти помилки та менше збоїв.

Тож зараз я хочу, щоб мої програми перекидались на вершину і зазнали краху. Надалі я, можливо, захочу вишукано з'їсти всі мої винятки - але не, якщо я можу змусити їх працювати на мене.

Ви можете прочитати більше про локальні збитки тут, якщо вас цікавить: Збір дампів у режимі користувача


-6

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

Можливо, у вас є глобальний спробу / ловіння, яке друкує елегантне повідомлення, яке повідомляє користувачеві про те, що виникла невідправна помилка та що їм потрібно надіслати електронною поштою bob в ІТ чи щось подібне, але збої є абсолютно непрофесійними та неприйнятними. Це банально для запобігання, і ви можете повідомити користувача про те, що щойно сталося, і як виправити речі, щоб це не повторилося.

Збій - це суто любительська година.


8
Отже, краще зробити вигляд, що все працює правильно, і замість цього замінити все постійне сховище на безглуздість?
Дедуплікатор

1
@Deduplicator: Ні: ви завжди можете зловити виняток і впоратися з ним.
Джорджіо

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

8
але збої є абсолютно непрофесійними та неприйнятними => витрачати час на роботі з непотрібними функціями непрофесійно: аварійне завершення роботи має значення, якщо воно має значення для кінцевого користувача, якщо цього не відбувається, то дозволити його збій (і отримання звіту про збій, із скиданням пам’яті) є більш економічним.
Матьє М.

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