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] justy-tylor.livejournal.com 2012-04-21 09:22 pm (UTC)(link)
Я на "всё пропало" отдельный буфер делал. Но это на мобилках. Где и как работают сокеты при кончившейся памяти - таким вопросом не задавался.

[identity profile] sbj-ss.livejournal.com 2012-04-21 09:33 pm (UTC)(link)
В реальной жизни захват и освобождение ресурсов не нумерованы. Т.е. будет acquireSomeRes(), allocSomeRes(), getSomeRes(), InitSomeRes() и парные им freeSomeRes(), releaseSomeRes(), deAllocSomeRes(), weDontNeedThisResAnyMore() - и, по закону подлости, именно в парном соответствии. А константа состояния захвата 47 не говорит ничего.
Почему я и настаиваю на использовании для stage перечисления вида
typedef enum _someOpStage {
SOME_OP_STAGE_Res1,
SOME_OP_STAGE_Res2,
/* ... */
} someOpStage
Edited 2012-04-21 21:37 (UTC)

[identity profile] theiced.livejournal.com 2012-04-21 09:35 pm (UTC)(link)
ну а как иначе назовёшь. вы не подумайте - я люблю си и его даже умею (ну ладно, умел, дааавно дело было) - вплоть на адового переписывания части кернела для бута на кастомной материнке с покоцанным биосом. но ассемблер он и есть ассемблер. тока портабельный ;]

[identity profile] kong-en-ge.livejournal.com 2012-04-21 09:36 pm (UTC)(link)
сколько интересных способов залезть на шкаф...

[identity profile] sbj-ss.livejournal.com 2012-04-21 09:36 pm (UTC)(link)
Если честно, я предпочитаю в принципе не заморачиваться подобного рода вопросами. Если человек хочет построить национальную библиотеку с помощью напильника и лопаты, флаг ему в руки. И пусть не обижается, что заработал грыжу. Соответственно если клиент заявляет, что ему RSS парсить, а потом на P3/256Mb DOM-парсером пытается колбасить гигабайты XML, это не моя проблема.

[identity profile] astoon.livejournal.com 2012-04-21 09:39 pm (UTC)(link)
Спасибо что живой.

[identity profile] berezovsky.livejournal.com 2012-04-21 09:41 pm (UTC)(link)
:-)

[identity profile] sbj-ss.livejournal.com 2012-04-21 09:45 pm (UTC)(link)
Чем определяется размер стека? :)

[identity profile] http://users.livejournal.com/mak_/ 2012-04-21 10:24 pm (UTC)(link)
сто лет уже не программировал:) assert не поможет?

[identity profile] sbj-ss.livejournal.com 2012-04-21 10:40 pm (UTC)(link)
Религия не позволяет считать это чем-то бОльшим, чем средство отладки :)
Да и вывести "can't lock file "c:\beetles and frogs.txt", function CrazyClownFunction" assert не даст. А вот юзеры конечной программы вылетающее окошко "Assertion failed, program matzo.exe line 1488" дико ненавидят.

[identity profile] qehgt.livejournal.com 2012-04-21 11:52 pm (UTC)(link)
Мы когда-то делали так:
http://habrahabr.ru/post/141507/

[identity profile] baramin.livejournal.com 2012-04-22 12:52 am (UTC)(link)
В нашем проекте я законодательно запретил С. Ибо, если если есть С, есть и С++. Иначе - нефиг.
Ну, если лезть обратно на пальму, то
1]
status = 0;
while (1) {
if ( fail(status = f1()) )
break;
if ( fail(status = f2()) )
break;
status = f3();
break;
}
//зачищаем всех, кто не NULL

2] лестница (MS COM approach)
var lhv1;
status = create1(&lhv1);
if (fail(status)) {
print_error(status);
} else {
//хитрость в том, что dispose пишется сразу.
//статус заверщения - глобален! Привет HRESULT
//здесь в том же формате пишем второй шаг (рекурсия:) ):
var lhv2;
status = create2(&lhv2);
if (fail(status)) {
print_error(status);
} else {
//next step is here
dispose(lhv2)
}

dispose(lhv1)
}
return status.

[identity profile] esil0x.livejournal.com 2012-04-22 01:18 am (UTC)(link)
setjmp/longjmp уже давно нигде не используется для исключений.

[identity profile] esil0x.livejournal.com 2012-04-22 01:20 am (UTC)(link)
Есть один правоверный способ грамотной обработки ошибок в программах на С, применение которого возможно в 99% случаев. Последовательность действий следующая:

1) Переименовать исходники в *.cpp
2) use raii
3) PROFIT!!!

С конкатенацией строк, говорят, тоже помогает.

[identity profile] theiced.livejournal.com 2012-04-22 01:24 am (UTC)(link)
это же ссылка на хабр!!112

[identity profile] theiced.livejournal.com 2012-04-22 01:25 am (UTC)(link)
это проверенный способ сделать вместо программы кусок говна.

[identity profile] esil0x.livejournal.com 2012-04-22 01:30 am (UTC)(link)
Это работает. Всем похуй.

[identity profile] esil0x.livejournal.com 2012-04-22 01:50 am (UTC)(link)
А, да, тут не упомянули ещё один вариант:

resource1 = alloc_resource1();
if (resource1) {
resource2 = alloc_resource2();
if (resource2) {

// use resource1 & resource2

free_resource2(resource2);
} else
log("alloc_resource2 failed");

free_resource1(resource1);
} else
log("alloc_resource1 failed");


Единственный большой недостаток - с увеличением количества ресурсов растёт вложенность. Иногда помогает разбиение на более мелкие функции.
Но лучше перестать грызть себе нокти и делать так, как я написал в предыдущем комменте.

[identity profile] max630.livejournal.com 2012-04-22 04:14 am (UTC)(link)
пиздеть не надо, в дебиан патчи аттачментами принимаются

[identity profile] permea-kra.livejournal.com 2012-04-22 04:50 am (UTC)(link)
Ну, такова жизнь. Хотя можно тупо писать в буферизованный FILE* , там производительность проседать особо не должна.

writev , кстати, есть в apache portable runtime.

[identity profile] thedeemon.livejournal.com 2012-04-22 05:31 am (UTC)(link)
Если смотреть дебаггером, в любом коде сплошная копипаста - mov mov mov jnz jnz jnz add add add lea lea lea :)
Надо писать так, чтобы ассемблерные инструкции не повторялись! Don't Repeat Yourself! :)

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

[identity profile] metaclass.livejournal.com 2012-04-22 07:04 am (UTC)(link)
Ребе, я теперь окончательно понял, почему ситиинфы под линукс не бывает :)

[identity profile] metaclass.livejournal.com 2012-04-22 07:07 am (UTC)(link)
Торвальдс воспрещает С++ :)
Впрочем, у меня как раз наверно и на С++ можно было бы все сделать и было бы проще.

[identity profile] sleepy-drago.livejournal.com 2012-04-22 07:10 am (UTC)(link)
зависит от степени лени девелопера.
долбо^W можно пользоваться макросом который прыгает за return success где в конце функции в обратном порядке написаны освобождалки ресурсов в нужное место.
Можно иметь и список функций очистки на функцию и вызывать их в обратном порядке. Можно иметь класс (тссс!) строки и конкатенатор к нему. И память выделяемую регионами так чтобы ни одного free в коде не было.
Посмотрите на код тех для кого вы пишете и делайте так же как они - иначе они не примут контрибуции :) Я ж не верю что это себе :D

Page 4 of 6