如何避免使用多个if-else来检查返回值是否为错误代码?

How to avoid using multiple if-else to check whether the returned value is an error code?

本文关键字:返回值 检查 是否 错误代码 if-else 何避免      更新时间:2023-10-16

我使用错误代码作为返回值。但是每次调用函数时,我都会使用 if-else 来检查返回值是否是一个好结果。 例如

int v1,v2,v3,v4;
v1 = func1();
if(v1 != OK){
// do somethings
}
else{
v2 = func2();
if(v2!=OK){
// do somethings
}
else{
v3 = func3();
if(v3!=OK){
// do somethings
}
else{
v4 = func4();
if(v4!=OK){
// do somethings
}
else{
.....
}
}
}
}

如果原型相同,则可以使用 loop:

using f_ptr = int (*)();
using error_f_ptr = void (*)();
std::pair<f_ptr, error_f_ptr> funcs[] = {
{&func1, &err1},
{&func2, &err2},
{&func3, &err3},
{&func4, &err4}
};
for (const auto& p : funcs) {
const int v = p.first();
if (v != OK) {
p.second();
return FAIL;
}
}
return OK;

您可以将代码包装到另一个函数中,以避免嵌套的 if-else 块。

void foo() {
int v = func1();
if (v != OK) {
// do somethings
return;
}
v = func2();
if (v != OK) {
// do somethings
return;
}
v = func3();
if (v != OK) {
// do somethings
return;
}
v = func4();
if (v != OK) {
// do somethings
return;
}
.....
}

以下是我在没有C++例外的情况下使用的一些模式。 在所有情况下,目的是在代码中具有单个返回点。 (因为单点回报通常会使代码更具可读性和可维护性 - 并非总是如此,但这是一件值得努力的好事。 此外,当您利用 RAII 时,所有解决方案都很棒(让局部变量的析构函数为您完成所有清理工作)。

经典方法是"三角形模式":

int MyCode()
{
int v1, v2, v3, v4;
int result = FAIL;
v1 = func1();
if (v1 == OK)
{
v2 = func2();
if (v2 == OK)
{
v3 = func3();
if (v3 == OK)
{
v4 = func4();
if (v4 == OK)
{
result = OK;
}
else
{
// handle func4 failure
}
}
else
{
// handle func3 failure
}
}
else
{
// handle func2 failure
}
else
{
// handle func1 failure
}
if (result != OK)
{
// general cleanup
}
}

对于上述情况,如果可以利用 RAII 或将大部分"句柄清理代码"放在末尾的// general cleanup块中,则不必在每个嵌套的else子句中编写冗余代码 - 可以完全排除 else 子句。

我喜欢使用的另一个变体是"链式成功检查":

int MyCode()
{
int result = OK;
result = func1();
if (result == OK)
{
// additional code to handle result of func1 before invoking func2
result = func2();
}
if (result == OK)
{
// additional code to handle result of func2 before invoking func3
result = func3();
}
else
{
// special error handling for func3 failing
}
if (result == OK)
{
result = func4();
}
if (result == OK)
{
// handle success case, if anything            
}
else
{
// general error handling and function cleanup goes here
}
return result;
}

乍一看很奇怪,但是当您以上述样式编写代码时,它可以让您看到预期的代码流(当成功是常态时)。 您可能会注意到,当错误情况发生时,需要对result==OK进行大量冗余检查。在发布版本中,编译器可以对此进行优化。

链式成功检查的另一种变体是使用...等着吧...不要惊慌失措...一个 goto 宏,专门用于在失败时跳到函数的末尾(人群喘息)。 但是看看它使代码看起来多么简单:

#define JUMP_ON_ERROR(expr) {result = (expr); if (result != OK) goto Cleanup;}
int MyCode()
{
int result = OK;
JUMP_ON_ERROR(func1());
JUMP_ON_ERROR(func2());
JUMP_ON_ERROR(func3());
JUMP_ON_ERROR(func4());
Cleanup:
if (result == OK)
{
// handle success
}
else
{
// handle failure
}
return result;
}

考虑使用 try catch 块并在函数中抛出异常,而不是返回错误值

int v1, v2, v3, v4;
try
{
v1 = func1();
v2 = func2();
v3 = func3();
v4 = func4();
}
catch (Func1Exception e)
{
//handle Func1Exception
}
catch (Func2Exception e)
{
//handle Func2Exception
}
catch (Func3Exception e)
{
//handle Func3Exception
}
catch (Func4Exception e)
{
//handle Func4Exception
}

看到这个和这个