处理定制C++;Cython中的例外 P>在Cython调用时,处理自定义C++异常有一定的困难。 我的情况如下:我有一个库,它对所有异常使用CustomLibraryException。我想要的基本上是获取错误消息并用它引发Python错误
这个方法有一些提示,但有点不具体。 第一种可能性是: cdef int bar()除+ValueError外 这会将处理定制C++;Cython中的例外 P>在Cython调用时,处理自定义C++异常有一定的困难。 我的情况如下:我有一个库,它对所有异常使用CustomLibraryException。我想要的基本上是获取错误消息并用它引发Python错误,c++,exception-handling,cython,C++,Exception Handling,Cython,这个方法有一些提示,但有点不具体。 第一种可能性是: cdef int bar()除+ValueError外 这会将CustomLibraryException转换为ValueError,但会丢失错误消息 另一种可能是使用 cdef int raise_py_error() cdef int something_dangerous() except +raise_py_error 我并不真正理解这个解决方案。我知道,RaSeEYPYIORT错误必须是一个C++函数,它可以处理错误。不过我不知道
CustomLibraryException
转换为ValueError
,但会丢失错误消息
另一种可能是使用
cdef int raise_py_error()
cdef int something_dangerous() except +raise_py_error
我并不真正理解这个解决方案。我知道,RaSeEYPYIORT错误必须是一个C++函数,它可以处理错误。不过我不知道怎么处理。函数没有得到一个参数,在C++中的代码< catch catch >代码>块中被调用。
如果有人在Cython中处理C++异常的工作实例,这将是非常有帮助的。
< P>如果<代码> CueSubLogyExpUT/<代码>源自如果不这样,那么最简单的做法就是把C++调用函数包在C++辅助函数中,转换异常:
double foo(char const *, Bla const &); // this is the one we're wrapping
double foo_that_throws_runtime_error(char const *str, Bla const &blaref)
{
try {
return foo(str, blaref);
} catch (CustomLibraryException const &e) {
throw std::runtime_error(e.get_the_message());
}
}
这将导致Python端出现运行时错误。另外,
static void __Pyx_CppExn2PyErr() {
// Catch a handful of different errors here and turn them into the
// equivalent Python errors.
try {
if (PyErr_Occurred())
; // let the latest Python exn pass through and ignore the current one
else
throw;
} catch (const std::bad_alloc& exn) {
PyErr_SetString(PyExc_MemoryError, exn.what());
} catch (const std::bad_cast& exn) {
PyErr_SetString(PyExc_TypeError, exn.what());
} catch (const std::domain_error& exn) {
PyErr_SetString(PyExc_ValueError, exn.what());
} catch (const std::invalid_argument& exn) {
PyErr_SetString(PyExc_ValueError, exn.what());
} catch (const std::ios_base::failure& exn) {
// Unfortunately, in standard C++ we have no way of distinguishing EOF
// from other errors here; be careful with the exception mask
PyErr_SetString(PyExc_IOError, exn.what());
} catch (const std::out_of_range& exn) {
// Change out_of_range to IndexError
PyErr_SetString(PyExc_IndexError, exn.what());
} catch (const std::overflow_error& exn) {
PyErr_SetString(PyExc_OverflowError, exn.what());
} catch (const std::range_error& exn) {
PyErr_SetString(PyExc_ArithmeticError, exn.what());
} catch (const std::underflow_error& exn) {
PyErr_SetString(PyExc_ArithmeticError, exn.what());
} catch (const std::exception& exn) {
PyErr_SetString(PyExc_RuntimeError, exn.what());
}
catch (...)
{
PyErr_SetString(PyExc_RuntimeError, "Unknown exception");
}
}
因此,您可以在included.h文件中定义您的自定义处理程序来覆盖泛型行为,也可以使用一次性自定义处理程序
cdef extern from "...":
void your_exn_throwing_fcn() except +your_custom_exn_handler
同意,文件页中的措辞还有待改进。虽然“Cython不能抛出C++异常”,但这里是一个RaseSyPyl错误,它符合我们的需要。 首先,在cython中定义自定义异常类,并使用“public”关键字对其进行处理 完成了。我现在看到了新的异常,按照预期使用错误代码构造:
JMapError: 520 timed connect failed: Connection refused
在Cython的源代码中,他们实际上在.pyx文件中实现了raise\u py\u错误。这使得在其他.pyx文件之间共享错误处理变得更加容易
快速解决方案只涉及2个文件:myerror.pyx:
class MyError(RuntimeError):
"Base class for errors raised from my C++."
pass
cdef int raise_my_py_error() except *:
raise MyError("There was a problem")
和myerror.pxd:
cdef int raise_my_py_error() except *
cdef int raise_my_py_error() except *
它允许您在所有文件中添加一个除了+my_py_error
但是,这“失去”C++异常的<代码> E.()(代码)< /代码>。因此,一个更有趣的解决方案需要更多的助手文件:
my_error_helper.h:
extern const char* get_my_py_error_message();
my_error_helper.cxx:
#include <exception>
const char* get_my_py_error_message()
{
try {
throw;
} catch (const my_custom_cxx_exception& e) {
return e.what();
}
}
my_error.pxd:
cdef int raise_my_py_error() except *
cdef int raise_my_py_error() except *
my_error.pyx:
cimport my_error_helper
class MyError(RuntimeError):
"Base class for errors raised from my C++."
pass
cdef int raise_my_py_error() except *:
msg = my_error_helper.get_my_py_error_message().decode('utf-8')
raise MyError(msg)
异常并非如您预期的那样从std::runtime
派生。谢谢你的帮助:)不过这并没有让情况变得更好。引发错误的函数是成员函数,我不想更改它们的代码。这是关于我的,许可证不允许我重新分发:-/@AndreasMueller:成员函数可以很容易地包装在独立函数中;只需传递他们应该操作的对象作为第一个参数:void wrapper(Obj&o,int-ham){return o.wrapped(ham);}
是的,我知道,我只是懒得这么做;)顺便说一句,你知道你是世界上排名前100的海报吗?@AndreasMueller:我一点也不惊讶。我在这里花了太多时间:)盘问着答案。你知道是否有可能使Cython直接编译C++文件,或者我需要手工制作一个LIB,然后链接到它?我找不到这方面的任何文件…我发现了。下面是一个在保留来自自定义eception的消息时进行错误处理的最小示例:您能否解释一下您如何准确地(使用哪个文件扩展名,哪些必要的导入)声明cython类并将其强制转换为PyObject
?(第一个代码块)默认情况下未定义PyObject,从“Python.h”
导入PyObject会让cython抱怨无法将Python对象强制转换为基元类型…第一个块(.pyx文件)缺少“来自cpython.ref cimport PyObject”。我冒昧地编辑了它。我仍然遗漏了一点:如果.pyx(定义jmaperor的那一个)与.pxd(有+raise_py_错误的那一个)具有相同的名称,那么在标准setup.py中每一个都可以正常工作。但是当它们有不同的名称时(比如,因为我想从两个不同的pxd文件中+raise_py_error),我还没有正确的setup.py语法(取决于我使用的是什么,我在模块导入时得到“undefined symbol jmaperor”,或者在编译时得到双重定义的符号)。
cdef int raise_my_py_error() except *
cimport my_error_helper
class MyError(RuntimeError):
"Base class for errors raised from my C++."
pass
cdef int raise_my_py_error() except *:
msg = my_error_helper.get_my_py_error_message().decode('utf-8')
raise MyError(msg)