实时环境中的异常是否仍然不受欢迎?

tow*_*owi 41 c++ embedded exception real-time c++11

几年前我被教过,在嵌入式系统或(非Linux-)内核开发C++实时应用程序中, 例外是不可取的.(也许那个教训来自gcc-2.95之前).但我也知道,异常处理变得更好.

那么,C++是否 -在实践中实时应用程序的上下文中的异常

  • 完全没用?
  • 甚至通过编译器开关关闭?
  • 还是非常小心的?
  • 或者现在处理得那么好,人们可以几乎自由地使用它们,并记住几件事情?
  • C++ 11会改变这个吗?

更新:异常处理是否真的需要 启用RTTI(正如一位回答者建议的那样)?是否涉及动态演员表或类似情节

Mat*_* M. 22

异常现在处理得很好,用于实现它们的策略实际上比测试返回代码更快,因为它们的成本(就速度而言)实际上是空的,只要你不抛出它.

但是它们的代价是:代码大小.异常通常与RTTI密切配合,不幸的是RTTI与任何其他C++特性不同,因为您要为整个项目激活或停用它,一旦激活它将为碰巧有虚拟方法的任何类生成补充代码因此,无视"你不为你不用心态付出代价".

此外,它确实需要补充代码来处理它.

因此,异常的成本应该不是根据速度来衡量,而是根据代码增长来衡量.

编辑:

来自@Space_C0wb0y:这篇博客文章给出了一个小概述,并介绍了两种实现异常跳转零成本的广泛方法.顾名思义,好的编译器现在使用零成本机制.

关于异常处理的维基百科文章谈到了使用的两种机制.的零成本的机制是表驱动的.

编辑:

@Vlad Lazarenko上面引用的博客中,抛出的异常可能会阻止编译器内联和优化寄存器中的代码.

  • 我知道两种典型的"设置"潜在异常的方法(大致):我认为,一个需要空间,另一个需要在运行时间.即使没有抛出异常. (2认同)
  • @VJo:表驱动方法基于程序计数器(http://en.wikipedia.org/wiki/Program_counter),虽然它在技术上是一个开销,但它已经付费,无论如何都没有例外.抛出异常时,将在表中查找计数器的值以查找适当的处理程序.因此,您不必设置任何内容(在运行时),但表确实占用空间(尽管在编译期间只读和预先计算). (2认同)

Ste*_*sop 11

回答更新:

异常处理是否真的需要启用RTTI

异常处理实际上需要比RTTI和动态强制转换更强大的功能.请考虑以下代码:

try {
    some_function_in_another_TU();
} catch (const int &i) {
} catch (const std::logic_error &e) {}
Run Code Online (Sandbox Code Playgroud)

因此,当另一个TU中的函数抛出时,它将查找堆栈(要么立即检查所有级别,要么在堆栈展开期间一次检查一个级别,这取决于实现)对于匹配对象的catch子句被抛出.

要执行此匹配,可能不需要RTTI的方面将类型存储在每个对象中,因为抛出异常的类型是throw表达式的静态类型.但它确实需要以某种instanceof方式比较类型,并且它需要在运行时执行此操作,因为some_function_in_another_TU可以从任何地方调用,并且堆栈上有任何类型的catch.dynamic_cast与之不同,它需要对没有虚拟成员函数的类型执行此运行时实例检查,对于那些不是类类型的类型.最后一部分不会增加难度,因为非类型类型没有层次结构,所以所需要的只是类型相等,但您仍然需要可以在运行时进行比较的类型标识符.

因此,如果启用异常,那么您需要RTTI的一部分进行类型比较,例如dynamic_cast类型比较但覆盖更多类型.您不一定需要RTTI的一部分来存储用于在每个类的vtable中执行此比较的数据,它可以从对象访问 - 数据只能在每个throw表达式和每个catch子句的点处进行编码.但我怀疑这是一个重要的节省,因为typeid对象不是很大,它们包含一个在符号表中经常需要的名称,加上一些实现定义的数据来描述类型层次结构.所以你可能也可能拥有所有的RTTI.


lit*_*adv 8

异常的问题不一定是速度(可能会有很大差异,具体取决于实现),但这是他们实际做的事情.

在实时世界中,当您对操作有时间限制时,您需要确切知道代码的作用.异常提供了可能影响代码整体运行时间的快捷方式(异常处理程序可能不适合实时约束,或者由于异常,您可能根本不会返回查询响应).

如果你的意思是"实时",实际上是"嵌入式",那么所提到的代码大小就成了一个问题.嵌入式代码可能不一定是实时的,但它可能具有大小约束(并且经常会).

此外,嵌入式系统通常设计为在无限事件循环中永久运行.异常可能会带你离开那个循环,并且还会破坏你的内存和数据(因为堆栈展开) - 再次,取决于你用它们做什么,以及编译器如何实际实现它.

所以比抱歉更安全:不要使用例外.如果你可以维持偶尔的系统故障,如果你在一个单独的任务中运行而不是可以轻松重启,如果你不是真正的实时,只是假装 - 那么你可能会尝试一下.如果您正在为心脏起搏器编写软件 - 我更愿意查看返回代码.

  • 我不同意"例外可能破坏你的记忆和数据".人们可以编写正确的代码,无论有没有例外 - 不同的风格.因此,我不认为"更安全,然后抱歉"是我正在寻找的答案.但关于代码大小的goot点.谢谢. (5认同)
  • "_Exception可能会带你离开那个循环,并且还会损坏你的内存和数据(因为堆栈展开)_"然后显然你没有正确使用异常.你有一个合理的论点吗? (3认同)

Cra*_*rks 5

每个实时环境都不支持C++异常,使其在任何地方都可以接受.

在视频游戏的特定示例中(每帧有16.6ms的软截止日期),领先的编译器以这样的方式实现C++异常,即只需在程序中打开异常处理就会大大减慢它的速度并增加代码大小,无论如何你是否真的抛出异常.鉴于性能和内存在游戏机上都是至关重要的,这是一个破坏者:例如,PS3的SPU单元拥有256kb的代码和数据内存!

除此之外,抛出异常仍然非常缓慢(如果你不相信我的话,可以测量它)并且可能导致堆释放,这在你还没有得到微秒的情况下也是不可取的.

我看到这个规则的一个例子就是每个应用程序运行时可能会抛出一次异常的情况- 不是每帧一次,而是一次.在这种情况下,结构化异常处理是一种可接受的方法,可以在游戏崩溃并将其转发给开发人员时从操作系统中捕获稳定性数据.