среда, 4 января 2023 г.

[prog.c++] Всегда ли выброс исключения из noexcept функции является ошибкой?

На написание этой заметки подтолкнула статья "Топ-10 ошибок в C++ проектах за 2022 год", а если точнее, то ошибка, которая там поставлена на шестое место. В двух словах: в операторе перемещения некого класса вызывается метод setName, в котором может быть брошено исключение. При этом сам оператор перемещения помечен как noexcept.

На мой взгляд, ситуация здесь не так проста, как может показаться.

И дело в том, что некоторые C++ программисты (включая меня самого в прошлом) смотрят на маркер noexcept (не бросает) как на маркер nofail (не сбоит, всегда OK). Т.е., если у нас есть noexcept-функция, то она не может завершиться с ошибкой.

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

Однако, noexcept означает nofail далеко не всегда.

Зато noexcept всегда означает norecover (если сбоит то без шансов на продолжение). Т.е., если в noexcept функции что-то пошло не так, то восстановиться до какого-то вменяемого состояния у нас уже нет возможности.

К сожалению, в C++ nofail от norecover не отличимы. Мы можем написать только noexcept, но не можем дать дополнительной информации: гарантируем ли мы отсутствие ошибок или же лишь подтверждаем, что при возникновении ошибки восстановление невозможно и единственный разумный путь -- это std::terminate.

И если рассматривать noexcept именно как norecover, то вызовы бросающих исключение функций в noexcept-функциях становятся вполне себе оправданными. Какой смысл оборачивать бросающие функции в try..catch чтобы в catch самому вызвать std::abort? Сам по себе noexcept сделает это не хуже нас.

В общем, я постепенно пришел к тому, чтобы рассматривать noexcept-функции прежде всего как norecover-функции. И только в некоторых, чаще всего в тривиальных случаях, как nofail-функции.

А посему теперь спокойно отношусь и к тому, что в noexcept-функция могут запросто вызываться бросающие исключения функции без оборачивания их в try..catch. И не считаю такое поведение ошибкой.

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

Полезно такое иметь потому, что:

  • человеку свойственно ошибаться и я могу думать, что вызванная мной функция f() является noexcept, а на самом деле нет;
  • все течет, все изменяется. Изначально f() могла быть noexcept, но со временем ее могли модифицировать и убрать маркер noexcept. Но я, как пользователь f(), об этом даже не узнаю.

Не думаю, однако, что когда-нибудь в C++ noexcept-блок появится.

Да и вообще, глядя на то, что подобавляли в C++20 (например, модули, в которые без поллитры не въедешь) и в C++23 (например, deducing this), все больше и больше прихожу к заключению, что я недостаточно умен, чтобы освоить настолько сложный инструмент :(

PS. Возвращаясь к упомянутой выше статье. Думаю, что предложенные автором статьи способы исправления обнаруженной анализатором "ошибки", мягко говоря, неуместны. Там нужно было копать совсем в другую сторону (а то и в несколько разных сторон).

вторник, 3 января 2023 г.

[prog.c++.sobjectizer] Бахвальства пост

Прошу простить мне минутку самолюбования, но вот свеженькое с reddit-а:

Отрадно здесь то, что в обсуждении Модели Акторов в C++ ссылки на SObjectizer стали появляться без моего участия :)

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


Еще интересное нашлось. Некоторое время назад уже упоминал в блоге чужой проект, который использует SObjectizer. Теперь вот обнаружилась статья, которая рассказывает про этот проект: LabNet hardware control software for the Raspberry Pi.


Пара слов про статус проекта. SObjectizer жив. Он используется и не только лишь мной. В этом плане все с ним хорошо.

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

Но. Но есть большое но :(

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

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

Посему есть понимание того, что хочется сделать. Нет понимания как и когда. Но есть желание. Поэтому как будет, так и будет.