Страницы

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

вторник, 6 ноября 2018 г.

Язык 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 ветке обработки ошибки добавить поддержку обработки исключения, которого раньше не существовало.


Ответ

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

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

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