?

Log in

No account? Create an account
nyaload

Журнал Пушыстого

Журнал Пушыстого

Previous Entry Share Next Entry
exceptions
nyaload
_winnie
1. Относительно checked exceptions в Java (список разрешённых исключений в декларации метода):
у меня нет проблем с мего-списком исключений, такой список от непонимания кода и непонимания как транслировать исключения. И от того, что для своего исключения из 3-х строчек надо создать ещё один файл FooBarException в проекте.

Есть зато проблема когда библиотечный код вызывает мой. Я не могу прозрачно перекинуть свои исключения обратно в свой код. Типичные примеры:

1)container.forEach( мой код )
2)
class MyClass: extends LibraryInterface
{
@override
void execute() { мой код }
}


Вчера пришлось переписать такой forEach на for из-за того что он звал функцию которая звала функцию в которой добавилось IOException.

2. Моя точка зрения на жизнь с исключениями:
Часто хватает одно общее на всех исключение, единственное содержание которого - строка об ошибке. Ну и добавлять в правильных местах
catch(error_description)
  throw UniException("Save Game failed\n" + error_description)
---
catch(error_description)
  throw UniExceptioncan't load file " + FileName + '\n') + error_description


При этом можно выдать внятное сообщение об ошибке, где для каждой ошибки пишется почему она произошла. В культуре использования int как возвращаемого кода ошибки такое практически невозможно и не делается.
не могу загрузить уровень 'level1'
  не могу загрузить данные из level1/chunks.zip
    не могу распарсить файл 'data/chunk_03_07.xml'


Таким образом, достаточно одного исключения на все случаи жизни. Возможно, стоит только отделить внешние ошибки ("нет файла") от диагностики внутренней логики ("сработал assert", "выход за пределы массива"), но иногда бывают запутанные случаи. Например, разработчик полагается на проверку индекса в массиве для входных данных ("дан файл со списком вершин, дан файл со списком индексов вершин треугольников") или делит на число введённое пользователем, полагаясь на ZeroDivisonException.

С локализацией технической информации никто не заморачивается, так как пользователь-блондинка всё равно не сможет понять что делать, даже если информация на родном языке. Обычно есть просто кнопка "послать разработчику" или "позвать сына". Тем не менее, можно добавить LOC_ID в своё универсальное исключение.

3. Геймдевелоперы живут без исключений, они им не нужны, часто можно вместо throw показывать MessageBox с описанием и тут же делать ExitProcess. Тем не менее, стоит предусмотреть вложенную диагностику ошибок (см. выше пример с вложенной диагностикой при загрузке уровня). Для этого нужен макрос похожий на макрос для профайлера секций,
{
  SET_ERROR_CONTEXT("Loading level '%s'", level_name.c_str())
  ....
}

SET_ERROR_CONTEXT создаёт объект на стеке, в конструкторе и деструкторе которого создаются "логические скобки" для уточнения ошибки.

Читать сообщение в MessageBox "функция zlib.deflate вернула не ноль" мне бессмысленно. Если я разработчик, то хочу знать какой файл в паке битый, какое заклинание испортили дизайнеры. Если я пользователь, я хочу знать какой пак для маминой игрушки перекопировать с CD и вообще если я сисадмин а не программист, то я могу не знать что такое zlib.deflate. А если я увижу имя битого пака, это другое дело.

Проще использовать логи, но SET_ERROR_CONTEXT эффективней, если ошибки нет, он не делает flush каждой строчки в лог, и не надо разбираться в логе с миллионом [DEBUG], [INFO] без скобочной структуры.


  • 1
(Deleted comment)
> 1. Делается на тред-локалах контекст, в него "нашим" кодом ложится список исключений в операции. Если есть библиотечная функция, которая делает колбэк в "наш" код, то наш код с исключениями оборачиваем в наш код с ловцом исключений, который в контекст оный пихает
Чо? Я просто пописать вышел проитерироваться по массиву хотел.

Edited at 2010-10-19 11:21 am (UTC)

(Deleted comment)
(Deleted comment)
Не увидел нашёл там про проблему прозрачности исключений в коллбеках и интерфейсах. Там осуждается "f() throws Exception", но мне кажется "что-то пошло не так" это вполне разумный интерфейс. И видно противоречие в этих ссылках:

But if you are not going to add extra information, then just throw a standard exception:
throw new Exception("Username already taken");

(Deleted comment)
> Не увидел нашёл там про проблему прозрачности исключений в коллбеках и интерфейсах.

Есть мой предикат-коллбэк. Или мой класс, реализация чужого интерфейса. Если мой код бросает исключение MyParsingException или java.io.FileNotFoundExeption, то хочется что бы чужой интерфейс вернул мне моё же исключение.

SET_ERROR_CONTEXT - это фактически NestedDiagnosticContext. log4j такое умеет.

Это по тому как эксепшны часто вместо ассертов используют, то есть в релиз версии ничего неожиданного в этой части кода происходить недолжно

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

Она возникла во времена, когда не было исключений. Тогда и вариантов-то не было.

Кстати, ребе, "во времена, когда не было исключений" были механизмы типа:
- on error goto в бейсике,
- емнип какой-то аналог в фортране,
- longjmp в C/C++,
- сторонняя библиотека, позволяющая отловить ошибку, обработать и вернуться к месту ошибки в borland pascal.
Так что варианты были. Просто на тот момент использование этого int в if представлялось удобным.

onerror goto это, конечно, сила, только это же ужас какой-то.

Согласен =). Но при аккуратном использовании и в небольших программах - таки работало.

И даже имело одно преимущество перед эксепшнами: можно было исправить ошибку и продолжить выполнение (ну, для ошибок типа IO error и т.п. - где она исправлялась). Хотя, конечно, при желании это можно написать и на эксепшнах, и на resultcode - просто код будет позамороченней, с явными циклами.

Почему? Совершенно нормальная практика. Почитайте код какого-нибудь ядра.

Подскажите хоть одно ядро написанное на Фортране или Бейсике.

Ну, на фортране были ОС, но мы ж сейчас про goto.

Мы про onerror goto, ЕВПОЧА.

ЭТО ДРУГОЙ GOTO
Насколько я понимаю, это установка метки, с которой продолжить выполнение в случае любой ошибки (не определена переменная и тп), типа signal(int signum, sighandler_t handler);

Совершенно верно. Это установка Error Handler. Пример на VBA:

Sub Load(FileName As String, SqlQuery As String, TargetCell As Range)
    On Error GoTo ErrorHandler
    Dim Connection As New ADODB.Connection
    Connection.Open ConnectionPrefix & FileName
    ...
    Exit Sub
ErrorHandler:
    HandleError Connection
End Sub

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

Не нравится, что ты вот так сходу рубишь «достаточно одного типа исключений» — это с виду действительно проще для чего-то маленького, но вообще различные типы исключений (если они разумно разделены) помогают потом ловить баги без запуска дебаггера. Ну и, плюс, ты с киданием одного типа теряешь все бенефиты checked exceptions.

Ещё, не стоит называть свои эксепшены “Что-тоError”, по конвенциям “Errors (members of the Error family) are usually thrown for more serious problems, such as OutOfMemoryError, that may not be so easy to handle. In general, code you write should throw only exceptions, not errors. Errors are usually thrown by the methods of the Java API, or by the Java virtual machine itself.” При этом, даже если ты не наследуешься от Error, но оно стоит в название, всё равно, человека, знающего Java это сильно смутит.


> Не нравится, что ты вот так сходу рубишь «достаточно одного типа исключений» — это с виду действительно проще для чего-то маленького,
У меня подозрение, что наоборот, с ними проще на маленьком, когда помнишь все исключения в проекте. Особенно, проще читать код без развесистых try-catch трансляций OneLibraryError, OtherLibraryError, ThirdLibraryError в MyLibraryError.

>но вообще различные типы исключений (если они разумно разделены) помогают потом ловить баги без запуска дебаггера.
Ну, какая разница, увидеть
FileNotFoundException: can't find file 'abc.xml' или
Exception: can't find file 'abc.xml'

> Ну и, плюс, ты с киданием одного типа теряешь все бенефиты checked exceptions.
Имхо, бенефиты могли бы быть, если бы не было RuntimeException, который не декларируется, и не было путаницы с семантикой, является ли выход за пределы массива или деление на ноль ожидаемой программистом ошибкой.

>Ещё, не стоит называть свои эксепшены “Что-тоError”,
Поправлю сейчас в посте, что бы не смущать программистов на Java :) Про Java я только в первом разделе, дальше идёт псевдокод на смеси Java/C++/Python. В питоне кстати всё называется Error, напр. IOError.

Бенефиты есть, путаницы с семантикой нет. RuntimeException — зло, но если код в проекте не говно, его обычно никто не кидает и не ловит (практически, как и с Error). Есть очень редкие случаи, когда это действительно нужно.

Checked exceptions — это то же самое, что и спецификация эффектов монадами (IO/Exception) в Haskell. В аккуратно написанном коде развесистых try-catch не так много, а где они есть, они обычно осмысленны. Да, местами задалбывает шум и многословный синтаксис, с другой стороны я предпочту, чтобы мне компилятор подсказал, что где-то может выпасть определённая проблема.

> Checked exceptions — это то же самое, что и спецификация эффектов монадами (IO/Exception) в Haskell.

C уточнением, что сделать возврат исключения из коллбэка таки можно (если делать монадами).

  • 1