Комментарии 20
Код возврата очень легко проигнорировать.
[[nodiscard]]
+ -Werror
и вот уже не очень легко проигнорировать код возврата.
[[nodiscard]]
это C++11. Исключения же проектировались раньше.
По поводу штук вроде __attribute__((warn_unused_result))
. Насколько я знаю, это расширения компиляторов -> писать кроссплатформенный код становится гораздо неприятнее.
Не очень понял в контексте «исключения проектировались раньше», ну да, это середина 90-х, вроде еще до шаблонов, как это связано с тем что в статье обсуждаются сегодняшние способы решения (и возможное завтра?)
со slonopotamus согласен, если зацепиться только за «легко проигнорировать» это ну неплохое решение (остается не забывать втыкать атрибут, что тут поделать).
Расширения, хз зачем, nodiscard давно поддерживается. Если рабоаешь в проекте где не завезли 17 плюсы, там куда больше проблем и болей чем «как бы тут клево ошибки обработать»
потому хорошо бы помечать их noexcept
начиная с C++11 если вы не объявили деструктор throw(...) / noexcept(..), он и так считается noexcept. Это кстати breaking change было, у кого-то даже что-то ломалось от этого :D
я работал с легаси кодовой базой где были исключения в деструкторах, но я не дожил до ее миграции на С++11.
Думаю, вы тоже видели код с конструкциями вида
catch (...) {}
, потому что “ну там какие-то исключения вылетают, а падать не хочется”.
Мне кажется это вполне приемлимый случай, если мы говорим о "неожиданных" исключениях (читай, багах), и при этом мы можем выйти после такого исключения в какое-то определёенное состояние. Ну например прибить/перезапустить какой-то один модуль, от чего не пострадает остальная система. И когда альтернативой по сути является полный крэш системы.
Вообще обработка ошибок на самом деле довольно недооцененная проблема. По сути ведь в системе всегда есть ошибки нескольких разных типов, реагировать на которые надо по-разному. Но при этом по сути, почти всегда, предлагается для всех них использовать одни и те-же механизмы. Мне кажется отсюда большая часть проблем и вылазит.
Довольно давно таскаю с собой вот такой вот header-only helper
Пример:
E<int> foo() {
if (something_wrong())
return Error() << "something happened";
return 100;
}
Error bar() {
if (auto rs = foo(); !rs)
return Error() << "foo error: " << error(rs);
return {}
}
...
auto rs = foo();
if (!rs)
return;
auto& value = value(rs);
auto rs = bar();
if (!rs) {
std::cout << rs;
}
int c = *foo();
Видимо, проект закрытый, выдаёт 404-ю ошибку по ссылке.
Во-первых, ссылка битая. Во-вторых, чем это отличается от expected?
Да, простите, дал ссылку на private репозиторий. Выложил в gists:
https://gist.github.com/hoxnox/2a877a810aae21c7e0f523160678abf0
Идеологически действительно очень похоже на expected. Небольшие отличия. Но это уже работает - достаточно подключить хидер-файл.
IMHO! Сойдет для мелких и пет проектов, но вряд ли подойдет для больших проектов. Проблема в том, что std::stringstream
уж больно жирный, например в GCC - 392 байта.
Думаю было бы оптимальней использовать просто std::string
, т.к. дешевле перевыделять динамическую память в исключительных случаях, ведь писать мы туда будем только при ошибках, чем постоянно (в худшем случае на вызов каждой функции) отжирать по 400 байт памяти. А сам std::string
, весит обычно не много, в том-же GCC всего 32 байта.
Кстати придумалось еще более простое решение. Надо просто в классе Error
сделать переменную msg_
указателем, а не значением. Тогда, пока у нас нет ошибки весь этот класс занимает всего 4 байта, что по сути и так минимально возможный размер объекта для 64-битных архитектур. А если уж случилась ошибка, то можно и заалоцировать такой большой класс как std::stringstream
.
Исключения задумывались в мире, где существуют деструкторы, а значит и RAII.
Угу, при этом исключение в деструкторе - ггвп.
Исключения они на то и исключения, чтобы детектить ошибки. Exceptions are for exceptional.
<sarcasm>Именно поэтому исключения названы исключениями, а не ошибками?</sarcasm>
Обработка ошибок и C++