9

Cython から呼び出すときに、カスタム C++ 例外の処理に問題があります。私の状況は次のとおりですCustomLibraryException。すべての例外を使用するライブラリがあります。私が欲しいのは、基本的にエラーメッセージを取得し、それで Python エラーを発生させることです。

ユーザーガイドにはいくつかのヒントがありますが、少し具体的ではありません. 最初の可能性は次のとおりです。

+ValueError 以外の cdef int bar()

これは を に変換しCustomLibraryExceptionますValueErrorが、エラー メッセージは失われます。

もう1つの可能性は、明示的にエラーを変換することです

cdef int raise_py_error()
cdef int something_dangerous() except +raise_py_error

私はこの解決策を本当に理解していません。raise_py_error は、何らかの方法でエラーを処理する C++ 関数でなければならないことを理解しました。私はそれを処理する方法がわかりません。関数は引数を取得せずcatch、C++ のブロック内で呼び出されます。

Cython で C++ 例外を処理する実際の例があれば、非常に役立ちます。

4

4 に答える 4

9

ドキュメントページの文言には、何かが望まれることに同意しました。「Cython は C++ の例外をスローできません」が、これは私たちが望むことを行う raise_py_error です。

まず、cython でカスタム例外クラスを定義し、"public" キーワードを使用してハンドルを作成します。

from cpython.ref cimport PyObject

class JMapError(RuntimeError):
  pass

cdef public PyObject* jmaperror = <PyObject*>JMapError

次に、例外ハンドラーを記述します (ドキュメントはあまり明確ではありません。これは C++ で記述してインポートする必要があります)。

#include "Python.h"
#include "jmap/cy_utils.H"
#include "jmap/errors.H"
#include <exception>
#include <string>

using namespace std;

extern PyObject *jmaperror;

void raise_py_error()
{
  try {
    throw;
  } catch (JMapError& e) {
    string msg = ::to_string(e.code()) +" "+ e.what();
    PyErr_SetString(jmaperror, msg.c_str());
  } catch (const std::exception& e) {
    PyErr_SetString(PyExc_RuntimeError, e.what() );
  }
}

最後に、ハンドラーを extern ブロックで cython に持ち込み、それを使用します。

cdef extern from "jmap/cy_utils.H":
  cdef void raise_py_error()

void _connect "connect"() except +raise_py_error

終わり。意図したとおりにエラー コードで構築された新しい例外が表示されるようになりました。

JMapError: 520 timed connect failed: Connection refused
于 2015-03-12T05:21:20.077 に答える
4

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");
  }
}

したがって#define __Pyx_CppExn2PyErr your_custom_exn_handler、インクルードされた .h ファイルで一般的な動作をオーバーライドするか、1 回限りのカスタム ハンドラーを次のように使用できます。

cdef extern from "...":
    void your_exn_throwing_fcn() except +your_custom_exn_handler
于 2012-09-14T15:15:27.773 に答える
2

If CustomLibraryException derives from std::runtime_error (as a well-behaved C++ exception should), then the behavior you're seeing is a bug in Cython.

If it doesn't, then the easiest thing to do is to wrap the C++ function you're calling in a C++ helper function that translates the exception:

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());
    }
}

This will cause a RuntimeError to be raised on the Python side. Alternatively, throw an std::invalid_argument to raise a ValueError, etc. (see the table in the page you linked to).

于 2012-05-21T12:57:06.557 に答える