LINUX.ORG.RU

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

 


0

0

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

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

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


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

Тебе, хоть ты здесь явно не главный претендент на правоту :)

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

Тебе нужно заворачивать все в большую структуру, писать врапперы для пары вызовов простейших функций, внутри врапперов разворачивать структуры. Функция, которая вызывает врапперы сама по себе будет читабельная, но код в общем будет нечитабелен. Поэтому уж лучше goto, к которому как я понял только религиозные предубеждения, а не технические.

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

> а ты гарантируешь, что в data.tiff_rows будет NULL?

я ж написал строкой ниже - всегда инициализирую указатели сразу при объявлении

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

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

У summatus тоже самое, я так понимаю это функции которые будут обрабатывать ошибки, разве трудно догадаться?:)

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

>Э, нет. Что такое error_.*? Рабочий код на C, пазалуста!

Блин, вы достали! Чего вы к автору оригинального кода не прикопались? Чего у него все эти функции делали?

Я просто сделал его код короче и понятней.

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

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

если б они были простыми - я б их не выносил, но речь ведь шла про вложенные циклы - а как по мне пара вложенных циклов + их тела прекрасные кандидаты на вынос в отдельную функцию и без надобности обрабатывать ошибки

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

>Я просто сделал его код короче и понятней.
нет, не сделал. реализуй функции-обработчики. а мы посмотрим!

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

> я так понимаю это функции которые будут обрабатывать ошибки

Бгг. То есть вызываем функцию обработки ошибки, и продолжаем исполнение основной функции? %)

Ну то есть против нормального использования goto - только тролли :). ЧТД.

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

Что делали функции unlink(path) и close(fd)? Вы точно на С когда-нибудь писали? Напишите код, _полностью_ аналогичный приведённому.

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

>>Я просто сделал его код короче и понятней.
>нет, не сделал. реализуй функции-обработчики. а мы посмотрим!


ты сделал макет. причем даже без соблюдения синтаксиса. хотя тебе привели реальный работающий пример.

вдруг, если б, да кабы, да во рту росли грибы...

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

>хотя тебе привели реальный работающий пример.

где? что такое check_fail()? пока тут небыло не одного аргумента за goto, только кусок нелепого кода который я привёл в подобающее состояние.

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

>Ты увеличил компактность, понизил читабельность

я увеличил читабельность, у тебя он был вообще неочём.

>и сделал код нерабочим.

он такой и был.

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

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

а подумать головой? в теле функции же и записываешь - благо на каждую функцию разные ошибки, если делать как вы говорите - получится навозная куча из if, for, goto и кода между ними

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

>их тела прекрасные кандидаты на вынос в отдельную функцию
а если в теле каждого цикла используется и изменяется десяток локальных переменных? головокружительные приключения со структурой (которую еще придется описывать специально для данных функций) обеспечены.

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

> а если в теле каждого цикла используется и изменяется десяток локальных переменных?

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

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

>где? что такое check_fail()?
внешняя функция.
в любом случает ты не произвел эквивалентное преобразование и говорить, что ты сделал этот код читабельней - чушь полнейшая!

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

Мужик, check_fail() -- это такая функция, которую нужно позвать, если check1() или check2() завершились с ошибкой. Считай, что они определены в какой-нибудь библиотеке. Так легче?

Напиши уже свой вариант кода, _полностью_ _аналогичный_ варианту с goto.

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

> в теле функции же и записываешь
> ага. с помощью goto :DDDD


goto умеет писать? :)

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

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)
        {
	   close(fd);
           return -1;
        }

	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)
        {
           close(fd);
           return -1;
        }

	if(chmod(path, perm) < 0)
	{
           unlink(path);
           close(fd);
           return -1;
        }

	if(listen(fd, nrlisten) < 0)
	{
           unlink(path);
           close(fd);
           return -1;
        }

	//тут был код автора который к Си отношения не имел.
	return fd;

}

далее мы видим, что сюда нужно добавить error message, если зделать это в оригинальном коде то получиться вообще жопа. 

Также, перед возвращением ошибки, мы обозначаем error_code и уже тогда возвращаем -1, получается чтото типо того

if(listen(fd, nrlisten) < 0)
	{
           error_code = listen_error;
           return -1;
        }

и уже в на верхнем уровне делаем

if((fd = create_socket(..)) < 0)
{
   close(fd)
   MakeErrMess(error_code);
}

что вообще упрощает код до минимума и делает его прозрачным даже для детсадовца.

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

>и говорить, что ты сделал этот код читабельней - чушь полнейшая!

сделал, не отпирайся.

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

> что вообще упрощает код до минимума и делает его прозрачным даже для детсадовца.

А так же добавляет глобальную переменную, кучу копипасты и будущих ошибок, когда кто-то неправильно скопипастит. Что и требовалось доказать.

Ужасно, другими словами.

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

return явно показывает выход из функции, а goto?


Вот пример реального кода, по некоторым названиям можно догадаться откуда код.


static int passthruCommand (struct mega_adapter_path *adapter, void *data, uint32_t len, uint8_t target, uint8_t *cdb, uint8_t cdblen)
{
    if ((adapter->type == MEGA_ADAPTER_V2) || (adapter->type == MEGA_ADAPTER_V34))
    {
	struct uioctl_t			u;
	int_mbox_t			*m = (int_mbox_t *) &u.mbox;
	mraid_passthru_t		*p = &u.pthru;

	memset (&u, 0, sizeof u);
	u.outlen = len;
	u.ui.fcs.opcode = M_RD_IOCTL_CMD;
	u.ui.fcs.adapno = MKADAP(hostMap (adapter->adapno));
	u.data = data;
	m->cmd = MBOXCMD_PASSTHRU;
	m->xferaddr = (uint32_t) p;
	p->timeout = 3;
	p->ars = 1;
	p->target = target;
	p->dataxferaddr = (uint32_t) data;
	p->dataxferlen = len;
	p->scsistatus = 239;	/* HMMM */
	memcpy (p->cdb, cdb, cdblen);
	p->cdblen = cdblen;
	if (data)
	    memset (data, 0, len);

	if (doIoctl (adapter, &u) < 0)
	{
	    megaErrno = errno;
	    return -1;
	}

	if (m->status)
	{
	    megaErrno = - (m->status);
	    return -1;
	}

	if (p->scsistatus & CHECK_CONDITION)
	{
	    megaErrno = - CHECK_CONDITION;
	    return -1;
	}
	if ((p->scsistatus & STATUS_MASK) != GOOD)
	{
	    megaErrno = - (p->scsistatus & STATUS_MASK);
	    return -1;
	}
    }
    else
    {
	struct megasas_iocpacket	u;
	struct megasas_pthru_frame	*f = (struct megasas_pthru_frame *) &u.frame;

	memset (&u, 0, sizeof u);
	u.host_no = hostMap (adapter->adapno);

	f->cmd = MFI_CMD_PD_SCSI_IO;
	f->target_id = target;
	f->cdb_len = cdblen;
	f->flags = MFI_FRAME_DIR_READ;
	memcpy (f->cdb, cdb, cdblen);

	if ((data != NULL) && (len > 0))
	{
	    u.sgl_off = ((void *) &f->sgl) - ((void *) f);
	    u.sge_count = 1;
	    u.sgl[0].iov_base = data;
	    u.sgl[0].iov_len = len;

	    f->sge_count = 1;
	    f->data_xfer_len = (u32) len;
	    f->sgl.sge32[0].phys_addr = (u32) data;
	    f->sgl.sge32[0].length = (u32) len;
	}

	if (doIoctl (adapter, &u) < 0)
	{
	    megaErrno = errno;
	    return -1;
	}

	if (f->cmd_status)
	{
	    megaErrno = - (f->cmd_status);
	    return -1;
	}

	if ((f->scsi_status & STATUS_MASK) != GOOD)
	{
	    megaErrno = - (f->scsi_status & STATUS_MASK);
	    return -1;
	}
    }

    return 0;
}

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

Да, и ещё: если такой вариант кому-то кажется читабельне, то ему стоит проверить читалку, в неё явно баги от копипасты завелись. 8))

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

>Да, и ещё: если такой вариант кому-то кажется читабельне, то ему стоит проверить читалку, в неё явно баги от копипасты завелись. 8))

К окулисту, бы^.. молодой человек:)

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

> return явно показывает выход из функции

а также return ничего "не знает" про выделенные ресурсы, учитывая, что функции ты пишешь большие, то в будущем ты можешь написать вначале функции выделение памяти, а в конце - ее освобождение, при этом не заметив return всередине

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

>В отличие от, да. Про копипасту почему скромно промолчал, практик? 8))

я ей вообще не занимаюсь:) иначебы не работал

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

>то в будущем ты можешь написать вначале функции выделение памяти, а в конце - ее освобождение, при этом не заметив return всередине

на этот случай у меня есть valgrind

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

> я ей вообще не занимаюсь:) иначебы не работал

Чукча не читатель, чукча писатель? Ты сам понапихал кучу копипасты в своём же примере (и напихал бы ещё больше, если бы не стал выкидывать куски кода для упрощения своей задачи).

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

>учитывая, что функции ты пишешь большие

если ты про функцию которую я привёл выше - это не моя, а из драйверов одного известного производителя железа:)

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

> на этот случай у меня есть valgrind

ну память - это для примера, ес-но там могут быть любые другие действия

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

valgrind есть не всегда, а пейсать надеясь на valgrind - первый признак быдлокода.

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

Я про функцию, которую ты привёл ещё выше как "правильный" вариант функции, где обработка ошибок была сделана через goto.

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

Я уже привёл пример где можно обойтись вообще без этого.

Повторюсь ещё раз - все ваши беды(в том числе и необходимость освобождать сразу кучу всего) от плохого проектирования.

На этом сегодня заканчиваю. Читайте реальный код, товарищи goto'шники:)

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

> надо быть внимательней:)

быть внимательней проще при написании кода, а не при изменении, т.к. в первом случае у тебя более полная картина в голове по данном куску кода

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

И всё же, про свой копипастинг-то есть что сказать в оправдание?

> Читайте реальный код, товарищи goto'шники:)

Разработчики ядра линукса и фряхи застрелились пред величием твоего интеллекта... 8))))))

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

> На этом сегодня заканчиваю. Читайте реальный код, товарищи goto'шники:)

бугага

Тебя уже мордой ткнули в реальный код, но ты всё не унимаешься. Или ядра linux и bsd это нереальный код?

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

Меня не тыкали в реальный код, та функция вообще чушь.

>Или ядра linux и bsd это нереальный код?

Реальный, но во многих своих частях не является образцом для подражания, мягко говоря.

Выше я привёл функцию из драйвера контроллера pci под линукс, вот так должна выглядеть обработка ошибок.

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

> Меня не тыкали в реальный код, та функция вообще чушь.

Чушь -- потому что ты не смог её переписать без использования goto и кучи копипасты? Оригинально... Хотя нет, где-то я такой подход уже видел.

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