Здравствуйте, fionbio, Вы писали:
F>а) Научившись программировать на Lisp'е, можно стать асом C#/Java/C++/etc. F>Программировать на мейнстриме будете с отвращением, но качественно. И смысл разных F>design patterns и пр. хрени станет яснее, чем для авторов этих концепций Ещё подход - F>использование Лиспа для прототипизирования.
Нет действительно у меня сложилось впечатление что многие "новейшие" концепции, как будто, бы первоначально обкатывались на Лиспе. Когда в CLOS появились :before :after и :around методы? До AspectJ или после?
И еще один вопрос возник. В Лиспе достаточно интересно реализован механизм обработки исключительных ситуаций. А Java/C#/C++, по сравнению с этим, мягко говоря, сильно ограниченны. С первого взгляда и IMHO
А есть ли другие языки с похожим на Лисп или альтернативными подходами к этому делу?
Здравствуйте, mishaa, Вы писали:
M>И еще один вопрос возник. В Лиспе достаточно интересно реализован механизм обработки исключительных ситуаций. А Java/C#/C++, по сравнению с этим, мягко говоря, сильно ограниченны. С первого взгляда и IMHO
А знаешь почему в мэйнстрим-языках так "хреново" с обработкой исключений? Я вот тоже задумался... Провел исследование. Ниже количество конструкций try/ххх в проектах:
* Rsdn.Editor - 10 (на 300+ Кб исходников)
* R# - 8 (На 1+ Мб исходников)
* RSDN@Home - 80 (На 2.3 Мб исходников)
Причем, что забавно в проектах Rsdn.Editor и R# почти все try/xxx — это try/finally или вообще отладочные куски кода.
Как ты думашь, чем при таких объемах могут не удовлетварить try/catch/finally?
... << RSDN@Home 1.2.0 alpha rev. 575>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, mishaa, Вы писали:
VD>А знаешь почему в мэйнстрим-языках так "хреново" с обработкой исключений? Я вот тоже задумался... Провел исследование. Ниже количество конструкций try/ххх в проектах:
[пропушено]] VD>Как ты думашь, чем при таких объемах могут не удовлетварить try/catch/finally?
Хм.. если инстументом не пользуются может он неудобный?
Здравствуйте, mishaa, Вы писали:
VD>>А знаешь почему в мэйнстрим-языках так "хреново" с обработкой исключений? Я вот тоже задумался... Провел исследование. Ниже количество конструкций try/ххх в проектах: M>[пропушено]] VD>>Как ты думашь, чем при таких объемах могут не удовлетварить try/catch/finally?
M>Хм.. если инстументом не пользуются может он неудобный?
M>А вот я посмотрел на реализацю Apache Ant
M>org.apache.tools.ant — 1042 (5585Кб) — неужто мало.
M>P.S. Если интересует, то считал я так: M>
find . -exec grep " try {" \{\}\; | wc -l
Видишь ли, в Java большое количество try/catch может объясняться наличием спецификации исключений у методов -- задал кто-то в публичном интерфейсе спецификацию исключений от балды и все -- в реализациях этих методов нужно ставить try/catch для того, чтобы перехватывать исключения, не удовлетворяющие спецификации и транслировать их в какое-то специфицированное исключение. В C++ и, как я понимаю, в C# такой проблемы нет, поэтому try/catch можно использовать только там, где это физически необходимо и имеет смысл.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
eao197 wrote:
> Видишь ли, в Java большое количество try/catch может объясняться > наличием спецификации исключений у методов -- задал кто-то в публичном > интерфейсе спецификацию исключений от балды и все -- в реализациях > этих методов нужно ставить try/catch для того, чтобы перехватывать > исключения, не удовлетворяющие спецификации и транслировать их в > какое-то специфицированное исключение. В C++ и, как я понимаю, в C# > такой проблемы нет
Явная спецификация исключений — это, ИМХО, большой плюс. Ее бы еще до
ума довести, например так:
Cyberax,
C> Явная спецификация исключений — это, ИМХО, большой плюс.
Как я понимаю, вопрос достаточно спорный... Это без труда обнаруживается простым запросом java checked exceptions, даже не содержащим ничего кроме самого
термина:
Checked exceptions seem like a really good idea at first. But it's all based on our unchallenged
assumption that static type checking detects your problems and is always best. Java is the first
language (that I know of) that uses checked exceptions and is thus the first experiment. However,
the kind of code you must write around these things and the common phenomenon of "swallowed"
exceptions begins to suggest there's a problem. In Python, exceptions are unchecked, and you can
catch them and do something if you want, but you aren't forced. And it seems to work just fine.
Checked exceptions are pretty much disastrous for the connecting parts of an application's
architecture however. These middle-level APIs don't need or generally want to know about
the specific types of failure that might occur at lower levels, and are too general purpose
to be able to adequately respond to the exceptional conditions that might occur.
We'll be examining more of the problems they described in the rest of this article — this section
deals only with those that bear directly on the above theory (that checked exceptions should be
used for everything that isn't a programming error). Before proceeding, I want to make it clear
that this development group isn't the only one to experience these problems. They are in good
company — as I confirmed at the ACCU Spring conference last year. It seems that this theory
doesn't work in practice.
The relevant problems described fall into three categories:
Breaking encapsulation
<...>
Loss of information
<...>
Information overload
<...>
When calling a method that throws a checked exception, you have a few options. You can catch the
exception and handle it yourself; however, this is not always possible. You can declare that you
also throw the checked exception, but this can lead to a proliferation of throws declarations as
exceptions need to propagate farther and farther up the stack. Many exceptions are best handled
at the top level of the stack, or even outside of the stack in an UncaughtExceptionHandler, and
it doesn't take many checked exceptions before this process can become untenable.
И т.д., и т.п.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Причем несложно заметить, что основные возражения о exception'ах идут от
любителей динамических языков, с их идеологией "нафиг нам системы
безопасности не нужны — у нас есть юнит-тесты".
Хотя доработать систему исключений не мешало бы, идей есть достаточно
много. Например добавить декларативный exceptions-chaining, try..catch с
поддержкой вложенных исключений, трансформаторы исключений и т.п.
C>Хотя доработать систему исключений не мешало бы, идей есть достаточно C>много. Например добавить декларативный exceptions-chaining, try..catch с C>поддержкой вложенных исключений, трансформаторы исключений и т.п.
А можете по подробенее рассказать про exceptions-chaining и транформаторы исключений?
Кстати в Лиспе ничем таким не пахнет (по умолчанию ессно), там exceptions's реализован как сигнал, уходящий вверх по стеку. Сигнал в принципе может быть любым, например аналогичный механизм испльзуется и для warning'ов.
А главная фича, так это определение различных restart case'ов. Нигде кроме Лиспа я про подобное не слышал.
-- Главное про деструктор копирования не забыть --
mishaa wrote:
> C>Хотя доработать систему исключений не мешало бы, идей есть достаточно > C>много. Например добавить декларативный exceptions-chaining, > try..catch с > C>поддержкой вложенных исключений, трансформаторы исключений и т.п. > А можете по подробенее рассказать про exceptions-chaining и > транформаторы исключений?
С chaining'ом все просто — одно исключение "вкладывается" в другое, то
есть получается цепочка исключений. Типа SQLException был вызван
NullPointerException'ом, который был вызван OutOfBoundsException'ом. С
jdk1.4 это стандартная фича.
Трансформаторы позволяют декларативно объявлять, что для некоторых
методов/классов/пакетов исключения одного типа будут преобразованы в
исключения другого типа (в том числе можно и в unchecked-исключения).
Такое пока можно сделать с помощью AOP.
> Кстати в Лиспе ничем таким не пахнет (по умолчанию ессно), там > exceptions's реализован как сигнал, уходящий вверх по стеку. Сигнал в > принципе может быть любым, например аналогичный механизм испльзуется и > для warning'ов. > А главная фича, так это определение различных restart case'ов. Нигде > кроме Лиспа я про подобное не слышал.
Здравствуйте, Cyberax, Вы писали:
>> C> Явная спецификация исключений — это, ИМХО, большой плюс. >> Как я понимаю, вопрос достаточно спорный...
C>Причем несложно заметить, что основные возражения о exception'ах идут от C>любителей динамических языков, с их идеологией "нафиг нам системы C>безопасности не нужны — у нас есть юнит-тесты".
Я не являюсь сторонником динамических языков, но меня в Java спецификация исключений напрягала. Как и отсутствие таковой (то, что сейчас есть не зря рекомендуют не использовать) в C++. В Java плохо то, что кто-то из каких-то своих соображений пропишет в спецификации какое-то исключение (типа NullPointerException) и все, труба. Начинаешь реализовывать интерфейс и вынужден все исключения либо подавлять, либо инкапсулировать. Хотя пользы от этого никакой. Имхо, самый лучший выход был бы просто писать throws Exception.
А в C++, напротив, нельзя понять, функция:
void f();
будет генерировать исключения или нет. Конечно, можно перестраховаться и все неизвестные функции в try/catch обрамлять (особенно в деструкторах). Но ведь это overhead не слабый получается.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Cyberax, Вы писали:
...
E>будет генерировать исключения или нет. Конечно, можно перестраховаться и все неизвестные функции в try/catch обрамлять (особенно в деструкторах). Но ведь это overhead не слабый получается.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Cyberax, Вы писали:
E>Я не являюсь сторонником динамических языков, но меня в Java спецификация исключений напрягала. Как и отсутствие таковой (то, что сейчас есть не зря рекомендуют не использовать) в C++. В Java плохо то, что кто-то из каких-то своих соображений пропишет в спецификации какое-то исключение (типа NullPointerException) и все, труба.
Ну против лома нет приема, если код плохо написан, то там не только с исключениями проблема. Ну а выкидывать низкоуровневый а ля NullPointerException вобше IMHO маразм. Зачем вобше Exception'ы нужны — чтобы их кооректоно обрабатывать можно было, а понять че да как и почему NullPointer, особено если он не сразу а на уровня два вверх уходит, и уж корректно обработать его невозможно.
E>А в C++, напротив, нельзя понять, функция: E>
E>void f();
E>
E>будет генерировать исключения или нет. Конечно, можно перестраховаться и все неизвестные функции в try/catch обрамлять (особенно в деструкторах). Но ведь это overhead не слабый получается.
Тем не менее C#-цы вроде не жалуются, а просто почти не используют try-catch, и утверждают что так и надо. интересно вобше с Exceptinos получается вроде и без них никак, но в тоже время и не переборшить надо. Есть интересно какие-нибудь Exception usage patterns ну или Style guidelines?
-- Главное про деструктор копирования не забыть --
mishaa wrote:
> Тем не менее C#-цы вроде не жалуются, а просто почти не используют > try-catch, и утверждают что так и надо. интересно вобше с Exceptinos > получается вроде и без них никак, но в тоже время и не переборшить > надо. Есть интересно какие-нибудь Exception usage patterns ну или > Style guidelines?
Жалуются, кстати, — те кто перешел на С# из Java. Где-то находил пост от
Cameron Purdy на TheServerSide.com, что их до того это напрягло, что они
стали использовать third-party тулзу для отслеживания обработки
исключений в C#.
eao197 wrote:
> C>Причем несложно заметить, что основные возражения о exception'ах > идут от > C>любителей динамических языков, с их идеологией "нафиг нам системы > C>безопасности не нужны — у нас есть юнит-тесты". > Я не являюсь сторонником динамических языков, но меня в Java > спецификация исключений напрягала.
Это как перейти с динамического языка на статический — необходимость
писать типы сначала жутко нервирует.
> Как и отсутствие таковой (то, что сейчас есть не зря рекомендуют не > использовать) в C++. В Java плохо то, что кто-то из каких-то своих > соображений пропишет в спецификации какое-то исключение (типа > NullPointerException) и все, труба.
Это демонстрация тезиса: "Любой инструмент может быть использован
неправильно". Если правильно использовать исключения, то они могут
помогать. Все как с типами.
> Начинаешь реализовывать интерфейс и вынужден все исключения либо > подавлять, либо инкапсулировать. Хотя пользы от этого никакой. Имхо, > самый лучший выход был бы просто писать throws Exception.
Вот с этим как-то надо бороться. Методы есть, но ими серьезно никто не
занимается. А зря....
Здравствуйте, Cyberax, Вы писали:
C>Это демонстрация тезиса: "Любой инструмент может быть использован C>неправильно". Если правильно использовать исключения, то они могут C>помогать. Все как с типами.
>> Начинаешь реализовывать интерфейс и вынужден все исключения либо >> подавлять, либо инкапсулировать. Хотя пользы от этого никакой. Имхо, >> самый лучший выход был бы просто писать throws Exception.
C>Вот с этим как-то надо бороться. Методы есть, но ими серьезно никто не C>занимается. А зря....
Вот собственно об этом я и говорил. Спецификация исключений, имхо, в теории полезная штука. Но вот в C++ и Java она реализована, имхо, неудачно.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
eao197 wrote:
> C>Вот с этим как-то надо бороться. Методы есть, но ими серьезно никто не > C>занимается. А зря.... > Вот собственно об этом я и говорил. Спецификация исключений, имхо, в > теории полезная штука. Но вот в C++ и Java она реализована, имхо, > неудачно.
В С++ мне жутко нравится механизм nothrow. Вот скрестить бы его с Java....
Это я про "throw()" — обозначение того, что функция не будет кидать
никакие исключения.
> А зачем он в Java? Указать что и unchecked exeptions не проходят? Дык, > а толку то. Может в C#-пе будет полезен.
В Яве/C# исключение может вылететь в любой момент (какой-нибудь
OutOfMemory), а в С++ можно гарантировать, что функции не кинут исключения.
Здравствуйте, Cyberax, Вы писали:
C>Это я про "throw()" — обозначение того, что функция не будет кидать C>никакие исключения.
Насколько я знаю, компиляторы не ругаются, если в функциях со спецификатором throw() вызываются функции со спецификацией исключений или вообще без спецификации исключений (и поэтому, потенциально, могущие порождать исключения). Все, что делает throw(), это, образно говоря:
Причем, кажется в boost и в mozilla, я встречал достаточно понятные и разумные обоснования по поводу того, что throw(), как и throw(something) лучше не использовать.
>> А зачем он в Java? Указать что и unchecked exeptions не проходят? Дык, >> а толку то. Может в C#-пе будет полезен.
C>В Яве/C# исключение может вылететь в любой момент (какой-нибудь C>OutOfMemory), а в С++ можно гарантировать, что функции не кинут исключения.
Это как?
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
eao197 wrote:
> C>Это я про "throw()" — обозначение того, что функция не будет кидать > C>никакие исключения. > Насколько я знаю, компиляторы не ругаются, если в функциях со > спецификатором throw() вызываются функции со спецификацией исключений > или вообще без спецификации исключений (и поэтому, потенциально, > могущие порождать исключения).
Вообще-то, по пункту 15.4.4 компилятор должен ругаться на такое.
VC++ выводит предупреждение в этом случае.
> Все, что делает throw(), это, образно говоря: > >try > { > <какой-то код> > } >catch( ... ) > { > unexpected(); // Т.е. throw bad_exception(); > } > >
Это как бы и есть гарантия того, что мой код не получит исключением в лоб.
> Причем, кажется в boost и в mozilla, я встречал достаточно понятные и > разумные обоснования по поводу того, что throw(), как и > throw(something) лучше не использовать.
Да, так как некоторые тупые компиляторы действительно генерируют такой
код во всех throw()-функциях. На нормальных компиляторах — можно.
>>> А зачем он в Java? Указать что и unchecked exeptions не проходят? Дык, >>> а толку то. Может в C#-пе будет полезен. > C>В Яве/C# исключение может вылететь в любой момент (какой-нибудь > C>OutOfMemory), а в С++ можно гарантировать, что функции не кинут > исключения. > Это как?
По JLS (Java Language Specification) исключение может вылететь в любой
момент (наприме, VirtualMachineError).
Здравствуйте, Cyberax, Вы писали:
C>eao197 wrote:
>> C>Это я про "throw()" — обозначение того, что функция не будет кидать >> C>никакие исключения. >> Насколько я знаю, компиляторы не ругаются, если в функциях со >> спецификатором throw() вызываются функции со спецификацией исключений >> или вообще без спецификации исключений (и поэтому, потенциально, >> могущие порождать исключения).
C>Вообще-то, по пункту 15.4.4 компилятор должен ругаться на такое.
C>VC++ выводит предупреждение в этом случае.
Берем пример:
class E {};
void f() throw( E )
{
throw E();
}
void a() throw()
{
f();
}
Компилируем cl -c -GX:
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.
throw.cpp
Предупреждений нет.
Пробуем cl -c -GX -W4:
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.
throw.cpp
throw.cpp(3) : warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
Берем g++ v.3.3.3 (cygwin), g++ -c. Никаких сообщений.
Берем Borland C++ 5.6, bcc32 -c -w. Никаких сообщений.
Берем Digital Mars C++ 8.35n, dmc -c -Ae. Никаких сообщений.
Берем Comeau Online:
Thank you for testing your code with Comeau C/C++!
Tell others about http://www.comeaucomputing.com/tryitout !
Your Comeau C/C++ test results are as follows:
Comeau C/C++ 4.3.3 (Aug 6 2003 15:13:37) for ONLINE_EVALUATION_BETA1
Copyright 1988-2003 Comeau Computing. All rights reserved.
MODE:strict errors C++
In strict mode, with -tused, Compile succeeded (but remember, the Comeau online compiler does not link).
Может там где-то ключики какие-то специальные есть?
>> Все, что делает throw(), это, образно говоря: >> >>try >> { >> <какой-то код> >> } >>catch( ... ) >> { >> unexpected(); // Т.е. throw bad_exception(); >> } >> >> C>Это как бы и есть гарантия того, что мой код не получит исключением в лоб.
Он его получит по лбу
Все равно возникнет bad_exception там, где вообще исключений не ждут.
>> Причем, кажется в boost и в mozilla, я встречал достаточно понятные и >> разумные обоснования по поводу того, что throw(), как и >> throw(something) лучше не использовать.
C>Да, так как некоторые тупые компиляторы действительно генерируют такой C>код во всех throw()-функциях. На нормальных компиляторах — можно.
Проблема с тем, что иногда приходится переходить на ненормальные компиляторы.
>>>> А зачем он в Java? Указать что и unchecked exeptions не проходят? Дык, >>>> а толку то. Может в C#-пе будет полезен. >> C>В Яве/C# исключение может вылететь в любой момент (какой-нибудь >> C>OutOfMemory), а в С++ можно гарантировать, что функции не кинут >> исключения. >> Это как?
C>По JLS (Java Language Specification) исключение может вылететь в любой C>момент (наприме, VirtualMachineError).
Мне интересно, как в C++ можно гарантировать, что функция не кинет исключения?
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, mishaa, Вы писали:
M>Есть интересно какие-нибудь Exception usage patterns ну или Style guidelines?
Например: http://www.boost.org/more/generic_exception_safety.html
У Страуструпа в специальном издании "Язык программирования C++" есть отдельное приложение, касающееся безопасности исключений.
У Саттера в "Решении сложных задач на C++" целая глава посвящена написанию безопасного по отношению к исключениям кода. В этой главе, имхо, главной нитью идет мысль, что исключения -- это единственный способ дать знать об ошибке в конструкторе.
А вообще, недавно перечитал об исключениях у Страуструпа и Саттера и пришел к мнению, что лучше три раза подумать, прежде чем throw написать.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Гм... Эту ссылку я приводил, там не возражения, а как раз рекомендация не налегать
на checked exceptions...
C> Причем несложно заметить, что основные возражения о exception'ах идут от C> любителей динамических языков, с их идеологией "нафиг нам системы C> безопасности не нужны — у нас есть юнит-тесты".
Это Bruce Eckel. Остальные приводят достаточно внятную аргументацию, основным положением
которой является то, что checked exceptions мешают основной идее исключений: изоляции
промежуточных уровней от знания об исключительных ситуациях, возникающих на нижних.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
eao197 wrote:
> C>Вообще-то, по пункту 15.4.4 компилятор должен ругаться на такое. > C>VC++ выводит предупреждение в этом случае. > Компилируем cl -c -GX: > >Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86 >Copyright (C) Microsoft Corporation 1984-2002. All rights reserved. >throw.cpp > > Берем g++ v.3.3.3 (cygwin), g++ -c. Никаких сообщений.
Вот gcc как раз выдает варнинги (про Комо — не знаю), какой-то ключ
нужен. Приду домой — посмотрю...
> C>Это как бы и есть гарантия того, что мой код не получит исключением > в лоб. > Он его получит по лбу > Все равно возникнет bad_exception там, где вообще исключений не ждут.
bad_exception — это уже полный кранты, аналог access violation.
> C>Да, так как некоторые тупые компиляторы действительно генерируют такой > C>код во всех throw()-функциях. На нормальных компиляторах — можно. > Проблема с тем, что иногда приходится переходить на ненормальные > компиляторы.
Для этого и прдумали макросы THROW() и никаких проблем.
> C>По JLS (Java Language Specification) исключение может вылететь в любой > C>момент (наприме, VirtualMachineError). > Мне интересно, как в C++ можно гарантировать, что функция не кинет > исключения?
С-шные функции исключения не кидают, они кидаются только в С++ных
функциях. Ну а вклеить в mangled-name информацию о том, что функция
кидает (или не кидает) исключения — не так уж сложно.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>eao197,
e>> А в C++, напротив, нельзя понять, функция: e>>
e>> void f();
e>>
e>> будет генерировать исключения или нет.
ПК>Вопрос такой: а как компилятор должен был бы использовать спецификацию исключений, ПК>подразумеваемую тобой?
Эх, Павел, если бы у меня у самого было какое-нибудь внятное предложение... А так, о чем не подумашь, везде тупик какой-то. И получается, что лучше того, что есть сейчас я все равно не придумал пока.
Хотелось бы иметь какой-то спецификатор, скажем nothrow, который указывал бы компилятору, что данная функция не намерена сама порождать исключения. И при этом чтобы компилятор не строил вокруг фунции обертку из try/catch вообще. А только использовал эту спецификацию для выдачи предупреждений. Т.е.:
выдал бы предупреждение о том, что в g() вызов функции f() может привести к возникновению исключения.
Только и здесь не все так ладно. Этоже получается, что любое обращение к new или STL уже не даст написать nothrow (или придется везде try/catch лепить).
В общем, не знаю я. Знаю только, что несколько раз получал в деструкторе исключение от функции, которая была не мной написана. И я вообще не знал, что в ней исключения могут генерироваться -- благо исходники были. Вот хотелось бы получить от компилятора в этом плане какую-нибудь помошь. Но вот как?
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
??>> Насколько я знаю, компиляторы не ругаются, если в функциях со
??>> спецификатором throw() вызываются функции со спецификацией исключений
??>> или вообще без спецификации исключений (и поэтому, потенциально,
??>> могущие порождать исключения).
C> Вообще-то, по пункту 15.4.4 компилятор должен ругаться на такое.
Гм... 15.4/4 говорит о присваивании указателей на функции, имеющих
exception specification...
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Только по умолчанию не "т.е. throw bad_exception()", а "call std::terminate()" (18.6.2.2), т.е. abort() (18.6.3.1).
e> Причем, кажется в boost и в mozilla, я встречал достаточно понятные и e> разумные обоснования по поводу того, что throw(), как и throw(something) e> лучше не использовать.
Да, лучше спецификацию исключений вообще не использовать. Если бы она была определена
несколько иным образом, то тогда хотя бы использование throw() имело бы смысл. Пока неясно, решится
ли комитет на эти изменения, но обсуждения их возможности ведутся...
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>eao197,
e>> Мне интересно, как в C++ можно гарантировать, что функция не кинет исключения?
ПК>Спецификация throw() подходит к этому ближе всего: в случае исключения исполнение программы будет прервано.
Согласен.
Однако, напоминает гарантированность отсутствия перхоти путем насильственного отсечения головы от тулувища при обнаружении перхоти
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
ПК>Только по умолчанию не "т.е. throw bad_exception()", а "call std::terminate()" (18.6.2.2), т.е. abort() (18.6.3.1).
В том-то и дело, что по умолчанию происходит вызов std::terminate. Если же установить собственный unexpected, то вполне можно генерить bad_exception. Все же bad_exception производна от std::exception и это дает возможность где-то на верхнем уровне приложения что-то сделать или перезапуститься. А вот c terminate такого увы...
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
eao197,
ПК>> Только по умолчанию не "т.е. throw bad_exception()", а "call ПК>> std::terminate()" (18.6.2.2), т.е. abort() (18.6.3.1).
e> В том-то и дело, что по умолчанию происходит вызов std::terminate. Если e> же установить собственный unexpected, то вполне можно генерить e> bad_exception. Все же bad_exception производна от std::exception и это e> дает возможность где-то на верхнем уровне приложения что-то сделать или e> перезапуститься. А вот c terminate такого увы...
Имхо, попытки генерировать std::bad_exception в std::unexpected для случая throw() сродни попыткам
бросать исключения в случае нарушения предусловий вместо завершения программы. И то, и другое
(за исключением редких случаев) свидетельствует об ошибке в программе, и попытки восстановления
в подавляющем большинстве подобных случаев смысла не имеют. Напротив, лучшее, что можно сделать --
прервать исполнение прямо в точке, где продиагностирована (логическая) ошибка, без раскрутки стека,
с тем, чтоб был сгенерирован дамп/снимок стека, позволяющий программисту разобраться в том, что
происходило. А пытаться продолжать исполнение -- серьезно рисковать данными пользователя.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Имхо, попытки генерировать std::bad_exception в std::unexpected для случая throw() сродни попыткам ПК>бросать исключения в случае нарушения предусловий вместо завершения программы. И то, и другое ПК>(за исключением редких случаев) свидетельствует об ошибке в программе, и попытки восстановления ПК>в подавляющем большинстве подобных случаев смысла не имеют. Напротив, лучшее, что можно сделать -- ПК>прервать исполнение прямо в точке, где продиагностирована (логическая) ошибка, без раскрутки стека, ПК>с тем, чтоб был сгенерирован дамп/снимок стека, позволяющий программисту разобраться в том, что ПК>происходило. А пытаться продолжать исполнение -- серьезно рисковать данными пользователя.
ПК>По этому поводу есть неплохое обсуждение в comp.lang.c++.moderated: ПК>http://groups-beta.google.com/group/comp.lang.c++.moderated/browse_frm/thread/80083ac31a1188da/c40035067fafc0e8 ПК>для начала можно
За ссылку спасибо. Найду время -- прочитаю.
Да и с твоим мнением я, в принципе, согласен. Я после прочтения третьего издания Страуструпа поигрался немного со спецификациями исключений и решил, что лучше с ними не связываться. Особенно с throw(). Иногда использовал throw(std::exception) чтобы показать, что я сам в этих функциях генерирую исключения, но потом и от этого отказался.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
eao197,
e> издания Страуструпа поигрался немного со спецификациями исключений и e> решил, что лучше с ними не связываться. Особенно с throw(). Иногда e> использовал throw(std::exception) чтобы показать, что я сам в этих e> функциях генерирую исключения, но потом и от этого отказался.
Мне вполне нравится, когда спецификации исключений указаны. Но не в коде, а в комментариях.
Плюс, пустая спецификация (throw()) еще может оказаться полезной некоторым компиляторам (vc, gcc...) для
оптимизации. Периодически для последнего вводят какое-нибудь расширение. Также не менее полезным
оказывается антоним -- спецификация, указывающая, что функция не возвращает управление.
template< class E >
void throw_(); // always throws E
Соответственно, для этих случаев можно завести макросы, от которых, кроме комментирования, еще
и польза будет:
void f() NOTHROW;
template< class E >
void throw_() NORETURN;
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Соответственно, для этих случаев можно завести макросы, от которых, кроме комментирования, еще ПК>и польза будет: ПК>
ПК>void f() NOTHROW;
ПК>template< class E >
ПК>void throw_() NORETURN;
ПК>
К сожалению, такие штуки для doxygen-а и ему подобных абсолютно безразличны. А я очень часто работаю не напрямую с заголовочными данными, а со сгенерированной doxygen-ом документацией.
Да и введение макроса NOTHROW будет означать, что при переходе с компилятора на компилятор я (или тот, кто будет сопровождать код после меня) должен буду помнить про правильную адаптацию NOTHROW к новому компилятору. Это все решаемо, но напрягает.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
eao197,
e> Да и введение макроса NOTHROW будет означать, что при переходе с e> компилятора на компилятор я (или тот, кто будет сопровождать код после e> меня) должен буду помнить про правильную адаптацию NOTHROW к новому e> компилятору. Это все решаемо, но напрягает.
Безусловно, поддержка стандартом этих вещей была бы очень полезной. Надеюсь, что-нибудь в следующей
версии спецификации C++ по этому поводу появится...
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, eao197, Вы писали:
E>будет генерировать исключения или нет. Конечно, можно перестраховаться и все неизвестные функции в try/catch обрамлять (особенно в деструкторах). Но ведь это overhead не слабый получается.
А зачем обрамлять то? Пропускай их и все. Если появится код которому по его логие потребуется обработать исключение, то он и будет знать что и как обрабатывать. А обрабатывать на всякий пожарный — это маразм.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, eao197, Вы писали:
E>Мне интересно, как в C++ можно гарантировать, что функция не кинет исключения?
А запросто... Использовать только "базовые" операции, про которые известно, что они не кидают исключения (ну там, присваивание базовых типов, арифметика, и т.п.), и другие функции, про которые уже известно, что они не кидают исключения.
Например, таковы AFAIK реализации std::swap<> для STL-контейнеров.
... << RSDN@Home 1.1.4 stable rev. 510>>
Re[6]: Движутся ли mainstream языки в сторону лиспа?
Здравствуйте, mishaa, Вы писали:
M>Хм.. если инстументом не пользуются может он неудобный?
В чем разница при использовании между исключениями Явы и Шарпа?
M>А вот я посмотрел на реализацю Apache Ant
M>org.apache.tools.ant — 1042 (5585Кб) — неужто мало.
M>P.S. Если интересует, то считал я так: M>
find . -exec grep " try {" \{\}\; | wc -l
Это только доказывает бессмысленность концепции явовских исключений. Ну, или кривость дизайна проекта.
Исключение — это не шататная ситуация (об этом и название говорит). Это способ избавиться от их обработки, а не средство увеличения возни с ними. Обрабатывать исключение нужно только если по другому никак. Тогда и проблем с ними не будет. А вот Явовские чекед-исключения приводят к необходимости или описывать все исключения, или обрабатывать их. Народ чтобы не иметь проблем просто перехватывает все подряд. Отсюда и количество.
По мне так в десктоп приложениях стандартным паттерном обработки исключения является перехват исключений где-то не подолеку от цикла обработки сообщений ОС. Тогда при проблемх будет обламываться одна команда инициированная пользователем. А приложение будет продолжать работать. Остальные места — это уже обход явно возникших проблем. И нужно максимально сокращать такие места.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Движутся ли mainstream языки в сторону лиспа?
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Cyberax,
C>> Явная спецификация исключений — это, ИМХО, большой плюс.
ПК>Как я понимаю, вопрос достаточно спорный... Это без труда обнаруживается простым запросом ПК>java checked exceptions, даже не содержащим ничего кроме самого ПК>термина:
Насколько я (в свое время) понял из дискуссии здесь, проблема не в языковой фиче (checked exceptions), а в дизайне стандартной Java-библиотеки, в которой checked exception используются не к месту (там, где нужны unchecked exceptions), а также в том, что в "официальных рекомендациях" советуют использовать в основном checked exceptions.
IM(very)HO checked exceptions лучше рассматривать как альтернативный механизм возврата значений из функции — и использовать соответствующим образом.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, eao197, Вы писали:
E>>будет генерировать исключения или нет. Конечно, можно перестраховаться и все неизвестные функции в try/catch обрамлять (особенно в деструкторах). Но ведь это overhead не слабый получается.
VD>А зачем обрамлять то? Пропускай их и все. Если появится код которому по его логие потребуется обработать исключение, то он и будет знать что и как обрабатывать. А обрабатывать на всякий пожарный — это маразм.
Если в деструкторах, то не маразм, а жизненая необходимость. Деструкторы не должны генерировать или выпускать исключений.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
VladD2,
> E> будет генерировать исключения или нет. Конечно, можно перестраховаться и все неизвестные функции в try/catch обрамлять (особенно в деструкторах). Но ведь это overhead не слабый получается.
> А зачем обрамлять то? Пропускай их и все. Если появится код которому по его логие потребуется обработать исключение, то он и будет знать что и как обрабатывать. А обрабатывать на всякий пожарный — это маразм.
Из деструкторов, равно как и из всяких Finalize и Dispose, исключения выпускать не рекомендуется.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[7]: Движутся ли mainstream языки в сторону лиспа?
Здравствуйте, VladD2, Вы писали:
VD>Ну, или кривость дизайна проекта.
Я бы по остерегся бы так лихо отзываться об апачевских проектах.
VD>Народ чтобы не иметь проблем просто перехватывает все подряд.
Плз, куски из анта в студию где что криво и где им просто так перехватить захотелось
VD>По мне так в десктоп приложениях стандартным паттерном обработки исключения является перехват исключений где-то не подолеку от цикла обработки сообщений ОС.
T.е. выходит написание расширяемых приложений к котороым можно добавить фичу не препреписывая низкоуровневый код вблизи от цикла обработки сообшений ОС на традиционных языках сродни танцами с бубном и постоянным оверхедом с обработкой исключений?
Кстати мне лично ничем checked exceptins не мешает Более того не понимаю почему методы наподобие Integer.parseInt(String) выкидывают unchecked exceptions. Единствнное что немнооожко напрягает, это что в catch недоступны локальные переменные объявленные внутри try, стиль — объявляй когда надо, нарушается.
-- Главное про деструктор копирования не забыть --
Re[10]: Движутся ли mainstream языки в сторону лиспа?
pvgoran,
> IM(very)HO checked exceptions лучше рассматривать как альтернативный механизм возврата значений из функции — и использовать соответствующим образом.
В таком случае вопрос: причем к возврату значений из функции механизм обработки исключительных ситуаций? Основная особенность исключений направлена на решение следующей проблемы: код, непосредственно вызывающий потенциально бросающую функцию обычно не в состоянии обработать исключительную ситуацию, и, соответственно, незачем его "заморачивать" обработкой всевозможных кодов возврата и т.п. Исключение можно обработать где-то наверху. Диагностируется оно (обычно) где-то глубоко внизу. Соответственно, механизм обработки исключений "заточен" именно на такой сценарий. throw — внизу, try catch — вверху, ничего в таком духе — между. Если на всех уровнях вводить знание о пролетающих исключениях, то, имхо, теряются все выгоды от механизма обработки исключений, кроме одной: исключение, в отличие от кодов возврата необходимо обработать. Но даже эта выгода серьезно подрывается дизайном checked exceptions в Java: количество try catch, которое люди "лепят", чтоб "обернуть" и т.п.
исключения на промежуточных уровнях, приводит к периодическому "проглатыванию" исключений.
Применительно же к уже существующим реализациям, не содержащим ничего, аналогичного checked exceptions (C++, .Net и т.п.), их введение post factum, вообще, кажется нереальным: имхо, польза от (гипотетически более удачного дизайна, чем в Java) checked exceptions будет только в случае, если это последовательно исполняется на всех уровнях, чего уже нет. Ревизия огромного количества существующего кода для добавления отсутствующих exception specifications, имхо, неосуществима.
Кроме того, в случае generics/шаблонов, вообще затруднительно представить, как именно должен выглядеть обобщенный код, чтоб не накладывать черезмерных ограничений на типы, которыми он может параметризовываться...
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Cyberax, Вы писали:
C>В Яве/C# исключение может вылететь в любой момент (какой-нибудь C>OutOfMemory), а в С++ можно гарантировать, что функции не кинут исключения.
На самом деле OutOfMemoryException вылетают не в C# или еще где, а в CLR или ОС. Точно так же они вылетают в ОС от того же МС. Только вылетают они в виде SEH и по-умолчанию не ловятся С++-ными catch-ами. Это в свою очередь приводит к полному падению приложения.
Ну, да при OutOfMemoryException уже мало что можно поделать. Памяти то нет и обрабатывать исключение просто не чем. А вот NullReferenceException очень даже обрабатывается. При этом в большинстве случае C#-ные приложения продолжают работать дальше, а большинство С++-ных выпадает "в осадок".
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Cyberax, Вы писали:
C>mishaa wrote:
>> Тем не менее C#-цы вроде не жалуются, а просто почти не используют >> try-catch, и утверждают что так и надо. интересно вобше с Exceptinos >> получается вроде и без них никак, но в тоже время и не переборшить >> надо. Есть интересно какие-нибудь Exception usage patterns ну или >> Style guidelines?
C>Жалуются, кстати, — те кто перешел на С# из Java. Где-то находил пост от C>Cameron Purdy на TheServerSide.com, что их до того это напрягло, что они C>стали использовать third-party тулзу для отслеживания обработки C>исключений в C#.
Это из серии "мотоцикл продал, но привычка осталась". Жалуются те кто не умет использовать технологию так как задумано е создателями. Вот на Яву действительно жалуются. Причем те кто еею пользуются. Очеть многие популярные Явские фрэймворки обходят Checked exceptions тем или иным образом.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, eao197, Вы писали:
E>А вообще, недавно перечитал об исключениях у Страуструпа и Саттера и пришел к мнению, что лучше три раза подумать, прежде чем throw написать.
Это потому как С++. Я в когда пишу на Шарпе throw пишу там где вижу, что некоторая ситуация не являетс штатной для данного кода. Ну, там параметр кривой — throw. Обязательный файл не найден — throw...
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, eao197, Вы писали:
E>Вот собственно об этом я и говорил. Спецификация исключений, имхо, в теории полезная штука. Но вот в C++ и Java она реализована, имхо, неудачно.
Согласен с одной оговоркой. Описание исключений генерируемых методом должен делать компилятор. Причем без единой подскзки. Вся информация у него есть. А я как потребитель должен иметь возможность легко узнать список исключений которые может выдать тот или иной метод.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, eao197, Вы писали:
E>Если в деструкторах, то не маразм, а жизненая необходимость. Деструкторы не должны генерировать или выпускать исключений.
Деструкторы — это зло! И чем их меньше, тем лучше.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Из деструкторов, равно как и из всяких Finalize и Dispose, исключения выпускать не рекомендуется.
Ну, из Dispose это неприятно но не смертельно. Да и суть Dispose-ов все же неуправляемые ресурсы освобождать. По сему писать их часто не приходится и проблем особых не возникает. А с деструкторами согласен.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Движутся ли mainstream языки в сторону лиспа?
Здравствуйте, mishaa, Вы писали:
M>Здравствуйте, VladD2, Вы писали:
VD>>Ну, или кривость дизайна проекта. M>Я бы по остерегся бы так лихо отзываться об апачевских проектах.
Я вообще-то не об апаческий, а вообще. Но почему, интересно, отзываться об Апачевских проектах дурно — это табу? Открытые проекты с разным контингентов участников...
Ну, да соглашусь, что в случае Явы куча try-ев это скорее необходимость вызванная идеологией языка. Что в прочем не делает их наличие полезным.
VD>>Народ чтобы не иметь проблем просто перехватывает все подряд. M>Плз, куски из анта в студию где что криво и где им просто так перехватить захотелось
Да, статистики более чем достаточно. Ну, и не нужно забывать про выделнное жирным. (цитату ты поквотил не очень красиво ).
M>T.е. выходит написание расширяемых приложений к котороым можно добавить фичу не препреписывая низкоуровневый код вблизи от цикла обработки сообшений ОС на традиционных языках сродни танцами с бубном и постоянным оверхедом с обработкой исключений?
А расширяемости 1000 try/catche-ев никак не помогает. Можно строить прекрасные компонентные системы которые и без оных будут расширяться. Просто относиться к исключениям нужно как к исключениям, а не как к части программы.
M>Кстати мне лично ничем checked exceptins не мешает
Рад за тебя. Другим мешают. К тому же ненужная вешь не становится нужной от того, что она не мешает.
M> Более того не понимаю почему методы наподобие Integer.parseInt(String) выкидывают unchecked exceptions.
О, как?! А если бы она выкидывала checked, то жить стало бы лучше и приятнее? Вот в дотнете тоже содрали явский Integer.parseInt. И все плевались до тех пор пока во втором фрэйморке не ввели TryParse который просто возвращает булево значение вместо того чтобы кидать исключение. В итоге проблема исчезла. И никакие checked exceptions не понадобились. А все потому, что при проектировании в Parse была изначально заложена ошибка (вренее скопирована с Явы). Ошибка банальная... Неверные данные в Parse/parseInt очень часто бывают не исключительно, а совершенно шаттной ситуацией. Это значит, что изначально нужно было делать две версии Parse/parseInt или вводить в эти функции параметр который бы регулировал генерацию исключения.
Я в таких случаях обычно действительно пользуюсь try/catche-ем, но один раз, в обертке закрывающей этот прощет архитекторов фрэймворка.
M> Единствнное что немнооожко напрягает, это что в catch недоступны локальные переменные объявленные внутри try, стиль — объявляй когда надо, нарушается.
Объявляй их во вне try. В чем тут проблема? Код то твоей.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2,
> На самом деле OutOfMemoryException вылетают не в C# или еще где, а в CLR или ОС. Точно так же они вылетают в ОС от того же МС. Только вылетают они в виде SEH и по-умолчанию не ловятся С++-ными catch-ами. Это в свою очередь приводит к полному падению приложения.
-1. При недостаче памяти в C++ вылетает std::bad_alloc.
> Ну, да при OutOfMemoryException уже мало что можно поделать. Памяти то нет и обрабатывать исключение просто не чем.
-1. После раскрутки стека и, соответственно, освобождения некоторой памяти вполне может образоваться нужный запас.
> А вот NullReferenceException очень даже обрабатывается. При этом в большинстве случае C#-ные приложения продолжают работать дальше, а большинство С++-ных выпадает "в осадок".
NullReferenceException — типичная логическая ошибка. Внешнему коду доверять странно, если он передал нулевую ссылку там, где этого делать нельзя. Соответственно, надеяться на то, что внешний код корректно "восстановится", тоже не приходится: он уже находится в каком-то невалидном состоянии, на которое не рассчитывали при его разработке. Попытка "восстановления" после подобной ошибки зачастую приводит к попаданию в нормальный поток исполнения с нарушенными инвариантами, после чего приложение скорее не работает, чем работает. Т.е., в случае управляемых сред, оно при этом обычно не "падает", но это даже хуже, т.к. у пользователя создается иллюзия исправной работы, в то время, как "внутри" уже самый настоящий глюкодром. Это очень легко наблюдать на многих приложениях, написанных на Java и C# в случаях возникновения непредусмотренных исключительных ситуаций. Т.к. чтоб работать в случае нарушенных инвариантов, нужно проверять все на свете, что, очевидно, сделать невозможно.
Вообще же, в более строгих случаях, для приложений с критическими требованиями, попытки продолжения работы после диагностирования логической ошибки традиционно недопустимы: в таком состоянии приложение должно максимально коротким путем завершить свою работу, при необходимости сохранив текущие данные. Далее, в случае серверного приложения -- задача watch dog сохранить crash dump, log файл и т.п., и перезапустить приложение.
В обсуждении, на которое ссылка уже давалась, этот момент подробно рассматривается.
В частности, приводится вполне наглядный пример диагностики в системе автоматического пилотирования отрицательной высоты. Что бы ты в этом случае предпочел, пребывая в самолете: чтоб система пыталась "восстановиться", и продолжить управление, как ни в чем не бывало, или же чтоб она все-таки завершила автопилотирование в надежде на независимые резервные системы с последующим переходом на ручное управление, буде те также откажут?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
VladD2,
> E>Вот собственно об этом я и говорил. Спецификация исключений, имхо, в теории полезная штука. Но вот в C++ и Java она реализована, имхо, неудачно.
> Согласен с одной оговоркой. Описание исключений генерируемых методом должен делать компилятор. Причем без единой подскзки. Вся информация у него есть.
Гм, не вполне понял мысль... Давай конкрентнее, для того же C#:
interface I
{
void f();
};
Сборка 1:
class C1 : I
{
void f() // или что надо написать, чтоб реализовать I::f...
{
throw new Exception1();
}
};
Сборка 2:
class C2 : I
{
void f() // или что надо написать, чтоб реализовать I::f...
{
throw new Exception2();
}
};
. . .
Сборка N:
void g( I i )
{
i.f(); // И какой список здесь должен генерировать компилятор?..
// Какие сборки будут подгружены, кроме данной, и от какого класса придет I — :xz:
}
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Cyberax, Вы писали:
C>Жалуются, кстати, — те кто перешел на С# из Java. Где-то находил пост от C>Cameron Purdy на TheServerSide.com, что их до того это напрягло, что они C>стали использовать third-party тулзу для отслеживания обработки C>исключений в C#.
И что они делали со знанием того где и какое исключение кидается?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Павел Кузнецов wrote:
> C> Однако справедливые возражения того же Гослинга или Гриффита > C> (http://octopull.demon.co.uk/java/ExceptionalJava.html) почему-то не > C> находятся сразу же. > Гм... Эту ссылку я приводил, там не возражения, а как раз рекомендация > не налегать > на checked exceptions...
A programming language can't solve all the problems. A language can't
guarantee that no matter how screwed up the environment gets the program
will survive. But anything the language can do to increase the
probability that programs will be reasonably graceful under fire is a
good thing. For example, just making people at least willfully ignore
return codes helps. In Java you can ignore exceptions, but you have to
willfully do it. You can't accidentally say, "I don't care." You have to
explicitly say, "I don't care."
Тут я согласен, исключение — это такой "код возврата" для аварийных
ситуаций, который игнорировать не рекомендуется. А если игнорировать
хочется — то надо делать это явно.
Минус такого подхода — некоторый overhead в лишних try{..}catch(...){}
блоках. Вот чтобы его убрать и нужно придумать какие-нибудь механизмы.
> C> Причем несложно заметить, что основные возражения о exception'ах > идут от > C> любителей динамических языков, с их идеологией "нафиг нам системы > C> безопасности не нужны — у нас есть юнит-тесты". > Это Bruce Eckel. Остальные приводят достаточно внятную аргументацию, > основным положением > которой является то, что checked exceptions мешают основной идее > исключений: изоляции > промежуточных уровней от знания об исключительных ситуациях, > возникающих на нижних.
А насколько нужна изоляция? Какой-нибудь null-pointer-exception через
несколько уровней потеряет всякий смысл, в нем не будет полезной
информации для попытки восстановить выполнение.
Павел Кузнецов wrote:
> ??>> Насколько я знаю, компиляторы не ругаются, если в функциях со > ??>> спецификатором throw() вызываются функции со спецификацией исключений > ??>> или вообще без спецификации исключений (и поэтому, потенциально, > ??>> могущие порождать исключения). > C> Вообще-то, по пункту 15.4.4 компилятор должен ругаться на такое. > Гм... 15.4/4 говорит о присваивании указателей на функции, имеющих > exception specification...
По памяти пункт сказал — и как всегда неправильно. Там в предидущем
пункте говорится о спецификации исключениях в наследниках, но про
контроль за throw() действительно ничего нет. Так что это я глючу
VladD2 wrote:
> E>Вот собственно об этом я и говорил. Спецификация исключений, имхо, в > теории полезная штука. Но вот в C++ и Java она реализована, имхо, > неудачно. > Согласен с одной оговоркой. Описание исключений генерируемых методом > должен делать компилятор. Причем без единой подскзки. Вся информация у > него есть. А я как потребитель должен иметь возможность легко узнать > список исключений которые может выдать тот или иной метод.
Welcome to IDEA. Не компилятор, правда, но вполне сойдет.
Павел Кузнецов wrote:
> Сборка 1: > >class C1 : I >{ > void f() // или что надо написать, чтоб реализовать I::f... > { > throw new Exception1(); > } >}; > >
В случае с checked exceptions — ошибка компиляции.
> Сборка N: > >void g( I i ) >{ > i.f(); // И какой список здесь должен генерировать компилятор?.. > // Какие сборки будут подгружены, кроме данной, и от какого класса придет I — >} >
Если checked exceptions есть, то в обоих сборках не должно быть
задекларировано бросков исключений.
IT wrote:
> C>Жалуются, кстати, — те кто перешел на С# из Java. Где-то находил > пост от > C>Cameron Purdy на TheServerSide.com, что их до того это напрягло, что > они > C>стали использовать third-party тулзу для отслеживания обработки > C>исключений в C#. > И что они делали со знанием того где и какое исключение кидается?
Смотрели, чтобы оно было обработано. Просто эти товарищи пишут 24x7
систему, которая должна работать как можно безглючнее.
У них это, кстати, получилось. За мой опыт работы с Tangasol
Coherence'ом мне его свалить не удалось ни разу.
VladD2 wrote:
> C>В Яве/C# исключение может вылететь в любой момент (какой-нибудь > C>OutOfMemory), а в С++ можно гарантировать, что функции не кинут > исключения. > На самом деле OutOfMemoryException вылетают не в C# или еще где, а в > CLR или ОС. Точно так же они вылетают в ОС от того же МС. Только > вылетают они в виде SEH и по-умолчанию не ловятся С++-ными catch-ами. > Это в свою очередь приводит к полному падению приложения.
void *__cdecl operator new(size_t size) _THROW1(_STD bad_alloc)
{ // try to allocate size bytesvoid *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
_STD _Nomemory();
return (p);
}
...
_CRTIMP2 void __cdecl _Nomemory()
{ // report out of memorystatic const _XSTD bad_alloc nomem;
_RAISE(nomem);
}
...
То есть в VC будет кидаться std::bad_alloc (по Стандарту), причем
исключение создается на стеке и не занимает динамическую память.
> Ну, да при OutOfMemoryException уже мало что можно поделать. Памяти то > нет и обрабатывать исключение просто не чем.
А автоматическое хранилище (aka stack) на что? Кстати, в случае OOM
вполне можно почистить какие-нибудь кэши и спокойно жить дальше. Так
делалется, например, в Tangasol Coherence.
VladD2 wrote:
> E>будет генерировать исключения или нет. Конечно, можно > перестраховаться и все неизвестные функции в try/catch обрамлять > (особенно в деструкторах). Но ведь это overhead не слабый получается. > А зачем обрамлять то? Пропускай их и все. Если появится код которому > по его логие потребуется обработать исключение, то он и будет знать > что и как обрабатывать.
Павел Кузнецов wrote:
> NullReferenceException — типичная логическая ошибка. Внешнему коду > доверять странно, если он передал нулевую ссылку там, где этого делать > нельзя. Соответственно, надеяться на то, что внешний код корректно > "восстановится", тоже не приходится: он уже находится в каком-то > невалидном состоянии, на которое не рассчитывали при его разработке. > Попытка "восстановления" после подобной ошибки зачастую приводит к > попаданию в нормальный поток исполнения с нарушенными инвариантами, > после чего приложение скорее не работает, чем работает.
Тут еще другой момент — обычно функция, выкинувшая исключение, работает
с достаточно локализованым набором данных. Так что в случае ошибки
вполне можно создать новое окружение и попробовать выполнить операцию
еще раз.
То есть если нам прилетит URLConnectionFailure, то можно попробовать
попросить юзера подергать сетевой кабель, а потом повторить попытку.
Готово. Может они что-то особенное делали в catch?
C>Просто эти товарищи пишут 24x7 систему, которая должна работать как можно безглючнее.
Это я понимаю. Но я не понимаю общности слова обработать. Записать в лог? Ну так для этого вышеприведённый вариант очень даже здорово подходит. Отослать на e-mail админу? То же самое.
C>У них это, кстати, получилось. За мой опыт работы с Tangasol C>Coherence'ом мне его свалить не удалось ни разу.
Это очень здорово. Не могу только понять зачем нужно отдельно обрабатывать каждый тип исключений.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT wrote:
> C>Смотрели, чтобы оно было обработано. > Могу им подарить один универсальный способ: > >try >{ > DoWhateverYouWant(); >} >catch (Exception ex) >{ >} > >
Это называется "проигнорировано", а не "обработано".
> Готово. Может они что-то особенное делали в catch?
Естественно.
> C>Просто эти товарищи пишут 24x7 систему, которая должна работать как > можно безглючнее. > Это я понимаю. Но я не понимаю общности слова обработать. Записать в > лог? Ну так для этого вышеприведённый вариант очень даже здорово > подходит. Отослать на e-mail админу? То же самое.
А как насчет прибить поврежденные данные и пересинхронизировать их с
других узлов?
> C>У них это, кстати, получилось. За мой опыт работы с Tangasol > C>Coherence'ом мне его свалить не удалось ни разу. > Это очень здорово. Не могу только понять зачем нужно отдельно > обрабатывать каждый тип исключений.
Нужно следить не столько за типами исключений, сколько за возможностью
вылета "прикладных" исключений. То есть не логических ошибок в коде, а
исключительных ситуаций в процессе работы.
Здравствуйте, Cyberax, Вы писали:
C>Это называется "проигнорировано", а не "обработано".
Именно.
>> Готово. Может они что-то особенное делали в catch?
C>Естественно.
Вот это самое интересное.
C>А как насчет прибить поврежденные данные и пересинхронизировать их с C>других узлов?
Для прибить (и даже восстановить) существуют такие вещи как транзакции. Что такое переиндексировать их с других узлов мне не совсем понятно.
C>Нужно следить не столько за типами исключений, сколько за возможностью вылета "прикладных" исключений.
Т.е. своих собственных?
C>То есть не логических ошибок в коде, а исключительных ситуаций в процессе работы.
Не пугай меня. Это называется строить логику программы на исключениях. Объектно-ориентированный longjump. Никакого отношения к надёжности программы это не имеет.
В общем, пока совершенно не понятно зачем нужно обрабатывать абсолютно все исключения.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Cyberax,
ПК>> class C1 : I ПК>> { ПК>> void f() // или что надо написать, чтоб реализовать I::f... ПК>> { ПК>> throw new Exception1(); ПК>> } ПК>> };
C> В случае с checked exceptions — ошибка компиляции.
Подожди... А что там компилятор нагенерил-то уже, и по какому критерию?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Павел Кузнецов wrote:
> ПК>> class C1 : I > ПК>> { > ПК>> void f() // или что надо написать, чтоб реализовать I::f... > ПК>> { > ПК>> throw new Exception1(); > ПК>> } > ПК>> }; > C> В случае с checked exceptions — ошибка компиляции. > Подожди... А что там компилятор нагенерил-то уже, и по какому критерию?
Cyberax,
C> А насколько нужна изоляция? Какой-нибудь null-pointer-exception через C> несколько уровней потеряет всякий смысл, в нем не будет полезной C> информации для попытки восстановить выполнение.
Я отношусь к тем, кто считает, что в таком случае null-pointer-exception имеет особенно
мало смысла: если вызывающий код готов к null-pointer-exception, то почему он вообще
передал нулевой указатель? Если не готов, то как ему поможет null-pointer-exception?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Павел Кузнецов wrote:
> C> А насколько нужна изоляция? Какой-нибудь null-pointer-exception через > C> несколько уровней потеряет всякий смысл, в нем не будет полезной > C> информации для попытки восстановить выполнение. > Я отношусь к тем, кто считает, что в таком случае > null-pointer-exception имеет особенно > мало смысла: если вызывающий код готов к null-pointer-exception, то > почему он вообще > передал нулевой указатель? Если не готов, то как ему поможет > null-pointer-exception?
ОК, пусть будет не NullPointerException (которых в С++ все равно нет
), а connection_refused_exception. То есть исключение, обозначающее
прикладную ошибку.
--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[9]: Движутся ли mainstream языки в сторону лиспа?
VD>до тех пор пока во втором фрэйморке не ввели TryParse который просто возвращает булево значение вместо того чтобы кидать исключение. В итоге проблема исчезла. И никакие checked exceptions не понадобились. А все потому, что при проектировании в Parse была изначально заложена ошибка (вренее скопирована с Явы). Ошибка банальная... Неверные данные в Parse/parseInt очень часто бывают не исключительно, а совершенно шаттной ситуацией.
Вобшем понятно, тут дело к свяшенной войне идет, что значить ИСКЛЮЧИТЕЛЬНАЯ ситуация, а уж checked она должна быть или unchecked это уже из ее определения будет видно.
Кстати может кто-нибудь попытается сформулировать какое-нибудь обобшенное определение?
Кстати возврашаясь к Лиспу, т.к. там при "исключении" (ну а точнее сигнале) стек может и не уничтожатся, объекты под GC не попадают, и есть возможность продолжить с того же места, так что понятие той самой ИСКЛЮЧИТЕЛЬНОЙ ситуации для него будет IMHO неприменимо.
-- Главное про деструктор копирования не забыть --
Re[11]: Движутся ли mainstream языки в сторону лиспа?
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>pvgoran,
>> IM(very)HO checked exceptions лучше рассматривать как альтернативный механизм возврата значений из функции — и использовать соответствующим образом.
ПК>В таком случае вопрос: причем к возврату значений из функции механизм обработки исключительных ситуаций?
Я имел в виду примерно следующую ситуацию: есть функция, которая возвращает управление либо обычным return'ом, либо одним из документированных исключений. При этом return соответствует самому частому варианту развития событий, а исключения — более редким, но все же ожидаемым, не ошибочным вариантам, и предполагается, что клиентский код должен обработать все варианты.
Пример — ввод-вывод через неблокирующий сокет. Нормальный исход (return) — когда операция чтения/записи выполнена, альтернативный исход (исключение) — когда принимать нечего (в случае ввода) или когда буфер передачи переполнен (в случае вывода).
Дргой пример — доступ к ассоциативному контейнеру без автосоздания элементов. Нормальный исход — когда элемент есть и доступ предоставлен, альтернативный исход — когда элемента нет.
ПК>Основная особенность исключений направлена на решение следующей проблемы: код, непосредственно вызывающий потенциально бросающую функцию обычно не в состоянии обработать исключительную ситуацию, и, соответственно, незачем его "заморачивать" обработкой всевозможных кодов возврата и т.п. Исключение можно обработать где-то наверху. Диагностируется оно (обычно) где-то глубоко внизу. Соответственно, механизм обработки исключений "заточен" именно на такой сценарий. throw — внизу, try catch — вверху, ничего в таком духе — между.
Да, "классическое" (intended) применение механизма обработки исключений — именно такое. В "моем" варианте альтернативные ситуации (представленные исключениями) должны в основном обрабатываться "на месте" (т.е. это "локальные" исключения).
В языках вроде OCaml'а подобные вещи можно сделать, создавая специальные типы. В C++/Java можно использовать исключения (хотя они и предназначены не только и, может быть, не столько для этого).
ПК>Если на всех уровнях вводить знание о пролетающих исключениях, то, имхо, теряются все выгоды от механизма обработки исключений, кроме одной: исключение, в отличие от кодов возврата необходимо обработать.
Не совсем точно. Остается еще независимость типа/содержания исключения от типа/содержания возвращаемого значения.
ПК> Но даже эта выгода серьезно подрывается дизайном checked exceptions в Java: количество try catch, которое люди "лепят", чтоб "обернуть" и т.п. ПК>исключения на промежуточных уровнях, приводит к периодическому "проглатыванию" исключений.
OK.
ПК>Применительно же к уже существующим реализациям, не содержащим ничего, аналогичного checked exceptions (C++, .Net и т.п.), их введение post factum, вообще, кажется нереальным: имхо, польза от (гипотетически более удачного дизайна, чем в Java) checked exceptions будет только в случае, если это последовательно исполняется на всех уровнях, чего уже нет. Ревизия огромного количества существующего кода для добавления отсутствующих exception specifications, имхо, неосуществима.
Согласен.
ПК>Кроме того, в случае generics/шаблонов, вообще затруднительно представить, как именно должен выглядеть обобщенный код, чтоб не накладывать черезмерных ограничений на типы, которыми он может параметризовываться...
Можно немного поспекулировать на эту тему... Например: сделаем exception specification частью сигнатуры функции (если это еще не сделано) и позволим задавать их с помощью списков типов a-la Александреску. Причем предусмотрим template instantiation matching с участием этих списков. Таким образом, например, можно сделать boost::bind "прозрачным" с точки зрения спецификаций исключений.
Хотя, похоже, это только начало. Нужно еще ввести запросы компилятору вида "какие исключения может выдать вызов функции/объекта с параметрами данного типа"... Если подумать об объеме кода, нужного, чтобы это все задекларировать, становится страшновато... (Наверное, так же страшно программистам на динамически типизированных языках представить наличие спецификаций типов. )
Cyberax,
ПК>>>> class C1 : I ПК>>>> { ПК>>>> void f() // или что надо написать, чтоб реализовать I::f... ПК>>>> { ПК>>>> throw new Exception1(); ПК>>>> } ПК>>>> };
C>>> В случае с checked exceptions — ошибка компиляции.
ПК>> Подожди... А что там компилятор нагенерил-то уже, и по какому критерию?
C> То есть? Не понял вопроса.
Ты отвечал на сообщение, являющееся ответом на реплику
Описание исключений генерируемых методом должен делать компилятор. Причем без единой подскзки.
Вся информация у него есть. А я как потребитель должен иметь возможность легко узнать список
исключений которые может выдать тот или иной метод.
Вот я и спрашиваю: раз ты предполагаешь, что компилятор в данном случае должен был посчитать, что f()
имеет пустую спецификацию исключений, то по какому алгоритму он действовал?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
>> Ну, да при OutOfMemoryException уже мало что можно поделать. Памяти то нет и обрабатывать исключение просто не чем.
ПК>-1. После раскрутки стека и, соответственно, освобождения некоторой памяти вполне может образоваться нужный запас.
+ в приложениях, которые должны работать в условиях ограниченной памяти память для обработчика OutOfMemoryException выделяется (резервируется) заранее, в самом начале работы приложения. Так что это не проблема ни в C++, ни в C#.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>-1. При недостаче памяти в C++ вылетает std::bad_alloc.
Даже если памяти не хватило внутри апишной функции?
>> Ну, да при OutOfMemoryException уже мало что можно поделать. Памяти то нет и обрабатывать исключение просто не чем.
ПК>-1. После раскрутки стека и, соответственно, освобождения некоторой памяти вполне может образоваться нужный запас.
Гарантии нет, а без гарантии мало что построить можно.
>> А вот NullReferenceException очень даже обрабатывается. При этом в большинстве случае C#-ные приложения продолжают работать дальше, а большинство С++-ных выпадает "в осадок".
ПК>NullReferenceException — типичная логическая ошибка.
Это может быть совершенно произвольная ошибка.
ПК> Внешнему коду доверять странно, если он передал нулевую ссылку там, где этого делать нельзя. Соответственно, надеяться на то, что внешний код корректно "восстановится", тоже не приходится: он уже находится в каком-то невалидном состоянии, на которое не рассчитывали при его разработке.
Ты в своих программах всегда проверяешь все параметры на null? А если он таки null, что делаешь? В фреймворке в этом случае вылетает чаще ArgumentNullException. Он лучше NullReferenceException, но лишь тем что предоставляет чуть больше информации о ситуации. С точки зрения устойчивости приложения никакой разницы нет.
ПК> Попытка "восстановления" после подобной ошибки зачастую приводит к попаданию в нормальный поток исполнения с нарушенными инвариантами, после чего приложение скорее не работает, чем работает.
Ни разу не сталкивался. Вон янус в настоящий момент после любых необработанных исключений в процессе работы продолжает жить. В текущем проекте на работе сервер тоже никогда не падал и не умирал за все время разработки. Причем особых усилий к тому никто специально не прикладывал. Тут все зависит от приложения — довольно большой класс приложений работает не непрерывно, а порциями. Для GUI это обычно реакция на сообщение, для серверов запрос клиента. В этом случае при необработанном исключении достаточно откатить текущую операцию.
ПК>Это очень легко наблюдать на многих приложениях, написанных на Java и C# в случаях возникновения непредусмотренных исключительных ситуаций.
AndrewVK,
ПК>> -1. При недостаче памяти в C++ вылетает std::bad_alloc.
A> Даже если памяти не хватило внутри апишной функции?
Нет, в этом случае эта функция вернет код ошибки, означающий нехватку памяти.
ПК>> -1. После раскрутки стека и, соответственно, освобождения некоторой ПК>> памяти вполне может образоваться нужный запас.
A> Гарантии нет, а без гарантии мало что построить можно.
Отчего же? Если памяти освободится недостаточно, при первом же запросе снова будет
выброшен std::bad_alloc, и мы "вылетим" еще выше по стеку, освобождая еще больше
памяти, и так, пока не освободится ее достаточное количество, или, если память заняло
другое приложение, пока наше приложение не завершится в связи с недостаточностью
ресурсов для работы.
В C++ процесс освобождения памяти вполне детерминирован. В чем проблема-то?
ПК>> NullReferenceException — типичная логическая ошибка.
A> Это может быть совершенно произвольная ошибка.
Например?
ПК>> Внешнему коду доверять странно, если он передал нулевую ссылку там, ПК>> где этого делать нельзя. Соответственно, надеяться на то, что внешний ПК>> код корректно "восстановится", тоже не приходится: он уже находится в ПК>> каком-то невалидном состоянии, на которое не рассчитывали при его ПК>> разработке.
A> Ты в своих программах всегда проверяешь все параметры на null? А если он A> таки null, что делаешь?
Нет, я полагаюсь на систему, которая сгенерирует аппаратное исключение в ответ на разыменование
нулевого указателя, и далее в дело вступит мой обработчик данного исключения, и создаст снимок
стека или дамп, чтоб я мог определить, в чем же, собственно, ошибка.
A> В фреймворке в этом случае вылетает чаще ArgumentNullException. A> Он лучше NullReferenceException, но лишь тем что A> предоставляет чуть больше информации о ситуации. С точки зрения A> устойчивости приложения никакой разницы нет.
С точки зрения устойчивости приложения некорректно порождать исключение при обнаружении
логической ошибки. Нужно обеспечивать внешние резервные средства, и исправлять ошибку,
а не маскировать ее исключениями.
ПК>> Попытка "восстановления" после подобной ошибки зачастую приводит к ПК>> попаданию в нормальный поток исполнения с нарушенными инвариантами, ПК>> после чего приложение скорее не работает, чем работает.
A> Ни разу не сталкивался. Вон янус в настоящий момент после любых A> необработанных исключений в процессе работы продолжает жить.
Вот на нем-то я как раз недавно и наблюдал типичный упомянутый глюкодром. Произошло
как раз после исключения в ответ на разорвавшееся соединение во время синхронизации.
После этого, даже когда соединение вернулось, ни одна синхронизация не проходила,
сообщая что-то о вновь возникшем исключении.
И так было, пока я не перезапустил приложение.
В общем, положил я его в итоге обратно в каталог Install, и больше не трогал.
ПК>> Это очень легко наблюдать на многих приложениях, написанных на Java и ПК>> C# в случаях возникновения непредусмотренных исключительных ситуаций.
A> Пример таких приложений можно?
См. выше.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
AndrewVK wrote:
> ПК>-1. При недостаче памяти в C++ вылетает std::bad_alloc. > Даже если памяти не хватило внутри апишной функции?
При недостатке памяти АПИшные функции будут возвращать код ошибки.
>>> Ну, да при OutOfMemoryException уже мало что можно поделать. Памяти > то нет и обрабатывать исключение просто не чем. > ПК>-1. После раскрутки стека и, соответственно, освобождения некоторой > памяти вполне может образоваться нужный запас. > Гарантии нет, а без гарантии мало что построить можно.
Почему? В С++ гарантировано, что после вылета исключения о OOM
автоматическое хранилище будет работать. Потом, даже если не удастся
освободить память, то приложение умрет. А если все-таки удастся?
> ПК> Внешнему коду доверять странно, если он передал нулевую ссылку > там, где этого делать нельзя. Соответственно, надеяться на то, что > внешний код корректно "восстановится", тоже не приходится: он уже > находится в каком-то невалидном состоянии, на которое не рассчитывали > при его разработке. > Ты в своих программах всегда проверяешь все параметры на null?
Если по логике работы параметр может быть null-ом — то естественно.
> А если он таки null, что делаешь?
Дампить прогу в кору и искать почему был ошибочно передан NULL.
> В фреймворке в этом случае вылетает чаще ArgumentNullException. Он > лучше NullReferenceException, но лишь тем что предоставляет чуть > больше информации о ситуации. С точки зрения устойчивости приложения > никакой разницы нет.
У меня была такая ситуация: программа (на Java) читает архив с
сериализованными классами (через свой десериализатор), в процессе
десериализации одного из классов в некоторых случаях происходил глюк, в
результате которого портилась статическая (точнее thread local)
переменная и кидалось NullPointerException. Программа это исключение
вылавливала ("catch(Throwable t)") и выводила красивое окошко с ошибкой
о поврежденном файле.
Казалось бы, что все замечательно. Но вот когда юзер открывал следующий
файл, то у него из-за неправильного состояния статической переменной
портился один узел графа. Причем портился незаметно. Юзер мог этого не
заметить и перезаписать документ на диск. C этим неправильным узлом.
Left2,
>>> Ну, да при OutOfMemoryException уже мало что можно поделать. Памяти то нет и обрабатывать исключение просто не чем.
> ПК>-1. После раскрутки стека и, соответственно, освобождения некоторой памяти вполне может образоваться нужный запас.
> + в приложениях, которые должны работать в условиях ограниченной памяти память для обработчика OutOfMemoryException выделяется (резервируется) заранее, в самом начале работы приложения. Так что это не проблема ни в C++, ни в C#.
Единственное, что в C#, действительно, мало, что изменится от того, что было выброшено OutOfMemoryException: при раскрутке стека память автоматически не освобождается, так что вся надежда на сборщик мусора. Интересно, может, он как-нибудь специально реагирует на OutOfMemoryException?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Имхо, попытки генерировать std::bad_exception в std::unexpected для случая throw() сродни попыткам ПК>бросать исключения в случае нарушения предусловий вместо завершения программы. И то, и другое ПК>(за исключением редких случаев) свидетельствует об ошибке в программе, и попытки восстановления ПК>в подавляющем большинстве подобных случаев смысла не имеют. Напротив, лучшее, что можно сделать -- ПК>прервать исполнение прямо в точке, где продиагностирована (логическая) ошибка, без раскрутки стека, ПК>с тем, чтоб был сгенерирован дамп/снимок стека, позволяющий программисту разобраться в том, что ПК>происходило. А пытаться продолжать исполнение -- серьезно рисковать данными пользователя.
ПК>По этому поводу есть неплохое обсуждение в comp.lang.c++.moderated: ПК>http://groups-beta.google.com/group/comp.lang.c++.moderated/browse_frm/thread/80083ac31a1188da/c40035067fafc0e8
Почитал это обсуждение. Мое плохое знание английского не позволило понять все досконально, но надеюсь, что основные моменты я уловил.
Исходя из того, что там написано, в проверках вида:
void do_something( const char * p ) {
if( !p )
throw std::invalid_argument( "do_something: p == 0!" );
...
}
нет никакого смысла и лучше дать возможность аварийно завершиться приложению, поскольку do_something не должна получать нулевого аргумента. Звучит вполне разумно. Особенно, если учесть, что p может иметь не нулевое, но некорректное значение (например, 0xDEADCAFE, из-за повисшего или "битого" указателя).
Тем не менее, я считаю, что здесь не все так однозначно. Я исхожу из двух предпосылок.
Во-первых, нужно различать, является ли do_something частью открытого интерфейса какой-либо библиотеки, или это закрытая часть реализации библиотеки. Если это закрытая часть, то я полностью согласен с тем, что порождать исключение бессмысленно, нужно использовать assert-ы. Но вот если do_something является частью открытого интерфейса, то здесь ситуация меняется и в дело вступает вторая предпосылка.
Во-вторых, существует целый класс ошибок, связанных с передачей в качестве аргументов заведомо неправильных данных. Откуда эти данные берутся и почему они становятся аргументами -- это другой вопрос. Тем не менее, в результате совершенно не критических, а иногда даже элементарных, ошибок в do_something может быть передан нулевой указатель. Причем именно нулевой, т.к. 0 -- это валидное значение для обозначения ни на что не указывающего указателя. Например:
Функция find_user_grop_name() возвращает имя группы, которой принадлежит пользователь. Но, если пользователь не принадлежит ни одной из групп, то find_user_grop_name возвращает 0. В данном фрагменте проблема программиста в том, что он должен был написать:
Но программист ошибся. Он мог вообще не знать, что в каких-то условиях пользователь может не принадлежать ни одной группе. Более того, на момент написание кода этого вообще могло не быть -- такая вольность в отношении пользователей могла появиться уже в процессе развития системы, во второй или третьей версии.
Итак, что в такой ситуации лучше делать? Позволить приложению время от времени падать с crush dump-ом или порождать исключение в надежде, что на каком-то уровне его смогут обработать и выполнить мягкий рестарт?
Имхо, в случаях, когда возможна проверка на заведомо некорректные параметры (как правило, это public интерфейс какого-либо класса, библиотеки или модуля), лучше все-таки порождать исключения. Во-первых, потому, что это самая ближайшая к месту появления проблемы точка (все-таки гораздо понятнее, когда в исключении будет сказано, что это do_something и причина в нулевом значении p). А то ведь crush dump мог быть инициирован где-нибудь на три уровня вложенности ниже. А еще хуже, что он мог быть вообще не инициирован, если в текущем состоянии программы параметр p игнорируется.
Во-вторых, потому, что это дает возможность приложению, если оно спроектировано на восстановление после подобных исключений, восстановиться на каком-то уровне. Ну например, есть многопоточное приложение сервер, обрабатывающее коммуникационные сессии. В рамках обработки одного из запросов одной из сессий диагностируется подобная ошибка. Происходит проброс исключения до самого верхнего уровня обработчика данной сессии. Он понимает, что все плохо, ни на какие из прочитанных в сессии данных надеятся нельзя. Но можно отослать клиенту специальный ответ (что-то типа General Failure или Service Unavailable) после чего просто закрыть сессию и удалить все связанные с ней ресурсы. Такое поведение позволяет оставить все остальные сессии в рабочем состоянии.
А в-третих, если исключения никто не перехватит, то все равно получится тот же самый crush dump.
Попробую резюмировать. Имхо, существует целый класс ошибок -- заведомо неверные аргументы для public-интерфейсных функций/методов. Бороться с такими ошибками, имхо, как раз удобнее выбрасывая исключения на самом раннем этапе. Поскольку есть большая вероятность, что нарушение предусловий (т.е. некорректность аргумента) вызвано не жесткими сбоями в коде (повисшие/битые указатели или что-либо подобное), а элементарными алгоритмическими ошибками или невнимательностью программиста.
Паша, может как раз такие вещи ты и называл в числе тех редких случаев, когда после ошибки в программе восстановление еще возможно?
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, IT, Вы писали:
IT>Не пугай меня. Это называется строить логику программы на исключениях. Объектно-ориентированный longjump. Никакого отношения к надёжности программы это не имеет.
Да ладно тебе — ты сам на таком проекте работаешь и до этого работал — когда некоторые товарисчи вместо проверки на null ловят NullReferenceException и потом гордо считают себя супер профессионалами.
т.е. был код:
public string FormatCurrencyLocalized(decimal amount)
{
if (Resources != null)
return String.Format(Resources.GetString("CurrencyFormat"), amount);
return String.Format("{0}:C", amount);
}
причем в GetString тоже стоит try/catch block который ловит Exception (именно базовый класс) и возвращает пустую строку в случае ошибки...
Могу еще напомнить про .NET Remoting брокер с предидущего проекта и логику на том же самом NullReferenceException... В общем наверное это патерн такой про который мы с тобой у умных дядек не прочитали — Object Oriented LongJump.
Здравствуйте, Павел Кузнецов, Вы писали:
>> Это потому как С++. Я в когда пишу на Шарпе throw пишу там где вижу, что некоторая ситуация не являетс штатной для данного кода.
ПК>Гм... А можно поинтересоваться, в чем разница с C++?..
Можно. Граблей рядом нет и не приходится постоянно оглядываться бросая исключения не попадет ли она на них.
>> Ну, там параметр кривой — throw. Обязательный файл не найден — throw...
ПК>Т.е. логические ошибки и исключительные ситуации в этом отношении ты не разделяешь?
Это еще почему? Логические ошибки вообще контролировать нельзя. На то они и ошбки. Если ты о незапланированных исключениях, то это данность. И боросться с ней нужно только там где без этого никак.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>-1. При недостаче памяти в C++ вылетает std::bad_alloc.
Да, тут я зря экстраполировал поведение фрэймворка на С++. Согласен. Но есть и исключения вроде переполнения стэка которые точно не предсказать и гарантированно не обработать.
>> Ну, да при OutOfMemoryException уже мало что можно поделать. Памяти то нет и обрабатывать исключение просто не чем.
ПК>-1. После раскрутки стека и, соответственно, освобождения некоторой памяти вполне может образоваться нужный запас.
Это уже лотерея. Хватит... не хватит... Если ты получил отлуп при заеме большого обхекта, то возможно дальше что-то и можно сделать. А если маленького?
Ну, и опять таки. Есть и примеры вроде переполнения стека. Тут уж и застраховаться от него не выйдет и обработать в большинстве случаев не выйдет. Короче приплыд. И ничего С++ тут гарантировать не может. О чем речь совственно и шала.
>> А вот NullReferenceException очень даже обрабатывается. При этом в большинстве случае C#-ные приложения продолжают работать дальше, а большинство С++-ных выпадает "в осадок".
ПК>NullReferenceException — типичная логическая ошибка.
Что называть логической, а что физической? Ты бы объяснил свою терминологию. К примеру, если я забы проинициализировать ссылочкную переменную, то это логическая ошибка или по невнимательности/опечатка?
ПК> Внешнему коду доверять странно, если он передал нулевую ссылку там, где этого делать нельзя. Соответственно, надеяться на то, что внешний код корректно "восстановится", тоже не приходится: он уже находится в каком-то невалидном состоянии, на которое не рассчитывали при его разработке.
Ой, ой, ой. Просто град из каких-то не определенных понятий. Что за "внешний код"? Что за доверять? Почему состояние стало обязательно каким-то не таким?
ПК>Попытка "восстановления" после подобной ошибки
Какой такой подобной? Что-то тебя не понять. То ли ты обощашь до нельзя, то ли наоборот говоришь о каком-то одном случае. А ситуации разные бывают.
ПК> зачастую приводит к попаданию в нормальный поток исполнения с нарушенными инвариантами, после чего приложение скорее не работает, чем работает. Т.е., в случае управляемых сред, оно при этом обычно не "падает", но это даже хуже, т.к. у пользователя создается иллюзия исправной работы, в то время, как "внутри" уже самый настоящий глюкодром.
И где на такое посмотреть? Не, я верю, что и такое бывает, но вот твое "обычно" как-то с моими наблюдениями не очень совпадает.
ПК> Это очень легко наблюдать на многих приложениях, написанных на Java и C# в случаях возникновения непредусмотренных исключительных ситуаций.
Не, не нужно брасаться пространными фразами о многих приложениях (их и в природе то нет). Ты пальцем ткни. Вот приложение... а вот "это даже хуже, т.к...". А то больно на чьито выдумки похоже.
ПК> Т.к. чтоб работать в случае нарушенных инвариантов, нужно проверять все на свете, что, очевидно, сделать невозможно.
Ну, а теперь давай отойдем от твоих обвинений управляемых сред в издевательствах над бедными пользователями и проанализируем несколько ситуаций из реальной жизни подкрепив их конкретными примерами.
Итак... Ситуация 1. Мы имеем десктоп приложение написанное на дотнете с использованием винформсов и потому во всю использующее событийную модель работы.
Все действия пользователя превращаются в некие события которые рано или поздно находят свои обработчики. Другими словами при появлении тех или иных внешних событий программа идет по разным путям выполнения. Какие-то пути вызываются часто, какие-то не очень. Но в основм все они модифицируют состояние модели или изменяют настройки представления. При этом в прграмме может оказаться несклько путей с ошибками. Если жить по приципу "все или ничего" исповедумом по умолчанию большинством С++-ных ГУИ-фрэймворков, то баг даже в самой незначительной функции приведет к выподанию в осадок всег приложения (что мы за частую и види). Обоснование этому поведению очень простое — а если баг произошел в следствии повреждения памяти? Данные модели могут быть испорчены и пользователь потеряет не только не записанные данные, но и весь документ? Логично? Почти...
ГУИ-фрэймворк дотнета — ВинФормс — придерживается другого паттерна. Если приложение запущено не под отладчиком, появлении необработанного события, он выдает диалог в котором показывает информацию об ошибке, дополнительную информацию (вроде кол-стэка) и предлагает завершить преложение или продолжить его. При выборе "продолжить" он просто продолжает разбор очереди собщений потока. Другими словами происходит прекращение обработки текущего обработчика и начинается обработка нового.
Конечно если вызванная команда критически важна для приложения или она привела в несотвествие его моедль, то тут уж ничего не попишишь и мы действительно увидим так красочно описанную тобой страшную картину апокалипсиса. Но вот твои слова "обычно" и "еще хуже" явно являются попыткой выдать желаемое за действительное. Обычно ошибки седят в далеко не самых важных частях программы и обычно же модельприложения не приходит в неопределенное состояние. Обычно как раз в таких случаях можно просто ткнуть Continue и продолжить работать дальше избегая вызова данной команды. По крайней мере после этого можно хотя бы записать не записанные данные (хотя бы и под другим именем... на всякий пожарный). Серьдечко у пользователя конечно ёкнет при этом, но инфркт не случится. Данные то ведь не потеряны. А это главное!
Вот тебе простенокий пример того как это происходит:
using System;
using System.Windows.Forms;
using System.Drawing;
public partial class Form1 : Form
{
public Form1()
{
FlowLayoutPanel layout = new FlowLayoutPanel();
Controls.Add(layout);
Button button1 = new Button();
button1.Text = "Нажми меня!";
button1.AutoSize = true;
Button button2 = new Button();
button2.Text = "Не жми меня!";
button2.Font = new Font(button2.Font, FontStyle.Bold);
button2.AutoSize = true;
layout.Controls.Add(button1);
layout.Controls.Add(button2);
button1.Click += delegate { MessageBox.Show(this, "Усё ОК!"); };
button2.Click += delegate { throw new Exception("Случилос страшное!"); };
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
Запусти его не под отладкой и по нажимай на кнопочки.
Причем это не гипотетический пример. В Янусе я частенько наталкивался на подобные "вылеты" и был сильно рад, что мне позволили продолжить работу сохранив пару-тройку недописанных сообщений.
Другой пример — веб-приложение. Или, если быть конкретным, www.rsdn.ru. Великий и могучий ПК не делающий ошибок и потому способный писать безглючные программы не удасужился помочь в разработке сайта, по этому его пришлось делать нам смертым. Так вот временами случаются баги. И как водится баги бывают разными. Бывают такие, что весь сайт работать не может. А бывают и по проще. Ну, там в диалоге регистрации нового юзера NullReferenceException вылетит. Или там в int.Perce кто-то без проверки буковки засунит, а сайтостроители просмотрели. Так вот юзер живет в сессии. А сессия крутися в отдельном потоке. Схлопнется, естествнно именно этот поток. Причем поток благополучно уйдет в пулл и будет использован повторно. И даже самый страшный SteckOverflowException не повалит весь сайт. И как не странно таких случаев (когда ошибка локальна) встречается куда больше чем таких когд весь сайт становится не работоспособным. Опять таки объясняется это просто — ошибок больше в редко используемом коде. А он обычно не является очень важным для всего сайта.
Хуже дела обстаят с пакетными задачами. Ну, теми что чаще всего оформляются в виде утилит командной строки. Вот они зачастую не выдерживают необработанных исключений, так как в них есть только один связанный поток исполнения. Хотя и тут бывают исклчения. Бывает, что задача делится на несколько важных блоков и вылет одного из них не так критичен как вылет всех. Ести функции таких потоков обрамлять try/catch-ами, то глядишь пользователи скажут спасибо.
ПК>Вообще же, в более строгих случаях, для приложений с критическими требованиями, попытки продолжения работы после диагностирования логической ошибки традиционно недопустимы: в таком состоянии приложение должно максимально коротким путем завершить свою работу, при необходимости сохранив текущие данные. Далее, в случае серверного приложения -- задача watch dog сохранить crash dump, log файл и т.п., и перезапустить приложение.
Бывает и так. Вот только в "при необходимости сохранив текущие данные" верится с трудом. Обычно все банальнее. Максиму что сохранятется — это дамп памяти доктором ватсоном.
Вот только приложения в которых действительно будет польза от такого поведения очень не много. По крайней мере не больше чем тех что получают выгоду от продолжения работы.
А вот приложений которые выпадают в осадок и при этом теряют данные хоть дтбавляй. Я сам не раз матерился после таких случаев. И все отмазки вроде "так будет лучше..." при этом только разрдажают.
ПК>В обсуждении, на которое ссылка уже давалась, этот момент подробно рассматривается.
О! Это не та место где можно действительно услышать не предвзятое мнение. Все же небезопасность самого языка накладывает печать на мышление. Все же жить без физической порчи данных куда проще и исключения при этом уже не выглядят такими уж страшными.
ПК>В частности, приводится вполне наглядный пример диагностики в системе автоматического пилотирования отрицательной высоты. Что бы ты в этом случае предпочел, пребывая в самолете: чтоб система пыталась "восстановиться", и продолжить управление, как ни в чем не бывало, или же чтоб она все-таки завершила автопилотирование в надежде на независимые резервные системы с последующим переходом на ручное управление, буде те также откажут?
Прежде всего я думаю, что пример это надуманный для 90% посетителей сайта (возможно я перестраховывась).
Ну, и я бы предпочел, чтобы я не разбился. Если система может гарантировать, что сбой произошел в неважной части и при этом не будет испорчена память, то я бы ередпочел какоий-нить мягкий режим отключения при котором у пилотов появилась бы надпись "Произошел сбой ххх. через ууу секунд автопилот будет отключен, нежели выход самалета в штопор в следствии того, что пилот в этот момент трепался с секретаршей.
В прочем, это настолько малоизвестная мне область и на столько далекая от меня, что я даже не хочу тут серьезно задумываться. Ведь это только один случай из тысячи. И за отмазку он не канает.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Сборка N: ПК>
ПК>void g( I i )
ПК>{
ПК> i.f(); // И какой список здесь должен генерировать компилятор?..
ПК> // Какие сборки будут подгружены, кроме данной, и от какого класса придет I — :xz:
ПК>}
ПК>
Ты имешь в виду, что бы я хотел видеть? Мне было бы достаточно документации на методы. Причем даже достаточно просто текстовой документации. На интерфейсы даже не обязательно. Я и так смогу узнать, что за объекты мне предстоит использовать.
Я не хочу 100%-ного решения. Мне достаточно дополнительной информации.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Вот я и спрашиваю: раз ты предполагаешь, что компилятор в данном случае должен был посчитать, что f() ПК>имеет пустую спецификацию исключений, то по какому алгоритму он действовал?
Ну, интерфейсы и виртуальные методы в таком случае можно было бы специфицировать (как в Яве). Хотя и это мого может хватить чтобы усложнить жизнь. Но уж точно это будет проще чем прописывать все ручками.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Cyberax, Вы писали:
>> E>будет генерировать исключения или нет. Конечно, можно >> перестраховаться и все неизвестные функции в try/catch обрамлять >> (особенно в деструкторах). Но ведь это overhead не слабый получается. >> А зачем обрамлять то? Пропускай их и все. Если появится код которому >> по его логие потребуется обработать исключение, то он и будет знать >> что и как обрабатывать.
C>А откуда он будет это знать?
Программист скажет. Ну, вылетит программка при тестировани... программист подсмотрит тип исключения и обработает его. Или из документации прочитает. В общем, не важно откуда.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Движутся ли mainstream языки в сторону лиспа?
M>Кстати может кто-нибудь попытается сформулировать какое-нибудь обобшенное определение?
Ну, не очень хорошое, но все же есть в википедии http://en.wikipedia.org/wiki/Exception
M>Кстати возврашаясь к Лиспу, т.к. там при "исключении" (ну а точнее сигнале) стек может и не уничтожатся, объекты под GC не попадают, и есть возможность продолжить с того же места, так что понятие той самой ИСКЛЮЧИТЕЛЬНОЙ ситуации для него будет IMHO неприменимо.
В Лиспе многое через зданицу. Менее экстремальные языки реализуют continuation-ы явно и подобных проблем не имеют. В общем, я не знаток лиспа, но точно могу сказать, что использование исключений для управления логикой программы точно не верное решение.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Cyberax, Вы писали:
C>Смотрели, чтобы оно было обработано. Просто эти товарищи пишут 24x7 C>систему, которая должна работать как можно безглючнее.
C>У них это, кстати, получилось. За мой опыт работы с Tangasol C>Coherence'ом мне его свалить не удалось ни разу.
Намекну тебе, что ты сейчас общашся с одним из авторов этого сайта который написал львиную долю кода для этого сайта. Так вот, как близкий к слежением за работой сайта берусь утвержадать, что наш сайт работает 24 часа 7 дней в неделю и перезагружается в основном только если пришел какой-то новый апдэйт требующий перзагрузку или проинсталирован нерадивый софт. И что характиено написан он на C# в котором нет Checked exceptions. Правда это никак не мешает сайту работать годами без перезагрзки и даже модифицироваться на ходу.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Cyberax, Вы писали:
C>А как насчет прибить поврежденные данные и пересинхронизировать их с C>других узлов?
Здорово. Именно этим занимаются те тысячи исключений в Ant-е (или где еще там?)?
C>Нужно следить не столько за типами исключений, сколько за возможностью C>вылета "прикладных" исключений. То есть не логических ошибок в коде, а C>исключительных ситуаций в процессе работы.
Вот за "прикладные исключения" нужно морду бить. Причем сильно и жестоко.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Единственное, что в C#, действительно, мало, что изменится от того, что было выброшено OutOfMemoryException: при раскрутке стека память автоматически не освобождается, так что вся надежда на сборщик мусора. Интересно, может, он как-нибудь специально реагирует на OutOfMemoryException?
Он неплохо реагирует на:
GC.Collect();
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Отчего же? Если памяти освободится недостаточно, при первом же запросе снова будет ПК>выброшен std::bad_alloc, и мы "вылетим" еще выше по стеку, освобождая еще больше ПК>памяти, и так, пока не освободится ее достаточное количество, или, если память заняло ПК>другое приложение, пока наше приложение не завершится в связи с недостаточностью ПК>ресурсов для работы.
Представил себе эту ситуацию. Прикольно! Такое бесзбойное приложение вылетающиее ровно столько чтобы начать хорошо работать. В таких приложениях нужно заранее в стек по больше ссылок на объекты запихать, и поглубзе влезть в процедуры... ну, чтобы вылетать действительно долго.
ПК>В C++ процесс освобождения памяти вполне детерминирован. В чем проблема-то?
В том, что это красивая сказака не имеющая ничего общего с жизнью.
A>> Это может быть совершенно произвольная ошибка.
ПК>Например?
Пример произвольной ошибки? Кррруто!
ПК>Нет, я полагаюсь на систему, которая сгенерирует аппаратное исключение в ответ на разыменование нулевого указателя, и далее в дело вступит мой обработчик данного исключения, и создаст снимок стека или дамп, чтоб я мог определить, в чем же, собственно, ошибка.
А ты для какого рынка программы то пишешь?
ПК>С точки зрения устойчивости приложения некорректно порождать исключение при обнаружении логической ошибки. Нужно обеспечивать внешние резервные средства, и исправлять ошибку, а не маскировать ее исключениями.
Действиетльно. Ну, тебе виднее. Правда дотнетные и явовские приложение которые с этой твоей точкой зрения не согласны наботают все же надежнее, но разве это что-то даказывает? И опять же это вседа можно пиаром назвать...
A>> Ни разу не сталкивался. Вон янус в настоящий момент после любых A>> необработанных исключений в процессе работы продолжает жить.
ПК>Вот на нем-то я как раз недавно и наблюдал типичный упомянутый глюкодром. Произошло ПК>как раз после исключения в ответ на разорвавшееся соединение во время синхронизации.
Паша, ты использовал Янус? А можно поглядеть на отправленные тобой сообщения?
Что-то мне не верится, в то что ты вообще его использовал. Янус как раз при проблемах со связью умудряется работать даже когда к серверу нельзя пробиться из броузера или через почту.
ПК>После этого, даже когда соединение вернулось, ни одна синхронизация не проходила, ПК>сообщая что-то о вновь возникшем исключении.
Ладно, поверим тебе. Но скажи мне, что было бы если в момент синхронизации (которая может начаться и по таймеру) ты будешь писать сообщение? Ты действительно настолько фанатично предант своей идее "что лучше честно вылететь", что будешь готов потерять все незаписанные сообещиния или все же предпочтешь реработающую синхронизацию? Ведь даже если она не работает презагрузить Янус никто ведь не мешает. Првда?
ПК>И так было, пока я не перезапустил приложение.
Беда. Надо было класть приложение. Причем лучше в плюсовом стиле. Без объявления войны. Даже диалоги разные показывать не стоит. Пусть знают наших. За то разработчики Януса получили бы возможность прочесть дамп памяти. Правда они им вряд ли бы помог, но за то как все првилнатобылобы!
ПК>В общем, положил я его в итоге обратно в каталог Install, и больше не трогал.
Да правду написал тот мужит "С++-программисты это те кто любит боль".
ПК>>> Это очень легко наблюдать на многих приложениях, написанных на Java и ПК>>> C# в случаях возникновения непредусмотренных исключительных ситуаций.
A>> Пример таких приложений можно?
ПК>См. выше.
Отмазка не удачная. Надо было Янус хотя бы попробвать.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, BorisKV, Вы писали:
IT>>Не пугай меня. Это называется строить логику программы на исключениях. Объектно-ориентированный longjump. Никакого отношения к надёжности программы это не имеет.
BKV>Да ладно тебе — ты сам на таком проекте работаешь и до этого работал
Знакомьтесь, Борис KV За время своей работы на посту тим. лида в IBM успел уволить с проекта двух американов и двух индусов, одного архитектора разжаловать в девелоперы, одного перевести с "повышением" в другой тим. В общем, добрейшей души человек. Вот только логику на эксепшинах и Object Oriented LongJump так и не сумел искоренить!!!
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Павел Кузнецов, Вы писали:
A>> ...янус...
ПК>Вот на нем-то я как раз недавно и наблюдал типичный упомянутый глюкодром. Произошло ПК>как раз после исключения в ответ на разорвавшееся соединение во время синхронизации. ПК>После этого, даже когда соединение вернулось, ни одна синхронизация не проходила, ПК>сообщая что-то о вновь возникшем исключении.
Специльно проверил твои слова. Запустил соеденение и быстренько выдернул шнур. Как раз попал на закачку новых сообщений. Вот что выводил Янус при этом:
> ПК>void g( I i )
> ПК>{
> ПК> i.f(); // И какой список здесь должен генерировать компилятор?..
> ПК> // Какие сборки будут подгружены, кроме данной, и от какого класса придет I — :xz:
> ПК>}
> ПК>
> > Ты имешь в виду, что бы я хотел видеть? Мне было бы достаточно документации на методы. Причем даже достаточно просто текстовой документации. На интерфейсы даже не обязательно. Я и так смогу узнать, что за объекты мне предстоит использовать.
Чтоб построить список исключений, порождаемом в методе класса, нужно будет также построить список исключений, порождаемых в реализациях методов интерфейсов, которые данный класс вызывает.
> Я не хочу 100%-ного решения. Мне достаточно дополнительной информации.
Боюсь, получится 5%-ное решение, практическая польза от которого будет достаточно небольшой.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, VladD2, Вы писали:
ПК>>Т.е. логические ошибки и исключительные ситуации в этом отношении ты не разделяешь?
VD>Это еще почему? Логические ошибки вообще контролировать нельзя. На то они и ошбки. Если ты о незапланированных исключениях, то это данность. И боросться с ней нужно только там где без этого никак.
Речь скорее всего о софте, который обязан работать 24x7 в любых условиях. Например. софт для 911. Но это совершенно отдельный класс задач, который решается не обработкой всех подряд типов исключений, а, в том числе, написанием софта, который следит за работоспособностью основного софта.
А обработка всех подряд типов исключений...? Я просто не понимаю что с ними потом делать
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
VladD2,
ПК>> -1. При недостаче памяти в C++ вылетает std::bad_alloc.
V> Да, тут я зря экстраполировал поведение фрэймворка на С++. Согласен. Но V> есть и исключения вроде переполнения стэка которые точно не предсказать V> и гарантированно не обработать.
Это уже совсем другая песня. Можно также сказать, что могут выключить питание
компьютера. Речь шла об исключениях, доступных в языке.
??>>> А вот NullReferenceException очень даже обрабатывается. При этом в
??>>> большинстве случае C#-ные приложения продолжают работать дальше, а
??>>> большинство С++-ных выпадает "в осадок".
ПК>> NullReferenceException — типичная логическая ошибка.
V> Что называть логической, а что физической? Ты бы объяснил свою V> терминологию. К примеру, если я забы проинициализировать ссылочкную V> переменную, то это логическая ошибка или по невнимательности/опечатка?
Это логическая ошибка. Другими словами, ошибка программиста. Для понимания "моей"
терминологии см., например, этот учебный курс:
The first thing you have to understand is the difference between programming errors
and exceptions. Programming errors are the misuses of programming abstractions
by programmers. Programming errors are mistakes committed by the programmers
in coding, so that the resulting program code diverges from the what the program
specification allows. Such errors should be corrected before the program is deployed.
Exceptions are unusual events that may occur at runtime even if the program is coded
correctly. Although such an event disrupts the normal execution logic of a program,
they are fully anticipated. That is, exceptions are part of the correct behavior of
a program. The cause of an exception is not an error commit by the programmer. Rather,
it is an unusual runtime event caused by anticipated circumstances.
Ключевые моменты: разница между ошибками и исключительными ситуациями, корректность
программы, выражающаяся в соблюдении контрактов, понятие предусловий. Более подробно
читай о так называемом программировании по контракту.
ПК>> Внешнему коду доверять странно, если он передал нулевую ссылку там, ПК>> где этого делать нельзя. Соответственно, надеяться на то, что внешний ПК>> код корректно "восстановится", тоже не приходится: он уже находится в ПК>> каком-то невалидном состоянии, на которое не рассчитывали при его ПК>> разработке.
V> Ой, ой, ой. Просто град из каких-то не определенных понятий. Что за V> "внешний код"? Что за доверять? Почему состояние стало обязательно V> каким-то не таким?
Потому что код, вызвавший данную функцию, нарушил контракт, не выполнив предусловие.
Соответственно, он некорректен.
ПК>> Попытка "восстановления" после подобной ошибки
V> Какой такой подобной?
После ошибки программиста.
V> Ну, а теперь давай отойдем от твоих обвинений управляемых сред в V> издевательствах над бедными пользователями и проанализируем несколько V> ситуаций из реальной жизни подкрепив их конкретными V> примерами.
Да не обвиняю я управляемые среды... Среда тут ни при чем. Просто в них чаще можно
встретить использование исключений для "обработки" ошибочных ситуаций.
V> модельприложения не приходит в неопределенное состояние. Обычно V> как раз в таких случаях можно просто ткнуть Continue и продолжить V> работать дальше избегая вызова данной команды. По крайней мере после V> этого можно хотя бы записать не записанные данные (хотя бы и под другим V> именем... на всякий пожарный). Серьдечко у пользователя конечно ёкнет V> при этом, но инфркт не случится. Данные то ведь не потеряны. А это V> главное!
Продолжение работы приложения в случае диагностирования ошибочного состояния
для сохранности данных не только не помогает, но и мешает. В любом случае, для
обеспечения сохранности данных нужны более надежные средства. Например, на случай
отключения питания, аппаратного сбоя и т.п. Периодическое сохранение состояния,
журналирование и т.п.
В этом случае никаких специальных действий от пользователя не нужно. Напротив,
нужно как можно быстрее завершить исполнение программы, вышедшей из режима,
предусмотренного разработчиками, чтобы пользователь не работал в этом состоянии,
потенциально не имея возможности сохранить данные, которые он произведет после
сбоя.
V> NullReferenceException вылетит. Или там в int.Perce кто-то без проверки V> буковки засунит, а сайтостроители просмотрели. Так вот юзер живет в V> сессии. А сессия крутися в отдельном потоке. Схлопнется, V> естествнно именно этот поток. Причем поток благополучно уйдет в пулл и V> будет использован повторно. И даже самый страшный SteckOverflowException V> не повалит весь сайт. И как не странно таких случаев (когда ошибка
В случае возможности изоляции потоков исполнения и их данных вполне можно завершать
исполнение только того потока исполнения, в котором диагностировано нарушение логики.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
eao197,
e> вопрос. Тем не менее, в результате совершенно не критических, а иногда e> даже элементарных, ошибок в do_something может быть передан нулевой e> указатель. Причем именно нулевой, т.к. 0 -- это валидное значение для e> обозначения ни на что не указывающего указателя. Например:
e> Функция find_user_grop_name() возвращает имя группы, которой принадлежит e> пользователь. Но, если пользователь не принадлежит ни одной из групп, то e> find_user_grop_name возвращает 0. В данном фрагменте проблема e> программиста в том, что он должен был написать:
e> Но программист ошибся. Он мог вообще не знать, что в каких-то условиях e> пользователь может не принадлежать ни одной группе. Более того, на e> момент написание кода этого вообще могло не быть -- такая вольность в e> отношении пользователей могла появиться уже в процессе развития системы, e> во второй или третьей версии.
Сначала я хотел бы остановиться на самой ситуации. В данном случае лучше было
тем или иным образом сделать потенциальное отсутствие возвращаемого значения
явным. Скажем, так:
в таком случае данная ошибка была бы поймана вовремя. А именно, во время
написания/модификации кода/интерфейса, т.к. пользователь физически не смог
бы пропустить факт потенциального отсутствия возвращаемого значения.
Но допустим, что интерфейс именно такой, как он обозначен ранее...
e> Итак, что в такой ситуации лучше делать? Позволить приложению время от e> времени падать с crush dump-ом или порождать исключение в надежде, что e> на каком-то уровне его смогут обработать и выполнить мягкий рестарт?
Основная проблема в такой логике заключается в том, что от "мягкого" рестарта
ничего не изменится, т.к. невалидное состояние приложения вызвана его логикой,
а не какими-то внешними данными, которые могут прийти в другое состояние.
Хуже того, т.к. мы сообщаем об ошибке средствами, предназначенными для обработки
ситуаций, предусмотренных логикой приложения, мы рискуем вообще "проглотить"
эту ошибку, и продолжить исполнение в невалидном состоянии.
Если можно быть уверенным в изоляции состояния, ассоциированного с потоком
исполнения, в котором диагностирована ошибка, то, конечно, необходимости
завершать работу всего приложения никакой нет.
Соответственно, если считать, что ошибки в каких-то фрагментах приложения
являются допустимой ситуацией для его работы, то нужно максимально изолировать
данные фрагменты.
Примерами могут служить плагины, множественные сессии исполнения в серверах
и т.п. Но я не вижу, как исключения помогают изоляции потоков исполнения за
исключением диагностики одной из ошибочных ситуаций, когда изолируемый фрагмент
выпускает исключение за свои пределы.
Но, имхо, рапортовать нарушение предусловия плагину в виде исключения совершенно
лишнее. В этом случае я бы предпочел, чтобы приложение-контейнер плагинов завершило
работу ошибочного плагина, а не играла с ним в пинг-понг исключениями.
e> Имхо, в случаях, когда возможна проверка на заведомо некорректные e> параметры (как правило, это public интерфейс какого-либо класса, e> библиотеки или модуля), лучше все-таки порождать исключения. Во-первых, e> потому, что это самая ближайшая к месту появления проблемы точка e> (все-таки гораздо понятнее, когда в исключении будет сказано, что это e> do_something и причина в нулевом значении p). А то ведь crush dump мог e> быть инициирован где-нибудь на три уровня вложенности ниже. А еще хуже, e> что он мог быть вообще не инициирован, если в текущем состоянии e> программы параметр p игнорируется.
В общем случае речь не идет об игнорировании нарушения предусловий. Напротив,
когда в этом отношении нельзя положиться на систему, лучше всего вводить
диагностику. И чем больше, тем лучше. Единственное, не стоит смешивать диагностику
ошибок с обработкой исключительных ситуаций, которые к ошибкам программиста
никакого отношения не имеют.
e> Во-вторых, потому, что это дает возможность приложению, если оно e> спроектировано на восстановление после подобных исключений, восстановиться e> на каком-то уровне.
Т.е. ты предлагаешь закладывать в поток исполнения логику, рассчитанную на
обработку ошибок программирования в этом же потоке? Имхо, это безнадежный путь.
Идя по такой дорожке мы должны в каждой точке предполагать возможность
некорректного состояния. Очевидно, что диагностировать все возможные нарушения
нереально.
Вместо этого придумали понятия инвариантов, предусловий, постусловий и т.п.
Т.е. логика программы строится на основе некоторых предположений о ее состоянии.
Когда программа приходит в непредусмотренное состояние (инварианты нарушены),
что диагностируется нарушением предусловий или постусловий, пытаться делать
предположения о том, может ли она прийти обратно в нормальное состояние
бессмысленно.
e> Ну например, e> есть многопоточное приложение сервер, обрабатывающее коммуникационные e> сессии. В рамках обработки одного из запросов одной из сессий e> диагностируется подобная ошибка. Происходит проброс исключения до самого e> верхнего уровня обработчика данной сессии. Он понимает, что все плохо, e> ни на какие из прочитанных в сессии данных надеятся нельзя. Но можно e> отослать клиенту специальный ответ (что-то типа General Failure или e> Service Unavailable) после чего просто закрыть сессию и удалить все e> связанные с ней ресурсы. Такое поведение позволяет оставить все e> остальные сессии в рабочем состоянии.
В таком случае подобная ситуация не является нарушением инвариантов сервера,
а является для него вполне предусмотренной ситуацией, хотя и исключительной.
Нарушаются инварианты некоторого контекста, ассоциированного с изолируемой
сессией. (Если, конечно, этот контекст изовлирован. Если нет, я бы не стал
полагаться на удачу, рискуя данными остальных пользователей.)
Но вот если, например, в коде, диспетчеризующем сессии, т.е. не обладающем
отделяемым контекстом, будет диагностирована подобная ситуация, очевидно, что
пытаться продолжать его исполнение, рискуя валидностью данных всех сессий,
неразумно.
При этом, возвращаясь к ситуации с некорректными сессиями, снова-таки, нет
никакой необходимости прибегать к исключениям для завершения исполнения
сессии. Напротив, намного лучше было бы использовать другой механизм,
гарантирующий, что сессия не сможет "перехватить" прерывание и продолжить
жизнь в невалидном состоянии.
e> Попробую резюмировать. Имхо, существует целый класс ошибок -- заведомо e> неверные аргументы для public-интерфейсных функций/методов. Бороться с e> такими ошибками, имхо, как раз удобнее выбрасывая исключения на самом e> раннем этапе. Поскольку есть большая вероятность, что нарушение e> предусловий (т.е. некорректность аргумента) вызвано не жесткими сбоями в e> коде (повисшие/битые указатели или что-либо подобное), а элементарными e> алгоритмическими ошибками или невнимательностью программиста.
Гм... А какая разница, какими ошибками вызвано нарушение предусловий?
Все равно нужно завершать поток исполнения, в котором диагностирована
ошибка. Согласен, что следует это делать как можно раньше, по возможности
не дожидаясь низкоуровневых/аппаратных сигналов/исключений.
e> Паша, может как раз такие вещи ты и называл в числе тех редких случаев, e> когда после ошибки в программе восстановление еще возможно?
Я подразумевал случаи, когда контекст исполнения изолирован.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Вот на нем-то я как раз недавно и наблюдал типичный упомянутый глюкодром. Произошло ПК>как раз после исключения в ответ на разорвавшееся соединение во время синхронизации. ПК>После этого, даже когда соединение вернулось, ни одна синхронизация не проходила, ПК>сообщая что-то о вновь возникшем исключении.
Твой пример скорее говорит о неотлаженности приложения, чем об устойчивости приложения.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
VladD2,
ПК>> В C++ процесс освобождения памяти вполне детерминирован. В чем ПК>> проблема-то?
V> В том, что это красивая сказака не имеющая ничего общего с жизнью.
Видимо, у нас, все-таки, жизнь разная...
ПК>> Нет, я полагаюсь на систему, которая сгенерирует аппаратное исключение ПК>> в ответ на разыменование нулевого указателя, и далее в дело вступит ПК>> мой обработчик данного исключения, и создаст снимок стека или дамп, ПК>> чтоб я мог определить, в чем же, собственно, ошибка.
V> А ты для какого рынка программы то пишешь?
Сейчас -- работаю в команде, пишущей движки для клиент-серверных бизнес-
приложений (свой orb, свой application server, аналог win fs, свой движок
форм и т.п.)
V> Действиетльно. Ну, тебе виднее. Правда дотнетные и явовские приложение V> которые с этой твоей точкой зрения не согласны наботают все же надежнее,
Надежнее, чем что? Если, чем C++ (делая догадки по остальным сообщениям
в данной теме), то это просто неверно: они по определению не могут работать
надежнее, чем их виртуальные машины, которые как раз на C++ и написаны.
A>>> Ни разу не сталкивался. Вон янус в настоящий момент после любых A>>> необработанных исключений в процессе работы продолжает жить.
ПК>> Вот на нем-то я как раз недавно и наблюдал типичный упомянутый ПК>> глюкодром. Произошло как раз после исключения в ответ на разорвавшееся ПК>> соединение во время синхронизации.
V> Паша, ты использовал Янус? А можно поглядеть на отправленные V> тобой сообщения?
Вряд ли. Я периодически скачиваю новую версию, немного играюсь, в очередной
раз разочаровываюсь, и деинсталлирую. Т.е. прогресс, безусловно, наблюдается,
но как-то пока не торкает... А жаль. В него можно было бы вставить репликацию,
которой мне в News-клиентах не хватает.
V> Что-то мне не верится, в то что ты вообще его использовал. Янус как V> раз при проблемах со связью умудряется работать даже когда к серверу V> нельзя пробиться из броузера или через почту.
Пробовал, пробовал. Если бы у него был лог-файл, я бы привел соответствующий
фрагмент. Попробовал воспроизвести -- не получилось воспроизвести эту ошибку,
но зато получил две других, тоже связанных с обработкой исключительных
ситуаций.
Янус установлен в C:\Bin\RSDN\Janus. База лежала в
D:\Users\Pavel\private\internet\janus.
Пробуя воспроизвести предыдущую ошибку, потушил Янус и удалил содержимое
каталога D:\Users\Pavel\private\internet\janus, перезапустил Янус.
Он справедливо сказал, что файла D:\Users\Pavel\private\internet\janus\config.xml
нет, и выдал окошко "Новый пользователь". Вбил пользователя и пароль, и нажал
"ОК". Не заметил, что в поле каталога для базы почему-то оказалось значение
C:\Bin\RSDN\Janus\Pavel. Получил такую ошибку:
************** Exception Text **************
System.Data.OleDb.OleDbException: Could not find file 'D:\Users\Pavel\private\internet\janus\janus.mdb'.
at System.Data.OleDb.OleDbConnection.ProcessResults(Int32 hr)
at System.Data.OleDb.OleDbConnection.InitializeProvider()
at System.Data.OleDb.OleDbConnection.Open()
at Rsdn.Janus.DatabaseManager.SetVar(String varName, Object varValue)
at Rsdn.Janus.DBVars.SetVar(String name, Object val)
at Rsdn.Janus.LocalUser.CreateUser(String dbp, String uname, String pwd)
at Rsdn.Janus.OptionsUserForm.SaveUserInfo()
at Rsdn.Janus.OptionsUserForm.btnOk_Click(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
<...>
Т.е. Янус решил, что база должна быть в D:\Users\Pavel\private\internet\janus,
а не в C:\Bin\RSDN\Janus\Pavel, где он же мне и предложил создать ее в диалоговом
окошке, хотя создал он ее таки в C:\Bin\RSDN\Janus\Pavel.
Удаляем все, и снова запускаем Янус. На этот раз, хотя в последний раз он
создал config.xml в D:\Users\Pavel\private\internet\janus, почему-то он говорит,
что не найден C:\Bin\RSDN\Janus\Pavel\config.xml. ОК. Появляется окошко "Новый
пользователь". Снова предлагает Вручную вбиваю правильный путь,
D:\Users\Pavel\private\internet\janus, имя и пароль, и жму ОК. Нет, не ОК:
************** Exception Text **************
System.IO.DirectoryNotFoundException: Could not find a part of the path "C:\Bin\RSDN\Janus\Pavel\config.xml".
at System.IO.__Error.WinIOError(Int32 errorCode, String str)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean useAsync,
String msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode)
at Rsdn.Janus.Config.Save()
at Rsdn.Janus.LocalUser.CreateUser(String dbp, String uname, String pwd)
at Rsdn.Janus.OptionsUserForm.SaveUserInfo()
at Rsdn.Janus.OptionsUserForm.btnOk_Click(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
<...>
В общем, пока не пробраузил кнопочкой "Обзор", ему не полегчало...
ПК>> После этого, даже когда соединение вернулось, ни одна синхронизация не ПК>> проходила, сообщая что-то о вновь возникшем исключении.
V> Ладно, поверим тебе. Но скажи мне, что было бы если в момент V> синхронизации (которая может начаться и по таймеру) ты будешь писать V> сообщение? Ты действительно настолько фанатично предант своей идее "что V> лучше честно вылететь", что будешь готов потерять все незаписанные V> сообещиния или все же предпочтешь реработающую синхронизацию?
Все зависит от того, что произошло. Если нарушение предусловия (ошибка
программиста), то я предпочту аварийное завершение, естественно, с сохранением
редактируемого сообщения. Впрочем, так же как и при принудительном завершении
приложения через Task Manager или при "жестком" перезапуске компьютера. Например,
так делает Опера: даже при отключении питания после запуска я возвращаюсь в то
же состояние, что и перед прерыванием работы.
А если просто-напросто прервалось соединение, то я не вижу в этом ничего
чрезвычайного: обычная исключительная ситуация, и я ожидаю от приложения
штатной ее обработки.
A>>> Пример таких приложений можно?
ПК>> См. выше.
V> Отмазка не удачная. Надо было Янус хотя бы попробвать.
Попробовал еще раз (см. выше). Пусть еще подрастет Предлагаю завязать с обсуждением
Януса. Я не хотел о нем говорить, но Андрей в очередной раз привел
VladD2,
V> Специльно проверил твои слова. Запустил соеденение и быстренько выдернул V> шнур. Как раз попал на закачку новых сообщений. <...> V> Отвечаю тебе я тоже без перезапуска Януса. Так что не нужно придумывать V> сказуи.
Дык, непредсказуемость есть непредсказуемость. Подобные ошибки тем и плохи,
что нерегулярны. У меня тоже воспроизвести не получилось. Когда воспроизводилось,
было четко видно, что соединение есть, все приложения работают, Янус тоже
наполовину работает, но где-то в процессе синхронизации обламывается. Немного
с другим сообщением (что-то о преждевременном конце gzip-стрима). И ошибку
в итоге какую-то странную выдавал, по-моему, даже без красного "крестика",
то ли треугольничком, то ли восклицательным знаком в облаке. После
перезапуска ему совершенно четко полегчало, и более он не чудил.
Хотел еще скриншот снять, но поленился.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
IT,
ПК>> соединение во время синхронизации. После этого, даже когда соединение ПК>> вернулось, ни одна синхронизация не проходила, сообщая что-то о вновь ПК>> возникшем исключении.
I> Твой пример скорее говорит о неотлаженности приложения, чем об устойчивости приложения.
Ну, как ни назови, в первоначальную картинку, обрисованную Андреем, не вписалось:
Вон янус в настоящий момент после любых необработанных исключений в процессе работы продолжает жить.
Хотя, жизнь, конечно, у всех разная
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Это логическая ошибка. Другими словами, ошибка программиста. Для понимания "моей" ПК>терминологии см., например, этот учебный курс:
ПК>
ПК>... Such errors should be corrected before the program is deployed.
Твоя терминология имеет смысл до тех пор пока какой-нибудь баг не ушёл в продакшин, после этого всем уже всё равно как его называть.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>NullReferenceException — типичная логическая ошибка. Внешнему коду доверять странно, если он передал нулевую ссылку там, где этого делать нельзя. Соответственно, надеяться на то, что внешний код корректно "восстановится", тоже не приходится: он уже находится в каком-то невалидном состоянии, на которое не рассчитывали при его разработке. Попытка "восстановления" после подобной ошибки зачастую приводит к попаданию в нормальный поток исполнения с нарушенными инвариантами, после чего приложение скорее не работает, чем работает.
Типичная проблема stateful подхода.
ПК> Т.е., в случае управляемых сред, оно при этом обычно не "падает", но это даже хуже, т.к. у пользователя создается иллюзия исправной работы, в то время, как "внутри" уже самый настоящий глюкодром. Это очень легко наблюдать на многих приложениях, написанных на Java и C# в случаях возникновения непредусмотренных исключительных ситуаций. Т.к. чтоб работать в случае нарушенных инвариантов, нужно проверять все на свете, что, очевидно, сделать невозможно.
Используй stateless и будет тебе шастя.
ПК>Вообще же, в более строгих случаях, для приложений с критическими требованиями, попытки продолжения работы после диагностирования логической ошибки традиционно недопустимы: в таком состоянии приложение должно максимально коротким путем завершить свою работу, при необходимости сохранив текущие данные.
Испорченные данные?
ПК>Далее, в случае серверного приложения -- задача watch dog сохранить crash dump, log файл и т.п., и перезапустить приложение.
Ерунда. Влад тебе уже рассказал про десктопы. В случае серверных приложение это тем более неверно. Серверный (да даже просто бизнес код) вообще не должен обрабатывать никаких исключений. Разве что только для логирования, да и то я бы это обсудил. Исключение из бизнес кода должно сквозняком улетать пользователю в первозданном виде. Сервер не должен пытаться его каким-то волшебным образом обработать. Это не его (простите мне мой французский) собачье дело. Не справился с поставленной задачей — доложи тому кто тебя тебе эту задачу поручил.
Другими словами — контекст вызова умирает вместе с вызовом и никаким существенным образом на работоспособность сервера это влиять не должно ни при удачном, ни при не очень исходе.
ПК>В частности, приводится вполне наглядный пример диагностики в системе автоматического пилотирования отрицательной высоты.
Мне было бы очень интересно ознакомиться с методикой обеспечения надёжности в таких системах, но никак не с попыткой обобщения данной предметной области на все виды программного обеспечения.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
ПК>> ... Such errors should be corrected before the program is deployed.
I> Твоя терминология имеет смысл до тех пор пока какой-нибудь баг не ушёл в I> продакшин, после этого всем уже всё равно как его называть.
Не вполне: исключительная ситуация остается исключительной ситуацией, а баг
остается багом. Если он маскируется под исключительную ситуацию, у него есть
все шансы стать трудновоспроизводимым багом, да еще и проявляющимся через
косвенные эффекты.
Если же подобные баги приводят к прерыванию исполнения, у такого бага все
шансы стать гвоздем в заднице службы поддержки. Но, по крайней мере, его
ловить будет легче, и вредить он будет только локально. Если, конечно,
приложение надежно (умеет не терять данные при всевозможных сбоях, и не
только программных).
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Ну, как ни назови, в первоначальную картинку, обрисованную Андреем, не вписалось: ПК>
ПК>Вон янус в настоящий момент после любых необработанных исключений в процессе работы продолжает жить.
Могу это ещё раз подтвердить. Янус, конечно, не самая безглючная программа. Но учитывая то, что это, с самого рождения, по совместительству полигон для удачных и не очень идей, boy scout camp, ну и чуть чуть читалка форумов, то рузультат можно признать вполне удовлетворительным. Не думаю, что любая похожая C++ софтинка выдержала бы подобные издевательства.
ПК>Хотя, жизнь, конечно, у всех разная
Между прочим, твоя жизнь (в том же самом смысле) тоже зависит от кода написанного на C#. Не забыл ещё через что ты сам читаешь RSDN и на чём написан RSDN NNTP Server?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Павел Кузнецов, Вы писали:
I>> Твоя терминология имеет смысл до тех пор пока какой-нибудь баг не ушёл в I>> продакшин, после этого всем уже всё равно как его называть.
ПК>Не вполне: исключительная ситуация остается исключительной ситуацией, а баг остается багом.
Тогда мне интересно твое определение исключительной ситуации для простого деревенского парня. А то вот это мне как-то не совсем по силам
Ключевые моменты: разница между ошибками и исключительными ситуациями, корректность программы, выражающаяся в соблюдении контрактов, понятие предусловий. Более подробно читай о так называемом программировании по контракту.
Что же такое исключительная ситуация?
ПК>Если, конечно, приложение надежно (умеет не терять данные при всевозможных сбоях, и не только программных).
Паша, читай более подробно о stateless. Правильные stateless приложения не теряют данные, т.к. им нечего терять.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT,
ПК>> Попытка "восстановления" после подобной ошибки зачастую приводит к ПК>> попаданию в нормальный поток исполнения с нарушенными инвариантами, ПК>> после чего приложение скорее не работает, чем работает.
I> Типичная проблема stateful подхода.
Ну, stateless далеко не универсален. Посмотрел бы я на stateless компилятор
или stateless графический редактор...
ПК>> приложение должно максимально коротким путем завершить свою работу, ПК>> при необходимости сохранив текущие данные.
I> Испорченные данные?
Нет, конечно, для этого должна быть максимально ранняя диагностика. Плюс должны
автосохраняться несколько версий. Собственно, тот же Word так и делает...
ПК>> Далее, в случае серверного приложения -- задача watch dog сохранить ПК>> crash dump, log файл и т.п., и перезапустить приложение.
I> Ерунда. Влад тебе уже рассказал про десктопы. В случае серверных I> приложение это тем более неверно.Серверный (да даже просто бизнес код) I> вообще не должен обрабатывать никаких исключений.
А я и не говорю об обработке исключений. Я говорю о поведении в случае диагностики
нарушения внутренней целостности сервера, вызванном ошибками программистов или
аппаратными сбоями, приведшими к порче внутренних данных или, более общо, к
непредусмотренному состоянию. Для меня это разные вещи.
Если инварианты программы включают возможность достаточно произвольной модификации
внутренних данных, то это ничего не меняет: все равно нужно будет определить, что
конкретно включают инварианты, и пред-/постусловия просто станут пошире, но все
равно никуда не исчезнут. И обрабатывать нарушение предусловий наравне с "обычными"
исключительными ситуациями (для меня) при этом более осмысленным тоже не станет.
I> Другими словами — контекст вызова умирает вместе с вызовом и никаким I> существенным образом на работоспособность сервера это влиять не должно I> ни при удачном, ни при не очень исходе.
С этим согласен полностью. В случае возможности изоляции контекста при обнаружении
нарушения инвариантов прерывать нужно только поток исполнения (сессию),
ассоциированный с соответствующим контекстом.
Но в случае диагностирования нарушения инвариантов в самом движке сервера,
крутящем сессии, прервать какую-то конкретную сессию уже не выйдет.
То же самое касается и той же виртуальной машины C# или Java. Если она в себе
обнаружит нарушение предусловий, уверен, никаких исключений при этом не будет.
Да и что может сделать клиент в подобной ситуации?.. Нажать "Continue"?
Да чего тут гадать, вот пример (специально пришлось поискать функцию, в которой
и предусловия проверяются, и бросается что-нибудь):
void OleVariant::MarshalOleRefVariantForObject(OBJECTREF *pObj, VARIANT *pOle)
{
THROWSCOMPLUSEXCEPTION();
_ASSERTE(pObj != NULL && pOle != NULL && V_VT(pOle) & VT_BYREF);
<...>
// If the variant types are still not the same then call VariantChangeType to
// try and coerse them.if (V_VT(&vtmp) != vt)
{
COMPlusThrow(kInvalidCastException, IDS_EE_CANNOT_COERCE_BYREF_VARIANT);
}
else
{
InsertContentsIntoByrefVariant(&vtmp, pOle);
}
}
GCPROTECT_END_VARIANTDATA();
}
}
}
Предусловия классически диагностируются с помощью assert. Отклонения входных данных,
не нарушающие предусловия, сообщаются клиенту с помощью исключений. Все по науке
Но вот с чем можно легко согласиться, так это с тем, что, безусловно, грань между
исключениями и ошибками не так уж ясна заранее, до анализа конкретной ситуации.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
"Cyberax" <37054@users.rsdn.ru> wrote in message news:1278475@news.rsdn.ru > mishaa wrote: > >>> В С++ мне жутко нравится механизм nothrow. Вот скрестить бы его с >> Java.... >> nothrow — это Вы про __declspec >> <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_langref_nothrow.asp> >> > > Это я про "throw()" — обозначение того, что функция не будет кидать > никакие исключения. > >> А зачем он в Java? Указать что и unchecked exeptions не проходят? >> Дык, а толку то. Может в C#-пе будет полезен. > > В Яве/C# исключение может вылететь в любой момент (какой-нибудь > OutOfMemory), а в С++ можно гарантировать, что функции не кинут > исключения.
В Whidbey .NET для этой цели были введены Constrained Execution Regions (CER) (http://blogs.msdn.com/bclteam/archive/2005/06/14/429181.aspx). В пределах CER (почти) не могут произойти асинхронные исключения (можно, конечно, специально постараться, но тогда сам виноват). Таким образом облегчается управление unmanaged ресурсами в managed коде. Хотя следует отметить, что CER обходятся не дёшево.
Такие проблемы в .NET по большому счету существуют именно из-за того, что в нём скрестили синхронные и асинхронные исключения и принебрегли тем фактом, что это фундаментально разные типы исключений, и в их обработке применяются фундаментально разные подходы. Вследствии этого уже никакого no-throw guarantee быть не может, да и код, который дает даже basic guarantee писать нетривиально (хотя с CER, наверное, возможно, но опять-же это требует дополнительных затрат в runtime).
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Сначала я хотел бы остановиться на самой ситуации. В данном случае лучше было ПК>тем или иным образом сделать потенциальное отсутствие возвращаемого значения ПК>явным. Скажем, так: ПК>
ПК>в таком случае данная ошибка была бы поймана вовремя. А именно, во время ПК>написания/модификации кода/интерфейса, т.к. пользователь физически не смог ПК>бы пропустить факт потенциального отсутствия возвращаемого значения.
ПК>Но допустим, что интерфейс именно такой, как он обозначен ранее...
Применительно к const char * значение 0 является таким же валидным значением, как и, например, "root" или "wheel". Проблема в другом: в том, что среди множества корректных возвращаемых значений есть подмножество, которое нельзя использовать в каком-то конкретном контексте. Скажем при обращении к do_something.
e>> Итак, что в такой ситуации лучше делать? Позволить приложению время от e>> времени падать с crush dump-ом или порождать исключение в надежде, что e>> на каком-то уровне его смогут обработать и выполнить мягкий рестарт?
ПК>Основная проблема в такой логике заключается в том, что от "мягкого" рестарта ПК>ничего не изменится, т.к. невалидное состояние приложения вызвана его логикой, ПК>а не какими-то внешними данными, которые могут прийти в другое состояние.
ПК>Хуже того, т.к. мы сообщаем об ошибке средствами, предназначенными для обработки ПК>ситуаций, предусмотренных логикой приложения, мы рискуем вообще "проглотить" ПК>эту ошибку, и продолжить исполнение в невалидном состоянии.
Вот здесь уже начинается момент, который мне не нравится. Термин "невалидное состояние" слишком общ и туманен. Для каждого конкретного приложения, более того, для каждого отдельного фрагмента приложения, "невалидность" может иметь совершенно разный смысл.
ПК>Если можно быть уверенным в изоляции состояния, ассоциированного с потоком ПК>исполнения, в котором диагностирована ошибка, то, конечно, необходимости ПК>завершать работу всего приложения никакой нет.
ПК>Соответственно, если считать, что ошибки в каких-то фрагментах приложения ПК>являются допустимой ситуацией для его работы, то нужно максимально изолировать ПК>данные фрагменты.
Да об этом и речь. И мое имхо состоит в том, что публичные внешние интерфейсы как раз являются фрагментами, которые могут предупреждать распространение "невалидности".
ПК>Примерами могут служить плагины, множественные сессии исполнения в серверах ПК>и т.п. Но я не вижу, как исключения помогают изоляции потоков исполнения за ПК>исключением диагностики одной из ошибочных ситуаций, когда изолируемый фрагмент ПК>выпускает исключение за свои пределы.
ПК>Но, имхо, рапортовать нарушение предусловия плагину в виде исключения совершенно ПК>лишнее. В этом случае я бы предпочел, чтобы приложение-контейнер плагинов завершило ПК>работу ошибочного плагина, а не играла с ним в пинг-понг исключениями.
А по мне так лучше, чтобы плагин поглотил все исключения, которые он мог поглотить, и перешел в какое-нибудь специальное состояние. Последующие попытки обращения к плагину в этом состоянии приводили бы к порождению исключений плагина. А вот эти исключения уже должен быть в состоянии обрабатывать основной модуль. Например, путем выгрузки плагина или его рестарта. Важно здесь наличие специального "ошибочного" состояния плагина, в котором работа с плагином невозможна, но сам плагин находится в корректном состоянии, может быть без проблем удален (т.е. обеспечивает базовую гарантию).
e>> Имхо, в случаях, когда возможна проверка на заведомо некорректные e>> параметры (как правило, это public интерфейс какого-либо класса, e>> библиотеки или модуля), лучше все-таки порождать исключения. Во-первых, e>> потому, что это самая ближайшая к месту появления проблемы точка e>> (все-таки гораздо понятнее, когда в исключении будет сказано, что это e>> do_something и причина в нулевом значении p). А то ведь crush dump мог e>> быть инициирован где-нибудь на три уровня вложенности ниже. А еще хуже, e>> что он мог быть вообще не инициирован, если в текущем состоянии e>> программы параметр p игнорируется.
ПК>В общем случае речь не идет об игнорировании нарушения предусловий. Напротив, ПК>когда в этом отношении нельзя положиться на систему, лучше всего вводить ПК>диагностику. И чем больше, тем лучше. Единственное, не стоит смешивать диагностику ПК>ошибок с обработкой исключительных ситуаций, которые к ошибкам программиста ПК>никакого отношения не имеют.
e>> Во-вторых, потому, что это дает возможность приложению, если оно e>> спроектировано на восстановление после подобных исключений, восстановиться e>> на каком-то уровне.
ПК>Т.е. ты предлагаешь закладывать в поток исполнения логику, рассчитанную на ПК>обработку ошибок программирования в этом же потоке? Имхо, это безнадежный путь. ПК>Идя по такой дорожке мы должны в каждой точке предполагать возможность ПК>некорректного состояния. Очевидно, что диагностировать все возможные нарушения ПК>нереально.
ПК>Вместо этого придумали понятия инвариантов, предусловий, постусловий и т.п. ПК>Т.е. логика программы строится на основе некоторых предположений о ее состоянии. ПК>Когда программа приходит в непредусмотренное состояние (инварианты нарушены), ПК>что диагностируется нарушением предусловий или постусловий, пытаться делать ПК>предположения о том, может ли она прийти обратно в нормальное состояние ПК>бессмысленно.
Нет, моя идея в другом (причем это не моя идея, я ее откуда-то давным-давно позаимствовал).
Вот пусть у нас есть объект B у которого есть публичные методы. Публичные методы B должны проводить диагностику заведомо некорректных параметров. Поскольку для B работа с некорректными параметрами является такой же исключительной ситуацией, как например, исчерпание свободного места на диске или невозможность подключения к удаленной БД. И B может сразу отказаться от продолжения работы путем порождения исключения. При этом состояние B остается валидным (т.к. ничего деструктивного он не совершил).
e>> Ну например, e>> есть многопоточное приложение сервер, обрабатывающее коммуникационные e>> сессии. В рамках обработки одного из запросов одной из сессий e>> диагностируется подобная ошибка. Происходит проброс исключения до самого e>> верхнего уровня обработчика данной сессии. Он понимает, что все плохо, e>> ни на какие из прочитанных в сессии данных надеятся нельзя. Но можно e>> отослать клиенту специальный ответ (что-то типа General Failure или e>> Service Unavailable) после чего просто закрыть сессию и удалить все e>> связанные с ней ресурсы. Такое поведение позволяет оставить все e>> остальные сессии в рабочем состоянии.
ПК>В таком случае подобная ситуация не является нарушением инвариантов сервера, ПК>а является для него вполне предусмотренной ситуацией, хотя и исключительной. ПК>Нарушаются инварианты некоторого контекста, ассоциированного с изолируемой ПК>сессией. (Если, конечно, этот контекст изовлирован. Если нет, я бы не стал ПК>полагаться на удачу, рискуя данными остальных пользователей.)
ПК>Но вот если, например, в коде, диспетчеризующем сессии, т.е. не обладающем ПК>отделяемым контекстом, будет диагностирована подобная ситуация, очевидно, что ПК>пытаться продолжать его исполнение, рискуя валидностью данных всех сессий, ПК>неразумно.
Именно так. Но я думаю ты с этим согласен, неразумно выпускать исключение из контекста сессии в контекст диспетчера сессий, если сам контекст сессии в состоянии с этой ситуацией справится. Причем сложность здесь в том, что контекст сессии может даже не знать, что за исключение, где и почему оно возникло. Я бы здесь сделал так: контекст сессии ловит все исключения, пытается откатить все транзакции (в общем смысле, не обязательно это транзакции в БД) и перейти в какое-то начальное/конечное состояние. Если за время такого отката исключений больше нет, то сессия обычным образом завершается (может быть с необходимым логированием, выдачей тревог, отсылкой e-mail и пр.). Но если и в процессе отката возникает исключение, то дело совсем плохо. Вот тут контекст сессии должен прекратить свои попытки и отдать исключение (возможно странслированное) наружу. Но при этом и контекст сессии должен обеспечить базовую гарантию (т.е. отсутствие утечек ресурсов и возможность корректного удаления). А диспетчер сессий пойдет таким же путем -- сначала попробует разобраться с ситуацией сам (прибив сбойную сессию). Если и у него возникнут непредвиденные проблемы, то пусть он их еще выше передает. Но так же обеспечивая базовую гарантию.
И так на каждом уровне абстракции.
e>> Попробую резюмировать. Имхо, существует целый класс ошибок -- заведомо e>> неверные аргументы для public-интерфейсных функций/методов. Бороться с e>> такими ошибками, имхо, как раз удобнее выбрасывая исключения на самом e>> раннем этапе. Поскольку есть большая вероятность, что нарушение e>> предусловий (т.е. некорректность аргумента) вызвано не жесткими сбоями в e>> коде (повисшие/битые указатели или что-либо подобное), а элементарными e>> алгоритмическими ошибками или невнимательностью программиста.
ПК>Гм... А какая разница, какими ошибками вызвано нарушение предусловий? ПК>Все равно нужно завершать поток исполнения, в котором диагностирована ПК>ошибка. Согласен, что следует это делать как можно раньше, по возможности ПК>не дожидаясь низкоуровневых/аппаратных сигналов/исключений.
Я думаю, что разница есть и она важна. Если ошибка вызвана битым/повисшим указателем, то бесполезно пытаться сделать что-то осмысленное. Не понятно, что еще разрушено и не сделаем ли мы хуже, если продолжим работу. Но если ошибка вызвана некорректными входными данными, то лучше эти данные отвергнуть. В том числе и посредством исключения.
Кроме того, мы как-то неявно подразумеваем, что некорректные входные данные (частный случай нарушения предусловий) будут приводить к краху приложения (поэтому мы и приводим в примерах нулевые указатели). Но ведь могут быть и другие последствия. Например, в итерационный метод решения СЛАУ могут передать слишком маленькое значение погрешности (скажем 0.00001 вместо 0.0001). Из-за чего итерация будет сходится, но на несколько порядков дольше. Нарушение предусловия здесь явное, но никакого сбоя не будет -- будет слишком медленная работа.
e>> Паша, может как раз такие вещи ты и называл в числе тех редких случаев, e>> когда после ошибки в программе восстановление еще возможно?
ПК>Я подразумевал случаи, когда контекст исполнения изолирован.
Похоже, что и я говорю о том же. Но я еще настаиваю на том, чтобы на каждом уровне абстракции контекст исполнения был максимально изолирован.
Что мне еще не понравилось в обсуждении в с++.moderated, так это то, что там чуствуется некая вера в то, что программы могут быть полностью отлаженными. И если появляется какая-то ошибка (некорректные аргументы для метода), то это от недостаточности тестирования. Поэтому вместо продолжения работы лучше с фанфарами грохнуться в core dump. Это напоминает веру в то, что статическая типизация или достаточное unit/regression/integration-тестирование способно повысить качество ПО. Имхо, сложность в том, что ошибки есть и будут в отлаженных программах всегда. И что кроме повышения объема и качества тестирования до запуска приложения в эксплуатацию нужно еще заботится о том, чтобы программа была способна выживать в условиях проявления необнаруженных ранее ошибок (об этом речь заходила в Re[23]: Что толку в Ада если Ариан 5 все равно упал
Очень интересно, что все участники дискуссии полностью проигнорировали этот фрагмент отчета. Собственно, ничего удивительного в этом нет, это типичное заблуждение, которое заключается в том, что ПО можно сделать свободным от дефектов и предусматривать какие-то меры восстановления от программных сбоев нет необходимости. И это-то при том, что именно программные ошибки занимают второе место среди причин отказов информационных систем (первое место — cockpit failure), а аппаратные сбои только на третьем.
На самом деле задача должна формулироваться так — необходимо сделать функционирование программно-аппаратного комплекса надежным даже в присутствии систематических дефектов софта, которых полностью избежать невозможно.
Если проводить аналогию с живыми организмами, то сваливание в core dump при обнаружении нарушения предусловия напоминает насильственное лишение жизни после ампутации конечности. Хотя жизнь показывает, то даже с очень большим числом повреждений организм вполне способен жить.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
"Павел Кузнецов" <5834@users.rsdn.ru> wrote in message news:1278839@news.rsdn.ru > eao197, > > e> Мне интересно, как в C++ можно гарантировать, что функция не кинет > исключения? > > Спецификация throw() подходит к этому ближе всего: в случае > исключения исполнение программы будет прервано.
По стандарту это так. Однако, не все компиляторы поддерживают спецификации исключений. И те, что не поддерживают, могут сделать совсем противоположную вещь, а именно использовать throw(), как гарантию от программиста, что данная функция не будет бросать, и генерировать оптимизированный код, который вообще не предусматривает обработку исключений, раскрутку стека и вызов деструкторов. Именно так ведет себя Visual C++ (throw() is equivalent to __declspec(nothrow)).
Такое поведение imo является единственно полезным применением спецификаций исключений в C++.
спасибо большое за интересное обсуждение, подробнее отвечу позже: уже
спать пора, да и подумать надо всем этим еще не мешает, т.к. изначально я как
раз из лагеря, бросающихся исключениями на каждый неосторожный чих клиента,
но потихоньку все больше и больше склоняюсь в сторону позиции, озвученной
мной в данном обсуждении, т.к. чувствую серьезное неудовлетворение
использованием исключений для диагностики нарушений контрактов.
Предварительно могу сказать, что, по крайней мере, для плагинов, тесно
интегрирующихся с приложением, мне нравится идея существования каких-то
отдельных исключений, которые плагин сам поймать не сможет, а вот приложение
через него сквозняком объект такого класса прошвырнет, как только обнаружит
в поведении плагина фатальные симптомы, и не даст плагину более чудить.
e> Кроме того, мы как-то неявно подразумеваем, что некорректные входные e> данные (частный случай нарушения предусловий) будут приводить к краху
Некорректные входные данные не есть частный случай нарушения предусловий.
e> приложения (поэтому мы и приводим в примерах нулевые указатели). Но ведь e> могут быть и другие последствия. Например, в итерационный метод решения e> СЛАУ могут передать слишком маленькое значение погрешности (скажем e> 0.00001 вместо 0.0001). Из-за чего итерация будет сходится, но на e> несколько порядков дольше. Нарушение предусловия здесь явное, но e> никакого сбоя не будет -- будет слишком медленная работа.
Это пример неудачных (даже не некорректных данных), но никак не нарушение
предусловия. Хотя, конечно, грань не так уж точна, но ее всегда можно
определить для заданного приложения, пусть и не универсально.
e> Что мне еще не понравилось в обсуждении в с++.moderated, так это то, что e> там чуствуется некая вера в то, что программы могут быть полностью e> отлаженными. И если появляется какая-то ошибка (некорректные аргументы e> для метода), то это от недостаточности тестирования. Поэтому вместо e> продолжения работы лучше с фанфарами грохнуться в core dump. Это
Напротив, в случае диагностирования ситуации, непредусмотренной логикой
программы, предлагают ее как можно быстрее завершить заранее откатанным способом,
минимально вовлекающим контекст, именно чтобы не напортить данные пользователя,
т.к. понятно, что есть какая-то ошибка, последствия которой неизвестны.
Asserts and "by contract" specifications catch programmer errors not run-time
errors! They catch invalid or unknown state problems caused by programming errors. Do not attempt to recover from assert failures. You are "out of known space."
Don't use asserts for runtime problems such as:
"can't find file"
invalid web site page arg
input arg "rate" too fast
In these cases, you should throw InvalidArgumentException or whatever. The system can
continue to operate in this case.
Asserts/conditions can be taken out after development, but you need input checks always.
What does it mean when contract fails?
If preconditions met, supplier guarantees postconditions.
Failure of precondition implies problem with client.
If postcondition fails, it's a bug in supplier.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
A>> Гарантии нет, а без гарантии мало что построить можно.
ПК>Отчего же?
От того, что, с точки зрения пользователя, вылетает сервер каждый раз или через раз разница небольшая.
ПК>>> NullReferenceException — типичная логическая ошибка.
A>> Это может быть совершенно произвольная ошибка.
ПК>Например?
Приведу, если скажешь что ты подразумеваешь под логической ошибкой.
A>> Ты в своих программах всегда проверяешь все параметры на null? А если он A>> таки null, что делаешь?
ПК>Нет, я полагаюсь на систему, которая сгенерирует аппаратное исключение в ответ на разыменование ПК>нулевого указателя, и далее в дело вступит мой обработчик данного исключения, и создаст снимок ПК>стека или дамп, чтоб я мог определить, в чем же, собственно, ошибка.
И чем это круче NullReferenceException?
ПК>С точки зрения устойчивости приложения некорректно порождать исключение при обнаружении ПК>логической ошибки.
Как раз наоборот. Если это именно ошибка, не подразумевающая какой то набор действий по ее исправлению на ходу, то с точки зрения надежности значительно лучше выкинуть исключение, поскольку код, который о такой ошибке не озаботился, просто проскочит, до тех опр пока мы не упремся в код, который умеет откатываться. Таким образом получается в большинстве случаев избежать перехода программы в невалидное состояние.
Если говорить о том софте, с которым я чаще всего имею дело, то обеспечить откат на самом верхнем уровне обычно не составляет проблем. И сделав это один раз я навсегда избавлюсь от падения софта от практически любого исключения.
ПК> Нужно обеспечивать внешние резервные средства, и исправлять ошибку, ПК>а не маскировать ее исключениями.
Нужно. Но невозможно исправить все ошибки. И куда как предпочтительнее, если сервер просто откатит текущую транзакцию из-за каких то хитрых входных условий, нежели он рухнет. Насчет маскировки не понял — исключение ничего не маскирует, оно наоборот выдает подробную информацию о проблеме, причем с указанием именно той точки, где проблема приключилась. Вот коды возврата маскируют ошибки безусловно.
A>> Ни разу не сталкивался. Вон янус в настоящий момент после любых A>> необработанных исключений в процессе работы продолжает жить.
ПК>Вот на нем-то я как раз недавно и наблюдал типичный упомянутый глюкодром. Произошло ПК>как раз после исключения в ответ на разорвавшееся соединение во время синхронизации. ПК>После этого, даже когда соединение вернулось, ни одна синхронизация не проходила, ПК>сообщая что-то о вновь возникшем исключении.
Первый раз о таком слышу. Надеюсь ты поверишь в то, что опыт использования сего продукта у меня обширный?
"VladD2" <73@users.rsdn.ru> wrote in message news:1279390@news.rsdn.ru > Здравствуйте, Павел Кузнецов, Вы писали: > >> Из деструкторов, равно как и из всяких Finalize и Dispose, >> исключения выпускать не рекомендуется. > > Ну, из Dispose это неприятно но не смертельно. Да и суть Dispose-ов > все же неуправляемые ресурсы освобождать. По сему писать их часто не > приходится и проблем особых не возникает. А с деструкторами согласен.
Не смертельно в смысле того, что runtime сможет продолжить выполнение программы. Однако с точки зрения надёжности выброс из Dispose имеет тот-же эффект, что и выброс из деструктора в C++. Гарантировать сохранение инвариантов, освобождение ресурсов и нормальную обработку исключений в таком случае невозможно.
PS: Напомню, что в C++/CLI деструкторы прямо эквивалентны Dispose(), и Visual C++ по крайней мере в Beta2 генерирует такой код для деструкторов, который в критических случаях просто проглатывает любые исключения, из него выбрасываемые.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Надежнее, чем что? Если, чем C++ (делая догадки по остальным сообщениям ПК>в данной теме), то это просто неверно: они по определению не могут работать ПК>надежнее, чем их виртуальные машины, которые как раз на C++ и написаны.
Это было бы верно, если бы в отладку каждого приложения на С++ вкладывали столько же, сколько в отладку CLR.
ПК>Вряд ли. Я периодически скачиваю новую версию, немного играюсь, в очередной ПК>раз разочаровываюсь, и деинсталлирую. Т.е. прогресс, безусловно, наблюдается, ПК>но как-то пока не торкает... А жаль.
Да, сильна
ПК>В него можно было бы вставить репликацию, ПК>которой мне в News-клиентах не хватает.
AndrewVK,
ПК>> Надежнее, чем что? Если, чем C++ (делая догадки по остальным ПК>> сообщениям в данной теме), то это просто неверно: они по определению ПК>> не могут работать надежнее, чем их виртуальные машины, которые как раз ПК>> на C++ и написаны.
A> Это было бы верно, если бы в отладку каждого приложения на С++ A> вкладывали столько же, сколько в отладку CLR.
Дык, не отладкой единой. При определенном подходе отладка в C++ нужна никак
не больше, чем в C#/Java, т.к. средств для организации статических проверок больше.
ПК>> В него можно было бы вставить репликацию, которой мне в News-клиентах не хватает.
A> Репликацию чего и куда?
Данных между несколькими разными базами. Скажем, на работе, дома на десктопе, и на лаптопе.
Больше всего напрягает рутинно помечать сообщения, прочитанные на одной из машин, как
прочитанные на других машинах.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Дык, не отладкой единой. При определенном подходе отладка в C++ нужна никак ПК>не больше, чем в C#/Java, т.к. средств для организации статических проверок больше.
Это не важно. Важно то что сравнивать надежность прикладных программ с надежностью CLR некорректно.
ПК>>> В него можно было бы вставить репликацию, которой мне в News-клиентах не хватает.
A>> Репликацию чего и куда?
ПК>Данных между несколькими разными базами. Скажем, на работе, дома на десктопе, и на лаптопе. ПК>Больше всего напрягает рутинно помечать сообщения, прочитанные на одной из машин, как ПК>прочитанные на других машинах.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Дык, непредсказуемость есть непредсказуемость. Подобные ошибки тем и плохи, ПК>что нерегулярны. У меня тоже воспроизвести не получилось. Когда воспроизводилось, ПК>было четко видно, что соединение есть, все приложения работают, Янус тоже ПК>наполовину работает, но где-то в процессе синхронизации обламывается. Немного ПК>с другим сообщением (что-то о преждевременном конце gzip-стрима). И ошибку ПК>в итоге какую-то странную выдавал, по-моему, даже без красного "крестика", ПК>то ли треугольничком, то ли восклицательным знаком в облаке. После ПК>перезапуска ему совершенно четко полегчало, и более он не чудил.
Скорее всего просто перезапуск совпал с улучшением состояния канала.
VladD2 wrote:
> C>А откуда он будет это знать? > Программист скажет. Ну, вылетит программка при тестировани... > программист подсмотрит тип исключения и обработает его. Или из > документации прочитает. В общем, не важно откуда.
В общем вы повторяете аргументы сторонников динамических языков.
VladD2 wrote:
> C>У них это, кстати, получилось. За мой опыт работы с Tangasol > C>Coherence'ом мне его свалить не удалось ни разу. > Намекну тебе, что ты сейчас общашся с одним из авторов этого сайта > который написал львиную долю кода для этого сайта. Так вот, как > близкий к слежением за работой сайта берусь утвержадать, что наш сайт > работает 24 часа 7 дней в неделю и перезагружается в основном только > если пришел какой-то новый апдэйт требующий перзагрузку или > проинсталирован нерадивый софт.
Здравствуйте, Cyberax, Вы писали:
C>VladD2 wrote:
>> Намекну тебе, что ты сейчас общашся с одним из авторов этого сайта
C>А RSDN.NNTP не ты писал, случайно?
А ты спрашиваеш с добрыми намерениями или со злыми?
Павел Кузнецов wrote:
> A> Репликацию чего и куда? > Данных между несколькими разными базами. Скажем, на работе, дома на > десктопе, и на лаптопе. > Больше всего напрягает рутинно помечать сообщения, прочитанные на > одной из машин, как > прочитанные на других машинах.
Скоро сбудется твоя ваша мечта
Сейчас как раз пишем систему распределенной синхронизации для Outlook'а.
Так что останется только добавить к нашей системе коннектор к RSDN.
AndrewVK,
ПК>> воспроизводилось, было четко видно, что соединение есть, все ПК>> приложения работают, Янус тоже наполовину работает, но где-то в ПК>> процессе синхронизации обламывается. Немного с другим сообщением ПК>> (что-то о преждевременном конце gzip-стрима). И ошибку в итоге ПК>> какую-то странную выдавал, по-моему, даже без красного "крестика", то ПК>> ли треугольничком, то ли восклицательным знаком в облаке. ПК>> После перезапуска ему совершенно четко полегчало, и более он не чудил.
A> Скорее всего просто перезапуск совпал с улучшением состояния канала.
Не было похоже: ошибку выдавал ровно на одном и том же количестве процентов
прогресс-круга, если так можно сказать.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Нет, конечно, для этого должна быть максимально ранняя диагностика. Плюс должны ПК>автосохраняться несколько версий. Собственно, тот же Word так и делает...
К сожалению пример с Word не корректен. В случае проблемы Word ничего не сохраняет и максимум до чего можно востановленновиться — это или до последней сохраненной вручную/авто версии самого файла или до Auto Recovery Record (по умолчанию сохраняется каждые 10 мин). Так что за все отвечает User — а Word только dump шлет в M$. Я из за глюков в свежевышедшем Word 2003 потерял кучу времени когда нон-стоп писал спеки для девелоперов пока не вышел патч который закрыл этот глюк.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Не было похоже: ошибку выдавал ровно на одном и том же количестве процентов ПК>прогресс-круга, если так можно сказать.
BorisKV,
ПК>> Нет, конечно, для этого должна быть максимально ранняя диагностика. ПК>> Плюс должны автосохраняться несколько версий. Собственно, тот же Word ПК>> так и делает...
B> К сожалению пример с Word не корректен. В случае проблемы Word ничего не B> сохраняет и максимум до чего можно востановленновиться — это или до B> последней сохраненной вручную/авто версии самого файла или до Auto B> Recovery Record (по умолчанию сохраняется каждые 10 мин)
Дык, вот эта Auto Recovery Record и есть один из вариантов того, что подразумевалось...
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
AndrewVK,
ПК>> Не было похоже: ошибку выдавал ровно на одном и том же количестве ПК>> процентов прогресс-круга, если так можно сказать.
A> Могла еще прокся или фаервол чудить.
А как в этом случае помог перезапус Януса? На совпадение совсем не похоже:
я всевозможные варианты пробовал минут 20, параллельно разнообразными
способами удостовериваясь, что соединение живое. Тот же NNTP клиент без
проблем параллельно выкачивал сообщения. Через Web все работало тоже
безо всяких...
В общем, нужно в Янус лог-файл добавлять: по нему были бы шансы узнать,
что происходило.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>А как в этом случае помог перезапус Януса?
ХЗ. Может время таймаута как раз подошло, может фаервол каким то образом умудряется понять от какого процесса запросы.
ПК>я всевозможные варианты пробовал минут 20, параллельно разнообразными ПК>способами удостовериваясь, что соединение живое. Тот же NNTP клиент без ПК>проблем параллельно выкачивал сообщения. Через Web все работало тоже ПК>безо всяких...
Странно это. Я алгоритмы как януса, так и фреймворка более менее представляю, там нет ничего, что бы запоминалось от сеанса к сеансу.
ПК>В общем, нужно в Янус лог-файл добавлять: по нему были бы шансы узнать, ПК>что происходило.
Это не сложно. Боюсь только не очень поможет — при сжатом потоке либо при наличии прокси сообщения приходят маловразумительные.
Andrei N.Sobchuck wrote:
>>> Намекну тебе, что ты сейчас общашся с одним из авторов этого сайта > C>А RSDN.NNTP не ты писал, случайно? > А ты спрашиваеш с добрыми намерениями или со злыми?
С добрыми, с добрыми. Хочу помочь человеку научится летать — с 9 этажа.
AndrewVK,
ПК>> В общем, нужно в Янус лог-файл добавлять: по нему были бы шансы ПК>> узнать, что происходило.
A> Это не сложно. Боюсь только не очень поможет — при сжатом потоке A> либо при наличии прокси сообщения приходят маловразумительные.
Речь идет о логе приложения, а не о записи полученных и отправленных
данных в файл.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Ну, stateless далеко не универсален. Посмотрел бы я на stateless компилятор или stateless графический редактор...
ПК>А я и не говорю об обработке исключений. Я говорю о поведении в случае диагностики нарушения внутренней целостности сервера,
Пока ты не определишься о чём ты, о стайтлес-компиляторе, о системе автопилотирования или о сервере, нам будет трудно разговаривать. У всех у этих задач разное понимание надёжности и соответственно разные способы её достижения.
ПК>вызванном ошибками программистов или аппаратными сбоями, приведшими к порче внутренних данных или, более общо, к непредусмотренному состоянию. Для меня это разные вещи.
Вот я и хочу понять в чём для тебя разница. Я как-то особой разницы не вижу
ПК>И обрабатывать нарушение предусловий наравне с "обычными" исключительными ситуациями (для меня) при этом более осмысленным тоже не станет.
Как я понял под предусловиями ты имеешь ввиду валидацию данных и реакцию на неё вызывающего кода? Ну и в чём здесь разница с точки зрения сервера? Вот ответь на такой вопрос. Допустим ты пытаешься вставить в базу данных запись о пользователе, система требует чтобы e-mail пользователя был уникальным. У тебя есть два способа сделать проверку. Первый — перед вставкой записи в БД проверить наличие такого e-mail и вернуть информацию о неудаче. Второй — поставить в БД констрейн и отдать всё на откуп базе данных. С точки зрения твоего понимания исключений, для тебя это одинаковые случаи или разные?
ПК>То же самое касается и той же виртуальной машины C# или Java. Если она в себе ПК>обнаружит нарушение предусловий, уверен, никаких исключений при этом не будет.
Будет. Я много возился с эмитом. Скормить ему левый код not big deal. Исключение вылетает в run-time. Причём другие части программы продолждают нормально работать.
ПК>Да и что может сделать клиент в подобной ситуации?.. Нажать "Continue"?
Почеу нет? Всё зависит от клиента.
ПК>Предусловия классически диагностируются с помощью assert. Отклонения входных данных, ПК>не нарушающие предусловия, сообщаются клиенту с помощью исключений. Все по науке
Так. Ты меня ещё больше запутал
ПК>Но вот с чем можно легко согласиться, так это с тем, что, безусловно, грань между ПК>исключениями и ошибками не так уж ясна заранее, до анализа конкретной ситуации.
Паша, будь проще и исключения к тебе потянутся... в смысле от тебя... ну в общем ты понял
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Единственное, что в C#, действительно, мало, что изменится от того, что было выброшено OutOfMemoryException: при раскрутке стека память автоматически не освобождается, так что вся надежда на сборщик мусора. Интересно, может, он как-нибудь специально реагирует на OutOfMemoryException?
Не-а. Просто он сам его и бросает.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Павел Кузнецов, Вы писали:
ПК>>-1. При недостаче памяти в C++ вылетает std::bad_alloc.
VD>Да, тут я зря экстраполировал поведение фрэймворка на С++. Согласен. Но есть и исключения вроде переполнения стэка которые точно не предсказать и гарантированно не обработать.
Не совсем так — смотрим на _resetstkoflw
Это, конечно, MS-specific — но шанс обработать переполнение стека вроде как есть
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Данных между несколькими разными базами. Скажем, на работе, дома на десктопе, и на лаптопе. ПК>Больше всего напрягает рутинно помечать сообщения, прочитанные на одной из машин, как ПК>прочитанные на других машинах.
А как ты решаешь эту проблему в случае NNTP?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT,
> ПК> Данных между несколькими разными базами. Скажем, на работе, дома на десктопе, и на лаптопе. Больше всего напрягает рутинно помечать сообщения, прочитанные на одной из машин, как прочитанные на других машинах.
> А как ты решаешь эту проблему в случае NNTP?
Дык, никак не решаю. Как раз в случае NNTP и напрягает помечать... В последний раз, когда полез смотреть Янус, думал именно это в нем найти/сделать. Но, т.к. в очередной раз не получилось заставить себя им пользоваться, пока оставил в покое. Может, потом как-нибудь что-то изменится? Во мне, или в Янусе...
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
>> А как ты решаешь эту проблему в случае NNTP?
ПК>Дык, никак не решаю. Как раз в случае NNTP и напрягает помечать... В последний раз, когда полез смотреть Янус, думал именно это в нем найти/сделать. Но, т.к. в очередной раз не получилось заставить себя им пользоваться, пока оставил в покое. Может, потом как-нибудь что-то изменится? Во мне, или в Янусе...
А я для себя решил Прикрутил к янусу MS SQL, базу поставил на рабочий лаптоп и готово. Прихожу домой, янус на лаптопе вырубаю, на домашней машине врубаю. Домашняя машина использует базу, которая на лаптопе. Сказка
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT,
> ПК> Дык, никак не решаю. Как раз в случае NNTP и напрягает помечать...
> А я для себя решил Прикрутил к янусу MS SQL, базу поставил на рабочий лаптоп и готово. Прихожу домой, янус на лаптопе вырубаю, на домашней машине врубаю. Домашняя машина использует базу, которая на лаптопе. Сказка
Интересный вариант... Только, боюсь, в моем случае не прокатит: у меня три машины (десктопы в офисе и дома, плюс лаптоп). Лаптоп ношу не всегда. Но мысль очень интересная... В общем, как соберусь с духом, опять в Янус полезу, наверное... "Ежики кололись, плакали, но лезли на кактус"
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, BorisKV, Вы писали:
IT>>>Не пугай меня. Это называется строить логику программы на исключениях. Объектно-ориентированный longjump. Никакого отношения к надёжности программы это не имеет.
BKV>>Да ладно тебе — ты сам на таком проекте работаешь и до этого работал
IT>Знакомьтесь, Борис KV За время своей работы на посту тим. лида в IBM успел уволить с проекта двух американов и двух индусов, одного архитектора разжаловать в девелоперы, одного перевести с "повышением" в другой тим. В общем, добрейшей души человек. Вот только логику на эксепшинах и Object Oriented LongJump так и не сумел искоренить!!!
Что именно плохого в простроении логики программы на исключениях (прикладных)? Серьезно.
Здравствуйте, dshe, Вы писали:
D>Что именно плохого в простроении логики программы на исключениях (прикладных)? Серьезно.
Кроме того что упомянул Павел (неочевидность кода) можно добавить ещё то, что в больших количествах исключения существенно подтормаживают скорость выполнения приложения.
Но это всё меркнет по сравнению с одной фичей, которая по моему мнению находится в одном ряду с такими вещами как IntelliSense. Я имею ввиду Debug -> Exceptions -> Break into the debugger. Я это дело включаю всегда и на поиск 99% багов у меня уходит ровно 0 минут 0 секунд. Просто запускаешь приложение, выполняешь сценарий и в случае нештатной ситуации отладчик останавливается там, где произошло исключение. Помнится в 6-й студии была такая штука как break point с условием, работало, но изрядно подтормаживало. Даже был такой вопрос в MCSD треке типа как остановиться на 19874-й итерации цикла чтобы посмотреть что там происходит. Ничего этого теперь не нужно. Просто запускай приложение и жди.
Но есть одно но, которое часто омрачает такую сказочную жизнь. Это любители логики на исключениях. Если приложение кидает исключения налево и направо, то эту фичу приходится отключать и ловить ошибку что называется вручную, применяя старые дедовские методы. А на это уже уходит больше чем 0 секунд и даже на много больше чем 0 минут. Я человек ленивый и всё это меня ужасно раздражает и выводит из себя
Так же замечено, что логикой на исключениях часто замазываются дыры толи по незнанию, толи по халатности. Начиная разбираться в конкретной ситуации чаще всего оказывается, что проблема решается более простыми и честными методами, а то и вообще try/catch просто используется для обхода бага, который надо исправить, а не замазывать.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
BKV>>Да ладно тебе — ты сам на таком проекте работаешь и до этого работал
IT>Знакомьтесь, Борис KV За время своей работы на посту тим. лида в IBM успел уволить с проекта двух американов и двух индусов, одного архитектора разжаловать в девелоперы, одного перевести с "повышением" в другой тим. В общем, добрейшей души человек. Вот только логику на эксепшинах и Object Oriented LongJump так и не сумел искоренить!!!
Ладно тебе прикалываться — я на самом деле добрейшей души человек — особенно как водки выпью — Amicore летает — ну вернее летает то что мы писали и туда же переводят остальную часть приложения. И небыло у нас там логики на исключениях. А для всего остального мы ввели Abstraction Layers Включая текущий проект. И опять же недалее как сегодня, отбил на совещании что на Ослика мы забиваем и наш тим спокойно будет делать Японию без его "солюшинов" — но это вообще дикий оффтоп.
Здравствуйте, Cyberax, Вы писали:
C>Andrei N.Sobchuck wrote:
>>>> Намекну тебе, что ты сейчас общашся с одним из авторов этого сайта >> C>А RSDN.NNTP не ты писал, случайно? >> А ты спрашиваеш с добрыми намерениями или со злыми?
C>С добрыми, с добрыми. Хочу помочь человеку научится летать — с 9 этажа.
C>-- C>С уважением, C> Alex Besogonov (alexy@izh.com)
Внесу свои пять копеек в вашу с Павлом совместную диссертацию.
E>Что мне еще не понравилось в обсуждении в с++.moderated, так это то, что там чуствуется некая вера в то, что программы могут быть полностью отлаженными.
На самом деле, может быть. Практика показывет, что даже при явном недостатке средств для статического анализа кода применение правильной комбинации существующих приёмов программирования и тестирования позволяет достичь очень высокого качества кода. Основная причина низкого качества кода вовсе не в слабости технологий программирования (которая конечно имеет место быть), а в низкой квалификации массового программиста и в банальной недобросовестности. 99% дефектов в софте возникают именно по этой причине.
E>И если появляется какая-то ошибка (некорректные аргументы для метода), то это от недостаточности тестирования. Поэтому вместо продолжения работы лучше с фанфарами грохнуться в core dump. Это напоминает веру в то, что статическая типизация или достаточное unit/regression/integration-тестирование способно повысить качество ПО. Имхо, сложность в том, что ошибки есть и будут в отлаженных программах всегда. И что кроме повышения объема и качества тестирования до запуска приложения в эксплуатацию нужно еще заботится о том, чтобы программа была способна выживать в условиях проявления необнаруженных ранее ошибок (об этом речь заходила в Re[23]: Что толку в Ада если Ариан 5 все равно упал
E>Очень интересно, что все участники дискуссии полностью проигнорировали этот фрагмент отчета. Собственно, ничего удивительного в этом нет, это типичное заблуждение, которое заключается в том, что ПО можно сделать свободным от дефектов и предусматривать какие-то меры восстановления от программных сбоев нет необходимости. И это-то при том, что именно программные ошибки занимают второе место среди причин отказов информационных систем (первое место — cockpit failure), а аппаратные сбои только на третьем.
E>На самом деле задача должна формулироваться так — необходимо сделать функционирование программно-аппаратного комплекса надежным даже в присутствии систематических дефектов софта, которых полностью избежать невозможно.
E>Если проводить аналогию с живыми организмами, то сваливание в core dump при обнаружении нарушения предусловия напоминает насильственное лишение жизни после ампутации конечности. Хотя жизнь показывает, то даже с очень большим числом повреждений организм вполне способен жить.
Это правда. Как показывает опыт, софт действительно обладает определённым запасом выживаемости, как это ни странно.
Здравствуйте, Шахтер, Вы писали:
Ш>Здравствуйте, eao197, Вы писали:
Ш>Внесу свои пять копеек в вашу с Павлом совместную диссертацию.
E>>Что мне еще не понравилось в обсуждении в с++.moderated, так это то, что там чуствуется некая вера в то, что программы могут быть полностью отлаженными.
Ш>На самом деле, может быть. Практика показывет, что даже при явном недостатке средств для статического анализа кода применение правильной комбинации существующих приёмов программирования и тестирования позволяет достичь очень высокого качества кода. Основная причина низкого качества кода вовсе не в слабости технологий программирования (которая конечно имеет место быть), а в низкой квалификации массового программиста и в банальной недобросовестности. 99% дефектов в софте возникают именно по этой причине.
Вот банальный пример, который сегодня мне попался на глаза. Ниже идет фрагмент кода операционной системы vxWorks.
int ftpCommandEnhanced
(
int ctrlSock, /* fd of control connection socket */char *fmt, /* format string of command to send */int arg1, /* first of six args to format string */int arg2,
int arg3,
int arg4,
int arg5,
int arg6,
char *replyString, /* storage for the last line of the server response or NULL */int replyStringLength /* Maximum character length of the replyString */
)
{
char buffer [128];int len;
if (ftplDebug & FTPL_DEBUG_OUTGOING)
{
printErr ("---> ");
printErr (fmt, arg1, arg2, arg3, arg4, arg5, arg6);
printErr ("\n");
}
/* Format Command to send to FTP server */
/* The next line will generate a warning with gcc 2.96+, this is O.K. */sprintf (buffer, fmt, arg1, arg2, arg3, arg4, arg5, arg6); len = strlen(buffer);
/* Append CR LF to format copy to force a single write to TCP */
sprintf(&buffer[len],"%s","\r\n");
len = strlen(buffer);
Данный код используется для отправки команд на FTP сервер.
Включая такие команды как STOR <имя файла>.
Я думаю, не надо напоминать, что длина имени файла может быть очень большой (больше 128 символов в Виндах).
Дефект, показанный в коде выше нельзя назвать случайной ошибкой. Это или недомыслие, или разгильдяйство.
Вот от такого разгильдяйства возникают проблемы на космических аппаратах. И в других дорогих и важных системах.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>dshe,
>> Что именно плохого в простроении логики программы на исключениях (прикладных)? Серьезно.
ПК>При широком использовании — неочевидность (неявность) связей и взаимодействия, с вытекающей сложностью в сопровождении.
Если логика посторена на unchecked исключениях, то действительно, связи становятся неявными. Но я если на checked?
Чтобы говорить более конкретно давайте рассмотрим пример.
Допустим, мы разрабатываем дизайн некого PersonFinder'а, который ищет людей по имени.
Вариант 1.
interface PersonFinder {
/**
* returns null if person has not been found
*/
Person findPersonByName(String name);
}
В этом варианте PersonFinder возвращает null если человек не был найден. При этом вызывающий код обязан проверить полученный результат на null. Причем, он обязан исходя из неявного соглашения описанного в комментариях, которые не все и не всегда читают и пишут. Компилятор не выдаст ни error'а, ни warning'а если результат не будет проверен на null. Это основной недостаток данного подхода.
Вариант 2.
interface PersonFinder {
/**
* throws unchecked PatientNotFoundException if person has not been found,
* never returns null
*/
Person findPersonByName(String name);
}
В этом варианте вызывающий код не обязан проверять результат на null. Однако, если человек не был найден, возникнет исключение, которое, скорее всего, обработается на самом внешнем уровне обработки ошибок, самым общим образом (т.е. как и все остальные ошибки). Оно, конечно, будет более информативным, чем NullPointerException (в первом варианте), но все равно, вызывающий код мог бы обработать данную ситуацию по особому, например, выдать user friendly диалог и предложить новые критерии поиска (которые он мог бы взять из объекта-исключения). Для этого, данное исключение должно быть словлено где-то в непосредственной близости от самого вызова. И опять же, компилятор ничего не скажет если оно не будет обработано вообще.
Вариант 2a.
interface PersonFinder {
/**
* returns true if person has been found,
*/boolean isPersonExist(String name);
/**
* throws unchecked PatientNotFoundException if person has not been found,
* never returns null
*/
Person findPersonByName(String name);
}
Этот вариант является усовершенствованием варианта 2. При этом PatientNotFoundException считается ошибкой программирования, поскольку метод findPersonByName должен использоваться приблизительно таким образом:
if(patientFinder.isPersonExist(name)) {
Person person = findPersonByName(name);
// . . .
}
Это в принципе должно работать, но поскольку поиск человека стал двуэтапным, может возникнуть проблема если человек "исчез" после вызова isPersonExist, но до вызова findPersonByName.
Вариант 3.
interface PersonFinder {
/**
* throws checked PatientNotFoundException if person has not been found,
* never returns null
*/
Person findPersonByName(String name) throws PatientNotFoundException;
}
Этот вариант также является усовершенствованием второго, однако в другом направлении. PatientNotFoundException становится checked. Т.е. вызывающий код обязан его обработать (это гарантируется компилятором). Он может выдать user friendly диалог, а может просто перебросить его как unchecked, для того, чтобы оно обработалось как ошибка на самом внешнем уровне обработки ошибок.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, dshe, Вы писали:
D>>Что именно плохого в простроении логики программы на исключениях (прикладных)? Серьезно.
IT>Кроме того что упомянул Павел (неочевидность кода) можно добавить ещё то, что в больших количествах исключения существенно подтормаживают скорость выполнения приложения.
Согласен. Производительность может существенно повлиять на дизайн. Тем не менее не только исключения влияют на производительность в худшую сторону, но и такие вещи как reflection или даже виртуальный вызов. Но это не повод их избегать. Могу еще добавить, что исключения должны возникать в исключительных ситуациях. Но на мой взгляд, не всегда исключительная ситуация это именно ошибка.
IT>Но это всё меркнет по сравнению с одной фичей, которая по моему мнению находится в одном ряду с такими вещами как IntelliSense. Я имею ввиду Debug -> Exceptions -> Break into the debugger. Я это дело включаю всегда и на поиск 99% багов у меня уходит ровно 0 минут 0 секунд. Просто запускаешь приложение, выполняешь сценарий и в случае нештатной ситуации отладчик останавливается там, где произошло исключение. Помнится в 6-й студии была такая штука как break point с условием, работало, но изрядно подтормаживало. Даже был такой вопрос в MCSD треке типа как остановиться на 19874-й итерации цикла чтобы посмотреть что там происходит. Ничего этого теперь не нужно. Просто запускай приложение и жди.
Я тоже так иногда делаю когда пользуюсь debugger'ом. Правда, breakpoint обычно ставлю только на unchecked исключения. Но чаще просто смотрю в логи -- stacktrace'ов бывает достаточно, чтобы понять где и какая ошибка произошла.
IT>Так же замечено, что логикой на исключениях часто замазываются дыры толи по незнанию, толи по халатности. Начиная разбираться в конкретной ситуации чаще всего оказывается, что проблема решается более простыми и честными методами, а то и вообще try/catch просто используется для обхода бага, который надо исправить, а не замазывать.
D>interface PersonFinder {
D> /**
D> * returns true if person has been found,
D> */
D> booleanisPersonExist(String name);
D> /**
D> * throws unchecked PatientNotFoundException if person has not been found,
D> * never returns null
D> */
D> Person findPersonByName(String name);
D>}
D>
Несколько оффтоп, но поражает насколько наши программисты английского не знают...
Может несколько резко, но меня лично вот от таких названий "коробит".
IT wrote:
> Но есть одно но, которое часто омрачает такую сказочную жизнь. Это > любители логики на исключениях. Если приложение кидает исключения > налево и направо, то эту фичу приходится отключать и ловить ошибку что > называется вручную, применяя старые дедовские методы. А на это уже > уходит больше чем 0 секунд и даже на много больше чем 0 минут. Я > человек ленивый и всё это меня ужасно раздражает и выводит из себя
Интересно, считать ли исключение об ошибке открытия файла — "прикладной"
ошибкой?
VladD2 wrote:
> C>А как насчет прибить поврежденные данные и пересинхронизировать их с > C>других узлов? > Здорово. Именно этим занимаются те тысячи исключений в Ant-е (или где > еще там?)?
Сейчас лень качать сырцы Ант'а — потом посмотрю и скажу.
> C>Нужно следить не столько за типами исключений, сколько за возможностью > C>вылета "прикладных" исключений. То есть не логических ошибок в коде, а > C>исключительных ситуаций в процессе работы. > Вот за "прикладные исключения" нужно морду бить. Причем сильно и жестоко.
"Прикладными" я называю исключения, которые определены в самом
приложении и используются для индикации ошибочных ситуаций.
Здравствуйте, Курилка, Вы писали:
К>Здравствуйте, dshe, Вы писали:
К>....
D>>Вариант 2a. D>>
D>>interface PersonFinder {
D>> /**
D>> * returns true if person has been found,
D>> */
D>> booleanisPersonExist(String name);
D>> /**
D>> * throws unchecked PatientNotFoundException if person has not been found,
D>> * never returns null
D>> */
D>> Person findPersonByName(String name);
D>>}
D>>
К>Несколько оффтоп, но поражает насколько наши программисты английского не знают... К>Может несколько резко, но меня лично вот от таких названий "коробит".
Лично я давно привык к тому, что язык, который используется для идентификаторов лишь отдаленно напоминает английский. Должно быть, с точки зрения литературного английского языка лучше было бы назвать метод как-нибудь так: doesPersonExist или personExists или... кстати, предложите свой вариант. Но is в данном случае показывает, что метод возвращает boolean в соответствии с соглашением об именовании методов (несмотря на то, что с точки зрения литературного английского это, может быть, выглядит дико).
Здравствуйте, Курилка, Вы писали:
К>Здравствуйте, dshe, Вы писали:
D>>
D>> booleanisPersonExist(String name);
D>>
К>Несколько оффтоп, но поражает насколько наши программисты английского не знают... К>Может несколько резко, но меня лично вот от таких названий "коробит".
Здравствуйте, dshe, Вы писали:
D>Здравствуйте, Курилка, Вы писали:
К>>Здравствуйте, dshe, Вы писали:
D>>>
D>>> booleanisPersonExist(String name);
D>>>
К>>Несколько оффтоп, но поражает насколько наши программисты английского не знают... К>>Может несколько резко, но меня лично вот от таких названий "коробит".
D>ок. как насчет варианта isPersonFound?
Логичнее, на мой взгляд, was, просто если выделять с помощью is метод чисто как предикат, то почему же не использовать b как в венгерской нотации или постфикс P как в лиспе? На мой взгляд код должен в первую очередь читаться...
Просто вот пример коллеги — IsChildren, был естественно заменён на HasChildren, согласись в первоначальном варианте надо задуматься чтобы осознать смысл в коде...
В принципе я ничего не имею того, чтобы у вас были свои стандарты, но всёже несоответствие "обычным" (языковым) конструкциям вносит лишнюю сумятицу...
Хотя, может быть, для людей далёких от английского скорее наоборот, только вот чтож эти люди в программировании делают, где без английского ой как туго...
D>interface PersonFinder {
D> /**
D> * throws checked PatientNotFoundException if person has not been found,
D> * never returns null
D> */
D> Person findPersonByName(String name) throws PatientNotFoundException;
D>}
D>
Есть еще одна более удобная разновидность этого варианта:
dshe,
d> Если логика посторена на unchecked исключениях, то действительно, связи d> становятся неявными. Но я если на checked?
Тогда в дело вступают остальные факторы, описанные IT.
d> Чтобы говорить более конкретно давайте рассмотрим пример. d> Допустим, мы разрабатываем дизайн некого PersonFinder'а, который ищет d> людей по имени. <...>
d> Вариант 3. d>
d> interface PersonFinder {
d> /**
d> * throws checked PatientNotFoundException if person has not been
d> found, * never returns null
d> */
d> Person findPersonByName(String name) throws PatientNotFoundException;
d> }
d>
d> <...> d> Пока что, я предпочитаю последний вариант.
В C++ я предпочитаю такой вариант:
boost::optional< Person >
findPersonByName( std::string name );
или, для C#:
Person?
findPersonByName( string name );
или (вот здесь я уже не вполне уверен, можно ли на Java так сделать --
если нет, тем хуже для Java ):
Optional<Person>
findPersonByName( String name );
И не нужно никаких лишних комментариев и, тем более, исключений.
Кроме того, вариант с типами работает не только при возвращении значений из
функций, но и внутри функций:
boost::optional<Person> person;
if ( ... )
person = ...;
else if ( ... )
person = ...;
else
...
...
// теперь физически нельзя пропустить тот момент, что person может не иметь
// значения в данной точкеif ( person )
do_something( *person );
...
И, что, не менее важно, при передаче аргументов (лень придумывать "реальное"
название, но, полагаю, понятно, что случаев, хоть отбавляй):
void f( T1 t1, T2 t2, boost::optional<Person> );
Случай с использованием исключений внутри функции реализуем, но приводит к
изменению структуры кода, а в случае с несколькими объектами -- к его усложнению
и запутыванию.
Случай с передачей аргументов исключениями не выражается, что демонстрирует
плохую масштабируемость подобноо подхода.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Andrei N.Sobchuck, Вы писали:
D>>Вариант 3. D>>
D>>interface PersonFinder {
D>> /**
D>> * throws checked PatientNotFoundException if person has not been found,
D>> * never returns null
D>> */
D>> Person findPersonByName(String name) throws PatientNotFoundException;
D>>}
D>>
ANS>Есть еще одна более удобная разновидность этого варианта:
ANS>
ANS>Правда в Java слишком велик синтаксический оверхед, а вот C#2 такое уже можно использовать
+1 за оригинальность.
В Java я бы это сделал так:
public interface PersonFinder {
public interface FindHandler {
public void personFound(PersonFinder finder, Person person);
public void personNotFound(PersonFinder finder, String name);
}
public void findPersonByName(String name, FindHandler handler);
}
и использование:
finder.findPersonByName(name, new PersonFinder() {
public void personFound(PersonFinder finder, Person person) {
// . . .
}
public void personNotFound(PersonFinder finder, String name) {
// . . .
}
})
К> Несколько оффтоп, но поражает насколько наши программисты английского не К> знают... Может несколько резко, но меня лично вот от таких названий К> "коробит".
С другой стороны, даже если поправить название:
bool doesPersonExist( String name );
при использовании в коде лучше не будет:
if ( doesPersonExist( "Tom" ) )
...
Ну не говорят так
Мне больше нравится так:
if ( person_exists( "Tom" ) )
...
С другой стороны, все равно найдется случай, где любое название будет выглядеть
не на месте с точки зрения английского.
Соответственно, можно расслабиться, и относиться к названиям не как к английскому,
а как к некоторому набору соглашений, где из английского взяты только слова, а их
комбинация подчиняется совсем другим правилам.
Поэтому все это суть личные предпочтения...
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>dshe,
ПК>В C++ я предпочитаю такой вариант: ПК>
ПК>boost::optional< Person >
ПК>findPersonByName( std::string name );
ПК>
ПК>или, для C#: ПК>
ПК>Person?
ПК>findPersonByName( string name );
ПК>
ПК>или (вот здесь я уже не вполне уверен, можно ли на Java так сделать -- ПК>если нет, тем хуже для Java ): ПК>
ПК>Optional<Person>
ПК>findPersonByName( String name );
ПК>
ПК>И не нужно никаких лишних комментариев и, тем более, исключений.
Насколько я понял, в твоем варианте вместо Person возвращается нечто, из чего потом можно Person'а получить. И именно то, что экземпляр Person'а не доступен сразу, является напоминанием программисту о необходимости проверки полученного из findPersonByName результата. Что ж, тоже вариант.
Приведу еще один небольшой аргумент в пользу исключений. Исключения могут с собой нести некоторую дополнительную информацию. Например, PersonNotFoundException мог бы содержать имена существующих людей, которые наиболее близки к искомому. Эту информацию можно было бы использовать для того, чтобы помочь пользователю в его поиске. Признаюсь, я очень редко пользовался этой возможностью и еще реже видел, чтобы класс исключения (написанный не мной) содержал в себе что-то более чем error message.
ПК>Случай с использованием исключений внутри функции реализуем, но приводит к ПК>изменению структуры кода, а в случае с несколькими объектами -- к его усложнению ПК>и запутыванию.
ПК>Случай с передачей аргументов исключениями не выражается, что демонстрирует ПК>плохую масштабируемость подобноо подхода.
На мой взгляд, использование Optional<?> не отрицает использования исключений.
Здравствуйте, VladD2, Вы писали:
C>>Нужно следить не столько за типами исключений, сколько за возможностью C>>вылета "прикладных" исключений. То есть не логических ошибок в коде, а C>>исключительных ситуаций в процессе работы.
VD>Вот за "прикладные исключения" нужно морду бить. Причем сильно и жестоко.
А куда же без них в серверном приложении? У тебя есть сервер, кто-то вызывает методы, параметры не так передал или просто невовремя вызвал метод, или операцию нельзя совершить... — серверу только и остается, что плюнуть ексепшеном в вызывателя.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, VladD2, Вы писали:
ПК>>>Т.е. логические ошибки и исключительные ситуации в этом отношении ты не разделяешь?
VD>>Это еще почему? Логические ошибки вообще контролировать нельзя. На то они и ошбки. Если ты о незапланированных исключениях, то это данность. И боросться с ней нужно только там где без этого никак.
IT>Речь скорее всего о софте, который обязан работать 24x7 в любых условиях. Например. софт для 911. Но это совершенно отдельный класс задач, который решается не обработкой всех подряд типов исключений, а, в том числе, написанием софта, который следит за работоспособностью основного софта.
IT>А обработка всех подряд типов исключений...? Я просто не понимаю что с ними потом делать
Ничего... Часто эти ексепшены в иерархии организованы, так что "мелочиться" на каждый эксепшен и не обязательно.
Здравствуйте, dshe, Вы писали:
IT>>Кроме того что упомянул Павел (неочевидность кода) можно добавить ещё то, что в больших количествах исключения существенно подтормаживают скорость выполнения приложения.
D>Согласен. Производительность может существенно повлиять на дизайн. Тем не менее не только исключения влияют на производительность в худшую сторону, но и такие вещи как reflection или даже виртуальный вызов. Но это не повод их избегать.
Я специально подчеркнул — в больших количествах. Точно так же именно в больших количествах и reflection может оказаться узким местом приложения. Одно дело когда исключения используются по назначению, например, даже для той же валидации ввода пользователя. В такой цепочке как браузер -> интернет -> лоад-балансер -> IIS -> ASP.NET -> твой код -> база данных -> исключение -> и поехали обратно, влияние одного исключения на весь процесс ничтожно мало и учитывать его время в данном случае просто смешно. Другое дело, когда дело дошло до твоего кода и в одном запросе он начал плодить и тут же душить исключения тысячами. Можешь проверить, задержка будет существенной, особенно при большой нагрузке.
D>Могу еще добавить, что исключения должны возникать в исключительных ситуациях.
Именно.
D>Но на мой взгляд, не всегда исключительная ситуация это именно ошибка.
Можно упростить все эти терминологические изыскания. Исключение должно применяться в том случае, если программа не может продолжать своего выполнения. По какой причине — это уже не важно.
D>Я тоже так иногда делаю когда пользуюсь debugger'ом. Правда, breakpoint обычно ставлю только на unchecked исключения. Но чаще просто смотрю в логи -- stacktrace'ов бывает достаточно, чтобы понять где и какая ошибка произошла.
stacktrace — это хорошо. Но часто для выяснения причины нужен контекст выполнения. В отладчике можно подняться на несколько вызовов выше и всё подробно исследовать. В общем, с этой фичей поиск причины неполадки является довольно простым занятием.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, vdimas, Вы писали:
IT>>А обработка всех подряд типов исключений...? Я просто не понимаю что с ними потом делать
V>Ничего... Часто эти ексепшены в иерархии организованы, так что "мелочиться" на каждый эксепшен и не обязательно.
Об этом и речь. Записали в лог или показали пользователю и забыли.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT wrote:
> C>Интересно, считать ли исключение об ошибке открытия файла — > "прикладной" ошибкой? > Твоя программа может после этого продолжать выполнение?
Здравствуйте, dshe, Вы писали:
D>В этом варианте PersonFinder возвращает null если человек не был найден. При этом вызывающий код обязан проверить полученный результат на null. Причем, он обязан исходя из неявного соглашения описанного в комментариях, которые не все и не всегда читают и пишут. Компилятор не выдаст ни error'а, ни warning'а если результат не будет проверен на null. Это основной недостаток данного подхода.
Думаю, это надуманный недостаток. Обычно такие недостатки легко выявляются на стадии тестирования.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Cyberax, Вы писали:
>> C>Интересно, считать ли исключение об ошибке открытия файла — "прикладной" ошибкой? >> Твоя программа может после этого продолжать выполнение?
C>Может, причем инварианты не нарушаются.
Тогда это штатная (ожидаемая) ситуация для твоей программы и можно вполне обойтись без исключения.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, vdimas, Вы писали:
VD>>Вот за "прикладные исключения" нужно морду бить. Причем сильно и жестоко.
V>А куда же без них в серверном приложении? У тебя есть сервер, кто-то вызывает методы, параметры не так передал или просто невовремя вызвал метод, или операцию нельзя совершить... — серверу только и остается, что плюнуть ексепшеном в вызывателя.
Речь в данном случае о другом. Речь о ситуации, когда сервер "выплёвывает" и тут же сам это хозяйство ловит, типа обрабатывает.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT wrote:
>>> C>Интересно, считать ли исключение об ошибке открытия файла — > "прикладной" ошибкой? >>> Твоя программа может после этого продолжать выполнение? > C>Может, причем инварианты не нарушаются. > Тогда это штатная (ожидаемая) ситуация для твоей программы и можно > вполне обойтись без исключения.
И возвращать код ошибки через 5-6 уровней? Нет уж, спасибо.
Здравствуйте, IT, Вы писали:
ПК>>Если, конечно, приложение надежно (умеет не терять данные при всевозможных сбоях, и не только программных).
IT>Паша, читай более подробно о stateless. Правильные stateless приложения не теряют данные, т.к. им нечего терять.
Хм... Современные "правильные" приложения состоят из многих составляющих, иногда физически распределенных. Скажем, у нас есть stateless-сервер. Ok. Но клиент набивал длинный текст в окошке GUI. Форма GUI не может быть полностью stateless (по крайней мере это труднодостижимо — скидывать на диск по каждому событию типа OnChar или OnClick). Далее, при ошибке сохранения данных формы на сервер, было бы неплохо не потерять введенные пользователем данные. Ошибка могла возникнуть не только по причине отсутствия связи. Все гораздо интересней, если связь есть, но сервак выплевывает ошибку. Наверно, Пашин совет касается подобных вещей.
---------
В нашей системе мы используем 2 корня иерархии ошибок: LogicError и RuntimeError. Первый случай описывает логические ошибки пользователя системы (неккоректные данные или действия). Второй тип ошибок — это ексепшены, выкинутые не прикладным кодом. Т.е. ошибка произошла "сама по себе" (Ресурсы или ОС виноваты) или по вине программиста. Пытаться что-то сделать при этом обычно бесполезно, как раз нужно на клиенте сохранить текущий контекст, на сервере сохранить подробный лог и stack-trace и закрыть данную клиентскую сессию с аккуратной прочисткой ресурсов, ибо последующее взаимодействие клиента с сервером в рамках той же сессии может привести к непредсказуемым результатам (если ошибка произошла по вине программистов).
IT>В случае серверных приложение это тем более неверно. Серверный (да даже просто бизнес код) вообще не должен обрабатывать никаких исключений. Разве что только для логирования, да и то я бы это обсудил. Исключение из бизнес кода должно сквозняком улетать пользователю в первозданном виде. Сервер не должен пытаться его каким-то волшебным образом обработать. Это не его (простите мне мой французский) собачье дело. Не справился с поставленной задачей — доложи тому кто тебя тебе эту задачу поручил.
Это иногда невозможно, если у нас гетерогенная среда (CORBA, например). Мы вынуждены ловить ексепшены, чтобы "завернуть" их в транспортный вид.
IT>Другими словами — контекст вызова умирает вместе с вызовом и никаким существенным образом на работоспособность сервера это влиять не должно ни при удачном, ни при не очень исходе.
Тоже не все так просто. В нашей системе редактируется некая сущность. Затем вызывается процедура валидации данных. Процедура сложная и зависит от многих вещей — короче, делаем валиадцию на серваке. Результат валидации — не просто yes/No — а тоже вполне осмысленный набор записей. И вот что характерно — мы хотим валидировать подобным образом текущие редактируемые данные ДО сохранения в базе. И как тут поможет stateless?
Здравствуйте, vdimas, Вы писали:
V>Хм... Современные "правильные" приложения состоят из многих составляющих, иногда физически распределенных. Скажем, у нас есть stateless-сервер. Ok. Но клиент набивал длинный текст в окошке GUI. Форма GUI не может быть полностью stateless (по крайней мере это труднодостижимо — скидывать на диск по каждому событию типа OnChar или OnClick).
— моя первая реплика.
V>Далее, при ошибке сохранения данных формы на сервер, было бы неплохо не потерять введенные пользователем данные. Ошибка могла возникнуть не только по причине отсутствия связи. Все гораздо интересней, если связь есть, но сервак выплевывает ошибку. Наверно, Пашин совет касается подобных вещей.
Согласись, что сохранение данных формы — это не дело сервера.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Cyberax, Вы писали:
>> C>И возвращать код ошибки через 5-6 уровней? Нет уж, спасибо.
Да, извини, просто ты сам вполне однозначно ответил на свой вопрос.
Я уже тут не раз говорил, что из двух подходов лучше тот, прикладной код для которого проще. Если тебе не удобно протягивать резалт коды через 5-6 уровней, то конечно же используй то, что тебе удобнее. На самом деле я не раз убеждался в том, что слепое следование теории и "общепринятым" правилам в программировании, области человеческой деятельности, у которой практически ещё нет истории и сложившихся методик, чаще приводит к казусам, чем к правильному результату.
По-этому, повторюсь ещё раз: хорошее решение должно порождать простой прикладной код, очень хорошее – очень простой, а идеальное решение – синоним примитивного кода. И не слушай никого
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
V>>Далее, при ошибке сохранения данных формы на сервер, было бы неплохо не потерять введенные пользователем данные. Ошибка могла возникнуть не только по причине отсутствия связи. Все гораздо интересней, если связь есть, но сервак выплевывает ошибку. Наверно, Пашин совет касается подобных вещей.
IT>Согласись, что сохранение данных формы — это не дело сервера.
Это ещё почему?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, vdimas, Вы писали:
V>Это иногда невозможно, если у нас гетерогенная среда (CORBA, например). Мы вынуждены ловить ексепшены, чтобы "завернуть" их в транспортный вид.
И протолкнуть его дальше. Тоже самое в случае ремоутинга. Такие среды это делают неявно, без нашего вмешательства. Другой пример — логирование исключений. В случае ремоутинга это тоже можно сделать прозрачно для прикладного программиста, иначе придётся ловить, логировать и прокидывать дальше. Надеюсь, AOP в скором времени сможет закрыть эту дырку.
IT>>Другими словами — контекст вызова умирает вместе с вызовом и никаким существенным образом на работоспособность сервера это влиять не должно ни при удачном, ни при не очень исходе.
V>Тоже не все так просто. В нашей системе редактируется некая сущность. Затем вызывается процедура валидации данных. Процедура сложная и зависит от многих вещей — короче, делаем валиадцию на серваке. Результат валидации — не просто yes/No — а тоже вполне осмысленный набор записей. И вот что характерно — мы хотим валидировать подобным образом текущие редактируемые данные ДО сохранения в базе. И как тут поможет stateless?
State всегда есть, в твоём примере он находится на клиенте. Сервер же остаётся stateless.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Геннадий Васильев, Вы писали:
V>>>Далее, при ошибке сохранения данных формы на сервер, было бы неплохо не потерять введенные пользователем данные. Ошибка могла возникнуть не только по причине отсутствия связи. Все гораздо интересней, если связь есть, но сервак выплевывает ошибку. Наверно, Пашин совет касается подобных вещей.
IT>>Согласись, что сохранение данных формы — это не дело сервера. ГВ>Это ещё почему?
Смотрим контекст. Я не о методе сервера SaveWhatever, а о хранении введённых данных пока этот вызов не завершиться успешно.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
> interface PersonFinder {
> /**
> * returns null if person has not been found
> */
> Person findPersonByName(String name);
> }
>
> В этом варианте PersonFinder возвращает null если человек не был > найден. При этом вызывающий код обязан проверить полученный результат > на null. Причем, он обязан исходя из неявного соглашения описанного в > комментариях, которые не все и не всегда читают и пишут. Компилятор > не выдаст ни error'а, ни warning'а если результат не будет проверен > на null. Это основной недостаток данного подхода.
Т.е. основной недостаток — это то, что программист невнимателен. Он даже не
удосужился прочитать документацию по тем API, которые использует.
Я достаточно времени провел программируя на C#. И в нём не допустить, что
значения могут быть null, нельзя. Просто сразу привыкаешь к этому факту.
После чего всё происходит на автомате: "ага, метод возвращает значение, а
если null?". Никаких проблем. Это как, например, привыкнуть к тому, что в
C++ нужно вызывать ++iter для итератора. Сильно подозреваю, что и в Java
такая привычка вырабатывается сразу.
> Вариант 3. >
> interface PersonFinder {
> /**
> * throws checked PatientNotFoundException if person has not been
> found,
> * never returns null
> */
> Person findPersonByName(String name) throws PatientNotFoundException;
> }
>
> > Этот вариант также является усовершенствованием второго, однако в > другом направлении. PatientNotFoundException становится checked. Т.е. > вызывающий код обязан его обработать (это гарантируется > компилятором). Он может выдать user friendly диалог, а может просто > перебросить его как unchecked, для того, чтобы оно обработалось как > ошибка на самом внешнем уровне обработки ошибок.
Начну из далека. Представь, что ты работаешь в support'е. И вот нужно тебе
найти баг в незнакомом тебе приложении. Запускаешь ты его под отладчиком.
Exception tracing включены. Вот приложение стартует, и ты наблюдаешь
сообщения отладчика о сотнях и сотнях выбрасываемых исключениях... Да,
ощущение возникает не из приятных. Программа вроде работает, но откуда весь
этот мусор? А может она просто делает вид, что работает? Задаёшься
вопросом: какого хрена думали её создатели.
Возвращаясь к нашим баранам, это в точности то, к чему приводит вариант #3.
Теперь более конкретно. Какой код нужно написать при использовании
интерфейса в варианте 1 и 3:
// 1:for each (String name in names) {
Person p = personFinder.findPersonByName(name);
if (p != null) {
// ...
// do something (2 screens of code)
// ...
}
}
// 3:for each (String name in names) {
try {
Person p = personFinder.findPersonByName(name);
// ...
// do something (2 screens of code)
// ...
} catch (PatientNotFoundException) {
}
}
Допустим, человек, не знакомый с этим кодом, читает его. Что он думает в
случае 1: "ага, берём имена, ищем по ним людей, if (p != null) — если нашли,
делаем..." В случае 3: "ага, берём имена, ищем по ним людей, с человеком p
делаем — стоп! а если p==null?". Листаем 2 экрана вперёд, получаем ответ на
этот вопрос.
Т.е. действий нужно совершать гораздо больше, так как интерфейс в варианте
3 — не интуитивный.
Кстати, узнаёшь: catch (PatientNotFoundException) {} — знаменитый Java
anti-pattern. Поймал исключение и пропустил его. Тут как тут. И никуда от
него не деться.
> // 3:
> for each (String name in names) {
> try {
> Person p = personFinder.findPersonByName(name);
> // ...
> // do something (2 screens of code)
> // ...
> } catch (PatientNotFoundException) {
> }
> }
>
> <...> > Кстати, узнаёшь: catch (PatientNotFoundException) {} — знаменитый Java > anti-pattern. Поймал исключение и пропустил его. Тут как тут. И никуда от > него не деться.
Еще интереснее, когда у нас будет не только PatientNotFoundException, но код станет сложнее, и будет запрашивать много разных вещей, которых может не существовать. При этом очень быстро может надоесть выписывать все эти различные типы исключений, и в итоге запросто напишут catch(Exception), игнорируя не только исключения, сигнализирующие о не найденном объекте, но и исключения, сигнализирующие об ошибках. Например (названия условные):
for each (Record r in records) {
try {
City city = cityFinder.findCityByName(r.cityName);
// ...
// do something
// ...
Hospital hospital = city.findHospitalByName(r.hospitalName);
// ...
// do something
// ...
Person patient = hospital.findPersonByName(r.patientName);
// ...
// do something
// ...
} catch (Exception) {
}
}
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, alexeiz, Вы писали:
A>dshe wrote:
A>Т.е. основной недостаток — это то, что программист невнимателен. Он даже не A>удосужился прочитать документацию по тем API, которые использует. Я достаточно A>времени провел программируя на C#.
Тогда, должно быть, тебе крупно повезло поскольку в реальной жизни (судя, правда, по моему опыту) чаще происходит что-то из следующего:
1. программист, который использует API, не читает документацию по API потому, что ее просто нет. Наиболее частое обоснование для ненаписания документации -- это отсутствие на это времени.
2. программист, который использует API, читает документацию, но эта документация устарела. Наиболее частое обоснование для поддержания документации up-to-date -- это отсутствие на это времени.
3. программист, который использует API, читает документацию, но эта документация не содержит информации, которая ему нужна. Вот пример такой документации:
/**
* This method gets bitrate.
* This method is part of the SuperPuper System
* Copyright (c) SuperPuper, Inc. 2000, 2001, 2002, 2003, 2004, 2005, 2006
* All Rights Reserved
* This method contains unpublished, confidential and proprietary
* information of SuperPuper, Inc. No disclosure or use of
* any portion of the contents of these materials may be made without the
* express written consent of SuperPuper, Inc.
*
* Author: Vasia Pupkin
* Description:
*/public void getBitrate();
Обычно такая документация появляется когда начальство (осознавая важность документирования) навязывает писать хоть какую-то документацию.
4. программист, который использует API, не читает документацию, поскольку не надеется увидеть там что-нибудь для себя полезное (в силу приведенных выше пунктов).
Поэтому, на мой взгляд, полагаться только на документацию как средства передачи информации о том, как использовать API, -- просто легкомысленно. Не менее важно делать API юзабельным и интуитивным, т.е. каким, чтобы было сразу понятно как его использовать и чтобы неправильно использовать было просто невозможно. А для этого можно использовать различные методы, такие как соглашение об именовании, строгий контроль типов, checked исключения (?). Вот те три варианта, которые я привел, и были попыткой найти наиболее интутивный интерфейс.
A>Начну из далека. Представь, что ты работаешь в support'е. И вот нужно тебе A>найти баг в незнакомом тебе приложении. Запускаешь ты его под отладчиком. A>Exception tracing включены. Вот приложение стартует, и ты наблюдаешь A>сообщения отладчика о сотнях и сотнях выбрасываемых исключениях... Да, A>ощущение возникает не из приятных. Программа вроде работает, но откуда весь A>этот мусор? А может она просто делает вид, что работает? Задаёшься A>вопросом: какого хрена думали её создатели.
Должно быть язык и платформа действительно сильно влияют на мировоззрение. Отлаживая программу на Java, у меня бы особого желания ставить breakpoint на все исключения не было бы. Слава богу, поставить breakpoint на java.lang.RuntimeException (программные ошибки, большинство unchecked исключений) не намного сложнее, чем на java.lang.Throwable (все исключения). Впрочем, когда я буду писать на C# я воспользуюсь твоим и IT советом включать Exception tracing.
A>Возвращаясь к нашим баранам, это в точности то, к чему приводит вариант #3. A>Допустим, человек, не знакомый с этим кодом, читает его. Что он думает в A>случае 1: "ага, берём имена, ищем по ним людей, if (p != null) — если нашли, A>делаем..." В случае 3: "ага, берём имена, ищем по ним людей, с человеком p A>делаем — стоп! а если p==null?". Листаем 2 экрана вперёд, получаем ответ на A>этот вопрос.
Человек, незнакомый с этим кодом, читая его, сразу видит наиболее типичный сценарий использования (не отвлекаясь на его ответвления-вариации), а пролистав 2 экрана вперед видит, что происходит в редких, исключительных случаях.
A>Кстати, узнаёшь: catch (PatientNotFoundException) {} — знаменитый Java A>anti-pattern. Поймал исключение и пропустил его.
Да, есть такое дело. Еще более опасный антипаттерн это
1.
catch(Exception exc) {}
поскольку что ловятся и игнорируются также и RuntimeException (ошибки программирования)
и
2. throws Exception в декларации методов, поскольку это вынуждает писать catch(Exception exc).
Я ни в коем случае не агитирую за использования данных антипаттернов.
dshe,
d> поскольку что ловятся и игнорируются также и RuntimeException (ошибки d> программирования) и
Интересно, насколько разные вещи, вплоть до противоположных, называются одинаково в разных языках...
В C++ исключения, обозначающие ошибки программирования, берут свое начало от std::logic_error (выход
за границы, неправильные аргументы и т.п.), а исключения, унаследованные от std::runtime_error,
означают как раз таки вполне обычные исключения (переполнение в вычислениях и т.п.)...
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
V>>Тоже не все так просто. В нашей системе редактируется некая сущность. Затем вызывается процедура валидации данных. Процедура сложная и зависит от многих вещей — короче, делаем валиадцию на серваке. Результат валидации — не просто yes/No — а тоже вполне осмысленный набор записей. И вот что характерно — мы хотим валидировать подобным образом текущие редактируемые данные ДО сохранения в базе. И как тут поможет stateless?
IT>State всегда есть, в твоём примере он находится на клиенте. Сервер же остаётся stateless.
Я бы поспрашивал подробнее именно этот момент, ибо ситуация у нас более чем частовстречающаяся. Итак, юзверь редактирует некие данные (например, набивает в список некое множество из объектов другого типа, т.е. ассоциирует объекты). Я говорил, у нас есть некий EntityWrapper ("entity view" в будущем), который предназначен для удаленного взаимодействия с сущностью. Именно он хранит у нас текущие измененные значения в описываемом случае. Я передаю данные из клиентского адаптера на этот серверный, но команду "Save" не вызываю (может и не вызову, если валидация не пройдет). Таим образом, на стороне сервера в момент валидации есть вся информация о том, в каком состоянии будет объект после операции Save. Разве это стателесс?
Здравствуйте, vdimas, Вы писали:
VD>>Вот за "прикладные исключения" нужно морду бить. Причем сильно и жестоко.
V>А куда же без них в серверном приложении? У тебя есть сервер, кто-то вызывает методы, параметры не так передал или просто невовремя вызвал метод, или операцию нельзя совершить... — серверу только и остается, что плюнуть ексепшеном в вызывателя.
Это норальное использование исключений. Здесь же я говорил об использовании исключений в целях управления потоком выполнения программы.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Интересный вариант... Только, боюсь, в моем случае не прокатит: у меня три машины (десктопы в офисе и дома, плюс лаптоп). Лаптоп ношу не всегда. Но мысль очень интересная... В общем, как соберусь с духом, опять в Янус полезу, наверное... "Ежики кололись, плакали, но лезли на кактус"
Купи себе USB2-винт. Положи на него БД от януса и пользуйся с любой машины.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
VD>> Янус как раз при проблемах со связью умудряется работать даже когда к серверу нельзя пробиться из броузера или через почту.
ANS>И умудряется не работать при отсутсвии оных проблем. Как это было в понедельник.
Незнаю. У меня работает. Хотя я пользуюсь альфами. У тебя же вроде вообще стабильная сборка.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Дык, вот эта Auto Recovery Record и есть один из вариантов того, что подразумевалось...
Ты видмо плохо знаком с вордом. Auto Recovery в нем — это попытка при вылете востановить данные и скинуть их во временный файл, чтобы при повторном вызове дать возможность пользователю их сохранить. Вот только в ворде очень часто память портится и сохраненные таким образом документы в дальнейшем начинают вылетать постоянно.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2,
ПК>> Дык, вот эта Auto Recovery Record и есть один из вариантов того, что ПК>> подразумевалось...
V> Ты видмо плохо знаком с вордом. Auto Recovery в нем — это попытка при V> вылете востановить данные и скинуть их во временный файл, чтобы при V> повторном вызове дать возможность пользователю их сохранить. Вот только V> в ворде очень часто память портится и сохраненные таким образом V> документы в дальнейшем начинают вылетать постоянно.
Ну, мы с тобой точно не знаем, почему именно документ получается "плохим". Я бы, скорее предположил,
что дело в порче инвариантов. В случае же продолжения работы было бы не лучше: пользователь успел бы
много наредактировать, прежде чем стало бы ясно, что документ уже ни к черту. В случае "вылета", если
Auto Recovery приводит к проблемам (чего я на своих документах не замечал, но и Word у меня сам по себе
не вылетает, за исключением отключения питания машины без shut down), то можно воспользоваться последней
autosaved версией.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
VladD2 wrote:
> VD>>Вот за "прикладные исключения" нужно морду бить. Причем сильно и > жестоко. > V>А куда же без них в серверном приложении? У тебя есть сервер, кто-то > вызывает методы, параметры не так передал или просто невовремя вызвал > метод, или операцию нельзя совершить... — серверу только и остается, > что плюнуть ексепшеном в вызывателя. > Это норальное использование исключений. Здесь же я говорил об > использовании исключений в целях управления потоком выполнения программы.
Является ли выброс исключения для индикации ошибки — управлением потоком?
Здравствуйте, IT, Вы писали:
V>>Тоже не все так просто. В нашей системе редактируется некая сущность. Затем вызывается процедура валидации данных. Процедура сложная и зависит от многих вещей — короче, делаем валиадцию на серваке. Результат валидации — не просто yes/No — а тоже вполне осмысленный набор записей. И вот что характерно — мы хотим валидировать подобным образом текущие редактируемые данные ДО сохранения в базе. И как тут поможет stateless?
IT>State всегда есть, в твоём примере он находится на клиенте. Сервер же остаётся stateless.
Хм. Что-то мне это напоминает
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, IT, Вы писали:
V>>>>Далее, при ошибке сохранения данных формы на сервер, было бы неплохо не потерять введенные пользователем данные. Ошибка могла возникнуть не только по причине отсутствия связи. Все гораздо интересней, если связь есть, но сервак выплевывает ошибку. Наверно, Пашин совет касается подобных вещей. IT>>>Согласись, что сохранение данных формы — это не дело сервера. ГВ>>Это ещё почему?
IT>Смотрим контекст. Я не о методе сервера SaveWhatever, а о хранении введённых данных пока этот вызов не завершиться успешно.
Я и говорю, что это спорный вопрос. Никто не мешает, и ИМХО, это полезно: хранить промежуточные, возможно невалидные данные в специальных таблицах/файлах и т.п., которые никак не упоминаются в "основных" таблицах до выполнения валидации. Короче говоря — хранить сеанс пользователя.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Cyberax, Вы писали:
C>Является ли выброс исключения для индикации ошибки — управлением потоком?
Смотря что считать ошибкой и, что считать индикацией. Одна и та же функция может одновременно использоваться по разному. Возьмем, к примеру, int.Parse. Если я использую его там где в строке просто обязана лежать цифра, то несомненно генерация исключения будет разумной реакцией на то, что в строке лижет не число. Например, я читаю данные из ХМЛ и первожу их в целые. По структуре ХМЛ в поле обязано лежать число в строковой форме. При этом наличие там не числа является исключительной ситуацией.
Другой вариант... Я читаю строку из поля ввода. При этом совершенно нормальной ситуацией бывает ошибочный ввод пользователя. При этом логикой программы должна быть предусмотрена процедура проверки кооректности ввода и выдачи пользователю сообщения об ошибке. К сожалению, во фрэймворке 1.х небыло функции позволяющей нормално решать данную проблему. int.Parse() всегда генерировал исключение. Код при этом становился громоздким и неудобным.
Во втором фрэймворке появился int.TryParse и все встало на свои места.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Видимо, у нас, все-таки, жизнь разная...
ПК>>>...нулевого указателя, и далее в дело вступит ПК>>> мой обработчик данного исключения, и создаст снимок стека или дамп, ПК>>> чтоб я мог определить, в чем же, собственно, ошибка.
V>> А ты для какого рынка программы то пишешь?
ПК>Сейчас -- работаю в команде, пишущей движки для клиент-серверных бизнес- ПК>приложений (свой orb, свой application server, аналог win fs, свой движок ПК>форм и т.п.)
Ясно. Тогда твое мировозрение как становится уже даже не смешным, а совсем грусным. Это именно тот список задач которые не стоит "класть", чтобы ты узнал в чем совбственно ошибка.
ПК>Надежнее, чем что?
Чем аналогичные С++-ные написанные в таких вот традициях "чтобы я мог узнать...".
ПК> Если, чем C++ (делая догадки по остальным сообщениям ПК>в данной теме), то это просто неверно: они по определению не могут работать ПК>надежнее, чем их виртуальные машины, которые как раз на C++ и написаны.
На С++ во фрэймворке написано не так много кода. В основном ядро и еще процедуры оптимизации. Все они протестированы по 100 раз и продолжают тестироваться миллионами пользователей по всему миру. Прикладные же приложения этим похвастаться подобым не могут. Так что не нужно их даже сравнивать.
ПК>Вряд ли. Я периодически скачиваю новую версию, немного играюсь, в очередной ПК>раз разочаровываюсь, и деинсталлирую. Т.е. прогресс, безусловно, наблюдается, ПК>но как-то пока не торкает... А жаль. В него можно было бы вставить репликацию, ПК>которой мне в News-клиентах не хватает.
И за все время даже ни одного тестового сообщения не отпавил? И чем же ты разочаровывашся?
ПК>Пробовал, пробовал. Если бы у него был лог-файл, я бы привел соответствующий ПК>фрагмент.
Ты будешь удевлен, но лог у него есть.
ПК>В общем, пока не пробраузил кнопочкой "Обзор", ему не полегчало...
Внимание, вопрос! И чем же провенились исключения? Возможно ошибка там в логике какая и есть. Но что было бы толку если бы Янус вылетил без объяснения причин?
ПК>Все зависит от того, что произошло. Если нарушение предусловия (ошибка ПК>программиста),
А чьи еще ошибки бывают в программах? Аппоратные? И как ты на них собирашся реагировать.
ПК> то я предпочту аварийное завершение,
Ясно. В общем, там дальше кирпич весит за которым овраг, но вам можно. (с)
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, vdimas, Вы писали:
V>Разве это стателесс?
Если вы не сохраняете эти данные сразу в базе, а храните в памяти сервера, то нет, конечно. А в чём проблема была хранить набиваемые данные на клиенте?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Геннадий Васильев, Вы писали:
IT>>Смотрим контекст. Я не о методе сервера SaveWhatever, а о хранении введённых данных пока этот вызов не завершиться успешно.
ГВ>Я и говорю, что это спорный вопрос. Никто не мешает, и ИМХО, это полезно: хранить промежуточные, возможно невалидные данные в специальных таблицах/файлах и т.п., которые никак не упоминаются в "основных" таблицах до выполнения валидации. Короче говоря — хранить сеанс пользователя.
Да ради бога, особенно если есть такие non functional requirements. Но у тебя тогда будет более чем один метод Save, не так ли?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Но у тебя тогда будет более чем один метод Save, не так ли?
Да, естественно. А при чём здесь количество методов Save?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, IT, Вы писали:
IT>Но у тебя тогда будет более чем один метод Save, не так ли?
Хотя... может так статься, что всё удастся упаковать и в один метод Save. Например, это может быть метод сохранения данных сеанса вкупе с идентификатором сеанса. Тогда при отлёте валидации (а равно и исключении) данные сеанса перейдут в какое-нибудь "draft" и будут сохранены отдельно.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
VladD2 wrote:
>>> C>А RSDN.NNTP не ты писал, случайно? >>> Не я. А что? > C>Так и быть, лопату дарить не буду. > И правильно сделаешь. Потому как кто к нам счем прийдет от того и помрет. > А что, кстати, с ним не так?
Куча мелких багов/недоработок, да вдобавок еще и тормозит . Сообщения в
Thunderbird'е открываются намного дольше, чем через web-интерфейс.
VladD2 wrote:
> C>Является ли выброс исключения для индикации ошибки — управлением > потоком? > Смотря что считать ошибкой и, что считать индикацией. Одна и та же > функция может одновременно использоваться по разному. Возьмем, к > примеру, int.Parse. Если я использую его там где в строке просто > обязана лежать цифра, то несомненно генерация исключения будет > разумной реакцией на то, что в строке лижет не число. Например, я > читаю данные из ХМЛ и первожу их в целые. По структуре ХМЛ в поле > *обязано *лежать число в строковой форме. При этом наличие там не > числа является исключительной ситуацией.
Логично.
> Другой вариант... Я читаю строку из поля ввода. При этом совершенно > нормальной ситуацией бывает ошибочный ввод пользователя. При этом > логикой программы должна быть предусмотрена процедура проверки > кооректности ввода и выдачи пользователю сообщения об ошибке. К > сожалению, во фрэймворке 1.х небыло функции позволяющей нормално > решать данную проблему. int.Parse() всегда генерировал исключение. Код > при этом становился громоздким и неудобным. > Во втором фрэймворке появился int.TryParse и все встало на свои места.
Возьмем более сложный случай: есть два метода загрузки документа —
быстрый и медленный. Сначала делается попытка использовать быстрый
метод. Но в определенный момент загрузки может случится ситуация такая,
что быстрый метод оказывается неприемлим, и нужно переключиться на
медленный режим и перепарсить весь документ. Ситуация _не_ ошибочная, и
встречается достаточно часто.
У меня в этом случае кидается format_mismatch_exception. То есть
используется управление потоком с помощью исключений.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Хотя... может так статься, что всё удастся упаковать и в один метод Save. Например, это может быть метод сохранения данных сеанса вкупе с идентификатором сеанса. Тогда при отлёте валидации (а равно и исключении) данные сеанса перейдут в какое-нибудь "draft" и будут сохранены отдельно.
DoEverything, на входе xml, на выходе xml
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
VladD2,
> ПК>Сейчас -- работаю в команде, пишущей движки для клиент-серверных бизнес-приложений (свой orb, свой application server, аналог win fs, свой движок форм и т.п.)
> Ясно. Тогда твое мировозрение как становится уже даже не смешным, а совсем грусным. Это именно тот список задач которые не стоит "класть", чтобы ты узнал в чем совбственно ошибка.
В этих задачах следует ошибки отлавливать "до того", а если в ходе работы произошла ситуация, грозящая нарушением инвариантов, лучше перезапуститься. Но первый вариант намного лучше.
> ПК> Надежнее, чем что?
> Чем аналогичные С++-ные написанные в таких вот традициях "чтобы я мог узнать...".
В этих традициях написана VM того же .Net: посмотри, как там с помощью _ASSERTE проверяются предусловия и постусловия. В случае их нарушения никаких исключений не будет. Все просто "тихо" упадет. При этом проверки в критических местах уже присутствуют. Вопрос: почему их в release версии не превращают в исключения? Ответ на этот вопрос поможет понять позицию, которую я в этой теме озвучивал.
> На С++ во фрэймворке написано не так много кода. В основном ядро и еще процедуры оптимизации. Все они протестированы по 100 раз и продолжают тестироваться миллионами пользователей по всему миру. Прикладные же приложения этим похвастаться подобым не могут. Так что не нужно их даже сравнивать.
Тестирование миллионами пользователей здесь вообще ни при чем, т.к. ошибки из разряда "вылетов" виртуальной машины пользователи рапортовать просто не должны. Их нужно отлавливать "до того", на этапе разработки и внутреннего тестирования. То же самое верно и для прикладных приложений.
> ПК> Вряд ли. Я периодически скачиваю новую версию, немного играюсь, в очередной раз разочаровываюсь, и деинсталлирую.
> И за все время даже ни одного тестового сообщения не отпавил?
Мне в Опере редактировать удобнее. Да, в общем, и все остальное тоже... Спасибо за идею с USB-диском. Я попробую ее к Опере применить. Только нужно диск достаточно большого объема, но небольшого размера, найти: у меня почтовая база в Опере уже больше 4 гигабайт занимает...
> ПК> Пробовал, пробовал. Если бы у него был лог-файл, я бы привел соответствующий фрагмент.
> Ты будешь удевлен, но лог у него есть.
Я не нашел. Но, боюсь, если лог в базе теперь уже поздно, т.к. ее я в ходе попыток воспроизвести ошибку удалил
> ПК>Все зависит от того, что произошло. Если нарушение предусловия (ошибка программиста),
> А чьи еще ошибки бывают в программах? Аппоратные? И как ты на них собирашся реагировать.
Бывают исключительные ситуации, которые программа и должна обрабатывать: нехватка ресурсов для текущей операции, какие-то нарушения в окружении, неверные данные и т.п. К ошибкам программирования это все не относится.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Cyberax, Вы писали:
C>Возьмем более сложный случай: есть два метода загрузки документа — C>быстрый и медленный. Сначала делается попытка использовать быстрый C>метод. Но в определенный момент загрузки может случится ситуация такая, C>что быстрый метод оказывается неприемлим, и нужно переключиться на C>медленный режим и перепарсить весь документ. Ситуация _не_ ошибочная, и C>встречается достаточно часто.
C>У меня в этом случае кидается format_mismatch_exception. То есть C>используется управление потоком с помощью исключений.
C>Это плохо?
Несомненн, плохо. Это именно использование исключений в целях управления логикой приложения.
Хотя странна и сама ситуация. Чем отличаются эти методы?
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Cyberax, Вы писали:
C>Куча мелких багов/недоработок,
Ну, ты бы на сапорт написал. А то вот ПК пользуется и доволен.
C>да вдобавок еще и тормозит . Сообщения в C>Thunderbird'е открываются намного дольше, чем через web-интерфейс.
Тебе не кажется, что тормоза связаны с общей загрузкой сайта? В часы пик и через веб-интерфейс пробиться не просто. Кстати, Янус в этом аспекте показывает себя просто замечательно. Обработка ведь пакетная.
C>Ну и падает часто.
Где? Я вроде не замечал. Хотя я им и не пользуюсь.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
V>> Но V>> есть и исключения вроде переполнения стэка которые точно не предсказать V>> и гарантированно не обработать.
ПК>Это уже совсем другая песня. Можно также сказать, что могут выключить питание ПК>компьютера. Речь шла об исключениях, доступных в языке.
Нет. Переполнение стека, особенно на дотнете — это ошибка программиста. А питание аппаратная проблема. К тому же легко решаемая (UPS спасет отца русской демократии...).
ПК>Это логическая ошибка. Другими словами, ошибка программиста. Для понимания "моей" ПК>терминологии см., например, этот учебный курс:
Мне не нужно ссылок на английские сайты да еще и с явной бредятиной. Ты просто ответь. Что в твоем понимании ошибка не логическая и приведи несколько примеров.
ПК>Ключевые моменты: разница между ошибками и исключительными ситуациями, корректность ПК>программы, выражающаяся в соблюдении контрактов, понятие предусловий. Более подробно ПК>читай о так называемом программировании по контракту.
Извини, но это является бредом для ООЯ. Нарушение того самого контракта по-твоему является шататной ситуацией? И как на нее реагировать? Выпадать в осадок?
Я предпочитаю пользоваться общепринятыми определениями http://en.wikipedia.org/wiki/Exception. Хотя честно говоря оно и не самого лучшего качества.
ПК>Потому что код, вызвавший данную функцию, нарушил контракт, не выполнив предусловие. ПК>Соответственно, он некорректен.
И что? Почему при этом состояние всей программы обязательно стало некорректным?
ПК>После ошибки программиста.
Да все исключения кроме аппоратных яывляются ошибками программистов. Тогда уж лучше использовать термины аппаратные исключения и исключения программные.
В любом случае более чем странно слышать о том, что нельзя постановиться после программного исключения. Примеров тебе было приведено море. Более того ты постоянно пользушся софтом который восстанавливаетя опсле не критичных исключений и что-то ни одного слова о пробемах от тебя не слышно.
ПК>Да не обвиняю я управляемые среды... Среда тут ни при чем. Просто в них чаще можно ПК>встретить использование исключений для "обработки" ошибочных ситуаций.
Исключительных. Ситуаций не предусмотренных логикой программы. И делается это по совсем банальным соображениям — управляемая среда за счет типобезопасности обеспечивает устойчивость приложения не давая ошибке программиста превратиться в крах приложения из-за порчи памяти.
ПК>Продолжение работы приложения в случае диагностирования ошибочного состояния ПК>для сохранности данных не только не помогает, но и мешает.
Примеры я тебе уже приводил. Убеждать тебя не хочу. Пусть этим займутся твои клиенты. Я же просто постерегусь таковым становиться... если что.
ПК> В любом случае, для ПК>обеспечения сохранности данных нужны более надежные средства. Например, на случай ПК>отключения питания, аппаратного сбоя и т.п. Периодическое сохранение состояния, ПК>журналирование и т.п.
Ага, точно! Журналирование. Будем журналировать ввод пользователя в Янусе чтобы потом его специально грохнуть. Самому то не смешно?
ПК>В этом случае никаких специальных действий от пользователя не нужно. Напротив, ПК>нужно как можно быстрее завершить исполнение программы, вышедшей из режима, ПК>предусмотренного разработчиками, чтобы пользователь не работал в этом состоянии, ПК>потенциально не имея возможности сохранить данные, которые он произведет после ПК>сбоя.
Не будет никто делать никакое журналирование в прикладных системах. Максимум транзакциями воспользуются. Только толку с того. Ну, и как тебе уже не раз говорили системы бывают многопользовательскими и класть сервер из-за ошибки в сесси — это идиотизм.
ПК>В случае возможности изоляции потоков исполнения и их данных вполне можно завершать ПК>исполнение только того потока исполнения, в котором диагностировано нарушение логики.
Ну, вот в ГУИ приложении, если оно разумно написано, любая команда пользователя и живет изолировано. Так зачем срубать все приложение из-за мелкой ошибки в одной команде? Хотя вопрос уже риторический.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Ну, мы с тобой точно не знаем, почему именно документ получается "плохим".
Да не плохим, а битым. И знать там не чего. По памяти уроды проходятся плюс имеют сложную структуру хранения вводимых данных.
ПК> Я бы, скорее предположил, что дело в порче инвариантов.
Конечно всегда есть шанс, что AV было вызванно потусторонними силами, а документ испортился не из-за этого, а из-за совпадения каких-то факторов. Но это каую же фантазию нужно иметь? И как нужно стараться себе это внушить?
ПК> В случае же продолжения работы было бы не лучше: пользователь успел бы ПК>много наредактировать, прежде чем стало бы ясно, что документ уже ни к черту.
Было бы лучше переисать ворд на типобезопасном ООЯ. Тода применяя довольно простые паттерны можно было гарантировать целостность модели и мелкие ошибки вроде выхода за границу массива уже не приводили бы к наложенным AV с хаотичной порчей памяти. А Ворд до сих пор даже не на С++ пишется. Насколько мне извесно он на голимом С написан.
ПК> В случае "вылета", если ПК>Auto Recovery приводит к проблемам (чего я на своих документах не замечал, но и Word у меня сам по себе не вылетает, за исключением отключения питания машины без shut down), то можно воспользоваться последней autosaved версией.
Это потому, что ты и 5% его возможностей не используешь. По пользуйся ревижонами и коментариями, да так чтобы их было от большого количества человек, да чтобы одно на другом сидело. Тогда ты еще и не то увидишь.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, alexeiz, Вы писали:
A>Не смертельно в смысле того, что runtime сможет продолжить выполнение программы. Однако с точки зрения надёжности выброс из Dispose имеет тот-же эффект, что и выброс из деструктора в C++. Гарантировать сохранение инвариантов, освобождение ресурсов и нормальную обработку исключений в таком случае невозможно.
Исключение в Dispose — это конечно не хорошо. Но все же последсвтия будут далеко не плюсовыми.
A>PS: Напомню, что в C++/CLI деструкторы прямо эквивалентны Dispose(),
И что?
A> и Visual C++ по крайней мере в Beta2 генерирует такой код для деструкторов, который в критических случаях просто проглатывает любые исключения, из него выбрасываемые.
Да? Вот глянул, что он там генерирует. Для следующего класса:
using namespace System;
ref class C
{
public:
~C() { Clinup(); }
!C() { Clinup(); }
// Не надо реализовыывать Dispose Pattern, т.к. ее поддерживает язык.
// :)) не вопрос заведем просто виртуальный метод.protected:
virtual void Clinup()
{
Console::WriteLine("C.Clinup()");
}
};
Преобразование в C# (с) Рефлектор. Для читаемости отформатировано мной.
И где ты здесь узрел какое-то проглатывание исключений? Это качественная реализация паттерна Dispose. Не более того!
Да, я согласен, что было очень верно реализовать этот паттерн пямо в языке, а не заставлять каждый раз реализовывать его программиста. Это несомненно страхует программистов от лишних ошибок.
Мне вот только не понятно почему некоторые программисты так сильно борются с внедрением паттернов в языки типа C#, но когда эти самые паттерны внедряются в их любимые языки, то они вместо того, чтобы гревать восхищаются этим.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Cyberax, Вы писали:
>> C>А откуда он будет это знать? >> Программист скажет. Ну, вылетит программка при тестировани... >> программист подсмотрит тип исключения и обработает его. Или из >> документации прочитает. В общем, не важно откуда.
C>В общем вы повторяете аргументы сторонников динамических языков.
Возможно, если пытаться проводить аналогии. Но есть огромная разница между обработкой исключительных ситуаций и системой типа языка.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2 wrote:
> C>Куча мелких багов/недоработок, > Ну, ты бы на сапорт написал. А то вот ПК пользуется и доволен.
Я тоже по большему счету доволен. А багрепорты всегда писать лень
(обещаю исправиться).
> C>да вдобавок еще и тормозит . Сообщения в > C>Thunderbird'е открываются намного дольше, чем через web-интерфейс. > Тебе не кажется, что тормоза связаны с общей загрузкой сайта? В часы > пик и через веб-интерфейс пробиться не просто.
Нет, тормозит даже когда веб-интерфейс работает быстро.
> Кстати, Янус в этом аспекте показывает себя просто замечательно. > Обработка ведь пакетная.
В Thunderbird — тоже при желании.
> C>Ну и падает часто. > Где? Я вроде не замечал. Хотя я им и не пользуюсь.
С завидной переодичностью получаю OutOfMemory — несколько раз в неделю,
иногда не дает с постить с ошибкой о недостаточности прав и т.п.
VladD2 wrote:
> C>Возьмем более сложный случай: есть два метода загрузки документа — > C>быстрый и медленный. Сначала делается попытка использовать быстрый > C>метод. Но в определенный момент загрузки может случится ситуация такая, > C>что быстрый метод оказывается неприемлим, и нужно переключиться на > C>медленный режим и перепарсить весь документ. Ситуация _не_ ошибочная, и > C>встречается достаточно часто. > C>У меня в этом случае кидается format_mismatch_exception. То есть > C>используется управление потоком с помощью исключений. > C>Это плохо? > Несомненн, плохо. Это именно использование исключений в целях > управления логикой приложения.
А какой вариант лучше? Сразу определить нужный метод загрузки — нельзя.
Предварительно обходить граф, чтобы проверить возможность быстрой
загрузки — неэффективно. Без исключений пришлось бы возвращать код
ошибки через несколько уровней вложенности.
> Хотя странна и сама ситуация. Чем отличаются эти методы?
Быстрый метод использует закэшированные локально преобразования (в виде
слинкованого бинарного изображения из собственного JIT-компилятора).
Если встречается неизвестное преобразование — включается второй режим,
когда преобразования врубаются в интерпретируемый режим с параллельной
перекомпиляцией.
И не надо предлагать мне использовать .NET и не мучаться с ручным JIT-ом...
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>В этих традициях написана VM того же .Net: посмотри, как там с помощью _ASSERTE проверяются предусловия и постусловия. В случае их нарушения никаких исключений не будет. Все просто "тихо" упадет.
_ASSERTE работает только в дебаге. И даже в нем от него ничего не упадет. А вот AV после словить можно. Думаю, выбор _ASSERTE здесь был обусловлен тем, что это все же код ядра и напрямую его никто вызывать не будет (а значит его можно как следует отестировать), ну, и требованиями производительности. Все же все эти проверки потребляют время, а в ядре рантайма это может оказаться критическим. Однако, если ты посмотришь мнее предвзято, то заметиль, что код ядра генерирует не мало исключений.
ПК> При этом проверки в критических местах уже присутствуют. Вопрос: почему их в release версии не превращают в исключения? Ответ на этот вопрос поможет понять позицию, которую я в этой теме озвучивал.
Не поможет. Так как в релизе они с вероятностью в 99% превратятся в AV со всеми вытекающими.
ПК>Тестирование миллионами пользователей здесь вообще ни при чем, т.к. ошибки из разряда "вылетов" виртуальной машины пользователи рапортовать просто не должны.
Должен, не должен. А оно есть. Как факт. И ничего ты с этим не поделашь.
ПК> Их нужно отлавливать "до того", на этапе разработки и внутреннего тестирования. То же самое верно и для прикладных приложений.
Желательно отлавливать до. Но никто от ошибок не застрахован. И ассерт может быть не верным, в конц концов.
ПК>Бывают исключительные ситуации, которые программа и должна обрабатывать: нехватка ресурсов
А она отчего появилась?
ПК> какие-то нарушения в окружении,
А они откуда?
ПК> неверные данные
Как не верные? Значит опять ошибка програмиста?
ПК>и т.п. К ошибкам программирования это все не относится.
Очень странное мнние.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2 wrote:
>>> Программист скажет. Ну, вылетит программка при тестировани... >>> программист подсмотрит тип исключения и обработает его. Или из >>> документации прочитает. В общем, не важно откуда. > C>В общем вы повторяете аргументы сторонников динамических языков. > Возможно, если пытаться проводить аналогии. Но есть огромная разница > между обработкой исключительных ситуаций и системой типа языка.
Мне вот так не кажется. Проверяемые исключения рассматриваются как часть
сигнатуры функции — то есть как часть ее типа. Точно так же, как и
статическая типизация, проверяемые исключения позволяют находить ошибки
во время компиляции.
Здравствуйте, Cyberax, Вы писали:
C>А какой вариант лучше? Сразу определить нужный метод загрузки — нельзя. C>Предварительно обходить граф, чтобы проверить возможность быстрой C>загрузки — неэффективно. Без исключений пришлось бы возвращать код C>ошибки через несколько уровней вложенности.
C>Быстрый метод использует закэшированные локально преобразования (в виде C>слинкованого бинарного изображения из собственного JIT-компилятора). C>Если встречается неизвестное преобразование — включается второй режим, C>когда преобразования врубаются в интерпретируемый режим с параллельной C>перекомпиляцией.
Думаю, в вашей ситуации разумно было бы передавать версию протокола в заголовке. Тогда сравнив ее с закэшированной можно было бы выбрать нужный метод без спарсинга.
C>И не надо предлагать мне использовать .NET и не мучаться с ручным JIT-ом...
Я даже подумать не успел, а ты уже хорошее решение придумал.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>В C++ я предпочитаю такой вариант: ПК>
ПК>boost::optional< Person >
ПК>findPersonByName( std::string name );
ПК>
ПК>или, для C#: ПК>
ПК>Person?
ПК>findPersonByName( string name );
ПК>
ПК>или (вот здесь я уже не вполне уверен, можно ли на Java так сделать -- ПК>если нет, тем хуже для Java ): ПК>
ПК>Optional<Person>
ПК>findPersonByName( String name );
ПК>
ПК>И не нужно никаких лишних комментариев и, тем более, исключений.
Кстати, в C# с большой вероятностью, а в Яве так на 100% Person будет ссылочным типом, так что даже Person? и т.п. не потребуется. Можно просто возвращать null если человека нет.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, dshe, Вы писали:
D>Приведу еще один небольшой аргумент в пользу исключений. Исключения могут с собой нести некоторую дополнительную информацию. Например, PersonNotFoundException мог бы содержать имена существующих людей, которые наиболее близки к искомому. Эту информацию можно было бы использовать для того, чтобы помочь пользователю в его поиске. Признаюсь, я очень редко пользовался этой возможностью и еще реже видел, чтобы класс исключения (написанный не мной) содержал в себе что-то более чем error message.
Ужасный аргумент. В таких случаях лучше сделать отдельную функцию которая возвратит результат запакованным в объект хранящий эту дополнительную информацию. А использовать в таких случаях исключения как раз самое неверное решение.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2,
> ПК>В C++ я предпочитаю такой вариант: > ПК>
> ПК>boost::optional< Person >
> ПК>findPersonByName( std::string name );
> ПК>
> ПК>или, для C#: > ПК>
> ПК>Person?
> ПК>findPersonByName( string name );
> ПК>
> ПК>или (вот здесь я уже не вполне уверен, можно ли на Java так сделать -- > ПК>если нет, тем хуже для Java ): > ПК>
> ПК>Optional<Person>
> ПК>findPersonByName( String name );
> ПК>
> ПК>И не нужно никаких лишних комментариев и, тем более, исключений.
> Кстати, в C# с большой вероятностью, а в Яве так на 100% Person будет ссылочным типом, так что даже Person? и т.п. не потребуется. Можно просто возвращать null если человека нет.
Прошу прощения. Не посмотрел на семантику nullable types в C#. Этот вариант вычеркиваем. Мало того, что это не работает с reference types, так еще и приведение неявное. Речь как раз шла о том, чтобы во время компиляции ловить случаи, в которых программист не проверил результат на существование. Результат (Person)null этого не требует, т.к. по типу не отличается от валидных значений Person.
boost::optional<Person>, с другой стороны: 1) исключает попытки работы с результатом findPersonByName без предварительной проверки, что вернули непустое значение; 2) явно говорит о том, что может быть возвращено пустое значение, перенося комментарии в код.
пожалуйста, дочитай до конца до того, как будешь отвечать.
Иначе сложно вести обсуждение, когда вопросы задаются, на которые
ответ можно было получить из следующих двух строк, сэкономив и мне,
и себе время.
ПК>> В этих традициях написана VM того же .Net: посмотри, как там с помощью ПК>> _ASSERTE проверяются предусловия и постусловия. В случае их нарушения ПК>> никаких исключений не будет. Все просто "тихо" упадет.
V> _ASSERTE работает только в дебаге. И даже в нем от него ничего не V> упадет. А вот AV после словить можно.
Это (например, AV) и было обозначено словами "тихо упадет".
V> Думаю, выбор _ASSERTE здесь был обусловлен тем, что это все же код ядра V> и напрямую его никто вызывать не будет (а значит его можно как следует V> отестировать)
Протестировать "как следует" невозможно ничего, если под этим понимать
некоторое абстрактное "полное" тестирование, гарантирующее отсутствие
ошибок. Снова-таки, каким бы хорошим бы ни было тестирование, какое-то
количество ошибок останется. В любом случае, хорошее тестирование
не является аргументом для отмены других подходов увеличения надежности
программы, особенно, когда все необходимое для этого в коде уже присутствует.
Иными словами, если бы выбрасывание исключений в случае обнаружения ошибок
программирования само по себе -- заметь, я уже начинаю подсказывать --
помогало повышать надежность системы, то, безусловно, _ASSERTE имело бы
прямой смысл превратить в release версии VM в исключения. Этого не сделали.
Соответственно, эта часть ответа на вопрос "почему их в release
версии не превращают в исключения?" неверна. Вовсе не потому, что VM
можно хорошо протестировать, и это делается. Причина -- в другом.
(Я знаю, я так и не дал ответа на уже несколько раз прозвучавшие вопросы
о том, в чем же так часто поминаемая мной разница между исключительными
ситуациями, и ошибками программирования. Я не уверен, что смогу дать
совершенно точное формальное определение того и другого, но думаю, что
ближе к концу сообщения то, что я имею в виду, должно стать более ясным.
Давай пока для простоты примем, что _ASSERTE проверяют именно корректность
программы, т.е. обозначают именно то, что я имею в виду, говоря о диагностике
ошибок программирования. "Правильно" это или "не правильно" -- вопрос
совсем отдельный. Мы ж пока пытаемся понять друг друга, не так ли? О, это
идея, я в остальной части сообщения с тобой в "горячо" -- "холодно" играть
буду. Там, где твои реплики сильно отличаются от того, о чем я говорил, буду
писать "холодно", там где подбираются поближе -- "тепло".)
V> ну, и требованиями производительности. Все же все эти проверки потребляют V> время, а в ядре рантайма это может оказаться критическим.
Если не строить догадки, а посмотреть на код, то можно будет увидеть, что
в подавляющем количестве мест, где используются _ASSERTE, стоимостью
выполняемых проверок можно абсолютно смело пренебречь. По крайней мере,
по сравнению с окружающим кодом. Например:
void AppDomain::ExceptionUnwind(Frame *pFrame)
{
LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind for %8.8x\n", pFrame));
#if _DEBUG_ADUNLOAD
printf("%x AppDomain::ExceptionUnwind for %8.8p\n", GetThread()->GetThreadId(), pFrame);
#endif
Thread *pThread = GetThread();
_ASSERTE(pThread);// if the frame was pushed in managed code, then the cleanup in the managed code finally will
// already have popped returned from the context, so don't need to do anything. However, if we
// are still the current frame on an ExceptionUnwind, then we need to clean ourselves off. And if
// the frame was pushed outside of EnterContext as part of a failed attempt to enter the context
// then the return context will be null, so don't need to do anything with this frame.
Context *pReturnContext = pFrame->GetReturnContext();
if (pReturnContext && pThread->GetContext() != pReturnContext)
{
pThread->ReturnToContext(pFrame, FALSE);
}
if (! pThread->ShouldChangeAbortToUnload(pFrame))
{
LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: not first transition or abort\n"));
return;
}
LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: changing to unload\n"));
BEGIN_ENSURE_COOPERATIVE_GC();
OBJECTREF throwable = NULL;
CreateExceptionObjectWithResource(kAppDomainUnloadedException, L"Remoting_AppDomainUnloaded_ThreadUnwound", &throwable);
// reset the exception to an AppDomainUnloadedExceptionif (throwable != NULL)
GetThread()->SetThrowable(throwable);
END_ENSURE_COOPERATIVE_GC();
}
Я не буду приводить множество других фрагментов кода VM, содержащих _ASSERTE.
Этот вполне характерен. Даже если допустить, что часть остальных фрагментов
содержит гипотетические "дорогие" проверки, вполне можно было бы ввести
две разновидности _ASSERTE, одну для "дорогих" проверок, другую -- для
"дешевых", и превращать в выбрасывание исключений только последние. Этого
тоже не сделали.
В общем, полагаю, должно быть понятно, что и вторая из названных тобой причин,
не объясняет, почему же проверки, диагностирующие ошибки программирования,
в release версии VM исключения не бросают, а просто убираются, оставляя
программу на произвол AV, как ты верно заметил.
Холодно.
V> Однако, если ты посмотришь мнее предвзято, то заметиль, что код ядра V> генерирует не мало исключений.
О! Теплее... Теперь осталось понять, где же проходит эта самая граница,
разделяющая assert и выбрасывание исключения. Я приведу еще один фрагмент
кода, где эту разницу можно уловить чуть получше:
void BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF **apObjRefs)
{
THROWSCOMPLUSEXCEPTION();
CHECKGC();
_ASSERTE((nRequested > 0) && apObjRefs);
Thread *pThread = SetupThread();
if (NULL == pThread)
{
COMPlusThrowOM();
}// Enter preemptive state, take the lock and go back to cooperative mode.
pThread->EnablePreemptiveGC();
m_pLargeHeapHandleTableCrst->Enter();
pThread->DisablePreemptiveGC();
EE_TRY_FOR_FINALLY
{
// Make sure the large heap handle table is initialized.if (!m_pLargeHeapHandleTable)
InitLargeHeapHandleTable();
// Allocate the handles.
m_pLargeHeapHandleTable->AllocateHandles(nRequested, apObjRefs);
}
EE_FINALLY
{
// Release the lock now that the operation is finished.
m_pLargeHeapHandleTableCrst->Leave();
} EE_END_FINALLY;
}
ПК>> При этом проверки в критических местах уже присутствуют. ПК>> Вопрос: почему их в release версии не превращают в исключения? ПК>> Ответ на этот вопрос поможет понять позицию, которую я в этой теме ПК>> озвучивал.
V> Не поможет. Так как в релизе они с вероятностью в 99% превратятся в AV V> со всеми вытекающими.
Что ты подразумевал под словами "они"? "Исключения" или "случаи, проверяемые
с помощью _ASSERTE, если последние будут отключены, как это и происходит по
умолчанию"? Если второе -- очевидно, что во многих из мест, где расставлены
_ASSERTE, будут сгенерированы AV и т.п. Вопрос как раз в этом и заключается:
почему Microsoft не пошли по, вроде бы, перспективному по твоим словам пути,
и не превратили эти _ASSERTE в релизной версии в исключения?
Прохладно...
ПК>> Тестирование миллионами пользователей здесь вообще ни при чем, т.к. ПК>> ошибки из разряда "вылетов" виртуальной машины пользователи ПК>> рапортовать просто не должны.
V> Должен, не должен. А оно есть. Как факт. И ничего ты с этим не поделашь.
Так ты так и не ответил, почему же разработчики VM предпочли "вылеты"
порождению исключений? Ведь множество проверок по коду уже расставлено.
Казалось бы -- сделай в релизе
#define _ASSERTE(x) do { if (!x) throw ...; } while (0)
и VM станет надежнее. Так почему, все-таки, этого не сделали?
ПК>> Их нужно отлавливать "до того", на этапе разработки и внутреннего ПК>> тестирования. То же самое верно и для прикладных приложений.
V> Желательно отлавливать до. Но никто от ошибок не застрахован. V> И ассерт может быть не верным, в конц концов.
Ага. Теплее. Одна причина, наконец, названа: диагностика ошибок может
содержать ошибки, и, соответственно, может снижать надежность
программы, внося в нее дополнительную сложность. Но, учитывая
тривиальность подавляющего количества _ASSERT, эта причина сама по себе
не могла перевесить декларируемые положительные стороны для повышения
надежности программы путем сообщения об ошибках программирования
с помощью исключений. Какие же еще причины, которые, действительно,
объясняют, почему же _ASSERTE просто-напросто выбрасывают из release
версии VM?
ПК>> Бывают исключительные ситуации, которые программа и должна ПК>> обрабатывать: нехватка ресурсов
V> А она отчего появилась?
Из-за самых разных факторов: какая-то из программ заняла память, кончилось
место на диске и т.п. Программа обычно с этим сделать что-либо разумное
в месте обнаружения ошибки не в силах, кроме как прервать текущую операцию,
и попробовать восстановить исполнение где-то на более высоком уровне, для
чего, собственно, исключения подходят очень неплохо. Суть в том, что эти
аспекты работы программы не объясняются ошибками в ней, а являются частью
ее предусмотренного поведения (подсказка).
ПК>> какие-то нарушения в окружении,
V> А они откуда?
Причин тоже может быть много: например, нет прав доступа к некоторому файлу,
или сетевое соединение разорвано. Это еще один характерный пример ситуаций,
которые надежная программа должна обрабатывать (подсказка: даже если
представить, что в ней совсем нет ошибок). При этом, будет это сделано
с помощью исключений или же какими-то другими способами -- влияет только
на удобство и прочие аспекты работы программиста. Это просто механизмы
выполнения того, что программа должна делать (очень горячо, уже
не просто подсказываю, а прямо таки пальцем показываю).
ПК>> неверные данные
V> Как не верные? Значит опять ошибка програмиста?
Нет, опять холодно. Это, например, неправильный ввод пользователя (ввел
"100" там, где больше "10" вводить нельзя). Или же сбой дисковой подсистемы,
скажем, файл с данными запорчен из-за "плохого сектора". Или просто
злобный пользователь взял, да и удалил файл, в котором хранились данные
приложения. В общем, снова-таки, возникновение этих ситуаций объясняется
не ошибками в программе, а какими-то совершенно внешними факторами.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
VladD2 wrote:
> C>Быстрый метод использует закэшированные локально преобразования (в виде > C>слинкованого бинарного изображения из собственного JIT-компилятора). > C>Если встречается неизвестное преобразование — включается второй режим, > C>когда преобразования врубаются в интерпретируемый режим с параллельной > C>перекомпиляцией. > Думаю, в вашей ситуации разумно было бы передавать версию протокола в > заголовке. Тогда сравнив ее с закэшированной можно было бы выбрать > нужный метод без спарсинга.
Версия протоколоа не подойдет (по причине полного ее отсутствия), помог
бы список используемых фильтров с SHA-timestamp'ами их кода. Однако:
1. Получится большая избыточность.
2. Текущий формат может быть прочитан исключительно последовательно и
добавления/изменения делаются простым append'ом.
> C>И не надо предлагать мне использовать .NET и не мучаться с ручным > JIT-ом... > Я даже подумать не успел, а ты уже хорошее решение придумал.
Я бы с удовольствием это решение использовал, но оно должно работать на
устройствах, где .NET не будет еще очень долго.
Здравствуйте, Cyberax, Вы писали:
C>Версия протоколоа не подойдет (по причине полного ее отсутствия), помог C>бы список используемых фильтров с SHA-timestamp'ами их кода. Однако: C>1. Получится большая избыточность. C>2. Текущий формат может быть прочитан исключительно последовательно и C>добавления/изменения делаются простым append'ом.
Ну, вот и нужно было ввести версию протокола. Это и есть ошибка дизайна. Если эта версия зависит от фильтров, то достаточно создать мап филтров к версиям протоколов.
... << RSDN@Home 1.2.0 alpha rev. 587>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2 wrote:
> C>Версия протоколоа не подойдет (по причине полного ее отсутствия), помог > C>бы список используемых фильтров с SHA-timestamp'ами их кода. Однако: > C>1. Получится большая избыточность. > C>2. Текущий формат может быть прочитан исключительно последовательно и > C>добавления/изменения делаются простым append'ом. > Ну, вот и нужно было ввести версию протокола. Это и есть ошибка > дизайна. Если эта версия зависит от фильтров, то достаточно создать > мап филтров к версиям протоколов.
Я же говорю — не подойдет. Фильтры не одни мы пишем, так что
централизованых версий нет в принципе.
Здравствуйте, Cyberax, Вы писали:
C>Я же говорю — не подойдет. Фильтры не одни мы пишем, так что C>централизованых версий нет в принципе.
Ну, распознаете же вы их на клиенте? Значит и на сервере распознаете. Вот распозновайте и создавайте некую чексумму которую и отправляйте на клиента в заголовке. Если она не совпала, то...
... << RSDN@Home 1.2.0 alpha rev. 587>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2 wrote:
> C>С завидной переодичностью получаю OutOfMemory — несколько раз в неделю, > Видимо когда мы бэкапы делаем.
Может быть...
> C>иногда не дает с постить с ошибкой о недостаточности прав и т.п. > А это скорее всего тему удалили. При этом она переносится в форум > Мусор в который писать никто не может.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, dshe, Вы писали:
D>>Приведу еще один небольшой аргумент в пользу исключений. Исключения могут с собой нести некоторую дополнительную информацию. Например, PersonNotFoundException мог бы содержать имена существующих людей, которые наиболее близки к искомому. Эту информацию можно было бы использовать для того, чтобы помочь пользователю в его поиске. Признаюсь, я очень редко пользовался этой возможностью и еще реже видел, чтобы класс исключения (написанный не мной) содержал в себе что-то более чем error message.
VD>Ужасный аргумент. В таких случаях лучше сделать отдельную функцию которая возвратит результат запакованным в объект хранящий эту дополнительную информацию. А использовать в таких случаях исключения как раз самое неверное решение.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, vdimas, Вы писали:
V>>Разве это стателесс?
IT>Если вы не сохраняете эти данные сразу в базе, а храните в памяти сервера, то нет, конечно. А в чём проблема была хранить набиваемые данные на клиенте?
Да проблема в трафике и в лишних методах прикладных интерфейсов. Нам бы пришлось для каждой сущности в случае нужды предварительной верификации на стороне апп-сервера гнать туда наши stateless-данные. А так мы просто перегоняем данные на EntityView на сторону сервака и вызываем общий метод Validate (что-то типа такого). Адаптер этого EntityView на ГУИ каждый раз в Send() шлет только измененные поля либо изменения списков связанных объектов с момента последней пересылки. Т.е. получился весьма общий механизм на предварительную пересылку и валидацию данных. Во время операции Save() отсылаются последние изменения, иже таковые были, и затем в рамках транзакции на серваке применяеются накопленные в EntityView изменения.
В общем, такая потребность вообще возникла из-за необходимости в некоторых местах явного разделения операций сохранения и валидации. В нашем случае, юзер редактирует некий Job, и периодически проверяет текущие набитые данные на валидность без сохранения (так они хотели).
Здравствуйте, Cyberax, Вы писали:
>>>> Программист скажет. Ну, вылетит программка при тестировани... >>>> программист подсмотрит тип исключения и обработает его. Или из >>>> документации прочитает. В общем, не важно откуда. >> C>В общем вы повторяете аргументы сторонников динамических языков. >> Возможно, если пытаться проводить аналогии. Но есть огромная разница >> между обработкой исключительных ситуаций и системой типа языка.
C>Мне вот так не кажется. Проверяемые исключения рассматриваются как часть C>сигнатуры функции — то есть как часть ее типа. Точно так же, как и C>статическая типизация, проверяемые исключения позволяют находить ошибки C>во время компиляции.
Всё ж это зависит от того, для чего механизмом исключений пользоваться.
Если для обработки ситуаций непредусмотренных разработчиком, то от использования проверяемых исключений один оверхед.
А еще механизм исключений можно использовать для /имитации/ ковариантных возвратов (так, чуть выше в треде предлагали привязывать доп-данные к исключению), для управления потоком выполнения, для имитации переменных с динамической областью видимости (для этого, правда, нужны перезапускаемые исключения).
Проверяемые исключения полезны только в первом случае.
И еще, можно заметить, что при наличии штатных средств исключения были бы не нужны. Например, твой случай это имитация откатов. Ничего плохого в использовании механизма исключений для реализации всех этих /непредусмотренных языком/ возможностей как бы и нет, за исключением (уж, извините за тавтологию) того, что среда может неадекватно реагировать на использование исключений для в "штатных" ситуациях. Плюс, если не готов к подобным "штатным" ситуациям, то легко, перехватив "лишнее" исключение, что-то поломать.
Andrei N.Sobchuck wrote:
> C>Мне вот так не кажется. Проверяемые исключения рассматриваются как > часть > C>сигнатуры функции — то есть как часть ее типа. Точно так же, как и > C>статическая типизация, проверяемые исключения позволяют находить ошибки > C>во время компиляции. > Всё ж это зависит от того, для чего механизмом исключений пользоваться. > Если для обработки ситуаций непредусмотренных разработчиком, то от > использования проверяемых исключений один оверхед.
Как раз именно для ошибочных ситуаций, предусмотренных разработчиком.
Например, метод write в класса Socket вполне логично может кидать
ConnectionTerminatedException (наследник от IOException), и вполне
логично задекларировать это исключение в сигнатуре метода.
Здравствуйте, WolfHound, Вы писали:
WH>Здравствуйте, VladD2, Вы писали:
VD>>Потому как кто к нам счем прийдет от того и помрет. WH>Кто к нам с чем тот от того и затем.
Не-а. "Кто к нам с чем зачем, тот от того и того."
Дык еще главное — усложняется осмысление программы другими так как они расчитывают видеть исключения только в исключительных ситуациях не вписывающихся в логку работы программы. Я, например, читая чужой код зачастую просто пропускаю все обработчики исключений так как они мне мало интересны.
Но в приципе любого из перечисленных пунктов достаточно чтобы не прменять исключения для ветвления основной логики программы.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Играть с тобой в горяче-холдно у меня желания нет. Время тратить на ответ по пунктам тоже. Больно много написано. Так что отвечу по существу.
Твою идею деления ошибок категорически не разделяю. В уонйк уонцнов ассер в типобезопасных средах мало чем отличается от ислючения.
Идею выпода в асадок по любому поводу тоже не разделяю.
Что касется фрэймворка... Остаюсь на своих позициях. Думаю, что натыкивание ассертов было вызвано все же надежной на тщательное (возможно автоматизированное) тестирование, большой альф- и бэта-цикл (в котором ассерты скорее всего вообще не убирались) и ра днругие заморочки. Но основной причинойконечно являлся минталитет программистов. Возможно он был схож с твоим. К этому выоду подталкивает и то, что C#-пная часть фрэймворка (которая намного больше по объему) в основном содержит именно генрацию исключений.
Кстати, примеры твои не совсем корректны. ExceptionUnwind это то самое исключение где генерация исключений является просто таки злом.
Кстати, то что можно действительно заметить анализируя код фрэймворка, так это то, что ассерты применяются приемуществнно в коде который не зависит от данных передаваемых прикладным кодом, а исключения генерируются в коде который принимает те самые прикладные данные или как-то иначе зависит от прикладного программиста.
Другими словами ассрты сидят в глубоком ядре, а весь библиотечный код напичкан исключениями. Возможно это и разумно. Учитывая все те же факторы высокой степени отлаженности ядра, производительности и квалификации программистов писавших ядро можно считать объем внутренних ошибок минимальным. А отсуствие внутренних исключений позволяет лучше выявлять ошибки прикладных программистов. Вот только с твоей теорией это никак не вяжется.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2,
V> Твою идею деления ошибок категорически не разделяю. В уонйк уонцнов V> ассер в типобезопасных средах мало чем отличается от ислючения. Идею V> выпода в асадок по любому поводу тоже не разделяю.
Я не предлагаю "выпадать в осадок по любому поводу", и не предлагал. Я говорю
о том, что в случае диагностирования ошибки программирования, по умолчанию
продолжать исполнение, надеясь "на авось", нельзя. Это подтверждается и различными
работами по программированию надежных систем. Вариант "просто попробовать продолжить"
в подобных случаях не рассматривается в принципе.
Отсутствие "проходов по памяти" и т.п. здесь определяющего значения не имеет. Речь
идет о состоянии программы, о сохранении инвариантов и т.п. Т.е. при программировании
делаются некоторые предположения о том, что может, и что не может произойти.
Предположения могут быть как явными, так и неявными. В любом случае, они есть.
Эти предположения, относящиеся к состоянию программы или какой-то части ее данных
до исполнения некоторой функции называются предусловиями, после -- постусловиями.
Сохраняющиеся на протяжении выполнения всей программы (за исключением точно
оговоренных мест) -- инвариантами.
Если инварианты, предусловия или постусловия нарушены, то говорить о каком-то
продолжении исполнения просто неразумно, т.к. на этот вариант развития событий по определению ничего не предусмотрено.
В случае же принятия решения о построении системы, в требования к которой входит
восстановление в случае отказов, вызванных ошибками программирования или серьезными
аппаратными ошибками, приводящими к нарушению целостности внутренних данных (сбои
памяти и т.п.), программирование становится намного дороже и сложнее, но
принципы остаются теми же. Просто возможность сбоя каких-то компонентов
включаются в рамки допустимого поведения программы. Предусловия, постусловия
и инварианты расширяются до каких-то пределов, но никуда не исчезают.
Вот в этих условиях, когда определенный набор ошибок програмирования становится
частью спецификации системы, вполне допустимо использовать исключения для
сообщения об этих ошибках, т.к. по требованию к системе она должна
уметь работать в присутствии этих ошибок. При этом, будут ли для этого использованы
исключения, коды возврата, или какие-то глобальные переменные типа errno -- совершенно
непринципиально, за исключением усилий программистов. На поведение системы выбор
механизма не должен оказывать никакого влияния.
В случае же обнаружения состояния, из которого не известно, как
восстанавливаться (а это по определению совпадает с нарушением предусловий,
постусловий или инвариантов), ничего другого, кроме как завершить исполнение
просто не остается, если мы, конечно, говорим о предсказуемом поведении системы.
• Fault detection The system must detect that a fault (an
incorrect system state) has occurred.
• Damage assessment The parts of the system state affected by
the fault must be detected.
• Fault recovery The system must restore its state to a known safe state.
• Fault repair The system may be modified to prevent recurrence of the
fault. As many software faults are transitory, this is often unnecessary.
Damage assessment
• Analyse system state to judge the extent of corruption caused by a system failure
• Must assess what parts of the state space have been affected by the failure
• Generally based on ‘validity functions’ which can be applied to the state
elements to assess if their value is within an allowed range
Fault recovery
• Forward recovery
• Apply repairs to a corrupted system state
• Backward recovery
• Restore the system state to a known safe state
• Forward recovery is usually application specific — domain knowledge is
required to compute possible state corrections
• Backward error recovery is simpler. Details of a safe state are maintained
and this replaces the corrupted system
Recovery blocks
• Force a different algorithm to be used for each version so they reduce the
probability of common errors
• However, the design of the acceptance test is difficult as it must be
independent of the computation used
• There are problems with this approach for realtime systems because of
the sequential operation of the redundant versions
Fault minimisation
• Current methods of software engineering now allow for the production of
fault-free software.
• Fault-free software means software which conforms to its specification.
It does NOT mean software which will always perform correctly as there
may be specification errors.
• The cost of producing fault free software is very high. It is only cost-
effective in exceptional situations. May be cheaper to accept software faults
И т.п.
Т.е. это очень дорогой процесс разработки, очень быстро теряющий свои качества
при нарушении целого ряда ключевых моментов. В любом случае, одним из ключевых
моментов является включение требования устойчивости к определенным ошибкам
в спецификацию. Ошибки, выходящие за пределы этой спецификации, фактически,
не поддаются восстановлению, если система не построена на той или иной форме
журналирования, да и в этом случае это не обязательно выйдет сделать.
Вот чуть более подробная ссылка, если тебе, действительно, интересна эта тема: http://www.ece.cmu.edu/~koopman/des_s99/contents.html
V> Что касется фрэймворка... Остаюсь на своих позициях. Думаю, что V> натыкивание ассертов было вызвано все же надежной на тщательное V> (возможно автоматизированное) тестирование, большой альф- и бэта-цикл (в V> котором ассерты скорее всего вообще не убирались) и ра днругие V> заморочки. Но основной причинойконечно являлся минталитет V> программистов. Возможно он был схож с твоим. К этому выоду V> подталкивает и то, что C#-пная часть фрэймворка (которая намного больше V> по объему) в основном содержит именно генрацию исключений.
Все проще. Те исключенительные ситуации, которые входят в спецификацию,
порождают исключения. Остальные причислены к ошибкам, и игнорируются, т.к. с ними нечего больше делать. В самом деле, посмотри внимательнее на случаи,
диагностируемые с помощью assert. Как именно ты предложил бы восстанавливаться
в случае подобных ошибок? В этих случаях система находится в непредсказуемом
состоянии, а кода, диагностирующего нарушения состояния, и возвращающего все
"на родину" просто нет. Если бы, скажем, вызывающий код умел обрабатывать
какой-нибудь NullPointerException, выбрасываемый функцией, в данный момент
проверяющей предусловие с помощью _ASSERTE, то он вместо этого просто-напросто
не передал бы туда нулевой указатель. Все дела
А если допустить, что VM выбрасывала бы какое-нибудь InternalErrorException
пользователю, что именно мог бы сделать в ответ пользователь, если
он не может даже полагаться на свои данные, и корректность работы самых
базовых функций?
V> Кстати, примеры твои не совсем корректны. ExceptionUnwind это то самое V> исключение где генерация исключений является просто таки злом.
Дык, я не выбирал примеры. Там можно, фактически, откуда угодно их дергать...
V> Другими словами ассрты сидят в глубоком ядре, а весь библиотечный код V> напичкан исключениями.
Да, потому что ошибки в прикладном коде не определяют состояние системы VM.
Для нее прикладной код -- как бы обрабатываемые данные. Нарушение обрабатываемых
данных? Не страшно, пусть прикладной код с этим и разбирается...
А вот внутреннее состояние системы должно отвечать определенным инвариантам,
в расчете на которые система и написана. При нарушении этих инвариантов она
просто не в состоянии работать, и т.к. у команды, писавшей VM в приоритетах
не было делать отказоустойчивую систему, они и не заморачивались: последовательное
воплощение принципов разработки отказоустойчивых систем им бы в ой какую копеечку
влетело, а непоследовательное просто-напросто бессмысленно, и может принести
больше вреда, чем пользы, т.к. система получит реальную вероятность работать
в невалидном состоянии, портя данные пользователей, которые те потом еще и
сохранят куда-нибудь, полагаясь на надежную работу VM.
V> квалификации программистов писавших ядро можно считать объем внутренних V> ошибок минимальным. А отсуствие внутренних исключений позволяет лучше V> выявлять ошибки прикладных программистов. Вот только с твоей теорией это V> никак не вяжется.
Очень даже вяжется, если попробовать понять о чем же я говорю, а не припысывать
мне идеи "выпадать в осадок по любому поводу", коих у меня и близко нет.
Просто аналогичные разработке VM принципы можно воплощать и в других проектах,
если не играть в разработку отказоустойчивых систем, бросаясь исключениями без
особого представления о том, кто и как их сможет обработать.
Если программа разделена на определенные независимые по состоянию части, между
которыми можно установить дополнительные требования по диагностированию взаимных
ошибок, то и NullReferenceException будут иметь определенный смысл, но и ошибками
программирования не будут, т.к. являются банальным следованием спецификации.
В случае же диагностирования именно ошибок программирования, т.е. какие-то напрочь
неучтенные обстоятельства, приведшие к непредвиденному состоянию системы, из
которого пути восстановления не предусмотрены, пытаться выбросить исключение
в надежде, что кто-то его сверху "обработает", без четкого представления о
возможных вариантах, кто и как, имхо, не слишком осмысленно.
Обычно подобные нарушения состояния вообще не диагностируются программой,
что мы и видели на примере VM .Net в release режиме.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Cyberax, Вы писали:
>> Если для обработки ситуаций непредусмотренных разработчиком, то от >> использования проверяемых исключений один оверхед.
C>Как раз именно для ошибочных ситуаций, предусмотренных разработчиком. C>Например, метод write в класса Socket вполне логично может кидать C>ConnectionTerminatedException (наследник от IOException), и вполне C>логично задекларировать это исключение в сигнатуре метода.
Я не о том разработчике. Я о разработчике, который пользователь библиотеки, а не её девелопер. Декларируя исключение ты принуждаеш пользователя реагировать. Всё, выбора нет. А может это исключение ему и не интересно.
Andrei N.Sobchuck wrote:
> C>Как раз именно для ошибочных ситуаций, предусмотренных разработчиком. > C>Например, метод write в класса Socket вполне логично может кидать > C>ConnectionTerminatedException (наследник от IOException), и вполне > C>логично задекларировать это исключение в сигнатуре метода. > Я не о том разработчике. Я о разработчике, который пользователь > библиотеки, а не её девелопер. Декларируя исключение ты принуждаеш > пользователя реагировать. Всё, выбора нет.
Да, именно это и есть моя цель — заставить пользователя прореагировать
на возможность выброса исключений.
> А может это исключение ему и не интересно.
Пусть ставить try..catch или разрешает дальнейший проброс — в той же
IDEA это делается нажатием двух клавиш.
Здравствуйте, Cyberax, Вы писали:
C>Тогда мне должны приходить NNTP cancel'ы, которые нормально C>обрабатываются. Так что все равно — недоработка.
Я незнаю как там должно быть в ННТП. Я говорю о том, что есть. ННТП пользуется сервисами сайта. Сам он думать не умеет. Если сайт скзал "низзя", значит низзя.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>В случае же обнаружения состояния, из которого не известно, как ПК>восстанавливаться (а это по определению совпадает с нарушением предусловий, ПК>постусловий или инвариантов), ничего другого, кроме как завершить исполнение ПК>просто не остается, если мы, конечно, говорим о предсказуемом поведении системы.
А, ну, ясно. Очередной пример ухода в споре в обсуждение глобальных филосовских проблем.
Понимаш ли в чем дело. Можно долго делать умное выражение лица... вдаваться в споры об наилучших способах создания супер-пупер-отказоустойчивых систем и т.п. А можно просто вспомнить, что 99% систем являются обычными.
Далее нужно спросить у пользователя таких истем интересно ли им слушать программисткий булшит про инварианты и прочие постусловия если в результате введенные им данные будут безвозвратно потеряны. И спросить у него же, согласен ли он не то что программа, к примеру, не будет корректно синхронизироваться и ее прийдется перезапустить, но при этом она не вылетит и не накроет недописанное сообщение. Если пользователь шибко умный и начнет толкать идеи о журналировании, то ему нужно скромно объяснить, что это все возможно, но заплатить за такую систему прийдется ой как дорого.
Думаю, вопросы эти риторические. И если бы ты весто того, чтобы толкать свою теорию просто занялся бы Янусом или сайтом, то сам бы понял всю абсурдность своих слов.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2,
V> Далее нужно спросить у пользователя таких истем интересно ли им слушать программисткий булшит V> про инварианты и прочие постусловия если в результате введенные им данные будут безвозвратно потеряны.
Вероятно, ему более интересно будет слушать программисткий булшит об управляемых "типобезопасных"
средах, о менталитете программистов и о прочем ни о чем... I rest my case.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Вероятно, ему более интересно будет слушать программисткий булшит об управляемых "типобезопасных" ПК>средах, о менталитете программистов и о прочем ни о чем... I rest my case.
Ему приятнее будет видеть, что его данные не потеряны, а не слушать треп. Но я понимаю, что твои принципы выше этого.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Спасибо за интересные ссылки. Первый PDF файл посмотрел. Вот что сразу понравилось:
Fault tolerance means that the system can
continue in operation in spite of software failure
(это нужно прочитать любителям вызывать abort() при обнаружении нарушения контракта , шутка).
Не мог бы ты прояснить один момент:
ПК>В случае же обнаружения состояния, из которого не известно, как ПК>восстанавливаться (а это по определению совпадает с нарушением предусловий, ПК>постусловий или инвариантов), ничего другого, кроме как завершить исполнение ПК>просто не остается, если мы, конечно, говорим о предсказуемом поведении системы.
Я вот не очень понимаю, почему мы можем определить наличие какого-то плохого состояния, но при этом мы не знаем, как из него выйти (т.е. как бы про такое состояние мы знаем, а вот как выйти, получается, нет)?
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Павел Кузнецов
E>Не мог бы ты прояснить один момент:
ПК>>В случае же обнаружения состояния, из которого не известно, как ПК>>восстанавливаться (а это по определению совпадает с нарушением предусловий, ПК>>постусловий или инвариантов), ничего другого, кроме как завершить исполнение ПК>>просто не остается, если мы, конечно, говорим о предсказуемом поведении системы.
E>Я вот не очень понимаю, почему мы можем определить наличие какого-то плохого состояния, но при этом мы не знаем, как из него выйти (т.е. как бы про такое состояние мы знаем, а вот как выйти, получается, нет)?
Программист пишет код подразумевая при этом, что некоторые инварианты (предусловия, постусловия...) будут выполняться. Тем не менее, бывают, что они не выполняются. Это говорит, о том, что программист сделал ошибку. Написать код, который восстанавливается после ошибок программиста как правило бывает не проще, чем написать код без ошибок. В то же время, обнаружить ошибку (добавив в код дополнительные проверки), бывает гораздо проще.
eao197,
e> Спасибо за интересные ссылки. Первый PDF файл посмотрел. Вот что сразу e> понравилось: e>
e> Fault tolerance means that the system can continue in operation in spite of software failure
e> (это нужно прочитать любителям вызывать abort() при обнаружении нарушения контракта , шутка).
Дык, я ж о чем и говорю: если пытаться писать систему, которая корректно
работает в присутствии отказов программных компонентов, то просто перестать вызывать
abort(), заменив это все на исключения, не достаточно: там работы намного больше.
А без этой работы можно напакостить очень много, пытаясь по умолчанию продолжать
исполнение после какого-нибудь NullReferenceException. Т.е. по умолчанию должно быть
завершение программы, а вот если известно, как восстанавливаться -- тогда уже можно
и восстановиться.
e> Не мог бы ты прояснить один момент:
ПК>> В случае же обнаружения состояния, из которого не известно, ПК>> как восстанавливаться (а это по определению совпадает с нарушением ПК>> предусловий, постусловий или инвариантов), ничего другого, кроме ПК>> как завершить исполнение просто не остается, если мы, конечно, говорим ПК>> о предсказуемом поведении системы.
e> Я вот не очень понимаю, почему мы можем определить наличие какого-то e> плохого состояния, но при этом мы не знаем, как из него выйти (т.е. как e> бы про такое состояние мы знаем, а вот как выйти, получается, нет)?
Например. Есть графический редактор. Скажем, Adobe Illustrator. Его данные
хранятся в виде сложной структуры (PDF). У него также есть некоторый валидатор,
который верифицирует корректность обрабатываемого файла. Если посреди работы
вдруг окажется, что корректность файла нарушена, далеко не факт, что в редакторе
присутствует код, который в состоянии проанализировать, как нужно исправить ситуацию.
Скажем, тег не закрыт, но где его закрывать -- программа не знает. В этом случае
вполне разумным может оказаться сообщить пользователю о том, что целостность файла
нарушена и полагаться на то, что есть средства, позволяющие восстановить работу
десятиминутной давности из autosave.
Если подобное нарушение, которое уже не так просто описать, но, полагаю, аналогия
должна быть вполне понятной, будет диагностировано во внутренних структурах программы,
а не в данных, ею обрабатываемых (или изолируемом контексте), то в этом случае
программа точно так же не будет знать, как же восстанавливаться, но точно так же
будет иметь возможность продиагностировать нарушение целостности.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, dshe, Вы писали:
D>Здравствуйте, Павел Кузнецов, Вы писали:
D>Этот вариант является усовершенствованием варианта 2. При этом PatientNotFoundException считается ошибкой программирования, поскольку метод findPersonByName должен использоваться приблизительно таким образом: D>
D>if(patientFinder.isPersonExist(name)) {
D> Person person = findPersonByName(name);
D> // . . .
D>}
D>
D> ...
варинт 4?
паттерн NullObject:
Если человек не найден , возвращатся экземпляр класса NullPerson (он может быть синглетоном) — он не содержит данных , и его методы ничего не делают
дальше :
Person person = findPersonByName(name);
if(!person.isNull()) // isNull есть у базового класса Person
{
// . . .
}
или так — тогда NullPerson должен реализовать INullObject
if(!(person instance of INullObject)) {
// . . .
}
Denis_TST,
> Если человек не найден , возвращатся экземпляр класса NullPerson (он может быть синглетоном) — он не содержит данных , и его методы ничего не делают > дальше : >
> Person person = findPersonByName(name);
> if(!person.isNull()) // isNull есть у базового класса Person
>
Но человек может забыть о том, что ему нужно проверять person на Null. В этом-то и была исходная проблема.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
e>> Спасибо за интересные ссылки. Первый PDF файл посмотрел. Вот что сразу e>> понравилось: e>>
e>> Fault tolerance means that the system can continue in operation in spite of software failure
e>> (это нужно прочитать любителям вызывать abort() при обнаружении нарушения контракта , шутка).
ПК>Дык, я ж о чем и говорю: если пытаться писать систему, которая корректно ПК>работает в присутствии отказов программных компонентов, то просто перестать вызывать ПК>abort(), заменив это все на исключения, не достаточно: там работы намного больше.
+1
Мне думается, что здесь можно перефразировать Саттера, который говорил, что о безопасности кода по отношению к исключениям нужно заботится заранее, что нельзя просто добавить ее в уже существующий код. Аналогично и со сбоями -- нужно проектировать систему устойчивой к сбоям, ведь вряд ли получится просто адаптировать к сбоям уже существующую систему.
ПК>А без этой работы можно напакостить очень много, пытаясь по умолчанию продолжать ПК>исполнение после какого-нибудь NullReferenceException. Т.е. по умолчанию должно быть ПК>завершение программы, а вот если известно, как восстанавливаться -- тогда уже можно ПК>и восстановиться.
Вот здесь, как мне кажется, в C++ попроще
В общем случае, платформенно переносимым способом, очень сложно перехватывать исключения о доступе по некорректному указателю. Если действовать в рамках стандарта C++ и не опускаться на уровень конкретной ОС, то перехватывать в C++ можно только то, что было в программе порождено. А не то, что по своему разумению выкинула VM.
ПК>Например. Есть графический редактор. Скажем, Adobe Illustrator. Его данные ПК>хранятся в виде сложной структуры (PDF). У него также есть некоторый валидатор, ПК>который верифицирует корректность обрабатываемого файла. Если посреди работы ПК>вдруг окажется, что корректность файла нарушена, далеко не факт, что в редакторе ПК>присутствует код, который в состоянии проанализировать, как нужно исправить ситуацию. ПК>Скажем, тег не закрыт, но где его закрывать -- программа не знает. В этом случае ПК>вполне разумным может оказаться сообщить пользователю о том, что целостность файла ПК>нарушена и полагаться на то, что есть средства, позволяющие восстановить работу ПК>десятиминутной давности из autosave.
ПК>Если подобное нарушение, которое уже не так просто описать, но, полагаю, аналогия ПК>должна быть вполне понятной, будет диагностировано во внутренних структурах программы, ПК>а не в данных, ею обрабатываемых (или изолируемом контексте), то в этом случае ПК>программа точно так же не будет знать, как же восстанавливаться, но точно так же ПК>будет иметь возможность продиагностировать нарушение целостности.
Паша, ты привел хороший пример. Он достаточно точно иллюстрирует, на мой взгляд, очевидную вещь -- если под восстанавливаемостью понимать способность исправить ошибку и продолжить работу дальше, как будто ничего не произошло, то дело обстоит именно так, как ты написал.
Но мне более симпатична другая, не менее очевидная вещь -- вместо того, чтобы пытаться исправить диагностированную ошибку лучше отвергнуть некорректные данные и вернуться в начальное состояние. Фактически, откатить транзакцию, как будто ничего и не было. Тот же Adobe Illustrator при обнаружении незакрытого тега должен очистить все уже выделенные ресурсы и вернуться в полностью корректное состояние, предшествовавшее попытке открытия файла.
Т.е., на мой взгляд, операции в приложении должны проектироваться как последовательности вложенных друг в друга транзакций. В самом простейшем случае сбой в какой-то N-ой транзакции приведет к откату всех (N-1) предшествующих транзакций и приложение вернется в начальное корректное состояние (т.е. восстановится в моем понимании). В более сложных случаях в каких-то узлах этой последовательности можно будет предусмотреть альтернативную цепочку исполнения при возникновении исключения. Например, у меня в ObjESSty при открытии БД ищется основной файл БД с определенным именем. Если он не найден -- это исключительная ситуация. Но в некотором месте есть возможность проверить -- может быть предыдущая сессия работы с БД завершилась неудачно, поэтому основной файл сейчас имеет другое имя. Если это так, то запускается другая цепочка минитранзакций. И если она доходит до конца, то вся операция открытия БД завершается успешно. Если же нет, то исключение отдается наружу. Но и там может быть предусмотрена альтернативная последовательность операций (например, может быть предпринята попытка восстановления БД из последней архивной копии).
Поэтому в моем представлении приложение состоит из большого количества уровней. При выполнении какой-либо операции управление передается сверху вниз. При этом каждый уровень может:
1) либо ожидать исключений чтобы предпринять попытку исправить ситуацию каким-либо иным образом (т.е. запустить другую последовательность транзакций);
2) либо ожидать исключений чтобы очистить свои ресурсы, восстановить свое начальное/конечное/специальное состояние и отдать исключение наружу (т.е. предоставлять базовую гарантию по отношению к исключениям);
3) либо ничего не ожидать и ничего не очищать (т.е. быть нейтральным по отношению к исключениям).
Имхо, наибольшую сложность представляют как раз 1-й и 3-й варианты. С первым вариантом все более-менее понятно. Там сложность проистекает именно из того, что альтернативные пути нужно предусматривать. А так же нужно четко понимать, при каких исключениях можно попробовать альтернативную цепочку транзакций, а при каких нет. Но что, если пойти по самому примитивному и оптимистичному пути -- расчитывать на то, что при порождении исключения нижележащие уровни приложения вернулись в какое-либо корректное состояние (т.е. обеспечили базовую гарантию)? Тогда, имхо, получается, что можно перехватить все исключения (может быть со специально оговоренными для приложения особенностями). Ведь все инициированные транзакции откатились, мы вернулись туда, откуда начали. Почему бы не попробовать еще раз? Вполне можно попробовать, если базовая гарантия действительно была обеспечена.
Самая плохая ситуация с 3-м вариантом. Если нижележащий уровень является нейтральным по отношению к исключениям и не обеспечивает базовую гарантию, то поймав где-то иключение, нельзя гарантировать, что мы находимся в исходной точке -- весьма вероятно, что какой-то мусор где-то остался. Тогда мы на самом деле оказываемся "out of known space." Отсюда получается, что уровни приложения (компоненты, модули и т.д.) должны обеспечивать базовую гарантию безопасности иключений. В противном случае самым простым выходом будет abort().
Думаю, что нужно специально подчеркнуть, что все сказанное относится к общему понятию "исключения", как к чему-то, что идет не так, там, где этого не предполагалось. А не к конкретно механизму исключений конкретного языка программирования (throw, try, catch). Здесь я с тобой согласен -- для информирования об исключительных ситуациях может использоваться как механизм исключений, как и обычные коды возврата.
Можно было бы еще развить тему того, насколько просто/сложно писать нейтральный по отношению к исключениям код. Но это отдельная тема, может быть в другой раз, но не сейчас.
Пока же можно затронуть пару вопросов, которые были подняты в данной теме.
Во-первых, нужно ли считать нарушение контракта (пред- и постусловий) настолько фатальной ошибкой, чтобы прерывать работу приложения? Или же воспринимать это как обычную внештатную ситуацию (тавтология, но в данном случае приемлимая)? Т.е., при программировании мы неизбежно оглядываемся на ряд факторов, с которыми нам не хотелось бы встречаться -- нехватка памяти, недостаточно места на диске, отсутствие доступа к нужному файлу, сбой диска при операции ввода/вывода, разрыв подключения к БД во время выполнения запроса и т.д. Против этих событий мы как-то пытаемся защитится (в зависимости от требований к надежности приложения). Так почему же не внести в эту категорию и нарушения контракта?
Здесь есть две точки зрения. Пессиместическая:
-- Капитан, в пятом отсеке вода!
-- Мать, мать, мать!!! Открыть кингстоны! Экипажу покинуть судно! А эта редиск..., этот нехороший человек, этот Василий Алибабаевич, говорил, что воды в пятом быть не может! И на испытаниях ее там никогда не бы.л..о...
Или оптимистическая:
-- Капитан, в пятом отсеке вода!
-- Ну и задрайте его нафиг! Машинное -- сбавить обороты. Радист -- доложить на базу об изменении сроков прибытия в порт. Старпом -- занесите описание проишествия в бортовой журнал. Передать всем -- будем идти вперед пока рубка над водой торчит!
(все персонажи являются вымышленными, любые совпадения с реальными лицами или событиями являются случайными )
Лично я придерживаюсь оптимистического подхода. Наверное, просто потому, что не приходилось разрабатывать софт для каких-нибудь систем жизнеобеспечения.
Во-вторых, мне кажестся достаточно дикой идея вставлять в приложение специальные обработчики исключений, которые бы вступали в работу когда уже все плохо -- NullReferenceException валится один за одним, да и вообще последний вздох остался. И которые пытаются взять откуда-то остатки каких-то документов и как-то их сохранить, чтобы потом восстановится можно было. Мое мнение заключается в том, что восстанавливаемость должна разрабатываться изначально, наряду с безопасностью исключений. И проводится фиксация контрольных точек должна только тогда, когда приложение гарантированно находится в корректном состоянии. Яркие примеры тому -- write-ahead-log в СУБД, переодические autosave и bak-файлы в текстовых редакторах.
При этом, если придерживаться нарисованной мной оптимистической картины (когда каждый нижележащий уровень приложения обеспечивает базовую гарантию исключений) такие гарантированно корректные точки в приложении возможны даже при раскрутке вверх исключительной ситуации. Т.е. на каком-то шаге обеспечение базовой гарантии как раз и будет состоять в том, чтобы зафиксировать где-то контрольную точку, после чего пропустить исключение наверх. Но опять же, такое поведение должно быть спроектированно, причем для конкретно этого уровня приложения и, возможно, только для определенных типов исключений.
Замечу, что я не смог затронуть еще одну линию романа. До сих пор как бы подразумевалось, что когда нижележащий уровень обеспечивает базовую гарантию, то нет никаких side effects от прерванных исключением операций. Но на практике разные побочные эффекты возможны. Но здесь, имхо, на уровне прикладной логики нужно как-то страховаться от того, чтобы побочные эффекты прерванной транзакции не привели затем к пагубным последствиям.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
eao197,
e> Аналогично и со сбоями -- нужно проектировать систему устойчивой к e> сбоям, ведь вряд ли получится просто адаптировать к сбоям уже e> существующую систему.
+1
ПК>> Например. Есть графический редактор. Скажем, Adobe Illustrator. Его ПК>> данные хранятся в виде сложной структуры (PDF). У него также есть ПК>> некоторый валидатор, который верифицирует корректность обрабатываемого ПК>> файла. Если посреди работы вдруг окажется, что корректность файла ПК>> нарушена, далеко не факт, что в редакторе присутствует код, который в ПК>> состоянии проанализировать, как нужно исправить ситуацию. <...>
e> Паша, ты привел хороший пример. Он достаточно точно иллюстрирует, на мой e> взгляд, очевидную вещь -- если под восстанавливаемостью понимать e> способность исправить ошибку и продолжить работу дальше, как e> будто ничего не произошло, то дело обстоит именно так, как ты написал.
e> Но мне более симпатична другая, не менее очевидная вещь -- вместо того, e> чтобы пытаться исправить диагностированную ошибку лучше отвергнуть e> некорректные данные и вернуться в начальное состояние. Фактически, e> откатить транзакцию, как будто ничего и не было.
Различают два принципиальных способа восстановления после ошибок: forward
recovery и backward recovery. Ты их как раз здесь и противопоставил: первый
сводится к исправлению диагностированного невалидного состояния, второй --
к возврату в последнее известное валидное состояние. Каждый из них имеет
свои сильные и слабые стороны.
Forward recovery обычно сложнее в реализации, но потенциально более эффективен.
Основным недостатком является то, что forward recovery позволяет надежно
восстановиться только в случае заранее предусмотренных нарушений. Основным
достоинством является то, что в случае ошибки система может компенсировать ее
эффекты, и таки продолжить исполнение. Этот подход в силу сложности реализуется
намного реже, чем backward recovery.
Backward recovery обычно проще в реализации, но может требовать изрядно много
ресурсов в случае определенных операций. Обычно сводится к выполнению множества
проверок до внесения изменений в состояние или на копии данных, которая
заменяет текущий контекст в случае успешного прохождения проверок. Проблемами
данного подхода являются: возможность ситуации, когда ошибка является
систематической, и воспроизводится вновь и вновь, не давая системе продолжать
работу; возможность пропустить невалидное изменение, и тогда может не быть
возможности вернуться в корректное состояние. Этот подход исользуется чаще.
e> Тот же Adobe Illustrator при обнаружении незакрытого тега должен очистить e> все уже выделенные ресурсы и вернуться в полностью корректное состояние, e> предшествовавшее попытке открытия файла.
Я выделил ключевые слова: "посреди работы". Негативный результат
запрограммированных проверок, очевидно, приведет к возврату в последнее
известное корректное состояние. Проблема лежит на шаг дальше: что если
проверки не обнаружили некорректную модификацию, и уже после того, как
состояние было изменено (т.е. transaction.commit), где-то в процессе
дальнейшей работы было обнаружено нарушение внутреннего состояния?
Для чистой backward recovery, если не делаются checkpoints, такая ситуация
не подлежит исправлению. Да и в случае checkpoints гарантий, что ошибочное
состояние не было сохранено, нет никаких.
Можно, конечно, спросить, почему же эти некорректные изменения не были обнаружены
до внесения модификаций? Ответы могут быть разными: полная верификация состояния
на каждое изменение может быть слишком дорогой; может присутствовать банальная
ошибка в верификаторе изменений, т.к. диагностировать нарушения по состоянию и планируемым изменениям может оказаться сложнее, чем по полному состоянию,
а хранить копию может быть не всегда возможно и т.п.
e> Т.е., на мой взгляд, операции в приложении должны проектироваться как e> последовательности вложенных друг в друга транзакций. В самом простейшем e> случае сбой в какой-то N-ой транзакции приведет к откату всех (N-1) e> предшествующих транзакций и приложение вернется в начальное корректное e> состояние (т.е. восстановится в моем понимании). В более сложных случаях e> в каких-то узлах этой последовательности можно будет предусмотреть e> альтернативную цепочку исполнения при возникновении исключения.
Это все здорово, но что будет, если невалидное состояние будет обнаружено
уже после того, как транзакции будут завершены успешно? Что делать в этом
случае, когда при "транзакционном" подходе обычно вся ставка на него, и процедур
для forward recovery, т.е. для исправления уже ошибочного состояния, обычно
не предусмотрено?
Обычно эта проблема "не стоит", т.к. при "транзакционном подходе" инварианты
проверяются зачастую только при попытке завершения транзакции/внесения изменений,
и такая ситуация будет (ошибочно) продиагнастирована как попытка внесения неверных
изменений, хотя на самом деле существующее состояние уже некорректно.
Однако если допустить, что проверки есть и по ходу "нормального" исполнения,
а не только в рамках транзакций, вопрос остается в силе: что предлагатся делать
в этом случае?
e> Самая плохая ситуация с 3-м вариантом. Если нижележащий уровень является e> нейтральным по отношению к исключениям и не обеспечивает базовую e> гарантию, то поймав где-то иключение, нельзя гарантировать, что мы e> находимся в исходной точке -- весьма вероятно, что какой-то мусор где-то e> остался. Тогда мы на самом деле оказываемся "out of known space." Отсюда e> получается, что уровни приложения (компоненты, модули и т.д.) должны e> обеспечивать базовую гарантию безопасности иключений. В противном случае e> самым простым выходом будет abort().
Даже когда уровни в принципе обеспечивают всевозможные гарантии безопасности
относительно возникновения исключений, в них вполне могут присутствовать ошибки.
Вот об этом-то и идет речь: что делать в случае обнаружения подобной ошибки
после того, как все "должно было вернуться на свои места", но "не вернулось"?
По сути всевозможные гарантии безопасности относительно исключений суть спецификация,
и корректное их выполнение ошибками не является. А вот невыполнение одним из
компонент своих гарантий -- ошибка. Если возможно изолировать контекст исполнения
данного компонента -- хорошо, есть шансы на сравнительно легкое восстановление
корректности состояния посредством "убийства" данного контекста (закрытие сессии
отдельного пользователя на сервере), но это далеко не всегда так (например, сам
движок сервера, который "крутит" сессии, таким макаром не изолируется, т.к.
влияет на все сессии сразу).
e> Во-первых, нужно ли считать нарушение контракта (пред- и постусловий) e> настолько фатальной ошибкой, чтобы прерывать работу приложения? Или же e> воспринимать это как обычную внештатную ситуацию (тавтология, но в e> данном случае приемлимая)?
Если в спецификации говорится, что, скажем, в случае нулевого аргумента данная
функция порождает исключение, то это уже вполне запланированная ситуация. Просто
предусловия функции расширили до включения варианта с нулевым аргументом.
e> Т.е., при программировании мы неизбежно оглядываемся на ряд факторов, e> с которыми нам не хотелось бы встречаться -- нехватка памяти, недостаточно e> места на диске, отсутствие доступа к нужному файлу, сбой диска при операции e> ввода/вывода, разрыв подключения к БД во время выполнения запроса и т.д. e> Против этих событий мы как-то пытаемся защитится (в зависимости от e> требований к надежности приложения). Так почему же не внести в эту e> категорию и нарушения контракта?
Потому что действия по восстановлению могут быть совершенно различными.
e> Здесь есть две точки зрения. Пессиместическая: e> -- Капитан, в пятом отсеке вода! e> -- Мать, мать, мать!!! Открыть кингстоны! Экипажу покинуть судно! А эта e> редиск..., этот нехороший человек, этот Василий Алибабаевич, говорил, e> что воды в пятом быть не может! И на испытаниях ее там никогда не e> бы.л..о...
e> Или оптимистическая: e> -- Капитан, в пятом отсеке вода! e> -- Ну и задрайте его нафиг! Машинное -- сбавить обороты. Радист -- e> доложить на базу об изменении сроков прибытия в порт. Старпом -- e> занесите описание проишествия в бортовой журнал. Передать всем -- будем e> идти вперед пока рубка над водой торчит!
Имхо, это несколько в другую сторону примеры: в конструкцию корабля заранее
внесены специальные особенности, рассчитанные на затопление отдельных
отсеков. Т.е. затопление пятого отсека является ситуацией вполне просчитанной,
хотя и нежелательной.
Более близкой к ошибкам программирования/нарушению инвариантов в рамках
данной модели была бы другая аналогия:
-- Капитан, в пятом отсеке вода!
-- Ну и задрайте его нафиг!
-- Капитан!!! Люки Васька на металлолом сдал, задраивать нечем!!!
Теперь вопрос, что лучше (в рамках данной аналогии): чтоб (автоматизированный)
капитан продолжал отдавать команды, как будто пятый отсек задраивают,
или же чтобы он все-таки перешел к эвакуации судна, пытаясь спасти хотя бы
экипаж, если не судно?
e> Во-вторых, мне кажестся достаточно дикой идея вставлять в приложение e> специальные обработчики исключений, которые бы вступали в работу когда e> уже все плохо -- NullReferenceException валится один за одним, да и e> вообще последний вздох остался. И которые пытаются взять откуда-то e> остатки каких-то документов и как-то их сохранить, чтобы потом e> восстановится можно было.
Да. Это одна из вполне принимаемых мною точек зрения: игнорировать нарушения
инвариантов. Но еще больше мне по душе точка зрения, сводящаяся к совершению
еще одного дополнительного шага. Все-таки отлавливать нарушения инвариантов
в определенных случаях, и хотя бы сохранять dump-файл, лог-файл и, возможно,
предлагать отправить отчет об ошибке разработчикам.
При этом достигаются еще и другие положительные моменты: пред-/постусловия
и инварианты становятся явно выраженными в коде в виде assertions, что в
будущем только помогает поддерживать программу.
e> Мое мнение заключается в том, что восстанавливаемость должна e> разрабатываться изначально, наряду с безопасностью исключений.
С этим, имхо, глупо спорить. Я -- только за такие правильные действия.
e> И проводится фиксация контрольных точек должна только тогда, когда e> приложение гарантированно находится в корректном состоянии.
А вот с этим уже сложнее: есть определенные пределы диагностики корректности
состояния по ходу внесения изменений, да и тривиальные ошибки никто не отменял.
Т.е. в случае, когда "гарантированно корректное состояние", действительно,
является корректным, вопросов нет. Вопросы возникают о том, что делать, когда,
вдруг, оказывается, что "гарантированно корректное состояние" корректным не
является...
e> При этом, если придерживаться нарисованной мной оптимистической картины e> (когда каждый нижележащий уровень приложения обеспечивает базовую e> гарантию исключений) такие гарантированно корректные точки в приложении e> возможны даже при раскрутке вверх исключительной ситуации.
Это все описания работы системы, в которой нет критических ошибок: она честно
восстанавливается в случаях, предусмотренных разработчиками. Что эта система
делает в случаях, не предусмотренных?
Как уже говорилось, желание продолжать работу в таких случаях
оборачивается все большим и большим расширением пред-/постусловий и
инвариантов, и удорожает разработку системы очень сильно, требуя дублирования
многих ее частей, часто с соблюдением требования их независимой разработки,
принудительным внесением различий в разные ветви исполнения и т.п.
Имхо, в таких случаях вызов abort() намного лучше, чем попытка
продолжения исполнения.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Как уже говорилось, желание продолжать работу в таких случаях ПК>оборачивается все большим и большим расширением пред-/постусловий и ПК>инвариантов, и удорожает разработку системы очень сильно, требуя дублирования ПК>многих ее частей, часто с соблюдением требования их независимой разработки, ПК>принудительным внесением различий в разные ветви исполнения и т.п.
ПК>Имхо, в таких случаях вызов abort() намного лучше, чем попытка ПК>продолжения исполнения.
И все таки, давай возьмем реальную ситуацию. Предположим есть сервер приложений (уже релизный). В результате ошибки при каких то хитрых комбинациях данных внутри сервера эта ошибка вылазит. Какова должна быть, по твоему, правильная последовательность действий:
1) Выкинуть исключение клиенту, продолжить работу.
2) Выкинуть исключение клиенту, грохнуть клиентскую сессию.
3) Убить процесс сервера.
Дополнительные условия — все доступные данные для анализа сбрасываются в лог в любом случае. Сессии друг от друга, разумеется, изолированы. Никаких специальных действий по обнаружению и обработке этой конкретной ошибки в коде нет.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Различают два принципиальных способа восстановления после ошибок: forward ПК>recovery и backward recovery. Ты их как раз здесь и противопоставил: первый ПК>сводится к исправлению диагностированного невалидного состояния, второй -- ПК>к возврату в последнее известное валидное состояние. Каждый из них имеет ПК>свои сильные и слабые стороны.
ПК>Forward recovery обычно сложнее в реализации, но потенциально более эффективен. ПК>Основным недостатком является то, что forward recovery позволяет надежно ПК>восстановиться только в случае заранее предусмотренных нарушений. Основным ПК>достоинством является то, что в случае ошибки система может компенсировать ее ПК>эффекты, и таки продолжить исполнение. Этот подход в силу сложности реализуется ПК>намного реже, чем backward recovery.
Лично у меня сложилось мнение, что forward recovery в большей степени относится к структуре обрабатываемых данных. Т.е. forward recovery вряд ли возможен, если в самих данных нет избыточности, за счет которой и обеспечивается восстановление сбойных данных.
e>> Тот же Adobe Illustrator при обнаружении незакрытого тега должен очистить e>> все уже выделенные ресурсы и вернуться в полностью корректное состояние, e>> предшествовавшее попытке открытия файла.
ПК>Я выделил ключевые слова: "посреди работы". Негативный результат ПК>запрограммированных проверок, очевидно, приведет к возврату в последнее ПК>известное корректное состояние. Проблема лежит на шаг дальше: что если ПК>проверки не обнаружили некорректную модификацию, и уже после того, как ПК>состояние было изменено (т.е. transaction.commit), где-то в процессе ПК>дальнейшей работы было обнаружено нарушение внутреннего состояния?
Мне кажется, что здесь затронуты два вопроса. Представим, что некая цепочка транзаций для выполнения действия A завершилась успешно. Затем началась другая цепочка транзакций для выполнения действия B и вот тут обнаружилась проблема, привнесенная в систему во время операции A (я полагаю, что ты именно это имел в виду). Имхо, первый вопрос состоит в том, что делать с операцией B? На мой взгляд -- откатывать. Второй вопрос состоит в том, каковы шансы столкнутся с последствиями операции A при очередной попытке выполнения операции B. Если результаты операции A где-то хранятся то мы получаем именно ту проблему с backward recovery, про которую ты говорил -- повторные входы в операцию B будут приводить к одному и тому же исходу -- откату операции B.
И мне кажется, что так и должно быть. Если в операции B не предусмотрены механизмы для forward recovery последствий ошибок в операции A, то все, что B может сделать -- это откатится, не запортив чего-то еще.
ПК>Для чистой backward recovery, если не делаются checkpoints, такая ситуация ПК>не подлежит исправлению. Да и в случае checkpoints гарантий, что ошибочное ПК>состояние не было сохранено, нет никаких.
Именно так.
ПК>Можно, конечно, спросить, почему же эти некорректные изменения не были обнаружены ПК>до внесения модификаций?
Это риторический вопрос. Shit happens, что тут еще скажешь.
ПК>Это все здорово, но что будет, если невалидное состояние будет обнаружено ПК>уже после того, как транзакции будут завершены успешно? Что делать в этом ПК>случае, когда при "транзакционном" подходе обычно вся ставка на него, и процедур ПК>для forward recovery, т.е. для исправления уже ошибочного состояния, обычно ПК>не предусмотрено?
Дык чего тут уже делать. Перепроектировать решение и добавлять таки какие-то механизмы forward recovery. Одновременно исправлять баги, приводящие к возникновению выявленных ошибок и создавать вспомогательные инструменты, которые модифицируют испорченные данные.
Но ключевой, с моей точки зрения, момент в том, что мы учимся справляться с проблемами уже после того, как эти поблемы обнаруживаются. А изначально сосредотачиваемся на том, чтобы максимально полно диагностировать "нормальность" ситуации и маскимально полно откатываться при обнаружении каких-то отклонений от нормы. Т.е. следуем правилам:
— проверяем все, что можем;
— ничего не портим, если не уверены в возможности отката;
— добавляем в систему решение обнаруженных проблем по мере их поступления.
Степень параноидальности первых двух пунктов зависит от характера задачи.
ПК>Обычно эта проблема "не стоит", т.к. при "транзакционном подходе" инварианты ПК>проверяются зачастую только при попытке завершения транзакции/внесения изменений, ПК>и такая ситуация будет (ошибочно) продиагнастирована как попытка внесения неверных ПК>изменений, хотя на самом деле существующее состояние уже некорректно.
ПК>Однако если допустить, что проверки есть и по ходу "нормального" исполнения, ПК>а не только в рамках транзакций, вопрос остается в силе: что предлагатся делать ПК>в этом случае?
Имхо, этот вопрос как бы предполагает, что есть какой-то один, универсальный ответ. Что-то типа поиска серебрянной пули.
Мне кажется, что в общем случае ответа на него нет. Я стою на оптимистических позициях и предполагаю, что даже если на N-ом уровне вложенности мы оказались в уже некорректном состоянии, то достаточно будет вернуться на M уровней вверх, чтобы выйти из него. А дальше уже задача разработчиков в том, чтобы определиться, почему так произошло. И внести в софт соответствующие исправления. Т.е. я возлагаю надежду на то, что откаты не позволят наломать лишних дров. И что лучше и проще решать обнаруженные проблемы, чем пытаться создать программу, которая изначально защищает от всего, что только может случится.
ПК>Если возможно изолировать контекст исполнения ПК>данного компонента -- хорошо, есть шансы на сравнительно легкое восстановление ПК>корректности состояния посредством "убийства" данного контекста (закрытие сессии ПК>отдельного пользователя на сервере), но это далеко не всегда так (например, сам ПК>движок сервера, который "крутит" сессии, таким макаром не изолируется, т.к. ПК>влияет на все сессии сразу).
Но ведь сам движок -- это еще один уровень, причем не самый верхний (если говорить не про отдельное приложение, а про весь комплекс целиком). Еще выше будет тот, кто этот движок запускает. Если движок является верхним уровнем приложения и столкнулся с невозможности обработки какого-то исключения -- у него нет выбора. Он должен завершить свою работу. Максимально корректно. А более высокий уровень (watch dog какой-нибудь) перезапустит это приложение.
e>> Во-первых, нужно ли считать нарушение контракта (пред- и постусловий) e>> настолько фатальной ошибкой, чтобы прерывать работу приложения? Или же e>> воспринимать это как обычную внештатную ситуацию (тавтология, но в e>> данном случае приемлимая)?
ПК>Если в спецификации говорится, что, скажем, в случае нулевого аргумента данная ПК>функция порождает исключение, то это уже вполне запланированная ситуация. Просто ПК>предусловия функции расширили до включения варианта с нулевым аргументом.
Так ведь мы же ведем речь о том, что спецификации и реализации могут различаться. Так же, как и спецификации и реальные условия. В спецификации на какой-то компонент могут быть не отражены все условия, при которых компонент обнаруживает нарушения контракта. Либо эти условия могли изменится при смене версии компонента. Просто мое мнение состоит в том, что при обнаружении нарушения контракта нельзя финишировать все приложение. Нужно порождать исключение в надежде, что где-то наверху с этим исключением справятся. К тому же, если эти надежды не оправдается и это исключение не будет обработано даже на самом верхнем уровне приложения, то приложение все равно завершит свою работу.
e>> Т.е., при программировании мы неизбежно оглядываемся на ряд факторов, e>> с которыми нам не хотелось бы встречаться -- нехватка памяти, недостаточно e>> места на диске, отсутствие доступа к нужному файлу, сбой диска при операции e>> ввода/вывода, разрыв подключения к БД во время выполнения запроса и т.д. e>> Против этих событий мы как-то пытаемся защитится (в зависимости от e>> требований к надежности приложения). Так почему же не внести в эту e>> категорию и нарушения контракта?
ПК>Потому что действия по восстановлению могут быть совершенно различными.
А вот мне кажется, что проще свести все действия по восстановлению к одному принципу -- к backward recovery
На самом деле, проще сначала заложиться как какой-то простой способ поведения, усложняя его по мере накопления реальных проблем. По аналогии с преждевременной оптимизацией приложения можно сказать, что преждевременная нацеленность на восстановление в любых условиях (даже тех, которые изначально неизвестны) -- это зло.
ПК>Имхо, это несколько в другую сторону примеры: в конструкцию корабля заранее ПК>внесены специальные особенности, рассчитанные на затопление отдельных ПК>отсеков. Т.е. затопление пятого отсека является ситуацией вполне просчитанной, ПК>хотя и нежелательной.
Мне кажется, что как раз в ту сторону. По аналогии с кораблем, приложение так же должно быть сконструированно в предположении, что оно должно оставаться работоспособным даже при полной неработоспособности отдельных его компонентов. Может быть и звучит это дико, но на самом деле не так уж это и невозможно. Я сейчас как раз нахожусь в стадии переосмысления некоторых вещей. Еще года полтора назад я думал, что нормальным решением является сложный многопоточный комплекс, который решает сразу все задачи, которые перед ним стоят (благо наш агентно-ориентированный подход позволял легко это делать). Но сейчас я все больше и больше убеждаюсь, что более жизнеспособной схемой является разделение этого комплекса на ряд более простых процессов, взаимодействующих через IPC -- этакий спрут из процессов и сокетов/пайпов (благо наш агентно-ориентированный подход позволяет легко делать и это). В такой ситуации каждый процесс подобен отсеку корабля, который можно затопить (отключить).
ПК>Более близкой к ошибкам программирования/нарушению инвариантов в рамках ПК>данной модели была бы другая аналогия: ПК>-- Капитан, в пятом отсеке вода! ПК>-- Ну и задрайте его нафиг! ПК>-- Капитан!!! Люки Васька на металлолом сдал, задраивать нечем!!!
Не совсем так
Если следовать моей логике, то вместо доклада про отсутствие люков будет доклад о наличии течи в соседних с пятым отсеков. После чего последует команда на задраивание и этих отсеков тоже. И так до тех пор, пока либо течь не будет где-нибудь изолирована, либо корабль все же не пойдет ко дну. В последнем случае хотелось бы иметь возможность дать команду экипажу покинуть судно. Хотя не обязательно делать это сразу для всего экипажа -- можно пробовать разрешать покидать судно личному составу затапливаемых отсеков. Тогда к моменту затопления всего корабля на нем никого не останется.
ПК>Теперь вопрос, что лучше (в рамках данной аналогии): чтоб (автоматизированный) ПК>капитан продолжал отдавать команды, как будто пятый отсек задраивают, ПК>или же чтобы он все-таки перешел к эвакуации судна, пытаясь спасти хотя бы ПК>экипаж, если не судно?
Лучше, чтобы в такой ситуации невозможность задраивания люков была предусмотрена в виде исключительной ситуации. И чтобы автоматизированный капитан умел при ее возникновении отдать команду на эвакуацию экипажа.
e>> Во-вторых, мне кажестся достаточно дикой идея вставлять в приложение e>> специальные обработчики исключений, которые бы вступали в работу когда e>> уже все плохо -- NullReferenceException валится один за одним, да и e>> вообще последний вздох остался. И которые пытаются взять откуда-то e>> остатки каких-то документов и как-то их сохранить, чтобы потом e>> восстановится можно было.
ПК>Да. Это одна из вполне принимаемых мною точек зрения: игнорировать нарушения ПК>инвариантов. Но еще больше мне по душе точка зрения, сводящаяся к совершению ПК>еще одного дополнительного шага. Все-таки отлавливать нарушения инвариантов ПК>в определенных случаях, и хотя бы сохранять dump-файл, лог-файл и, возможно, ПК>предлагать отправить отчет об ошибке разработчикам.
ПК>При этом достигаются еще и другие положительные моменты: пред-/постусловия ПК>и инварианты становятся явно выраженными в коде в виде assertions, что в ПК>будущем только помогает поддерживать программу.
+1
Но мне бы хотелось, чтобы эти варианты были не разрушающими. Т.е. модуль диагностировал нарушение, сделал сохранение информации о нем в dump/log, но все приложение не прервал.
e>> И проводится фиксация контрольных точек должна только тогда, когда e>> приложение гарантированно находится в корректном состоянии.
ПК>А вот с этим уже сложнее: есть определенные пределы диагностики корректности ПК>состояния по ходу внесения изменений, да и тривиальные ошибки никто не отменял. ПК>Т.е. в случае, когда "гарантированно корректное состояние", действительно, ПК>является корректным, вопросов нет. Вопросы возникают о том, что делать, когда, ПК>вдруг, оказывается, что "гарантированно корректное состояние" корректным не ПК>является...
Так ведь от всего не застрахуешься. Выходом, имхо, является итерационность разработки. Как только проблемы обнаруживаются, они устраняются, а надежность всего приложения -- повышается.
ПК>Это все описания работы системы, в которой нет критических ошибок: она честно ПК>восстанавливается в случаях, предусмотренных разработчиками. Что эта система ПК>делает в случаях, не предусмотренных?
Паша, здесь я не очень понимаю, что имеется в виду под критическими ошибками. Если мы говорим о С++, то обращение к неверному указателю, которое повлекло за собой Access Violation -- это пример критической ошибки, которая (если нет хитрых системно-зависимых хаков) и приведет автоматом к abort(). Про managed среды сказать ничего не могу, т.к. с Java работал уже слишком давно, а на Ruby таких серьезных задач не решал. Исключения же в C++, которые произведены от std::exception, имхо, позволяют делать откат из сбойного модуля на более высокий уровень (в пределе это тот же main, тогда исключение и будет играть роль abort()).
ПК>Как уже говорилось, желание продолжать работу в таких случаях ПК>оборачивается все большим и большим расширением пред-/постусловий и ПК>инвариантов, и удорожает разработку системы очень сильно, требуя дублирования ПК>многих ее частей, часто с соблюдением требования их независимой разработки, ПК>принудительным внесением различий в разные ветви исполнения и т.п.
ПК>Имхо, в таких случаях вызов abort() намного лучше, чем попытка ПК>продолжения исполнения.
В таких случаях -- это когда есть тотальное дублирование, да еще с привлечением разработанных по разным алгоритмам и на разных технологиях? Если так, то мне кажется, что в таких случаях требования к восстанавливаемости иные. Тогда мы должны как можно раньше диагностировать проблему и моментально умереть чтобы дать другому модулю возможность справится с этими проблемами.
А вот что, если мы не можем позволить себе две реализации одного модуля по разным алгоритмам? Если можно сделать только одну реализацию? Простой вызов abort() может привести к тому, что если будет выявлена систематическая ошибка, то никакое дублирование или повторный запуск watch dog-ом не поможет -- каждая копия приложения будет падать в одном и том же месте с одним и тем же core dump-ом.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Павел Кузнецов, Вы писали:
V>> Действиетльно. Ну, тебе виднее. Правда дотнетные и явовские приложение V>> которые с этой твоей точкой зрения не согласны наботают все же надежнее,
ПК>Надежнее, чем что? Если, чем C++ (делая догадки по остальным сообщениям ПК>в данной теме), то это просто неверно: они по определению не могут работать ПК>надежнее, чем их виртуальные машины, которые как раз на C++ и написаны.
Паша, извини, что вмешиваюсь, но а IT есть масса примеров построения надежных систем из ненадежных компонентов. Тебя же не удивляет reliability TCP, построенного поверх абсолютно unreliable IP?
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Cyberax,
C>> Явная спецификация исключений — это, ИМХО, большой плюс.
ПК>Как я понимаю, вопрос достаточно спорный... Это без труда обнаруживается простым запросом ПК>java checked exceptions, даже не содержащим ничего кроме самого ПК>термина:
Herb Sutter и Andrei Alexandrescu в книге C++ Coding Standards: 101 Rules, Guidelines, and Best Practices
пункт 75 Avoid exception specifications разделяют твой взгляд на вещи.
Sinclair,
V>>> Действиетльно. Ну, тебе виднее. Правда дотнетные и явовские приложение V>>> которые с этой твоей точкой зрения не согласны наботают все же V>>> надежнее,
ПК>> Надежнее, чем что? Если, чем C++ (делая догадки по остальным ПК>> сообщениям в данной теме), то это просто неверно: они по определению ПК>> не могут работать надежнее, чем их виртуальные машины, которые как раз ПК>> на C++ и написаны.
S> Паша, извини, что вмешиваюсь, но а IT есть масса примеров построения S> надежных систем из ненадежных компонентов. Тебя же не удивляет S> reliability TCP, построенного поверх абсолютно unreliable IP?
Good point. Значит, не по определению, а по факту.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, EvilChild, Вы писали:
EC>Здравствуйте, Павел Кузнецов, Вы писали:
ПК>>Cyberax,
C>>> Явная спецификация исключений — это, ИМХО, большой плюс.
ПК>>Как я понимаю, вопрос достаточно спорный... Это без труда обнаруживается простым запросом ПК>>java checked exceptions, даже не содержащим ничего кроме самого ПК>>термина:
EC>Herb Sutter и Andrei Alexandrescu в книге EC>C++ Coding Standards: 101 Rules, Guidelines, and Best Practices EC>пункт 75 Avoid exception specifications разделяют твой взгляд на вещи.
Есть большая разница между спецификаций исключений как в C++ и как в Java. В C++ исключения проверяются на этапе выполнения (в момент их возникновения), в Java -- на этапе компиляции.
Здравствуйте, dshe, Вы писали:
EC>>Herb Sutter и Andrei Alexandrescu в книге EC>>C++ Coding Standards: 101 Rules, Guidelines, and Best Practices EC>>пункт 75 Avoid exception specifications разделяют твой взгляд на вещи.
D>Есть большая разница между спецификаций исключений как в C++ и как в Java. В C++ исключения проверяются на этапе выполнения (в момент их возникновения), в Java -- на этапе компиляции.
В C++, вроде как, компилятор обязан обрамить вызов метода со спецификацией исключения try{}catch{}'ем.
Здравствуйте, eao197, Вы писали:
E>Поэтому в моем представлении приложение состоит из большого количества уровней. При выполнении какой-либо операции управление передается сверху вниз. При этом каждый уровень может: E>1) либо ожидать исключений чтобы предпринять попытку исправить ситуацию каким-либо иным образом (т.е. запустить другую последовательность транзакций); E>2) либо ожидать исключений чтобы очистить свои ресурсы, восстановить свое начальное/конечное/специальное состояние и отдать исключение наружу (т.е. предоставлять базовую гарантию по отношению к исключениям); E>3) либо ничего не ожидать и ничего не очищать (т.е. быть нейтральным по отношению к исключениям).
Очень хорошо. Я вот подумываю — возможно ли изобрести платформу, которая бы гарантировала поведение 2 встроенным образом? Пусть повышенные затраты — зато 100% уверенность в корректном результате.
Т.е. примерно вот так:
/// checked C# code samplestring s = "We are going to greet";
try
{
foreach(Person p in Holidayers)
{
s+= " " + p.Name + ","
}
}
catch
{
// перед входом в обработчик все объекты возвращаются в то состояние, которое они имели перед try.
s+= " everyone";
// поэтому теперь s = "We are going to greet everyone";
}
Console.WriteLn(s);
Насколько это реализуемо? Насколько это будет востребовано?
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Насколько это реализуемо?
В императивных языках с трудом. Причем с большим. Так как прийдется востаналивать не переменные, а состояние объектов. Для этого нужно или иметь автоматическое клонирование или вообще вести некий транзакционный механизм.
Дело бы упростилось бы если объекты были бы неизменяемыми. тогда достаточно было бы востановить ссылки как в тоем примере со строкой.
S>Насколько это будет востребовано?
Ну, это мечата очень многий. Декларативне тык-сызыть транзакции в коде. Это позволило бы писать очень устойчивый к сбоям код.
Вот только для этого нужно видимо язык специльно затачивать.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Sinclair wrote:
> Очень хорошо. Я вот подумываю — возможно ли изобрести платформу, > которая бы гарантировала поведение 2 встроенным образом? Пусть > повышенные затраты — зато 100% уверенность в корректном результате.
Встроенным — вряд ли. А вот обеспечить для программиста удобные методы
достижения этой цели — вполне.
Такое поведение называется strong exception guarantee в С++. Правда пока
за его соблюдением приходится следить руками, но сейчас комитет как раз
думает об усовершенствованиях в работе с исключениями.
Здравствуйте, Sinclair, Вы писали:
E>>2) либо ожидать исключений чтобы очистить свои ресурсы, восстановить свое начальное/конечное/специальное состояние и отдать исключение наружу (т.е. предоставлять базовую гарантию по отношению к исключениям);
S>Очень хорошо. Я вот подумываю — возможно ли изобрести платформу, которая бы гарантировала поведение 2 встроенным образом? Пусть повышенные затраты — зато 100% уверенность в корректном результате. S>Т.е. примерно вот так: S>
S>/// checked C# code sample
S>string s = "We are going to greet";
S>try
S>{
S> foreach(Person p in Holidayers)
S> {
S> s+= " " + p.Name + ","
S> }
S>}
S>catch
S>{
S> // перед входом в обработчик все объекты возвращаются в то состояние, которое они имели перед try.
S> s+= " everyone";
S> // поэтому теперь s = "We are going to greet everyone";
S>}
S>Console.WriteLn(s);
S>
S>Насколько это реализуемо?
Без особых проблем (на нормальных языках ), но чуточку многословно:
#include <iostream>
#include <string>
#include <stdexcept>
template< class T >
class value_guard_t
{
T & holder_;
T old_;
bool committed_;
public :
value_guard_t( T & holder )
: holder_( holder )
, old_( holder )
, committed_( false )
{}
~value_guard_t()
{
if( !committed_ )
holder_ = old_;
}
void
commit()
{
committed_ = true;
}
};
void
throwable_action( std::string & a )
{
a = "This is damaged content!";
throw std::runtime_error( "Something going wrong!" );
}
void
try_to_be_exception_safe()
{
std::string s( "We are going to greet" );
try
{
value_guard_t< std::string > s_guard( s );
throwable_action( s );
s_guard.commit();
}
catch( const std::exception & x )
{
std::cerr << "Oops! Exception: " << x.what() << std::endl;
s += " everyone";
}
std::cout << "And now: " << s << std::endl;
}
int
main()
{
try_to_be_exception_safe();
}
S> Насколько это будет востребовано?
Как я показал, в C++ такая возможность есть и она используется там, где это необходимо.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
VladD2 wrote:
> S>Насколько это будет востребовано? > Ну, это мечата очень многий. Декларативне тык-сызыть транзакции в > коде. Это позволило бы писать очень устойчивый к сбоям код. > Вот только для этого нужно видимо язык специльно затачивать.
Уже сделано два года назад в JBoss — там как раз есть декларативные
транзакции на графах объектов.
Здравствуйте, VladD2, Вы писали: VD>В императивных языках с трудом. Причем с большим. Так как прийдется востаналивать не переменные, а состояние объектов.
Совершенно верно. VD>Для этого нужно или иметь автоматическое клонирование или вообще вести некий транзакционный механизм.
Причем вроде как технологии-то сами по себе наработаны — в СУБД, к примеру, все это уже есть. Правда они ой как не любят частичных откатов. Написание кода типа моего примера должно было бы приводить к построению стека сэйвпоинтов. Впрочем, обработка исключений и сейчас штука небесплатная. VD>Дело бы упростилось бы если объекты были бы неизменяемыми. тогда достаточно было бы востановить ссылки как в тоем примере со строкой.
Не, так ничего полезного, скорее всего, не выйдет. Хотя мысль не лишена некоторой изящности. Я себе с трудом представляю систему, где все объекты immutable — тогда и ссылки-то восстанавливать будет негде VD>Ну, это мечата очень многий. Декларативне тык-сызыть транзакции в коде. Это позволило бы писать очень устойчивый к сбоям код. VD>Вот только для этого нужно видимо язык специльно затачивать.
Именно. Вот я и думаю, как это сделать . Что, собственно, нужно для такого языка? Грубо говоря, поддержка лога и трассировка всего подряд. С умным статическим анализом, который позволяет не хранить в лог лишнего в случае гарантии неотката...
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
E>void E>throwable_action( std::string & a ) E> { E> a = "This is damaged content!";
E> throw std::runtime_error( "Something going wrong!" ); E> }
E>void E>try_to_be_exception_safe() E> { E> std::string s( "We are going to greet" ); E> try E> { E> value_guard_t< std::string > s_guard( s );
E> throwable_action( s );
E> s_guard.commit(); E> } E> catch( const std::exception & x ) E> { E> std::cerr << "Oops! Exception: " << x.what() << std::endl; E> s += " everyone"; E> }
E> std::cout << "And now: " << s << std::endl; E> }
E>int E>main() E> { E> try_to_be_exception_safe(); E> } E>[/ccode]
S>> Насколько это будет востребовано?
E>Как я показал, в C++ такая возможность есть и она используется там, где это необходимо.
Эта возможность требует написания большого количества мусорного кода. Достаточно забыть сделать commit у одного гарда перед концом try-блока, как все сломается, причем плохо предсказуемым образом. Можно, конечно, привязять всех обороняемых к одному гарду, но это существенно усложнит жизнь всем. Кроме того, твой вариант полагается на корректное поведение конструктора копирования (я правильно понял?). Малейшая неточность в поведении этого конструктора — и мы неделями ловим причины сверхестественного поведения.
Не думаю, что нам так уж легко удастся написать универсальный guard для всех случаев.
Я-то думаю насчет встраивания такого паттерна в язык.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
E>>Как я показал, в C++ такая возможность есть и она используется там, где это необходимо. S>Эта возможность требует написания большого количества мусорного кода.
Надеюсь, под мусорным понимается не код самих guard-ов, а указание guard-ов внутри try/catch с последующим вызовом commit-а. Да, такое дело есть. Но, с другой стороны, это явное указание в коде того факта, что здесь мы пытаемся обеспечить гарантию безопасности по отношению к исключениями.
S> Достаточно забыть сделать commit у одного гарда перед концом try-блока, как все сломается,
Не думаю, что сломается.
S> причем плохо предсказуемым образом.
Наоборот, при первом же тестовом прогоне мы обнаружим у переменной неправильное (начальное) значение.
S> Можно, конечно, привязять всех обороняемых к одному гарду, но это существенно усложнит жизнь всем.
Не думаю. Можно сделать композитный guard. Что-то типа:
S> Кроме того, твой вариант полагается на корректное поведение конструктора копирования (я правильно понял?). Малейшая неточность в поведении этого конструктора — и мы неделями ловим причины сверхестественного поведения.
Именно так. Но я вообще слабо представляю себе попытку написать что-то сложное без предположения о корректности конструктора копирования.
S>Не думаю, что нам так уж легко удастся написать универсальный guard для всех случаев.
Имхо, это и не нужно. Для сложных случаев вполне можно использовать локальные классы:
S>Я-то думаю насчет встраивания такого паттерна в язык.
А я хотел показать, что такой паттерн, имхо, является одним из случаев паттерна RAII.
И при этом программист прекрасно понимает, какую цену он заплатит за обеспечение сильной гарантии. И если цена его не будет устраивать, он может выбрать иной путь работы в случае исключений. А вот если подобный паттерн встроить в язык, то не факт, что у разработчика подобный выбор будет вообще.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, EvilChild, Вы писали:
EC>Здравствуйте, dshe, Вы писали:
EC>>>Herb Sutter и Andrei Alexandrescu в книге EC>>>C++ Coding Standards: 101 Rules, Guidelines, and Best Practices EC>>>пункт 75 Avoid exception specifications разделяют твой взгляд на вещи.
D>>Есть большая разница между спецификаций исключений как в C++ и как в Java. В C++ исключения проверяются на этапе выполнения (в момент их возникновения), в Java -- на этапе компиляции.
EC>В C++, вроде как, компилятор обязан обрамить вызов метода со спецификацией исключения try{}catch{}'ем.
прекрасно откомпилится, но при вызове функции func2() программа благополучно скорится, поскольку исключение, возникшее в func1(), не может выйти за пределы func2(), так как она, объявлена как небросающая исключения в принципе. C++ компилятор не дает проверок на этапе компиляции, а генерит код, который делает проверки на этапе выполнения.
С другой стороны Java не позволяет скомпилировать такой код:
Здравствуйте, eao197, Вы писали: E>Надеюсь, под мусорным понимается не код самих guard-ов, а указание guard-ов внутри try/catch с последующим вызовом commit-а. Да, такое дело есть. Но, с другой стороны, это явное указание в коде того факта, что здесь мы пытаемся обеспечить гарантию безопасности по отношению к исключениями.
Конечно. Мы же рассматриваем прикладной код. S>> Достаточно забыть сделать commit у одного гарда перед концом try-блока, как все сломается, S>> причем плохо предсказуемым образом. E>Наоборот, при первом же тестовом прогоне мы обнаружим у переменной неправильное (начальное) значение.
Это только в том случае, если тестовый прогон действительно меняет значение переменной. И если мы действительно проверяем значение каждой переменной, а не только тех, которые попадают "наружу". Офф: У меня был великолепнейший случай бага с преобразованиями часовых поясов — пояс вычитался, вместо того, чтобы прибавляться. К сожалению, у нас GMT +6, и время получалось правильное. Только когда код стали тестировать при GMT +11 все и обнаружилось.
E>Не думаю. Можно сделать композитный guard. Что-то типа: E>
Именно это я и имел в виду.
E>Имхо, это и не нужно. Для сложных случаев вполне можно использовать локальные классы:
Ага. Ну то есть мусорный код, который еще и пишется в несимметричном основному коду месте. E>А я хотел показать, что такой паттерн, имхо, является одним из случаев паттерна RAII. E>И при этом программист прекрасно понимает, какую цену он заплатит за обеспечение сильной гарантии. И если цена его не будет устраивать, он может выбрать иной путь работы в случае исключений. А вот если подобный паттерн встроить в язык, то не факт, что у разработчика подобный выбор будет вообще.
А его и не надо. Этот выбор делается при выборе языка. Не знаю правда, что хуже — делать язык с уникальным синтаксисом, или наоборот — со строго совместимым. Во втором случае, программист может просто перекомпилировать куски своего кода с C# на "C##" и радоваться. Зато при этом есть риск запутаться — совершенно одинаковый код будет иметь различную семантику. Например, вот такое вот чудо на "C##" приведет совсем не к тому результату, который ожидался от C#:
public static void Fail()
{
string step = null;
try
{
a = "step1";
//blablabla
a = "step2";
throw new Exception("Shift happens");
}
catch(e)
{
Log.Trace(e, step);
throw;
}
}
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>>> Достаточно забыть сделать commit у одного гарда перед концом try-блока, как все сломается, S>>> причем плохо предсказуемым образом. E>>Наоборот, при первом же тестовом прогоне мы обнаружим у переменной неправильное (начальное) значение. S>Это только в том случае, если тестовый прогон действительно меняет значение переменной. И если мы действительно проверяем значение каждой переменной, а не только тех, которые попадают "наружу". Офф: У меня был великолепнейший случай бага с преобразованиями часовых поясов — пояс вычитался, вместо того, чтобы прибавляться. К сожалению, у нас GMT +6, и время получалось правильное. Только когда код стали тестировать при GMT +11 все и обнаружилось.
Твой аргумент ничего не доказывает. А наоборот, показывает необходимость тестирования в максимально приближенным к боевым условиям.
E>>Имхо, это и не нужно. Для сложных случаев вполне можно использовать локальные классы: S>Ага. Ну то есть мусорный код, который еще и пишется в несимметричном основному коду месте.
Так ведь локальный класс guard-а это уже предельный случай. А ведь можно пользоваться более удобными средствами, например: Change the Way You Write Exception-Safe Code — Forever
E>>А я хотел показать, что такой паттерн, имхо, является одним из случаев паттерна RAII. E>>И при этом программист прекрасно понимает, какую цену он заплатит за обеспечение сильной гарантии. И если цена его не будет устраивать, он может выбрать иной путь работы в случае исключений. А вот если подобный паттерн встроить в язык, то не факт, что у разработчика подобный выбор будет вообще. S>А его и не надо. Этот выбор делается при выборе языка.
Имхо, это слишком экстремальная точка зрения.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Sinclair, Вы писали:
S>Насколько это реализуемо? Насколько это будет востребовано?
ИМХО внутри одного метода это уже перебор, потому что не очень хорошо формализуемо. А вот на границах методов очень даже. Если внутри метода (помеченного атрибутом) произошло необработанное исключение, то нужно вернуть состояние нелокальных объектов обратно. Остается проблема неотслеживаемых автоматически изменений, но ее можно решить.
Здравствуйте, AndrewVK, Вы писали: AVK>ИМХО внутри одного метода это уже перебор, потому что не очень хорошо формализуемо. А вот на границах методов очень даже. Если внутри метода (помеченного атрибутом) произошло необработанное исключение, то нужно вернуть состояние нелокальных объектов обратно. Остается проблема неотслеживаемых автоматически изменений, но ее можно решить.
Об этом я тоже думал, хотя и не очень долго. Дело в том, чтобы писать как раз код первого типа (по приведенной выше классификации). Т.е. мы сначала пытаемся пойти по одной ветке, и в случае проблем откатываемся и пробуем другую.
Вот как раз попробование другой ветки (catch) требует некоторого предсказуемого состояния системы. Например, того, которое было до try. Поэтому у меня и появляется мысль сделать откат привязанным именно к try/catch.
Граница метода не очень хорошо в этом смысле подходит. Можно, конечно, использовать и ее, но тогда вместо
Здравствуйте, Sinclair, Вы писали:
S>Об этом я тоже думал, хотя и не очень долго. Дело в том, чтобы писать как раз код первого типа (по приведенной выше классификации). Т.е. мы сначала пытаемся пойти по одной ветке, и в случае проблем откатываемся и пробуем другую.
Можно, но во-первых это всегда можно оформить ввиде методов, а во-вторых управление транзакциями в таком случае лучше сделать явным.
S>Граница метода не очень хорошо в этом смысле подходит. Можно, конечно, использовать и ее, но тогда вместо
S>
VladD2 wrote:
> C>Уже сделано два года назад в JBoss — там как раз есть декларативные > C>транзакции на графах объектов. > Это специализированное решение для узкого круга задач. Попробуй там > вэлью-типы откатить или даже объекты не унаследованные от какй-нить хрени.
Ничего не нужно наследовать — JBoss инструментирует байт-код, так что
все работает прозрачно (если, конечно, явно не пытаться испортить себе
жизнь).
Причем поддерживается _прозрачное_ распространение транзакций на базы
данных или другие ресурсы (для которых есть коннекторы), так что
становится очень удобно писать бизнес-логику.
Здравствуйте, Cyberax, Вы писали:
C>AndrewVK wrote:
>> ПК>-1. При недостаче памяти в C++ вылетает std::bad_alloc. >> Даже если памяти не хватило внутри апишной функции?
C>При недостатке памяти АПИшные функции будут возвращать код ошибки.
Который лучше сразу конвертить в std::bad_alloc для для однообразности работы и в соответствии с соглашениями стандартной библиотеки C++.
Здравствуйте, VladD2, Вы писали:
ПК>>или (вот здесь я уже не вполне уверен, можно ли на Java так сделать -- ПК>>если нет, тем хуже для Java ): ПК>>
ПК>>Optional<Person>
ПК>>findPersonByName( String name );
ПК>>
ПК>>И не нужно никаких лишних комментариев и, тем более, исключений.
VD>Кстати, в C# с большой вероятностью, а в Яве так на 100% Person будет ссылочным типом, так что даже Person? и т.п. не потребуется. Можно просто возвращать null если человека нет.
Возвращать просто null вместо оссылки на какой-либо объект в Java — это плохая практика.
Во-первых, с большей долей вероятности в рантайме произойдёт unchecked-исключение java.lang.NullPointerException, которое будет брошено в вызывающем методе или немного выше (если вызывающий метод является "трансфером объекта").
Во-вторых, для обработки возвращаемого значения (null/not null) придётся писать дополнительный код в вызывающем методе, проверяющий, равна ли ссылка null.
В-третьих, понадобиться обязательно заглянуть в документацию по спецификации вызываемого метода и возвращаемого значения. (Вот удивиться прикладной программист, когда узнает, что метод иногда возвращает "ничего". Не правда ли, занакомое состояние, когда программируешь на C#? )
В случае с объявленным checked-исключением или с применением паттерна NullObject в вызываемом коде таких проблем не возникает: вызывающий код пишется гладко — если возникает checked-исключение, то оно замечается на этапе компиляции и, естественно обрабатывается/пересылается; если используется NullObject — это ещё лучше, работа с исключительной ситуацией не требуется.
p.s. Навеяно книжками:
1. Джошуа Блох "Эффективное программирование", статья 27 "Возвращайте массив нулевой длины, а не null" (то же самое и по строками);
2. Стивен Стелтинг "Java без сбоев: обработка исключений, тестирование, отладка". — кстати один из авторов книжки "Применение шаблонов Java".
По словам Мартина Фаулера (из книги "Рефакторинг: улучшение существующего кода") паттерн NullObject впервые заметил Рон Джеффриз. Вот небольшая выдержка из статьи:
Мы впервые стали применять паттерн нулевого объекта (NullObject), когда Рич Гарзанити обнаружил, что код системы часто проверяет, существует ли объект, прежде чем послать ему сообщение (здесь: "сообщение" — метафора вызова метода в ООП). Мы запрашивали у объекта его метод person, а затем сравнивали результат с null. Если объект присутствовал, мы запрашивали у него метод rate. Это делалось в нескольких местах, и повторение кода в них стало нас раздражать.
Поэтому мы создали объект отсутсвующего лица, который сообщал, что у него нулевой (не null!! — Прим. моё) rate (мы называем наши null-объекты существующими объектами). Вскоре у отсутствующего лица было уже много методов. Сейчас у нас уже больше 80 классов нулевых объектов.
Чаще всего нулевые объекты мы применяем при выводе информации. Например, когда выводится информация о лице, то у соответствующего объекта может отсутствовать любой из примерно 20 атрибутов. Если бы они могли принимать значение null, вывод информации о лице стал бы очень сложным. Вместо этого мы подключаем различные нулевые объекты, которые умеют отображать себя правильным образом. В результате мы избавились от большого объёма процедурного кода.
<...дальше идёт рассказ о применении NullObject's для тестовых транзакций в БД и коллекции бухгалтерских данных...>
<...>
Интересная особенность применения нулевых объектов состоит в том, что почти никогда не возникают аварийные ситуации. Поскольку нулевой объект отвечает на те же сообщения, что и реальный объект, система в целом ведёт себя обычным образом. Из-за этого иногда трудно локализовать проблему, потому что всё работает нормально.
<...>
Помните, нулевые объекты постоянны — в них никогда ничего не меняется. Соответственно, мы реализуем их по паттерну "Одиночка" (Singleton pattern). Например, при каждом запросе отсутствующего лица вы будете получать один и тот же экземпляр этого NullObject-класса.
У Мартина Фаулера в книжке как раз и объяснено на примерах где, когда и как желательно применять паттерн NullObject.
Здравствуйте, Sinclair, Вы писали:
S>Офф: У меня был великолепнейший случай бага с преобразованиями часовых поясов — пояс вычитался, вместо того, чтобы прибавляться. К сожалению, у нас GMT +6, и время получалось правильное. Только когда код стали тестировать при GMT +11 все и обнаружилось.
Офф: а какой у меня был интереснейший баг, который проявляется только 31 декабря високосного года... Вот как раз в прошлом году и пришлось по-стахановски поработать, чтоб выдать клиентам заплатку, которая только на этот день и нужна.
C>С chaining'ом все просто — одно исключение "вкладывается" в другое, то C>есть получается цепочка исключений. Типа SQLException был вызван C>NullPointerException'ом, который был вызван OutOfBoundsException'ом. С C>jdk1.4 это стандартная фича.
Это типа
catch(SqlException ex)
{
throw new MyException("message", ex);
}
Так это уже есть в шарпе. Можно по InnerException разбирать кому стало плохо.
...Ei incumbit probatio, qui dicit, non qui negat...
Здравствуйте, VladD2, Вы писали:
E>>Вот собственно об этом я и говорил. Спецификация исключений, имхо, в теории полезная штука. Но вот в C++ и Java она реализована, имхо, неудачно.
VD>Согласен с одной оговоркой. Описание исключений генерируемых методом должен делать компилятор. Причем без единой подскзки. Вся информация у него есть. А я как потребитель должен иметь возможность легко узнать список исключений которые может выдать тот или иной метод.
в случае, когда код — удаленный, компилятору доступна только спецификация (контракт, интерфейс). я к тому, что спецификация метода — первична, ибо картину для случаев, когда ее первичностью можно пренебречь, перечеркивает хотя бы один "неудобный" пример.
Здравствуйте, C0s, Вы писали:
C0s>в случае, когда код — удаленный, компилятору доступна только спецификация (контракт, интерфейс). я к тому, что спецификация метода — первична, ибо картину для случаев, когда ее первичностью можно пренебречь, перечеркивает хотя бы один "неудобный" пример.
Откровенно говоря список исключений это приятный бенефит. Лично я вообще не заморачиваюсь с ним. Если мне нужно обработать некое исключение, то я о нем уже знаю. А иначе просто не нужно их обрабатывать или обрабатывать все (например, выводить в лог).
Тут нужно сравнивать бенефит от наличия исчерпывающего списка и проблемы возникающие в следствии этого. Есть мнение, что проблем куда больше.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Checked exceptions. Правда это никак не мешает сайту работать годами без перезагрзки и даже модифицироваться на ходу.
Здравствуйте, ironwit, Вы писали:
VD>>Checked exceptions. Правда это никак не мешает сайту работать годами без перезагрзки и даже модифицироваться на ходу.
I>можно примерно сказать как? для чайника
Можно. Отчего же нельзя? Руками напимсал. На C#. Вот в ближайшее время хочет переписать теми же рукаи на Nemerle.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, ironwit, Вы писали:
I>>можно примерно сказать как? для чайника
VD>Можно. Отчего же нельзя? Руками напимсал. На C#. Вот в ближайшее время хочет переписать теми же рукаи на Nemerle.
Влад, ты точно программист. Ответ абсолютно верен но абсолютно не несет смысловой нагрузки
Здравствуйте, ironwit, Вы писали:
I>Влад, ты точно программист. Ответ абсолютно верен но абсолютно не несет смысловой нагрузки
Какой вопрос таков и ответ. Вообще я рассчитывал на наличие чувства юмора у собеседника.
I>А можно в общих словах технологию?
Собственно расказывать особо не очем. Сначала был W2K, ASP, Jet, VS2003. Потом W2003, .Net 1.х, MS SQL 2000, VS2003. Потому W2003, .Net 2.0, MS SQL 2005, VS2005. Плюсь IT-шный Тулкит потихоничку использоваться начал.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.