无法捕获异常!
我正在使用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 个回答
可能是有一个函数,直接或间接地被监视器的 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
抛出一个符合当前失败函数异常规范的异常,但无论如何,你的意外处理程序不能正常结束。它必须抛出异常或者以其他方式终止。
要解决这个问题,你真的需要深入到被调用的代码中,修正它,以确保不违反异常规范,或者修正规范本身,或者修正代码,使其不抛出不被预期的异常。
另一种可能性是,在原始异常被抛出后,堆栈展开时又抛出了一个异常。这也会导致程序终止。在这种情况下,虽然你可以替换标准的终止函数,但你别无选择,只能中止程序。终止处理程序不能抛出异常或返回,它必须终止程序。
我对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块里有东西抛出了另一个异常?没有更多的细节,我能想到的就只有这些情况可能导致你遇到的问题。
如果你想在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");
}
}