处理定制C++;Cython中的例外 P>在Cython调用时,处理自定义C++异常有一定的困难。 我的情况如下:我有一个库,它对所有异常使用CustomLibraryException。我想要的基本上是获取错误消息并用它引发Python错误

处理定制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++函数,它可以处理错误。不过我不知道

这个方法有一些提示,但有点不具体。 第一种可能性是:

cdef int bar()除+ValueError外

这会将
CustomLibraryException
转换为
ValueError
,但会丢失错误消息

另一种可能是使用

cdef int raise_py_error()
cdef int something_dangerous() except +raise_py_error
我并不真正理解这个解决方案。我知道,RaSeEYPYIORT错误必须是一个C++函数,它可以处理错误。不过我不知道怎么处理。函数没有得到一个参数,在C++中的代码< catch catch >代码>块中被调用。

如果有人在Cython中处理C++异常的工作实例,这将是非常有帮助的。

< P>如果<代码> CueSubLogyExpUT/<代码>源自 STD::RunTimeOrthError < /C> >(作为一个行为良好的C++异常),那么您所看到的行为在Cython是一个错误。

如果不这样,那么最简单的做法就是把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端出现运行时错误。另外,代码> >代码> >代码> STD::ValueDyTrime< /Cuto >代码> Apdio/<代码> >代码> ValueError < /Cuff>等(参见链接到的页面中的表).< /P> < P> Cython中的默认C++异常处理程序应该详细说明如何实现您所要做的:

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)