На форуме обнаружилась аналогичная тема, также прошлых лет:
viewtopic.php?f=26&t=808Выделю ключевое:
viewtopic.php?f=26&t=808#p11949Илья Ермаков писал(а):
Vlad писал(а):
И если в языке делать это обработку непросто (проводится политика "нам это не нужно, потому что у нас есть коды возвратов, а если коды для вас не модно, то извращайтесь с ООП разъемами"), то на практике это приводит к тому, что ошибки или не обрабатываются (игнорируются) или обрабатываются "на отвяжись" (в лучшем случае возвращается невнятный код ошибки).
И Вы тоже не поняли мысли... Саму тему Geniepro начал отсюда: viewtopic.php?p=11906#p11906 - там упоминалось мнение Бертрана Мейера по поводу обработки исключений.
Ошибки, про которые Вы говорите (при взаимодействии с внешней средой) - это пока ещё НЕ ИСКЛЮЧЕНИЯ. Не исключения до тех пор, пока их можно делегировать на верхний уровень. Т.е. пока это ожидаемо. И здесь лучше коды ошибок.
Речь идёт о ситуации, когда делегировать выше мы не можем - потому что наша служба имеет высокоуровневый интерфейс, который имеет контракт (пред-постусловия) более сильный, чем можно обеспечить на базе слоя, лежащего ниже. Например, мы дали выше абстракцию абсолютно надёжной связи, а ниже-то лежит сеть, из которой узел может в любой момент пропасть... И вот это уже действительно исключение, потому что связано с нарушением контракта. И нужно эту разницу в контрактах компенсировать. Только делегация вверх по стеку в таких ситуациях часто бессмысленна, особенно в параллельных приложениях. Так что try-catch оказывается бесполезен. А выручают "разъёмы". Так на кой мне в языке тот механизм, который в единственном реально нужном и важном случае не позволяет организовать обработку НАСТОЯЩИХ исключений? (а не багов, кои должны обрабатываться ASSERT + библиотечной try-обёрткой где-то на уровне главного цикла сообщений приложения, и не предусмотренных ошибок, которые делегируются всегда точно на уровень вверх и для которых ничего лучше кодов ошибок не придумано...)
Для полноты картины приведу здесь цитату из источника дискуссии:
viewtopic.php?p=11906#p11906merk писал(а):
...
полезность исключительных ситуаций формально постулируется так. не помню автора, возможно майер(если не ошибаюсь), автор Эйфеля.
исключительная ситауция должна возникнуть тогда, когда функция проверив на корректность предусловия, не может своим кодом выполнить постусловия. Все остальное от лукавого. То есть с точки зрения теории, выход из функции заперт невыполнимыми постусловиями. тогда необходима иная форма выхода - что и есть генерирование искл. ситуации.
Любовь к исключениям просто для передачи результата навроде - хотели файл открыть а его нету! это вульгарное использование механизма исключений, порождающее споры с мордобоем, зачем они вообще.
Исходя из определения видно, что если вы ослабите постусловия функций, вы теоретически можете сократить необходимость исключений до нуля - при постусловиях, что любая постситуация не нарушает корректности функции.
то есть спор - нужно-не нужно абстрактен. зависит от того, какими вы видите постусловия ваших функций. лично я исключения стараюсь не использовать вообще, если это в моей власти.
Да уж, споры "с мордобоем" не утихают до сих пор. Напомню, от чего "ожила" эта тема форума:
viewtopic.php?f=6&t=1454&start=40#p101429prospero78 писал(а):
Мадзи писал(а):
Если входные данные проверяются перед обработкой на допустимость, а также существует проверка выходных данных, то исключительным ситуациям взяться просто неоткуда.
Ос, сенсей!
А далее были небольшие прения, что же есть именно исключительная ситуация. Теперь, согласно цитате выше, можно опереться на авторитет Бертрана Мейера. Итак, исключение возникает тогда, когда функция с корректным предусловием не может выполнить постусловие. Причём определение допускает некоторые теоретические манипуляции. Напр., исключительную ситуацию понимать как сбой, отказ -- "fail", а выявление некорректности предусловия -- паника -- "panic". А может наоборот, пусть здесь теоретики предложат истину. Я же попытаюсь акцентировать на проблематике, которая возникает вокруг "вульгарного" использования исключений, т.е. тогда, когда предполагается прикладная алгоритмическая реакция на неуспех операций. Следует отметить, что нет никакой "коллизии в теории", к примеру, в рамках операции доступа к элементу контейнера по индексу аля "a[...]", где гарантируется успех в случае корректного предусловия, и при допустимом предусловии может возникнуть лишь технические проблемы (напр., сбой оборудования). Поэтому здесь возникает (целенаправленно согласно идеологическим принципам в семантике организации вычислений в данном случае) потребность не в реакции на неуспех, а в алгоритмической гарантии предусловия.
А вот исторически сложившеюся практику "вульгарности" использования исключений раскрывает документ из той смежной темы форума: "Zero-Overhead Exception Handling Using Metaprogramming", реализация обработчиков исключений в виде спец-процедур для Оберона:
viewtopic.php?f=26&t=808#p11933Исходная ссылка на документ не работает, имеется, напр., здесь:
https://www.semanticscholar.org/paper/Z ... 1a86e9cba0Там неплохо обобщена суть обработки исключений. Основная выгода отделения реакции на проблемы от основного алгоритма:
Цитата:
...
Exception handling is the ability to separate the reaction to a program failure (i.e., to an exception)
from the place where the failure occured. This has two advantages:
• It keeps algorithms free of error handling code.
• It allows a programmer to implement the reaction to different occurrences of the same exception in
a single place.
...
Основные принципы реакции на неудачу:
Цитата:
General principles. Exception handling is built around three concepts: a block of statements that is
protected against exceptions; one or more exception handlers that are specified for the protected
block; and a mechanism to raise exceptions. If an exception is raised during the excution of the
protected block, the corresponding handler is executed. Some exceptions can also be raised by the
system because of illegal operations (e.g. division by zero). After the exception was handled the
program can be continued in one of three ways:
• Terminate semantics: The protected block is terminated and the program continues with the first
statement after the block.
• Resume semantics: The execution of the protected block is resumed after the point where the
exception was raised.
• Retry semantics: The protected block is re-executed from the beginning (after the exception
handler has repaired some conditions which caused the block to fail in the first try).
Many languages support only terminate semantics...
Действительно, в большинстве случаев в языках имеется лишь "terminate semantics". Ранее здесь были примеры прочих подходов ("Conditions and Restarts", "continuations", в данной статье рассматриваются Eiffel, Smalltalk, а также продемонстрирована resume-семантика в представленной технике обработки исключений).
Причём явление "terminate semantics", видимо, необходимо понимать широко. Не всегда возможен выход из процесса как таковой. К примеру, актор (исполнитель как процесс, автомат) может завершить очередной такт своей работы и остаться в системе (не завершён, не выгружен) вместе со своим состоянием (в т.ч. возможен перевод объекта в специализированное состояние -- изменение поведения).
Здесь в теме были попытки обобщённо взглянуть на проблематику механизма исключений и придать ему "особый статус":
Руслан Богатырев писал(а):
Исключения (exceptions) -- это особый механизм, которым просто так пренебрегать не стоит. При работе с аппаратурой известны два режима: (1) работа по готовности и (2) работа по прерыванию. Исключение можно рассматривать как программное прерывание. Исключение имеет плюсы и минусы, схожие с плюсами и минусами при работе по аппаратным прерываниям. Для начального обучения программированию, думаю, изучать исключения не стоит. Да и во многих прикладных проектах они могут принести больше вреда, нежели пользы. Вот если касаться системного ПО -- это совсем другое дело.
Исключения предназначены отнюдь не только для обработки ошибок. Они образуют своего рода сигнальную шину, по которой передаются сигналы о важных событиях, связанных с выполнением программного кода. Как правило, нестандартные/нештатные события, которые требуют соответствующего арбитража. Обрабатывающий блок не всегда способен принять решение на месте, нужно делегирование в вышестоящие инстанции. Делегирование обеспечивать по готовности (на уровне традиционных кодов ошибок) бывает крайне неудобно и неэффективно.
При делегировании важно обеспечивать ретрансляцию исключительных событий (ошибок) в термины вышестоящей инстанции (т.н. низкоуровневые и высокоуровневые исключения). Опять-таки, подобную ретрансляцию лучше осуществлять с помощью исключений (соотв. блоков обработки исключений).
Но надо понимать, что исключения еще более опасны, чем goto. Они могут приводить к таким наведенным эффектам, которые локализовать и нейтрализовать бывает крайне трудно. ASSERT в Обероне -- это компромисс. Это упрощенные исключения, позволяющие не потерять контроль над "сигнальной шиной". При желании можно, оставаясь в терминах ASSERT, обеспечить в языке полноценную обработку исключений. Но цена за это -- опасность возникновения ошибок иного рода и наведенных эффектов, вызванных самими исключениями.
Кстати, насчёт ASSERT. Фактически, этот механизм есть некие упрощённые исключения, и здесь в его адрес уместна основная критика блоков try-catch. Как к примеру:
Madzi писал(а):
Alexey_Donskoy писал(а):
В системах 24х7 оба примера никуда не годятся
Давайте уж сравнивать сравнимые вещи. Например:
Вариант 1: try a:=x/y except a:=inf end;
Вариант 2: if abs(y)>=eps then a:=x/y else a:=inf;
Или же:
Вариант 1: try a:=x/y except {значение а не изменяется} end;
Вариант 2: if abs(y)>=eps then a:=x/y;
По-моему, так эти варианты ничего по существу не доказывают. Их использование зависит от задачи. Удобство не сильно отличается. Затраты, в общем, тоже не сильно.
Примеры показывают, что во втором случае программист заранее предусмотрел "исключительный" вариант. Практика показывает, что первый вариант возникает когда автору некогда/лень разбираться с алгоритмом, который иногда сбоит...
Точно также, оставленные assert-ы в "продакшине" ни разу не означают "надёжность". Оправданием может быть лишь прагматика (дополнительное тестирование затруднительно или пока ещё не проведено, верификация проблематична или невозможна, требуется значительное увеличение стоимости или/и временных затрат и пр.).
К характеристике механизма исключений/ошибок стоит ещё добавить следующее полезное свойство (из смежной темы про исключения):
viewtopic.php?f=26&t=808&start=80#p68382Сергей Прохоренко писал(а):
...
Но, в отличие от оператора Exit (отсутствующего в PureBuilder), оператор Escape():
объявляет сигнал отказа – переменную логического типа – и присваивает ей значение «истина»,
объявляет все данные из вызываемой процедуры скомпрометированными и делает их недоступными в вызывающей процедуре. Не происходит возврата параметров или возвращаемого значения в вызывающую функцию/процедуру. Поэтому сигналы отказа невозможно использовать для моделирования нормальной логики (нарушая при этом принципы структурного программирования), а можно использовать только для случаев аварийного завершения вызываемой процедур.
...
Вся возможная семантика исключений там не рассматривалась, но важное -- "скомпрометированность" данных (а далее в той теме форума наблюдается "затык" с глобальными объектами в программах, но это второй вопрос). Популярные try-блоки "классических" исключений, несмотря на их зафиксированные здесь недостатки, "скомпрометированность" данных обеспечивают (прерывание процесса, которое к тому же не проигнорируешь, не позволяет воспользоваться некорректными данными). Однако, ограниченно в общем случае. Например:
Код:
var a: ...
var b: ...
...
try
a = A();
b = B();
catch
...
end;
C(a);
D(b);
...
Т.е. после секции catch есть доступ к потенциально некорректному состоянию (в некоторых частных случаях где-то компиляторы могут выдавать предупреждения и т.п., но в целом идеология механизма не даёт гарантий. Какие-то возможные сопутствующие изменения состояния с целью установления корректности в секции catch -- отдельная задача со своими алгоритмическими гарантиями).