在C中,是否有一种模式可以不再检查调用其他函数的函数中的错误?
e.g。如果函数foo()依次调用a(),b()和c(),则必须在继续之前检查每个函数的返回值。如果a(),b()或c()也调用其他函数,这些函数也可能调用其他函数,这会留下一长串错误检查,可能会出现同样的错误......
int16_t a(x_t x) {return DO_SOME_WORK(x);}
int16_t b(y_t y) {return DO_OTHER_WORK(y);}
int16_t c(z_t z) {return DO_MORE_WORk(z);}
foo (x_t x, y_t y, z_t z) {
int16_t err = 0;
// I can handle errors returned by either:
err = a(x));
if (err) return err;
err = b(y);
if (err) return err;
err = c(z);
return err;
// Or
err = a(x);
if (!err) err = b(y);
if (!err) err = c(z);
return err;
}
我更喜欢第二种方法,因为有一个明确的单一退出点,但是另一种方法对处理进行了本地化,并且任何一种方法都引入了很多ifs,并且使用大型程序,这是很多额外的代码,特别是在无效的函数中但是,例如,调用其他函数并传递它们之间的输出。 感谢
答案 0 :(得分:4)
在这种情况下,您可以利用&&
的短路评估,当左侧操作数为false
时,不评估右侧操作数。
如果您可能希望在对a()
,b()
或c()
的调用之间执行其他处理,则以下内容允许:
bool foo (x_t x, y_t y, z_t z)
{
bool err = a( x );
err = err && b( y ) ;
err = err && c( z ) ;
return err;
}
如果该函数仅由调用a()
,b()
或c()
组成,那么您可以使用更简洁:
bool foo (x_t x, y_t y, z_t z)
{
return a( x ) &&
b( y ) &&
c( z ) ;
}
解决@kkrambo关于函数返回的任何数值没有传播给调用者的观点可以解决,但作为解决方案的吸引力越来越小:
int16_t foo (x_t x, y_t y, z_t z)
{
int16_t ret ;
bool err = (ret = a( x ));
err = err && (ret = b( y )) ;
err = err && (ret = c( z )) ;
return ret ;
}
或
int16_t foo (x_t x, y_t y, z_t z)
{
uint16_t ret ;
(ret = a( x )) &&
(ret = b( y )) &&
(ret = c( z )) ;
return ret ;
}
你甚至可以做得越来越恐怖:
int16_t foo (x_t x, y_t y, z_t z)
{
int16_t err ;
if( (err = a(x)) ) {}
else if( (err = b(y)) ) {}
else if( (err = c(z)) ) {}
return err ;
}
简化以下err
布尔值:
bool foo (x_t x, y_t y, z_t z)
{
bool err = true ;
if( a(x) ) {}
else if( b(y) ) {}
else if( c(z) ) {}
else { err = false }
return err ;
}
答案 1 :(得分:3)
如果函数的每个函数调用都需要错误检查,那么每次调用该函数时都应检查错误。原因是即使它是相同的函数,输入也是不同的,并且全局(和静态局部)变量是不同的,从而改变结果。这意味着如果你想检查错误,你实际上并没有浪费计算工作。
编辑:我不知道这是否明智但你可以使用宏来清理
的语法err = a(x);
if(err) return err;
看起来像
#define error_check(f, e) \
e = f; \
if(e) return e;
所以在你的代码中你可以写
error_check(a(x),err)
我没有使用宏来实现,但这应该可行。
答案 2 :(得分:1)
由于目标是返回错误代码而不是简单的布尔值,因此OP提供的两种方法至少是合理的,如果不是更可取的话。像if (a(x) || b(x)) return true
这样的短路代码无法维持err
和a()
派生的b()
值。
归结为风格。
如果代码简单由几行组成,任何方法都足够了。但鉴于可能存在a(x)
,b(x)
和c(x)
之间交织的重要代码,建议不要使用样式2,因为它会在多行代码中分隔流。我喜欢风格3超过1,因为它更简洁,但1和1之间的差异。 3很小。
为了应对样式#2在一个位置收集错误返回的能力,考虑一个更高级别的函数包装器,它可以明确地确保所有序言和结尾活动的发生。
最佳选择:遵循团队风格指南,否则最适合您维护,而不是写作。
int16_t foo (x_t x, y_t y, z_t z) { // added matching return type
int16_t err = 0;
// OP style 1
err = a(x));
if (err) return err;
err = b(y);
if (err) return err;
err = c(z);
return err;
// OP style 2
err = a(x);
if (!err) err = b(y);
if (!err) err = c(z);
return err;
// style 3
// via enum or define, create an OK symbol
if ((err = a(x)) != OK) return err;
if ((err = b(x)) != OK) return err;
if ((err = c(x)) != OK) return err;
//
}