LINUX.ORG.RU

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

 


0

0

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

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

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


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

>> что ж за любовь такая к return?

> хм... и чем это плохо?

А из твоего примера не очевидно? У тебя там showError в трех местах. А еще представь, что тебе надо протрассировать исполнение - для получения кода возврата тебе придется ставить 3 печати или брякпойнта.

tailgunner ★★★★★
()

Интересно, что Дийскстра на самом деле имел в виду, и не место ли ему
на одной скамье позора рядом с Виртом, у которого ясный цикл поиска

int i=0, j=0;
for(;i<m;i++)
  for(;j<n;j++)
    if( fabs(a[i][j])>epsilon ) 
      goto found;
found:

превращается в такую вот блевотину:

var 
  i:integer, j:integer; found:boolean;
begin
  i:=0; j:=0; found:=false;
  while i<m and not found do 
  begin
    while j<n and not found do 
    begin
      found:=Abs(a[i,j])>epsilon;
      j:=j+1;
    end  
    i:=i+1;
  end  

1. В случае, когда m = 1 000 000 000 и n=2, имеем в полтора раза больше проверок "not found", чем в С.

2. Это код менее "функционален". Допустим, нам требуется абы какая пара значений i,j -- это вполне реалистично, если считать массив а набором невязкок, и мы собираемся уменьшить очередную слишком большую невязку. Тогда сишный код можно очевидно распараллелить на любое количество потоков, а в паскалевском коде все итерации цикла используют и запись в found, и чтение из нее.


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

> И ты считаешь это нормально написанным фрагментом кода? O_o

давайте уточним - вы мне сказали: "напиши этот пример таким-то образом", я так и сделал, сам бы я так не писал

> ты сам-то в правильности уверен?


да

> Да, и зачем нужна переменная success?


например, чтоб потом освободить ресурсы при ошибке, но в данном случае можно и без нее конечно

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

> вы мне сказали: "напиши этот пример таким-то образом", я так и сделал

Не "этот". В моем примере сигнатуры функций были другие.

> сам бы я так не писал

Слава ТНБ.

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

> В моем примере сигнатуры функций были другие.

тогда можно так:
res = r1 = f1() >= 0 &&
...

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

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

Так у тебя 6 случаев копипасты, решение с goto намного прозрачнее.

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

> потому что там java

О, Капитан, как вас не хватало ITT... %)

tailgunner ★★★★★
()

2tailgunner
А почему нежелательны множественные точки выхода из функции? По-моему это обычная практика в джавоподобных языках, где за ресурсами смотреть надо редко.

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

> Потому что там finally

finally имхо текстуально стоит не там, где надо

RAII (например как заметили автор D и lester) не всегда приложимо напрямик, так что иногда *надо* создавать классы прямо на месте использования.

Я бы предложил так:

MyPtr<Resource*> r( allocate_resource(some,args), 
  ({ 
    free_resource($1,other,args);
    do;some;error;reporting;
    and;error;code;calculation; 
  }) );

или без гнутых расширений 

MyPtr<Resource*> r( allocate_resource(some,args), 
  ( 
    free_resource(_1,other,args),
    do,some,error,reporting,
    and,error,code,calculation
  )
);

а может вместо boost::lambda::_1 взять их из с++0х

При этом множественные return и наличие break оправданы. Хотя это конечно не С.

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

> Потому что там finally

finally имхо текстуально стоит не там, где надо

try{
  foo1;
  try{
    foo2;
    try{
      foo3;
    }catch(){
      bar3;
    }finally{
      clean3;
    }
  }catch(){
    bar2;
  }finally{
    clean2;
  }
}catch(){
  bar1;
}finally{
  clean1;
}

А вот альтернатива:

MyPtr<Resource1*> r1( foo1, clean1($1) );
if( something_bad_with_r1 ) { 
  bar1; 
  return ...; 
}
MyPtr<Resource2*> r2( foo2, clean2($1) );
if( something_bad_with_r2 ) { 
  bar2; 
  return ...; 
}
MyPtr<Resource3*> r2( foo2, clean2($1) );
if( something_bad_with_r2 ) { 
  bar3; 
  return ...; 
}

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

Профессора, а что насчет содержания переменнолй errno? она вроде бы всегда содержит коды ошибок или я не права?

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

> а что насчет содержания переменнолй errno?

Это уже давно не переменная.

> она вроде бы всегда содержит коды ошибок или я не права?

Только если ошибка произошла и только для системных вызовов.

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

> А почему нежелательны множественные точки выхода из функции?

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

Если тебя интересует мнение патриархов, поройся в литературе 30-летней давности.

> По-моему это обычная практика

До знаменитой статьи Дейкстры организация циклов с помощью goto тоже была общепринятой практикой :)

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

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

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

>Так у тебя 6 случаев копипасты, решение с goto намного прозрачнее.

Я не виноват, что эта функция написана через жопу. Но лучше так, чем лапша с goto.

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

>Многочисленные точки возврата делают код процедуры менее ясным (логическая структура нелинейна

про линейность говорит человек использующий гото:)

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

Наличие goto автоматически не делает из кода лапшу. Лапшу можно сделать и без goto. В данном случае лапши никакой нет.

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

>> Многочисленные точки возврата делают код процедуры менее ясным (логическая структура нелинейна

> про линейность говорит человек использующий гото:)

Я использую goto строго определенным образом - для эмуляции (локальных) исключений.

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

На твой взгляд. Я соглашусь, что этот код впринципе нормальный. Но можно сделать лучше:)

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

>Я использую goto строго определенным образом - для эмуляции (локальных) исключений.

Всё спор заканчиваем:) Я не использую goto вообще. Главное чтоб код был вменяемым.

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

>А мы в топе.

с трудом сдерживаюсь от obvious fix'а

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

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

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

От необходимости в exceptionах правильным проектированием имхо не уйти. А с выходом из нескольких вложеных циклов я сталкиваюсь частенько во многих классических алгоритмах. Правда я пишу на яве, там для и того и другого есть свои конструкции. Когда приходится писать на c++ goto для использую только в этом случае.

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

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

Фанатичное следование некоторым концепциям, описанным некоторыми классиками, - идиотизм. Нужно думать башкой и решать поставленные задачи: быстро и качественно. Goto можно использовать так, чтобы не нарушить понятность алгоритма. А можно зажечь красные глазки и написать без goto там, где его можно очень логично и удобно применить. Испоганить при этом код или ацки спроектировать, проморгав дедлайн.

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

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

Я выше уже 10 раз описал свою точку зрения по поводу goto. Нравится - пользуйтель. Я уже понял что убеждать вас в необходимости заранее продумывать алгоритм бесполезно.

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

>Правда я пишу на яве, там для и того и другого есть свои конструкции.

Я в яве не особо, а что там за конструкции для выхода из вложенных циклов?

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

>дедлайн

говорите уж по русски - просрочив сроки ;)

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

2tailgunner: посмотрел сырцы ядра, там что-то никто не
стремиться минимизировать кол-во возвратов:



struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb,
                                             ext4_group_t block_group,
                                             struct buffer_head ** bh)
{
        unsigned long group_desc;
        unsigned long offset;
        struct ext4_group_desc * desc;
        struct ext4_sb_info *sbi = EXT4_SB(sb);

        if (block_group >= sbi->s_groups_count) {
                ext4_error (sb, "ext4_get_group_desc",
                            "block_group >= groups_count - "
                            "block_group = %lu, groups_count = %lu",
                            block_group, sbi->s_groups_count);

                return NULL;
        }
        smp_rmb();

        group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb);
        offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1);
        if (!sbi->s_group_desc[group_desc]) {
                ext4_error (sb, "ext4_get_group_desc",
                            "Group descriptor not loaded - "
                            "block_group = %lu, group_desc = %lu, desc = %lu",
                             block_group, group_desc, offset);
                return NULL;
        }

        desc = (struct ext4_group_desc *)(
                (__u8 *)sbi->s_group_desc[group_desc]->b_data +
                offset * EXT4_DESC_SIZE(sb));
        if (bh)
                *bh = sbi->s_group_desc[group_desc];
        return desc;
}


Может наличие одного return в функции это мегакруто но я не стал бы 
переделывать этот код. К трассировке приложения кол-во ретюрнов 
отношение имеет слабое. На то она и трасировка чтобы не просто видеть
 что функция вызвалась и завершилась а ещё и почему она это сделала.

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

break/continue по метке цикла, что-то вроде

outer:
for (int i = 0; i < n; ++i) {
  for (int j = 0; j < n; ++j) {
    ...
    if (condition) {
      break outer;
    }
  }
}

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

> посмотрел сырцы ядра, там что-то никто не стремиться минимизировать кол-во возвратов:

В ядре нет единого стандарта кодирования.

> struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb,

И что? Та же ext4, inode.c:ext4_alloc_blocks.

> Может наличие одного return в функции это мегакруто но я не стал бы переделывать этот код

Я этого и не предлагал.

> К трассировке приложения кол-во ретюрнов отношение имеет слабое.

Как скажешь.

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

> И что? Та же ext4, inode.c:ext4_alloc_blocks.

А там так написано потому что с goto код лучше ложиться.

Собстно, это я всё сказал к тому что нету универсальной парадигмы программирования. Вот. Спорить бессмысленно. Это так же как утверждать что один ЯП лучше другого(кроме пых-пыха, php должен умереть :)).

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

Умереть должны тупые пыхокодеры. Умные пусть остаются.

/me не пыхокодер:)

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

Если нужен качественный код, то лишний слой абстракции не нужен. Если к дедлайну нужно успеть, тогда нужен конечно.

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

>> а исключения вообще нужны? Или без них тоже лучше?

> Если нужен качественный код, то лишний слой абстракции не нужен

Хм. А исключения, скажем, в Си++, Питоне и прочих - это лишний слой абстракции?

> Если к дедлайну нужно успеть, тогда нужен конечно.

Если это помогает успеть к дедлайну, это не может быть плохим %)

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

> Дибилушка, незнающий элементарного.

Ты уже придумал способ открыть UDS другим способом, без goto и гор твоей любимой копипасты, о Великий Проектировщик Хеллоу Ворлдов?

> Тебя gag вчера уже ткнул в твоё невежество, так что помалкивай.

Дай ссылку на свою альтернативную реальность, а то в этой всё как-то не так, как тебе мнится.

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