LINUX.ORG.RU

exceptions в C++

 ,


5

9

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

Кто-нибудь может накидать каких-нибудь технических статей на тему как следует или не следует применять эксепшены в плюсах?

UPD:
Из любопытного
почитать (стр. 32)
посмотреть

★★★★★

Последнее исправление: morse (всего исправлений: 1)

Ответ на: комментарий от RazrFalcon

В плюсах есть все. Включая мучительный выбор «что же использовать».

KennyMinigun ★★★★★
()
Ответ на: комментарий от DarkEld3r

Всё также надо писать проверки после каждого вызова

Так это достоинство

Вот это новость. Ну расскажи, как им боком писать на каждую строчку ещё пачку строчек это достоинство.

Можно подумать, что с исключениями, да и, собственно, со всем остальным не точно так же. (Про)думать вообще полезно.

Нет. Можно начать проект вообще не думая об исключениях, озаботившись только печатью what() на самом высоком уровне. И в подавляющем большинстве случаев ничего больше не нужно. Не зависимо от того, кидаешь ты сам исключения, не кидаешь, кидает чужой код который ты зовёшь, не кидает, продумана и есть вообще у тебя развесистая иерархия или только один my_error: public std::runtime_error, это будет работать. Теперь захотелось тебе не падать полностью, а писать что операция провалилась и работать дальше. Ты вставляешь ровно один try/catch ровно в одном месте. И, опять же, назависимо от того кто что и как кидает и какая иерархия исключений наворочена, больше тебе не надо думать ни о чём. Также и наоборот, ты можешь перелопатить всю иерархию и не изменить больше ни строчки кода.

Пропустить ошибку вообще-то очень легко: можно поймать базовый класс и забыть обработать что-то специфичное. Можно и вообще забыть обработать и толку потом, что оно на самом верху поймается? Ну напишешь в лог «ой, случилась ошибка», это не обработка, а заметание проблемы под ковёр. В сложных случаях обработка исключений тоже выглядит ужасно, в каких-то ситуациях ужаснее «кодов возврата».

Какое-то у тебя странное представление об ошибках. Ошибки возникают по вине внешней среды, в общем случае ты ничего и не должен и не можешь сделать, кроме как записать в лог или вернуть клиенту подробную причину проблемы. Или ты собрался chmod на файлы делать, если у тебя ENOPERM прилетело и google drive по fuse подключасть если ENOSPC? Случаи когда код действительно что-то может сделать единичны, о них известно априори, и они решаются ловлей конкретного исключения, именно поэтому их нельзя просто так пропустить.

slovazap ★★★★★
()
Ответ на: комментарий от evilface

А я думал про что-то в стиле boost::optional. Чтоб кидалось исключение, если производится доступ к невалидному результату. Только вот пока не до конца представляю как реализовать operator T()

KennyMinigun ★★★★★
()
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от KennyMinigun

А я думал про что-то в стиле boost::optional

Это даже худшая идея.

tailgunner ★★★★★
()
Ответ на: комментарий от Legioner

Enum может хранить значение. Плюс есть автоматические конвертирование типов через From.

http://smallcultfollowing.com/babysteps/blog/2015/05/05/where-rusts-enum-shines/

двух значений можно и без него прекрасно обойтись.

Почему двух?

RazrFalcon ★★★★★
()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от DarkEld3r

Я про плюсы. Там только Variant, который не безопасный (нужно самому проверять наличие в нём нужного типа, в то время как match в rust делает это за нас на этапе компиляции).

RazrFalcon ★★★★★
()
Ответ на: комментарий от DarkEld3r

Я бы не сказал. Как по мне, раст в разы проще плюсов. Даже если считать чисто по фичам. С на стероидах - да, плюсы - нет.

RazrFalcon ★★★★★
()
Ответ на: комментарий от slovazap

Раскрывай мысль.

Уже раскрыли выше. См. предыдущий пост.

ты должен добавить её поддержку в весь код выше по стеку

В rust - нет.

либо вообще ничего не делать

И прога упадет из-за не обработанного исключения. Красота.

RazrFalcon ★★★★★
()
Ответ на: комментарий от Legioner

в том же С++ компилятор просто так и не напомнит про это

Зависит от компилятора. Но никто не отменял __warn_unused_result__ и подобные.

RazrFalcon ★★★★★
()
Ответ на: комментарий от Legioner

код начинает пестрить этими обработками и выглядеть неприглядно.

Вкусовщина. Шапша из try-catch тоже не вершина стиля.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Уже раскрыли выше. См. предыдущий пост.

Выше много чего написано много кем. Укажи конкретный пост где ты готов подписаться под каждый словом или раскрывай сам, если есть что сказать.

либо вообще ничего не делать

И прога упадет из-за не обработанного исключения.

Потрудись-ка сначала прочитать, а потом процитировать всё предложение на которое отвечаешь.

либо вообще ничего не делать, если оно попало под более общий класс или обработке вообще не важно что кинулось, как на практике обычно и происходит

slovazap ★★★★★
()
Ответ на: комментарий от DarkEld3r

В сложных случаях обработка исключений тоже выглядит ужасно, в каких-то ситуациях ужаснее «кодов возврата».

К слову, такое бывает у людей с кодами возврата головного мозга. Эти не понимают исключений и работают с ними также, т.е. городят catch для каждого типа исключения, например.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Можно начать проект вообще не думая об исключениях

Ровно до тех пор, пока какая-нибудь веселая библиотека не начнет швыряться исключениями в конструкторах/деструкторах твоих классов. Сразу такое веселье начинается.

kawaii_neko ★★★★
()
Ответ на: комментарий от Legioner

Из моей практики в 99% случаев достаточно варианта...

С аргументами более-менее согласен, не согласен с процентом, ну или мне так везёт. Опять же, недостатки конкретно плюсов не значат, что нельзя (в другом языке или новом стандарте - не принципиально) сделать лучше. Городить аналог растового Result я тоже мало смысла вижу, но где ситуация позволяет предпочитаю использовать optional.

И да, правильные «коды возврата» нельзя забыть обработать. И обработка ошибок не бессмысленная.

Раньше я сам был за исключения и считал их самым лучшим решением. В последнее время, точка зрения поменялась. Сомневаюсь, что тут можно привести какие-то железные аргументы, ведь всегда их можно свести к «вы просто не умеете их готовить».

DarkEld3r ★★★★★
()
Ответ на: комментарий от slovazap

Вот это новость. Ну расскажи, как им боком писать на каждую строчку ещё пачку строчек это достоинство.

Достоинство - невозможность забыть обработать. Ну да, идёт вместе с недостатком «нельзя не обработать», серебряных пуль не бывает.

Количество дополнительных строчек зависит от языка и ситуации. Если исключение необходимо обрабатывать рядом с его выбросом, то ситуация не в их пользу.

Нет. Можно начать проект вообще не думая об исключениях, озаботившись только...

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

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

Сколько пафоса. Случаев когда с ошибками можно и нужно что-то делать более чем достаточно, причём не всегда оно предметной областью обусловлено. Перед использованием сторонней библиотеки тщательно изучаешь какие исключения и в каких случаях могут вылететь? Похвально, но так, к сожалению, делают далеко не все. Повторение мантры «на верхнем уровне поймаем» ситуацию усугубляет. Если кодовая база достаточно большая, то в собственных утилитах/врапперах тоже разобраться бывает не просто. Причём оно ещё можно и рефакториться и могут появляться новые исключения.

Ну и повторюсь: мне уже не в первый раз встречается ситуация когда вначале для ускорения втыкают исключения в разные «подозрительные места», а затем появляется желание максимально обрабатывать ошибки и исключения постепенно выпиливаются. Происходит это совсем не безболезненно.

DarkEld3r ★★★★★
()
Ответ на: комментарий от RazrFalcon

Я бы не сказал. Как по мне, раст в разы проще плюсов. Даже если считать чисто по фичам.

Не думаю, что тут можно привести объективные доказательства, даже «толщина стандарта» показатель косвенный, но в расте хватает всяких сложных штук. Может они и не часто нужны, но ведь и на плюсах можно писать без многих фич.

Другое дело, что раст всё-таки язык молодой и в нём нет легаси-костылей из-за чего он кажется проще. Хотя, опять же, сложность можно мерить по-разному, а из-за лайфтаймов порог вхождения может и выше.

DarkEld3r ★★★★★
()
Ответ на: комментарий от kawaii_neko

Исключение в конструкторе абсолютно нормально.

А исключение в деструкторе это всегда ошибка дизайна либо у тебя либо в чужом коде. Я такого на практике не встречал, но вообще есть static_assert(std::is_nothrow_destructible(...))

slovazap ★★★★★
()
Ответ на: комментарий от RazrFalcon

Enum может хранить значение.

Можно сделать struct, который будет хранить значение или union.

Плюс есть автоматические конвертирование типов через From.

Почему автоматическое? Там же вроде макросом try! это делается. В C++ точно так же можно сделать.

Почему двух?

Ok и Err.

Legioner ★★★★★
()
Ответ на: комментарий от DarkEld3r

Достоинство - невозможность забыть обработать.

Ну и что мешает делать это без исключений? Сначала лепим везде

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

Перед использованием сторонней библиотеки тщательно изучаешь какие исключения и в каких случаях могут вылететь?

Нет же, мне абсолютно наплевать что там может вылететь - оно в любом случае поймается где надо и корректно обработается. И если в кишках оно там хоть на другую библиотеку перейдёт вместе с полностью другим набором исключений, я ни строчки своего кода не изменю. А в документацию (также как и ты, кстати) я полезу тогда и только тогда когда захочу обработать специальным образом отдельную ошибку.

Ну и повторюсь: мне уже не в первый раз встречается ситуация когда вначале для ускорения втыкают исключения в разные «подозрительные места», а затем появляется желание максимально обрабатывать ошибки и исключения постепенно выпиливаются. Происходит это совсем не безболезненно

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

slovazap ★★★★★
()
Ответ на: комментарий от DarkEld3r

С аргументами более-менее согласен, не согласен с процентом, ну или мне так везёт.

Или мне :)

Городить аналог растового Result я тоже мало смысла вижу, но где ситуация позволяет предпочитаю использовать optional.

Ну optional это скорее с nullable уже сравнивать надо. Если на ошибку, как таковую, нам плевать, это уже совсем другая ситуация.

И да, правильные «коды возврата» нельзя забыть обработать.

Ну как в Rust сделано — да, нельзя. Там это на уровне языка и везде одинаково. Как в C/C++ — ну разве что умный статический анализатор сможет понять, где там код возврата мы проигнорировали, а где проигнорировали возвращаемое значение, которое не код возврата и на которое нам пофиг. Ну или проверять вообще все возвращаемые значения, тоже вариант, но я не уверен, что так будет удобно писать код.

И обработка ошибок не бессмысленная.

Бессмысленная это проброс этой ошибки наверх. Она не бессмысленная, но это просто захламляет код.

Раньше я сам был за исключения и считал их самым лучшим решением. В последнее время, точка зрения поменялась. Сомневаюсь, что тут можно привести какие-то железные аргументы, ведь всегда их можно свести к «вы просто не умеете их готовить».

Да, тема сложная. Я лично с исключениями в Java никогда не имел никаких проблем. Коды возврата, как они сделаны в Rust, мне тоже нравятся (все неудобства сведены к абсолютному минимуму макросом try! или даже оператором ? в последних версиях). В С++ писать exception-safe код мне было неудобно и казалось довольно нетривиальным занятием, но наверное я его плохо знал, в этом плане мне казался удобней код с кодами возврата — там по крайней мере не приходится думать, что на каждой строчке может вылететь exception и что-нибудь сломать. В Java почему-то такой ситуации не возникает. С одной стороны там нет деструкторов и проблем с исключениями в них (ну почти нет, исключение в finally таки неприятно), с другой стороны там GC, поэтому нет кучи проблем с управлением памятью.

В общем для языка с GC я не вижу причин не использовать исключения и поэтому мне, например, Go не нравится. Для языка с ручным управлением памятью и удобным синтаксисом можно и коды возврата.

Legioner ★★★★★
()
Ответ на: комментарий от RazrFalcon

Вкусовщина. Шапша из try-catch тоже не вершина стиля.

Согласен. Но, опять же из моего опыта обработка ошибок не занимает много места. А вот пробрасывание ошибки наверх — занимает. Ну и try-catch по сути ничем не отличаются от switch-case, который будет в случае с кодами возврата.

Legioner ★★★★★
()

Мне как-то достался код такого типа:

try {
  foo();
  try {
    bar();
    try {
      baz();
    } catch(...) {
      show("Error");
      return;
    }
  } catch(...) {
    show("Error");
    return;
  }
} catch(...) {
  show("Error");
  return;
}
Наверно, автор где-то в этом треде, пишет про неудобство исключений :D

anonymous
()
Ответ на: комментарий от DarkEld3r

Если не считать боязни лайфтаймов, что мне не совсем понятно ибо они усваиваются за пару дней, и pattern matching, то никаких других фич, отличных от С++, в расте нет.

В то время как плюсы содержат: исключения, variadic templates, перегрузку методов, аргументы по умолчанию, struct + class, friend, protected, наследование, множественное наследование, виртуальные методы, constexpr, специализацию шаблонов, тонны нюансов из-за UB (типа use-after-free/move), (полу)ручное управление памятью, различные реализации std и ABI, forward declaration, касты, динамический полиморфизм (в расте он тоже есть, но прибацанный)...

И это только то, с чем столкнулся я. И это всё желательно знать и уметь. Поэтому кривая обучения явно в пользу раста.

RazrFalcon ★★★★★
()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Я про плюсы. Там только Variant, который не безопасный (нужно самому проверять наличие в нём нужного типа

Можно написать свой.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Свой вариант? Но зачем, если уже есть существующий? Тем более я не вижу как получить гарантии растовского enum в плюсах.

То есть в плюсах я могу написать variant.toString(), а она на самом деле Rect, и получу мусор. В раст так не получится, ибо PM.

RazrFalcon ★★★★★
()
Ответ на: комментарий от DarkEld3r

Ну и что мешает делать это без исключений? Сначала лепим везде unwrap, затем в нужных местах правим, всё абсолютно аналогично.

Самое смешное, что unwrap по сути это и есть исключение. Вылетает, раскручивает стек, пока его не поймают. Один в один же. Т.е. ты и предложил использовать исключения.

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

Тут есть одна большая проблема. Есть множество функций, которые теоретически могут вернуть ошибку. Я бы даже сказал — теоретически могут вернуть много разных ошибок. Но на практике нас эти случаи не интересуют от слова совсем. Например пишем мы LOR, отображаем страницу. Делаем мы SQL-запрос.

  connection = connect();
  // тут может быть нехватка памяти при выделении памяти для внутренних структур, но с этим мы ничего поделать не сможем, лучше всего это залоггировать по мере возможности и сдохнуть, а там пусть админу какие-нибудь тулзы про это сообщат и он примет меры
  // тут может кончиться стек и с этим мы, конечно, тоже ничего поделать не сможем, то же самое
  // может параметры соединения с БД в конфиге пустые, пускай админ разбирается, не наше дело
  // тут может не получиться соединение с БД, по какой причине — не понятно, но суть в том, что ничего с этим мы тоже поделать не сможем, нет базы — нет ЛОРа, пусть там выше кто умеет отдаст юзеру страничку с ошибкой, залоггируем, чтобы админ глянул почему так и всё
  // тут может не найтись драйвер БД из конфига и опять же нифига мы с этим поделать не можем, видимо админ только что проинсталлировал форум и забыл положить драйвер в classpath, как обычно — падаем
  statement = connection.prepare("select id, text from message where topic_id = ?");
  // нехватка памяти, стек — аналогично
  // неправильный SQL — WTF? правильный же он, ёпта. Не может такого быть. Разве что код работает в 2200 году и SQL стал другим. Но что мы тут с этим можем поделать?
  // какая-нибудь ещё ошибка БД, да что угодно может быть, например БД пошла выключаться и отказывается выполнять наши команды, и что может сделать маленький хрупкий контролер с этим? да ничего, только выкинуть эксепшн

и такого кода полно. Который 99.99% времени работает. А если он не работает — значит что-то сильно не так и ничего с этим код этот поделать не может. Может поделать админ. Или программист, исправив баг. Или другой программист, вызывая этот код с правильными параметрами. А значит всё, что должен сделать этот код — как можно тщательней задокументировать проблему и вернуть ошибку. И изредка эта ошибка будет действительно обрабатываться. Например регистрируется пользователь — делаем insert и ловим constraintviolationexception, мол есть уже пользователь с таким ником, но это не ошибка, поэтому конкретно это исключение в этом месте мы обработаем по-другому. Или у нас отказоустойчивый сервис с ручным лоад-балансингом и если одна база не ответила, значит попробуем на второй (хотя опять же этот код скорее всего будет в инфраструктурном коде, который это всё дело и будет реализовывать под капотом). Но чаще всего работает один сценарий и работает нормально. Причём программист, пишущий функции, не знает и не может знать, что нормально, что ненормально, а что иногда нормально, а иногда не нормально. Поэтому попытки делить ошибки на фатальные и не очень — просто не имеют смысла. Что в одном месте будет фатальным, в другом месте будет нормой.

Есть серые зоны. Например вместо числа передали строку. Мы знаем, что это пользователь своими шаловливыми ручонками в developer tools увидел запрос и подменяет параметр. В идеале конечно это пользовательский ввод, и надо бы ему отобразить что-то умное, но по факту можно и 500 показать.

Хотя эта тема уже больше для checked vs unchecked exceptions, но и с топиком тоже немного пересекается.

Legioner ★★★★★
()
Ответ на: комментарий от KennyMinigun

У меня assert() на случай попытки получения непроверенного результата. Преимущество: при недостаточном тестировании результат может быть всегда валидным и в случае «исключения при доступе к невалидному результату» будет падение в продакшене. А здесь получается что если на ошибку не проверили — сразу свалились. Конечно, можно сделать if (r.isError()) {}; res = r.result(), но это уже злонамеренная ошибка.

evilface ★★
()
Ответ на: комментарий от slovazap

Это «лепим» и мешает.

И чем же оно мешает?

Оно «мешает» не больше чем необходимость писать return, например. Забыл, компилятор выругался, поправил, всё. Никаких «многих строк кода» или какой-то хитрой логики не требуется. Зато потом эти места легко найти.

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

Во первых, можно и забыть написать catch на самом верхнем уровне. Во вторых, ты правда не различаешь ситуации «хрен знает где поймалось, в лог записалось» и «на определённом (логическом) уровне забыли обработать ошибки и в результате абстракции протекают»?

И я в третий раз говорю: на всех проектах, в которых участвовал, были исключения. Где-то изолированно, не во всех модулях, но были. Так что не надо спорить со своими фантазиями про «разработку без исключений».

Собственно, моё «мировоззрение» относительно исключений - это как раз результат работы с ними. Возврат ошибок не идеализирую, но часть проблем он решает, пусть и добавляет другие. И да, я признаю, что исключения бывают удобнее для определённых случаев (пусть и для случаев «наговнякаем побыстее, потом будем фиксить»), ты же в своей упёртости ничего вокруг не видишь.

А в документацию (также как и ты, кстати) я полезу тогда и только тогда когда захочу обработать специальным образом отдельную ошибку.

О чём и речь. С исключениями ты сначала напишешь код, потом он где-то (в идеале, при тестировании) отвалится и тогда ты полезешь в доку и будешь catch-и городить. С явным возвратом ошибок подумать придётся сразу и, если вдруг добавятся новые ошибки, то снова не придётся ждать пока они сами всплывут. Хотя да, прежде чем ты не начал спорить, я признаю, что бывает так, что ничего править и не приходится. Другое дело, что надёжность при втором подходе явно выше, а затрат, особенно если нас устраивает подход «обработать когда-то потом» не сильно больше.

когда об этом можно просто забыть.

Отличный подход.

Кстати, зачем ты вообще заводил разговор о вложенных исключениях и об иерархиях, если всё равно устраивает тупая запись в лог?

DarkEld3r ★★★★★
()
Ответ на: комментарий от Legioner

Если на ошибку, как таковую, нам плевать, это уже совсем другая ситуация.

Тут тоже нюансы бывают. Всё-таки опциональное значение - это не (совсем) «плевать на ошибку», бывают ситуации когда результат «не удалось получить значение» вполне устраивает. Зато такое возвращаемое значение явно говорит о возможности неудачи, в отличии от потенциального исключения и это уже задача другого кода решать, что с этим делать.

Ну и если я правильно понимаю, nullable - это не совсем оно. Ведь в джаве все ссылочные типы такие, так? Тогда это не очень удобно из-за того, что не видно может ли тип быть нулевым или нет. Явная опциональность удобнее и, вроде, её в джаву уже завезли?..

DarkEld3r ★★★★★
()
Ответ на: комментарий от Legioner

Вылетает, раскручивает стек, пока его не поймают.

Только ловить его не так просто: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html#notes

Но на практике нас эти случаи не интересуют от слова совсем.

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

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Ты вынужден будешь писать sum_val.as<SomeType>().some_method(). Если sum_val не содержит SomeType, ошибка компиляции; если текущее значение adt_val имеет другой тип, бросается std::logic_error.

tailgunner ★★★★★
()
Ответ на: комментарий от RazrFalcon

Поэтому кривая обучения явно в пользу раста.

Ты всё-таки смотришь с точки зрения человека, который знает плюсы и готов учиться новому. Собственно, я думал точно так же, но опыт показывает, что не всё так просто. С одной стороны есть «закостенелы плюсовики», которые могут знать все нюансы стандарта, но ныть, что лайфтамы сложные, забывая, что плюсы они осваивали годами. С другой стороны, люди «избалованные» автоматическим управлением памятью. Добавь сюда синдром утёнка - всё-таки раст не стремится быть очень похожим на мейнстрим.

В то время как плюсы содержат

В эту игру можно играть вдвоём: паника (причём поведение настраивается), макросы (repetition, рекурсивные, (недо)гигиена, отдельный, не такой, как у остальных языковых сущностей, импорт/экспорт), «процедурные макросы» (compiler plugins), лайфтаймы (и ограничения лексических лайфтаймов, unbounded лайфтаймы, Higher-Rank Trait Bounds и т.д.), «правила одалживания», трейты, трейд-обжекты, ассоциированные типы, атрибуты, сопоставление с образцом, ну и можно долго продолжать. С достаточной вероятностью случайный термин из книги будет не знаком «чистому сишнику».

Повторюсь: мне тоже раст показался несложным и главное логичным, но плюсы-то (да и не только их) я к этому времени худо-бедно знал.

DarkEld3r ★★★★★
()
Ответ на: комментарий от slovazap

А исключение в деструкторе это всегда ошибка дизайна либо у тебя либо в чужом коде

class timer {
  ostream &out;
  string &desc;
  int elapsed();
public:
  timer(const string &id, ostream &os = cout) : out(os), desc(id) { out << "Start block " << desc << endl; }
  ~timer() { out << "End block " << desc << " in " << elapsed() << endl; }
};

А теперь тебе подсовывают файловый стрим со включенными битами исключений.

В крестах прострелить себе ногу легче легкого — язык к этому располагает.

kawaii_neko ★★★★
()
Ответ на: комментарий от DarkEld3r

Оно «мешает» не больше чем необходимость писать return, например

Нет, оно мешает не меньше чем писать call при каждом вызове функции.

Во первых, можно и забыть написать catch на самом верхнем уровне

Можно, и это хорошо. Значит обработка ошибок не нужна, а что нужно будет выковырято из корки. Зато когда (и только тогда когда) обработка понадобится она будет добавлена туда (и только туда) куда надо.

Во вторых, ты правда не различаешь ситуации «хрен знает где поймалось, в лог записалось» и «на определённом (логическом) уровне забыли обработать ошибки и в результате абстракции протекают»?

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

С явным возвратом ошибок подумать придётся сразу и, если вдруг добавятся новые ошибки, то снова не придётся ждать пока они сами всплывут

Рассказывай давай про «подумать» после своего «лепим unwrap». Либо ты после каждого вызова собираешься явно обрабатывать каждую возможную ошибку, тогда да - когда появится новая компилятор у тебя ругнётся. Но тогда ты нагло врёшь про «не мешает». Либо просто врёшь и таки потеряешь неижиданные ошибки.

Кстати, зачем ты вообще заводил разговор о вложенных исключениях и об иерархиях, если всё равно устраивает тупая запись в лог?

Кого устраивает? Я сказал что с исключениями можно и так и так. Как нужно так и делается. Это называется гибкость. Не нужно заранее читать документацию чтобы обрабатывать ошибки которые не надо обрабатывать никаким особым образом. Это называется простота. И при этом ни одна ошибка не останется необработанной. Это называется надёжность. И zero overhead. Это называется эффективность. Не совсем понимаю с чем ты споришь и не вижу ни одного контрпримера. Хотя учитывая что ты уже начал переходить на личности, всё уже понятно.

ты же в своей упёртости

slovazap ★★★★★
()
Ответ на: комментарий от Legioner

Самое смешное, что unwrap по сути это и есть исключение.

А ещё смешнее, что я сам подумывал именно так написать, но нет - это «не совсем» исключение. Начиная с того, что пользоваться паникой, как полноценной заменой исключений, не получится/не удобно. И заканчивая тем, что оно не принято. Последнее, хоть и формально ничего не гарантирует, но фактически играет важную роль.

Ключевое отличие в следующем: есть проект щедро обмазанный исключениями и использующий сторонний библиотеки, которые тоже их кидают. Если в каких-то функциях ты решает убрать исключения или наоборот добавить, то вызывающий их код ни о чём не узнает. В итоге будут или бесполезные catch или потенциальная проблема. С Option/Result такого не будет.

И использовать unwrap я предлагал как раз как временную меру: для быстрого написания каркаса приложения.

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

И каким образом это аргумент за или против исключений? Аналогичную логику можно делать на «кодах возврата».

DarkEld3r ★★★★★
()
Ответ на: комментарий от RazrFalcon

То есть забыть обработать код возврата - это беда, а игнорировать исключения - ок?

Да. Потому что если ты не обработал код возврата, у тебя выполнение продолжится дальше с места ошибки. Если ты проигнорировал исключение, у тебя выполнение продолжится в обработчике исключения, а не с места ошибки.

Legioner ★★★★★
()
Ответ на: комментарий от DarkEld3r

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

Весь англоязычный инет завален статьями о переписывание всего подряд на Go. rust'у до такой популярности, как до луны. При том, что Go привлекает не только js/python-кодеров, но и java/scala/etc. Но для меня проблема Go в том, что у него тот же порок, что и у Си: примитивность выдаваемая за простоту.

паника (причём поведение настраивается)

Урезанный throw.

макросы

Сишный прероцессор конечно уныл, но то, что вытворяют в boost с макросами - ад. Уж лучше macro_rules! (хотя я только его азы выучил, он для меня похлеще лайфтаймов).

Если мы вычтем всё общее между rust и c++, то у c++ останется вагон и маленькая тележка фич, а у rust: лайфтаймы, PM, ADT и трейты. Я об этом.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Но match проверяет при компиляции

При компиляции match компилятор проверяет наличие SomeType в типе; при компиляции as<SomeType> делается то же самое. Наличие значения типа SomeType в переменной проверяется динамически.

tailgunner ★★★★★
()
Ответ на: комментарий от RazrFalcon

Поэтому кривая обучения явно в пользу раста.

но явно не в пользу сообществу, как у хgo и php

anonymous
()
Ответ на: комментарий от slovazap

Значит обработка ошибок не нужна

Или что её забыли дописать.

Именно на том уровне где нужно исключение будет обработано, никогда не поднимется выше, и ни в одном месте кода кроме этого не нужно будет написать ни строчки.

Это враньё.

Вот есть функция bool is_something(...); предполагается, что исключения она не бросает. Внутри используется 100500 разных функций (учитывая вложенность). И что дальше? Сделаешь внутри catch(std::exception&)? Если да, то стразу учитывай, что это можно забыть или писавший функцию не подумал о таких нюансах. Вот тебе и исключение поднялось выше, чем нужно.

Рассказывай давай про «подумать» после своего «лепим unwrap».

За контекстом вообще не следишь? Могу напомнить: это был аргумент, что с без исключений тоже можно «просто начать писать», а об обработке ошибок задуматься потом.

Но вообще не вижу противоречий: с исключениями тебе вообще ничто напоминать не будет, а с unwrap каждый раз придётся задуматься или «так сойдёт» или таки надо обработать в этом конкретном месте. Плюс первого подхода: аж на пару символов меньше писать, второго - проще будет найти и исправить.

Кого устраивает? Я сказал что с исключениями можно и так и так. Как нужно так и делается.

Потому я и просил примеры, что применений этой «гибкости» не встречал. Их не последовало.

Не нужно заранее читать документацию чтобы обрабатывать ошибки которые не надо обрабатывать никаким особым образом.

И отлавливать проблемы в тестировании (в лучшем случае). Это называется ненадёжность. Что характерно, при подходе с явной обработкой, без чтения документации можно обойтись даже если требуется обработать вообще все ошибки - всё равно они в коде видны.

И при этом ни одна ошибка не останется необработанной.

В конечном итоге, после долгого тестирования/эксплуатации. Если повезёт.

Хотя учитывая что ты уже начал переходить на личности, всё уже понятно.

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от RazrFalcon

Можно пример кода? Какая-то шаблонная магия, не иначе.

тебе бы книжки почитать, умнее станешь)

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.