Re[10]: Пример не серьезного и аналогичного ему серьезного к
От: eugals Россия  
Дата: 15.07.04 16:27
Оценка: :)
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Мысли на счет исключений:

SYG>Исключения бывают "вредные" — то есть те которые и быть-то не должны — они сигнализируют о сбое работы программы, если они возникают, значит программа написана криво; а бывают "полезные" — это те исключения, которых ожидают и обрабатывают согласно логике работы программы, они заранее были запланированы. Так вот в Component Pascal так сказать "вредные" исключения (выход индекса за пределы массива, неправильное приведение типа, не предусмотренный вариант выбора в инструкции CASE и т.п.) — просто автоматически немедленно останавливают работу программы и все — программиста на мыло!

Не, "автоматически немедленно" останавливать работу программы это полумера. Надо, чтобы она сама себя тут же и деинсталлировала и возвращала пользователю уплаченные за себя деньги, после чего "автоматически" коннектилась к сайту разработчиков и отправляла "на мыло" провинившегося программиста по имени "Component Pascal"

(шутка)
... << RSDN@Home 1.1.3 stable >>
Re[2]: Серьёзный код :)
От: KolanT  
Дата: 15.07.04 21:51
Оценка: 1 (1) :))
Здравствуйте,
Может серьезный код — код где можно сделать серьезные ошибки.
Re[11]: Пример не серьезного и аналогичного ему серьезного к
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 16.07.04 07:40
Оценка: +1 -4
Здравствуйте, WolfHound, Вы писали:

WH>Открыли фаил, поработали и забыли, открыли еще один фаил поработали и забыли... тк время уборки мусора недетерминировано открытых файлов скопится может очень много... и не исключен вариант что у системы закончатся лимит открытых файлов и все...

WH>Вот только не надо мне втирать что я должен помнить о том что я открыл фаил... Во первых я ни кому ни чего не должен, во вторых я человек и могу забыть, в третьих любимый тобой С++ помнит это за меня.

Можно поконкретнее? Что конкретно обозначают слова "забыли" и "помнит это за меня"? А то просто нету предмета для обсуждения.

WH>А что мне делать если пользователь ввел не верные данные?


Это вопрос по программированию или по какой теме? Наверное, в таких случаях надо:
1) Уж точно даже не пытаться работать с неверными данными.
2) Как можно скорее сообщить об этом пользователю, если это возможно.
3) Если пользователь не в состоянии быстро ответить, то перейти к выполнению какой-нибудь другой задачи, чтобы время не терять.

SYG>>Странный Вы. Component Pascal-ю более 10 лет между прочим.

WH>Если он такой крутой и ему уже 10 лет то почему на нем никто не пишет?
Я сам на работе на нем не пишу, но слышал, что есть такие конторы где пишут на оберонах (в основном для космоса, для беспилотных летательных аппаратов, электростанций и тому подобных вещей).

WH>А если еще учесть что исключения это классы, а их можно наследовать то допустим мы можем сделать file_exception и отнаследовать от него open_file_exception, read_file_exception... теперь нам фиолетово какое именно из этих исключений вылетело мы их все можем поймать при помощи catch(file_exception const& ex). А теперь сделай тоже самое на кодах возврата.


Во-первых, если Вам обязательно надо что-то возвращать, то ни кто не заставляет Вас возвращать именно номер_кода_ошибки. Возвращайте пожалуйста полиморфную переменную внутри которой инкапсулирована информация о причине отказа выполнить операцию — то есть все так же как и при механизме исключений — "фиолетово". Сборщик мусора поможет Вам не беспокоится о возможной растрате памяти на эти полиморфные переменные.

Во-вторых, Вы не обязаны что-то возвращать. Если Вам известно что какая-то операция потенциально опасна, то прежде чем ее выполнять, Вы "морально обязаны" проверить правильность параметров (проверить индекс массива на попадание в нужный диапазон, проверить указатель на неравенство NIL, проверить динамический тип полиморфной переменной, проверить не получится ли у Вас арифметическое переполнение, деление на ноль и т.д.) и только после того как Вы убедились в том, что данные правильные, вот только после этого приступать к выполнению потенциально опасной операции. Если в этом случае эта операция все-равно провалится, то поскольку согласно логике программы данные правильные и никакой ошибки быть не должно — это означает, что причина ошибки находится вне логики программы (кто-то отцепил жесткий диск, повредил молотком процессор и т.п.), следовательно дальнейшее выполнение программы не имеет смысла.

Вы можете возразить, что существуют некоторые потенциально опасные операции, перед выполнением которых невозможно точно знать смогут ли они быть выполнены или нет. Никакая проверка данных не может дать ответа на вопрос о том возможно ли выполнение этой операции. И вот именно в этих случаях без механизма исключительных ситуаций обойтись нельзя. Я принимаю такое возражение, соглашаюсь с ним. Однако давайте попробуем перечислить примеры таких операций:
1) Разыменование указателя не равного NIL.
2) "Насильное" приведение типов.

Подробно:
1) Никогда нельзя знать "повис" указатель или нет. Никакая проверка не может ответить на этот вопрос. Разыменование указателя — это рискованная операция. Нам остается только помолится и попробовать это сделать, в случае неудачи будет возбуждена исключительная ситуация, мы ее обработаем и все будет чики-пуки! Однако есть альтернатива — Сборщик мусора. Если в языке есть сборщик мусора, то в программе никогда не будет повисших указателей. Проблема решена. Разыменование указателя не равного NIL — абсолютно безопасна.

2) Надо всего лишь запретить "насильное" приведение типов. Точно так же как в свое время запретили инструкцию goto. Проблема решена. Разрешенные динамические преобразования типов абсолютно безопасны.

Если Вам известны еще примеры потенциально опасных операций правильный исход которых заранее проверить невозможно, то пишите, обсудим.
Re[3]: Серьёзный код :)
От: vdimas Россия  
Дата: 16.07.04 09:46
Оценка:
Здравствуйте, AndreyFedotov, Вы писали:

AF>>> Подумав над данным вопросом сам, пришёл к выводу, что для меня "серъёзный" код — это код со сложной архитектурой и множеством взаимосвязей и взаимозависимостей.


D>>Это хреновый код который не поддается пониманию и рефакторингу, такой код обычно выбрасывается на помойку после ухода программиста, писавшего его.


AF> Это если код слишком сложен и содержит слишком много зависимостей, по сравнению с тем, что требуется для решения поставленной задачи. А если сама задача очень сложна? Экспертная система? Серврер БД? ERP система?


Тогда на помойку выбрасывается фирма, после ухода этого программиста.
Re[3]: Серьёзный код :)
От: vdimas Россия  
Дата: 16.07.04 09:49
Оценка:
Здравствуйте, Candle645, Вы писали:

C>Как раз наоборот — программист выше определенного (и достаточно высокого) уровня будет писать максимально простой код


немного не так...
максимально простой и логичный по сравнению с новичком, и только в том случае, когда задача и новичку под силу...
Re[12]: Пример не серьезного и аналогичного ему серьезного к
От: WolfHound  
Дата: 16.07.04 16:56
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Можно поконкретнее? Что конкретно обозначают слова "забыли" и "помнит это за меня"? А то просто нету предмета для обсуждения.

Пример на C#
        static void Main(string[] args)
        {
            FileStream file=File.Create("some.file");

            file.Close();
        }

если забыть сделать file.Close() то фаил не будет закрыт до сборки мусора.
В С++
    std::fstream file("some.file");
    file.close();

file.close() делать не обязательно ибо фиал будет закрыт в деструкторе fstream при выходе переменной file из области видимости.

SYG>1) Уж точно даже не пытаться работать с неверными данными.

Это точно.
SYG>2) Как можно скорее сообщить об этом пользователю, если это возможно.
А как сообщить об этом пользователю? Допустим у меня есть некий очень сложный код который не знает в каком окружении он работает, а даже если бы и знал то пристовать к бользователю в обход того кода который взаимодействует с пользователем это бардак.
Дык вот вернуть ошибку из глубины этого кода без исключений довольно проблематично.

SYG>Я сам на работе на нем не пишу, но слышал, что есть такие конторы где пишут на оберонах (в основном для космоса, для беспилотных летательных аппаратов, электростанций и

Ты с Адой не путаешь? Даже если нет то по масштабу использования он в месте с Адой и рядом не стоял с С++.
Те за 10 лет ему удалось занять ничтожную часть производства ПО от сюда вывод...

SYG>Во-первых, если Вам обязательно надо что-то возвращать, то ни кто не заставляет Вас возвращать именно номер_кода_ошибки.

В любом случае этот код в силе
Re[10]: Пример не серьезного и аналогичного ему серьезного к
Автор: WolfHound
Дата: 15.07.04


SYG>Во-вторых, Вы не обязаны что-то возвращать. Если Вам известно что какая-то операция потенциально опасна, то прежде чем ее выполнять, Вы "морально обязаны" проверить правильность параметров

Вот это и есть исключительная ситуация. Дальше работать нельзя и надо с этим что-то делать, а сделать это внутри некоторого алгоритмы не реально тем болие что какправило не понятно что делать.
В этом случае спокойно кидаем исключение и пусть с ошибкой разбирается тот кто знает что с ней делать.

SYG>1) Никогда нельзя знать "повис" указатель или нет.

Поверь мне они у меня и без ГЦ никогда не появится ибо:
Предохраняйтесь смартпоинтерами и ваши указатели не будут висячими.

SYG>2) Надо всего лишь запретить "насильное" приведение типов.

Не надо. Иногда без него ни куда. Но такие штуки у нормальных людей спрятаны в библиотеку за дуракоустойчивым интерфейсом.
SYG>Точно так же как в свое время запретили инструкцию goto.
Генерить парсер на конечном автомате без goto мягко говоря проблематично...
SYG>Проблема решена.
Как всегда одни проблемы решены, а другие добавлены...
Причем опять же как всегда решены мифические, а добавлены вполне реальные.
... << RSDN@Home 1.1.3 beta 1 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[12]: Пример не серьезного и аналогичного ему серьезного к
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 17.07.04 16:45
Оценка: +1
SYG> Если в этом случае эта операция все-равно провалится, то поскольку согласно логике программы данные правильные и никакой ошибки быть не должно — это означает, что причина ошибки находится вне логики программы (кто-то отцепил жесткий диск, повредил молотком процессор и т.п.), следовательно дальнейшее выполнение программы не имеет смысла.

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

Возьмем, например, браузер — коннект с сервером может быть в любой момент прерван, но весь браузер не должен закрываться.

Или возьмем, например, текстовый редактор и операцию сохранения файла на дискету: дискета может быть битая и операция сохранения не выполнится, но это опять же не означает, что весь текстовый редактор необходимо при этом закрывать.

Или возьмем графический редактор, который работает с множеством картинок и при загрузке очередной картинки вдруг выяснилось, что память кончилась — опять же бессмысленно закрывать весь граф. редактор


SYG>Если Вам известны еще примеры потенциально опасных операций правильный исход которых заранее проверить невозможно, то пишите, обсудим.


1. Выделение памяти
2. Любые операции с внешним миром: операции с диском, сетью, интернетом, пользователем, другими программами и т.д.
3. Для случая распределенной программы, вообще, любой вызов функции может закончится неудачей, т.к. может быть именно в этот момент пропала связь с удаленным модулем.
Re[13]: Сборщик мусора - это не роскошь, а осознанная необхо
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 19.07.04 06:35
Оценка: -1 :)
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, S.Yu.Gubanov, Вы писали:


SYG>>Можно поконкретнее? Что конкретно обозначают слова "забыли" и "помнит это за меня"? А то просто нету предмета для обсуждения.

WH>Пример на C#
WH>
WH>        static void Main(string[] args)
WH>        {
WH>            FileStream file=File.Create("some.file");

WH>            file.Close();
WH>        }
WH>

WH>если забыть сделать file.Close() то фаил не будет закрыт до сборки мусора.
WH>В С++
WH>
WH>    std::fstream file("some.file");
WH>    file.close();
WH>

WH>file.close() делать не обязательно ибо фиал будет закрыт в деструкторе fstream при выходе переменной file из области видимости.


Это, мягко говоря, немного устаревший способ работы с файлами. Всвязи с появлением ООП, люди уже более десятка лет с файлами работают на более высоком уровне абстракции. Могу вкратце описать как это делается в системе BlackBox от Oberon Microsystems. В этой системе для этой цели используется паттерн проектирования под названием "Carrier-Rider-Mapper". Carrier — это персистентный объект содержащий внутри себя какие-либо данные (можно думать о нем как об объектно-ориентированном аналоге устаревшего понятия — "файл"). Для простоты примера давайте предположим, что речь идет о файле с текстом. Carrier — это персистентный объект внутри которого инкапсулирован текст. Доступ к этому тексту одновременно можно предоставить сразу нескольким клиентам, например, открыть несколько окон с одним и тем же содержимым. Каждый клиент должен, как минимум, знать в каком месте текста он в данный момент имеет "текущую позицию". Так как количество клиентов произвольное, то информация о текущей позиции не может хранится в самом объекте Carrier, для этих целей объект Carrier умеет создавать так называемые Rider-объекты (паттерн — "итератор"). Так что клиенты обращаются не на прямую к объекту Carrier, а посредством Rider-ов. Между Carrier-ом и Rider-ами существует отношение один-ко-многим. Аналогично Rider-ам объект Carrier может создавать объекты Writer-ы, которые умеют "писать", в отличие от "читающих" Rider-ов. На этом приключения не заканчиваются. Кроме Carrier, Rider-ов и Writer-ов существуют еще так называемые Mapper-ы. Дело в том что Rider-ы и Writer-ы представляют низкоуровневый интерфейс (теоретически, вплоть до: записать один байт, прочитать один байт). Mapper-ы — это объекты-прослойки между Rider-ами, Writer-ами и клиентами. Они предоставляют более высокоуровневый уже проблемно-ориентированный набор операций для чтения и записи. Разные приложения могут использовать разные Mapper-ы работая с одним и тем же Carrier-ом.

Пример:
Client <-- Scanner <-- Reader <-- Carrier
Client --> Formatter --> Writer --> Carrier

Scanner и Formatter — это Mapper-ы.

Вобщем, клиенты общаются с Carrier-ом даже не через Reader/Writer, а через Mapper-ы. Сами Reader/Writer-ы нужны для того чтобы предотвратить комбинаторный взрыв количества реализаций разных мапперов под разные кариеры. Если есть n-штук Carrier-ов и m-штук Mapper-ов, то в этом случае надо написать реализацию только для n+m полиморфных переменных, а вовсе не для n*m как могло бы быть без Reader/Writer-ов.

Теперь вернемся к Вашему вопросу о "забыть сделать file.Close() то фаил не будет закрыт до сборки мусора". Понимаете в чем дело-то, клиент понятия не имеет о том сколько еще других клиентов приконнекчено к Carrier-у, а значит клиент в принципе не может выполнить операцию file.Close(), в интерфейсах форматтеров, райдеров, врайтеров и карьеров такой операции нет по принципиальным причинам. Подконнекчиваясь к кариеру клиенту совершенно безразлично был ли это кариер только что создан (только что открыли файл) или он уже существовал (файл уже был открыт кем-то другим). Клиент получает в свое пользование Carrier-скую полиморфную переменную и делает с ней что хочет, а потом забывает о ней. Так же поступают другие клиенты. Сам же Carrier являясь персистентным объектом, всегда существует в единственном экземпляре, то есть Ваши опасения по забывчивости открыть несколько раз один и тот же файл совершенно напрасны (работает некоторая модификация паттерна Singleton).

Теперь, собственно, при чем тут сборщик мусора. Рассмотрим более общую ситуацию. Пусть есть многомодульная система. Большинство модулей на момент написания не имели никакого представления друг о друге. Модули — единицы компиляции и исполнения программы, они могут динамически загружаться/выгружаться во время работы Программы. Под Программой, можно понимать всю операционную систему, а модули — ее расширения. Модули создают внутри себя объекты и в виде полиморфных переменных передают их для использования другим модулям, те модули, в свою очередь, могут передать эти полиморфные переменные третьим модулям и т.д.. Модуль создавший обьект и отдавший его другому модулю в принципе понятия не имеет кто еще пользуется этим объектом и как долго он это намерен делать. А значит модуль-создатель в принципе не может нести ответсвенность за уничтожение им созданного объекта. Но никакой другой модуль тоже в принципе не может нести ответсвенность за уничтожение чужого объекта. Подсчет ссылок на объект проблемы не решает поскольку, в общем случае, возможны замкнутые петли (циклические) ссылки объектов друг на друга. Утечка памяти в многомодульных системах неизбежна по принципиально неразрешимым причинам. Единственным выходом из этой ситуации является наличие единого на всю операционную систему полноценного сборщика мусора. Сборщик мусора — это не роскошь, и не фича придуманная для ленивых программистов. Сборщик мусора — это осознанная необходимость. Существование многомодульныех систем, модули которых обмениваются друг с другом объектами, невозможно без одного на всех полноценного сборщика мусора.

//----------------------------------------

Паттерн Carrier-Rider-Mapper был получен в результате исследовательского проекта "Insight ETHOS: On Object-Orientation in Operating Systems" Clementh Szyperski; Zьrich, 1992. (также этот паттерн сейчас известен как "Bridge pattern" — это по классификации "Банды четырех" Гамма, Хелм, Джонсон, Влиссидес). Клеменс Шиперский — один из со-основателей компании Oberon Microsystems. Также он является основоположником КОП — компонентно ориентированного программирования. Его статья о том что такое КОП и почему одно лишь "голое" ООП само по себе не состоятельно, датируется кажется то-ли 1994, то-ли 1995 годом (под рукой нет, я ее засунул куда-то). Сейчас он работает в Микрософте, платформа .NET позаимствовала от ОС Оберон кое-что.
Re[13]: Внешнее не имеет непосредственной власти над внутрен
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 19.07.04 06:46
Оценка: -1
Здравствуйте, DarkGray, Вы писали:


SYG>> Если в этом случае эта операция все-равно провалится, то поскольку согласно логике программы данные правильные и никакой ошибки быть не должно — это означает, что причина ошибки находится вне логики программы (кто-то отцепил жесткий диск, повредил молотком процессор и т.п.), следовательно дальнейшее выполнение программы не имеет смысла.


DG>Вы почему-то априори считаете, что текущая выполняемая функция и вся программа — это одно и тоже, и из этого делаете вывод, что раз текущая функция не смогла выполниться, то значит надо закрывать всю программу.

DG>Но для большинства задач — это не так.

DG>Возьмем, например, браузер — коннект с сервером может быть в любой момент прерван, но весь браузер не должен закрываться.


DG>Или возьмем, например, текстовый редактор и операцию сохранения файла на дискету: дискета может быть битая и операция сохранения не выполнится, но это опять же не означает, что весь текстовый редактор необходимо при этом закрывать.


DG>Или возьмем графический редактор, который работает с множеством картинок и при загрузке очередной картинки вдруг выяснилось, что память кончилась — опять же бессмысленно закрывать весь граф. редактор



SYG>>Если Вам известны еще примеры потенциально опасных операций правильный исход которых заранее проверить невозможно, то пишите, обсудим.


DG>1. Выделение памяти

DG>2. Любые операции с внешним миром: операции с диском, сетью, интернетом, пользователем, другими программами и т.д.
DG>3. Для случая распределенной программы, вообще, любой вызов функции может закончится неудачей, т.к. может быть именно в этот момент пропала связь с удаленным модулем.


Видите ли, для начала надо просто понять одну простую вещь. Ничто внешнее по отношению к нашей программе ни при каких обстоятельствах никогда не может создать исключительную ситуацию здесь внутри нашей программы. Все что происходит внутри нашей программы делается только потому что программа так была написана. Ни что внешнее вообще понятия не имеет о внутренностях программы — в этом заключается принцип инкапсуляции. Вера в то что что-то внешнее способно само что-то сделать внутри программы сродни вере в целительную силу танцев с бубном. После того как мы поймем эту простую вещь наша дальнейшая жизнь будет значительно проще.
Теперь по пунктам.
DG> 1. Выделение памяти
Выделение памяти для пользователей языка программирования является абсолютно безопасной операцией
NEW(p);
IF p # NIL THEN ... END;
Если памяти недостаточно, то процедура NEW() всего лишь навсего вернет пустой (NIL) указатель. Никаких исключительных ситуаций при этом не возникнет.

DG> 2. Любые операции с внешним миром: операции с диском, сетью, интернетом, пользователем, другими программами и т.д.

DG> 3. Для случая распределенной программы, вообще, любой вызов функции может закончится неудачей, т.к. может быть именно в этот момент пропала связь с удаленным модулем.

Все эти операции абсолютно безопасны. Не читается дискета? — Великолепно, так и сообщим пользоваелю что не читается дискета, при чем здесь исключительная ситуация? Буфер порта TCP/IP протокола пустой — так и скажем пользователю что интернет упал. Где здесь исключение? Другая программа не отвечает — великолепно, не больно то и хотелось. Здесь нигде нет ни одной исключительной ситуации. Все ситуации — штатные, обычные, заранее известные.

Вам не удалось привести ни одного примера потенциально опасной операции чей фатальный исход невозможно предугадать заранее путем проверки входных данных. Примеры, которые Вы привели, абсолютно безопасны просто в случае неудачи, например, возвращают FALSE вместо TRUE, но исключительных ситуаций они при этом не создают. Вы перепутали операции которые не могут быть выполнены — то есть завершаются фатально (например возвращая FALSE), с операциями которые во время выполнения приводят к возникновению исключительных ситуаций. К потенциально опасным операциям относятся: деление на ноль и другие арифметические переполнения, неправильный индекс массива, разыменование повисшего указателя, и, быть может, что-то еще. Но вот что?
Re[11]: Полумеры говорите?
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 19.07.04 07:07
Оценка: :)
Здравствуйте, eugals, Вы писали:

E>Здравствуйте, S.Yu.Gubanov, Вы писали:


SYG>>Мысли на счет исключений:

SYG>>Исключения бывают "вредные" — то есть те которые и быть-то не должны — они сигнализируют о сбое работы программы, если они возникают, значит программа написана криво; а бывают "полезные" — это те исключения, которых ожидают и обрабатывают согласно логике работы программы, они заранее были запланированы. Так вот в Component Pascal так сказать "вредные" исключения (выход индекса за пределы массива, неправильное приведение типа, не предусмотренный вариант выбора в инструкции CASE и т.п.) — просто автоматически немедленно останавливают работу программы и все — программиста на мыло!

E>Не, "автоматически немедленно" останавливать работу программы это полумера. Надо, чтобы она сама себя тут же и деинсталлировала и возвращала пользователю уплаченные за себя деньги, после чего "автоматически" коннектилась к сайту разработчиков и отправляла "на мыло" провинившегося программиста по имени "Component Pascal"


E> (шутка)



Кто-то сказал, что цитаты производят большее впечатление чем слова принадлежащие лично говорящему. Что же, процитирую Сергея Деревяго:

http://ders.angen.net/cpp3/xcppcomm/xcppcomm.html#149

Стр.149: Задача 2.14. Исключения в конструкторах. Часть 2
M: Item 18. Constructor Failures, Part 2: Absorption?

А теперь маленькое философское отступление. Те, кто писал действительно серьезный код знают, что исключений, возбуждаемых при ошибках программиста (напр. std::logic_error, который определяется стандартом как: для извещения об ошибках, которые, предположительно, могут быть обнаружены до выполнения программы) быть не должно. Самое лучшее, что мы можем сделать при обнаружении подобного рода ошибки -- это сразу же прервать выполнение программы, сгенерировав информацию, необходимую для определения сути происшедшего (напр. core dump). Дело в том, что "ошибка здесь" является, как правило, следствием "ошибки там" и продолжение исполнения некорректного кода в надежде на лучшее приведет к Ужасным Последствиям (напр. сумма, эквивалентная нескольким десяткам лет зарплаты программиста, будет зачислена на неизвестный счет где-нибудь в Буркина-Фасо. Но это, конечно, маловероятно, т.к. чаще всего обнаруженное несоответствие двойной бухгалтерии приводит к временной остановке всех операций до выяснения причин; а убыток от нескольких часов простоя серьезного банка приводит к гораздо более дорогостоящим последствиям...).
Re: Серьёзный код :)
От: Бодхисатва  
Дата: 19.07.04 07:09
Оценка:
Здравствуйте, AndreyFedotov, Вы писали:

AF> Мы тут чудненько пофлеймили на тему "о применимости С++ в серьёзном коде" , но как то за кадром остался вопрос о том, что же такое "серьёзный код". Были правда некоторые попытки определить это понятие, но они быстро иссякли.

AF> А вы как думаете — какой код можно назвать "серъёзным" или "сложным"? Как Вы вообще — разделяете ли код по каким-либо критериям и на какие то категории? И если да, то по каким?
AF> Некоторые уже упоминались. Например "лохокод"
AF> Подумав над данным вопросом сам, пришёл к выводу, что для меня "серъёзный" код — это код со сложной архитектурой и множеством взаимосвязей и взаимозависимостей. Какой нибудь навороченый алгоритм, с кучей условий (какая нибудь сложная сортировка или шифрование) — код сложный (алгоритмически), но тем не менее к серьёзному я его не отношу. По тем же причинам к серъёзному коду не отношу большинство модулей ОС, так как на мой взгляд — это код специфический — который должен быть написан крайне аккуратно и хорошо продуман, но чего-либо принципиально сложного в нем нет.
AF> Наверняка у каждого из нас своя точка зрения по этому поводу. Предлагаю поделиться...

Мне кажеться, что "серъёзный" является синонимом "хорошего". Хороший код это адекватный код, в котором нет излишних наворотов и искуственнного усложнения. Обычно, такой код хорошо структуирован и полностью соответсвует принципу DRY(don't repeat yourself).
Re[14]: Внешнее не имеет непосредственной власти над внутрен
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 19.07.04 07:35
Оценка: 3 (1) +2
SYG>Теперь по пунктам.
DG>> 1. Выделение памяти
SYG>Выделение памяти для пользователей языка программирования является абсолютно безопасной операцией
SYG>NEW(p);
SYG>IF p # NIL THEN ... END;
SYG>Если памяти недостаточно, то процедура NEW() всего лишь навсего вернет пустой (NIL) указатель. Никаких исключительных ситуаций при этом не возникнет.

Чуть философски отвлечемся.
Есть старое изречение "Счастливы все одиннаковы, несчастливы — каждый по своему".
Это изречение можно применить и к программам, в том смысле, что работа без ошибок всегда одиннакова — работа программы всегда выполняется идет по одной трасе, но каждая ошибка уникальна, т.к. сразу меняется трасса работы программы.

Если каждую ошибку обрабатывать отдельно, то сразу возникает проблема комбинаторного взрыва, т.к. на каждую трассу "хорошего" выполнения у нас может быть 10, 100, миллион трасс выполнения с обработкой ошибок.

Получается, что любыми способами надо упрощать обработку ошибок, иначе написать что-то действительно сложнее будет нереально в конечные сроки.

Для уменьшения этого комбинаторного взрыва применяется паттерн "транзакция" — если не выполнилась часть операции, то не выполняется вся операция с корректным откатом выполненных подопераций.
Для поддержки этого паттерна в языки были введены try/finally, деструкторы, исключения, using, декларируются такие под паттерны, как захват/освобождение ресурса должно оформляться в виде конструктор/деструктор.

Этот паттерн резко упрощает обработку ошибок, Т.к. теперь есть только один вид ошибки — нарушение нормального хода выполение программы. И уже не так важно, как именно этот ход нарушился.
Причем это нарушение может быть обработано на любом уровне, соответственно откатывая или не откатывая вышестоящие транзакции.

DG>> 2. Любые операции с внешним миром: операции с диском, сетью, интернетом, пользователем, другими программами и т.д.

DG>> 3. Для случая распределенной программы, вообще, любой вызов функции может закончится неудачей, т.к. может быть именно в этот момент пропала связь с удаленным модулем.

SYG>Все эти операции абсолютно безопасны. Не читается дискета? — Великолепно, так и сообщим пользоваелю что не читается дискета, при чем здесь исключительная ситуация? Буфер порта TCP/IP протокола пустой — так и скажем пользователю что интернет упал. Где здесь исключение? Другая программа не отвечает — великолепно, не больно то и хотелось. Здесь нигде нет ни одной исключительной ситуации. Все ситуации — штатные, обычные, заранее известные.


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

1. Во-первых, как подоперация, которая обращается к дискете узнает, кто такой "пользователь"? И значит, как она ему выдаст ошибку? Может быть программа, вообще, работает в автоматическом режиме и никакого пользователя нет.
2. Во-вторых, кто будет все эти варианты разбирать и описывать? И сколько вариантов по Вашему надо обработать вариантов, у того простого действия, как запись файла на диск?


SYG>Вам не удалось привести ни одного примера потенциально опасной операции чей фатальный исход невозможно предугадать заранее путем проверки входных данных. Примеры, которые Вы привели, абсолютно безопасны просто в случае неудачи, например, возвращают FALSE вместо TRUE, но исключительных ситуаций они при этом не создают. Вы перепутали операции которые не могут быть выполнены — то есть завершаются фатально (например возвращая FALSE), с операциями которые во время выполнения приводят к возникновению исключительных ситуаций. К потенциально опасным операциям относятся: деление на ноль и другие арифметические переполнения, неправильный индекс массива, разыменование повисшего указателя, и, быть может, что-то еще. Но вот что?


Вы противоречите сами себе.
Что же опасного в делении на ноль? ЭТу операцию тоже можно заранее избежать:
if (y == 0)
  return error;
else
  z = x/y;


И с массивом ничего страшного не происходит
if (index < 0 || index >= array.length)
  return error;
else
  item = array[index];

даже с разименованием повисшего указателя нет ничего страшного:
if (!isAlive(pointer))
  return error;
else
  value = *pointer;



Можно сказать, что вы передергиваете.
Сначала говорите, что все в руках программы, и никаких исключительных ситуаций для программы нет, а потом вдруг приводите какие-то примеры, которые почему-то чуть более исключительные, чем все остальные.

Вот Вы в соседнем топике вспоминали про модули, про инкапсуляцию, про уменьшение комбинаторики.
Но почему в рамках этого обсуждения сразу об этом забываете.
А ведь в данном случае программа тоже состоит из модулей, и отдельным модулям как можно меньше хочется знать об ошибках друг друга, чтобы не увеличивать комбинаторное число вариантов.
Рассмотрим, например, модуль графический редактор. Модуль граф. редактор попросил модуль "хранилище" сохранить данные, и при этом модулю "граф. редактор" не важно, почему модуль "Хранилище" на смогло сохранить файл: память кончилась, дискету вытащили, прав не хватило, программист при написании модуля "хранилище" накосячил — для модуля "граф. редактор" — все эти ситуации исключительны, потому что граф. редактор хотел сохранить файл, а его обломали. И необходимо менять "нормальную" трассу выполнения программы.
Re[12]: Полумеры говорите?
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 19.07.04 08:11
Оценка:
SYG>А теперь маленькое философское отступление. Те, кто писал действительно серьезный код знают, что исключений, возбуждаемых при ошибках программиста (напр. std::logic_error, который определяется стандартом как: для извещения об ошибках, которые, предположительно, могут быть обнаружены до выполнения программы) быть не должно. Самое лучшее, что мы можем сделать при обнаружении подобного рода ошибки -- это сразу же прервать выполнение программы, сгенерировав информацию, необходимую для определения сути происшедшего (напр. core dump). Дело в том, что "ошибка здесь" является, как правило, следствием "ошибки там" и продолжение исполнения некорректного кода в надежде на лучшее приведет к Ужасным Последствиям (напр. сумма, эквивалентная нескольким десяткам лет зарплаты программиста, будет зачислена на неизвестный счет где-нибудь в Буркина-Фасо. Но это, конечно, маловероятно, т.к. чаще всего обнаруженное несоответствие двойной бухгалтерии приводит к временной остановке всех операций до выяснения причин; а убыток от нескольких часов простоя серьезного банка приводит к гораздо более дорогостоящим последствиям...).


Рассмотрим, например, программу управляющую аэропортом. В том числе эта программа рассчитывает насколько времени у самолета хватит топлива.

А теперь допустим, что на посадку вдруг решил зайти планер, у которого соответственно расход горючего ноль, программа попыталась рассчитать на сколько времени такому "самолету" хватит горючего, но произошла ошибка — деление ноль.

По Вашему получается, что в этом случае должен быть остановлена вся программа, при этом оставив висеть в воздухе все управляемые данной программой самолеты.
Т.е. получается, что Вы готовы на себя взять ответственность за жизнь всех людей, которые находились в воздухе?
Может все-таки стоило остановить только для данного самолета в данное время один единственный модуль, который рассчитывает расход горючего?

Вернемся к реальной жизни. В реальной жизни часто стоит задача построить надежную систему из ненадежных элементов, любые организации людей построены на этом принципе — т.е. любой человек может ошибаться, "ломаться", "выходить из строя", но организация в целом при этом продолжает стабильно работать.

Таже самая задача стоит и в программирование, в программировании тоже необходимо строить надежные системы из ненадежных элементов, из ненадежных модулей. В тяжелых случаях — отдельные модули, вообще, могут быть саботирующими — встроенными через хакерскую атаку.
Но выше перечисленный подход приводит к тому, что надежность всей системы не может быть выше, чем надежность самого ненадежного элемента, т.к. ненадежный элемент может в любой момент остановить работу всей системы.

ps
Можно даже сказать, что на совести людей, которые руководствуются логикой из цитаты, такие аварии как взрыв шатла во Франции, отказы марсианских программ. Именно в этих авариях небольшие ошибки в отдельных модулях привели к останову всей программы, и в конечном счете, к аварии.
Re[13]: Супер безопасность
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 19.07.04 08:46
Оценка: -1
Здравствуйте, DarkGray, Вы писали:

DG>Может все-таки стоило остановить только для данного самолета в данное время один единственный модуль, который рассчитывает расход горючего?


Что же Вы о создателях Оберон систем так плохо думаете? Разумеется будет остановлена работа только того модуля, который выполнил недопустимую операцию. Модуль будет динамически выгружен из системы. Когда другие модули запросят доступ к нему, он автоматически загрузится в память обратно. Так сказать — горячая перезагрузка — на лету. Вместо этого модуля, если угодно, может быть загружен модуль от другого производителя. Или, гипотетически, этот модуль будет "на лету" перекомпилирован заново, ошибка в нем исправлена, а его новый скомпилированный вариант динамически загружен в память. Что может быть устойчивее и надежнее? Вы бы, извините конечно, прежде чем глупые фантазии излагать вспомнили бы в каких областях Обероны используются: для случаев требующих особой безопасности — космос, электростанции, беспилотные летательные (военные) аппараты.


Ну как, можно считать вопрос на этом исчерпанным?
Re[12]: Полумеры говорите?
От: eugals Россия  
Дата: 19.07.04 08:55
Оценка: +1
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>А теперь маленькое философское отступление. Те, кто писал действительно серьезный код знают, что исключений, возбуждаемых при ошибках программиста (напр. std::logic_error, который определяется стандартом как: для извещения об ошибках, которые, предположительно, могут быть обнаружены до выполнения программы) быть не должно. Самое лучшее, что мы можем сделать при обнаружении подобного рода ошибки -- это сразу же прервать выполнение программы, сгенерировав информацию, необходимую для определения сути происшедшего (напр. core dump). Дело в том, что "ошибка здесь" является, как правило, следствием "ошибки там" и продолжение исполнения некорректного кода в надежде на лучшее приведет к Ужасным Последствиям (напр. сумма, эквивалентная нескольким десяткам лет зарплаты программиста, будет зачислена на неизвестный счет где-нибудь в Буркина-Фасо. Но это, конечно, маловероятно, т.к. чаще всего обнаруженное несоответствие двойной бухгалтерии приводит к временной остановке всех операций до выяснения причин; а убыток от нескольких часов простоя серьезного банка приводит к гораздо более дорогостоящим последствиям...).


1. Core dump во многих случаях мало полезен (или вообще бесполезен).
Часто нужно более информативное, краткое и точное описание проблемы.
Вот некоторые (очевидные) аргументы:
    — его неудобно читать (по сравнение с текстовым описание ошибки) и/или траспортировать (файл в 512мб по почте в службу поддержки уже не пошлешь)
    - если речь идет о распределенной системе (пусть даже простой клиент-серверной), то полный дамп не всегда и сформировать-то получится. Программы ведь на разных машинах живут.
    - не стоит пугать пользователей бессмысленными сообщениями типа "Внутренняя ошибка приложения. Подробнее смотрите дамп памяти (512мб нулей и единиц), а лучше обратитесь в службу поддержки. В дельнейшем не рекомендуем Вам выполнять тот набор действий, который привел к ошибке (вы ведь наверняка должны точно помнить две сотни кнопок, которые нажали за последние три часа?)
Кстати, а хотя бы core dump твой ComponentPascal делать умеет?

2. Не всякие программы оперируют "суммами эквивалентными нескольким десяткам годовых зарплат программиста" . Говорят, что бываю такие, которые вообще никак с деньгами не связаны

3. Во многих случаях, при "экстренном завершении" программы необходимо, чтобы она всё же успела выполнить некоторый финализующий код (в идеале — отработали деструкторы всех объектов), например:
    - на экране появился хоть какой-нибудь диалог, сообщающий (на русском языке, с отсылкой к службе поддержки и ссылками на пункты хелпа)
    - было возможность немедленно отправить разработчикам по почте багрепорт
    - если это был текстовый редактор, то нужно предложить пользователю сохранить текст, который он набирал в последние несколько часов.
    - были закрыты все файлы/коннекты-к-бд/сессии-работы-со-внешними-устройствами (например приводами чтения кредитных карт)
Кстати, если не выполнить действия из предыдущего пункта, то при следующем запуске как раз таки и может случиться та самая ошибка, приводящая к "убыткам от нескольких часов простоя серьезного банка"

4. Напоминаю, что есть такая технология, называется "plug in".
Я так понимаю, ты предлагаешь разработчику основного приложения смириться с тем, что оно может быть в любой момент закрыто, если в одном из плагинов (даже не им написанных) произошла ошибка?
Как по мне, так лучше просто отключить проблемный плагин.
... << RSDN@Home 1.1.3 stable >>
Re[15]: Недопустимая операция = Исключительная ситуация
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 19.07.04 09:12
Оценка: -4 :)
Здравствуйте, DarkGray.

Вы просто напросто используете термин "исключительная ситуация" для все чего ни попадя:

DG>для модуля "граф. редактор" — все эти ситуации исключительны, потому что граф. редактор хотел сохранить файл, а его обломали.


Тем не менее, ни в одном из приведенных Вами примеров не было по настоящему "исключительной ситуации", то есть такой когда программа выполняет некоторое действие другими словами называемое "недопустимая операция". Нельзя вычислить квадратный корень из отрицательного числа, нельзя разделить на ноль, нельзя работать с чужой памятью, но при этом, сколько угодно раз можно отказать в сохранении или открытии файла — это не является "недопустимой операцией". Разницу понимаете?


P.S.
К делу вообще-то не относится, но, видимо Вы еще и слово "модуль" используете для всего чего ни попадя. Нет такого модуля "граф. редактор". Программа "граф. редактор" — состоит из нескольких десятков модулей (а может и сотен). Модуль — это довольно мелкая единица, она меньше чем программа, но больше чем класс.

Программа >> Модуль >> Класс

Если Вы программируете на С++, то первым грубым приближением к понятию модуль будет просто один файл с исходным кодом, но этот файл компилируется в одну DLL-ку. Вот сколько файлов Вам понадобилось для написания программы "граф. редактор" — вот из стольки модулей она и состоит, причем каждый из них динамически загружается/выгружается во время работы программы. Язык С++ разумеется этого не поддерживает (так чтоб автоматом каждый файл в отдельную DLL-ку компилить), но в общем и целом модулем называется именно это.
Re[13]: Полумеры говорите?
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 19.07.04 09:23
Оценка:
Здравствуйте, eugals, Вы писали:

E>Кстати, а хотя бы core dump твой ComponentPascal делать умеет?


Во-первых, Component Pascal — это язык и сам по себе он ничего делать не умеет. На нем писать самому надо. Во-вторых, скачайте из интернета доки и узнайте. Я почему-то о С++ подобных вопросов не задаю, типа "А ваш С++, ваще, хотя бы умеет делать то-то или то-то?"

E>4. Напоминаю, что есть такая технология, называется "plug in".

E>Я так понимаю, ты предлагаешь разработчику основного приложения смириться с тем, что оно может быть в любой момент закрыто, если в одном из плагинов (даже не им написанных) произошла ошибка?
E>Как по мне, так лучше просто отключить проблемный плагин.

Уже ответил:
http://www.rsdn.ru/Forum/Message.aspx?mid=725690&amp;only=1
Автор: S.Yu.Gubanov
Дата: 19.07.04
Re[8]: Пример не серьезного и аналогичного ему серьезного ко
От: Sir Wiz Россия  
Дата: 19.07.04 09:42
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Ну это быстренько можно "подпортить", и "рульность" С++ поубавится

SYG>1) Пусть a,b,c — это очень большие объекты, так что на стеке втроем им места не хватит...
SYG>2) Пусть a,b,c — это не объекты, а полиморфные переменные. В терминологии С++ статический тип a,b,c — пусть будет указатели на объекты "абстрактных классов". Так как в стандартном С++ есть только блок try catch, а FINALLY — нету, то жизнь сразу усложняется. Придется использовать классы обертки — "умные указатели", а это уже из другой оперы.
Ответ на оба вопроса — Master Pointers ("ведушие указатели", Джеф Элджер, например). Нет никакой "другой оперы".
... << RSDN@Home 1.1.3 stable >>
Re[14]: Полумеры говорите?
От: eugals Россия  
Дата: 19.07.04 09:49
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Во-первых, Component Pascal — это язык и сам по себе он ничего делать не умеет. На нем писать самому надо. Во-вторых, скачайте из интернета доки и узнайте.

Запустил сейчас на RSDN поиск строки "Compоnent Pascal" — 13 ccылок. Из них наверное половина твоих.
Пожалуй я, с твое разрешения, прощу себе, что до сегодняшнего дня про него кроме названия ничего не слышал
Что же касается твоего предложения "скачать доки", то я от этого на данный момент воздержусь — пока что твои рекламные сообщение не убеждают, что оно будет стоит потраченного времени. А судя по твоей реакции на мои попытки узнать о CP что-нибудь вразумительное, врял ли когда-нибудь это положение изменится

SYG> Я почему-то о С++ подобных вопросов не задаю, типа "А ваш С++, ваще, хотя бы умеет делать то-то или то-то?"

А ты задай — ответят
(кстити, набери в поиске RSDN "С++" и посчитай сколько ссылок будет там)

SYG>Уже ответил:

SYG>http://www.rsdn.ru/Forum/Message.aspx?mid=725690&amp;only=1
Автор: S.Yu.Gubanov
Дата: 19.07.04

только на последний пункт, да и то неубедительно (почему — выскажусь в той ветке).
... << RSDN@Home 1.1.3 stable >>
Re[16]: Недопустимая операция = Исключительная ситуация
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 19.07.04 10:02
Оценка: +1
SYG>Вы просто напросто используете термин "исключительная ситуация" для все чего ни попадя:

DG>>для модуля "граф. редактор" — все эти ситуации исключительны, потому что граф. редактор хотел сохранить файл, а его обломали.


SYG>Тем не менее, ни в одном из приведенных Вами примеров не было по настоящему "исключительной ситуации", то есть такой когда программа выполняет некоторое действие другими словами называемое "недопустимая операция". Нельзя вычислить квадратный корень из отрицательного числа, нельзя разделить на ноль, нельзя работать с чужой памятью, но при этом, сколько угодно раз можно отказать в сохранении или открытии файла — это не является "недопустимой операцией". Разницу понимаете?


Нет, не понимаю.
Я не вижу разницу между операциями "Деление на ноль", "Взять неберущийся интеграл", "Открыть файл, которого нет на диске", "Открыть файл, который не доступен".

Во всех этих случаях, можно сказать, что есть некий логический просчет, который и привел к возникновению данной ситуации.
И мне не понятно, почему какие-то из этих случаев объявляются более исключительными, чем другие, другими словами я не вижу критерия по которому производится это деление.


SYG>P.S.

SYG>К делу вообще-то не относится, но, видимо Вы еще и слово "модуль" используете для всего чего ни попадя. Нет такого модуля "граф. редактор". Программа "граф. редактор" — состоит из нескольких десятков модулей (а может и сотен). Модуль — это довольно мелкая единица, она меньше чем программа, но больше чем класс.

Вопрос на засыпку:
если мы несколько модулей объединили вместе, то какой "элемент" мы получили в итоге? сразу программу?


SYG>Программа >> Модуль >> Класс


Чуть продолжу Вашу мысль, получается, что если мы граф. редактор используем, как подчасть более большой программы, то граф. редактор автоматом становится модулем, потому что с одной стороны — это не класс, но с другой стороны — это и не программа, т.к. редактор получился встроенным в другую программу.

ps
но если чуть пофилософствовать, то понятие модуль в себя включает и такие понятия, как программа, класс, package, unit, объект и т.д. Все зависит только от того, на каком уровне абстракции мы находимся, и какой язык программирования используем.

SYG>Если Вы программируете на С++, то первым грубым приближением к понятию модуль будет просто один файл с исходным кодом, но этот файл компилируется в одну DLL-ку. Вот сколько файлов Вам понадобилось для написания программы "граф. редактор" — вот из стольки модулей она и состоит, причем каждый из них динамически загружается/выгружается во время работы программы. Язык С++ разумеется этого не поддерживает (так чтоб автоматом каждый файл в отдельную DLL-ку компилить), но в общем и целом модулем называется именно это.


Возьмем, например, Word.
Встроенный в Word граф. редактор диаграмм — на Ваш взгляд — это модуль, программа или класс?


ps
Мы с Вами очень подробно подискутировали о терминах, но по существу какие-то возражения будут?
Или в целом, Вы согласны с моими доводами?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.