你是要从违反合同中恢复过来吗?

Are you meant to recover from contract violations?

提问人:Lorah Attkins 提问时间:7/19/2021 最后编辑:Lorah Attkins 更新时间:7/21/2021 访问量:247

问:

有了指南支持库和像gsl_Expects这样的实用程序,C++暂时实现了合约(有计划在未来将这些东西烘焙到语言中)。使用此功能并根据您的项目设置,可能会违反合同:

  • 抛出异常或
  • 呼叫终止

我想知道恢复策略应该是什么。显然,在第二种情况下(您设置了违反合同以调用终止)中没有,但即使在第一种情况下也很难恢复;由于异常是某种内部类型(例如 这源于 ) 很难构建恢复策略:我应该捕获哪些异常,在哪个级别,是否有有用的信息可以帮助我恢复(我认为 file+line 是为了让人类读者进行事后分析)。fail_fastlogic_error

因此,鉴于所有这些,我的问题是:您是否打算从违反合同中恢复过来?如果是,如何?有没有描述如何做到这一点的来源?如果不是,将我的错误检查+处理与合同检查分开,只考虑硬错误,这是一个好主意吗?

我想第二种选择是我倾向于的,但令人讨厌的是,这些违规行为会导致我的整个程序崩溃,而不是取消单个功能。

我可以像这样构建合约承载函数:

std::optional<return_t> my_function(Arg arg)
{
  std::optional<return_t> ret;

  try {
    gsl_Expects(...); // do contract checking 
    /* rest of function */
  } catch (fail_fast& e) {
    // report contract violation
  }

  return ret;  // Exceptions from contract violations are turned into empty optional
               // Other exceptions are handled like before (locally or from the caller)
}

但感觉它违背了使用合约的目的。

C++ cpp-core-guidelines 指南-支持库

评论

1赞 Karl Knechtel 7/19/2021
我认为这取决于你为什么首先使用合同。
0赞 Lorah Attkins 7/19/2021
@KarlKnechtel 您能描述一下可以收回合同的使用情况吗?即使将它们视为例外,也会使回滚成为一场噩梦。我很想看到这种策略的一个例子。
3赞 Eljay 7/19/2021
Joe Duffy 的文章 Bugs Aren't Recoverable Errors!(错误不可恢复的错误)部分讨论了这种情况。尽管本文针对的是不同的编程语言,但它通常适用于所有编程语言。

答:

2赞 Alex Guteniev 7/19/2021 #1

可能有一些策略。

假设一个程序显示一些 UI,用户打开某个文件,并且由于该文件已损坏,加载数据违反了某些协定。

异常处理不会在还原文件结构的一致状态方面进行一些“恢复”,相反,它只会破坏与文件关联的结构,并报告错误。

这仍然不是“硬”错误。与 UI 关联的结构完好无损,程序可以继续。

当然,这可能被认为是不太好的做法。理想情况下,所有外部数据都经过验证,因此不会因不良数据而违反合同,因此违反合同是致命的。不确定你是否在编写理想的程序。我没有。

3赞 Cubic 7/19/2021 #2

你不应该从合同错误中恢复过来。合约错误是程序中的逻辑错误,换句话说,程序是错误的。没有合理的方法可以从中恢复,您能做的最好的事情就是杀死并重新启动发生故障的整个子系统,并希望这是一次罕见的故障。

您可以预期的特殊情况不应由合同处理,这就是例外机制的用途。

评论

0赞 Lorah Attkins 7/19/2021
感谢您直截了当的回答。我倾向于同样的观点。问题在于这样做的实用性。以平方根函数为例。当然,合同检查是这个数字是正数(我认为指南中给出的例子),但为什么失败会导致我的程序崩溃?square root
0赞 Cubic 7/19/2021
因为另一种选择是继续在未知的无效状态下运行程序,这更糟。我想不出一种情况是可以接受的,但崩溃却是不可接受的。
0赞 Lorah Attkins 7/19/2021
或报告错误并放弃该计算
0赞 Cubic 7/19/2021
只有当您知道错误发生的原因时,这才能起作用。当然,你会知道你能想到的情况,但你能想到的情况无论如何都应该通过明确的错误检查来处理。合约用于捕获您没有想到的错误,从理论上讲,这些错误可能是由程序的任何部分行为不端引起的。这就是我在第二段中提到的,如果你想处理程序的子系统是错误的,你需要隔离,让它崩溃,并在崩溃时重新启动它。