Чистый C, обработка ошибок
Apr. 21st, 2012 09:12 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Это, а как в 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.
Т.е. обычная программа: я открываю всякие ком-порты, сокеты и файлы, что-то с ними пытаюсь делать, затем закрываю. В дельфи/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.
no subject
Date: 2012-04-21 06:40 pm (UTC)где finalizer - функция, подчищающая говно за some_call
no subject
Date: 2012-04-21 06:45 pm (UTC)(no subject)
From:(no subject)
From:no subject
Date: 2012-04-21 07:07 pm (UTC)CHECK (AllocateResource1, Fin0) ;
CHECK (AllocateResource2, Fin1) ;
CHECK (AllocateResource3, Fin2) ;
use resources
FreeResource3 ;
Fin2:
FreeResource2 ;
Fin1:
FreeResource1 ;
Fin0:
и макрос
CHECK (func, label) \
if (!func ())
goto label ;
no subject
Date: 2012-04-21 07:43 pm (UTC)Видел и такое:
Кстати, что забавно - встречено в MSDN :)
(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2012-04-21 07:11 pm (UTC)no subject
Date: 2012-04-21 07:28 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2012-04-21 07:39 pm (UTC)no subject
Date: 2012-04-21 07:55 pm (UTC)В частности, разработчики libjpeg укурились настолько, что у них не предусмотрен возврат в программу после возникновения ошибки открытия файла (ну а что, не тот формат и бабац). GIF'ов с расширением .jpg на просторах инета хватает.
Пришлось откровенно издеваться.
no subject
Date: 2012-04-21 07:59 pm (UTC)Особенно забавно это делать на телефонах, когда в любой момент может кончиться память. И тогда надо последовательно освобождать все ресурсы графической подсистемы на данный кадр, а затем кидать в JVM эксепшн "сотонинская жаба, убери мусор".
no subject
Date: 2012-04-21 08:27 pm (UTC)Понятно, что не есть это хороший тон, но это специфика работы. В частности, в нашем XML-языке при невозможности что-то сделать на выходе команды генерируется узел с текстом ошибки, но - опять же в "куче". Не хватило место под первый malloc() - так не хватит и на цепочку последующих. И если системе килобайта жалко, то она поставлена не то что на колени, а раком, и даже на логгер особо рассчитывать не приходится. Как бы ни было обидно.
no subject
Date: 2012-04-21 08:32 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2012-04-21 08:27 pm (UTC)Другое дело, что процесс это достаточно унылый: в силу бедности языка приходится писать много лишнего кода, и контекст восприятия рвется. Но ничего, для сельской местности сойдёт, по мне всяко лучше, чем goto cleanup :)
no subject
Date: 2012-04-21 08:27 pm (UTC)no subject
Date: 2012-04-21 08:31 pm (UTC)В особо умных исходниках принято. Такой макрос называется ASSERT :)
Но вообще, в сях мудрить не стоит - чем проще, тем лучше. Поэтому, как правило, тупо проверяется каждое возвращаемое значение каждой функции.
"А, и это - принято ли в C заниматься конкатенацией строк по поводу и без повода? А то, скажем, я привык в простых программах особо не мудрить и при необходимости складывать строки, если нет явных требований к производительности. Например, какая-нибудь дурь типа генерации строковых команд из шаблонов и параметров - тупо поскладывал строки и вернул результат. А в С придется strcat использовать, буфера какие-то объявлять, память выделять, трястись за ее удаление или же писать результат прямо в выходной файл, что вообще tight coupling."
Принято, если нет явных требований к производительности :) А вот память выделять без явной необходимости не стоит. Все буфера выделяются статически или в стеке, соответственно и освобождать ничё не требуется. Размер выделяемого буфера, естественно, выбирается эмпирически. А что вы хотели, low level programming.
no subject
Date: 2012-04-21 08:41 pm (UTC)Если проверять при этом размеры, то наверно, ничего страшного. Надо себе сломать мозг и делать универсально только там где это действительно нужно, типа "а тут нашей проге через stdin дадут 2гб строку из 20 гб файла"
no subject
Date: 2012-04-21 08:35 pm (UTC)no subject
Date: 2012-04-21 08:39 pm (UTC)Емнип, там трясуться за производительность, поэтому в линупсах даже придумали writev. Вот куда он потом дополз - вопрос более интересный.
no subject
Date: 2012-04-21 08:44 pm (UTC)(no subject)
From:(no subject)
From:no subject
Date: 2012-04-21 08:55 pm (UTC)Ну и тут делайте. setjmp/longjmp
no subject
Date: 2012-04-22 01:18 am (UTC)(no subject)
From:no subject
Date: 2012-04-21 08:57 pm (UTC)no subject
Date: 2012-04-21 09:39 pm (UTC)(no subject)
From:(no subject)
From:no subject
Date: 2012-04-21 09:36 pm (UTC)no subject
Date: 2012-04-22 07:04 am (UTC)(no subject)
From:no subject
Date: 2012-04-21 10:24 pm (UTC)no subject
Date: 2012-04-21 10:40 pm (UTC)Да и вывести "can't lock file "c:\beetles and frogs.txt", function CrazyClownFunction" assert не даст. А вот юзеры конечной программы вылетающее окошко "Assertion failed, program matzo.exe line 1488" дико ненавидят.
no subject
Date: 2012-04-21 11:52 pm (UTC)http://habrahabr.ru/post/141507/
no subject
Date: 2012-04-22 01:24 am (UTC)no subject
Date: 2012-04-22 12:52 am (UTC)Ну, если лезть обратно на пальму, то
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.
no subject
Date: 2012-04-22 07:07 am (UTC)Впрочем, у меня как раз наверно и на С++ можно было бы все сделать и было бы проще.
(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2012-04-22 01:20 am (UTC)1) Переименовать исходники в *.cpp
2) use raii
3) PROFIT!!!
С конкатенацией строк, говорят, тоже помогает.
no subject
Date: 2012-04-22 01:25 am (UTC)(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2012-04-22 01:50 am (UTC)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");
Единственный большой недостаток - с увеличением количества ресурсов растёт вложенность. Иногда помогает разбиение на более мелкие функции.
Но лучше перестать грызть себе нокти и делать так, как я написал в предыдущем комменте.
no subject
Date: 2012-04-22 07:10 am (UTC)долбо^W можно пользоваться макросом который прыгает за return success где в конце функции в обратном порядке написаны освобождалки ресурсов в нужное место.
Можно иметь и список функций очистки на функцию и вызывать их в обратном порядке. Можно иметь класс (тссс!) строки и конкатенатор к нему. И память выделяемую регионами так чтобы ни одного free в коде не было.
Посмотрите на код тех для кого вы пишете и делайте так же как они - иначе они не примут контрибуции :) Я ж не верю что это себе :D
no subject
Date: 2012-04-22 07:11 am (UTC)(no subject)
From:no subject
Date: 2012-04-22 09:03 am (UTC)no subject
Date: 2012-04-22 09:08 am (UTC)(no subject)
From:no subject
Date: 2012-04-22 11:29 am (UTC)Дешево и сердито. Плюс вариации. Можно, конечно, и exceptions замутить (типа таких (http://www.on-time.com/ddj0011.htm)), но в сборном проекте они не особенно полезны, вот если монолитная система, тогда может быть.
Насчет строк -- или, как сказали,
aprintf
, если есть, или поискать какую-нибудь более развесистую библиотеку.no subject
Date: 2012-04-22 02:39 pm (UTC)no subject
Date: 2012-04-22 05:07 pm (UTC)no subject
Date: 2012-04-22 07:53 pm (UTC)ресурсы освобождать очень просто:
return OK;
fail:
if(res1) free_res(res1);
и так по каждому;
return FAIL;
}
no subject
Date: 2012-04-22 08:11 pm (UTC)