Здравствуйте, lpd, Вы писали:
lpd>У механизма исклюений есть как плюсы так и минусы. Одними из минусов кроме быстродействия являются усложнение логики(и уменьшение прозрачности кода) и увеличение связности программы — когда вызывающий код должен понимать и обрабатывать данные exceptionа из вызываемого.
По быстродействию я уже отписывался — им можно пренебречь. Если по этому поводу есть чего добавить нового из аргументов — то не стесняйтесь.
Насчёт увеличения связности при обработке ошибок — это естественный процесс. И ни один способ обработки ошибок не позволит избежать его. Вызывающий код так или иначе должен понимать и обрабатывать ошибки из вызываемого. Только вот увеличение связности в случае кодов возврата несоизмеримо больше, чем в случае исключений.
Здравствуйте, push, Вы писали:
P>Здравствуйте, lpd, Вы писали:
lpd>>У механизма исклюений есть как плюсы так и минусы. Одними из минусов кроме быстродействия являются усложнение логики(и уменьшение прозрачности кода) и увеличение связности программы — когда вызывающий код должен понимать и обрабатывать данные exceptionа из вызываемого.
P>По быстродействию я уже отписывался — им можно пренебречь. Если по этому поводу есть чего добавить нового из аргументов — то не стесняйтесь.
Ты приводил пример проекта POS-терминала, где тебе исключения помогли. Еще раз замечу, что это была не real-time система. Говорить, что быстродействием _всегда_ можно пренебречь — преувеличение. P>Насчёт увеличения связности при обработке ошибок — это естественный процесс. И ни один способ обработки ошибок не позволит избежать его. Вызывающий код так или иначе должен понимать и обрабатывать ошибки из вызываемого. Только вот увеличение связности в случае кодов возврата несоизмеримо больше, чем в случае исключений.
Исключения передают ошибку на несколько уровней вверх. Получается, что программист, разрабатывающий обработчик, должен познакомиться со всем, что в конечном счете вызывается и может выбросить исключение. По сравнению с постепенной пошаговой явной передачей ошибки на уровни вверх, или по сравнению с общими структурами данных, содержащих описание ситуации, код с исключениями получается короче, но сложнее.
Вообще, исключения в ряде случаев хороши. Но злоупотреблять ими тоже не стоит.
У сложных вещей обычно есть и хорошие, и плохие аспекты.
Берегите Родину, мать вашу. (ДДТ)
Здравствуйте, lpd, Вы писали:
P>>По быстродействию я уже отписывался — им можно пренебречь. Если по этому поводу есть чего добавить нового из аргументов — то не стесняйтесь. lpd>Ты приводил пример проекта POS-терминала, где тебе исключения помогли. Еще раз замечу, что это была не real-time система. Говорить, что быстродействием _всегда_ можно пренебречь — преувеличение.
Ок, перефразирую: в прикладной области стоимость исключений можно принять нулю.
Хотя, с другой стороны, хоть у меня и нет опыта в real-time системах но я подозреваю, что там лучшее место обработки проблемы — это место её возникновения. Потому потребности в исключениях там просто не возникает.
lpd>Исключения передают ошибку на несколько уровней вверх. Получается, что программист, разрабатывающий обработчик, должен познакомиться со всем, что в конечном счете вызывается и может выбросить исключение. По сравнению с постепенной пошаговой явной передачей ошибки на уровни вверх, или по сравнению с общими структурами данных, содержащих описание ситуации, код с исключениями получается короче, но сложнее.
Я вот про что: при работе с исключениями лучшей практикой является построение иерархий. И при работе с конкретным модулем можно ловить только базовое для модуля исключение, что избавляет от изучения их всех. А уже по потребности ловить специализированное исключение. Замена модуля на другой из дополнительных накладных расходов потянет только замену типов отлавливаемых исключений.
При работе с кодами возврата их надо протягивать по стеку к месту обработки (и проверять все коды, чтобы определить что произошло). По пути начальный код ошибки надо преобразовывать к кодам возврата каждой вызывающей функции. И чем дальше место обработки и сложнее программа — тем это всё более трудоёмкий процесс. Замена модуля на другой из дополнительных накладных расходов потребует много работы на модернизацию обработки ошибок.
Пример ситуация: у вас крутится N конечных автоматов.
И ветка в которых возможны отказы просто переводят их в другие состояния.
Вы просто итерируете это с заданными частотами. Вот вам реал тайм.
Ваша задача не вывалится за кванты допустимых времён.
В такой модели зачем вам исключения? А коды ошибок вполне естественны.
В обычном C принято возвращать код ошибки int=0 если всё ок или код ошибки, а не true/false
if (problems) do_somthing_else;
и проверка выглядят примерно так
int fn() {
int h1=0,h2=0,r=1;
if (m1_open(&h1)) goto cleanup;
if (m2_open(&h2,h1)) goto cleanup;
r=0;
cleanup:
if (h2) m2_close(h2);
if (h1) m1_close(h1);
return r;
}
При этом никто не рвёт последовательность выполнения команд. Я точно знаю что будет выполняться всё последовательно.
И вам совершенно фиолетово что там происходит.
Если вызвали в C++ какую-нибуть функцию, то абсолютно нет гарантии что она не выкинет исключение. Или что выкинет когда вы его усердно ловите.
И соответственно вам приходится учитывать это поведения и вы не можете писать код подобный тому что выше.
Вам приходится создавать новые сложность в виде обёрток и потом бороться с появившимися проблемами.
Тут не в быстродействии проблемы. А в том что приходится усложнят код на ровном месте, где можно было бы обойтись и без этого.
Пример вы приготовились ловить исключение и написали
try { z=x/y; } catch(...) { z=0; }
И тут оказывается что функция деления на 0 не выбрасывает исключения для данного типа например double.
Или решили обратится по NULL
try { int *p=0; *p; } catch(...) {}
О горе тут нет исключения тут сигнал. Твою-ж налево.
То есть чтобы обрабтать эту сутуацию вам всё равно приходится знать как себя ведёт ченый ящик. Что в корне не правильно.
Я не спорю что исключения удобная вешь, но они ни коем образом не панацея и ничуть не упрощают обработку ошибок.
Здравствуйте, lpd, Вы писали:
lpd>Ты приводил пример проекта POS-терминала, где тебе исключения помогли. Еще раз замечу, что это была не real-time система. Говорить, что быстродействием _всегда_ можно пренебречь — преувеличение.
Реальное время -- это не про быстродействие. Реальное время -- это про гарантированное время отклика. Там где настоящий hard-real-time и время реакции на уровне микросекунд, там исключения не используются по причинам плохой предсказуемости (а так же и невозможности работы с динамической памятью, например). Но там вообще много чего не используется. Та же динамическая память, как уже было сказано.
Ну а там, где не real-time, а просто online processing, исключения живут и здравствуют. Причем не только в C++.
Здравствуйте, so5team, Вы писали:
S>там исключения не используются по причинам плохой предсказуемости
Причём предсказуемость не в смысле "может вылететь откуда угодно", а в смысле ограничения сверху на время обработки (точнее утилиты для получения значения ограничения). Об этом Страуструп говорил: https://youtu.be/OB-bdWKwXsU?t=4599
Здравствуйте, Evgeny.Panasyuk, Вы писали:
S>>там исключения не используются по причинам плохой предсказуемости
EP>Причём предсказуемость не в смысле "может вылететь откуда угодно", а в смысле ограничения сверху на время обработки (точнее утилиты для получения значения ограничения). Об этом Страуструп говорил: https://youtu.be/OB-bdWKwXsU?t=4599
Ну да, трудно предсказывать, сколько времени займет выполнение конкретного куска кода в присутствии исключений. А посему сложно сказать уложимся в отведенное время реакции или нет.
Здравствуйте, landerhigh, Вы писали:
L>Здравствуйте, kov_serg, Вы писали:
L>_>Пример ситуация: у вас крутится N конечных автоматов.
L>Дальше можно было не продолжать.
Как нистранно микроконтлоллеры по такой схеме управляют оборудованием и всё работает как часы.
Такой-же код можно попросить сгенерировать simulink например.
_>>Я не спорю что исключения удобная вешь, но они ни коем образом не панацея и ничуть не упрощают обработку ошибок.
L>Ага, а еще микроскопом очень удобно открывать бутылки с пивом.
Скажу больше: чайная ложка жидкого азота на кружку пива — очень удобно.
L>P.S. Каждой задаче — свое решение
Для каждой задачи может быть более удобный инструмент. А вот с решением сложно, иногда точного решене не возможно. И решают не точно. При при этом возможно куча вариантов.
Разве я спорю, для синтеза микросхем VHDL или verilog, для написания огромных монстров очень подходит java тудаже подтягивается C#, для сайтов PHP я яваскиптом (даже на этих архитектурно не правильных языках можно работать).
C++ универсальный язык можно применить куда угодно даже для веба, но за это приходится дорого платить — длительное время сборки, большой порог вхождения, дорогие программисты. Тот же erlan позволяет писать в 10раз меньше кода в системах массового обслуживания чем аналог на C++.
С простой язык был задуман для написания не больших програм, которые можно комбинировать языками другого уровня.
И обработка ошибок можно сделать очень по детски, но при этом будет работать и все ресурсы будут освобождены.
А вот на C++ не сможете так написать, не потому что это невозможно, а потому что другая парадигма.
Да и ресурсы должны отслеживаться специальными классами и компилятором чтоб не писать лишнего.
А вот что у нас в C++ для достижения изолированности частей и модульности есть? Только namespace-ы. С одной стороны это минус, но сдругой пока не позволило скатится ко второй яве. Выскажу лично мнение: как только в C++ появятся нормальные модули и изолированность кода, то программы станут такими же как принято в entrprise java "требователбными к железу" и даже разработчики не смогут объяснить что же там у них такое происходит в недрах их абстракций и черных ящиков.
И какой-нибуть серверный hello world будет генерировать гигабайтные war файлы.
ps: "Настоящий физик может писать фортраном на любом языке". Дело не в инструменте, а в методах его использования.
Здравствуйте, push, Вы писали:
P>Ок, перефразирую: в прикладной области стоимость исключений можно принять нулю. P>Хотя, с другой стороны, хоть у меня и нет опыта в real-time системах но я подозреваю, что там лучшее место обработки проблемы — это место её возникновения. Потому потребности в исключениях там просто не возникает.
В реалтаймовых системах обычно можно отделить место, которое требует реалтаймового отклика от места, которое медленно думает и ни в каком реалтайме не нуждается. Причем быстрое место обычно не сложное, а сложное — не быстрое. Соответственно, писать их можно по-разному.
Кроме того, надо понимать, что реалтайм — это наличие гарантий времени отклика, но не обязательно очень маленькие времена отклика. Система, которая гарантированно реагирует на внешний раздражитель в течении секунды — реалтаймовая, система, которая обычно, но без гарантий, реагирует в течении 100 наносекунд — не реалтаймовая.
Здравствуйте, kov_serg, Вы писали:
L>>_>Пример ситуация: у вас крутится N конечных автоматов. L>>Дальше можно было не продолжать. _>Как нистранно микроконтлоллеры по такой схеме управляют оборудованием и всё работает как часы.
Конечный автомат не обрабатывает исключительные ситуации. По определению. Все события КА ожидаемы, иначе это не КА.
Поэтому пытаться доказать "ущербность" исключений, приводя в пример КА, есть демагогия и передергивание.
_>И обработка ошибок можно сделать очень по детски, но при этом будет работать и все ресурсы будут освобождены. _>
Ага, ровно до того момента, пока функция init() не начинает захватывать второй, третий, двадцать пятый ресурс. Не надо сказки рассказывать
_>А вот на C++ не сможете так написать, не потому что это невозможно, а потому что другая парадигма.
На C++? Могу, какие проблемы позвать exit в случае облома? Только зачем?
А вот слабо изобразить функцию init(), которой требуется корректно захватывать более одного ресурса и корректно освобождать все ранее захваченные в случае облома одного, и которая не превращается в страшенное сплетение макарон или очередной пример подхода "C++ плохой, поэтому мы эмулируем деструкторы вручную" на чистом Си?
_>И какой-нибуть серверный hello world будет генерировать гигабайтные war файлы.
A>Я бы мог пройтись практически по каждому пункту твоего поста, и показать что исключения сосут у кодов возврата.
Да проблема не столько в этом.
Обработку ошибок надо ПРОЕКТИРОВУАТЬ.
Хоть так, хоть эдак.
А какими средствами — дело десятое.
И при проектировании решать, какая ситуация исключительная, а какая — нет.
И кстати, в STL-11 обработке ошибок уделено офигительно много внимания. Глава в Джоссатисе — ну очень объемная.
И, кстати, там не только исключения, но и коды ошибок.
Стандартизированы.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, landerhigh, Вы писали:
L>Ага, ровно до того момента, пока функция init() не начинает захватывать второй, третий, двадцать пятый ресурс. Не надо сказки рассказывать
_>>А вот на C++ не сможете так написать, не потому что это невозможно, а потому что другая парадигма. L>На C++? Могу, какие проблемы позвать exit в случае облома? Только зачем?
L>А вот слабо изобразить функцию init(), которой требуется корректно захватывать более одного ресурса и корректно освобождать все ранее захваченные в случае облома одного, и которая не превращается в страшенное сплетение макарон или очередной пример подхода "C++ плохой, поэтому мы эмулируем деструкторы вручную" на чистом Си?
Почему слабо — изображу например так:
// m.hint m_init(void** h);
int m_fn(void *h);
void m_done(void** h);
// m.c#include"m.h"#include"m1.h"#include"m2.h"typedef struct {
void *h1,*h2;
} M;
int m_init(void** h) {
M *m; int r=1;
m=(M*)malloc(sizeof(M));
*h=m; if (!m) return 1;
m->h1=0;
m->h2=0;
if (m1_init(&m->h1)) goto leave;
if (m2_init(&m->h2)) goto leave;
r=0;
leave:
if(r) m_done(h);
return r;
}
void m_done(void** h) {
if (*h) {
M* m=(M*)*h;
m2_done(&m->h2);
m1_done(&m->h1);
free(m);
*h=0;
}
}
int m_fn(void* h) {
M *m=(M*)h;int r=1;
if (m1_fn(m->h1)) goto fail;
if (m2_fn(m->h2)) { r=2; goto fail; }
r=0;
fail:
return r;
}
Где тут макароны всё предельно линейно.
С не эмулирует работу деструкторов. Это C++ ввели деструкторы для облегчения написания кода освобождения ресурсов, но при этом расставили себе грабли. Например дестуктор сначала обновляет указатель на таблицу виртуальных методов потом выполняет код. Что в случае с микрософтовским CThread и борлоновским TThread имеет очень забавные последствия. Поэтому их для автоматического освобождения ресурсов не применить.
Исключения из деструктора не покидаешь, а вдруг мы в финализаторе и еще не готовы аварийно завершиться.
Добавили классы добавили множественное наследование и еще виртаульное наследование. А потом вдруг захотели указатели на функции иметь.
И опять себе создали проблеммы на ровном месте под название указатель на член класса и начали с ним бороться.
Здравствуйте, kov_serg, Вы писали:
L>>А вот слабо изобразить функцию init(), которой требуется корректно захватывать более одного ресурса и корректно освобождать все ранее захваченные в случае облома одного, и которая не превращается в страшенное сплетение макарон или очередной пример подхода "C++ плохой, поэтому мы эмулируем деструкторы вручную" на чистом Си? _>Почему слабо — изображу например так: _>Где тут макароны всё предельно линейно.
Макароны и есть. Хрестоматийные. Всего два ресурса и уже кошмар.
Добавляем еще пару ресурсов и получаем гнездо багов.
Здравствуйте, push, Вы писали:
A>>Я код возврата выдам напрямую в параметр коллбека. А вот что будешь ты делать с исключением — пока загадка. P>Код возврата чего? Ты же сказал, что даешь свой коллбек асинхронному фреймворку — ну у тебя возникла проблема и куда что возвращаешь?
код возврата OpenFile. Я зову асинхронно функцию OpenFileAsync, передаю ей коллбек для возврата результата. Коллбек on_result(FILE). У меня не возникло проблем. Проблемы у тебя, где поставить try catch.
P>>>Я так понимаю начинаем придумывать как сделать плохо с помощью С++?
я хочу понять как сделать хорошо с исключениями
A>>коллбеках которые вызываются из другого контекста ( другой тред, очередной цикл event-loop ). P>std::promise/std::future
зачем мне эти инструменты? Это монады для вытаскивания отложенных значений. В моём примере коллбек зовётся только тогда, когда результат (или его отсутствие) уже готов. Зачем здесь promise/future?
A>>Великолепно. Осталось только узнать, как OpenFile понимает, ожидается ли возникновение ошибки клиентским кодом или не ожидается? P> Это с какой такой радости он должен что-то понимать??? Такое в принципе невозможно. Он сигнализирует о проблеме, а дальше это уже проблема клиента как реагировать. A>>Что если таки ожидается и я в случае ошибки беру данные из другого файла/сети/whatever? P>Ну и бери. И что?
Ох. Тяжело
Смотри:
optional<DATA> loadData(filename) {
optional<DATA> data;
if ( optional<FILE> f = OpenFile(filename) ) {
// OK. File is found.
data = ReadFromFile(*f);
}
else if ( optional<NETFILE> f = OpenFileFromNetwork( config::get_remote_url() + filename) )
{
// Otherwise, try to open file from network share
data = ReadFromNetFile(*f);
}
else if ( optional<FILE> f = OpenFile("default.file") )
{
// Otherwise, try to read data from the default file
data = ReadFromFile(*f);
}
else if ( optional<NETFILE> f = OpenFileFromNetwork(config::get_remote_url() + "default.file") )
{
// Otherwise, try to read data from the default file from network
data = ReadFromNetFile(*f);
}
return data;
}
Очевидно, что для клиентского кода, ошибка в случае OpenFile и ReadFromNetFile вполне ожидаема, и для неё отдельные бранчи. Как loadData будет выглядеть в случае с исключениями?
P>Если в твоей проге это нормальный flow — то не бросай исключение.
Всмысле не бросай? OpenFile и ReadFromNetFile писал не я, а фанат исключений. И он решил что эти функции должны всегда бросать исключение в случае неудачи (push не даст соврать).
P>А в общем случае — отсутствие файла это исключение, так как его наличие должно было быть обеспечено раньше (да, в разработке принято проверять наличие чего-либо, прежде чем использовать).
Вот это новости. Правильно ли я понимаю, что я должен всегда чекать всё, что потребуется OpenFile, чтобы она ненароком не выкинуло исключений? (ну там, наличие файла, права доступа, количество доступных файловых хендлов в системе, итд ).
P>Чем именно неудобно — что сказал правду об отсутствии файла?
тем что вынуждает меня приседать с исключениями. Ещё раз, напиши loadData, только так чтоб OpenFile/OpenFileFromNetwork возвращали FILE/NETFILE соответственно в случае успеха, и бросали исключение в случае неудачи
P>Я вообще-то разрабатываю на плюсах и знаю как и что. С помощью исключений можно получить без напряга столько инфы, сколько при ручном сборе с помощью кодов возврата никогда не будет (ибо банально слишком трудоёмко). Открой для себя boost::exception. По пути раскрутки стека можно собрать практически дамп всех слоёв приложения.
Про раскрутку стека и асихронные вызовы вроде и так всё понятно, это сразу фейл. Но даже с синхронными вызовами, какого типа исключения должен ловить фремворк, например, который зовёт твои функции? (Подсказка: Откуда он будет знать про твои типы исключений? И про boost::exception? )
Здравствуйте, LaptevVV, Вы писали:
LVV>Да проблема не столько в этом. LVV>Обработку ошибок надо ПРОЕКТИРОВУАТЬ.
это несомненно. Вопрос больше в том, почему некоторые считают механизм исключений основой обработки ошибок. В то время как на практике исключения удобны только для весьма специфических сценариев.
LVV>И при проектировании решать, какая ситуация исключительная, а какая — нет.
В том то и дело, что это может решить только клиентский код (тот что выше по стеку). А при написании сервисного кода (того что ниже по стеку) такие решения всегда очень спекулятивны.
Здравствуйте, landerhigh, Вы писали:
L>Макароны и есть. Хрестоматийные. Всего два ресурса и уже кошмар.
Это еще не кошмар. Вы еще кошмаров не видели.
Привидите пример как вы организуете модули в C++ и я посмотрю у кого макароны.
L>Добавляем еще пару ресурсов и получаем гнездо багов.
Ничего подобного. Всё очень локализовано, изолировано, не связно и легко отлаживается.
_>>Где тут макароны всё предельно линейно.
NB>кто-то забыл проинитить h?
Видите как просто найти ошибку. Даже не нужно компилировать, достаточно беглого взгляда На C++ иногда приходится очень глубоко копать что бы понять почему поведение отличается от запланированного.
Здравствуйте, kov_serg, Вы писали:
L>>Макароны и есть. Хрестоматийные. Всего два ресурса и уже кошмар. _>Это еще не кошмар. Вы еще кошмаров не видели.
Сильное заявление.
_>Привидите пример как вы организуете модули в C++ и я посмотрю у кого макароны.
Не вижу проблемы, которая стоит заострения внимания.
L>>Добавляем еще пару ресурсов и получаем гнездо багов. _>Ничего подобного. Всё очень локализовано, изолировано, не связно и легко отлаживается.
Отлаживается? Код, который нужно отлаживать — говнокод по определению. Обсуждению не подлежит.