Любые хорошие идиомы для обработки ошибок в прямых программах на C? - PullRequest
29 голосов
/ 07 мая 2010

Возвращаясь к работе на Си.

Многие из моих функций выглядят так:

int err = do_something(arg1, arg2, arg3, &result);

С намерением результат заполняется функцией, а возвращаемое значение является состоянием вызова.

Темная сторона - это что-то наивное, похожее на это:

int err = func1(...);
if (!err) {
    err = func2(...);
    if (!err) {
        err = func3(...);
    }
}
return err;

Я мог бы макрос это, я полагаю:

#define ERR(x) if (!err) { err = (x) }
int err = 0;
ERR(func1(...));
ERR(func2(...));
ERR(func3(...));
return err;

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

Очевидно, в Java, C #, C ++ есть исключения, которые очень хорошо работают для такого рода вещей.

Мне просто любопытно, что другие люди делают и как другие люди в настоящее время обрабатывают ошибки в своих программах на Си.

Ответы [ 12 ]

1 голос
/ 08 мая 2010

Что-то, что я недавно видел, это idom:

int err;
do 
{
  err = func1 (...);
  if (!err) break;

  err = func2 (...);
  if (!err) break;

  err = func3 (...);
  if (!err) break;

  /* add more calls here */

} while (0);

if (err)
{
  /* handle the error here */
  return E_ERROR; /* or something else */
}
 else 
{
  return E_SUCCESS;
}

Аргументы Pro:

Он избегает goto (для этого используется комбинация while (0) / break).Зачем тебе это делать?Он снижает цикломатическую сложность и по-прежнему будет проходить большинство проверок статического анализатора кода (кто-нибудь MISRA?).Для проектов, которые тестируются на цикломатическую сложность, это просто бог, потому что он хранит все вещи инициализации вместе.

Противоположные аргументы:

Значение конструкции цикла do / while не очевидно, потому чтоконструкция петли используется в качестве дешевой замены goto, и это можно увидеть только в конце цикла.Я уверен, что впервые эта конструкция вызовет много моментов "WTF".

По крайней мере, необходим комментарий, чтобы объяснить, почему код написан так, как требуется.

0 голосов
/ 08 ноября 2011

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

Конкретный пример: контекст десериализации. Декодирование любого элемента может завершиться неудачно, но функция может продолжиться без проверки ошибок, поскольку все функции decode_* не работают, когда запись сериализации находится в состоянии ошибки. Это вопрос удобства, возможности или оптимизации для вставки decode_has_error. В приведенном ниже примере проверка ошибок отсутствует, об этом позаботится звонящий.

void list_decode(struct serialization_record *rec,                       
                 struct list *list,                                     
                 void *(*child_decode)(struct serialization_record *)) {
    uint32_t length;                                                             
    decode_begin(rec, TAG);                                  
    decode_uint32(rec, &length);                                          
    for (uint32_t i = 0; i < length; i++) {                                
        list_append(list, child_decode(rec));
    }                                                                        
    decode_end(rec, TAG);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...