Re[6]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 09.11.20 13:29
Оценка:
Здравствуйте, ononim, Вы писали:

O>Ну, сделай два типа исключений — один для retry-able, второй — для fatal проблем.


Для повторяемых операций исключения допустимы лишь в случае, когда что-то пошло совсем непредсказуемо, и верхний уровень (человек или код) решает повторить попытку в надежде на скорейший успех. В ситуации, когда перспектива многократных повторов заранее известна, исключения — моветон. Хотя нынче многие строят алгоритм именно так, используя исключения просто для информирования о состоянии.
Re[7]: Возврат ошибок из недр вложенных вызовов
От: ononim  
Дата: 09.11.20 23:28
Оценка:
O>>Ну, сделай два типа исключений — один для retry-able, второй — для fatal проблем.
ЕМ>Для повторяемых операций исключения допустимы лишь в случае, когда что-то пошло совсем непредсказуемо, и верхний уровень (человек или код) решает повторить попытку в надежде на скорейший успех. В ситуации, когда перспектива многократных повторов заранее известна, исключения — моветон. Хотя нынче многие строят алгоритм именно так, используя исключения просто для информирования о состоянии.
В конечном итоге это все вкусовщина. Активное использование исключений в целом налагает определенные требование на код, а значит предполагает адекватный уровень команды, что не всегда достижимо, если не вы эту команду контролируете. Но имхо делить ситуацию на "ужас-ужас-ужас" "ужас" и "норм" и использовать разные механизмы для разных уровней ужаса — это лишь повышает сложность, а профит не очень заметен. Разве что, если надо чтоб "ужас" быстро отрабатывал.
Как много веселых ребят, и все делают велосипед...
Re[8]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 10.11.20 20:10
Оценка:
Здравствуйте, ononim, Вы писали:

O>Активное использование исключений в целом налагает определенные требование на код


Хорошая практика программирования предполагает "активное" использование исключений только в том смысле, что они часто объявляются, но редко (в исключительных случаях) выбрасываются. Бросать исключения в ожидаемых ситуациях — плохая практика.

O>имхо делить ситуацию на "ужас-ужас-ужас" "ужас" и "норм" и использовать разные механизмы для разных уровней ужаса — это лишь повышает сложность, а профит не очень заметен. Разве что, если надо чтоб "ужас" быстро отрабатывал.


"Быстро" — это насколько? Любое исключение C++ обрабатывается через исключение ОС (по крайней мере, под виндой). Если каждая функция, которая может отработать с различными особенностями, для информирования о них будет бросать исключения — накладные расходы возрастут весьма заметно. Конечно, если таких функций немного, то на общем фоне они могут и потеряться, но сам подход к такому использованию исключений ущербен, поскольку к нему легко привыкнуть, а когда вдруг окажется, что расходы стали чрезмерными, затраты на переделку, скорее всего, окажутся неприемлемыми. И начнется традиционное "купите процессор побыстрее".
Re: Возврат ошибок из недр вложенных вызовов
От: gwg-605 Россия  
Дата: 08.03.21 22:51
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Типичная ситуация: функция верхнего уровня вызывает функцию более низкого уровня, та — следующую и т.п., и где-то на N-м уровне очередной вызов возвращает ошибку. Если это что-то уникальное, то можно вернуть наверх (хоть через обычные return, хоть через исключения) уникальный код и/или описание. Но если ошибка общего характера (нехватка ресурсов, отказ в доступе, разрыв соединения и т.п.), то неплохо бы вернуть наверх более подробную информацию, которую можно показать пользователю, чтобы он имел более-менее адекватное представление о проблеме.


Надо понимать, что есть три класса ошибок:
1. Ошибки которые может исправить только пользователь, типа неправильны путь к файлу, неправильное значение параметра и тп
2. Ожидаемые кодом ошибки которые могут корректно обрабатываться самим кодом, полное разнообразие
3. Приложение умерло, типа нет памяти


ЕМ>Вообще, какие способы возврата уточненных результатов из многократно вложенных вызовов ныне считаются кошерными?

Думаю это вопрос из серии "священные войны"

Я пришел к некоторому объекту: CError который используется для ошибок из пунктов №1 и №2. Это класс враппер над референсным контейнером для ошибки. Если ошибки нет, то передается просто nullptr, иначе указатель на контейнер с даннмыи об ошибке. Ошибки имеют уникальный идентификатор — для обработки внутри кода, и есть опциональное текстовое поле описывающее ошибку для пользователя. Также ошибка может содержать вложенные ошибки и доп данные (пара ключ-данные).

Функция которая создает ошибку решает присвоить только номер ошибки:
err.Set( ErrorBufferNotEnough )

или сформировать читабельное сообщение об ошибке:
err.Set( FileNotFound, "File '%s' not found", file_name )


На верхнем уровне функция может просто пределать ошибку выше:
err = f();
if( err.IsError() ) {
  return err;
}

или сформировать новую:
err = f();
if( err.IsError() ) {
  return CError( ErrorFatal );
}

или сформировать стек ошибок:
err = f();
if( err.IsError() ) {
  return CError( ErrorFatal, err );
}

или передавать ошибки со сформированнымим сообщениями, а другие хендлить другим образом:
err = f();
if( err.HasMsg() ) {
  return err;
} else if( err.IsError() ) {
  return CError( ErrorFatal, err );
}


Всегда можно получить ошибку err.GetMessage( opt = 0 ), по умолчанию получаешь сообщени об ошибке самого верхнего уровня, но в зависимости от опций можно получить весь стек ошибок. Если было сформировано сообщение, то получим это сообщение, если нет то стандартное сообщение ассоциированное с номером ошибки.

Так же можно аттачить доп данные (ключ-данные) к ошибке:
CError result;
if( check username field ) {
   CError err( ErrorInvalidConfig, "Invalid value '%s'", val );
   err[ "field" ] = "username";
   result += err;
}
if( check first_name field ) {
   CError err( ErrorInvalidConfig, "Invalid value '%s'", val );
   err[ "field" ] = "first_name";
   result += err;
}
return result;

GetMessage() вернет:
username: Invalid value 'zzzz'
first_name: Invalid value 'ddddd'


Сами ошибки можно передавать разными способами:
возвращаемое значение:
CError = f1();

как output значение:
CError err;
auto r = f1( err );

через TLS:
auto r = f1();
if( r == InvalidValue ) {
   CError err = CError::GetLastError();
}


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

Для ошибок из третьего пункта бросаю исключение. в 99% случаев это бэд аллок.

В общем как-то так.
Re[2]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 11.03.21 14:13
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6>Я пришел к некоторому объекту: CError который используется для ошибок из пунктов №1 и №2. Это класс враппер над референсным контейнером для ошибки. Если ошибки нет, то передается просто nullptr, иначе указатель на контейнер с даннмыи об ошибке.


Я примерно к тому же пришел за время существования темы. Только пока у меня везде прокидывается изнутри наружу простенький контейнер, содержащий код ошибки и указатель на ее расширенное описание. Возвращать указатель, наверное, будет и изящнее, и экономичнее, но мне не хотелось бы закладываться на плюсовые исключения, которые до сих пор толком не поддерживаются в ядре. Впрочем, на случай нехватки памяти можно предусмотреть статический объект, или даже какой-то их запас, если захочется конкретизировать ситуацию.
Re[8]: tagged_scalar
От: sergii.p  
Дата: 12.03.21 06:59
Оценка:
Здравствуйте, so5team, Вы писали:

S>Шаблон tagged_scalar там сделан для того, чтобы при вызове конструктора limiter нельзя было перепутать аргументы, которые имеют один и тот же тип. Такой вот незамысловатый трюк для того, чтобы уменьшить количество ошибок в коде (ну нельзя же все на откуп assert-ам отдавать, ей Богу).


попутно замечу, что подобный tagged_scalar для типов unsigned int легче можно реализовать с помощью enum. Не надо дополнительно реализовывать операции сравнения, вычисление хэша и т. п. std::byte реализован именно так. Так что можно сказать, канонический подход.
https://godbolt.org/z/vbx1qK
Re[2]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 14.03.21 09:12
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6>Я пришел к некоторому объекту: CError


Забыл уточнить: в Вашей практике бывала надобность в счетчике ссылок для таких объектов? Или они всегда безусловно уничтожались после анализа содержимого?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.