Грокаем C++
9.36K subscribers
45 photos
1 video
3 files
567 links
Два сеньора C++ - Владимир и Денис - отныне ваши гиды в этом дремучем мире плюсов.

По всем вопросам (+ реклама) @ninjatelegramm

Менеджер: @Spiral_Yuri
Реклама: https://telega.in/c/grokaemcpp
Мы на TGstat: https://tgstat.ru/channel/@grokaemcpp/stat
Download Telegram
​​Возврат ошибки. std::optional
#опытным

У std::variant довольно громоздкий интерфейс при возврате ошибки вместе с результатом работы функции. Но в С++17 появился еще один класс, который имеет семантику "Или" для типов + более дружелюбный интерфейс.

Это std::optional. Этот шаблонный класс либо содержит нужный тип, либо не содержит его. Вот так может выглядеть код:

struct Error {
std::string message;
};

std::optional<double> safe_divide(double a, double b) {
if (b == 0.0) { // здесь нужна нормальная проверка на равенство с epsilon
return std::nullopt;
}
return a / b;
}

auto div_result = safe_divide(10.0, 2.0);

if (div_result.has_value()) {
std::cout << "Result: " << div_result.value() << std::endl;
} else {
std::cout << "Error: there is no value" << std::endl;
}
// или с операторами
if (div_result) { // operator bool
std::cout << "Result: " << *div_result << std::endl; // operator*
} else {
std::cout << "Error: there is no value" << std::endl;
}


Для того, чтобы вернуть пустой optional, используется константа std::nullopt. А в остальном интерфейс очень похож на std::expected за исключением доступа к ошибке.

Но на мой взгляд, std::optional не очень подходит для обработки ошибок.

👉🏿 Он имеет семантику наличия или отсутствия значения. Отсутствие значения - это в принципе нормальная ситуация в программировании. Вы сделали Select к базе и получили пустоту, запросили что-то по апи и получили пустоту - вот самое место для std::optional.

Получается, что вы в одном месте кодовой базы используете опшинал для простой индикации наличия результата, а в другом случае отсутствие значения означает ошибку. Это несколько сбивает с толку. Хочется использовать разные инструменты для обоих этих случаев.

👉🏿 Если вам нужно специфицировать, какая конкретно ошибка произошла, то std::optional умывает руки. Нужно либо output параметры использовать, либо в принципе другой класс.

Если есть 23-й стандарт или доступ к бусту, то лучше использовать std::expected или boost::outcome.

Use the right tool. Stay cool.

#cpp17
15🔥8👍6😁2👎1
Обработка ошибок Шердингера
#опытным

Мы уже поговорили о том, что есть 2 подхода к обработке ошибок - исключения и возврат кода ошибки(std::expected или output параметры).

И хоть стандартная библиотека насквозь пропитана исключениями, она все-таки иногда, очень редко предоставляет альтернативные варианты. Например std::from_chars или std::to_chars.

Интересно, что в библиотеке std::filesystem очень многие функции и методы имеют две перегрузки работы с данными: одна с исключениями, другая - без. Например:

bool exists( const std::filesystem::path& p );
bool exists( const std::filesystem::path& p, std::error_code& ec ) noexcept;

// or

bool remove( const std::filesystem::path& p );
bool remove( const std::filesystem::path& p, std::error_code& ec ) noexcept;


std::filesystem завезли в стандарт относительно поздно, поэтому было время задуматься о людях, пишущих небросающий код.

Однако выше приведены "образцово показательные" перегрузки. Посмотрите вот на это:

directory_iterator& operator++();
directory_iterator& increment( std::error_code& ec );


Есть класс std::filesystem::directory_iterator и эти итераторы нужно уметь инкрементировать, чтобы двигаться по элементам директории. Так как сигнатура операторов в С++ не поддерживает лишние параметры, то для варианта с кодом ошибок приходится определять именованный метод.

Обратите внимание, что increment не объявлен как noexcept!

То есть используя increment, вы не можете гарантировать отсутствие исключений. Да, ошибки при работе с файловой системой ОС передаются в качестве кодов ошибок. Но тот же std::bad_alloc increment кинуть может.

По всей видимости, мотивация не выбрасывать исключения связана с тем, что вызывающие стороны, использующие версию с исключениями, часто замусорены локальными блоками try/catch для обработки «рутинных» событий. Условно: при работе с файлами может оказаться, что у программы нет прав доступа для них. Это в целом нормальная ситуация в файловой системе, но в первой перегрузке эти ситуации репортятся через исключения, как исключительные ситуации.

Дизайн странный и путает людей. Поэтому будьте аккуратны с std::filesystem, если реально хотите убрать исключения с глаз долой.

Don't be confused. Stay cool.

#cpp17
👍1810🔥6😁2