Pull to refresh

Comments 20

Код возврата очень легко проигнорировать.

[[nodiscard]] + -Werror и вот уже не очень легко проигнорировать код возврата.

UFO just landed and posted this here

В C23 добавят [[nodiscard]] и [[maybe_unused]]. И gcc, и clang позволяют уже сейчас включить C23.

[[nodiscard]]это C++11. Исключения же проектировались раньше.

По поводу штук вроде __attribute__((warn_unused_result)). Насколько я знаю, это расширения компиляторов -> писать кроссплатформенный код становится гораздо неприятнее.

C++17 атрибут. (не важно особо, так, придираюсь)

Не очень понял в контексте «исключения проектировались раньше», ну да, это середина 90-х, вроде еще до шаблонов, как это связано с тем что в статье обсуждаются сегодняшние способы решения (и возможное завтра?)
со slonopotamus согласен, если зацепиться только за «легко проигнорировать» это ну неплохое решение (остается не забывать втыкать атрибут, что тут поделать).

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

Ну я скорее конкретно этот момент прокомментировал. Понятно, что это не единственная проблема. Например ещё код обработки сильно переплетался с бизнес-логикой. И разные другие неприятности.

Очевидно с таким способом можно жить, если немножко сознательности проявлять. В go вот живут.

потому хорошо бы помечать их noexcept

начиная с C++11 если вы не объявили деструктор throw(...) / noexcept(..), он и так считается noexcept. Это кстати breaking change было, у кого-то даже что-то ломалось от этого :D
я работал с легаси кодовой базой где были исключения в деструкторах, но я не дожил до ее миграции на С++11.

начиная с C++11 если вы не объявили деструктор throw(...) / noexcept(..), он и так считается noexcept

Если все предки и члены этого типа также noexcept.

Хорошее уточнение. (noexcept или не указан). Ну именно деструкторы членов, да.

Думаю, вы тоже видели код с конструкциями вида 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>

Sign up to leave a comment.

Articles