2009-09-08 9 views
22

Sto scrivendo un wrapper SWIG attorno ad una libreria C++ personalizzata che definisce i propri tipi di eccezioni C++. I tipi di eccezioni della libreria sono più ricchi e specifici delle eccezioni standard. (Ad esempio, una classe rappresenta gli errori di analisi e ha una raccolta di numeri di riga.) Come posso propagare tali eccezioni a Python conservando il tipo di eccezione?Come si propagano le eccezioni C++ a Python in una libreria wrapper SWIG?

risposta

0

Dal swig documentation

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

Questa è vicino. Conserva il tipo di eccezione ma come incapsulare i tipi di eccezioni personalizzati? Forse gli esempi SWIG coprono questo. – Barry

15

So che questa domanda è vecchia di qualche settimana ma l'ho appena trovata mentre cercavo una soluzione per me. Quindi proverò a dare una risposta, ma avvertirò in anticipo che potrebbe non essere una soluzione interessante dato che i file di interfaccia di swig possono essere più complicati della codifica manuale del wrapper. Inoltre, per quanto posso dire, la documentazione di swig non tratta mai direttamente le eccezioni definite dall'utente.

Diciamo che si vuole buttare la seguente eccezione dal C++ modulo di codice, mylibrary.cpp, insieme a un messaggio di errore poco per essere catturati nel codice Python:

throw MyException("Highly irregular condition..."); /* C++ code */ 

MyException è un'eccezione definita dall'utente altrove.

Prima di iniziare, prendere nota di come questa eccezione deve essere rilevata nel proprio python. Per i nostri scopi qui, diciamo che si dispone di codice python come la seguente:

import mylibrary 
try: 
    s = mylibrary.do_something('foobar') 
except mylibrary.MyException, e: 
    print(e) 

Penso che questo descrive il problema.

La mia soluzione consiste nel fare quattro aggiunte al vostro file di interfaccia sorso (mylibrary.i) come segue:

Fase 1: Nella direttiva di intestazione (la% di solito senza nome {...}% blocco) aggiungere un dichiarazione per il puntatore all'eccezione python aware, che chiameremo pMyException. Fase 2 di seguito definirà questo:

%{ 
#define SWIG_FILE_WITH_INIT /* for eg */ 
extern char* do_something(char*); /* or #include "mylibrary.h" etc */ 
static PyObject* pMyException; /* add this! */ 
%} 

Fase 2: Aggiungi una direttiva di inizializzazione (la "m" è particolarmente eclatanti, ma è quello che sorso v1.3.40 attualmente ha bisogno in quel punto nel suo file involucro costruito) - pMyException è stato dichiarato al punto 1 di cui sopra:

%init %{ 
    pMyException = PyErr_NewException("_mylibrary.MyException", NULL, NULL); 
    Py_INCREF(pMyException); 
    PyModule_AddObject(m, "MyException", pMyException); 
%} 

fase 3: Come accennato in un precedente post, abbiamo bisogno di una direttiva un'eccezione - nota "% ad eccezione (Python)" è deprecato. Ciò avvolgere il +++ funzione c "fai_qualcosa" in un blocco che rileva l'eccezione C++ e la converte l'eccezione pitone definito al punto 2 try-except:

%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*); 

Fase 4: Poiché sorso imposta un pitone wrapping (un modulo 'shadow') attorno alla DLL di .pyd, dobbiamo anche assicurarci che il nostro codice python possa 'vedere attraverso' il file .pyd. Di seguito ha lavorato per me ed è preferibile modificare il codice sorso py involucro direttamente:

%pythoncode %{ 
    MyException = _mylibrary.MyException 
%} 

E 'probabilmente troppo tardi per essere di grande utilità per il manifesto originale, ma forse qualcun altro troverà i suggerimenti di cui sopra di un certo uso .Per lavori di piccole dimensioni si può preferire la pulizia di un wrapper di estensione C++ codificato a mano per la confusione di un file di interfaccia swig.


Aggiunto:

Nella messa in vendita di mio file interfaccia di cui sopra, ho omesso la direttiva modulo standard perché volevo solo descrivere le aggiunte necessarie per fare delle eccezioni lavoro. Ma la linea del modulo dovrebbe essere simile:

%module mylibrary 

Inoltre, il setup.py (se si utilizza distutils, che consiglio, almeno per iniziare) dovrebbe avere un codice simile al seguente, altrimenti passo 4 fallirà quando _mylibrary non viene riconosciuto:

/* 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"],) 

nota la bandiera di compilazione/EHsc, che avevo bisogno su Windows per la compilazione delle eccezioni. La tua piattaforma potrebbe non richiedere quella bandiera; google ha i dettagli.

Ho testato il codice utilizzando i miei nomi che ho convertito qui in "mylibrary" e "MyException" per aiutare a generalizzare la soluzione. Speriamo che gli errori di trascrizione siano pochi o nessuno. I punti principali sono le direttive% init e% pythoncode insieme a

static PyObject* pMyException; 

nella direttiva header.

La speranza che chiarisce la soluzione.

+0

Questo sembra molto promettente - lo proverò. Non è troppo tardi, btw, perché per ora stiamo solo recuperando RuntimeError e esaminando il testo dei messaggi di errore - schifo. – Barry

+0

È più generico sostituire "return NULL" dopo aver chiamato "PyErr_SetString" nel tuo esempio con una macro chiamata "SWIG_fail", che dovrebbe sempre funzionare, ad esempio indipendentemente dal tipo di dati restituito. – Hermes

8

aggiungerò un po 'qui, dal momento che l'esempio dato qui ora dice che "tranne% (Python)" è deprecato ...

Ora è possibile (come del sorso 1.3.40, comunque) eseguire una traduzione totalmente generica, indipendente dal linguaggio di script. Il mio esempio potrebbe essere:

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

Questo genererà un'eccezione RuntimeError in qualsiasi linguaggio di scripting supportato, tra cui Python, senza ottenere cose specifiche pitone nelle altre intestazioni.

È necessario mettere questo prima delle chiamate che desiderano questa gestione delle eccezioni.

+2

+1, basta un solo accertarsi di avere l'eccezione% include.nel suo file di interfaccia SWIG (introducendo la macro SWIG_exception) –

-1

U può anche utilizzare:

catture: http://www.swig.org/Doc3.0/SWIGPlus.html#SWIGPlus_catches

Esempio:

%catches(std::exception, std::string, int, ...); 

che genera per ogni funzione un blocco try cattura:

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: Questo non funziona, Python si blocca. '% catches' non è sufficiente a meno che non specifichi' throw() 'nelle dichiarazioni di funzione. E '% catches' è superfluo in quel caso. La documentazione SWIG collegata menziona solo la direttiva '% catches' insieme al nome di una funzione. – Melebius

+0

% rileva in realtà "eccezioni avvolge" più funzioni avvolte in swig, quindi eccezione%. – hugo24

Problemi correlati