2009-09-08 20 views
22

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

0

Z swig documentation

%except(python) { 
try { 
$function 
} 
catch (RangeError) { 
    PyErr_SetString(PyExc_IndexError,"index out-of-bounds"); 
    return NULL; 
} 
} 
+0

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

15

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.

+0

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

+0

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

8

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.

+2

+1, wystarczy upewnić się, że% zawiera wyjątek.w jego pliku interfejsu SWIG (wprowadzenie makra SWIG_exception) –

-1

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

-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

+0

% przechwytuje faktycznie "wyjątek opakowuje" więcej funkcji zawijane swig następnie% wyjątek. – hugo24

Powiązane problemy