Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Язык C и способы информирования о результатах действий

#c #исключения


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

В языке С существует огромное количество способов сообщать о результатах действий
и возникших ошибках. Сейчас в меня кто-то кинет тухлым овощем, возражая, что в C имеются
только лишь примитивные коды возврата.

Да, только коды возврата... Но они используются очень по-разному.

Конечно, можно реализовать исключения, используя setjmp/longjmp. Но, как сказал Д.
Ритчи: Если тебе нужен PL/1, ты знаешь, где его взять...

Вот варианты обработки результатов выполнения, которые мне встречаются чаще всего:


Функция возвращает код, который является отчетом о ее работе:

const int r_code = some_func(a, b, c);

Или, например, функция возвращает указатель на созданный объект, а в случае, если
объект создать не удалось, функция возвращает NULL, не предоставляя никакой дополнительной
информации о причине неудачи:

object *obj = object_create(a, b, c);

Так же, функция может возвращать указатель на созданный объект, записывая в заданное
расположение детальный отчет об операции (а не просто дополнительную информацию об ошибке):

information i;
object *obj = object_create(a, b, c, &i);

Еще функция может использовать глобальную переменную текущего потока для предоставления
информации об ошибке:

FILE *f = fopen(a, b);
if (f == NULL)
{
    // Обработка errno()



И еще примерно бесконечное количество вариантов...

При написании кода часто приходится жонглировать различными способами обработки ошибок
и информирования о результатах действий.

Мои вопросы:


Есть ли в C универсальный способ для всего этого? А если нет, то в чем причина?
Было бы интересно узнать, какие еще существуют подходы, в чем их плюсы и минусы?
Есть ли большая разница в сложности разработки/поддержки кода, который использует/не
использует исключения? Речь идет не про исключения в C++, а про исключения в языках
с полноценной сборкой мусора - аля Java.


Например, я некоторое время писал на Java, так вот, когда я увидел реальный промышленный
код, в котором ловились и проверялись все исключения, какие только возможно, то я вообще
не понимал, как это можно поддерживать и развивать, когда на 5 строчек библиотечных
вызовов приходится писать сотни строк try/catch/finally... И при любой малейшей правке
приходится перекапывать весь код, чтобы где-то в 100500 ветке обработки ошибки добавить
поддержку обработки исключения, которого раньше не существовало.
    


Ответы

Ответ 1



Попробую ответить, хотя это и очень сложно, учитывая, кто спрашивает и как. Есть ли в C универсальный способ для всего этого? А если нет, то в чем причина? Универсального способа нет. Рекомендованного к использованию нет. Почему так? все очень просто - это чистый си. Тут программист сам ответственный за свой код, никто не будет ему вытирать ручки и рот. Было бы интересно узнать, какие еще существуют подходы, в чем их плюсы и минусы? В принципе были перечислены все обычные способы. Еще есть несколько методов забить на ошибки (очень часто применяется) использовать сторонний фреймворк (да, для си такое есть. К примеру cello). Есть ли большая разница в сложности разработки/поддержки кода, который использует/не использует исключения? Речь идет не про исключения в C++, а про исключения в языках с полноценной сборкой мусора - аля Java. Сборка мусора имеет весьма относительное отношение к исключениям. И делать какие то умозаключения с изначально неверного предположения чревато странными выводами. Поэтому, даже не будут обсуждать где лучше исключения или сборка мусора. Оставим это холивар-экспертам. Но в жава с спецификацией исключений действительно беда. Нужно либо пробрасывать постоянно весь список допустимых исключений, либо писать базовый и не переживать. В с++ это также пытались сделать, но вовремя отказались (и сделали noexcept, который более понятный). При разработке с исключениями самое главное, что сложно допустить ситуацию, когда программа пошла в разнос. К примеру, одна функция инициализирует объект, а вторая его использует. Если первая может вернуть NULL, а вторая этого не ожидает... то ошибку можно долго и нудно искать. С исключениями это придется обработать и дальше код не пойдет (но все равно, программист это должен закодить, само оно редко так бывает сразу и красиво).

Ответ 2



Механизм исключений очень удобен. Он позволяет обработать исключение именно в том месте, где известно, как его обработать. Пример: есть у вас функция записи в лог Как вы ее пишете на кодах ошибок int log(char* msg, int len) { FILE * f = fopen(.....) if (f == NULL) return FILE_NOT_OPEN; if (fwrite(f, msg, len) != len) return MESSAGE_NOT_WRITE; fclose(f); return SUCCESS; } Уже в таком коде вы теряете исходную ошибку. Файл не открыт. А почему? Не все байты записались. По какой причине? Если вы результат функции log не обработаете сразу после вызова, то опять потеряете информацию. И получите, что-то типа int doProcess(...) { if (log(...) != SUCCESS) return LOG_NOT_WRITE; } Чтобы не потерять код ошибки нужно иметь единую систему кодов во всей системе. Включая все используемые библиотеки. Теперь как этот же код будет выглядеть на исключениях void log(char* msg, int len) { try { FILE * f = fopen(.....); try { fwrite(f, msg, len); } finally { fclose(f); } } catch (Exception e) { throw new LogWriteException(e); } } void doProcess(...) { ...... log(....) ..... } try { doProcess(); } catch (LogWriteException e) { processLogFailed(e); } резко упало количество кода и повысилась читабельность Мы обрабатываем ошибки на том уровне, где знаем и можем их обработать Мы не теряем информацию об оригинальном исключении. В своих программах я все апишные вызовы делаю так HANDLE Win32Check(HANDLE res) { if (!res) throw new OSError(GetLastError()); return res; } HANDLE h = Win32Check(CreateEvent(....)); try { ..... } finally { CloseHandle(h); } И напоследок - наличие или отсутствие сборки мусора никак не влияет на возможность использования исключений.

Комментариев нет:

Отправить комментарий