【问题标题】:object is not destroyed after catching exception in mixed C and C++ code programming在混合 C 和 C++ 代码编程中捕获异常后对象不会被破坏
【发布时间】: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编译。

标签: c++ gcc visual-c++


【解决方案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 函数,那么这个示例代码肯定不好。您可能想为开发人员发送一个新问题。
    猜你喜欢
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-24
    • 2014-05-26
    • 2020-04-12
    相关资源
    最近更新 更多