在混合 C 和 C++ 代码编程中捕获异常后对象不会被破坏
Posted
技术标签:
【中文标题】在混合 C 和 C++ 代码编程中捕获异常后对象不会被破坏【英文标题】:object is not destroyed after catching exception in mixed C and C++ code programming 【发布时间】:2018-07-06 08:35:43 【问题描述】:我正在编写一个混合使用 c 和 c++ 的程序,我遇到了一个关于 c++ 异常处理程序中的对象销毁的问题。我写了一个简单的案例来重现问题。
main.cpp
#include <iostream>
extern "C" void test(void(*f)(void));
struct foo
~foo()
std::cout << "foo destruction" << std::endl;
;
void error_handler(void)
throw 1;
int main()
try
foo f;
test(error_handler);
catch (...)
test.c
void test(void(*handler)(void))
handler();
当我在 Visual Studio 2015 和 Visual Studio 2017 中构建它时,foo
的析构函数未被调用。但是当我使用 gcc 5.4 对其进行测试时,foo
的析构函数工作正常。
是否可以通过调用函数指针(指向cpp代码中实现的函数的指针)在C代码中抛出C++异常?上面的代码是非法的还是只是一个 msvc 错误?
【问题讨论】:
简单的回答:没有。 问题是 Visual Studio 并没有在最后停止程序。如果你使用代码块,你会看到析构函数的输出。 这将要求抛出的异常通过 C 代码并返回到 C++。当然,C 标准对此只字未提。 @Matthias 这不是原因。断点也没有被击中。 用/EHa
编译。
【参考方案1】:
Windows 异常机制被明确设计为能够运行析构函数并执行 finally 类型清理,即使存在异常和其他语言的类似情况。
所以你要问的应该在 Windows 上正常工作 - 这是它的设计目的。
但是,您需要在 Visual Studio 中显式启用此功能。默认情况下,Visual Studio 使用 /EHsc
异常模型设置 C++ 代码,该模型明确假设 extern "C"
函数不会抛出或传递异常。这是一个优化,通常是一个很好的优化。
但是,如果您需要假设 extern "C"
函数确实抛出或传递异常,那么您需要更改您的异常模型。你可能想要/EHs
。
但是,我建议您在更改它之前阅读它的后果here。
编辑:是否使用此功能值得商榷。通常对于异常(和其他类似机制),抛出者和调用者之间的堆栈上的所有代码都需要是异常安全的。如果您拥有代码,那么这很好,如果堆栈上有 windows 回调或其他库之类的东西,那么您需要找到一个保证,这是可以的。一般来说windows internal code 不是。
【讨论】:
【参考方案2】:跨语言边界抛出异常总是会使程序处于不一致的状态。在抛出 C++ 异常的情况下执行的堆栈展开保证仅适用于(二进制兼容的)C++ 堆栈帧。 C 语言甚至没有异常的概念。即使堆栈展开设法展开 C 堆栈帧,它也不会对它们执行任何清理。因此,任何传递给 C 代码的回调函数都应声明为noexcept
,并以某种不涉及跨语言边界抛出异常的方式处理错误:
void error_handler(void) noexcept
try
throw 1;
catch(…)
// TODO convert to error code or store for later using exception_ptr
【讨论】:
其实我在使用libharu的时候遇到了这个问题。我按照文档实现错误处理,这里是文档github.com/libharu/libharu/wiki/… @benlypan 我不熟悉那个库,但是如果HPDF_New
是一个 C 函数,那么这个示例代码肯定不好。您可能想为开发人员发送一个新问题。以上是关于在混合 C 和 C++ 代码编程中捕获异常后对象不会被破坏的主要内容,如果未能解决你的问题,请参考以下文章