[英]Avoiding redundancy in checking the return values from C subroutines?
以下代码中有太多冗余:
int init( )
{
int err;
err = init_a();
if(err)
return -1;
err = init_b();
if(err)
return -1;
err = init_c();
if(err)
return -1;
// etc.
return 0;
}
有没有更好的方法来检查err
的返回值?
如果你真的有大量的init函数并且不怕function个指针,
#include <stddef.h>
int init_a(void), init_b(void), init_c(void);
int init(void);
static int (*const func[])(void) = {
&init_a,
&init_b,
&init_c,
// etc.
};
#define ELEMENTS(array) (sizeof(array) / sizeof(array[0]))
int init(void) {
for (size_t i = 0; i < ELEMENTS(func); ++i) {
if (func[i]() != 0) {
return -1;
}
}
return 0;
}
对于另一个 init_x(),您需要做的就是将其插入到 func[] 数组的适当索引处。 交换和删除功能也非常容易,几乎不会带来不便。 你甚至可以return 1 + i;
如果你想知道哪个 function 失败了。
现在您的 init() 已成为由 func[] 数组驱动的数据(与由 init() 中的语句驱动的代码相反)。
仅当您的 init_* 函数具有相同的原型并采用相同的 arguments(如果有)时,这才有效。
使用短路评估:
bool init( )
{
return init_a() || init_b() || init_c();
}
如果您可以接受使用 boolean 值作为成功或失败的最终指示(在我的示例中 failure = true
),那么短路运算符可以使事情变得非常紧凑。
C 语言采用短路,因此在具有多个操作数的逻辑表达式中,代码将仅“按需要”执行以推导出表达式的最终值。 因此,如果表达式A || B || C
中的A
为真A || B || C
A || B || C
则B
和C
不予评价。 如果 init_N() 函数均未返回失败,则返回值将为成功。
如果您更喜欢 success = true
with return A && B && B
,您当然可以否定逻辑(但您的代码表明 success 是false
)
另外,如果你想允许错误的原因,即错误代码,被报告给调用者,只需传递一个指向错误代码的指针,并让所有子初始化将它设置为有用的东西:
bool init(int* err)
{
return init_a(err) || init_b(err) || init_c(err);
}
如果必须按顺序调用这些初始化子过程,这在硬件/驱动软件中很常见,则需要在每个步骤后进行状态检查。 在您的示例中,返回不同的错误代码更具指示性。
int init(void) {
int err = init_a();
if (0 != err) {
return -1;
}
err = init_b();
if (0 != err) {
return -2;
}
err = init_c();
if (0 != err) {
return -3;
}
// etc.
return 0;
}
以下代码中有太多冗余:
我不知道那件事。 有一个重复的主题,我想这就是你在说的,但没有什么是多余的。 如果您正在寻找 DRYer 代码,那么可以借助预处理器宏来解决此类问题:
#define RETURN_IF_NZ(x, r) do { if (x) return (r); } while (0)
int init(void) {
RETURN_IF_NZ(init_a(), -1);
RETURN_IF_NZ(init_b(), -1);
RETURN_IF_NZ(init_c(), -1);
// ...
return 0;
}
优点包括:
缺点包括:
一些人不太重视宏的使用,因为对于大多数用途来说,函数应该是首选。 但是,这是宏可以处理而函数不能处理的情况之一。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.