【问题标题】:Short circuit error handling in CC中的短路错误处理
【发布时间】:2010-10-19 03:03:46
【问题描述】:

我想知道是否有更好的方法来处理 C 语言中的情况,即当您在一系列表达式中遇到错误时想要退出函数。 (在这种情况下,它是一个在错误时返回 NULL 的函数)

例如在一些 C 代码中,他们试图通过将一系列语句与 AND (&&) 组合来缩短错误处理。

return doSomething() && 
     doSomething2() && 
     doSomething3() && ... ;

这让我很恼火,因为我们在一个语句中塞进了太多一行。但我想另一种选择是

if (!(value = doSomething()))
    return NULL;
if (!(value = doSomething2()))
    return NULL;
etc
return value;

但是我在 perl 和 bash 脚本中看到的短路错误评估呢..

int die(int retvalue) {
    exit(retvalue);
}
.....
(value = doSomething()) || die(1);
(value = doSomething2()) || die(2);
(value = doSomething3()) || die(3);
return value;

这样做的主要问题是 RHS 必须是一个表达式,因此您不能真正从函数中转到或返回。有人会觉得这很有价值还是太有限了?

编辑:我想我应该打算在第一个示例中包含换行符。问题是,如果您决定在中间添加另一个表达式,则需要小心。

【问题讨论】:

    标签: c error-handling short-circuiting


    【解决方案1】:

    我以前见过这个:

    int Function (void)
    {
        int Result = DoSomething();
    
        if (Result) Result = DoSomething2();
        if (Result) Result = DoSomething3();
        if (Result) Result = DoSeomthing4();
    
        return Result;
    }
    

    它看起来很整洁,很容易对齐。它不会立即退出,但不会执行任何其他操作。这当然假设函数在成功时返回非零值。否则,如果函数在失败时返回非零值,您可以简单地使用 if (!Result)

    【讨论】:

      【解决方案2】:

      根据我的经验,用 C 语言编写它的惯用方式是使用一系列 if 语句来启动函数,这些语句检查函数其余部分的先决条件。如果不满足先决条件,则立即返回错误代码。这样,当你到达函数体的主要部分时,你就知道一切正常,并且可以使代码尽可能简单。

      换句话说,你的第二种方法。

      例如,可以编写一个函数来复制字符串,如下所示:

      int copy_string(char *source, char *target, size_t max)
      {
          if (source == NULL)
              return -1;
          if (target == NULL)
              return -1;
          source_len = strlen(source);
          if (source_len + 1 > max)   // +1 for NUL
              return -1;
          memcpy(target, source, source_len + 1);
          return 0;
      }
      

      (我喜欢 Unix 系统调用约定为错误返回 -1,但这是一个有争议的风格问题,与这个问题无关。)

      【讨论】:

        【解决方案3】:

        这样的事情怎么样?

        int result;
        
        result = doSomething();
        
        result = result && doSomething2();
        
        result = result && doSomething3();
        
        return result;
        

        这使用短路的方式与您的第一个示例类似,但它允许您将其分成多行并添加 cmets 等。

        【讨论】:

        • 取得结果 &&= doSomething2()
        • 我愿意,但 C 语言中没有 "&&=" 运算符。
        【解决方案4】:

        如果您唯一担心的是在一条线上塞得太多,为什么不直接使用:

        return doSomething()
            && doSomething2()
            && doSomething3()
            && ... ;
        

        我倾向于把第二种情况写成:

        if (!(value = doSomething()))  return NULL;
        if (!(value = doSomething2())) return NULL;
        : : : : :
        return value;
        

        (甚至排列返回以使其可读)因为我喜欢在屏幕上看到尽可能多的代码(并且您可以根据需要在行之间插入其他检查)。

        【讨论】:

        • 这有助于提高可读性,但无助于“一行”超载这一事实。
        • @devinb,我不知道为什么这个事实实际上是一个问题。您需要能够对源代码做两件事(a)阅读和理解它,(b)编译它。编译器对任何一个版本都没有问题,所以我认为这很烦人,因为它很难阅读。重新格式化可以解决这个问题。
        • 只要将解决方案编译成相同的功能和性能,它们都是相同的。有些,例如 goto 类型的解决方案可能被认为可读性较差(不是我,因为我认为它像一台计算机:-)。
        • 我同意 goto 解决方案并不完美。但是,将函数调用与 return 语句分开是有好处的,那就是错误处理。无论是 GOTO(不是每个人的一杯茶)还是通过断言、退出、try_catch(可以在 C 中完成)或其他方式。
        • 当您将所有这些函数调用(每个都有可能的副作用)堆积到一个语句中时,您将限制处理程序正确评估问题所在的能力和灵活性。为什么这个这么重要?因为对于 99.9% 的编码时间,该程序将无法运行。
        【解决方案5】:

        我已经看到在 C 中使用 GOTO 来达到这个目的。

        因为 C 中没有“finally”构造,所以即使您提前退出函数,也需要一种方法来释放所有内存。

        所以本质上如下 (如果我错了,有人可以纠正我的 C 语法吗,我有点生疏了)

        int foo()
        {
            /* Do Stuff */
            if(!DoSomething())
                GOTO Failure;
        
            if(!DoSomething2())
                GOTO Failure;
        
            if(!DoSomething3())
                GOTO Failure;
        
            /* return success */
            return true; 
        
            Failure:
            /* release allocated resources */
            /* print to log if necessary */
            return false;
        }
        

        重要提示 不要将 GOTO 用于执行流程。它们应该只在错误时使用,并且只用于当前函数的末尾。如果您将它们用于其他任何事情,您正在创建可能会破坏现实结构的意大利面条式代码。只是不要这样做。

        编辑

        正如其中一位发帖人所指出的,使用 Exit(x) 将杀死您的整个程序,从而使该解决方案保留用于致命错误。但是,您最初提出的解决方案 (DS() && DS2() && DS3()) 全部放在一行上,这会给错误处理带来问题。

        如果您想将函数包装在某种特定于函数的错误处理中,那么当您将函数调用全部包装在一行中时,就没有办法做到这一点。所以,至少你可以做类似的事情

        int result1 = 0;
        int result2 = 0;
        int result3 = 0;
        
        result1 = DoSomething();
        
        if(result1)
            result2 = DoSomething2();
        
        if(result2)
            result3 = DoSomething3();
        
        return result1 && result2 && result3;
        

        因为这种方法不会排除错误处理。

        【讨论】:

        • 我不知道为什么。你的回答很有道理。
        • 请注意,@devinb,您的编辑完成了所有三件事,这与问题中的短路版本(或您的原始版本)不同。如果没有副作用(或性能不受影响)很好,但不能保证。
        【解决方案6】:

        我建议您反对您提出的技术。首先,C 中的 exit 终止进程;它不只是返回。所以它仅限于少数致命错误的情况。

        在我看来,您试图避免的第一个解决方案是最好和最容易理解的。

        【讨论】:

          猜你喜欢
          • 2023-03-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-06
          相关资源
          最近更新 更多