Piszę wrapper SWIG wokół niestandardowej biblioteki C++, która definiuje własne typy wyjątków C++. Typy wyjątków biblioteki są bogatsze i bardziej szczegółowe niż standardowe wyjątki. (Na przykład jedna klasa reprezentuje błędy analizy i ma kolekcję numerów linii.) W jaki sposób mogę propagować te wyjątki z powrotem do Pythona, zachowując typ wyjątku?Jak propagować wyjątki C++ do Pythona w bibliotece otokowej SWIG?
Odpowiedz
%except(python) {
try {
$function
}
catch (RangeError) {
PyErr_SetString(PyExc_IndexError,"index out-of-bounds");
return NULL;
}
}
Czy swig exception documentation any help? Wymienia on defining different exception handlers ..
Wiem, że to pytanie ma kilka tygodni, ale właśnie znalazłem to, ponieważ szukałem rozwiązania dla siebie. Więc spróbuję odpowiedzieć, ale ostrzegam z góry, że może to nie być atrakcyjne rozwiązanie, ponieważ pliki interfejsu swig mogą być bardziej skomplikowane niż ręczne kodowanie opakowania. Ponadto, o ile mogę powiedzieć, dokumentacja swig nigdy nie zajmuje się bezpośrednio z wyjątkami zdefiniowanymi przez użytkownika.
Powiedzmy, że chcesz rzucić następujący wyjątek od C++ moduł kodu, mylibrary.cpp, wraz z małym błędzie być złapany w kodzie Pythona:
throw MyException("Highly irregular condition..."); /* C++ code */
MyException jest wyjątkiem zdefiniowane przez użytkownika gdzie indziej.
Przed rozpoczęciem należy zanotować sposób, w jaki ten wyjątek powinien zostać przechwycony w pytonie. Dla naszych celów tutaj, załóżmy, że masz kod Pythona, taki jak:
import mylibrary
try:
s = mylibrary.do_something('foobar')
except mylibrary.MyException, e:
print(e)
Myślę, że to opisuje twój problem.
Moje rozwiązanie polega na udostępnianiu cztery dodatki do pliku interfejsu swig (mylibrary.i) następująco:
Krok 1: w dyrektywie nagłówka (the zwykle bezimienny% {...}% bloku) dodaj deklaracja dla wskaźnika do wyjątku świadomego python, który nazwiemy pMyException. Krok 2 poniżej definiuje następująco:
%{
#define SWIG_FILE_WITH_INIT /* for eg */
extern char* do_something(char*); /* or #include "mylibrary.h" etc */
static PyObject* pMyException; /* add this! */
%}
Krok 2: Dodaj dyrektywę inicjalizacji („M” jest szczególnie skandaliczne, ale to co haust v1.3.40 aktualnie potrzebuje w tym momencie w jego skonstruowanego pliku otoki) - pMyException został ogłoszony w punkcie 1 powyżej:
%init %{
pMyException = PyErr_NewException("_mylibrary.MyException", NULL, NULL);
Py_INCREF(pMyException);
PyModule_AddObject(m, "MyException", pMyException);
%}
krok 3: Jak wspomniano we wcześniejszym poście, musimy dyrektywę wyjątku - nota "% z wyjątkiem (python)" jest przestarzała. To owinąć C+++ funkcję „do_something” w próbie z wyjątkiem bloku, który wychwytuje C++ wyjątek i konwertuje go na wyjątkiem pytona określonej w etapie 2 powyżej:
%exception do_something {
try {
$action
} catch (MyException &e) {
PyErr_SetString(pMyException, const_cast<char*>(e.what()));
SWIG_fail;
}
}
/* The usual functions to be wrapped are listed here: */
extern char* do_something(char*);
Etap 4: Ponieważ haust ustawia pytona wrapping (moduł 'shadow') wokół pliku .pyd dll, musimy również upewnić się, że nasz kod Pythona może "przejrzeć" plik .pyd. Następujące pracował dla mnie i jest to korzystne dla edycji kodu haust py otoki bezpośrednio:
%pythoncode %{
MyException = _mylibrary.MyException
%}
to chyba zbyt późno, aby być o wiele wykorzystania do ich twórców, ale może ktoś inny znajdzie propozycje powyższe przydać .W przypadku małych zleceń możesz preferować czystość ręcznie kodowanej owijki rozszerzeń C++ do pomieszania pliku interfejsu swig.
Dodano:
w moim pliku interfejs aukcji powyżej, pominąłem standardową dyrektywę modułu ponieważ chciałem tylko opisać dodatki konieczne, aby wyjątki pracować. Ale linia moduł powinien wyglądać następująco:
%module mylibrary
Również swojej setup.py (jeśli używasz distutils, które polecam przynajmniej zacząć) powinny mieć kod podobny do poniższego, inaczej kroku 4 zawiedzie kiedy _mylibrary nie jest rozpoznawane:
/* setup.py: */
from distutils.core import setup, Extension
mylibrary_module = Extension('_mylibrary', extra_compile_args = ['/EHsc'],
sources=['mylibrary_wrap.cpp', 'mylibrary.cpp'],)
setup(name="mylibrary",
version="1.0",
description='Testing user defined exceptions...',
ext_modules=[mylibrary_module],
py_modules = ["mylibrary"],)
Uwaga flagi kompilacji/EHsc, której potrzebowałem na Windows do kompilacji wyjątków. Twoja platforma może nie wymagać tej flagi; Google ma szczegóły.
Przetestowałem kod przy użyciu własnych nazw, które przekonwertowałem tutaj na "mylibrary" i "MyException", aby pomóc w uogólnieniu rozwiązania. Mam nadzieję, że błędy w transkrypcji są niewielkie lub żadne. Główne punkty startowe są% i% pythoncode dyrektyw wraz z
static PyObject* pMyException;
w dyrektywie nagłówka.
Nadzieję, że wyjaśnia rozwiązanie.
To wygląda bardzo obiecująco - wypróbuję to. Nie jest za późno, bo teraz po prostu łapiemy RuntimeError i sprawdzamy tekst komunikatów o błędach - fuj. – Barry
Jest bardziej ogólne, aby zastąpić "return NULL", który robisz po wywołaniu "PyErr_SetString" w twoim przykładzie z makrem o nazwie "SWIG_fail", który powinien zawsze działać, tj. Niezależnie od zwróconego typu danych. – Hermes
dodam trochę tu, ponieważ przykład podano tutaj teraz mówi, że „% z wyjątkiem (Python)” to przestarzała ...
Można teraz (jak z swig 1.3.40, w każdym razie) zrób całkowicie ogólne, niezależne od skryptów tłumaczenie. Mój przykład będzie:
%exception {
try {
$action
} catch (myException &e) {
std::string s("myModule error: "), s2(e.what());
s = s + s2;
SWIG_exception(SWIG_RuntimeError, s.c_str());
} catch (myOtherException &e) {
std::string s("otherModule error: "), s2(e.what());
s = s + s2;
SWIG_exception(SWIG_RuntimeError, s.c_str());
} catch (...) {
SWIG_exception(SWIG_RuntimeError, "unknown exception");
}
}
To wygeneruje wyjątek RuntimeError w dowolnym obsługiwanym językiem skryptowym, tym Python, bez uzyskiwania Pythona konkretne rzeczy w innych nagłówków.
Musisz umieścić ten przed wywołań, które wymagają tej obsługi wyjątków.
+1, wystarczy upewnić się, że% zawiera wyjątek.w jego pliku interfejsu SWIG (wprowadzenie makra SWIG_exception) –
U można również użyć:
połowy: http://www.swig.org/Doc3.0/SWIGPlus.html#SWIGPlus_catches
przykład:
%catches(std::exception, std::string, int, ...);
generującym dla każdej funkcji bloku próba zbiornika:
try {
result = (namespace::Function *)new namespace::Function ((uint16_t const *)arg1);
}
catch(std::exception &_e) {
SWIG_exception_fail(SWIG_SystemError, (&_e)->what());
}
catch(std::string &_e) {
SWIG_Python_Raise(SWIG_From_std_string(static_cast<std::string>(_e)), "std::string", 0); SWIG_fail;
}
catch(int &_e) {
SWIG_Python_Raise(SWIG_From_int(static_cast<int>(_e)), "int", 0); SWIG_fail;
}
catch(...) {
SWIG_exception_fail(SWIG_RuntimeError,"unknown exception");
}
-1: To nie działa, Python ulega awarii. '% catches' nie jest wystarczające, chyba że określę' throw() 'w deklaracjach funkcji. A w tym przypadku zbędne jest "% połowów". Powiązana dokumentacja SWIG wspomina jedynie przy użyciu dyrektywy '% catches' wraz z nazwą funkcji. – Melebius
% przechwytuje faktycznie "wyjątek opakowuje" więcej funkcji zawijane swig następnie% wyjątek. – hugo24
ten znajduje się w pobliżu. Zachowuje typ wyjątku, ale jak zaimplementować niestandardowe typy wyjątków? Być może przykłady SWIG to potwierdzają. – Barry