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;
}