【问题标题】:Return error code after first detected error第一次检测到错误后返回错误代码
【发布时间】:2018-05-17 06:25:47
【问题描述】:

我有一个函数,它进行一些初始化并调用其他函数,每个函数都返回一个错误代码。在第一次检测到这样的错误后,我希望能够从此函数返回:

int error_code = FirstFunction();
if (error_code != 0) {
    return error_code;
}
error_code = SecondFunction();
if (error_code != 0) {
    return error_code;
}
// etc...

但是,这不仅看起来很麻烦,而且还有多个退货声明,而且出于合规原因,在我公司这是不允许的。

我怎样才能重新排列它,以便只有一个 return 语句,但在第一个错误代码之后仍然停止?我能想到的唯一方法是嵌套 if 语句:

int error_code = FirstFunction();
if (error_code == 0) {
    error_code = SecondFunction();
    if (error_code == 0) {
        error_code = ThirdFunction();
        // etc...
    }
}
return error_code;

但这很快就会变得不合理。有没有其他方法可以做到这一点?

编辑:在我的程序中,返回码 0 表示成功(OK),非零表示失败/错误(NOT OK)

【问题讨论】:

  • 那么1你会回来吗?
  • 0 以外的任何值。0 表示没有错误,非零表示错误
  • 发布error_code的类型。

标签: c if-statement return


【解决方案1】:

您不必嵌套所有的函数调用,下面的代码也可以完成这项工作,并且应该符合您的代码编写规则:

error_code = FirstFunction();
if (error_code == 0) {
    error_code = SecondFunction();
}
if (error_code == 0) {
    error_code = ThirdFunction();
}
// etc...
return error_code;

【讨论】:

    【解决方案2】:

    这是另一种精益方法,可以根据哪个函数失败返回不同的错误代码:

    int func(void)
    {
        int code;
        int error_code = (code = FirstFunction()) ? code :
                         (code = SecondFunction()) ? code :
                         (code = ThirdFunction()) ? code : 0;
    
        /* ... */
    
        return error_code;
    }
    

    【讨论】:

      【解决方案3】:

      精益和干净(like this one,但避免不喜欢的gotos):

      int foo(void)
      {
        int error_code;
      
        do {
          if (0 != (error_code = FirstFunction()))
          {
            break;
          }
      
          if (0 != (error_code = SecondFunction()))
          {
            break;
          }
      
          ...
      
        } while (0);
      
        return error_code;
      }
      

      顺便说一句,这遵循更常见的模式:0 可以,其他一切都不是。根据需要调整)


      你甚至可以使用宏来混淆它:

      #define RUN_AND_BREAK_ON_ERROR(rc, f, ...) \
        if (0 != (rc = f(__VA_ARGS__))) \
        { \
          break; \
        } 
      
      int foo(void)
      {
        int error_code;
      
        do {
          RUN_AND_BREAK_ON_ERROR(error_code, FirstFunction, <args go here>);
          RUN_AND_BREAK_ON_ERROR(error_code, SecondFunction, <args go here>);
          ...
      
        } while (0);
      
        return error_code;
      }
      

      【讨论】:

      • @AnttiHaapala:为什么?为什么这种事经常发生在我身上……:}
      • 可能是 1,3,7-三甲基黄嘌呤缺乏症 ;) 或者 Visual Basic 太多
      【解决方案4】:
      if( (error_code = FirstFunction()) || (error_code = SecondFunction()) || ... ){
         return error_code ; 
      }
      
      return error_code; //denoting no error.
      

      这将只返回第一个返回非零的函数。这个想法是,对于if 语句,返回nonzero 的第一个函数将短路整个评估并从返回非零error_code 的函数返回error_code。另一件事是赋值语句的值是分配的值。这就是它起作用的原因。

      更简单的方法是顺序 if-else

      if( error_code = FirstFunction() ) {}
      else if( error_code = SecondFunction() ) {}
      ...
      
      return error_code; 
      

      【讨论】:

      • 这很接近,我喜欢它在比较中捕获错误代码的方式,但 Benoit 的答案更接近我想要的。
      • 注意:2 else 不是必需的。
      • @chux.: 你是说最后一个?早些时候我打错了电话FirstFunction 两次。你是这个意思吗?
      • 不需要elses,因为当if条件失败时,return被执行;但是在第二个示例中,OP 问题的重点不是避免多个返回点吗?
      • @DavidBowling.:已编辑。实际上我已经提供了{} - 一个空白的if 也可以。
      【解决方案5】:

      如果所有这些函数都采用相同类型的参数并具有相同的返回类型,则可以将它们放在一个函数数组中并对其进行迭代。当发现错误时,它会简单地跳出循环并返回。

       int (*function_array[max_array])();
       /*Fill the array with the functions you need*/
      
       for(i=0;i<max_array;i++){
          if((error_code=function_array[i]())!=OK){
              break;
          }
      }
      
      return error_code;
      

      OK 是这些函数的成功返回值)

      【讨论】:

      • 我知道 OP 没有提到参数,但是,这种使用函数指针数组的方法仍然依赖于使用相同参数的所有函数,数量和类型相同。
      • 确实我没想到。也许它可以用另一个参数数组来解决。但在这一点上,它可能不值得。
      【解决方案6】:

      嗯,有一个用过的,例如在 Linux 内核中:

      int somefunc(whatever)
      {
          if (do_something()) {
              ret = -EINVAL;
              goto err;
          }
          if (do_something_else()) {
              ret = -EPERM;
              goto err;
          }
          /* ... */
          ret = 0;
      err:
          some_mandatory_cleanup();
          return ret;
      }
      

      但我怀疑这会更不受欢迎。 (在你尖叫之前,重点是最后的强制清理。goto 将其安排为始终执行,但仍将其排除在外。)

      真的,我认为您的第一个 sn-p 中的代码很好,问题在于您的指南。即使我们只在一处写return error_code;,也不足以保证保存在变量中的错误代码总是正确的,或者函数完成了所有可能需要的清理工作。 (考虑分配内存的东西,无论如何都必须释放它。)

      【讨论】:

      • 尽管我很想更改指南,但它们是我们客户整体项目要求的一部分,不在我的控制范围内。
      • 是的,如果我尝试使用 goto,他们会解雇我:D
      猜你喜欢
      • 2014-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-15
      • 1970-01-01
      • 2014-07-03
      • 1970-01-01
      • 2015-11-23
      相关资源
      最近更新 更多