У різних дисциплінах програмного забезпечення існує багато філософій про те, як бібліотеки повинні справлятися з помилками чи іншими винятковими умовами. Кілька з тих, кого я бачив:
- Повернути код помилки з результатом, повернутим аргументом вказівника. Це те, що робить PETSc.
- Повернення помилок за вартовим значенням. Наприклад, malloc повертає NULL, якщо він не міг виділити пам'ять,
sqrt
поверне NaN, якщо ви перейдете в негативному числі і т. Д. Цей підхід використовується в багатьох функціях libc. - Киньте винятки. Використовується в угоді.II, Трилінос тощо.
- Повернути тип варіанту; наприклад, функція C ++, яка повертає об'єкт типу,
Result
якщо він працює правильно і використовує тип,Error
щоб описати, як він не вдавсяstd::variant<Error, Result>
. - Використовуйте ствердження та збої. Використовується в p4est та деяких частинах igraph.
Проблеми з кожним підходом:
- Перевірка кожної помилки вводить багато зайвого коду. Значення, в яких буде зберігатися результат, завжди повинні бути оголошені спочатку, вводячи безліч тимчасових змінних, які можуть бути використані лише один раз. Цей підхід пояснює, яка помилка виникла, але важко визначити, чому або, для глибокого стека викликів, де.
- Випадок помилки легко ігнорувати. Крім того, багато функцій навіть не можуть мати значущої вартості, якщо весь діапазон типів виводу є правдоподібним результатом. Багато з тих же проблем, що і №1.
- Можливо лише в C ++, Python тощо, а не в C або Fortran. Можна імітувати на C за допомогою чаклуна setjmp / longjmp або libunwind .
- Можливо лише в C ++, Rust, OCaml тощо, а не в C або Fortran. Можна імітувати на C за допомогою макро-чаклунства.
- Можливо, найбільш інформативний. Але якщо ви скористаєтесь таким підходом до, скажімо, бібліотеки С, для якої ви потім пишете обгортку Python, дурна помилка, як передача індексу поза межами масиву, призведе до збою інтерпретатора Python.
Значна частина порад в Інтернеті щодо поводження з помилками написана з точки зору операційних систем, вбудованої розробки або веб-додатків. Збої неприйнятні, і ви повинні турбуватися про безпеку. У наукових застосувань таких проблем немає майже в однаковій мірі.
Ще один розгляд полягає в тому, які види помилок підлягають відновленню чи ні. Вихід з ладу не може бути відновлений, і, у будь-якому випадку, вбивця поза пам'яті ОС дістанеться до нього, перш ніж це зробити. Індекс поза межами розміру масиву також не підлягає відновленню. Для мене як користувача найприємніше, що може зробити бібліотека - це збій з інформативним повідомленням про помилку. З іншого боку, невдача, скажімо, ітеративного лінійного розв'язувача для конвергенції могла бути відшкодована за допомогою вирішувача прямої факторизації.
Як наукові бібліотеки повинні повідомляти про помилки та очікувати, що їх обробляють? Звичайно, я розумію, що це залежить від того, на якій мові бібліотека реалізована. Але, наскільки я можу сказати, для будь-якої достатньо корисної бібліотеки люди захочуть називати її якоюсь іншою мовою, ніж тією, якою вона реалізована.
В інший бік, я думаю, що підхід №5 може бути значно вдосконалений для бібліотеки С, якщо він визначає глобальний покажчик функції обробника тверджень як частина публічного API. Обробник тверджень за замовчуванням повідомляє про номер файлу / рядка та збої. Прив'язки C ++ для цієї бібліотеки визначали б новий обробник тверджень, який замість цього видає виняток C ++. Аналогічно, прив'язки Python визначали б оброблювач тверджень, який використовує API CPython для викидання винятку Python. Але я не знаю жодного прикладу, який би використовував такий підхід.