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] w00dy.livejournal.com 2012-04-21 07:07 pm (UTC)(link)
Тю, делай код в стиле:
CHECK (AllocateResource1, Fin0) ;
CHECK (AllocateResource2, Fin1) ;
CHECK (AllocateResource3, Fin2) ;

use resources

FreeResource3 ;
Fin2:
FreeResource2 ;
Fin1:
FreeResource1 ;
Fin0:


и макрос
CHECK (func, label) \
if (!func ())
goto label ;
Edited 2012-04-21 19:09 (UTC)

[identity profile] sbj-ss.livejournal.com 2012-04-21 07:43 pm (UTC)(link)
Да там народ издевается, как может.
Видел и такое:
int stage = 0;
if (func1() < 0)
  goto done;
stage = 1;
if (func2() < 0)
  goto done;
stage = 2;
if (func3() < 0)
  goto done;
done:
switch(stage)
{
  case 2:
    free_res2();
  case 1:
    free_res1();
  default:
    break;
}

Кстати, что забавно - встречено в MSDN :)

[identity profile] vp.livejournal.com 2012-04-21 07:46 pm (UTC)(link)
Какой феерический трындец.
(deleted comment)

[identity profile] sbj-ss.livejournal.com 2012-04-21 08:21 pm (UTC)(link)
Ребе, есть одна проблема. Помните, почему во времена царя Ирода Бейсика было хорошим тоном нумеровать команды с шагом в 10? :)
Тут то же самое. Если при доработке кода между вызовами func1 и func2 потребуется делать вызов func3, то придётся либо делать stage вещественным числом (и до 1.0625 запросто дойдёт), либо запутывать себе голову.
Код такой имеет определённое право на жизнь, да, но хотя бы enum для stage объявить не мешает.
Edited 2012-04-21 20:28 (UTC)
(deleted comment)

(no subject)

[identity profile] sbj-ss.livejournal.com - 2012-04-21 20:39 (UTC) - Expand
(deleted comment)
(deleted comment)

(no subject)

[identity profile] sbj-ss.livejournal.com - 2012-04-21 21:45 (UTC) - Expand

(no subject)

[identity profile] sbj-ss.livejournal.com - 2012-04-21 21:33 (UTC) - Expand

[identity profile] theiced.livejournal.com 2012-04-21 08:35 pm (UTC)(link)
где трындец? нормальный код. только надо забыть бред всунутый в моск долбоёбами про то что "goto плохо".

[identity profile] sbj-ss.livejournal.com 2012-04-21 08:40 pm (UTC)(link)
С "goto плохо" - в жопие, где говно, с места, это понятно :)

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 20:48 (UTC) - Expand

[identity profile] theiced.livejournal.com 2012-04-21 08:49 pm (UTC)(link)
не ну понятно что бензопилой можно себе хуй отпилить. бензопила то не виновата :]

[identity profile] vp.livejournal.com 2012-04-21 08:51 pm (UTC)(link)
А как быть с тем, что использование goto - это последние 20 лет является безоговорочным поводом для ИТ-убийства канделябром? :)

[identity profile] metaclass.livejournal.com 2012-04-21 08:52 pm (UTC)(link)
В Цэ goto для обработки ошибок разрешается :)

(no subject)

[identity profile] vp.livejournal.com - 2012-04-21 20:57 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:00 (UTC) - Expand

(no subject)

[identity profile] vp.livejournal.com - 2012-04-21 21:01 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:09 (UTC) - Expand

(no subject)

[identity profile] vp.livejournal.com - 2012-04-21 21:11 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:13 (UTC) - Expand

(no subject)

[identity profile] vp.livejournal.com - 2012-04-21 21:18 (UTC) - Expand

(no subject)

[identity profile] dair-spb.livejournal.com - 2012-04-22 08:55 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2012-04-22 09:08 (UTC) - Expand

(no subject)

[identity profile] max630.livejournal.com - 2012-04-22 04:14 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2012-04-21 21:02 (UTC) - Expand

(no subject)

[identity profile] vp.livejournal.com - 2012-04-21 21:05 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:06 (UTC) - Expand

(no subject)

[identity profile] vp.livejournal.com - 2012-04-21 21:09 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2012-04-21 21:18 (UTC) - Expand

(no subject)

[identity profile] vp.livejournal.com - 2012-04-21 21:19 (UTC) - Expand

(no subject)

[identity profile] thedeemon.livejournal.com - 2012-04-22 05:31 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2012-04-21 21:07 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:10 (UTC) - Expand

(no subject)

[identity profile] sbj-ss.livejournal.com - 2012-04-21 21:17 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:35 (UTC) - Expand

[identity profile] theiced.livejournal.com 2012-04-21 08:54 pm (UTC)(link)
ребе, только среди идиотов которые не умеют думать вообще. их потолок - говносайтики на аспнет и они нам не интересны :]

[identity profile] theiced.livejournal.com 2012-04-21 08:55 pm (UTC)(link)
ну и вы же понимаете что ваши любимые эксепшны это на самом деле "готу хуй пойми куда".

(no subject)

[identity profile] vp.livejournal.com - 2012-04-21 21:00 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:02 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2012-04-21 21:06 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2012-04-21 21:01 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:04 (UTC) - Expand

(no subject)

[identity profile] berezovsky.livejournal.com - 2012-04-21 21:06 (UTC) - Expand

(no subject)

[identity profile] theiced.livejournal.com - 2012-04-21 21:08 (UTC) - Expand

[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)
Да, хороший вариант.