【问题标题】:Can't catch exception!无法捕获异常!
【发布时间】:2010-10-29 03:25:27
【问题描述】:

我正在使用 swig 用 python 包装来自 C++ 库的类。它总体上可以工作,但是从库中抛出了一个异常,我似乎无法在 swig 界面中捕获它,所以它只会使 python 应用程序崩溃!

类 PyMonitor.cc 描述了所需类 Monitor 的 swig 接口。 如果连接失败,Monitor 的构造函数会抛出异常。我想在 PyMonitor 中处理这个异常,例如:

PyMonitor.cc:

#include "Monitor.h"  

// ...  

bool PyMonitor::connect() {  
    try {  
        _monitor = new Monitor(_host, _calibration);  
    } catch (...) {  
        printf("oops!\n");  
    }  
}

// ...

但是,connect() 方法从不捕获异常,我只是得到一个“抛出...后调用终止”错误,然后程序中止。

我对 swig 不太了解,但在我看来,这一切都很好 C++,异常应该在终止程序之前传播到 connect() 方法。

有什么想法吗?

【问题讨论】:

  • 您确定输入了您的连接方法吗?您可以调试代码并在调试器中单步执行。这应该会告诉你问题出在哪里。
  • 是的,connect方法肯定输入了,我在上面的代码中省略了它们,但是在分配给_monitor之前和之后的try块中有printf和fflush(stdout)。打印了之前的一个,但是 Monitor 的构造函数抛出了异常,并且第二个没有输出。关于调试,您对此有任何指示吗?由于它实际上是调用这些方法的python代码,因此在这种情况下我对调试知之甚少。基本的 gdb 技巧会起作用吗?
  • 也许 Monitor 构造函数捕获异常并在内部终止运行时...?
  • 你是否在你的 c++ 库中使用了一个自定义的内存分配器,它暴露给了 python?

标签: c++ python exception swig


【解决方案1】:

如果你想在那里解析异常,你必须将它们转发给 Python。 请参阅SWIG Documentation。 为了转发异常,您只需在 SWIG 接口 (.i) 文件中添加一些代码。基本上,它可以在 .i 文件中的任何位置。

应在此处指定所有类型的异常,并且 SWIG 捕获列出的异常类型(在本例中为 std::runtime_error、std::invalid_argument、std::out_of_range)、所有其他异常被捕获为未知异常(因此被正确转发!)。

// Handle standard exceptions.
// NOTE: needs to be before the %import!
%include "exception.i"
%exception
{
 try
 {
   $action
 }
 catch (const std::runtime_error& e) {
   SWIG_exception(SWIG_RuntimeError, e.what());
 } 
 catch (const std::invalid_argument& e) {
   SWIG_exception(SWIG_ValueError, e.what());
 }
 catch (const std::out_of_range& e) {
   SWIG_exception(SWIG_IndexError, e.what());
 }
 catch (...) { 
   SWIG_exception(SWIG_RuntimeError, "unknown exception");
 } 
}

【讨论】:

    【解决方案2】:

    我不熟悉 swig,也不熟悉同时使用 C++ 和 Python,但如果这是在最新版本的 Microsoft Visual C++ 下,那么 Monitor 类可能会抛出 C 结构化异常,而不是 C++键入的异常。 C 结构化异常不会被 C++ 异常处理程序捕获,即使是 catch(...) 也是如此。

    如果是这种情况,您可以使用__try/__except 关键字(而不是try/catch),或使用_set_se_translator 函数将C 结构化异常转换为C++ 类型异常。

    (如果我没记错的话,旧版本的 MSVC++ 将 C 结构化异常视为 C++ int 类型,并且 会被 C++ 处理程序捕获。)

    如果在 Microsoft Visual C++ 下这个不是,那么我不确定这是怎么发生的。

    编辑:既然你说这不是 MSVC,也许其他东西在你的代码得到它之前捕获异常(并终止程序),或者你的 catch 块中可能有一些东西抛出另一个异常?如果没有更多细节可以处理,我能想到的只有这些情况会导致这些症状。

    【讨论】:

    • 对不起,我应该指定,我只是使用 gnu 工具链,而不是 MVC++。 Monitor 抛出的异常是一个常见的 C++ 异常,在 c++ 对象上使用 throw 关键字。
    【解决方案3】:

    监视器constructor 直接或间接调用的函数可能违反其异常规范并且不允许抛出std::bad_exception。如果您没有替换用于捕获此问题的标准函数,那么它将解释您所看到的行为。

    要测试这个假设,您可以尝试定义自己的处理程序:

    void my_unexpected()
    {
        std::cerr << "Bad things have happened!\n";
        std::terminate();
    }
    
    
    bool PyMonitor::connect() {  
    
        std::set_unexpected( my_unexpected );
    
        try {  
            _monitor = new Monitor(_host, _calibration);  
        } catch (...) {  
            printf("oops!\n");  
        }  
    }
    

    如果您收到“坏事发生了!”错误消息然后您已确认是这种情况,但不幸的是,您可以做的可能不多。如果你'幸运',你可能会从my_unexpected 抛出一个异常,这是当前失败的函数的异常规范所允许的,但无论如何你的意外处理程序都不允许正常终止。它必须抛出或以其他方式终止。

    要解决此问题,您确实需要进入被调用代码并更正它以便不违反异常规范,方法是修复规范本身或修复代码以使其不抛出异常'没想到。

    另一种可能性是在堆栈展开期间抛出了一个异常,这是由抛出的原始异常引起的。这也会导致进程终止。在这种情况下,虽然您可以替换标准终止功能,但您别无选择,只能中止程序。终止处理程序不允许抛出或返回,它必须终止程序。

    【讨论】:

    • 这是一个有趣的想法。我尝试使用上面的 set_unexpected ,但我无法抓住它。但是,当我尝试使用 set_terminate 进行类似操作时,我能够再次“获得一些控制”,只需打印出一条消息并调用 abort。我开始认为 swig 以某种方式让 PyMonitor 和 Monitor 在不同的进程空间或其他东西中运行,并且 Monitor 构造函数中抛出的异常在进入 PyMonitor 之前以某种方式展开到其堆栈顶部。这可能吗?
    猜你喜欢
    • 1970-01-01
    • 2021-10-09
    • 2021-09-17
    • 2021-10-03
    • 2018-02-04
    • 2018-02-04
    • 2011-04-19
    • 2017-08-10
    相关资源
    最近更新 更多