在混合 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++ 异常处理程序中的对象销毁的问题。我写了一个简单的案例来重现问题。

ma​​in.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++ 代码编程中捕获异常后对象不会被破坏的主要内容,如果未能解决你的问题,请参考以下文章

objective-c和Cpp怎么混合编程?

XCTAssertThrows 可以捕获 c++ 异常吗?

C#:捕获混合托管/非托管进程的所有错误/异常

捕获多个自定义异常? - C++

C++ 与 Python 混合编程

C++ 与 Python 混合编程