-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Проблема
При проектировании систем без исключений возникает необходимость возвращать ошибки через std::error_code, std::optional<T>, std::expected<T>, ..., что приводит к массовым проверкам возвращаемых значений:
- Early-return (многословно):
if (auto ec = fn1(arg1)) {
// обработка
return ec;
}
if (auto ec = fn2(arg2, arg3)) {
// обработка
return ec;
}- Явное присвоение (бойлерплейт):
std::error_code ec;
ec || (ec = fn1(arg1));
ec || (ec = fn2(arg2, arg3));
return ec;- Перегрузки с передачей error_code (калечит интерфейсы функций):
std::error_code ec;
ec || fn1(arg1, ec);
ec || fn2(arg2, arg3, ec);
return ec;- Кастомные классы с ленивыми вычислениями (синтаксический мусор):
result_chain<std::error_code> ec;
ec || [&]{ return fn1(arg1); };
ec || [&]{ return fn2(arg2, arg3); };
return ec.value();При этом
- Перегрузки операторов
||,&&не поддерживают short-circuit evaluation - Макросы мало кто любит, к тому же их нельзя вынести в модуль
Предложение
Ввести атрибут/спецификатор breakeval, который:
- Применяется к функции/методу
- Заставляет компилятор всегда организовывать вычисление самого левого аргумента первым
- Останавливает дальнейшие вычисления аргументов функции, если результат вычисления первого аргумента
false - Ошибка компиляции, если тип первого параметра не приводим к bool
Синтаксис:
class Class {
bool operator bool() const { return !ec_; }
breakeval Class& operator||(error_type& ec); // при *this == false вычисления останавливаются
};
breakeval Class& operator||(Class& c, error_type& ec); // при c == false вычисления останавливаютсяОбласти применения
1. Логгирование
// Вместо макросов:
bool logger::operator bool() { return log_level_ < current_log_level(); };
template<typename Args...>
breakeval void logger::operator()(Args&&...) { ... }
...
log::info ("Something i need to log", "another data");
log::warn ("Attention! Its optional evaluation");2. Цепочки вычислений
std::error_code ec = make_error_code( );
// Аргументы оператора || не будут вычисляться благодаря breakeval
ec || large_arg_calc() || large_arg_calc2();
return ec;
// value_or с ленивым вычислением
// (не будет вычислять large_default_value_creation() в позитивном сценарии)
get_optional_value().value_or( large_default_value_creation() );3. Работа с потенциально инвалидными объектами
std::ofstream os("some_path");
os << get_large_title() << get_large_body() << get_some_other_data();
// Вычисления останавливаются при первой ошибке потокаЧто дает?
- Читаемость: можно будет упразднить синтаксический мусор проверок
- Обратная совместимость: введение нового атрибута не сломает существующий код
- Эффективность: позволяет исключать ненужные вычисления, постепенно это можно будет внедрить и в std::
- Универсальность: работает не только с объектами, но и с глобальными функциями
PanzerschrekGitSparTV and dywoq
Metadata
Metadata
Assignees
Labels
No labels