Я смотрю на тестирование ошибок и методы отчетности из вызовов функций, особенно при вызове нескольких функций. В качестве примера того, что я имею в виду, для простоты каждая функция возвращает bool:
success = false;
if (fnOne ())
{
if (fnTwo ())
{
if (fnThree ( ))
{
success = true;
}
else
{
cout << "fnThree failed" <<endl;
}
}
else
{
cout << "fnTwo failed" <<endl;
}
}
else
{
cout << "fnOne failed" <<endl;
}
Я обнаружил, что в приведенном выше примере (который я вижу повсюду) код быстро становится нечитаемым, особенно когда его вызывающий код становится многоэкранным по высоте.
В настоящее время мой способ справиться с этим в C ++ (включая тег ‘c’ на случай, если у кого-то есть плавная техника C), я храню bool и строку в моем объекте. Bool представляет успех / сбой, а строка представляет причину сбоя. Я вызываю функцию, и если функция не срабатывает, функция внутренне переводит объект в состояние сбоя и предоставляет основанную на строке причину. Я все еще не на 100% доволен этим методом … но это лучшее, что у меня есть. Пример того, как это выглядит:
void myobj::fnOne (void)
{
if (m_fluxCapacitorProngCount > 3)
{
setState (false, "myobj::fnOne - Flux capacitor has been breeding again");
}
}
void myobj::fnTwo (void)
{
if (m_answerToLifeUniverseAndEverything != 42)
{
setState (false, "myobj::fnTwo - Probability drive enabled?");
}
}
void myobj::setup (void)
{
// Ensure time travel is possible
if (valid())
{
fnOne ();
}
// Ensure the universe has not changed
if (valid())
{
fnTwo ();
}
// Error? show the reason
if (valid() == false)
{
cout << getStateReason () << end;
}
}
Где valid () возвращает true / false, а getStateReason () возвращает строку, предоставленную в функции, когда произошла ошибка.
Мне нравится, что это растет без необходимости вкладывать условия, для меня я нахожу это более читабельным, но я уверен, что есть проблемы …
Каков наилучший [самый чистый] способ обработки обнаружения и сообщения условий возврата нескольких функций?
Этот код должен быть понятнее, чем ваш первый вариант:
if (!fnOne ())
{
cout << "fnOne failed" <<endl;
return;
}
if (!fnTwo ())
{
cout << "fnTwo failed" <<endl;
return;
}
if (!fnThree ())
{
cout << "fnThree failed" <<endl;
return;
}
success = true;
В целом, для C ++ вы можете использовать исключения для обработки ошибок.
Если вы действительно хотите, чтобы одна функция возвращала значение, которое представляет успех / неудачу нескольких других функций (и только это — не обобщенное возвращаемое значение из каждой функции, что потребовало бы некоторого способа возврата массива / кортежа / вектора значений) Вот один из подходов:
int bigFunction()
{ int return_value = 0;
if (function1() != 0)
return_value |= (1 << 0);
if (function2() != 0)
return_value |= (1 << 1);
if (function3() != 0)
return_value |= (1 << 2);
// ....
return return_value;
}
Идея состоит в том, чтобы выделить один бит каждый в возвращаемом значении, чтобы указать успех / неудачу каждой подфункции. Если ваши подфункции имеют небольшой набор возможных возвращаемых значений, которые вы на самом деле хотите захватить, вы можете использовать более одного бита на функцию — то есть два бита позволят вам получить четыре разных значения для этого поля.
С другой стороны, что-то вроде этого означает, что вы, вероятно, либо а) пишете какой-то довольно низкоуровневый код, такой как драйвер устройства или ядро, или что-то в этом роде, либо б) возможно, существует более эффективный подход к решению проблемы.
Устранение ошибок в вашем коде (ошибок) и ошибок, возникающих из-за пользовательского ввода, само по себе является огромной темой. Техника, которую вы используете, зависит от сложности вашего кода и ожидаемой жизни кода. Стратегия обработки ошибок, которую вы использовали бы для домашнего проекта, является менее сложной, чем стратегия обработки ошибок, которую вы использовали бы для семестрового проекта, которая будет менее сложной, чем стратегия обработки ошибок, которую вы использовали бы для внутреннего проекта, который будет менее сложный, чем проект, который будет широко распространен среди клиентов.
Стратегия 1: написать сообщение об ошибке и прервать
Простейшая стратегия обработки ошибок, которую вы можете использовать в домашнем задании, — написать сообщение stdout
а затем позвоните abort()
,
void fun1(int in)
{
if (in < 0 )
{
printf("Can't work with a negative number.\n");
abort();
}
// Rest of the function.
}
Стратегия 2: установить глобальный код ошибки и вернуться
Следующий уровень обработки ошибок включает в себя обнаружение неверного ввода и обработку его без вызова abort()
, Вы можете установить глобально доступный код ошибки, чтобы указать тип ошибки. Я бы порекомендовал использовать этот подход для домашних заданий, семестровых проектов и исследовательских проектов.
void fun2(int in)
{
if (in < 0 )
{
// Indicate that "fun2" came accross a NEGATIVE_INTEGER_ERROR.
setErrorCode(NEGATIVE_INTEGER_ERROR, "fun2");
return;
}
// Rest of the function.
}
void funUser(int in)
{
// Call fun2
fun2(in);
// If fun2 had any errors, deal with it.
if (checkErrorCode())
{
return;
}
// Rest of the function.
}
Следующий уровень обработки ошибок включает в себя обнаружение неверного ввода и устранение его с помощью других опций. Вы можете вернуть код ошибки из функции. Если вы используете C ++, вы можете выбросить исключение. Оба эти варианта являются допустимыми способами работы с крупными проектами — будь они собственными силами или распределены для более широкого потребления. Они применимы к любому проекту, в котором пользовательская база находится за пределами команды разработчиков.
Стратегия 3: вернуть код ошибки из функции
int fun3(int in)
{
if (in < 0 )
{
// Indicate that "fun3" came accross a NEGATIVE_INTEGER_ERROR.
return NEGATIVE_INTEGER_ERROR;
}
// Rest of the function.
}
void funUser(int in)
{
// Call fun3
int ecode = fun3(in);
// If fun3 had any errors, deal with it.
if (ecode)
{
return;
}
// Rest of the function.
}
Стратегия 4: выбросить код ошибки из функции (C ++)
void fun4(int in)
{
if (in < 0 )
{
// Indicate that "fun4" came accross a NEGATIVE_INTEGER_ERROR.
throw NEGATIVE_INTEGER_ERROR;
}
// Rest of the function.
}
void funUser(int in)
{
// Call fun4. Be prepared to deal with the exception or let it be
// dealt with another function higher up in the call stack.
// It makes sense to catch the exception only if this function do
// something useful with it.
fun4(in);
// Rest of the function.
}
Надеюсь, что это даст вам достаточно опыта, чтобы принять подходящую стратегию обработки ошибок для вашего проекта.