简体   繁体   English

在检查 C 子例程的返回值时避免冗余?

[英]Avoiding redundancy in checking the return values from C subroutines?

There is so much redundancy in the following code:以下代码中有太多冗余:

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

Is there a better way of checking the err return values?有没有更好的方法来检查err的返回值?

If you really have large numbers of init functions and are not afraid of function pointers,如果你真的有大量的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;
}

For another init_x() all you need to do is insert it at the appropriate index of the func[] array.对于另一个 init_x(),您需要做的就是将其插入到 func[] 数组的适当索引处。 It's also super easy, barely an inconvenience, to swap around and delete functions.交换和删除功能也非常容易,几乎不会带来不便。 You could even return 1 + i;你甚至可以return 1 + i; if you want to know which function failed.如果你想知道哪个 function 失败了。

Now your init() has become data driven by the func[] array (as opposed to code driven by statements in init()).现在您的 init() 已成为由 func[] 数组驱动的数据(与由 init() 中的语句驱动的代码相反)。

This only works if your init_* functions have the same prototype and take the same arguments, if any.仅当您的 init_* 函数具有相同的原型并采用相同的 arguments(如果有)时,这才有效。

Use Short-circuit Evaluation :使用短路评估

bool init( )
{
    return init_a() || init_b() || init_c();
}

If you can accept using a boolean value as final indication of success or failure (in my example failure = true ), then Short-circuiting operators can make things quite compact.如果您可以接受使用 boolean 值作为成功或失败的最终指示(在我的示例中 failure = true ),那么短路运算符可以使事情变得非常紧凑。

The C language employs short-circuiting, so in a logical expression with several operands, the code will only execute "as far as is needed" to deduce the final value of the expression. C 语言采用短路,因此在具有多个操作数的逻辑表达式中,代码将仅“按需要”执行以推导出表达式的最终值。 So if A is true in the expression A || B || C因此,如果表达式A || B || C中的A为真A || B || C A || B || C then B and C will not be evaluated. A || B || CBC不予评价。 If none of the init_N() functions return failure, then the return value will be success.如果 init_N() 函数均未返回失败,则返回值将为成功。

You can of course negate the logic if you prefer success = true with return A && B && B (but your code suggests success is false )如果您更喜欢 success = true with return A && B && B ,您当然可以否定逻辑(但您的代码表明 success 是false

Also, if you want to allow the cause of the error, ie an error code, to be reported back to the caller, just pass in a pointer to an error code, and let all sub-inits set it to something useful:另外,如果你想允许错误的原因,即错误代码,被报告给调用者,只需传递一个指向错误代码的指针,并让所有子初始化将它设置为有用的东西:

bool init(int* err)
{
    return init_a(err) || init_b(err) || init_c(err);
}

If these initialization sub-processes must be called in order, which is common in hardware/driver software, status check after each step is necessary.如果必须按顺序调用这些初始化子过程,这在硬件/驱动软件中很常见,则需要在每个步骤后进行状态检查。 In your example, return a different error code is more indicative.在您的示例中,返回不同的错误代码更具指示性。

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

There is so much redundancy in the following code:以下代码中有太多冗余:

I don't know about that.我不知道那件事。 There is a repeating motif , which I guess is what you are talking about, but nothing there is redundant.有一个重复的主题,我想这就是你在说的,但没有什么是多余的。 If you are looking for DRYer code, then this sort of thing can be addressed with the help of preprocessor macros:如果您正在寻找 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;
}

Among the advantages are:优点包括:

  • No (explicit) repetition of the error-handling code没有(显式)重复错误处理代码
  • The behavior of the function is more easily followed (once the macro is understood) because the key elements are less obscured by error-handling scaffolding function 的行为更容易遵循(一旦理解了宏),因为关键元素较少被错误处理脚手架所掩盖

Among the disadvantages are:缺点包括:

  • Not as easy to debug as the original version (but easier than some alternatives that have been presented)不像原始版本那么容易调试(但比已经提出的一些替代方案更容易)
  • Humans have to know, guess, or look up the meaning of the macro人类必须知道、猜测或查找宏的含义

Macro usage is poorly regarded by some, on the basis that functions should be preferred for most purposes.一些人不太重视宏的使用,因为对于大多数用途来说,函数应该是首选。 However, this is among the cases that macros can handle, but functions can't.但是,这是宏可以处理而函数不能处理的情况之一。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM