代码之家  ›  专栏  ›  技术社区  ›  Hamish Grubijan

如何在引擎盖下实施例外?

  •  62
  • Hamish Grubijan  · 技术社区  · 15 年前

    几乎每个人都使用它们,但很多人,包括我,都认为它们只是工作是理所当然的。

    我在找高质量的材料。我使用的语言是:Java、C、C、Python、C++,所以这些都是我最感兴趣的。

    现在,C++可能是一个很好的起点,因为你可以在该语言中抛出任何东西。

    此外,C接近装配。如何使用纯C构造而不使用程序集来模拟异常?

    最后,我听到一个传言,谷歌的员工不使用例外的一些项目,由于速度的考虑。这只是谣言吗?没有它们,什么实质性的事情怎么能完成?

    谢谢您。

    10 回复  |  直到 11 年前
        1
  •  40
  •   Jörg W Mittag    15 年前

    异常只是高级非本地流控制构造更一般情况下的一个特定示例。其他示例包括:

    • 通知 (异常的概括,最初来自一些旧的lisp对象系统,现在在commonlisp和ioke中实现)
    • 连续性 (更结构化的形式 GOTO ,在高级、高阶语言中很流行,
    • 协同程序 (子程序的泛化,尤其在Lua中流行)
    • 发电机 _la python(基本上是一种受限制的协程形式)
    • 纤维 (合作轻量级线程)当然还有前面提到的
    • 古托 .

    (我肯定还有很多我错过的。)

    这些构造的一个有趣的特性是,它们在表达能力上都大致相等:如果您有 你可以很容易地建立所有其他的。

    因此,如何最好地实现异常取决于您可用的其他构造:

    • 每个CPU都有 古托 因此,如果你必须这样做的话,你可以一直回到过去。
    • C有 setjmp / longjmp 这基本上是Macgyver的延续(由管道胶带和牙签组成,不是真正的东西,但如果你没有更好的东西,至少可以让你摆脱眼前的麻烦)。
    • JVM和CLI都有它们自己的异常,这意味着如果您的语言的异常语义与Java /C语言相匹配,那么您就可以在家中自由了(但是如果没有,那么您就被拧紧了)。
    • ParrotVM同时作为异常和延续。
    • Windows有自己的异常处理框架,语言实现者可以使用该框架在顶部构建自己的异常。

    一个非常有趣的用例,两个 使用 例外情况 这个 实施 例外的是微软Live实验室的Volta项目。Volta的目标是通过一个按钮为Web应用程序提供架构重构。因此,您可以通过将一层Web应用程序 [Browser] [DB] NET代码上的属性和代码将自动在客户端或数据库中运行。为了做到这一点,显然.NET代码必须转换为JavaScript源代码。

    现在,你 能够 只需用javascript编写一个完整的虚拟机并运行未修改的字节码。(基本上,将CLR从C++移植到JavaScript)。实际上有一些项目(例如HUTROBY VM),但是这是低效的,不能与其他JavaScript代码非常可互操作。

    因此,他们编写了一个编译器,将CIL字节码编译为JavaScript源代码。然而,javascript缺少.NET所具有的某些特性(生成器、线程,以及两个异常模型不完全兼容),更重要的是,它缺少编译器编写程序所具有的某些特性。 (或 古托 或者延续),可以用来实现上述缺失的特性。

    但是,javascript 有例外。所以,他们使用 javascript异常 实施 电压连续性 然后他们用 电压连续性 实施 .NET异常 , .NET生成器 甚至 .NET托管线程 (!!!!)

    所以,要回答你最初的问题:

    如何在引擎盖下实施例外?

    讽刺的是,除了例外!无论如何,至少在这个非常具体的情况下。

    另一个很好的例子是Go邮件列表中的一些异常建议,它们使用Goroutines(类似于并发协程和CSP过程的混合)实现异常。另一个例子是haskell,它使用monad、lazy评估、尾调用优化和高阶函数来实现异常。一些现代CPU还支持异常的基本构建块(例如,专门为AZUL系统Java计算加速器设计的VEGA-3 CPU)。

        2
  •  22
  •   Adam Goode    15 年前

    这里是实现C++异常的常用方法:
    http://www.codesourcery.com/public/cxx-abi/abi-eh.html

    它适用于Itanium体系结构,但这里描述的实现也用于其他体系结构。请注意,它是一个长文档,因为C++异常是复杂的。

    下面是有关LLVM如何实现异常的良好描述:
    http://llvm.org/docs/ExceptionHandling.html

    由于LLVM是许多运行时的通用中间表示,因此所描述的机制可以应用于许多语言。

        3
  •  17
  •   Eli Bendersky    15 年前

    在他的书中 C接口和实现:创建可重用软件的技术 ,D.R.Hanson使用一组宏和 setjmp/longjmp . 他提供了尝试/加法/除法/最后宏,它可以模仿几乎所有C++异常所做的和更多的事情。

    代码可以阅读 here (看except.h/except.c)。

    P.S.是关于谷歌的问题。他们的员工实际上被允许在新代码中使用异常,而在旧代码中禁止使用异常的正式原因是,它已经以这种方式编写,混合样式也没有意义。

    就我个人而言,我也认为没有例外的C++不是最好的主意。

        4
  •  7
  •   Remus Rusanu    15 年前

    C/C++编译器使用底层操作系统来进行异常处理。像.NET或Java这样的框架也依赖于虚拟机上的操作系统设施。例如,在Windows中,真正的重量级提升是由结构化异常处理基础架构SEH完成的。你应该完全阅读旧的参考文章: A Crash Course on the Depths of Win32™ Structured Exception Handling .

    至于不使用异常的成本,它们是昂贵的,但与什么相比呢?与返回的错误代码相比?在考虑了正确性和代码质量的成本之后,对于商业应用程序,异常总是会赢的。除了少数非常关键的操作系统级功能外,异常总体上总是更好的。

    最后,但并非最不重要的是,存在使用异常进行流控制的反模式。例外应该是例外的,而滥用流控制例外的代码将在性能上付出代价。

        5
  •  6
  •   Norman Ramsey    15 年前

    有史以来最好的论文 实施 例外情况(引擎盖下)是 Exception Handling in CLU 作者:芭芭拉·利斯科夫和艾伦·斯奈德。每次我启动一个新的编译器时都会参考它。

    对于C中实现的更高级视图,使用 setjmp longjmp ,我推荐戴夫·汉森的 C Interfaces and Implementations (就像伊莱·本德尔斯基)。

        6
  •  4
  •   djs    15 年前

    异常实现需要处理的关键是,一旦抛出异常,如何返回到异常处理程序。既然您已经在C++中尝试了一个任意数量的嵌套函数调用,它就必须 展开调用堆栈 正在搜索处理程序。无论如何实施,这必须引起 代码大小成本 为了执行这个操作而维护足够的信息(通常是指可以接受异常的调用的数据表)。它还意味着动态代码 执行路径将更长 而不仅仅是从函数调用返回(在大多数平台上这是一个相当便宜的操作)。根据实施情况,可能还存在其他成本。

    相对成本将根据使用的语言而变化。使用的语言越高级,代码大小成本就越不重要,并且无论是否使用异常,都可以保留信息。

    使用异常(通常是C++)通常出于良好原因而避免使用的应用程序是嵌入式固件。在典型的小型裸机或RTOS平台中,您可能有1MB的代码空间,或者64K,甚至更小。有些平台太小了,连C都不实用。在这种环境下,由于上述成本,尺寸影响是相关的。它还影响标准库本身。嵌入式工具链供应商通常会生成一个无例外功能的库,这会对代码大小产生巨大影响。高度优化的编译器还可以分析调用图并优化释放操作所需的调用帧信息,从而显著减少空间。异常也使得分析难以实时的需求变得更加困难。

    在更典型的环境中,代码大小成本几乎肯定是不相关的,性能因素可能是关键。您是否使用它们将取决于您的性能需求以及您希望如何使用它们。在非异常情况下使用异常可以实现优雅的设计,但性能成本可能不适用于高性能系统。实现和相对成本因平台和编译器而异,因此真正了解异常是否存在问题的最佳方法是分析您自己的代码的性能。

        7
  •  4
  •   AndiDog    11 年前

    setjmp() longjmp() 通常情况下。

    异常捕获确实有一个非常重要的成本,但在大多数情况下,这不是什么大问题。

        8
  •  3
  •   Alex Martelli    15 年前

    谷歌中的C++代码(对于某些Windows特定的情况保存)不要使用异常:CFR the guidelines ,简短的形式:“我们不使用C++异常”。从讨论中引用(按箭头在URL上展开):

    我们反对使用例外情况的建议是 不以哲学或 道德基础,但实际的基础。 因为我们想用我们的 Google和 如果那些 项目使用异常,我们需要 在谷歌中提出反对例外的建议 开源项目也是如此。东西 如果我们有 从头再来一遍。

    此规则不适用于其他语言的谷歌代码,如Java和Python。

        9
  •  1
  •   Oak    15 年前

    关于性能-很少使用异常可能会产生微不足道的影响,但不要滥用它们。

    我个人看到了Java代码,它执行了两个数量级的差(大约X100的时间),因为在一个重要的循环中使用了异常,而不是更标准的IF /返回。

        10
  •  1
  •   Mike    15 年前

    一些运行时间像 the Objective-C runtime 具有零成本64位异常。这意味着进入一个try块不需要花费任何费用。然而,当抛出异常时,这是非常昂贵的。这遵循“为平均情况优化”的范例——例外是指例外,因此最好在没有例外的情况下快速处理,即使是以显著较慢的例外为代价。

    推荐文章