Так вот Мейер в качестве одной из дисциплин обработки исключений определяет "Повторение" (Retrying), но об этом чуть ниже.
E>>Так же и с файлами. SJA>Что то-же ? Не совсем понял... SJA>С файлами представляю это так: SJA>Если нет места на диске, но алгоритм расчитан так, что ищет место на других дисках и пробует сохранится туда — исключение не требуется (всё обрабатывается на одном уровне). Если уже нигде места нет — бросаем исключение — пусть кто знает обрабатывает.
Вот смотри, как я вижу себе это на примере log-файлов. Есть подсистема ведения логов, которая циклически использует, скажем, пять файлов для сохранения информации. При выполнении очередной операции записи может обнаружена нехватка места на диске. Тогда следует попробовать удалить один из старых файлов и попробовать произвести запись еще раз. Выглядеть это может так (на C++):
void store_log_record( const log_record_t & r )
{
while( true ) // Цикл вместо Мейеровского retry.
{
try
{
// Пытаемся сохранить запись.
try_store_record( r );
// Т.к. исключений нет, то завершаем работу.return;
}
catch( const no_enough_space_t & x )
{
// Нет места, пытаемся освободить.
// Если процедура освобождения окажется неудачной, то она
// породит свое исключение, по которому мы вылетим из
// store_log_record.
try_free_space( x.spaced_needed() );
}
}
}
Т.е. получается, что исключение no_enough_space_t является фатальным для try_store_record() всегда, но не всегда является таковым для store_log_record(). А метод store_log_record() как раз использует мейеровскую дисциплину "Повторение".
SJA>Отсюда моё ИМХО: Исключение нужно бросать в том случае, если на данном логическом уровне (в данном модуле) неизвестен алгоритм работы с некоторой ситуацией. Которая и является исключительной на данном уровне.
Так ведь и я о том же. Но вот вернемся к моему исходному примеру.
void process_trx( const some_trx_t & trx )
{
try
{
// Нормальная обработка транзакции, расчитанная на обычные условия.
// В этой функции неизвестно, как обрабатывать лишние и повторные
// транзакции, поэтому там порождаются исключения.
ordinal_processing( trx );
}
catch( const too_much_trx_t & x )
{
// Обработка N+1 транзакции.
send_negative_response( trx, error_reason::too_much_trx );
}
catch( const repeated_trx_t & x )
{
// Обработка повторной транзакции.
ignore_repeated_trx( trx );
}
catch( const std::exception & x )
{
// А вот это уже действительно плохо!
cleanup();
initiate_shutdown();
}
}
Т.е. уровень ordinal_processing() не может справится с ошибками too_much_trx и repeated_trx, поэтому он и порождает исключения. Но вот уровен process_trx как раз знает, как с этими ошибками бороться, и именно он инициирует их обработку. Т.е. в точности то, что ты и сказал (имхо).
Но ведь важно то, что для всего алгоритма process_trx ситуации too_much_trx и repeated_trx -- это не ошибки, это вполне штатные ситуции. Поэтому таких исключений нет снаружи process_trx. Но сам process_trx реальзован так, что он использует данные исключения у себя локально и для ordinal_processing() они как раз являются ошибками. Т.е. то, что на одном уровне абстракции является ошибкой, на другом -- просто штатная ситуация.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Sergey J. A. wrote:
> Именно. Это не ошибка, а часть алгоритма. Совершенно точно извесно как > она будет обрабатыватся, причём самим сборщиком мусора. Поэтому нет > никакой нужды передавать эту ситуацию в верхние уровни. Следовательно > это и не должно считатся исключительной ситуацией и не должно вызывать > исключение.
Да, напомните мне, плиз, почему в Java/C# есть OutOfMemoryException
Кстати, даже с GC вполне может быть, что памяти просто не хватает
физически (то есть она занята живыми объектами, а не мусором). Тогда
исключение по OOM — вполне логично, так как приложение в ответ на него
может удалить часть данных (почистить кэши, например) и освободить
достаточно памяти для дальнейшей работы.
Здравствуйте, reductor, Вы писали:
ГА>>>Избежать-то всегда можно. Вот и избегают некоторые фанатичные последователи этого правила, выходя из цикла 3-го уровня вложенности с помощью булевских флагов или даже бросая исключения .
IT>>Совершенно верно. Это правило было актуально 15 лет назад. Сегодня оно является таким же устаревшим как венгерка и SELECT*.
R>Устарело? R>Что, разобрались с остановкой тьюринг-полного автомата?
При чем тут?
R>научились писать после этого статические анализаторы, которые находят и предсказывают поведение GOTO и множественного return?
Да, особенно хорошо это работает в том же С++ с инлайн-подстановкой тел ф-ий.
R>Вырастили поколение суперлюдей, которые в состоянии смотрететь и тут же понимать что происходит, разбивая это в уме на подвыражения?
При чем тут? Вот стандартный алгоритм линейного поиска с 2-мя return:
Очень хорошо всеми понимается
R>Наконец, уже написали компиляторы нормально это разруливающие и процессоры, у которых нет оверхеда от этого?
Компилятор именно в 1 return и разруливает, при чем ему остается гораздо больше свободы для оптимизации, чем при явном указании, как именно на 1 return надо выйти. Как пример — обратное развертывание while и for — циклов.
Поэтому все остальные аргументы — слабоваты, из-за того, что компилятор сам сводит кол-вы выходов из ф-ии к требуемому кол-ву. А аргумент насчет понимания логики программы — субъективный и сильно надуманный. Гораздо большее число программистов совершенно не испытывает затруднений при разработке кода с многими return.
предоставить первый.
R>Не поймите меня правильно, но мне казалось, что я это сделал. R>И тут и там.
Сделал, на секунд позже чем я здесь ответил. Ну да фиг с ним.
В общем, как я понял, ты очень сильно переживаешь не перенапрягуться ли компиляторы Не поверишь, но мне это по барабану. Пусть напрягаются, это их работа. Тем более что в том же C# return — это банальный jmp в конец метода, никаких деструкторов раскручивать не надо. К тому же, проблемы о которых ты говоришь возникают не только при return, есть ещё break, continue, try/cathc/finally. От этого всего тоже надо отказаться?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, reductor, Вы писали:
R>Вообще непонятно почему все так против использования иксепшенов для описания логики, когда в большинстве рассматриваемых тут языков это самый быстрый и простой способ эмуляции pattern matching (не знаю как это по-русски), (чего о тех же классах с коллбеками по ним не скажешь).
У меня есть как минимум две причины.
Первая — эксепшены довольно тормозная вещь. В данном примере при перемещении коретки пользователем это вряд ли будет заметно. Но программист использующий подобный паттерн легко напишет и серверную логику на эксепшинах. А чё?
Вторая. Современные средства отладки сегодня позволяют практически отказаться от пошагового брождения по программе с целью локализации места ошибки. При возникновении исключения — отладчик автоматически останавливается в нужном месте. Время фикса багов в таком случае возрастает на порядки. Использование исключений где попало, особенно в циклически повторяющемся коде убивает всю идею. И приходится опять отлаживать код по старинке, с точками останова, бесконечными заходами и выходами во все возможные методы в программе.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT wrote:
> Вторая. Современные средства отладки сегодня позволяют практически > отказаться от пошагового брождения по программе с целью локализации > места ошибки. При возникновении исключения — отладчик автоматически > останавливается в нужном месте. Время фикса багов в таком случае > возрастает на порядки. Использование исключений где попало, особенно в > циклически повторяющемся коде убивает всю идею. И приходится опять > отлаживать код по старинке, с точками останова, бесконечными заходами > и выходами во все возможные методы в программе.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, reductor, Вы писали:
СГ>>>А что здесь есть кто-то утверждающий полезность единственности точки выхода из процедуры?
R>>Ничего не хочу сказать ни про кого плохого, но я возьмусь утверждать, что несколько точек выхода — это хуже, чем один. R>>Во всех случаях, когда этого можно избежать.
IT>Почему? Можно аргументировать?
Плодятся временные переменные. Особенно, когда размыты логические границы метода, т.е. действие метода можно разбить на несколько отдельных. Мне нужно получить лишь информацию под номером 0, но компилятор вежливо просит создать переменную для получения и информации под номером 1.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, reductor, Вы писали:
R>>Вообще непонятно почему все так против использования иксепшенов для описания логики, когда в большинстве рассматриваемых тут языков это самый быстрый и простой способ эмуляции pattern matching (не знаю как это по-русски), (чего о тех же классах с коллбеками по ним не скажешь).
IT>У меня есть как минимум две причины. IT>Первая — эксепшены довольно тормозная вещь. В данном примере при перемещении коретки пользователем это вряд ли будет заметно. Но программист использующий подобный паттерн легко напишет и серверную логику на эксепшинах. А чё? IT>Вторая. Современные средства отладки сегодня позволяют практически отказаться от пошагового брождения по программе с целью локализации места ошибки. При возникновении исключения — отладчик автоматически останавливается в нужном месте. Время фикса багов в таком случае возрастает на порядки. Использование исключений где попало, особенно в циклически повторяющемся коде убивает всю идею. И приходится опять отлаживать код по старинке, с точками останова, бесконечными заходами и выходами во все возможные методы в программе.
Третье. Средство профилирования, способные перехватывать любые ошибки в коде, засыплят команду тестеров сообщениями, содержищие подчас бесполезную информацию.
Здравствуйте, Ракот, Вы писали:
Р>Плодятся временные переменные. Особенно, когда размыты логические границы метода, т.е. действие метода можно разбить на несколько отдельных. Мне нужно получить лишь информацию под номером 0, но компилятор вежливо просит создать переменную для получения и информации под номером 1.
Избавиться от лишних локальных переменных — задача оптимизатора.
Если нам не помогут, то мы тоже никого не пощадим.
IT wrote:
>>> Как assert'ы помогут мне избавиться от логики на ексепшинах? > C>Для отладки их используйте, а не исключения > OK, поставим вопрос по другому. Как они мне помогут локализовать место > ошибки?
Вообще-то, когда срабатывает assert, то у меня появляется окошечко
"Assertion failed in file .... line ..." с предложением запустить
отладчик. Если работает под отладкой — сразу вываливается в отладчик.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Ракот, Вы писали:
Р>>Плодятся временные переменные. Особенно, когда размыты логические границы метода, т.е. действие метода можно разбить на несколько отдельных. Мне нужно получить лишь информацию под номером 0, но компилятор вежливо просит создать переменную для получения и информации под номером 1.
IT>Избавиться от лишних локальных переменных — задача оптимизатора.
Все, конечно, верно. Только в коде это не уберешь — теряется чистота.
Здравствуйте, Cyberax, Вы писали:
C>Вообще-то, когда срабатывает assert, то у меня появляется окошечко C>"Assertion failed in file .... line ..." с предложением запустить C>отладчик. Если работает под отладкой — сразу вываливается в отладчик.
У меня тоже самое появляется при необработанных исключениях. А вот если их затрайкечить... Насколько мне известно, assert тоже кидает исключение, так что поймать его и придушить нет никаких проблем.
Если нам не помогут, то мы тоже никого не пощадим.
IT wrote:
> C>Вообще-то, когда срабатывает assert, то у меня появляется окошечко > C>"Assertion failed in file .... line ..." с предложением запустить > C>отладчик. Если работает под отладкой — сразу вываливается в отладчик. > У меня тоже самое появляется при необработанных исключениях. А вот > если их затрайкечить... Насколько мне известно, assert тоже кидает > исключение, так что поймать его и придушить нет никаких проблем.
В С++ сработавший assert вызывает функцию DebugBreak, так что поймать ее
сложно будет. Ну а в С# можно поставить, чтобы отладчик срабатывал на
вылетевшее AssertionFailedException. Ну или вызвать DebugBreak через
interop.
Здравствуйте, Cyberax, Вы писали:
C>reductor wrote:
>> Если чуть серьезнее, то логика — это штука немного формальная. Желание >> джампнуть куда-нибудь (частным случаем чего кроме GOTO является и >> множественный return), и сделать поведение программы непредсказуемым, >> это не от большого понимания того, что сам делаешь. >> А еще — это не религия и понятие "фанатизм" к ней не применимо. Люди >> или могут обосновать свой вывод и выбор или нет.
C>Нет, это уже религия. Все нормальные программисты уже давно перестали C>флеймить по поводу GOTO и структурного программирования, так как по C>нынешним меркам это уже далеко не самый главный вопрос. Все C>программисты, которых я знаю, думают примерно так: "GOTO это плохо?" — C>"Плохо". "Структурное программирование хорошо?" — "Да". "Единый return, C>выход из вложенных циклов по флагам, отсутствие break — оно надо?" — "А C>нафига?"
Я не знаю что такое "нормальные программисты". Тем более что такое "все нормальные программисты".
Я знаю, что такое формальная логика и изоморфизм Карри-Говарда. А так же как я могу и как не могу обосновать, что мой модуль выполняет именно то, что он него ожидается при всех условиях.
Что касается выхода из циклов по флагам и отсутствия break, то вообще не понимаю в чем проблема — что мешает вынести цикл в отдельную функцию и выходить исключительно из цикла, делая код более предсказуемым?
return в цикле и множественный return — это вовсе не то же самое.
Я не очень хочу продолжать переливать из пустого в порожнее еще 1000 писем в мейлбоксе. Если у кого-то есть код и он может показать, что с множественным выходом (не путать с выходом из цикла и несколькими _условиями_ выхода) будет проще и обосновать и отладить и оно будет быстрее работать — прекрасно. А пока что это разговоры в пользу бедных. И шарлатанство.
предоставить первый.
R>>Не поймите меня правильно, но мне казалось, что я это сделал. R>>И тут и там.
IT>Сделал, на секунд позже чем я здесь ответил. Ну да фиг с ним.
IT>В общем, как я понял, ты очень сильно переживаешь не перенапрягуться ли компиляторы Не поверишь, но мне это по барабану. Пусть напрягаются, это их работа. Тем более что в том же C# return — это банальный jmp в конец метода, никаких деструкторов раскручивать не надо. К тому же, проблемы о которых ты говоришь возникают не только при return, есть ещё break, continue, try/cathc/finally. От этого всего тоже надо отказаться?
Компиляторы конечно пусть выполняют свою работу, но есть все же некоторое соответствие между количеством их работы и нашим пониманием того, что мы делаем. А так же между количеством их работы и умственным напряжением того человека, который будет читать наш код. Функция со множественным выходом изоморфна нескольким с одним. break внутри цикла изоморфен функции с единственным выходом из тела цикла, continue уже становится понятно изоморфен чему, ага? try/catch/finally — это, извините, просто continuation. то есть по сути, это не выход, а _вход_. Понимая это, можно сразу увидеть, где нужно несколько функций, а где нет и потому и несколько return лишние. Понимая это, начинаешь писать код и понятнее и короче, чем писал до того.
И главное — себе любиомому потом время экономишь на отладке.
И отказываться я никого ни от чего не убеждаю. Это занятие бессмысленное.
Здравствуйте, vdimas, Вы писали:
V>for(int i=0; i<arr.size(); i++) V> if(arr[i]==x) V> return i; V>return -1; V>[/ccode]
V>Очень хорошо всеми понимается
Вы, мне кажется, путаете две близкие вещи.
Посмотрим, вот ваша функция на Haskell, где вообще нет оператора выхода:
contains _ [] = False
contains y (x:xs) | x == y = True
| otherwise = contains y xs
1-в-1, То же самое.
А вот функция, которая пытается сделать сразу 10 вещей закрывая дыры в логике преждевременным выходом так легко формальным образом не выразится. Можно делать выводы.
Несколько выходов — это всегда повод к тому, чтобы задать себе вопрос — не создаю ли я тут эмуляцию вложеных функций. И ответить на этот вопрос или утвердительно или отрицательно.
А все остальное уже из этого следует.
reductor wrote:
> Я не знаю что такое "нормальные программисты". Тем более что такое > "все нормальные программисты". > Я знаю, что такое формальная логика и изоморфизм Карри-Говарда. А так > же как я могу и как не могу обосновать, что мой модуль выполняет > именно то, что он него ожидается при всех условиях.
И что? Чем этому мешает break и goto? Прекрасно можно доказывать
кооректность программ и с ними.
> Что касается выхода из циклов по флагам и отсутствия break, то вообще > не понимаю в чем проблема — что мешает вынести цикл в отдельную > функцию и выходить исключительно из цикла, делая код более предсказуемым?
Слишком много переменных может потребоваться передавать в виде параметров.
> Я не очень хочу продолжать переливать из пустого в порожнее еще 1000 > писем в мейлбоксе. Если у кого-то есть код и он может показать, что с > множественным выходом (не путать с выходом из цикла и несколькими > _условиями_ выхода) будет проще и обосновать и отладить и оно будет > быстрее работать — прекрасно. А пока что это разговоры в пользу > бедных. И шарлатанство.
Нет уж, сами затеяли спор — сами и показывайте код, который
множественный выход делает недоказуемым и вообще отстойным.