Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, enji, Вы писали:
E>>ой ли?
_>Ну во-первых если делать всё всё по правильному, то не забудь ещё реализацию своего класса исключений для данной функции... )
не надо, есть boost::exception. Ну и класс нужен не для функции, а для подсистемы в целом — возможно, несколько классов.
_>А во-вторых я на практике обычно не использую даже коды возврата — только булево значение, т.к. обычно пользователю вообще не нужно знать низкоуровневые детали. А в данном примере я написал так, чтобы никто не придрался (мол с исключениями передаётся больше информации).
Дык и в самом деле больше С другой стороны можно сгородить свой собственный тип возврата — в который пихать доп инфу. Что-то вроде boost::error_code.
Основной плюс исключений, имхо — их нельзя проигнорировать.
Здравствуйте, uncommon, Вы писали:
I>>Есть мнение, что имеет смысл учиться не синтаксису, а более важным вещам — времени всего 24 часа.
U>В обсуждаемом вопросе синтаксиса практически нет. Здесь дело в непонимании самой концепции. А синтаксис, какой бы он не был, стоит просто запомнить. Это часть профессии.
Языковая фича == синтаксис. Исключения вызывают проблемы в основном в С++. В Джаве той же или джаваскрипте как то незаметно таких проблем.
Здравствуйте, Cyberax, Вы писали:
_>>>Это в твоих проектах такие имена функций обычно, да? ))) I>>Намекаешь, что не в состоянии провести заявленое различие ? C>Очевидно, что метод вот такой: "fn Y() -> Result<BOOL, Error> "
Какие методы — я указал. Они возвращают BOOL. В принципе, могут возвращать что угодно — строку, указатель, хендл.
Здравствуйте, enji, Вы писали:
E>Дык и в самом деле больше С другой стороны можно сгородить свой собственный тип возврата — в который пихать доп инфу. Что-то вроде boost::error_code.
E>Основной плюс исключений, имхо — их нельзя проигнорировать.
Основной плюс — изоляция от обработки. С кодами возврата, если внезапно между обработкой и местом ошибки вклинивается пару методов, надо начинать переписывание всего нижележащего слоя или начинать городить раскрутку ошибки вручную, чего на практике и наблюдаем.
Принципиально есть единственный способ всё забороть — нанять архитектора-всемогутора, который заглянет в будущее и создаст нерушимый дизайн на любой срок вперёд.
Без этого будет тяжело сделать любую более менее полезную логику. Скажем, есть отсылка емейла. В зависимости от приложения может быть достаточно и "невозможно отправить дизайн", а может и не хватит "аттачмент не доступен — не хватает прав на файловую систему". В первом случае хватит булевого кода возврата, а во втором надо пилить распространение информации об ошибках.
Что характерно, второй случай никак не вмещается в концепцию кодов возврата. На самом деле не вмещается уже простая линейная логика.
x = a();
if(x == null)
return null;
x = b(x);
if(x == null)
return null;
x = c(x);
if(x == null)
return null;
x = d(x);
if(x == null)
return null;
return e(x);
Здравствуйте, Ikemefula, Вы писали:
I>Что характерно, второй случай никак не вмещается в концепцию кодов возврата. На самом деле не вмещается уже простая линейная логика.
Да вмещается он, просто надо вместо целого кода возвращать объект. А там уже можно делать вложенные коды и т.п. Но от ручного распространения без какого-то сахара со стороны языка не уйти, да
Здравствуйте, Cyberax, Вы писали:
U>>Деструктор вынужден игнорировать ошибку в close. В результате из деструктора невозможно сообщить об ошибке. Поэтому все операции, которые могут закончиться с ошибкой надо делать до вызова деструктора. C>Второй вопрос, как быть с RAII-кодом, в котором в деструкторах огромное количество логики?
Здравствуйте, enji, Вы писали:
I>>Что характерно, второй случай никак не вмещается в концепцию кодов возврата. На самом деле не вмещается уже простая линейная логика.
E>Да вмещается он, просто надо вместо целого кода возвращать объект. А там уже можно делать вложенные коды и т.п. Но от ручного распространения без какого-то сахара со стороны языка не уйти, да
Наверное ирония была плохо заметна — `концепция кодов вовзрата` предусматривает только вещи навроде open('a') || open('b') || open('c');
Здравствуйте, Cyberax, Вы писали:
C>Здравствуйте, uncommon, Вы писали:
U>>Деструктор вынужден игнорировать ошибку в close. В результате из деструктора невозможно сообщить об ошибке. Поэтому все операции, которые могут закончиться с ошибкой надо делать до вызова деструктора. C>Второй вопрос, как быть с RAII-кодом, в котором в деструкторах огромное количество логики?
Например? Что-то мне кажется, что такие деструкторы — результат плохого дизайна. Как может быть много логики в деструкторе RAII класса, где должны только освобождаться ресурсы?
vsb>Какие есть серьёзные аргументы против исключений и за (извиняюсь за каламбур) возврат к кодам возврата? Ведь это же ужасный код, когда после вызова каждой функции мы тут же проверяем err и если он не null, просто передаём его наверх. Это то, от чего избавляют исключения. vsb>Я перечитал много разных статей, но нигде не видел чётких аргументов в пользу отказа от исключений. Go вообще забавный язык, panic()/recover() это исключений как они есть, один в один. Любой код с исключениями тривиальными заменами преобразовывается в код с panic()/recover(). Но при этом они утверждают, что от исключений они избавились.
С точки зрения программиста, в коде могут случиться ожидаемые ошибки, которые он готов обработать, например нет файла. Файла, который хочет открыть пользователь. И неожиданные ошибки, которые он не хочет обрабатывать и хочет крешиться, например нет файла. Файла библиотки программы. Когда программист пишет код, он хочет для мест с ожидаемыми ошибками указать, какие исключение ловить. И не может — потому что во всех существующих реализациях нет возможности быстро понять, какие именно исключения может кинуть функция в той или иной ситуации. К примеру, использует разработчик функцию функцию работы с сетью, и хочет написать что потеря соединения — это ожидаемая ошибка, ее нужно ловить, по всем остальным нужно крешиться. Все работает два дня, а потом крешится на каком-нибудь OSError, которое "тоже потеря сетевого соединения, но на другом уровне абстракции". Еще через неделю там же крешится на SocketError, которое тоже потеря сетевого соединения, но в другом месте... И так может продолжаться бесконечно.
Здравствуйте, AlexRK, Вы писали:
ARK>А как же RAII?
А что такое RAII? Это сказал кто-то, modern programer из каких-то гуглов или фейсбуков, а может вообще академик с бадуна,
что "Resource Acquisition Is Initialization"-это модно и есть признак развития. И стадо обезьян загудело. Все так и кинулись внедрять
себе в код это развитие. Только то, что для того, чтоб это без проблем работало, нужно или переписывать всю ОС с самого загрузчика по новому,
включая все системные либы, или нужно формировать мощную прослойку, на уровне интерпретатора (достаточно хорошо реализовано во всех существующих),
или стандартной библиотеки языка компилируемого (Да в C++ RAII названо, но не релизовано как надо, полноценная реализация требует добавления
специализированных средств в stdc++) между ОС, и приложением использующим RAII, чтоб она и взяла на себя все нестыковки RAII с существующими
низкоуровневыми системами.
Здравствуйте, smeeld, Вы писали:
S>А что такое RAII? Это сказал кто-то, modern programer из каких-то гуглов или фейсбуков, а может вообще академик с бадуна, S>что "Resource Acquisition Is Initialization"-это модно и есть признак развития. И стадо обезьян загудело. Все так и кинулись внедрять S>себе в код это развитие. Только то, что для того, чтоб это без проблем работало, нужно или переписывать всю ОС с самого загрузчика по новому, S>включая все системные либы, или нужно формировать мощную прослойку, на уровне интерпретатора (достаточно хорошо реализовано во всех существующих), S>или стандартной библиотеки языка компилируемого (Да в C++ RAII названо, но не релизовано как надо, полноценная реализация требует добавления S>специализированных средств в stdc++) между ОС, и приложением использующим RAII, чтоб она и взяла на себя все нестыковки RAII с существующими S>низкоуровневыми системами.
Здравствуйте, smeeld, Вы писали:
S>Здравствуйте, AlexRK, Вы писали:
ARK>>А как же RAII?
S>А что такое RAII? Это сказал кто-то, modern programer из каких-то гуглов или фейсбуков, а может вообще академик с бадуна,
Т.е., по твоим сведениям, это не профессор Страуструп придумал 30 лет назад?
S>что "Resource Acquisition Is Initialization"-это модно и есть признак развития. И стадо обезьян загудело. Все так и кинулись внедрять S>себе в код это развитие. Только то, что для того, чтоб это без проблем работало,
Без каких именно проблем?
S>нужно или переписывать всю ОС с самого загрузчика по новому,
Зачем же переписывать? Вот было в ОС malloc/free — сверху сделали надстройку — std::vector. Над fopen/fclose — std::fstream. Что переписывать-то?
S>включая все системные либы, или нужно формировать мощную прослойку, на уровне интерпретатора (достаточно хорошо реализовано во всех существующих), S>или стандартной библиотеки языка компилируемого (Да в C++ RAII названо, но не релизовано как надо,
Как надо?
S>полноценная реализация требует добавления специализированных средств в stdc++)
Каких средств?
S>между ОС, и приложением использующим RAII, чтоб она и взяла на себя все нестыковки RAII с существующими S>низкоуровневыми системами.
Что за нестыковки?
Если не поможет, будем действовать током... 600 Вольт (C)
Здравствуйте, Pzz, Вы писали:
Pzz>Этим исключениям хорошо бы как-то законодательно синтаксически ограничить дальность полета. Потому что когда в ваш высокоуровневый код, работающий в терминах высокоуровневых абстракций, откуда-нибудь с самого нижнего уровня прилетит исключение про то, чего вы в своем высокоуровневом коде сказать-то и не можете, то что вы с ним будете делать? Выдадите пользователю ошибку с шешнадцетиричным номером и невнятной диагностикой? То-то пользователь будет счастлив.
Так не нужно кидать исключения из низкоуровневого кода. Для низкого уровня — коды ошибок, для верхнего — исключения.
Здравствуйте, enji, Вы писали: E>Да вмещается он, просто надо вместо целого кода возвращать объект. А там уже можно делать вложенные коды и т.п. Но от ручного распространения без какого-то сахара со стороны языка не уйти, да
Стесняюсь спросить — а в чём преимущество-то у этого "объекта ошибки" по сравнению с "объектом-исключением"?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, uncommon, Вы писали: U>Например? Что-то мне кажется, что такие деструкторы — результат плохого дизайна. Как может быть много логики в деструкторе RAII класса, где должны только освобождаться ресурсы?\
Ну, например ресурсы — это буфер. Чтобы его освободить, его надо флашнуть. Что делать? Требовать ручного flush перед выходом из каждого scope? Причём его отсутствие мы заметим только в рантайме, и то не каждый раз.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, uncommon, Вы писали: U>>Например? Что-то мне кажется, что такие деструкторы — результат плохого дизайна. Как может быть много логики в деструкторе RAII класса, где должны только освобождаться ресурсы?\ S>Ну, например ресурсы — это буфер. Чтобы его освободить, его надо флашнуть. Что делать? Требовать ручного flush перед выходом из каждого scope? Причём его отсутствие мы заметим только в рантайме, и то не каждый раз.
Чтобы освободить буфер, его не надо флашнуть в деструкторе. Флашать уже поздно. Мы здесь говорим о двух принципиально разных операциях: 1) освобождение ресурсов, 2) операции завершения/коммита, которые могут завершиться неудачно. Эти завершающие операции надо делать явно, хочешь ты этого или нет. Приемлимого решения делать их в деструкторе нет. Эта проблема есть и в стандартной библиотеке в классе std::thread. Если thread является joinable, но join() не был позван явно перед деструктором, то деструктор вызывает std::terminate().
Здравствуйте, uncommon, Вы писали:
U>Чтобы освободить буфер, его не надо флашнуть в деструкторе. Флашать уже поздно.
Это, как мне кажется, последствия конкретных решений в дизайне деструкторов и исключений, а не фундаментальное свойство математики. U>Мы здесь говорим о двух принципиально разных операциях: 1) освобождение ресурсов, 2) операции завершения/коммита, которые могут завершиться неудачно. Эти завершающие операции надо делать явно, хочешь ты этого или нет.
С моей точки зрения, мне важна семантика некоего "возврата", т.е. гарантии, что в момент выхода из scope у меня некий объект восстанавливает своё состояние к исходному.
Частный случай — освобождение ресурсов. Т.е. меня интересует пара антонимов типа push/pop, acquire/release, change/revert.
Типичный пример — смена курсора мышки на часики. Я хочу иметь гарантию того, что часики вернутся в стрелочку, независимо от того, каким способом я вышел из scope — return, break, или throw. U>Приемлимого решения делать их в деструкторе нет. Эта проблема есть и в стандартной библиотеке в классе std::thread. Если thread является joinable, но join() не был позван явно перед деструктором, то деструктор вызывает std::terminate().
Вызывает интерес поступательный процесс: можно ли изобрести другую технологию, в которой можно будет описывать то, чего я хочу?
Может быть, два "деструктора" у классов — commit-деструктор и rollback-деструктор?
Или другую семантику раскрутки стека, которая позволяет спасти больше информации об ошибке? Типа "мы читали из одного файла и писали в другой, потом при чтении у нас возникла ошибка формата, а при экстренном выходе мы и предыдущие данные в выходной файл записать не смогли".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>С моей точки зрения, мне важна семантика некоего "возврата", т.е. гарантии, что в момент выхода из scope у меня некий объект восстанавливает своё состояние к исходному. S>Частный случай — освобождение ресурсов. Т.е. меня интересует пара антонимов типа push/pop, acquire/release, change/revert. S>Типичный пример — смена курсора мышки на часики. Я хочу иметь гарантию того, что часики вернутся в стрелочку, независимо от того, каким способом я вышел из scope — return, break, или throw.
Ну так это обеспечивают обычные RAII guards. (Естественно, предполагается, что смена часиков на стрелочку не бросит исключения.)
S>Вызывает интерес поступательный процесс: можно ли изобрести другую технологию, в которой можно будет описывать то, чего я хочу? S>Может быть, два "деструктора" у классов — commit-деструктор и rollback-деструктор?
для этого у guard-а делается флажок, который взводится, если все хорошо (обычно метод называется commit), и тогда деструктор идет по сценарию commit, а иначе (по умолчанию) — по сценарию rollback.
S>Или другую семантику раскрутки стека, которая позволяет спасти больше информации об ошибке? Типа "мы читали из одного файла и писали в другой, потом при чтении у нас возникла ошибка формата, а при экстренном выходе мы и предыдущие данные в выходной файл записать не смогли".
Для этого, очевидно, нужно предпринять специальные усилия (т.е. какие-то параллельные структуры) по сохранению сути того, что мы собирались сделать и не смогли.
Самое простое — очередь задач, где обломившаяся задача не удаляется из очереди и ее можно попробовать запустить еще раз или что-то с ней сделать.
Другой вариант — сохранять сами исключения (у меня есть игрушечный вариант на С++11, если интересно, выложу куда-нть) и всю необходимую информацию прямо в них.
Но тут, понятно, нас всегда подстерегают ошибки выделения памяти.
Здравствуйте, Sinclair, Вы писали:
S>С моей точки зрения, мне важна семантика некоего "возврата", т.е. гарантии, что в момент выхода из scope у меня некий объект восстанавливает своё состояние к исходному. S>Частный случай — освобождение ресурсов. Т.е. меня интересует пара антонимов типа push/pop, acquire/release, change/revert.
Абсолютно верно. Это одна из очевидных вещей, которые почему-то нормально не реализованы в современных ЯП.
Кстати, пара "антонимов" — это всего лишь вырожденный случай некоторой цепочки вычислений, в которой должны быть гарантированно вызваны некоторые методы в некотором порядке.
S>Вызывает интерес поступательный процесс: можно ли изобрести другую технологию, в которой можно будет описывать то, чего я хочу? S>Может быть, два "деструктора" у классов — commit-деструктор и rollback-деструктор? S>Или другую семантику раскрутки стека, которая позволяет спасти больше информации об ошибке? Типа "мы читали из одного файла и писали в другой, потом при чтении у нас возникла ошибка формата, а при экстренном выходе мы и предыдущие данные в выходной файл записать не смогли".
Вот здесь вся "прелесть" исключений и вылезает. ИМХО, создание корректного кода, "рассуждения" о коде будут сильно затруднены в присутствии спагетти деструкторов и неочевидных с виду переходов.
Здравствуйте, jazzer, Вы писали:
J>Ну так это обеспечивают обычные RAII guards. (Естественно, предполагается, что смена часиков на стрелочку не бросит исключения.)