几乎每个人都使用它们,但很多人,包括我,只是认为他们只是工作是理所当然的。
我正在寻找高品质的材料。我使用的语言是:Java,C,C#,Python,C ++,所以这些对我来说是最感兴趣的。
现在,C ++可能是一个很好的起点,因为你可以用这种语言抛出任何东西。
此外,C接近装配。如何使用纯C结构模拟异常而不使用汇编?
最后,我听到一则传言,由于速度考虑,Google员工不会对某些项目使用例外。这只是谣言吗?没有它们,如何才能实现任何实质性的目标呢?
谢谢。
答案 0 :(得分:40)
异常只是高级非本地流控制结构的更一般情况的一个具体示例。其他例子是:
GOTO
形式,在高级,高阶语言中很流行),GOTO
。(我确信我错过了很多其他人。)
这些结构的一个有趣特性是它们在表达能力上大致相同:如果你有一个,你可以很容易地构建所有其他的。
那么,如何最好地实现异常取决于您可用的其他结构:
GOTO
,因此如果必须,您可以随时回复。setjmp
/ longjmp
这基本上是MacGyver的延续(用胶带和牙签制成,不是真的,但至少会让你摆脱困境,如果你没有更好的东西可用)。 一个非常有趣的用例,例外和 实现例外的用法是Microsoft Live Lab的Volta Project。 (现已不复存在。)Volta的目标是通过按钮为Web应用程序提供架构重构。因此,您可以将您的单层Web应用程序转换为两层或三层应用程序,只需在.NET代码上添加一些[Browser]
或[DB]
属性,然后代码就会自动运行在客户端或DB。为此,显然必须将.NET代码翻译成JavaScript源代码。
现在,您可以在JavaScript中编写整个VM并运行未修改的字节码。 (基本上,将CLR从C ++移植到JavaScript。)实际上有一些项目可以执行此操作(例如HotRuby VM),但这样做既低效又与其他JavaScript代码无法互操作。
因此,他们编写了一个编译器,将CIL字节码编译为JavaScript源代码。但是,JavaScript缺少.NET所具有的某些功能(生成器,线程,两个异常模型也不是100%兼容),更重要的是它缺少编译器编写者喜欢的某些功能({{{ 1}}或continuation),可用于实现上述缺失的功能。
但是,JavaScript 确实有例外。因此,他们使用 JavaScript Exceptions 来实现 Volta Continuations ,然后他们使用 Volta Continuations 来实现 .NET Exceptions , .NET生成器甚至 .NET托管主题(!!!)
所以,回答你原来的问题:
如何在引擎下实施例外?
有了例外,具有讽刺意味!至少在这个非常具体的案例中,无论如何。
另一个很好的例子是Go邮件列表中的一些异常提议,它们使用Goroutines实现异常(类似于并发协程和CSP进程的混合)。另一个例子是Haskell,它使用Monads,惰性求值,尾调用优化和高阶函数来实现异常。一些现代CPU还支持异常的基本构建块(例如专为Azul Systems Java计算加速器设计的Vega-3 CPU)。
答案 1 :(得分:22)
以下是实现C ++异常的常用方法:
http://www.codesourcery.com/public/cxx-abi/abi-eh.html
它适用于Itanium体系结构,但此处描述的实现也用于其他体系结构。请注意,它是一个很长的文档,因为C ++异常很复杂。
以下是LLVM如何实现异常的良好描述:
http://llvm.org/docs/ExceptionHandling.html
由于LLVM是许多运行时的通用中间表示,因此所描述的机制可以应用于多种语言。
答案 2 :(得分:17)
在他的书“ C接口和实现:创建可重用软件的技术”中,D。R. Hanson使用一组宏和setjmp/longjmp
在纯C中提供了一个很好的异常实现。他提供了TRY / RAISE / EXCEPT / FINALLY宏,可以模拟C ++异常所做的一切以及更多。
可以仔细阅读代码here(查看except.h / except.c)。
P.S。关于Google的问题。他们的员工实际上被允许在新代码中使用异常,并且禁止使用旧代码的官方原因是因为它已经以这种方式编写而且混合样式没有意义。
就个人而言,我也认为没有例外的C ++不是最好的主意。
答案 3 :(得分:7)
C / C ++编译器使用底层OS工具进行异常处理。像.Net或Java这样的框架也在VM中依赖于OS工具。例如,在Windows中,真正繁重的工作是由SEH(结构化异常处理基础架构)完成的。您绝对应该阅读旧的参考文章:A Crash Course on the Depths of Win32™ Structured Exception Handling。
至于不使用例外的成本,它们是昂贵的,但与什么相比?与返回错误代码相比?在考虑正确性和代码质量的成本之后,商业应用程序的异常总是会获胜。除了几个非常关键的操作系统级别功能外,总体而言异常总是更好。
最后但并非最不重要的是,使用流程控制异常的反模式。例外应该是例外的,滥用流量控制例外的代码将为性能付出代价。
答案 4 :(得分:6)
有关异常实现的最佳论文(引言)是Barbara Liskov和Alan Snyder撰写的Exception Handling in CLU。每次我开始使用新编译器时都会提到它。
对于使用setjmp
和longjmp
的C语言实现的更高层次视图,我推荐Dave Hanson的C Interfaces and Implementations(如Eli Bendersky)。
答案 5 :(得分:4)
setjmp()
和longjmp()
。
异常捕获确实会产生非常重要的成本,但对于大多数用途而言,这并不是什么大问题。
答案 6 :(得分:4)
异常实现需要处理的关键是如何在抛出异常后返回异常处理程序。由于您可能在C ++中使用try语句后进行了任意数量的嵌套函数调用,因此它必须展开调用堆栈来搜索处理程序。无论如何实现,这必须导致代码大小成本维护足够的信息以执行此操作(并且通常意味着可以采取例外的调用的数据表)。这也意味着动态代码执行路径将比从函数调用返回更长(在大多数平台上这是一个相当便宜的操作)。根据实施情况,可能还有其他费用。
相对费用取决于使用的语言。使用的高级语言,代码大小成本越不重要,无论是否使用异常,都可以保留信息。
嵌入式固件通常是出于好的原因而经常避免使用异常(通常是C ++)的应用程序。在典型的小型裸机或RTOS平台中,您可能有1MB的代码空间,或64K,甚至更小。有些平台太小,即使C也不实用。在这种环境中,由于上述成本,尺寸影响是相关的。它也会影响标准库本身。嵌入式工具链供应商通常会生成一个无异常库的库,这会对代码大小产生巨大影响。高度优化的编译器还可以分析调用图并针对展开操作优化掉所需的调用帧信息,以显着减少空间。例外也使得分析硬实时要求变得更加困难。
在更典型的环境中,代码大小成本几乎肯定无关紧要,性能因素可能很关键。您是否使用它们取决于您的性能要求以及您希望如何使用它们。在非特殊情况下使用异常可以实现优雅的设计,但性能成本可能是高性能系统无法接受的。实现和相对成本因平台和编译器而异,因此真正了解异常是否存在问题的最佳方法是分析您自己的代码的性能。
答案 7 :(得分:3)
Google的C ++代码(除了某些特定于Windows的案例)不使用例外:cfr the guidelines,简称:“我们不使用C ++例外”。从讨论中引用(点击箭头以扩展URL):
我们反对使用例外的建议是 不是基于哲学或哲学 道德理由,但实际的。 因为我们想用我们的 谷歌和谷歌的开源项目 如果那些,那很难做到 项目使用例外,我们需要 建议不要使用Google中的例外情况 开源项目也是如此。事情 如果我们有可能会有所不同 从头开始重新做一遍。
此规则不适用于其他语言的Google代码,例如Java和Python。
答案 8 :(得分:1)
关于性能 - 稀疏使用异常可能会产生微不足道的影响,但不要滥用它们。
我个人看到Java代码执行的程度比它可能的情况差了两个数量级(大约花了x100的时间),因为在一个重要的循环中使用了异常而不是更多的标准if / returns。
答案 9 :(得分:1)
像the Objective-C runtime这样的运行时具有零成本的64位异常。这意味着输入try块不需要任何费用。但是,抛出异常时,这是非常昂贵的。这遵循“针对普通情况进行优化”的范例 - 例外意味着特殊情况,因此最好在没有异常的情况下做出更好的情况,即使它是以明显更慢的异常为代价的。