Коли твердження повинні залишатися у виробничому коді? [зачинено]


166

Зараз у дискусії comp.lang.c ++ ведеться дискусія про те, чи слід твердження, які в C ++ існують лише у налагодженнях за замовчуванням, слід зберігати у виробничому коді чи ні.

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

Під твердженням я маю на увазі:

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

Я не обов'язково говорю про C або C ++.

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

Яка ваша думка / досвід?

Ура,

Карл

Дивіться відповідне запитання тут


Відповіді та оновлення

Гей, Грем,

Твердження є помилковим, чистим і простим, і тому слід поводитися як один. Оскільки з помилкою слід звертатися в режимі випуску, то твердження вам дійсно не потрібні.

Ось чому я віддаю перевагу слову «жучок», коли говорять про твердження. Це робить речі набагато зрозумілішими. Для мене слово "помилка" занадто розпливчасте. Файл, що відсутній - це помилка, а не помилка, і програма повинна з цим боротися. Спроба знеструмити нульовий покажчик - це помилка, і програма повинна визнати, що щось пахне поганим сиром.

Отже, слід перевірити покажчик із твердженням, але наявність файлу з нормальним кодом обробки помилок.


Трохи поза темою, але важливий момент у дискусії.

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

Карл


Томас,

Так, у мене є Code Complete, і я повинен сказати, що я абсолютно не згоден з цією порадою.

Скажіть, що ваш спеціальний розподільник пам’яті накрутив і нулює шматок пам’яті, який все ще використовується деяким іншим об’єктом. Мені здається нульовим покажчиком, що цей об'єкт регулярно відмінюється, і одне з інваріантів полягає в тому, що цей покажчик ніколи не буває нульовим, і у вас є кілька тверджень, щоб переконатися, що він залишається таким. Що робити, якщо вказівник раптом є нульовим. Ви просто якщо () навколо нього, сподіваючись, що він працює?

Пам'ятайте, ми тут говоримо про товарний код, тому немає нарви на налагоджувач та перевірку місцевого стану. Це справжня помилка на машині користувача.

Карл


3
Є цікавий пов’язаний пост у SE Software Software (хоча дискусія орієнтована на c ++): Чи повинні бути твердження у версії версій
сонник,

Відповіді:


84

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


16
@jkschneider: одиничні тести призначені для тестування речей, що входять до коду програми. твердження полягають у тому, щоби припущення, на яких ґрунтується код, насправді є дійсними, перш ніж код продовжить обробку за цими припущеннями. Вони "документація" в тому сенсі, що, якщо виявиться, що це не так, програма перерве, висловить припущення та вкаже, що припущення не витримало. Ви також можете прочитати твердження як документальне підтвердження цього в коді.

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

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

59

Дозвольте мені навести кодекс Стіва Макконнелла. Розділ про твердження - 8.2.

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

Однак пізніше в цьому ж розділі даються ці поради:

Щоб отримати надійний код, будь-ласка, утверджуйте, а потім в будь-якому випадку обробляйте помилку.

Я думаю, що поки продуктивність не є проблемою, залиште це твердження, а не покажіть повідомлення, нехай він записує у файл журналу. Я думаю, що порада є також у «Кодексі», але я зараз не знаходжу її.


7
Я думаю, що друга цитата з Code Complete означає, що у вас має бути затвердження - яке буде складено у виробничому коді - і у вас також має бути "if (! Condition) {AttemptGracefulRecovery ();}", тобто don нехай порушення інваріантів програми збої в програмі.
yoyo

34

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

якщо це не варто вимірювати, щоб довести, що це більш ефективно, то не варто жертвувати ясністю для азартних ігор. "- Стів Макконнелл 1993

http://c2.com/cgi/wiki?ShipWithAssertionsOn


11
Насправді, найгірше, що трапляється, це коли збій коду через щось, що НЕ є ствердженням. Якщо код, безумовно, згортається згодом зі стовідсотковою ймовірністю, то ствердження обов'язково повинно бути там. Якщо ви дерефіруєте вказівник, вам доведеться неявно стверджувати, що він раніше не був нульовим. Якщо розділити на число, ви стверджуєте, що це не нуль. Вийміть твердження, і всі місця збоїв НЕ ДОКУМЕНТИ. Справжньою проблемою є не структурування програми, щоб дозволити краху підсистем і перезапустити сторожовий пес.
Роб

5
assert ref != null;відрізняється від того, що if (ref == null) throw new IllegalArgumentException();ви не повинні використовувати перший для передумов, які можуть бути помилковими. Вам потрібно використовувати assertречі, які не можуть бути помилковими. Наприклад, щоб int i = -1 * someNumber; i = i * i;потім нагадати людям про те, що iпозитивно,assert i > 0;
Капітан Людина

1
"Насправді, найгірше, що трапляється, коли код виходить з ладу через щось, що НЕ є затвердженням. Якщо код, безумовно, згортається пізніше зі 100% -ною ймовірністю, то затвердження обов'язково має бути там." - це хибна дихотомія.
Роб Грант

2
@RobertGrant: Для багатьох програм збої - це далеко не найгірше, що може статися. Для програми, яка повинна перевірити надійність конструкції будівлі чи моста, неправильно повідомляти про те, що конструкція є здоровою, може бути приблизно найгіршим, що вона могла зробити. Для програми, яка піддається зовнішньому світу, але має доступ лише до читання до конфіденційних даних, витік цих даних може бути гіршим, ніж майже все, що може зробити програма. Думка про те, що аварія без значущої вказівки на причину - "найгірше, що могло статися", ігнорує багато небезпек, які набагато гірші.
supercat

@supercat Я не погодився з цитованим коментарем.
Роб Грант

21

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

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

Більш цікаве питання таке: на етапі тестування, коли ви вимикаєте твердження?


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

9
Якщо це неминуче, ви зірвете, задавши нульовий покажчик, переданий у fn; не існує вибору явного вирішення цього питання. У вас або є якийсь спосіб витонченого поводження з умовою (бо вона може надходити від вхідного зовнішнього світу), або ви врізаєтесь у ДОКУМЕНТОВАНЕ місце з утвердженням, а не на випадковому місці, яке може зіпсувати речі на шляху. Як поводиться Ассрт, все-таки має бути рішенням, що відповідає модулю. Можливо, ваш сторожовий собака відновить процес, або ви витираєте шматок пам'яті для цього модуля, щоб скинути його до стану запуску (м'який об'єкт "перезавантажитися").
Роб

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

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

1
Твердження часто сповільнюють систему. Оскільки вони не призначені для виробництва, вони можуть бути повільними та неефективними, що може знадобитися для виконання певних тестів. Наприклад, Microsoft одного разу додала в Excel функцію швидкого перерахунку. Коли одна комірка змінилася, ця функція обмежила перерахунок лише клітинами, які потребували її. Вони перевірили це за твердженням, яке перерахувало весь аркуш розсилки та порівняло результати. Це змусило розробку версії реально повільно, але вона також вилучила багато помилок. Коли вони випустили цю функцію, вона виявилася дуже надійною.
MiguelMunoz

16

Твердження ніколи не повинні залишатися у виробничому коді. Якщо певне твердження здається, що воно може бути корисним у виробничому коді, то воно не повинно бути твердженням; це повинно бути час виконання перевірки помилок, тобто що - то кодується таким чином: if( condition != expected ) throw exception.

Термін "твердження" позначає "перевірку лише для розробки, яка не проводитиметься на полі".

Якщо ви почнете думати, що твердження можуть потрапити в поле, то неминуче ви також почнете робити інші небезпечні думки, як-от задумуватися, чи дійсно якесь твердження варто зробити. Не існує твердження, яке не варто робити. Ви ніколи не повинні запитувати себе "я повинен це стверджувати чи ні"? Ви повинні лише запитати себе: "Чи щось я забув стверджувати?"


6

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

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


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

5

У моєму C ++ я визначаю REQUIRE (x), який схожий на assrt (x), за винятком того, що він кидає виняток, якщо твердження не працює в версії версії.

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

Вони кажуть, що не слід показувати ці твердження повідомленням кінцевим користувачам, оскільки вони не зрозуміють їх. Так? Кінцеві користувачі можуть надіслати вам електронний лист із знімком екрана чи текстом повідомлення про помилку, що допоможе вам налагодити. Якщо користувач просто каже, що "вийшов з ладу", у вас є менше можливостей виправити це. Було б краще надсилати до себе повідомлення про відмову автоматично через Інтернет, але це працює лише в тому випадку, якщо користувач має доступ до Інтернету і ви можете отримати їх дозвіл.


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

4

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


2

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

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


2

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

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

Сказавши це, ми також намагаємось уникати тверджень про виробництво у критичних робочих процесах.


5
Я б назвав ваше "виробниче твердження" "винятком" і кодував його як таке.
DaveWalley

1
Ви, мабуть, праві, але продукт спочатку був написаний на C. Навіть коли ми змінили його на C ++, компілятори, які ми використовували на деяких наших платформах, не підтримували винятків. Більшість старого коду не була переписана на C ++, тому ці твердження досі використовуються.
Graeme Perrow

1

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


Стверджувати: констатувати факт чи переконання. Вони не для тестування (лише), а для того, щоб викласти те, що ви вважаєте правдивим (тобто ваші припущення), щоб ваша програма не продовжувалась, якщо ваші припущення з якихось причин помиляються. Наприклад, це правильне використання тверджень: assert (pow (1,0) <1). Це насправді не підходяще місце для перевірки помилок, тому що якщо це неправда, то майже вся сучасна математика помиляється, і ... ну як би ти взагалі почав це впоратися? Поводження з невірним припущенням виходить за межі програми; ти береш це на віру. Але ви все одно перевіряєте.

1

Я вважаю, що найкраще обробляти всі помилки, що знаходяться в обсягах, і використовувати твердження для припущень, які ми стверджуємо, ЩО ВИСТАЛЬНІ.

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

Однак скажімо, що ваш fopen () задокументований як завжди повертає дійсну, відкриту ручку файлу. Ви відкриваєте файл і передаєте його у свою функцію readfile ().

Ця функція readfile, в цьому контексті, і, напевно, відповідно до специфікації проекту, може припустити, що він отримає дійсний файл ptr. Отже, було б марно додавати код обробки помилок для негативного випадку в такій простої програмі. Однак слід хоча б документувати припущення, якось - переконатися якось --- що це насправді так, перш ніж продовжувати його виконання. Він не повинен ПРИСТУПНО припускати, що він завжди буде дійсним, якщо він викликаний неправильно, або копіюється / вставляється, наприклад, в якусь іншу програму.

Отже, readfile () {assert (fptr! = NULL); ..} доречно в цьому випадку, хоча повномасштабне поводження з помилками не є (ігнорування факту, що насправді для читання файлу все-таки знадобиться деяка система обробки помилок).

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


1

Припустимо, фрагмент коду є у виробництві, і він потрапляє у твердження, яке, як правило, спрацьовує. Твердження знайшло помилку! За винятком цього немає, оскільки твердження вимкнено.

То що ж відбувається зараз? Або програма (1) аварійно завершиться збоєм у точці, яка надалі буде вилучена з джерела проблеми, або (2) почнеться весело до завершення, ймовірно, дасть неправильний результат.

Жоден сценарій не запрошує. Залиште твердження активними навіть у виробництві.


0

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

Я пропоную приклад

file = create-some-file();
_throwExceptionIf( file.exists() == false, "FILE DOES NOT EXIST");

проти

file = create-some-file();
ASSERT(file.exists());

Як додаток поводиться з твердженням? Я віддаю перевагу старому try catchметоду поводження зі смертельними помилками.


2
Винятки становлять незвичайні ситуації, з якими ви очікуєте стикатися в робочому додатку, як у вашому прикладі тут. Твердження стосуються ситуацій, з якими ви очікуєте, що ніколи не зіткнетесь. Отже, якщо ви зіткнулися з ними, у вашому коді має бути помилка. У вашому прикладі і винятком є ​​явно правильний підхід. Але твердження все ще дуже корисні для лову помилок.
МігельМуноз

0

Більшу частину часу, коли я використовую твердження в java (ключове слово assrt), я автоматично додаю деякі виробничі коди після. Відповідно до випадку це може бути повідомлення про реєстрацію, виняток ... або нічого.

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


0

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

Є кілька моментів, щоб уникнути включення тверджень у виробничий код: 1. Ви не хочете, щоб ваш кінцевий користувач бачив повідомлення типу "ASSERTION не вдалося MyPrivateClass.cpp рядок 147. Кінцевий користувач НЕ ви QA інженер. 2. ASSERTION може впливати на продуктивність

Однак є одна вагома причина, щоб залишити твердження: ЗАСТОСУВАННЯ може вплинути на продуктивність та терміни, і, на жаль, це іноді має значення (особливо у вбудованих системах).

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

~ Іцик


Ні, твердження є для припущених фактів. Якщо наш код повертається 27, коли передбачається, що ВЖЕ повернеться 25, проблема також може бути помилкою у наших фізичних припущеннях про Всесвіт: можливо, ці два біти перейшли на 5 можливих значень, вперше в історії обчислень. Твердження існує, щоб підтвердити, що ваш код все ще працює за тими припущеннями, для яких він був написаний. Якщо фізика вийде з вікна, ваш код повинен помітити, залиште привід в спокої і киньте, поки він попереду;) Але так, це не помилка в коді, а який тип помилок ви могли б зробити?

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

1
це не НЕОБХІДНО означає, що є помилка. Наприклад, багато проектів, в яких я беру участь, містять перелік припущених фактів у документації. Ці припущення є для того, щоб захистити код розробників від називання баггі, коли ділові люди могли сказати їм неправильну річ або коли третя сторона не має інформації про конкретні змінні. Твердження можуть бути використані для перевірки того, що програма повинна / не повинна працювати, чи сторонні системи правильні, а не лише те, чи правильна вона ІТ.

-8

Твердження є помилковим, чистим і простим, і тому слід поводитися як один.

Оскільки з помилкою слід звертатися в режимі випуску, то твердження вам дійсно не потрібні.

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


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

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