LINUX.ORG.RU

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

 


0

0

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

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

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


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

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

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

Может это и тупо вызовы функций, но оно позволяет избежать дублирование кода и увеличивает читабельность в том случае что мы разбираем.

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

>К счастью, это фантазии.

это твои ужасные сны, ставшее реальностью:)

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

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

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

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

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

ну и ладно:) В Си их вообще нет, реализуються тобой и мной.

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

> Читаеться безусловно намного лучше

Угу, ведь нет богохульного слова goto. Это мое сообщение ты тоже наверно с трудом читаешь :)

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

не надо, насмотрелся уже:) Можешь вообще написать функцию обработчик которая взависимоти от статуса errno будет делать всякие гадости:)

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

щас пролистал Стивенса по диагонали... ах нет же, он тоже пишет if() { somefunc(); return -1; }

ужас какой:)

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

>Ну почитай как они реализованы в С++,
они - экцепшены?
щаз прям там "тупо вызов функций".
надо не читать, надо смотреть, как это выглядит в машинном коде.
одно только восстановление стека при прыжке хотябы в вызвавшую функцию - это полный Пи. а что будет при возврате эксепшена через несколько функций я боюсь даже представить.

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

итак, запаситесь пакетиками) песец пришел (осторожно, быдлопрога для оффтопика):

DWORD MessageReceive(HWND hw,char *box)
{
  DWORD result=0;
  if(BoxReadParams(box))
  {
    if(!params->virt)
    {
      ProgressUpdate("Подключение к %s",params->pop3);
      if(SocketConnectionable(hw,params->pop3)) if(Pop3Open(params->pop3))
      {
        ProgressUpdate("Вход",0);
        char login[1024],password[1024];
        LOGON lg={login,sizeof(login),password,sizeof(password)};
        lstrcpy(login,params->user?params->user:"");
        lstrcpy(password,params->pass?params->pass:"");
        if(Logon(hw,&lg)) if(Pop3Enter(login,password))
        {
          DWORD n=Pop3Stat();
          ProgressUpdate("Новых писем: %lu",(char *)n);
          for(DWORD i=1;i<=n;i++)
          {
            ProgressUpdate("Загрузка письма %lu",(char *)i);
            char *r=Pop3Retr(i);
            if(r&&Pop3Dele(i))
            {
              result++;
              MessageReceived(box,r);
              ProgressUpdate("Загрузка завершена",0);
            }
            else ProgressUpdate("Загрузка прервана!",0);
            HeapFree(GetProcessHeap(),0,r);
          }
        }
        else ProgressUpdate("Вход невозможен!",0);
        ProgressUpdate("Закрываем соединение",0);
        Pop3Quit();
      }
      else ProgressUpdate("Сервер не найден!",0);
    }
    BoxFreeParams();
  }
  else ProgressUpdate("Параметры %s не найдены!",box);
  return result;
}


BOOL MessageSend(HWND hw,char *box,char *cell,LPDATABASEITEM dbi,DWORD n)
{
  BOOL result=0;
  if(BoxReadParams(box))
  {
    char mail[1024];
    FileTimeToFileName(&dbi->creation,mail);
    ProgressUpdate("Загрузка письма",mail);
    DWORD sz;
    LPBYTE buf=ReadFile(box,cell,mail,&sz);
    if(buf)
    {
      ProgressUpdate("Подключение к %s",params->smtp);
      if(SocketConnectionable(hw,params->smtp)) if(SMTPOpen(params->smtp))
      {
        ProgressUpdate("Вход",0);
        if(SMTPHelo("FastMail"))
        {
          ProgressUpdate("Отправление письма на %s",dbi->to);
          if(SMTPFrom(dbi->from))
          {
            if(SMTPTo(dbi->to))
            {
              if(SMTPData(buf,sz))
              {
                result++;
                MessageSended(box,dbi,n);
                ProgressUpdate("Сообщение отправлено",0);
              }
              else ProgressUpdate("Ошибка при отправлении сообщения!",0);
            }
            else ProgressUpdate("Неизвестный адрес %s!",dbi->to);
          }
          else ProgressUpdate("Неизвестный адрес %s!",dbi->from);
        }
        else ProgressUpdate("Сервер не отвечает!",0);
        ProgressUpdate("Закрываем соединение",0);
        SMTPQuit();
      }
      else ProgressUpdate("Сервер не найден!",0);
      HeapFree(GetProcessHeap(),0,buf);
    }
    else ProgressUpdate("Письмо не найдено!",0);
    BoxFreeParams();
  }
  else ProgressUpdate("Параметры %s не найдены!",box);
  return result;
}

НО: зато никаких goto и никакого дублирования кода :)
PS: буээээ...

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

>Куча close(fd) которых можно избежать.

Можно, но для этого нужно смотреть как вызывается эта функция.Так как дал её автор - сней явно чтото не то&

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

Ты где это отрыл? :) Я себе даже представить такого не мог, жесть какаято..:)))

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

Повезло вам, а для меня это код как код. В моей практике было гораздо хуже. Когда переменные назывались a, a1, p_a1, p_a2, an1, b, p, p_n, p_p итп. Комментов минимум и те в cp1251 и были понятны одному автору. Пхпшный движок был. Если бы не знал точно что это реальный код подумал бы что обфрускация. Если найду кусок то покажу.

А вообще на dailywtf часто страхотень показывают.

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

>> процедура имеет ровно одну точку входа и ровно одну точку выхода"

> Большинство современных языков программирования мы можем выбросить на свалку? не пиши больше такую чушь

Ты такой дремучий, что это даже мило %) Для протокола: про одну точку входа и одну точку выхода - это принцип структурного программирования. Это не я сказал, а кто-то из великих. Но, конечно, ты и их научишь, как надо программировать :D

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

>Ты такой дремучий, что это даже мило %

Да, дремучий тут явно не я))

>про одну точку входа и одну точку выхода - это принцип структурного программирования.

Ты хоть понимаешь, что это значит? как догадаешься пиши, жгун:)

Ещё тебе на закуску ¨Операции, внутри блока, выполняются строго последовательно¨ - тоже про структурное программирование. Думай про гото:)

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

>Ты такой дремучий, что это даже мило %)

И таки да, этот тред показал, что у тебя аргументация типично троля, ктоторому нечего сказать.

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

Самое главное это бесконечно вложенные условия - код вообще нечитаем.

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

>> про одну точку входа и одну точку выхода - это принцип структурного программирования.

> Ты хоть понимаешь, что это значит? как догадаешься пиши, жгун:)

Это значит, что return в функции должен быть один. // К.О.

Впрочем, если у тебя есть глубоко личное и единственно правильное понимание этого тезиса, поделись %)

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

>Это значит, что return в функции должен быть один. // К.О.

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

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

Ты так и не ответил про ¨операции выполняються строго по порядку¨?

Почитай, и заметь - не слово про точки выхода:
Структу́рное программи́рование — методология разработки программного обеспечения, в основе которой лежит представление программы в виде иерархической структуры блоков. Предложена в 70-х годах XX века Э. Дейкстрой, разработана и дополнена Н. Виртом.

Любая программа представляет собой структуру, построенную из трёх типов базовых конструкций:

* последовательное исполнение — однократное выполнение операций в том порядке, в котором они записаны в тексте программы;
* ветвление — однократное выполнение одной из двух или более операций, в зависимости от выполнения некоторого заданного условия;
* цикл — многократное исполнение одной и той же операции до тех пор, пока выполняется некоторое заданное условие (условие продолжения цикла).

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

>> Это значит, что return в функции должен быть один. // К.О.

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

Вот же ты клоун :) Функция (в рамках нити) _всегда_ выходит в ровно одной точке. Но писать ее нужно так, чтобы эта точка всегда была одна (modulo оптимизации).

А параллельное программирование можно и не рассматривать.

> Ты так и не ответил про ¨операции выполняються строго по порядку¨?

Да, операции выполняются по порядку. Их порядок определяется управляющими конструкциями.

tailgunner ★★★★★
()

int allocate_resource(some_data_t *data)
{
    resource_t *res;

    res = malloc(sizeof *res);
    if(!res) {
        show_error("malloc()");
        goto out0;
    }

    res->data1 = create_resource1(data);
    if(!res->data1) {
        show_error("create_resource1()");
        goto out1;
    }

    res->data2 = create_resource2(data);
    if(!res->data2) {
        show_error("create_resource2()");
        goto out2;
    }

    res->data3 = create_resource3(data);
    if(!res->data3) {
        show_error("create_resource3()");
        goto out3;
    }
    return res;

out3:
    destroy_resource2(res->data2);
out2:
    destroy_resource1(res->data1);
out1:
    free(res);
out0:
    return NULL;
}

CL-USER
()
Ответ на: комментарий от CL-USER

Нашел прикольные макросы:

#define except_throw(x) goto x

#define except_catch(x) while (0) x:

Как имитировать блок finally?

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

>Да, операции выполняются по порядку. Их порядок определяется управляющими конструкциями.

Блин, ну и кто тут клоун? Дитё, иди учи мат часть хотябы в школу! операции выполняються в порядке написания. goto нарушает принципы структурного программирования. многократные return нет.

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

Троль необразованный, читай это до просветления:

Любая программа представляет собой структуру, построенную из трёх типов базовых конструкций:

* последовательное исполнение — однократное выполнение операций в том порядке, в котором они записаны в тексте программы; * ветвление — однократное выполнение одной из двух или более операций, в зависимости от выполнения некоторого заданного условия; * цикл — многократное исполнение одной и той же операции до тех пор, пока выполняется некоторое заданное условие (условие продолжения цикла).

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

> А можно, в образовательных целях, по пунктам что здесь не так.

Так не пишут. Этот кусок не надо было реализовать не вложенными if-ами а эксепшенами или их аналогом.

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

> Как имитировать блок finally?

Например, как сдесь: http://www.on-time.com/ddj0011.htm

Есть и более изощрённые случаи с setjmp. Полностью C в C++ не превращается, но задумки интересные. Тока я пришёл к мнению что без надобности городить огород не стоит.

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

> Этот кусок не надо было реализовать не вложенными if-ами а эксепшенами

ждем примера, как бы это выглядело

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

>Блин, ну и кто тут клоун? Дитё, иди учи мат часть хотябы в школу! операции выполняються в порядке написания. goto нарушает принципы структурного программирования. многократные return нет.

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

Кстати, вы исключения тоже ненавидите? Если нет, то у них принцип _идентичен_ операции goto, просто запись немного другая. Кстати а оператор break - эт опоследовательное выполнение команд в том виде в котором они записаны в коде?

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

кстати делая return в середине функции у вас управление передается сразу за пределы функции, а как же ваше последовательное выполнение команд в порядке их записи?

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

>читаемость кода объективно становится _лучше_?

Что значит объективно?:) На мой взгляд в стиле с return читаемость намного лучше, не нужно бугать по коду глазами - код логичен и последователен, в отличии от случаев goto. Return обозначает конец логического блока, goto - безусловный переход нипойми куда.

>вы исключения тоже ненавидите? Если нет, то у них принцип _идентичен_ операции goto

Совершенно не согласен, он идентичен как раз моему случаю - вызову функции на определённый код ошибки - чтото вроде case(error_code).

>просто запись немного другая

в случае с исключениями запись логична, и глаза по коду не разбегаються:)

>В вашем коде явно нагорожена колбаса

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

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

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

>если читаемость кода объективно становится _лучше_

Читаемость я ставлю во главу угла, с goto её нет:)

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

> с исключениями запись логична, и глаза по коду не разбегаються

ну точно. ровно такая же портянка:

try {
  // много кода
}
catch {}
catch {}
catch {}

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

> ну точно. ровно такая же портянка:
>

> try {

> // много кода

> }

> catch {}

> catch {}

> catch {}


и как вы предлагаете освобождать ресурсы в этих catch - копипастой?

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

Ещё есть http://www.ossp.org/pkg/lib/ex/. В ряде случаев крайне удобно. Но для обработки ошибок внутри функции использовать такое смысла нет.

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

> и как вы предлагаете освобождать ресурсы в этих catch - копипастой?

я не предлагаю. человек выше говорит что читабельность _много_ лучше, чем примерно такого же виде конструкция с goto. имхо он бредит.

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

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

Я тебе уже писал, что требуется от некоторого куска в приложении. Спроектируй его без любимой тобой копипасты и нелюбимого тобой goto, троллюшко. Вперёд.

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

> В C++ с освобождением ресурсов проще - можно завернуть всё в умные указатели.

ну так то для памяти только, в С тоже можно alloca использовать

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