Здравствуйте, GlebZ, Вы писали:
GZ>Нет. Как раз у него и срабатывают assert на этапе разработки. Собственно он и есть пользователь assert. В дальнейшем они не нужны.
Наоборот. Посмотрите на эту ситуацию "наизнанку" То что ассерт размещен в его коде — не означает что в его срабатывании виноват он. Ошибка снаружи.
Пример.
Я разработал компонент, который предназначен для вычисления квадратного корня из числа:
Я ЗАПРЕТИЛ впихивать внутрь моего компонента отрицательные числа. Для того чтобы мой запрет не был очередным "последним китайским предупреждением", я сказал, что тот кто попытается впихнуть в мой компонент отрицательное число, тот пожалеет об этом — здорово получит по мозгам, так как MySuperCalculator.Sqrt(x), я "заминировал ассертом" на проверку предусловия ASSERT(x >= 0)
Вот исходный код моего модуля:
MODULE MySuperCalculator;
IMPORT Math;
PROCEDURE Sqrt* (x: REAL): REAL;
BEGIN
ASSERT(x >= 0, 20); (* <-- это оборона от внешних по отношению к моему компоненту ошибок *)
RETURN Math.Sqrt(x)
END Sqrt;
END MySuperCalculator.
Этот ассерт направлен не против меня, а против других программистов, которые будут использовать мой компонент.
Re[13]: Sqrt - хороший пример компонента обороняющегося от о
MS>>ASSERT просто обрушит программу не позволяя сохранить данные.
СГ>Это смотря какую программу. На Си/С++ — легко. В Блэкбоксе на Component Pascal — ничего подобного, там просто снимется выполнение этой ошибочной команды. Данные никуда пропадать от этого не обязаны — смотря как напишешь. Но не зависимо от этого, в любом случае — сработавший ассерт — это ошибка в программе и её надо переписывать.
Ладно я понимаю, как можно "снять выполнение ошибочной комманды" если значение Sqrt() нигде не сохраняется.
А если Sqrt() используется в выражении, то как можно "снять выполнение ошибочной комманды"?
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Этот ассерт направлен не против меня, а против других программистов, которые будут использовать мой компонент.
Ага. А теперь посмотрим тот же код.
double MySqrt(double x)
{
ASSERT(x>0);//это внутренняя ошибка программиста пишущего модуль.
//при ее срабатывании поднимается отладчик, и этот программист
//может посмотреть что случилосьif (x>0)
throw new MyException("вы кормите MySqrt отрицательными значениями");//а вот это
//сообщение для некорректного программиста модуля
//ему будет вполне понятно что случилосьreturn Math.Sqrl(x);
}
А теперь представь себе что это рассчет матриц в полумегабайтном коде и ошибка произошла на 10 уровне вложенности.
void CountMatrix()
{
try
{
}
catch(MyException e)
{
Logger.Write(e);
throw new MyException("Произошла внутренняя ошибка. Бери трубку и звони производителям", e);
}
}
Теперь понятна разница между ASSERT и EXCEPTION? Исключение мы можем представить пользователю в понятном виде, ассерт — никогда.
С уважением, Gleb.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[14]: Sqrt - хороший пример компонента обороняющегося от о
Здравствуйте, MShura, Вы писали:
MS>Ладно я понимаю, как можно "снять выполнение ошибочной комманды" если значение Sqrt() нигде не сохраняется. MS>А если Sqrt() используется в выражении, то как можно "снять выполнение ошибочной комманды"?
Как снять выполнение команды не потеряв при этом данные?
Интерфейс модуля:
DEFINITION MyModule;
PROCEDURE Command1;
PROCEDURE Command2
END MyModule.
Исходный текст модуля:
MODULE MyModule;
IMPORT
...
TYPE
MyData = ...
VAR
data: MyData;
PROCEDURE Init;
BEGIN(* инициализация данных *)END Init;
PROCEDURE Command1*;
BEGIN(* код команды 1, выполняющей операцию с данными data *)END Command1;
PROCEDURE Command2*;
BEGIN(* код команды 2, выполняющей операцию с данными data *)END Command2;
PROCEDURE Close;
BEGIN(* закрытие данных *)END Close;
BEGIN
Init
CLOSE
Close
END MyModule.
Выполняя процедуры: MyModule.Command1, MyModule.Command2 как команды, мы не потеряем данные data даже если выполнение этих команд будет аварийно завершено.
Команда — это средство предоставляемое системой. Фактически это есть запуск процедуры по её (текстовому) имени "MyModule.MyCommand" (допустимые типы аргументов процедуры предопределены системой, в частности — любую процедуру без параметров можно выполнять как команду). Запуск команды делается системой внутри системной ловушки, так что если внутри процедуры происходит "авария", то "исключение" ловится системной ловушкой. Это всё равно что в Windows/Linux в командной строке набрать имя программы и нажать Enter — программа будет запущена по имени, а в оберон-системах, в любом текстовом документе пишете имя процедуры: MyModule.MyCommand, выделяете мышью и нажав на соответсвующий пункт меню запускаете эту процедуру на выполнение как команду (разумеется есть масса других способов инициировать выполнение команды, в том числе и в "фоновом" режиме — не одной же коммандной строкой ограничиваться). Если модуль MyModule не был загружен в память, то он автоматически загружается. Выгрузить его можно потом выполнив другую специальную системную команду. Команда в оберон-системе в этом смысле похожа на программу в Windows/Linux. Снять выполнение текущей команды (если она что-то долго выполняется и быть может зависла) можно нажав на клавиатуре кнопку Break (аналог Ctrl+Alt+Del в Windows).
Здравствуйте, GlebZ, Вы писали:
GZ>Теперь понятна разница между ASSERT и EXCEPTION? Исключение мы можем представить пользователю в понятном виде, ассерт — никогда.
Это у Вас в С++ так, а у нас в Component Pascal подругому — exteption нету, а вместо них есть ASSERT
Ну, а что касается пользователя, то единственный понятный вид сообщения об ошибке для него таков:
"В программе обнаружена ошибка, поэтому использование программы далее — бессмысленно. Обратитесь к разработчику."
всё остальное ему до лампочки...
Re[15]: Sqrt - хороший пример компонента обороняющегося от о
Здравствуйте, Сергей Губанов, Вы писали:
MS>>Ладно я понимаю, как можно "снять выполнение ошибочной комманды" если значение Sqrt() нигде не сохраняется. MS>>А если Sqrt() используется в выражении, то как можно "снять выполнение ошибочной комманды"?
СГ>Как снять выполнение команды не потеряв при этом данные?
СГ>Команда в оберон-системе в этом смысле похожа на программу в Windows/Linux. Снять выполнение текущей команды (если она что-то долго выполняется и быть может зависла) можно нажав на клавиатуре кнопку Break (аналог Ctrl+Alt+Del в Windows).
Интересно.
А если эта команда вызывает 10 других команд, или рекурсивно саму себя, что именно будет прервано? Как поведут себя внешние по отношению к этой команды?
Что будет, если вызывающий ожидает от команды возвращаемого значения (через var-параметр или результат функции), как ему продолжить выполнение, если результата он не получил? Как он узнает, что было прерывание и результат не получен?
Здравствуйте, Pzz, Вы писали:
Pzz>Что должна делать программа марсохода при обнаружении _внутренней Pzz>ошибки_ на Марсе, когда ASSERT отключен (а ошибка не отключена)?
А если ASSERT включен на Марсе, что тогда, легче будет?
Я бы не применял в таких программах вообще ASSERT и проектировал бы с таким важным условием.
Здравствуйте, Cyberax, Вы писали:
>> Что должна делать программа марсохода при обнаружении _внутренней >> ошибки_ на Марсе, когда ASSERT отключен (а ошибка не отключена)?
C>Перезагружать систему, после N перезагрузок входить в безопасный режим.
...командировать специалиста...
Re[16]: Sqrt - хороший пример компонента обороняющегося от о
Здравствуйте, Кодёнок, Вы писали:
Кё>Интересно.
Кё>А если эта команда вызывает 10 других команд, или рекурсивно саму себя, что именно будет прервано? Как поведут себя внешние по отношению к этой команды?
Кё>Что будет, если вызывающий ожидает от команды возвращаемого значения (через var-параметр или результат функции), как ему продолжить выполнение, если результата он не получил? Как он узнает, что было прерывание и результат не получен?
1) Это процедура может вызвать саму себя, но это будут вызовы в контексте одной и той же команды.
2) Никакого возвращаемого значения у команды нет.
В Обероне задачи бывают двух типов: интерактивные и фоновые. Команда — это интерактивная задача, её исполнение инициирует непосредственно пользователь (отдаёт команду — отсюда и название). Фонововые задачи запускает (ставит в очередь исполнения) либо сам пользователь, либо другие задачи (команды), в том числе и фоновые. Ситуация описанная Вами, по всей видимости, решается через фоновые задачи. Например так: одна задача ставит в очередь задач другую и засыпает, потом просыпается и смотрит что произошло. В Блэкбоксе фоновые задачи представлены расширениями типа Services.Action
DEFINITION Services;
CONST
immediately = -1;
now = 0;
resolution = 1000;
TYPE
Action = POINTER TO ABSTRACT RECORD
(a: Action) Do-, NEW, ABSTRACT
END;
...
PROCEDURE DoLater (a: Action; notBefore: LONGINT);
PROCEDURE RemoveAction (a: Action);
...
PROCEDURE Ticks (): LONGINT;
...
END Services.
Метод Action.Do — это квант работы задачи (многозадачность в Блэкбоксе кооперативная), он выполняется в окружении системной ловушки и если в нём произойдёт авария, то эта задача будет снята. Задача должна каждый раз себя устанавливать заново после каждого "такта" своей работы: Services.DoLater(task, Services.Ticks() + milliseconds);
Re[17]: Sqrt - хороший пример компонента обороняющегося от о
Здравствуйте, minorlogic, Вы писали:
M>Тогда подрезюмирую.
M>1. Разработчики внесли ASSERT в свои библиотеки подразумевающий другую идеологию использования, чем это общепринято испторически.
M>2. НИКАКИЕ выводы сделанные о использовании ASSERT в BlackBox нельзя перносить на другие языки/платформы.
M>3. Стыд разработчикам , что они использовали такое узнаваемое слово как "ASSERT" в ключе отличном от общепринятого.
Стыд и позор также Стиву Макконелу за такие вот мысли:
По мере возможности делайте интерфейсы программными, а не семан-
тическими Каждый интерфейс состоит из программной и семантической
частей. Первая включает типы данных и другие атрибуты интерфейса, которые могут
быть проверены компилятором. Вторая складывается из предположений об ис-
пользовании интерфейса, которые компилятор проверить не может. Семантический
интерфейс может включать такие соображения, как «Метод А должен быть выз-
ван перед Методом B» или «Метод А вызовет ошибку, если переданный в него Эле-
мент Данных 1 не будет перед этим инициализирован». Семантический интерфейс
следует документировать в комментариях, но вообще интерфейсы должны как
можно меньше зависеть от документации. Любой аспект интерфейса, который не
может быть проверен компилятором, является потенциальным источником оши-
бок. Старайтесь преобразовывать семантические элементы интерфейса в програм-
мные, используя утверждения (assertions) или иными способами.
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
Хоар
Re[15]: Sqrt - хороший пример компонента обороняющегося от о
Здравствуйте, Курилка, Вы писали:
К>А вот такой фрагмент тебя не заставляет задуматься: К>
К>Любой аспект интерфейса, который не
К>может быть проверен компилятором, является потенциальным источником ошибок.
К>?
К>assert в рантайме не есть проверка компилятором, тебе так не кажется? К>Имхо Стиву как раз ни разу не стыд и не позор.
Я что-то переврал?
В приведенной цитате Макконнелла не содержалось совета использовать утверждения?
Или я отрицал важность статического контроля типов?
В чем претензия?
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
Хоар
Re[16]: Sqrt - хороший пример компонента обороняющегося от о
AVC>>Стыд и позор также Стиву Макконелу за такие вот мысли: AVC>>
AVC>>... Старайтесь преобразовывать семантические элементы интерфейса в програм-
AVC>>мные, используя утверждения (assertions) или иными способами.
MP>Есть практические рекомендации, как этого достигать? С примерами?
Да, есть.
См. главы 11 и 12 книги Мейера "Объектно-ориентированное конструирование программных систем".
Там вводится понятие Design By Contract (проектирование по контракту).
А одним из примеров в 12-й главе является как раз функция вычисления квадратного корня sqrt(x).
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.