2012-05-21 14 views
6

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

user guideにはいくつかのヒントがありますが、それは少し不明です。 最初の可能性が行うことである:これはValueErrorCustomLibraryExceptionに変換

CDEFのINTバー()+とValueError

除いて、エラーメッセージを失います。

他の可能性は明示的に、私は本当にこのソリューションをunderstantません

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

を使用して、エラーを変換することです。私はraise_py_errorが何とかエラーを処理するC++関数でなければならないことを理解しました。私はそれをどう扱うかわからない。この関数は引数を取得せず、C++のcatchブロック内で呼び出されます。

CythonでC++例外を処理する実例があれば、大きな助けになるでしょう。

答えて

2

CustomLibraryExceptionstd::runtime_errorから派生している場合(正常に動作するC++例外として)、Cythonのバグが表示されます。

そうでない場合は、一番簡単な方法は、C++の関数をラップすることですあなたは例外を変換するC++ヘルパー関数で呼び出している:これはRuntimeErrorが可能になります

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側で発生します。あるいは、throwstd::invalid_argumentraiseValueErrorなどです(リンク先のページの表を参照)。

+0

例外は期待通りに '' std :: runtime''から派生していません。助けてくれてありがとう:それはそれをはるかに良くすることはありません。エラーを発生させる関数はメンバ関数なので、コードを変更したくありません。これは私の[gco wrappers](http://peekaboo-vision.blogspot.de/2012/05/graphcuts-for-python-pygco.html)に関するもので、ライセンスでは私には再配布できません: -/ –

+1

@ AndreasMueller:メンバ関数は独立した関数で簡単にラップできます。 'void wrapper(Obj&o、int ham){return o.wrapped(ham);}この関数は、最初の引数として操作するオブジェクトを渡します。 } ' –

+1

ええ、私はまだそれをやるのが怠惰だと知っていました;) Btw、あなたはSOのトップ100のポスターだとお知りになりましたか? –

3

デフォルトC++ Cythonでの例外ハンドラは、あなたがやろうとしているものを達成する方法を正確に説明しなければならない:

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

だからあなたはどちらか含ま.hファイル内#define __Pyx_CppExn2PyErr your_custom_exn_handlerは一般的な動作をオーバーライドすることができ、または使用ワンオフカスタムハンドラーは、

cdef extern from "...": 
    void your_exn_throwing_fcn() except +your_custom_exn_handler 
7

としています。文書ページの言葉には、何か必要なものがあります。 "CythonではC++例外"を投げることはできませんが、ここではraise_py_errorがあります。

まず、次に(ドキュメントは、この中に書かれなければならないスーパー明らかにされていない例外ハンドラを記述cythonでカスタム例外クラスを定義し、「パブリック」キーワード

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 
+0

尋ねる答え。あなたは、C + +ファイルを直接コンパイルすることが可能かどうかを知っていますか?それとも、libを手作業でリンクする必要がありますか?私はこの文書を見つけることができませんでした... –

+0

私は知っていました。ここでは、カスタム処理からのメッセージを保存しながらエラー処理の最小例を示します:https://github.com/SintefRaufossManufacturing/python-hcn/blob/master/hcn/cy_handler.cpp –

+1

どのように正確に(どのファイル拡張子、あなたがcythonクラスを宣言し、それを 'PyObject'にキャストするのに使う必要がありますか? (最初のコードブロック)PyObjectはデフォルトでは定義されておらず、 '' Python.h ''からインポートします '' Pythonオブジェクトをプリミティブ型にキャストすることはできません... – Silmathoron