![]() |
1
40
异常只是高级非本地流控制构造更一般情况下的一个特定示例。其他示例包括:
(我肯定还有很多我错过的。) 这些构造的一个有趣的特性是,它们在表达能力上都大致相等:如果您有 一 你可以很容易地建立所有其他的。 因此,如何最好地实现异常取决于您可用的其他构造:
一个非常有趣的用例,两个
使用
例外情况
和
这个
实施
例外的是微软Live实验室的Volta项目。Volta的目标是通过一个按钮为Web应用程序提供架构重构。因此,您可以通过将一层Web应用程序
现在,你 能够 只需用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
这里是实现C++异常的常用方法:
它适用于Itanium体系结构,但这里描述的实现也用于其他体系结构。请注意,它是一个长文档,因为C++异常是复杂的。
下面是有关LLVM如何实现异常的良好描述:
由于LLVM是许多运行时的通用中间表示,因此所描述的机制可以应用于许多语言。 |
![]() |
3
17
在他的书中
C接口和实现:创建可重用软件的技术
,D.R.Hanson使用一组宏和
代码可以阅读 here (看except.h/except.c)。 P.S.是关于谷歌的问题。他们的员工实际上被允许在新代码中使用异常,而在旧代码中禁止使用异常的正式原因是,它已经以这种方式编写,混合样式也没有意义。 就我个人而言,我也认为没有例外的C++不是最好的主意。 |
![]() |
4
7
C/C++编译器使用底层操作系统来进行异常处理。像.NET或Java这样的框架也依赖于虚拟机上的操作系统设施。例如,在Windows中,真正的重量级提升是由结构化异常处理基础架构SEH完成的。你应该完全阅读旧的参考文章: A Crash Course on the Depths of Win32⢠Structured Exception Handling . 至于不使用异常的成本,它们是昂贵的,但与什么相比呢?与返回的错误代码相比?在考虑了正确性和代码质量的成本之后,对于商业应用程序,异常总是会赢的。除了少数非常关键的操作系统级功能外,异常总体上总是更好的。 最后,但并非最不重要的是,存在使用异常进行流控制的反模式。例外应该是例外的,而滥用流控制例外的代码将在性能上付出代价。 |
![]() |
5
6
有史以来最好的论文 实施 例外情况(引擎盖下)是 Exception Handling in CLU 作者:芭芭拉·利斯科夫和艾伦·斯奈德。每次我启动一个新的编译器时都会参考它。
对于C中实现的更高级视图,使用
|
![]() |
6
4
异常实现需要处理的关键是,一旦抛出异常,如何返回到异常处理程序。既然您已经在C++中尝试了一个任意数量的嵌套函数调用,它就必须 展开调用堆栈 正在搜索处理程序。无论如何实施,这必须引起 代码大小成本 为了执行这个操作而维护足够的信息(通常是指可以接受异常的调用的数据表)。它还意味着动态代码 执行路径将更长 而不仅仅是从函数调用返回(在大多数平台上这是一个相当便宜的操作)。根据实施情况,可能还存在其他成本。 相对成本将根据使用的语言而变化。使用的语言越高级,代码大小成本就越不重要,并且无论是否使用异常,都可以保留信息。 使用异常(通常是C++)通常出于良好原因而避免使用的应用程序是嵌入式固件。在典型的小型裸机或RTOS平台中,您可能有1MB的代码空间,或者64K,甚至更小。有些平台太小了,连C都不实用。在这种环境下,由于上述成本,尺寸影响是相关的。它还影响标准库本身。嵌入式工具链供应商通常会生成一个无例外功能的库,这会对代码大小产生巨大影响。高度优化的编译器还可以分析调用图并优化释放操作所需的调用帧信息,从而显著减少空间。异常也使得分析难以实时的需求变得更加困难。 在更典型的环境中,代码大小成本几乎肯定是不相关的,性能因素可能是关键。您是否使用它们将取决于您的性能需求以及您希望如何使用它们。在非异常情况下使用异常可以实现优雅的设计,但性能成本可能不适用于高性能系统。实现和相对成本因平台和编译器而异,因此真正了解异常是否存在问题的最佳方法是分析您自己的代码的性能。 |
![]() |
7
4
异常捕获确实有一个非常重要的成本,但在大多数情况下,这不是什么大问题。 |
![]() |
8
3
谷歌中的C++代码(对于某些Windows特定的情况保存)不要使用异常:CFR the guidelines ,简短的形式:“我们不使用C++异常”。从讨论中引用(按箭头在URL上展开):
此规则不适用于其他语言的谷歌代码,如Java和Python。 |
![]() |
9
1
关于性能-很少使用异常可能会产生微不足道的影响,但不要滥用它们。 我个人看到了Java代码,它执行了两个数量级的差(大约X100的时间),因为在一个重要的循环中使用了异常,而不是更标准的IF /返回。 |
![]() |
10
1
一些运行时间像 the Objective-C runtime 具有零成本64位异常。这意味着进入一个try块不需要花费任何费用。然而,当抛出异常时,这是非常昂贵的。这遵循“为平均情况优化”的范例——例外是指例外,因此最好在没有例外的情况下快速处理,即使是以显著较慢的例外为代价。 |