python c++ c swig cython

Extendiendo python-para tragar, no para tragar o Cython



c++ swig (9)

Debería considerar Boost.Python si no está planeando generar enlaces para otros lenguajes también con swig.

Si tiene muchas funciones y clases para enlazar, Py ++ es una gran herramienta que genera automáticamente el código necesario para realizar las vinculaciones.

Pybindgen también puede ser una opción, pero es un proyecto nuevo y menos completo que Boost.Python.

Editar:

Quizás deba ser más explícito acerca de pro y contra.

  • Trago:

    pro: puede generar enlaces para muchos lenguajes de scripting.

    contra: no me gusta la forma en que funciona el analizador. No sé si hizo algún progreso, pero hace dos años el analizador C ++ era bastante limitado. La mayoría de las veces tuve que copiar / pasar mis encabezados .h agregar un % caracteres y dar pistas adicionales al analizador de tragos.

    También era necesario lidiar con Python C-API de vez en cuando para conversiones de tipo complicadas (no tan).

    No lo estoy usando más.

  • Boost.Python:

    pro: es una biblioteca muy completa. Le permite hacer casi todo lo que es posible con C-API, pero en C ++. Nunca tuve que escribir código C-API con esta biblioteca. Tampoco encontré ningún error debido a la biblioteca. El código para enlaces funciona como un amuleto o compilación de basura.

    Probablemente sea una de las mejores soluciones actualmente disponibles si ya tiene alguna biblioteca C ++ para vincular. Pero si solo tiene una pequeña función C para reescribir, probablemente lo intente con Cython.

    Contras: si no tienes una biblioteca precompilada de Boost.Python vas a usar Bjam (tipo de reemplazo). Realmente odio Bjam y su sintaxis.

    Las bibliotecas de Python creadas con BP tienden a volverse obesas. También lleva mucho tiempo compilarlos.

  • Py ++ (descontinuado): es Boost.Python hecho fácil. Py ++ usa un analizador de C ++ para leer su código y luego genera el código de Boost.Python automáticamente. También tiene un gran apoyo de su autor (no, no soy yo ;-)).

    Contras: solo los problemas debidos a Boost.Python. Actualización: a partir de 2014, este proyecto parece descontinuado.

  • Pybindgen:

    Genera el código que trata con el C-API. Puede describir funciones y clases en un archivo Python, o dejar que Pybindgen lea sus encabezados y generar enlaces automáticamente (para esto utiliza pygccxml, una biblioteca python escrita por el autor de Py ++).

    contras: es un proyecto joven, con un equipo más pequeño que Boost.Python. Todavía hay algunas limitaciones: no puede usar herencia múltiple para sus clases de C ++, Callbacks (no automáticamente, se puede escribir un código de manejo de devolución de llamada personalizado). Traducción de excepciones de Python a C.

    Definitivamente vale la pena mirarlo bien.

  • Una nueva: el 2009/01/20 el autor de Py ++ anunció un nuevo paquete para interconectar el código C / C ++ con python. Se basa en ctypes. ¡No lo intenté ya, pero lo haré! Nota: este proyecto parece descontinuado, como Py ++.

  • CFFI : No sabía la existencia de esto hasta hace muy poco, así que por ahora no puedo dar mi opinión. Parece que puede definir funciones C en cadenas Python y llamarlas directamente desde el mismo módulo Python.

  • Cython : este es el método que estoy usando actualmente en mis proyectos. Básicamente, usted escribe el código en archivos especiales .pyx. Esos archivos se compilan (traducen) en código C, que a su vez se compilan en módulos CPython. El código de Cython puede parecerse a Python normal (y, de hecho, Python puro son archivos válidos .pyx de Cython), pero también puede obtener más información, como tipos de variables. Esta escritura opcional permite que Cython genere un código C más rápido. El código en los archivos de Cython puede invocar tanto las funciones puras de Python como las funciones C y C ++ (y también los métodos C ++).

    Me tomó algo de tiempo pensar en Cython, que en el mismo código, llamar a la función C y C ++, mezclar las variables Python y C, y así sucesivamente. Pero es un lenguaje muy poderoso, con una comunidad activa (en 2014) y amigable.

Encontré el cuello de botella en mi código python, jugué con psycho, etc. Luego decidí escribir la extensión ac / c ++ para el rendimiento.

Con la ayuda de Swig casi no necesitas preocuparte por los argumentos, etc. Todo funciona bien.

Ahora mi pregunta: swig crea un py-file bastante grande que hace un montón de ''comprobaciones'' y ''PySwigObject'' antes de llamar al código .pyd o .so real.

¿Alguno de ustedes tiene alguna experiencia sobre si hay algo más de rendimiento que obtener si escribe a mano este archivo o deja que lo haga?


Antes de renunciar a su código python, eche un vistazo a ShedSkin . Reclaman un mejor rendimiento que Psyco en algún código (y también afirman que todavía es experimental).

De lo contrario, hay varias opciones para enlazar el código C / C ++ a python.

Boost es largo de compilar, pero es realmente la solución más flexible y fácil de usar.

Nunca he usado SWIG, pero comparado con boost, no es tan flexible como su framework de enlaces genéricos, no es un framework dedicado a python.

La siguiente elección es Pyrex . Permite escribir código pseudo python que se compila como una extensión C.


Si no es una gran extensión, boost :: python también podría ser una opción, se ejecuta más rápido que swig, porque controlas lo que está sucediendo, pero tardará más en dev.

De todos modos, la sobrecarga de Swig es aceptable si la cantidad de trabajo dentro de una sola llamada es lo suficientemente grande. Por ejemplo, si usted emite un bloque lógico de tamaño mediano que desea mover a C / C ++, pero ese bloque se llama dentro de un círculo cerrado, con frecuencia, puede que tenga que evitar el trago, pero realmente no puedo pensar de cualquier ejemplo del mundo real a excepción de los sombreadores de gráficos con guiones.


Usar Cython es bastante bueno. Puede escribir su extensión C con una sintaxis similar a Python y hacer que genere código C. Texto repetitivo incluido. Como ya tienes el código en python, solo tienes que hacer algunos cambios en el código de cuello de botella y se generará código C a partir de él.

Ejemplo. hello.pyx :

cdef int hello(int a, int b): return a + b

Eso genera 601 líneas de código repetitivo:

/* Generated by Cython 0.10.3 on Mon Jan 19 08:24:44 2009 */ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "structmember.h" #ifndef PY_LONG_LONG #define PY_LONG_LONG LONG_LONG #endif #ifndef DL_EXPORT #define DL_EXPORT(t) t #endif #if PY_VERSION_HEX < 0x02040000 #define METH_COEXIST 0 #endif #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #define PyInt_FromSsize_t(z) PyInt_FromLong(z) #define PyInt_AsSsize_t(o) PyInt_AsLong(o) #define PyNumber_Index(o) PyNumber_Int(o) #define PyIndex_Check(o) PyNumber_Check(o) #endif #if PY_VERSION_HEX < 0x02060000 #define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) #define PyVarObject_HEAD_INIT(type, size) / PyObject_HEAD_INIT(type) size, #define PyType_Modified(t) typedef struct { void *buf; PyObject *obj; Py_ssize_t len; Py_ssize_t itemsize; int readonly; int ndim; char *format; Py_ssize_t *shape; Py_ssize_t *strides; Py_ssize_t *suboffsets; void *internal; } Py_buffer; #define PyBUF_SIMPLE 0 #define PyBUF_WRITABLE 0x0001 #define PyBUF_LOCK 0x0002 #define PyBUF_FORMAT 0x0004 #define PyBUF_ND 0x0008 #define PyBUF_STRIDES (0x0010 | PyBUF_ND) #define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) #define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) #define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) #define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) #endif #if PY_MAJOR_VERSION < 3 #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" #else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #endif #if PY_MAJOR_VERSION >= 3 #define Py_TPFLAGS_CHECKTYPES 0 #define Py_TPFLAGS_HAVE_INDEX 0 #endif #if (PY_VERSION_HEX < 0x02060000) || (PY_MAJOR_VERSION >= 3) #define Py_TPFLAGS_HAVE_NEWBUFFER 0 #endif #if PY_MAJOR_VERSION >= 3 #define PyBaseString_Type PyUnicode_Type #define PyString_Type PyBytes_Type #define PyInt_Type PyLong_Type #define PyInt_Check(op) PyLong_Check(op) #define PyInt_CheckExact(op) PyLong_CheckExact(op) #define PyInt_FromString PyLong_FromString #define PyInt_FromUnicode PyLong_FromUnicode #define PyInt_FromLong PyLong_FromLong #define PyInt_FromSize_t PyLong_FromSize_t #define PyInt_FromSsize_t PyLong_FromSsize_t #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG #define PyInt_AsSsize_t PyLong_AsSsize_t #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) #else #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y) #define PyBytes_Type PyString_Type #endif #if PY_MAJOR_VERSION >= 3 #define PyMethod_New(func, self, klass) PyInstanceMethod_New(func) #endif #if !defined(WIN32) && !defined(MS_WINDOWS) #ifndef __stdcall #define __stdcall #endif #ifndef __cdecl #define __cdecl #endif #else #define _USE_MATH_DEFINES #endif #ifdef __cplusplus #define __PYX_EXTERN_C extern "C" #else #define __PYX_EXTERN_C extern #endif #include <math.h> #define __PYX_HAVE_API__helloworld #ifdef __GNUC__ #define INLINE __inline__ #elif _WIN32 #define INLINE __inline #else #define INLINE #endif typedef struct {PyObject **p; char *s; long n; char is_unicode; char intern; char is_identifier;} __Pyx_StringTabEntry; /*proto*/ static int __pyx_skip_dispatch = 0; /* Type Conversion Predeclarations */ #if PY_MAJOR_VERSION < 3 #define __Pyx_PyBytes_FromString PyString_FromString #define __Pyx_PyBytes_AsString PyString_AsString #else #define __Pyx_PyBytes_FromString PyBytes_FromString #define __Pyx_PyBytes_AsString PyBytes_AsString #endif #define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) static INLINE int __Pyx_PyObject_IsTrue(PyObject* x); static INLINE PY_LONG_LONG __pyx_PyInt_AsLongLong(PyObject* x); static INLINE unsigned PY_LONG_LONG __pyx_PyInt_AsUnsignedLongLong(PyObject* x); static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b); #define __pyx_PyInt_AsLong(x) (PyInt_CheckExact(x) ? PyInt_AS_LONG(x) : PyInt_AsLong(x)) #define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) static INLINE unsigned char __pyx_PyInt_unsigned_char(PyObject* x); static INLINE unsigned short __pyx_PyInt_unsigned_short(PyObject* x); static INLINE char __pyx_PyInt_char(PyObject* x); static INLINE short __pyx_PyInt_short(PyObject* x); static INLINE int __pyx_PyInt_int(PyObject* x); static INLINE long __pyx_PyInt_long(PyObject* x); static INLINE signed char __pyx_PyInt_signed_char(PyObject* x); static INLINE signed short __pyx_PyInt_signed_short(PyObject* x); static INLINE signed int __pyx_PyInt_signed_int(PyObject* x); static INLINE signed long __pyx_PyInt_signed_long(PyObject* x); static INLINE long double __pyx_PyInt_long_double(PyObject* x); #ifdef __GNUC__ /* Test for GCC > 2.95 */ #if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else /* __GNUC__ > 2 ... */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ > 2 ... */ #else /* __GNUC__ */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ */ static PyObject *__pyx_m; static PyObject *__pyx_b; static PyObject *__pyx_empty_tuple; static int __pyx_lineno; static int __pyx_clineno = 0; static const char * __pyx_cfilenm= __FILE__; static const char *__pyx_filename; static const char **__pyx_f; static void __Pyx_AddTraceback(const char *funcname); /*proto*/ /* Type declarations */ /* Module declarations from helloworld */ static int __pyx_f_10helloworld_hello(int, int); /*proto*/ /* Implementation of helloworld */ /* "/home/nosklo/devel/ctest/hello.pyx":1 * cdef int hello(int a, int b): # <<<<<<<<<<<<<< * return a + b * */ static int __pyx_f_10helloworld_hello(int __pyx_v_a, int __pyx_v_b) { int __pyx_r; /* "/home/nosklo/devel/ctest/hello.pyx":2 * cdef int hello(int a, int b): * return a + b # <<<<<<<<<<<<<< * */ __pyx_r = (__pyx_v_a + __pyx_v_b); goto __pyx_L0; __pyx_r = 0; __pyx_L0:; return __pyx_r; } static struct PyMethodDef __pyx_methods[] = { {0, 0, 0, 0} }; static void __pyx_init_filenames(void); /*proto*/ #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef __pyx_moduledef = { PyModuleDef_HEAD_INIT, "helloworld", 0, /* m_doc */ -1, /* m_size */ __pyx_methods /* m_methods */, NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL /* m_free */ }; #endif static int __Pyx_InitCachedBuiltins(void) { return 0; return -1; } static int __Pyx_InitGlobals(void) { return 0; return -1; } #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC inithelloworld(void); /*proto*/ PyMODINIT_FUNC inithelloworld(void) #else PyMODINIT_FUNC PyInit_helloworld(void); /*proto*/ PyMODINIT_FUNC PyInit_helloworld(void) #endif { __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Library function declarations ---*/ __pyx_init_filenames(); /*--- Initialize various global constants etc. ---*/ if (unlikely(__Pyx_InitGlobals() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Module creation code ---*/ #if PY_MAJOR_VERSION < 3 __pyx_m = Py_InitModule4("helloworld", __pyx_methods, 0, 0, PYTHON_API_VERSION); #else __pyx_m = PyModule_Create(&__pyx_moduledef); #endif if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; #if PY_MAJOR_VERSION < 3 Py_INCREF(__pyx_m); #endif __pyx_b = PyImport_AddModule(__Pyx_BUILTIN_MODULE_NAME); if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; /*--- Builtin init code ---*/ if (unlikely(__Pyx_InitCachedBuiltins() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_skip_dispatch = 0; /*--- Global init code ---*/ /*--- Function export code ---*/ /*--- Type init code ---*/ /*--- Type import code ---*/ /*--- Function import code ---*/ /*--- Execution code ---*/ /* "/home/nosklo/devel/ctest/hello.pyx":1 * cdef int hello(int a, int b): # <<<<<<<<<<<<<< * return a + b * */ #if PY_MAJOR_VERSION < 3 return; #else return __pyx_m; #endif __pyx_L1_error:; __Pyx_AddTraceback("helloworld"); #if PY_MAJOR_VERSION >= 3 return NULL; #endif } static const char *__pyx_filenames[] = { "hello.pyx", }; /* Runtime support code */ static void __pyx_init_filenames(void) { __pyx_f = __pyx_filenames; } #include "compile.h" #include "frameobject.h" #include "traceback.h" static void __Pyx_AddTraceback(const char *funcname) { PyObject *py_srcfile = 0; PyObject *py_funcname = 0; PyObject *py_globals = 0; PyObject *empty_string = 0; PyCodeObject *py_code = 0; PyFrameObject *py_frame = 0; #if PY_MAJOR_VERSION < 3 py_srcfile = PyString_FromString(__pyx_filename); #else py_srcfile = PyUnicode_FromString(__pyx_filename); #endif if (!py_srcfile) goto bad; if (__pyx_clineno) { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno); #else py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno); #endif } else { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromString(funcname); #else py_funcname = PyUnicode_FromString(funcname); #endif } if (!py_funcname) goto bad; py_globals = PyModule_GetDict(__pyx_m); if (!py_globals) goto bad; #if PY_MAJOR_VERSION < 3 empty_string = PyString_FromStringAndSize("", 0); #else empty_string = PyBytes_FromStringAndSize("", 0); #endif if (!empty_string) goto bad; py_code = PyCode_New( 0, /*int argcount,*/ #if PY_MAJOR_VERSION >= 3 0, /*int kwonlyargcount,*/ #endif 0, /*int nlocals,*/ 0, /*int stacksize,*/ 0, /*int flags,*/ empty_string, /*PyObject *code,*/ __pyx_empty_tuple, /*PyObject *consts,*/ __pyx_empty_tuple, /*PyObject *names,*/ __pyx_empty_tuple, /*PyObject *varnames,*/ __pyx_empty_tuple, /*PyObject *freevars,*/ __pyx_empty_tuple, /*PyObject *cellvars,*/ py_srcfile, /*PyObject *filename,*/ py_funcname, /*PyObject *name,*/ __pyx_lineno, /*int firstlineno,*/ empty_string /*PyObject *lnotab*/ ); if (!py_code) goto bad; py_frame = PyFrame_New( PyThreadState_GET(), /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ py_globals, /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); if (!py_frame) goto bad; py_frame->f_lineno = __pyx_lineno; PyTraceBack_Here(py_frame); bad: Py_XDECREF(py_srcfile); Py_XDECREF(py_funcname); Py_XDECREF(empty_string); Py_XDECREF(py_code); Py_XDECREF(py_frame); } /* Type Conversion Functions */ static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b) { Py_ssize_t ival; PyObject* x = PyNumber_Index(b); if (!x) return -1; ival = PyInt_AsSsize_t(x); Py_DECREF(x); return ival; } static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { if (x == Py_True) return 1; else if (x == Py_False) return 0; else return PyObject_IsTrue(x); } static INLINE PY_LONG_LONG __pyx_PyInt_AsLongLong(PyObject* x) { if (PyInt_CheckExact(x)) { return PyInt_AS_LONG(x); } else if (PyLong_CheckExact(x)) { return PyLong_AsLongLong(x); } else { PY_LONG_LONG val; PyObject* tmp = PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1; val = __pyx_PyInt_AsLongLong(tmp); Py_DECREF(tmp); return val; } } static INLINE unsigned PY_LONG_LONG __pyx_PyInt_AsUnsignedLongLong(PyObject* x) { if (PyInt_CheckExact(x)) { long val = PyInt_AS_LONG(x); if (unlikely(val < 0)) { PyErr_SetString(PyExc_TypeError, "Negative assignment to unsigned type."); return (unsigned PY_LONG_LONG)-1; } return val; } else if (PyLong_CheckExact(x)) { return PyLong_AsUnsignedLongLong(x); } else { PY_LONG_LONG val; PyObject* tmp = PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1; val = __pyx_PyInt_AsUnsignedLongLong(tmp); Py_DECREF(tmp); return val; } } static INLINE unsigned char __pyx_PyInt_unsigned_char(PyObject* x) { if (sizeof(unsigned char) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); unsigned char val = (unsigned char)long_val; if (unlikely((val != long_val) || (long_val < 0))) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to unsigned char"); return (unsigned char)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE unsigned short __pyx_PyInt_unsigned_short(PyObject* x) { if (sizeof(unsigned short) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); unsigned short val = (unsigned short)long_val; if (unlikely((val != long_val) || (long_val < 0))) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to unsigned short"); return (unsigned short)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE char __pyx_PyInt_char(PyObject* x) { if (sizeof(char) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); char val = (char)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to char"); return (char)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE short __pyx_PyInt_short(PyObject* x) { if (sizeof(short) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); short val = (short)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to short"); return (short)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE int __pyx_PyInt_int(PyObject* x) { if (sizeof(int) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); int val = (int)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to int"); return (int)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE long __pyx_PyInt_long(PyObject* x) { if (sizeof(long) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); long val = (long)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to long"); return (long)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE signed char __pyx_PyInt_signed_char(PyObject* x) { if (sizeof(signed char) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); signed char val = (signed char)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed char"); return (signed char)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE signed short __pyx_PyInt_signed_short(PyObject* x) { if (sizeof(signed short) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); signed short val = (signed short)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed short"); return (signed short)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE signed int __pyx_PyInt_signed_int(PyObject* x) { if (sizeof(signed int) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); signed int val = (signed int)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed int"); return (signed int)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE signed long __pyx_PyInt_signed_long(PyObject* x) { if (sizeof(signed long) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); signed long val = (signed long)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed long"); return (signed long)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE long double __pyx_PyInt_long_double(PyObject* x) { if (sizeof(long double) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); long double val = (long double)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to long double"); return (long double)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } }


Hay dragones aquí. No tragues, no aumentes. Para cualquier proyecto complicado, el código que tiene que completar usted mismo para que funcionen se vuelve inmanejable rápidamente. Si se trata de una API C simple para su biblioteca (sin clases), puede usar ctypes. Será fácil e indoloro, y no tendrá que pasar horas revisando la documentación de estos proyectos de envoltura laberínticos tratando de encontrar la pequeña nota sobre la función que necesita.


Como le preocupan la velocidad y los gastos generales, sugiero considerar PyBindGen .

Tengo experiencia en usarlo para envolver una gran biblioteca interna de C ++. Después de probar SWIG, SIP y Boost.Python prefiero PyBindGen por las siguientes razones:

  1. Un contenedor PyBindGen es puro-Python, no es necesario aprender otro formato de archivo
  2. PyBindGen genera llamadas API de Python C directamente, no existe una capa indirecta de robo de velocidad como SWIG.
  3. El código C generado es limpio y simple de entender. También me gusta Cython, pero tratar de leer su salida C puede ser difícil a veces.
  4. Los contenedores de secuencia STL son compatibles (utilizamos muchos std :: vectores)

Una observación: en base a la evaluación comparativa realizada por los desarrolladores de pybindgen, no hay una diferencia significativa entre boost.python y swig. No he hecho mi propia evaluación comparativa para verificar cuánto de esto depende del uso adecuado de la funcionalidad boost.python.

Tenga en cuenta también que puede haber una razón por la cual pybindgen parece ser, en general, bastante más rápido que swig y boost.python: puede que no produzca un enlace tan versátil como los otros dos. Por ejemplo, propagación de excepciones, comprobación de tipos de argumentos de llamadas, etc. Todavía no he tenido la oportunidad de utilizar pybindgen, pero tengo la intención de hacerlo.

Boost es, en general, un paquete bastante grande para instalar, y la última vez que vi no puedes simplemente instalar boost python, realmente necesitas toda la biblioteca de Boost. Como otros han mencionado, la compilación será lenta debido al uso intensivo de la programación de plantillas, lo que también significa mensajes de error típicamente bastante crípticos en tiempo de compilación.

Resumen: dado lo fácil que es instalar SWIG y usarlo, que genera un enlace decente que es robusto y versátil, y que un archivo de interfaz permite que su DLL C ++ esté disponible en varios otros lenguajes como LUA, C # y Java, yo preferiría sobre boost.python. Pero a menos que realmente necesites soporte en varios idiomas, analizaría PyBindGen por su supuesta velocidad y prestaría mucha atención a la robustez y versatilidad de la vinculación que genera.


SWIG 2.0.4 ha presentado una nueva opción de compilación que mejora el rendimiento. Hice algunos benchmarking usando un programa de ejemplo que hace muchas llamadas rápidas a una extensión de C ++. Construí la extensión usando boost.python, PyBindGen, SIP y SWIG con y sin la opción -builtin. Estos son los resultados (promedio de 100 carreras):

SWIG with -builtin 2.67s SIP 2.70s PyBindGen 2.74s boost.python 3.07s SWIG without -builtin 4.65s

SWIG solía ser más lento. Con la nueva opción -builtin, SWIG parece ser el más rápido.


Sin duda, siempre obtendrá una ganancia de rendimiento haciendo esto a mano, pero la ganancia será muy pequeña en comparación con el esfuerzo requerido para hacer esto. No tengo ninguna figura para darle, pero no lo recomiendo, porque tendrá que mantener la interfaz a mano, ¡y esta no es una opción si su módulo es grande!

Hizo lo correcto al elegir usar un lenguaje de scripting porque quería un desarrollo rápido. De esta manera has evitado el síndrome de optimización temprana, y ahora quieres optimizar las partes con cuello de botella, ¡genial! Pero si usted hace la interfaz C / python a mano, seguramente caerá en el síndrome de optimización temprana.

Si desea algo con menos código de interfaz, puede pensar en crear un dll a partir de su código C y usar esa biblioteca directamente desde python con cstruct .

Considera también Cython si quieres usar solo código python en tu programa.