【问题标题】:What happens if exception gets thrown "through" c code? [duplicate]如果“通过”c 代码抛出异常会发生什么? [复制]
【发布时间】:2011-09-19 14:07:46
【问题描述】:

可能重复:
Will C++ exceptions safely propagate through C code?

如果你有 c 代码,例如 png 库,你自己的 io 处理程序是用 c++ 编写的,并且由于某些 io 错误而引发异常。可以让它通过c代码并在c代码之外捕获它吗?我知道必须注意内存泄漏,但通常所有结构都会预先分配。

【问题讨论】:

  • 如果你想使用 C 库来调用你的 C++ 回调,你会在其中抛出异常并确保在自己的代码中捕获它,那么你可以在 GCC 中使用@987654323 启用异常处理@ 编译你的 C 库时,它应该可以工作。
  • 我认为这会引发未定义的行为。至少 C 代码不是异常安全的,所以它会左右泄漏资源。另请参阅此答案:stackoverflow.com/questions/6253574/…
  • +1 是我见过的唯一一个可以合法使用 [c] 和 [c++] 标签的问题。
  • @sbi:当然,如果发生异常,您将对 C 代码的错误承担全部责任。我想这只是用于奇怪的场景,你真的想为其他目的编写自己的 C 库,但又想通过它传递 C++ 函数,一切都在你的控制之下......
  • @Kerrek:但它不在你的控制之下。如果 C++ 异常突然出现,您将无法在 C 代码中自行清理。在 C 语言中你缺乏这样做的工具。这就是为什么对这些问题只有一个合理的答案:不要让 C++ 异常通过其他语言的代码,无论是 C 语言还是其他任何语言。

标签: c++ c exception


【解决方案1】:

这完全取决于编译器是否可行。显然,没有任何语言标准可以说明其他语言应该做什么。

在最好的情况下,异常将传递 C 代码并返回到下一个 C++ 级别,同时可能泄漏任何动态分配的 C 结构。在不太好的情况下,它会崩溃和燃烧!

当然,一种可能性是将 C 代码编译为 C++,同时检查它是否是异常中性的。

【讨论】:

  • 语言标准不能说其他语言应该做什么,但他们可以实现应该做什么,这包括语言- 不可知的运行时 ABI。至少,该标准可以做出这种实现定义的行为。
【解决方案2】:

您的问题的答案是实现定义的。

对于 GCC,正如@Kerrek 所说,使用-fexceptions 编译 C 代码将确保在通过 C 代码抛出异常时运行时保持一致。 (请注意,现代异常机制仅通过 setjmp/longjmp 实现;它们实际上逐帧展开堆栈,以便正确处理析构函数和 try/catch 块。)

但是,C 代码本身可能不会期望通过它引发异常。很多C代码都是这样写的:

acquire a resource
do some stuff
release resource

这里的“获取资源”可能表示mallocpthread_mutex_lockfopen。如果您的 C++ 代码是“做一些事情”的一部分,并且它会引发异常,那么……哎呀。

资源泄漏不是唯一的问题;正确性也是一般的。想象一下:

subtract $100 from savings account
add $100 to checking account

现在假设在第一步完成后但在第二步完成之前抛出异常。

简而言之,尽管您的实现可能提供了一种让您通过 C 代码抛出异常的方法,但这是一个坏主意,除非您在编写 C 代码时也知道确切的可能抛出异常的位置。

【讨论】:

  • 有点晚了,但是你的银行例子不是很好,因为你不想处理这样的事情。你想使用事务,所以你总是可以回滚状态。这意味着 subtract 什么都不是,而是一个临时状态,因此不是真正的问题,因为您可以回滚减法。现在就是将您的示例应用到现实生活中的时候。
  • 非常感谢。这就是解决“你能通过一个用 C 编写的共享库从你的主程序中抛出一个异常,然后在你的主程序中再次捕获它吗?”这个问题的答案。 - 是的,你可以!
【解决方案3】:

如果您的 C++ 代码符合以下条件,则可以:

  1. 编译时启用了异常。
  2. 异常安全。

如果满足以下条件,您的 C 或 C++ 代码也可以:

  1. 在抛出异常和捕获异常之间不需要显式收集资源。

使用 C++ 异常支持编译的代码添加了特殊的钩子,以便在抛出异常但未在该范围内捕获时进行清理。在堆栈上分配的 C++ 对象将调用其析构函数。

在抛出异常时,在 C(或不支持异常的 C++)中回收的唯一资源是在每个堆栈帧中分配的空间。

GCC 手册的This section 很有帮助:

 -f异常
           启用异常处理。生成所需的额外代码
           传播异常。对于某些目标,这意味着 GCC 将
           为所有函数生成帧展开信息,这些信息可以
           产生显着的数据大小开销,尽管它不影响
           执行。如果不指定此选项,GCC 将启用它
           默认情况下,对于通常需要异常的 C++ 等语言
           处理,并为 C 之类的语言禁用它,这些语言通常不会
           需要它。但是,您可能需要在以下情况下启用此选项
           编译需要与异常正确互操作的 C 代码
           用 C++ 编写的处理程序。您可能还希望禁用此选项
           如果您正在编译不使用异常的旧 C++ 程序
           处理。

【讨论】:

  • 如果您的代码在返回之前在 C++ 代码的最后一点调用 RAII 类的析构函数中的资源分配函数,则您的 C 代码可以保存资源。这就是 libjpeg API 所做的。当运行时发现异常会被C++栈帧展开,当最后一个展开时调用错误回调,释放所有C资源并重置状态,然后异常越过登陆C++的C代码继续展开.一切随心所欲。
【解决方案4】:

通常不会真正“通过”代码抛出异常;正如动作所暗示的那样,它们被“抛出”代码。

例如;至少老式的 Objective-C 异常通常是用 setjmp 和 longjmp 实现的;本质上是存储代码的地址以供以后使用。

用类似的机制实现 C++ 异常是有意义的;因此;抛出异常的代码类型“通过”,或者更准确地说,“结束”无关紧要,如果有的话。甚至可以想象这样一种设置,即通过 C++ 捕获引发 Objective-C 异常,反之亦然。

编辑:正如 Bo Persson 提到的那样;这并不是说中断 C 代码的 C++ 异常不会造成破坏和泄漏;但是抛出异常通常是一件坏事™;所以这不太重要。

PS: 非常感谢您提出一个同时使用 C 和 C++ 标记的问题。 ;)

【讨论】:

  • 或者有人可能会说(尤其是在这个问题的上下文中)异常是“抛出”代码。 8v)
【解决方案5】:

C 对异常一无所知,因此无法说出会发生什么。

要编写符合要求的代码,您需要在返回 C 库之前捕获异常,转换为错误代码,然后将 C 库错误转换回另一端的异常。

【讨论】:

  • “不知道”并不是这种行为的真正原因。
  • @Matt:但这是无法说出会发生什么的原因。
  • 我记得 GCC ABI 定义了 C 头上的异常,尽管我再也找不到那个细节的参考,我发现这个“期望与 C++ 互操作的 C 语言代码应该是使用 -fexceptions 编译。这将使调试作为 C++ 引发的堆栈展开的一部分调用的 C 语言函数成为可能。特别是,展开到没有异常处理数据的帧将导致运行时中止。如果展开器用完展开信息在找到处理程序之前,调用 std::terminate()。"
  • 因此,我允许 c++ 异常直接通过 libjpeg 以在所有点最大化有关抛出站点的信息,并且我在 C++ 回调中的 RAII 类的析构函数中调用 jpeg 错误回调以释放所有 C 的资源。我没有遇到任何调试问题。
  • 对于 x86-64 上的 GCC,文档不正确。在这种情况下需要 -fno-asynchronous-unwind-tables 来导致中止。
猜你喜欢
  • 1970-01-01
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-07
相关资源
最近更新 更多