2009-09-08 13 views
22

Estoy escribiendo un contenedor SWIG en torno a una biblioteca personalizada de C++ que define sus propios tipos de excepción C++. Los tipos de excepciones de la biblioteca son más completos y específicos que las excepciones estándar. (Por ejemplo, una clase representa errores de análisis y tiene una colección de números de línea.) ¿Cómo puedo propagar esas excepciones de vuelta a Python conservando el tipo de excepción?¿Cómo se propagan las excepciones de C++ a Python en una biblioteca de contenedor SWIG?

Respuesta

0

Desde el swig documentation

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

Esto está cerca. Conserva el tipo de la excepción, pero ¿cómo encapsulo tipos de excepción personalizados? Tal vez los ejemplos de SWIG lo cubran. – Barry

15

Sé que esta pregunta tiene algunas semanas pero la encontré porque estaba buscando una solución para mí. Así que voy a echar una ojeada a una respuesta, pero advertiré de antemano que puede no ser una solución atractiva, ya que los archivos de interfaz de intercambio pueden ser más complicados que la codificación manual del contenedor. Además, hasta donde puedo decir, la documentación del trago nunca trata directamente con las excepciones definidas por el usuario.

Digamos que usted quiere lanzar la siguiente excepción de su C++ módulo de código, mylibrary.cpp, junto con un mensaje de error poco de estar atrapado en su código Python:

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

MiExcepción es una excepción definida por el usuario en otra parte.

Antes de comenzar, anote cómo se debe capturar esta excepción en su python. Para nuestros propósitos aquí, digamos que tiene código python como el siguiente:

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

Creo que esto describe su problema.

Mi solución consiste en realizar cuatro adiciones a su archivo de interfaz trago (mylibrary.i) de la siguiente manera:

Paso 1: En la directiva de encabezado (el% por lo general no identificado {...}% bloque) añadir una declaración para el puntero a la excepción de python aware, que llamaremos pMyException. Paso 2 a continuación va a definir esto:

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

Paso 2: Agregar una directiva inicialización (la "m" es particularmente atroz, pero eso es lo swig v1.3.40 actualmente necesita en ese momento en su archivo de envoltura construida) - pMyException fue declarado en el paso 1 anterior:

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

paso 3: Como se mencionó en un post anterior, necesitamos una directiva excepción - en cuenta "% excepto (pitón)" está en desuso. Esto envolver el +++ función c "hacer_algo" en un try-excepto bloque que atrapa el C++ excepción y la convierte en la excepción pitón definida en el paso 2 anterior:

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

Paso 4: Debido trago establece un pitón envolviendo (un módulo 'sombra') alrededor de .pyd dll, también necesitamos asegurarnos de que nuestro código python pueda 'ver a través' del archivo .pyd. Los siguientes trabajó para mí y es preferible editar el código trago py envoltorio directamente:

%pythoncode %{ 
    MyException = _mylibrary.MyException 
%} 

Es probable que sea demasiado tarde para ser de mucha utilidad para el cartel original, pero tal vez alguien más va a encontrar las sugerencias anteriores de alguna utilidad .Para trabajos pequeños, puede preferir la limpieza de una envoltura de extensión de C++ codificada a mano a la confusión de un archivo de interfaz swig.


Agregado:

En mi archivo de interfaz lista anterior, que omite la directiva módulo estándar porque yo sólo quería describir las adiciones necesarias para hacer excepciones funcionan. Sin embargo, su línea de módulo debe verse como:

%module mylibrary 

Además, su setup.py (si está utilizando distutils, que recomiendo al menos para empezar) debe tener un código similar al siguiente, de lo contrario el paso 4 se producirá un error cuando _mylibrary no se reconoce:

/* 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 de la bandera de compilación/EHsc, que necesitaba en Windows para compilar excepciones. Su plataforma puede no requerir esa bandera; google tiene los detalles.

He probado el código usando mis propios nombres que he convertido aquí en "mylibrary" y "MyException" para ayudar a generalizar la solución. Con suerte, los errores de transcripción son pocos o ninguno. Los puntos principales son las directivas% init y% pythoncode junto con

static PyObject* pMyException; 

en la directiva de encabezado.

Espero que aclare la solución.

+0

Esto parece muy prometedor, lo intentaré. No es demasiado tarde, por cierto, porque por ahora solo estamos detectando RuntimeError y examinando el texto de los mensajes de error - ¡puaj! – Barry

+0

Es más genérico reemplazar "return NULL" que después de llamar "PyErr_SetString" en su ejemplo con una macro llamada "SWIG_fail", que siempre debería funcionar, es decir, independientemente del tipo de datos devuelto. – Hermes

8

Voy a añadir un poco aquí, ya que el ejemplo dado aquí ahora dice que "a excepción% (pitón)" es obsoleta ...

Ahora puede (a partir de trago 1.3.40, de todos modos) hacer una traducción totalmente genérica, independiente de las secuencias de comandos. Mi ejemplo sería:

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

Esto generará una excepción RuntimeError en cualquier lenguaje de programación compatible, incluyendo Python, sin conseguir la materia específica de pitón en sus otras cabeceras.

Debe poner este antes de las llamadas que desean este manejo de excepciones.

+2

+1, solo hay que asegurarse de tener% incluir excepción.i en su archivo de interfaz SWIG (introduciendo la macro SWIG_exception) –

-1

U también puede utilizar:

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

Ejemplo:

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

que genera para cada función de un bloque catch try:

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: Esto no funciona, Python se cuelga. '% capturas' no es suficiente a menos que especifique' throw() 'dentro de las declaraciones de funciones. Y el '% de capturas' es superfluo en ese caso. La documentación vinculada de SWIG solo menciona el uso de la directiva '% capturas' junto con un nombre de función. – Melebius

+0

% atrapa en realidad "excepciones envueltas" más funciones envueltas en swims luego% excepción. – hugo24

Cuestiones relacionadas