LINUX.ORG.RU

Ада: исключения и безопасность

 


2

3

Про Аду еще не забыли?

Известно, что исключения в C++ с одной стороны упрощают обработку нештатных ситуаций, а с другой стороны делают программный код часто сложнее, потому что такой код уже должен поддерживать «гарантии безопасности», что составляет определенную сложность для языка без сборки мусора (GC). Очень много нюансов.

Исключения в Аде появились раньше, чем в C++. Они были уже в стандарте 83-го года. Это была одна из продающихся фишек первого стандарта языка. Как поддержка исключений соотносится с безопасной средой исполнения кода? В Аде есть элементы GC? Там что-то было про подчищение памяти еще в стандарте 83-го года, но я уже давно все забыл.

Гарантируется ли в Аде безопасность на базовом уровне? Не может ли брошенное исключение привести к утечке или какому другому дефекту в памяти? Если нет, то как это соотносится со скоростью исполнения кода?

★★★★★

исключения делают код сложнее

но почему? нет, я понимаю почему, но, почему писатель кода использует исключения так, что усложняет жизнь себе сам?

лови исключения как можно ближе к их выбросам, в идеале - один уровень.

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

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

Про C++ написаны книги, где есть ответы на эти вопросы. Я хотел бы больше поговорить про Аду.

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

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

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

А это фишка? Больше похоже на очередной подводный камень. Джавовские checked exceptions, например, этой фишки не имеют.

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

В итоге приводят к сильному замусориванию кода, поскольку есть много ситуаций, когда доподлинно известно, что исключения не будет в данном конкретном частном случае. Например, при парсинге константы. Или потому что аргументы провалидированы где-то в другом месте. Весь код обрастает try..catch с игнорированием исключений (и хорошо ещё если исключение перевыбрасывают обернув в RuntimeException, а ведь могут и вовсе проигнорировать, что приведёт к непредсказуемым эффектам в случае, если где-то исключение таки вылезет), среди которых тонут реально необходимые проверки. Внезапно, во всех других JVM языках - Kotlin, Groovy, Scala - от этой концепции отказались. Да и в других, не JVM, языках такой подход не нашёл широкого применения. На бумаге выглядит красиво, а на практике не очень.

try..catch должен иметь ценность, а не применяться повсеместно. Исключительная ситуация на то и исключительная.

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

Как бы да и как бы нет. Любое выделение памяти из кучи потенциально может бросить исключение (в С++ можно не бросать, но это надо руками прописывать в каждом случае). То есть получается, что исключений нужно ждать почти всегда.

Да бог с этими языками с GC! Там почти всегда есть базовая гарантия безопасности. Даже в растишке она есть, как мне кажется. В С++ ее надо добавиться самостоятельно. И мне вот интересно, а как дело обстоит в Аде, в прошлом конкуренте С++, некогда самом реальном конкуренте С++ из всех существующих.

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

Там почти всегда есть базовая гарантия безопасности.

Гарантии безопасности касаются не столько аспектов работы с памятью, сколько с незавершенными операциями вообще. Это может быть незакрытый файл, неотпущенный мьютекс, незавершенная модификация нескольких контейнеров подряд и т.д.

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

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

Ну, обычная практика, что в языках с GC на финализатор ставят освобождение ресурса, если его не освободили явно в коде. То есть, объект убирается сборщиком мусора, и автоматически срабатывает освобождение ресурса.

Так что, эта проблема остро не стоит в языках с GC. Вдобавок, есть распространенная практика освобождать ресурс через try-finally. То есть, языки с GC на порядки проще относятся к пробросу исключений, чем C++, и им за это почти ничего не бывает, вполне переживают безответственный стиль программирования. И даже растишка, кстати, переживает, почему и пользуется популярностью.

Вот и интересно узнать про Аду. Как этот монстр приспособлен?

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

То есть, объект убирается сборщиком мусора, и автоматически срабатывает освобождение ресурса.

Это плохо работает, т.к. время, когда объект будет убран и вызовется финализатор не детерминированно.

Вдобавок, есть распространенная практика освобождать ресурс через try-finally.

Это не относится к GC. И ничем не лучше практики goto err. Тоже самое, вид сбоку.

Представьте, что у вас есть объект с тремя контейнерами внутри. И есть метод add, который должен добавить элемент во все эти три контейнера.

И попробуйте обеспечить, скажем, строгую гарантию безопасности для этой операции на основе try-finally или goto err.

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

И даже растишка, кстати, переживает

Так у него RAII есть через Drop.

и пользуется популярностью.

Да, говорят о нем много. Говорят.

Вот и интересно узнать про Аду. Как этот монстр приспособлен?

Вроде бы в Ada 83 и 95 не было средств для обеспечения RAII. На счет более свежих стандартов не в курсе.

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

Вроде бы в Ada 83 и 95 не было средств для обеспечения RAII. На счет более свежих стандартов не в курсе.

Не было?! Ничего себе! Как жили-то?

Там какая-то сборка висячих объектов была по стандарту 83-го года, но я чего-то как-то вскользь это изучил, что все забыл. Да и в книгах особо не упирали на эту тему, давая ее на откуп реализациям языка.

Серьезно, если в Аде нет RAII, то это даже не смешно. Как тут можно сравнивать Аду с C++?

---

Что касается строгой гарантии безопасности, то вроде бы нигде, кроме C++, ею так не заморачиваются. Может быть, мне так кажется только.

dave ★★★★★
() автор топика

Ада: исключения и безопасность

Как ты скучно тему назвал. Исключения и безопасность Ада - вот это интересно, заманчиво.

no-such-file ★★★★★
()
Ответ на: комментарий от dave

В Расте, кстати, ее в этом плане меньше, чем в грамотно написанном С++. В Расте нет исключений, поэтому невозможно сигнализировать об ошибке в деструкторе.

Можно извращаться через panic! и set_panic_handler, но это по большом счету «internals», и использоваться не должно.

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

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

А можно раскрыть мысль? А то в C++ как-то не принято бросать исключения из деструкторов. Вплоть до того вызова std::terminate из-за того, что деструктор noexcept.

Или вы про то, что внутри деструктора исключения ловятся и обрабатываются, а наружу не выпускаются?

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

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

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

В C++ очень сильно полагаются на то, что деструктор является noexcept, иначе половина C++ будет поломана. В unsafe rust стоит придерживаться того же правила в drop. Или ты о чем-то другом?

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

лови те, которые интересуют, иначе вообще не лови.

deep-purple ★★★★★
()
Ответ на: комментарий от KivApple

checked exceptions это ужасно вообще. это огромная ошибка. сделанном на основе идей джавы c# тоже никаких checked exceptions нет.

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

лови исключения как можно ближе к их выбросам, в идеале - один уровень

И какой толк с таких исключений?

ya-betmen ★★★★★
()
Ответ на: комментарий от deep-purple

лови исключения как можно ближе к их выбросам, в идеале - один уровень.

чушь. нужно делать ровно наоборот.

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

а, т.е. ловить ты их будешь в майне, а бросать где попало в глубинище )) нюню.

прям так и вижу:

троу «ой, не смог открыть файло!»;

...

// гдето в нижней части майна

кач (бл) { ретурн 1; }

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

Что именно тебя рассмешило? Для критичных исключений так и делают.

А то что ты предлагаешь – это коды возврата, извращенно эмулированные на исключениях.

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

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

Не вводите @eao197 в заблуждение.

struct MyType {}

impl Drop for MyType {
    fn drop(&mut self) {
        panic!("oh nooooo!");
    }
}

fn main() {
    MyType {};
}
thread 'main' panicked at 'oh nooooo!', src/main.rs:5:9
RazrFalcon ★★★★★
()
Ответ на: комментарий от KivApple

try..catch должен иметь ценность, а не применяться повсеместно

К сожалению в C++ нет нормального механизма обработки ошибок (про С вообще молчу), поэтому все используют исключения.

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

поэтому все используют исключения.

Ага, если бы.

Пару лет назад был опрос среди C++ников. Там выяснилось, что больше половины опрошенных не имеют возможности применять исключения.

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

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

Хотя я встречал либы которые поддерживают оба варианта, что совсем печально. Поддержка исключений и ручной проброс ошибок через макросы в одном флаконе. Вроде этой какой-то парсер был.

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

Исключения и проброс ошибок не исключают друг друга. Не вижу противоречия. Тот же Result можно имитировать через std::variant. По скорости примерно то же будет.

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

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

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

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

не исключают друг друга

Технически – нет, но пихать сразу все не есть хорошо, юзеру придется проверять сразу два пути, если потребуется обрабатывать все ошибки.

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

Тот же Result можно имитировать через std::variant.

Для Result-а есть expected-lite, реализованный на основе предложения в стандарт C++. Само это предложение в стандарт в C++20 не попало. По словам Полухина в следующий стандарт оно может попасть, либо же туда попадет другой подход к выбросу исключений в C++, который сейчас пытается продвигать Саттер.

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

У Саттера интересное предложение. Сдается мне, его так и не примут (он еще в С++17 его пытался продвинуть), но мечтать не запрещено.

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

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

Это очень холиварная тема.

Тот же Result можно имитировать через std::variant. По скорости примерно то же будет.

При чём тут скорость. Главное удобство, которого нет.

RazrFalcon ★★★★★
()

Динамическое распределение памяти - это прежде всего конкуренция за ресурс и следовательно непредсказуемое поведение корректно написанной программы в процессе выполнения.

В тех областях, где используется Ада зачастую тем или иным способом запрещено использование динамического распределения памяти (да и много чего ещё, см. Ravenscar profile например).

Поэтому, с точки зрения выделения/высвобождения памяти механизм исключений ничем особо не выделяется в целевой области применения языка.

Если же Аду рассматривать как ЯП общего назначения, то для «автоматизации» освобождения и «предотвращения» утечек при работе с динамической памятью предусмотрен начиная со стандарта Ada95 пакет Ada.Finalization и в стандарте Ada2012 сильно развились подпулы памяти.

Есть ещё множество более тонких инструментов типа лимитированных типов, выражений-исключений и т. д.

Если применять эти инструменты, то распространение исключения (и не только) не приведёт к утечке.

Но если честно, акцент со всех этих «утечек» и борьбы с ними в Аде уже давно сместился. Начиная с 2014 года во всю развивается и внедряется подмножество Ады - SPARK. Это формальное доказательство корректности программ.

ksicom
()
Ответ на: комментарий от deep-purple

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

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

ya-betmen ★★★★★
()
Ответ на: комментарий от ksicom

Спасибо за развернутый ответ!

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

то что ты предлагаешь – это коды возврата, извращенно эмулированные на исключениях

Так там выше и говорят что это одно и то же.

deep-purple ★★★★★
()
Ответ на: комментарий от ya-betmen

Идея ошибок в том, что ты их можешь обрабатывать там где тебе удобно

Я поправлю за тебя:

Идея исключений

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

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

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

Так там выше и говорят что это одно и то же.

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

А не про то, что исключения и коды возврата – это одно и тоже.

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

-- Смотри! Там бледная луна!
-- Нет, это спутник земли!

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

В расте есть ?

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

:)))

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

Ну то есть исключения не нужны. Одного catch в main с завершением и логированием хватит всем.

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

троу «ой, не смог открыть файло!»;

Ну так обрабатывать надо такую ситуацию, а не исключение бросать.

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

В расте есть ?

Вонючие портянки с матчингом ошибок сбрызнули одеколоном. Это судьба ФП-плебеев: поесть говна и запить сахарным сиропом.

anonymous
()

забыли. я её убрала из сборки gcc. я не видела ни одного проекта на этом языке за много лет.

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

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

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