metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2012-04-21 09:12 pm

Чистый C, обработка ошибок

Это, а как в C принято обрабатывать ошибки?
Т.е. обычная программа: я открываю всякие ком-порты, сокеты и файлы, что-то с ними пытаюсь делать, затем закрываю. В дельфи/java/C#/Clojure это всегда делается через обработку исключений в виде try-finally/using или чего-то аналогичного, в C++ - RAII, а вот что делать в С? Аналогично, с выводом сообщений об ошибке - try{..}catch(Exception e) {logger.Fatal(e};raise}

Я каждую вызываемую функцию проверяю на адекватность возвращаемого результата и при ошибке вывожу в stderr сообщение и strerror(errno), причем выглядит это достаточно единообразно, чтобы хотелось автоматизировать, но принято ли делать хитрожопые макросы типа CHECKERROR(some_call(),"some_call failed") и из них вываливаться из программы при ошибках?

А, и это - принято ли в C заниматься конкатенацией строк по поводу и без повода? А то, скажем, я привык в простых программах особо не мудрить и при необходимости складывать строки, если нет явных требований к производительности. Например, какая-нибудь дурь типа генерации строковых команд из шаблонов и параметров - тупо поскладывал строки и вернул результат. А в С придется strcat использовать, буфера какие-то объявлять, память выделять, трястись за ее удаление или же писать результат прямо в выходной файл, что вообще tight coupling.

[identity profile] sbj-ss.livejournal.com 2012-04-21 08:05 pm (UTC)(link)
А вообще, чтобы не забивать себе голову мильёном меток, я обычно делаю тупо и в лоб.
res1_t *res1 = NULL;
res2_t *res2 = NULL;
/* Здесь могут быть получены некоторые ресурсы, и отнюдь не обязательно все.
   Это нормально, просто тот же xmlGetNoNsProp (получить значение атрибута XML-элемента)
   вернёт NULL, если такой атрибут не указан (что в моём случае не ошибка).
 */
/* ... */
if (!cond_1)
  goto done;
if (!cond_2)
 goto done;
/* ... */
done:
if (res1) 
  free_res1(res1);
if (res2)
  free_res2(res2);
Edited 2012-04-21 20:08 (UTC)

[identity profile] metaclass.livejournal.com 2012-04-21 08:11 pm (UTC)(link)
О, вот так и надо.

[identity profile] blackyblack.livejournal.com 2012-04-21 08:39 pm (UTC)(link)
Я, всё-таки, предпочитаю не использовать гото, а делать так:

while(1)
{
if (!cond)
break;

//success
break;
}

//finalizer

[identity profile] metaclass.livejournal.com 2012-04-21 08:45 pm (UTC)(link)
А, лишний уровень вложенности и код, наверно гото проще для восприятия будет.

[identity profile] blackyblack.livejournal.com 2012-04-22 06:35 am (UTC)(link)
Если уж нигде гото нет, то и сюда не стоит пихать. :)
А лишний уровень вложенности компенсируется тем, что циклы можно ставить повсюду безусловные. Тогда в теле цикла можно не париться с условием выхода.

[identity profile] sbj-ss.livejournal.com 2012-04-21 08:45 pm (UTC)(link)
Syntactic sugar. Результирующий машинный код, сдаётся мне, будет идентичен.

[identity profile] vp.livejournal.com 2012-04-21 08:48 pm (UTC)(link)
FreeAndNil, да :)

[identity profile] fkng-stupid-lj.livejournal.com 2012-04-22 11:37 am (UTC)(link)
Имхо, проверки лишние в конце. Вполне можно писать код так, чтобы goto переходил точно к нужной команде освобождения. Меткам название придумывать, конечно, скучно, я их просто нумерую -- e0, e1, e2, и т. д.

[identity profile] sbj-ss.livejournal.com 2012-04-22 12:33 pm (UTC)(link)
Повторюсь - возможна ситуация, когда не все ресурсы захвачены, но это не ошибка. Ну не указан какой-то опциональный атрибут у узла. В таких случаях приходится проверять.

[identity profile] fkng-stupid-lj.livejournal.com 2012-04-22 02:22 pm (UTC)(link)
Такие случаи, имхо, логичнее проверять непосредственно у метки:

e2: if (bar) free_bar(bar); /* то ли выделен, то ли нет */
e1: free_baz(baz); /* заведомо выделен к этому моменту */
e0: return NULL;

[identity profile] sbj-ss.livejournal.com 2012-04-22 02:23 pm (UTC)(link)
Да, хороший вариант.