无法捕获异常!

4 投票
3 回答
3515 浏览
提问于 2025-04-15 11:51

我正在使用swig这个工具,把一个C++库中的类包装成Python可以用的。整体上运行得不错,但有一个例外情况是在库内部抛出的,我在swig接口里捕捉不到这个异常,所以Python程序就崩溃了!

这个类PyMonitor.cc描述了swig接口,目标类是Monitor。Monitor的构造函数如果连接失败就会抛出一个异常。我想在PyMonitor里处理这个异常,比如:

PyMonitor.cc:

#include "Monitor.h"  

// ...  

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

// ...

但是,connect()方法从来没有捕捉到这个异常,我只看到“terminate called after throwing ...”的错误信息,程序就中止了。

我对swig了解不多,但我觉得这都是正常的C++代码,异常应该在杀掉程序之前传递到connect()方法。

有什么想法吗?

3 个回答

1

可能是有一个函数,直接或间接地被监视器的 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 抛出一个符合当前失败函数异常规范的异常,但无论如何,你的意外处理程序不能正常结束。它必须抛出异常或者以其他方式终止。

要解决这个问题,你真的需要深入到被调用的代码中,修正它,以确保不违反异常规范,或者修正规范本身,或者修正代码,使其不抛出不被预期的异常。

另一种可能性是,在原始异常被抛出后,堆栈展开时又抛出了一个异常。这也会导致程序终止。在这种情况下,虽然你可以替换标准的终止函数,但你别无选择,只能中止程序。终止处理程序不能抛出异常或返回,它必须终止程序。

1

我对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块里有东西抛出了另一个异常?没有更多的细节,我能想到的就只有这些情况可能导致你遇到的问题。

5

如果你想在Python中处理异常,就需要把这些异常转发过去。具体的做法可以参考SWIG文档

为了转发异常,你只需要在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");
 } 
}

撰写回答