错误处理中的宿命论

前言


本文是对本文的回应: C ++ 2a中的错误处理将发生什么 。 每个段落之后,我都痒了,伤口愈合了,开始流血。 也许我认为所写的内容太贴心了。 但是我只想对C ++程序员在21世纪所表现出的短视和文盲大声疾呼。 甚至一开始都不是。


让我们开始吧。


分类


按照惯例,程序中的所有错误情况都可以分为2个大类:
  1. 致命错误。
  2. 不是致命的或预期的错误。


我现在会发现错误。 但致命的错误-从某种意义上讲,它们也是可以预期的。 我们期望内存传输通常会导致速度下降,但可能不会导致速度下降。 这是预料之中的,不是吗? 引入分类时,始终可以检查其一致性。


但这是一个经常发生的细微错误。


让我们看一下致命错误。


除以0 。 我不知道为什么这个错误是致命的? 在这种情况下,我很想抛出一个异常并将其捕获以进行进一步处理。 她为什么致命? 为什么将自己程序的某种行为强加给我,而我却无法以任何方式影响它? C ++是否与灵活性无关,是否与语言转向程序员有关? 虽然...


空指针取消引用 。 我立即回想起Java,有一个NullPointerException可以处理。 Poco库中还有一个NullPointerException ! 那么为什么聋哑人的坚韧性的标准开发者会重复同样的口头禅呢?


通常,为什么我要开始这个话题? 这非常重要,它仅揭示了开发人员对错误处理的理解。 这本身不是关于错误处理的问题,而是重要行动的序幕。 它总是与应用程序的可靠性,容错能力有关,有时甚至与最稀有和最危险的程序类型有关,也与事务性和一致的行为有关。


在这方面,所有关于除以零和取消引用指针的争执看起来都像鸟儿争夺面包屑。 当然是一个重要的过程。 但仅从鸟类的角度来看。


让我们回到宿命论及其消失的问题……我将以一个简单的问题开始:如果我通过网络接收到不正确的数据,这是否是致命错误?


简单正确的答案:取决于。 显然,在大多数情况下,这不是致命错误,如果数据不正确,则必须验证通过网络接收的所有数据并返回4xx。 是否有需要崩溃的情况? 并以狂叫声崩溃,例如,SMS就会来。 是的,不是一个。


有。 我可以从我的学科领域给出一个示例:分布式共识算法。 一个节点接收到一个响应,该响应包含来自另一个节点的一系列更改的哈希。 而且此哈希与本地哈希不同。 这意味着出了点问题,继续进一步执行是很危险的:数据可能会分散(如果尚未分散)。 当服务的可用性不如其一致性重要时,就会发生这种情况。 在这种情况下,我们需要摔倒并咆哮,以便所有人听到。 即 我们通过网络接收了数据,他们进行了验证并失败了。 对我们来说,这个错误无处致命。 会出现此错误吗? 好吧,是的,我们编写了带有验证的代码。 相反,这是愚蠢的。 之后,只有我们不想继续该程序。 需要手动干预,自动化无法正常工作。


宿命论的选择


错误处理中最不明显的事情是确定什么是致命的,什么不是致命的。 但是每个程序员在整个开发活动中都会向自己问这个问题。 因此,它以某种方式回答了这个问题。 正确的答案出于某些原因来自实践。


但是,这只是冰山的可见部分。 在深处,还有一个更加可怕的问题。 要了解全部深度,您需要设置一个简单的任务并尝试回答它。


挑战 。 为某事建立框架。


一切都很简单。 我们建立了例如网络交互的框架。 或解析JSON。 或者,最糟糕的是XML。 问题立即出现:但是当套接字发生错误时,这是​​否是致命错误? 释义:我应该抛出异常还是返回错误? 这是否是例外情况? 还是可以返回std::optional ? 还是修女? (^ 1)


矛盾的结论是框架本身无法回答这个问题。 只有使用它的代码才知道。 这就是为什么优秀的boost.asio库同时使用两个选项的原因。 根据代码作者和应用逻辑的个人喜好,可以选择一种或另一种错误处理方法。 刚开始,我对这种方法感到有些尴尬,但是随着时间的流逝,我对这种方法的灵活性充满了兴趣。


但是,这还不是全部。 最坏的时刻来了。 在这里,我们正在编写应用程序代码,但是在我们看来,它已被应用。 对于另一个更高级别的代码,我们的代码将是库。 即 将应用程序/库(框架等)划分为纯惯例,这取决于组件的重用级别。 您总是可以将某些内容固定在顶部,而应用程序代码将不再是这样。 这立即意味着有效和无效的选择已经由使用和不使用的代码决定。


如果我们跳到一边,事实证明有时甚至无法了解谁在使用谁。 即 组件A可以使用组件B ,而组件B可以使用组件A (^ 2)。 即 谁决定将要发生的事情还不清楚。


纠缠解散


当您看到所有这些耻辱时,就会立即出现一个问题:如何忍受它? 怎么办 有什么准则可供您选择,以免被淹死?


为此,环顾四周并了解如何在其他地方解决此类问题很有用。 但是,必须明智地进行搜索:有必要将“邮票收集”与完整的解决方案区分开。


什么是集邮? 这是一个集体术语,表示我们交换了一个目标,但交换了其他东西。 例如:我们有一个目标-与亲人通话和沟通。 而且我们曾经一次,并购买了一个昂贵的玩具,因为“时尚”和“漂亮”(^ 3)。 熟悉吗? 您认为程序员不会发生这种情况吗? 不要自欺欺人。


错误处理不是目标。 每当我们谈到错误处理时,我们都会立即陷入停顿。 因为这是实现目标的一种方式。 最初的目标是使我们的软件可靠,简单且易于理解。 必须设定并始终遵循这些目标。 错误处理是胡扯,不值得讨论。 我想抛出一个例外-但为了健康! 返回了一个错误-做得好! 想要一个单子吗? 恭喜,您创造了进步的幻觉,但只在自己的头脑中(^ 4)。


然后我想写正确的方法,但是我已经写了。 伤口he愈,止血。 简而言之,提示是:


  1. 分成界限清晰的组件。
  2. 在边界上,描述其飞行方式和方式。 希望它是均匀的。 但是更重要的是。
  3. 轻松处理将使用它的代码中的错误。
  4. 如果可以在不加载用户代码的情况下进行内部处理,请不要将其突出显示。 用户需要处理的错误越少越好。
  5. 尊重您的用户,不要混蛋! 编写具有预期行为的直观界面,这样他就无需阅读评论和发誓。

第五条建议是最重要的,因为 他结合了前四个。


PS在童年时期,我总是好奇地看着蚁丘。 成千上万的蚂蚁,每个人都在做某事,爬行自己的生意。 该过程已启动。 现在我也很感兴趣地观看。 也在蚁丘后面。 成千上万的人正在做他们的小事。 祝他们辛苦工作!


^ 1:人们喜欢时髦的东西。 当每个人都玩够了时,C ++程序员醒了,然后一切都变了。


^ 2:当组件B中有多个抽象将它们连接时,可能会发生这种情况。 请参阅控制反转


^ 3:第二天,ba,屏幕崩溃了。


^ 4:我不反对单子,我反对抽烟,就像,看,这是单子,即 endofunctors的monoidal类别中的monoid! 听到掌声和赞同的点头。 在遥远的地方,几乎听不到有人高潮。

Source: https://habr.com/ru/post/zh-CN426971/


All Articles