C: Стиль кода для возврата ошибок?

Все мои функции выглядят так:

short Function()
{
   short ret = 0;

   ret = FunctionA();   
   if(ret != 0) return ret;

   ret = FunctionB();
   if(ret != 0) return ret;

   ret = FunctionC();
   if(ret != 0) return ret;

   return 0;
}

Есть ли лучший способ написать это? Без необходимости повторять

if(ret != 0) return ret;

все время?

8 ответов

Решение

Если используется короткозамкнутый || как предложено в другом ответе не вариант, вы можете определить макрос для этого:

#define TRY(var, x) if ((var = (x))) return var

Тогда в вашем коде:

short Function()
{
  short ret;

  TRY(ret, FunctionA());
  TRY(ret, FunctionB());
  TRY(ret, FunctionC());

  return 0;
}

ПРИМЕЧАНИЕ: вы должны быть очень осторожны при принятии решения об использовании макросов, но в этом случае я думаю, что это может быть чистым способом решения проблемы. Следует отметить, однако, что эти утверждения скрывают тот факт, что функция может возвращаться рано в каждом из них. Если у вас есть открытые дескрипторы ресурсов (файловые дескрипторы, указатели на mallocданные, ...), они будут течь. Вы и все, кто работает с кодом, должны знать об этом и использовать соответствующие процедуры обработки ошибок и очистки для более сложных случаев, чем этот.

short Function()
{
   short ret = 0;

   if(
       (ret = FunctionA()) != 0 ||
       (ret = FunctionB()) != 0 ||
       (ret = FunctionC()) != 0
     )
   {
      return ret;
   }

   return 0;
}

Я иду другим путем и показываю вам, как я на самом деле это делаю в программах, которые я пишу:

short Function() {
    short ret = 0;

    ret = FunctionA();
    if(ret != 0) {
        SomeUsefulMessageOrAssertionHere();
        return ret;
    }
    ...

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

Есть много способов переписать это, но я не могу придумать ничего более простого и легкого для глаз, чем ваш.
Вот еще один вариант, специально разработанный для минимизации количества if ... return заявления.

int i;
for (i = 0; i<3; +i) {
   switch (i) {
      case 0: ret = FunctionA(); break;
      case 1: ret = FunctionB(); break;
      case 2: ret = FunctionC(); break;
   }
   if (ret != 0) return ret;
}
return 0;

Это должно быть эквивалентно, если я не ошибся

short Function()
{
   short ret = FunctionA();   
   if(ret == 0)
   {
        ret = FunctionB();
        if(ret == 0)
        {
            ret = FunctionC();
        }

    }
   return ret;
}

Вы могли бы написать:

short ret;
if (ret = FunctionA()) return ret;
if (ret = FunctionB()) return ret;
if (ret = FunctionC()) return ret;

Хотя, возможно, вы удивите некоторых своих коллег!

Альтернатива, использующая короткое замыкание, вдохновленное постом @Tim:

short ret;
return (ret = FunctionA()) || (ret = FunctionB()) || (ret = FunctionC()) ? ret : 0;

В C++ вы можете сказать:

if (short ret = FunctionA()) return ret;
short Function() {

    short temp;

    return (temp = FunctionA()) ? temp : (temp = FunctionB()) ? temp : FunctionC();

}

Мне всегда нравились функциональные указатели.

int main()
{
    typedef ret-type (*Fptr)( ... args-types ... );

    const int N_FUNC = 3;
    Fptr functions[] = { FunctionA, FunctionB, FunctionC };

    short ret = 0;
    for( int i=0; ! ret && i < N_FUNCS; i++ )
        ret = functions[i];

    return ret;
}
Другие вопросы по тегам