Здравствуйте, Сергей Губанов, Вы писали:
E>>Во что же тогда программирование превратиться?
СГ>А Вы подумайте.
А что мне думать? Я уже одинадцать лет этим профессионально занимаюсь.
Программирование -- это работа, часто напряженная, нудная и тяжелая. Но для меня это еще и хобби, поэтому ни смотря ни на что я еще продолжаю получать от нее удовольствие. Временами, как раз, от возможности использования исключений.
Кстати, со мной лучше на "ты", поскольку, как правило, переход на "Вы" означает начало выяснения отношений.
СГ>Если верить рекламе языков (Java, C#), то "программирование — это лёгкое и непринуждённое занятие". Обманыевает эта реклама, хотя бы пять процентов головного мозга напрягать всё-равно надо.
Ну блин, верить рекламе...
В отличии от Запада, у нас был хороший афоризм: "Хороший товар в рекламе не нуждается"
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Единственное правильное использование ловли исключений — это чтобы в случае чего грамотно прибрать за собой.
Оставим в стороне "единственную правильность ловли исключений", это вопрос религиозный. СГ>При наличии автоматической сборки мусора, прибрать за собой — гораздо менее актуальная задача.
Т.е. при наличии сборки мусора нарушенные инварианты программы менее вредны?
Здравствуйте, eao197, Вы писали:
E>А вот по поводу безопасности исключений если задуматься, то вообще голова кругом пойдет. Мне, например, интересно. В C++ много говорится о гарантиях безопасности (ну там базовая гарантия, сильная гарантия). А в других языках с исключениями (в том же Ruby, к примеру) от этом не упоминают. Из-за чего, спрашивается? В языках с GC нет таких пагубных последствий от исключений? Или просто их community еще до таких серьезных вопросов не доросло?
Трудно ответить, крайне мало опыта в других языках. В С++-сообществе есть много горячих тем, которые практически никого больше не волнуют, вроде "у меня работает, но что скажет Стандарт" , исключения в деструкторах, умные указатели и т.д. Но обработка ошибок — проблема фундаментальная в любом языке, и если кого-то мало волнуют вопросы безопасности исключений, то это говорит или о низкой квалификации программиста, или о низкой стоимости сбоя в разрабатываемой системе.
Здравствуйте, gear nuke, Вы писали:
GN>Смысл исключений — увеличить скорость работы. Вместо постоянных лишних проверок вводится иной механизм, отнимающий время лишь в исключительных случаях. Механизм настолько важен, что обычно реализуется аппаратно процессором. Например, механизм подкачки (paging) в современных ОС реализован именно на аппаратных исключениях. По-другому его сделать так эффективно нереально. В силу существования аппаратного механизма, большенство компилируемых языков его используют и для высокоуровневых исключений.
Для этого тоже, но далеко не в первую очередь, иначе исключения были бы только в низкоуровненвых языках. Вы, как эксперт в ассемблере, смещаете акцент, ИМХО, не в ту сторону. А высокоуровневые языки существуют потому, что программы пишутся не для компьютеров, а для людей, и здесь исключения проявляют себя с лучшей стороны.
Здравствуйте, Сергей Губанов, Вы писали:
E>> В языках с GC нет таких пагубных последствий от исключений?
СГ>Единственное правильное использование ловли исключений — это чтобы в случае чего грамотно прибрать за собой. СГ>При наличии автоматической сборки мусора, прибрать за собой — гораздо менее актуальная задача.
Не сказал бы. Если бы это было так, то не придумывали бы таких вещей, как ScopeGuard.
Вот например, есть у меня класс, который содержит внутри два map-а для доступа к объекту по разным ключам:
void some_container_t::insert( const some_object_t & object )
{
// Если исключение произойдет здесь, то ничего страшного.
m_first_key_map[ object.first_key() ] = &object;
// А вот если здесь, то в первой карте останется элемент, для которого нет соответствия во второй карте.
m_second_key_map[ object.second_key() ] = &object;
}
Подобный код обеспечивает только базовую гарантию (при условии, что ее предоставляют объекты m_first_key и m_second_key). Но вот пользоваться экземпляром some_container_t уже будет нельзя. Здесь было бы более удобно, если бы some_container_t::insert предоставлял сильную гарантию -- т.е. возникновение исключения оставляет его в неизменном состоянии. Наиболее простой путь для этого -- изымать вставленный в m_first_key_map объект, если при вставке во вторую карту произошло исключение:
(хотя самым безопасным было бы создание копий m_first_key_map, m_second_key_map, их модификация и операция swap с исходными значениями m_first_key_map, m_second_key_map. Однако понятно, что для некоторых задач это совершенно неприемлимо).
Теперь попробуем посмотреть, как бы это было сделано в Ruby, где есть GC. Тривиальное решение (базовая гарантия):
Тривиальное решение по обеспечению сильной гарантии.
def insert( object )
@first_key_map[ object.first_key ] ||= object
begin
@second_key_map[ object.second_key ] ||= object
rescue
@first_key_map.erase object.first_key
raise
end
end
А вот более изощренное решение:
def exception_guard( rescue_action )
begin
yield
rescue
rescue_action.call
raise
end
end
def insert( object )
@first_key_map[ object.first_key ] ||= object
exception_guard(
# Этот код запуститься, если возникнет исключение при вставке во вторую карту.
Proc.new do @first_key_map.erase object.first_key end
) do
@second_key_map[ object.second_key ] ||= object
end
end
Т.е., проблемы те же. И даже способы решения приблизительно одинаковые (в Ruby даже, имхо, способы создания ScodeGuard-ов попроще). Но вот не говорят в Ruby о безопасности исключений и базовые/сильные гарантии (да и здесь .NET-приверженцы от этом не упоминают). Почему?
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Andy77, Вы писали:
СГ>>Я делю ошибки на две категории: СГ>>1) ошибки в данных (программа на вход получила неправильные данные); СГ>>2) ошибки в программе (программист ошибся: а) просто ошибся; б) сложно ошибся — в дизайне/архитектуре).
СГ>>Ошибки категори (1) легко обрабатывается без механизма exceptions, так надо проектировать просто.
A>Писать функции, возвращающие код ошибки? Иногда так и стоит делать, но за правило я бы это ни в коем случае не брал.
eao197 wrote:
> А вот по поводу безопасности исключений если задуматься, то вообще > голова кругом пойдет. Мне, например, интересно. В C++ много говорится > о гарантиях безопасности (ну там базовая гарантия, сильная гарантия). > А в других языках с исключениями (в том же Ruby, к примеру) от этом не > упоминают. Из-за чего, спрашивается? В языках с GC нет таких пагубных > последствий от исключений? Или просто их community еще до таких > серьезных вопросов не доросло?
Скорее второе. Ну и действительно важные приложения (типа app-серверов)
стараются писать так, чтобы в случае вылета исключений можно было бы
просто прибить working set у вылетевшего модуля и запустить его заново.
Здравствуйте, Глеб Алексеев,
GN>>Смысл исключений — увеличить скорость работы. Вместо постоянных лишних проверок вводится иной механизм, отнимающий время лишь в исключительных случаях. []
ГА>Для этого тоже, но далеко не в первую очередь, иначе исключения были бы только в низкоуровненвых языках. Вы, как эксперт в ассемблере, смещаете акцент, ИМХО, не в ту сторону. А высокоуровневые языки существуют потому, что программы пишутся не для компьютеров, а для людей, и здесь исключения проявляют себя с лучшей стороны.
Ну я с "не той стороны" всё равно противоречий не вижу . В HLL основной упор делается на лёгкость понимания человеком — так исключения и упрощают код, исключая из исходника множество проверок. И не дают возможность "забыть" проверить. Скорость и простота в данном случае сильно связаны. Если смотреть с позиции HLL — то, естесственно, в первую очередь простота более заметна .
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
eao197 wrote:
> Т.е., проблемы те же. И даже способы решения приблизительно одинаковые > (в Ruby даже, имхо, способы создания ScodeGuard-ов попроще). Но вот не > говорят в Ruby о безопасности исключений и базовые/сильные гарантии > (да и здесь .NET-приверженцы от этом не упоминают). Почему?
На Ruby обычно, в основном, скрипты пишут. Ну а там упадет — и ладно. В
C#/Java в десктопных приложениях тоже такой же подход, в основном.
В серверных приложениях предпочитают ограничивать объекты строго
контролируемым окружением, чтобы его при желании можно было просто
прибить и создать снова.
Здравствуйте, Cyberax, Вы писали:
>> Т.е., проблемы те же. И даже способы решения приблизительно одинаковые >> (в Ruby даже, имхо, способы создания ScodeGuard-ов попроще). Но вот не >> говорят в Ruby о безопасности исключений и базовые/сильные гарантии >> (да и здесь .NET-приверженцы от этом не упоминают). Почему?
C>На Ruby обычно, в основном, скрипты пишут. Ну а там упадет — и ладно.
Ну не скажи. Есть и скрипты, которые работают в режиме 24x7
Вот у меня, скажем, есть Ruby скрипты, которые архивируют старые логи и делают snapshot-ы восстановочных БД. Затем засыпают, просыпаются и повторяют свои действия.
Один раз упали (причина, кстати, то же была интересная ). Итог -- быстро исчерпалось свободное место на тестовом сервере.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Сергей Губанов, Вы писали: СГ>Ничего неожиданного в этом нет. С самого начала известно, что соединение может разорваться в любой момент, да и денег на счете может быть меньше чем запрошено.
И что? Сергей, вам что, непонятно, чем отличается успешная отправка килобайта данных от отправки только половины? Или вы не догадываетесь, что невозможно предусмотреть все мыслимые случаи в низкоуровневой библиотеке, т.к. это противоречит принципам инкапсуляции?
Нет, я пожалуй воздержусь от дальнейшего разжевывания. Ну-ка, приведите мне пример кода, на любом языке, который выполняет следующие действия:
1. Снимает заданное количество рублей со счета А
2. Кладет это количество рублей на счет Б.
3. Записывает данные о транзакции в файл
4. Если что-то не удалось сделать, отправляет письмо на определенный адрес, с указанием подробностей ошибки.
Пожалуйста, воздержитесь от использования исключений. А мы посмотрим.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Если верить рекламе языков (Java, C#), то "программирование — это лёгкое и непринуждённое занятие". Обманыевает эта реклама, хотя бы пять процентов головного мозга напрягать всё-равно надо.
Не очень хотелось высказываться здесь, но, похоже, у нас разные подходы к получению значения 5 процентов.
А Вы знаете языки, которые не заставляют разработчика напрягаться вообще?
Здравствуйте, Sinclair, Вы писали:
S>Ну-ка, приведите мне пример кода, на любом языке, который выполняет следующие действия: S>1. Снимает заданное количество рублей со счета А S>2. Кладет это количество рублей на счет Б. S>3. Записывает данные о транзакции в файл S>4. Если что-то не удалось сделать, отправляет письмо на определенный адрес, с указанием подробностей ошибки.
S>Пожалуйста, воздержитесь от использования исключений. А мы посмотрим.
Во-первых, и в главных, не "Ну-ка"-йте тут. Что ещё за моду взяли.
Во-вторых, Вы забыли определить какими примитивами разрешено пользоваться.
А то ведь, можно и так:
PROCEDURE Move (VAR src, dst, sum: REAL): BOOLEAN;
BEGIN
IF src >= sum THEN
src := src - sum;
dst := dst + sum;
...
Здравствуйте, Сергей Губанов, Вы писали:
СГ>PROCEDURE Move (VAR src, dst, sum: REAL): BOOLEAN; СГ>BEGIN СГ> IF src >= sum THEN СГ> src := src — sum; СГ> dst := dst + sum; СГ>... СГ>[/pascal]
Совершенно очевидно, что такой код к использованию не пригоден, правда?
А вообще — слишком много внимания к проблеме, не стоящей выеденного яйца. Комрад не понимает, что такое исключения и пытается бороться с несуществующими проблемами. Пускай борется в одиночку.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Во-первых, и в главных, не "Ну-ка"-йте тут. Что ещё за моду взяли.
Сергей, это дружественная форма общения. Мы тут давно знаем, кто такой Антон, кто ты, тесный круг общения позволяет между собой иногда отбрасывать условности реального мира. Эта фраза — не оскорбление, это эмоциональный заряд. Поверь, как бы мы не спорили о "всём сущем", к тебе как личности отношение самое дружественное.
СГ>А то ведь, можно и так:
СГ>PROCEDURE Move (VAR src, dst, sum: REAL): BOOLEAN;
СГ>BEGIN
СГ> IF src >= sum THEN
СГ> src := src - sum;
СГ> dst := dst + sum;
СГ>...
СГ>
Ага! И полюбому зачисляем на счёт-приёмник сумму. Здорово. Все бы так уже богачами были.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Во-первых, и в главных, не "Ну-ка"-йте тут. Что ещё за моду взяли.
СГ>Во-вторых, Вы забыли определить какими примитивами разрешено пользоваться.
Такими, которые обеспечивают ACID. Т.е. снятие/положение денег — некая сложная, но атомарная операция. Не обязательно приводить их полный код. Можно пользоваться ими как черными ящиками.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Такими, которые обеспечивают ACID. Т.е. снятие/положение денег — некая сложная, но атомарная операция. Не обязательно приводить их полный код. Можно пользоваться ими как черными ящиками.
Программа, я так понимаю, работает в банке со счета которого деньги снимаются.
TYPE
СчётВУдалённомБанке* = POINTER TO ABSTRACT RECORD END;
ЛокальныйСчёт* = POINTER TO RECORD
остаток: Деньги;
журнал: Журнал; (* not NIL *)
(* ... *)END;
Журнал* = POINTER TO ABSTRACT RECORD END;
PROCEDURE (this: СчётВУдалённомБанке) Зачислить* (сумма: Деньги; откуда: Откуда; причинаОтказа: Причина): BOOLEAN, NEW, ABSTRACT;
PROCEDURE (this: Журнал) РапортОбУдачномОсуществленииДенежногоПеревода* (src: ЛокальныйСчёт; dst: СчётВУдалённомБанке; сумма: Деньги), NEW, ABSTRACT;
PROCEDURE (this: Журнал) РапортОбОтказеУдалённогоБанкаНаЗачислениеДенег* (src: ЛокальныйСчёт; dst: СчётВУдалённомБанке; сумма: Деньги; причина: Причина), NEW, ABSTRACT;
PROCEDURE (this: Журнал) РапортОНехваткеДенегНаСчётеДляОсуществленияДенежногоПеревода* (src: ЛокальныйСчёт; dst: СчётВУдалённомБанке; сумма: Деньги), NEW, ABSTRACT;
PROCEDURE (this: ЛокальныйСчёт) ОсуществитьДенежныйПеревод* (счёт: СчётВУдалённомБанке; сумма: Деньги): BOOLEAN;
VAR результат: BOOLEAN; причинаОтказа: Причина;
BEGIN
результат := FALSE;
IF (счёт # NIL) & (сумма > 0) THEN
this.НачалоЭксклюзивнойОперации;
IF this.остаток >= сумма THEN
IF счёт.Зачислить(сумма, this.ДеньгиОтМеня, причинаОтказа) THEN
this.остаток := this.остаток - сумма;
this.журнал.РапортОбУдачномОсуществленииДенежногоПеревода(this, счёт, сумма);
результат := TRUE;
ELSE
this.журнал.РапортОбОтказеУдалённогоБанкаНаЗачислениеДенег(this, счёт, сумма, причинаОтказа)
END
ELSE
this.журнал.РапортОНехваткеДенегНаСчётеДляОсуществленияДенежногоПеревода(this, счёт, сумма)
END;
this.КонецЭксклюзивнойОперации;
END;
RETURN результат
END ОсуществитьДенежныйПеревод;
Запись в лог и отправку по e-mail делает объект Журнал.