LINUX.ORG.RU

[C] [жизнь без исключений] Как вы обрабатываете ошибки?

 


0

0

Пишу на C прошивку для микроконтроллера. Возникла следующая проблема:

while (1) {
	..
	for (..) {
		..
		if (..) {
			вдруг неожиданно произошло что-то очень не хорошее, например, аппаратная ошибка несовместимая с нормальной работой программы, нужно выйти из цикла while (1)
		}
		..
	}
	..
	switch (..) {
	case ..:
		if (..) такая же ситуация...
		break;
		..
	}
	..
}
.. 
if (флаг если ошибка) {
	обработка
}
..

И как тут быть? Неужели goto, и будет мне вагон счастья? А как же «напишешь «goto» и за тобой придёт бабай! Буууу!» (c), Дейкстра и весь такой прочий антураж?


Ответ на: комментарий от Reset

С goto ресурсов понадобиться столькоже, только код будет нечитаем. Чтобы понять что у меня за ошибка я каждый раз буду лазить в конец функции.. класс:)

golodranez ★★★★
()
Ответ на: комментарий от golodranez

>> А... так ты его просто готовить не умеешь.

> Может ты не умеешь готовить нормальный алгоритм?))

Может быть. Но: 1) я не один такой 2) для того, чтобы нписать ясные goto (то есть передача управления всегда вперед, на плотно расположенные метки) не надо готовить "нормальный алгоритм".

P.S. так как насчет исключений - они нужны, или нужно готовить "нормальные алгоритмы", которые без них обойдутся?

tailgunner ★★★★★
()
Ответ на: комментарий от golodranez

>В Си всегда можно обойтись без goto
да, можно.

>И в 100% решение без goto будет лучше.

нет, не в 100%.

xydo ★★
()
Ответ на: комментарий от golodranez

>Но вообще надо сильно извратиться, чтобы у тебя было два вложенных цикла и тебе нужнобыло выходить сразу из обоих - это причина сильно задуматься над своим алгоритмом, а не использовать goto.

Что-то делаем с большим TIFF файлом и вдруг внезапно какой-то сегмент файла не замапился в ОЗУ из-за того что есть ошибка в структуре файла (он битый). Сойдет?

Absurd ★★★
()
Ответ на: комментарий от golodranez

> Чтобы понять что у меня за ошибка я каждый раз буду лазить в конец функции.. класс:)

Ааааа держите меня трое!!!11 %) Тебе надо "лазить" в конец функции, она на экран не помещается? И нормальных имен ты меткам дать не можешь? :D

tailgunner ★★★★★
()
Ответ на: комментарий от golodranez

Посмотри исходники крупных проектов. Исходники linux'а, bsd. Где там и что нечитаемо? Всё прекрасно читаемо. С goto код получается компактным и понятным. А без goto разрастается так, что тебе приходится "лазить в конец функции".

Reset ★★★★★
()
Ответ на: комментарий от tailgunner

>P.S. так как насчет исключений - они нужны, или нужно готовить "нормальные алгоритмы", которые без них обойдутся?

Мне считай что не нужны:)

>для того, чтобы нписать ясные goto (то есть передача управления всегда вперед, на плотно расположенные метки) не надо готовить "нормальный алгоритм". 

Делашь код нечитаемым.

Пример 1:

if(func1())
{
   обработка 1
}

if(func2())
{
   обработка 2
}

if(func3())
{
   обработка 3
}

if(func4())
{
   обработка 4
}



Пример 2:

if(func1())
  goto label1;
if(func2())
  goto label2;
if(func3())
  goto label3;
if(func4())
  goto label4;

label1:
{
   обработка1;
}
label2:
{
   обработка2;
}
label3:
{
   обработка3;
}
label4:
{
   обработка5;
}

Во втором варианте мы видим жопу:)

golodranez ★★★★
()
Ответ на: комментарий от golodranez

>Если в функции произошла ошибка, то безусловно при правильно проектировании будет return; break применяется для изменения поведения цикла при определённых условиях.

ужасающие вложенные fucked-циклы нам уже показали. а еще бывает, что в рамках одной функции проиходят ошибки в разных ее местах, и кроме выполнения мгновеннго return необходимо выполнить ряд действий, необходимых при любой из произошедших ошибок: очистить временно занятую функцией (ее переменными) память, закрыть сокеты, запостить в stderr отчет об ошибке, послать привет маме и т.д.

делать это в каждом if(error){} утомительно, вынести в отдельную функцию нереально (реально, но с передачей в нее кучи параметров из ошибочной функции).

>если же тебе туго без goto, то сильно задумываешься, а потом переписываешь нафиг эту часть программы

вот это я и называю чрезмерным упованием на надуманную "красоту" алгоритма.

staseg ★★★★★
()
Ответ на: комментарий от golodranez

Это вырожденный случай и в реальной жизни может встретиться только в индусокоде. В нормальном случае обработка всех ошибок в пределах одной функции делается однотипным образом.

Reset ★★★★★
()
Ответ на: комментарий от golodranez

Еще раз для тех кто в танке -- код с goto компактен и влазит на один экран, поэтому "лазить в конец функции" не нужно.

Reset ★★★★★
()
Ответ на: комментарий от Reset

>В нормальном случае обработка всех ошибок в пределах одной функции делается однотипным образом.

Вот про что я и говорю:) Нафиг в таком случае не нужен никакой goto.

golodranez ★★★★
()
Ответ на: комментарий от golodranez

> Во втором варианте мы видим жопу:)

можно еще больше извратиться :)

struct funcData data;
data.p1 = ...
...

res = func1( data ) && func2( data ) && func3( data ) && func4( data );

free( data.p1 );
...

return res;


где внутри функций будет вывод ошибки

lester ★★★★
()
Ответ на: комментарий от golodranez

Неправильно.

Правильно так:

if (func1())
    обработка1;

if (func2()) {
    обработка2;
    обработка1;
}

if (func3()) {
    обработка3;
    обработка2;
    обработка1;
}

if (func4()) {
    обработка4;
    обработка3;
    обработка2;
    обработка1;
}

против:

if (func1())
    goto err_func1;
if (func2())
    goto err_func2;
if (func3())
    goto err_func3;
if (func4())
    goto err_func4;

return ok;

err_func4:
    обработка4;
err_func3:
    обработка3;
err_func2:
    обработка2;
err_func1:
    обработка1;
    
return err;

Так где жопа?

kemm
()
Ответ на: комментарий от xydo

>бредовый код. никто, кто использует goto такое Г. не напишет. не впадай в крайности, чувак!

Топикстартер чуть выше хочет такой код:) Иначе просто функция, как в моём варианте.

golodranez ★★★★
()
Ответ на: комментарий от kemm

>Так где жопа?
плюсую твой пример! в ядре такое очень часто встречается.

xydo ★★
()
Ответ на: комментарий от golodranez

Эти варианты дадут разный результат.

Вот реальный пример:

int create_socket(const char *path, unsigned int perm, unsigned int nrlisten)
{
	struct sockaddr_un sockaddr;
	int err, fd;

	fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (fd == -1)
		goto error;

	err = fcntl(fd, F_SETFL, O_NONBLOCK);
	if (err) 
		goto error_close_sock;

	sockaddr.sun_family = AF_UNIX;
	strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);

	err = bind(fd, (struct sockaddr *)&sockaddr, SUN_LEN(&sockaddr));
	if (err) 
		goto error_close_sock;

	err = chmod(path, perm);
	if (err)
		goto error_unlink_sock;

	err = listen(fd, nrlisten);
	if (err) 
		goto error_unlink_sock;

	err = check1();
	if( err ) 
		goto error_check_fail;

	err = check2();
	if( err ) 
		goto error_check_fail;

	return fd;

error_check_fail:
	check_fail();
error_unlink_sock:
	unlink(path);
error_close_sock:
	close(fd);
error:
	return -1;
}

summatus
()
Ответ на: комментарий от golodranez

>Топикстартер чуть выше хочет такой код:)
бред. он не сказал, какой код он хочет))
goto можно использовать по-разному.
в каких-то случаях это будет неопавданно. как в твоем примере, в других случаях (как привел kemm) - наоборот.

xydo ★★
()
Ответ на: комментарий от golodranez

>Как уже написал Reset обработки в основном однотипны, а у тебя хрень какаято:)
нихрена они не однотипны.

xydo ★★
()
Ответ на: комментарий от Reset

> можно, но читабельность страдает

разве вариант kemm:

if (func1())
    goto err_func1;
if (func2())
    goto err_func2;
if (func3())
    goto err_func3;
if (func4())
    goto err_func4;

return ok;

err_func4:
    обработка4;
err_func3:
    обработка3;
err_func2:
    обработка2;
err_func1:
    обработка1;

читабельней чем

res = 
  func1( data ) && 
  func2( data ) && 
  func3( data ) && 
  func4( data );

?

lester ★★★★
()
Ответ на: комментарий от summatus

Где же это реальный пример если при открытии сокета у тебя ошибка не обрабатываеться, а просто идёт goto на return -1 ?

Теперь про весь код целиком, прошу прощения, но это нечитаемая куча [censored]. Замени сразу на нужные обработчики и будет тебе человеческий код.

golodranez ★★★★
()
Ответ на: комментарий от lester

Угу, только в func[1-4]() мы захватываем всевозможные ресурсы, которые надо освободить. Расскажи, как это сделать в твоём варианте.

Т.е. напиши, что должно идти после твоего 'res = func1() && ...;', чтобы работало ровно так, как мой вариант с goto?

kemm
()
Ответ на: комментарий от golodranez

>если при открытии сокета у тебя ошибка не обрабатываеться, а просто идёт goto на return -1 ?
присмотрись внимательней. там в середине есть return fd; :)
только не надо про то, что не наглядно. я вот сразу увидел

xydo ★★
()
Ответ на: комментарий от Reset

>Он из реальной жизни, а вот твой точно бесполезен и высосан из пальца.

Мой бесполезен в случае применения goto, в реальности в основном так и пишут.

golodranez ★★★★
()
Ответ на: комментарий от golodranez

> Замени сразу на нужные обработчики и будет тебе человеческий код.

Замени сам этот вариант на свой и запость, а мы оценим, который понятнее и безопаснее.

kemm
()
Ответ на: комментарий от golodranez

>> P.S. так как насчет исключений - они нужны, или нужно готовить "нормальные алгоритмы", которые без них обойдутся?

> Мне считай что не нужны:)

Хм. Подозреваю троллинг.

Алсо, твой торой вариант неэквивалентен первому - у тебя возврат функциями не-0 то считается нормальным завершением, то ошибкой.

tailgunner ★★★★★
()
Ответ на: комментарий от kemm

>Замени сам этот вариант на свой и запость, а мы оценим, который понятнее и безопаснее.
+1. "человеческий" код в студию)

xydo ★★
()
Ответ на: комментарий от kemm

> Т.е. напиши, что должно идти после твоего 'res = func1() && ...;', чтобы работало ровно так, как мой вариант с goto?

ну очевидно же - в func1, например, пишем data.buffer = malloc..., или data.ifile = fopen( ... ), а в конце "после моего 'res =...":

free( data.buffer );
free( data.tiff_rows );
if( fdata.ifile ) fclose( fdata.ifile );
...

lester ★★★★
()
Ответ на: комментарий от xydo

>и, да, ядро не должно обрабатывать ошибку. оно должно возращать ее код :)
точнее, должно, но в данном случае ничего более return -1 не требуется

xydo ★★
()
Ответ на: комментарий от summatus

int create_socket(const char *path, unsigned int perm, unsigned int nrlisten)
{
	struct sockaddr_un sockaddr;
	int err, fd;

	if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
	     return -1;

	if(fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
	     error_close_sock;

	sockaddr.sun_family = AF_UNIX;
	strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);

	if(bind(fd, (struct sockaddr *)&sockaddr, SUN_LEN(&sockaddr)) < 0)
             error_close_sock;

	if(chmod(path, perm) < 0)
	     error_unlink_sock;

	if(listen(fd, nrlisten) < 0)
	     error_unlink_sock;

	if(check1() < 0)
	    error_check_fail;

	if(check2() < 0)
	    error_check_fail;

	return fd;

}

golodranez ★★★★
()
Ответ на: комментарий от xydo

> а если data.tiff_rows не была выделена? (например, она выделяется в func2, а func1 сразу вернула ошибку)
> SEGFAULT


free( NULL ) - это не SEGFAULT

П.С. да - я всегда обнуляю все указатели

lester ★★★★
()
Ответ на: комментарий от golodranez

> error_close_sock;
> error_close_sock;

> error_unlink_sock;

> error_unlink_sock;

> error_check_fail;

> error_check_fail;

что это за фигня? реальный код на Си, плиз!!

xydo ★★
()
Ответ на: комментарий от golodranez

>кому надо сказать спасибо?
людям, впадающим в крайности

xydo ★★
()
Ответ на: комментарий от lester

Угу. А если там не malloc()? А если мы там с железкой работаем, и для правильной работы надо что-нибудь куда-нибудь записать в случае ошибки (и никогда больше)?

Сферические кони в вакууме такие сферические...

kemm
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.