powered payton language developing compiler c++ python c

c++ - payton - python developing



¿Llamando a C/C++ desde Python? (14)

¿Cuál sería la forma más rápida de construir un enlace de Python a una biblioteca C o C ++?

(Estoy usando Windows si esto importa.)


Comencé mi viaje en el enlace Python <-> C ++ desde esta página, con el objetivo de vincular tipos de datos de alto nivel (vectores STL multidimensionales con listas de Python) :-)

Después de haber probado las soluciones basadas tanto en ctypes como boost.python (y no ser un ingeniero de software) las encontré complejas cuando se requiere la vinculación de tipos de datos de alto nivel, mientras que para SWIG SWIG me parece mucho más simple.

Este ejemplo utiliza SWIG, y se ha probado en Linux (pero SWIG está disponible y se usa ampliamente en Windows también).

El objetivo es poner a disposición de Python una función C ++ que tome una matriz en forma de un vector 2D STL y devuelva un promedio de cada fila (como un vector 1D STL).

El código en C ++ ("code.cpp") es el siguiente:

#include <vector> #include "code.h" using namespace std; vector<double> average (vector< vector<double> > i_matrix) { // Compute average of each row.. vector <double> averages; for (int r = 0; r < i_matrix.size(); r++){ double rsum = 0.0; double ncols= i_matrix[r].size(); for (int c = 0; c< i_matrix[r].size(); c++){ rsum += i_matrix[r][c]; } averages.push_back(rsum/ncols); } return averages; }

El encabezado equivalente ("code.h") es:

#ifndef _code #define _code #include <vector> std::vector<double> average (std::vector< std::vector<double> > i_matrix); #endif

Primero compilamos el código C ++ para crear un archivo de objeto:

g++ -c -fPIC code.cpp

Luego definimos un archivo de definición de interfaz SWIG ("code.i") para nuestras funciones C ++.

%module code %{ #include "code.h" %} %include "std_vector.i" namespace std { /* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */ %template(VecDouble) vector<double>; %template(VecVecdouble) vector< vector<double> >; } %include "code.h"

Usando SWIG, generamos un código fuente de la interfaz C ++ del archivo de definición de la interfaz SWIG.

swig -c++ -python code.i

Finalmente, compilamos el archivo fuente generado de la interfaz C ++ y enlazamos todo para generar una biblioteca compartida que Python puede importar directamente (lo que importa es "_"):

g++ -c -fPIC code_wrap.cxx -I/usr/include/python2.7 -I/usr/lib/python2.7 g++ -shared -Wl,-soname,_code.so -o _code.so code.o code_wrap.o

Ahora podemos usar la función en los scripts de Python:

#!/usr/bin/env python import code a= [[3,5,7],[8,10,12]] print a b = code.average(a) print "Assignment done" print a print b


Creo que cffi para python puede ser una opción.

El objetivo es llamar al código C de Python. Debería poder hacerlo sin aprender un tercer idioma: cada alternativa requiere que aprenda su propio idioma (Cython, SWIG) o API (ctypes). Así que intentamos asumir que conoces Python y C y minimizar los bits extra de API que necesitas aprender.

http://cffi.readthedocs.org/en/release-0.7/


Cython es definitivamente el camino a seguir, a menos que prevea escribir envoltorios de Java, en cuyo caso SWIG puede ser preferible.

Recomiendo usar la utilidad de línea de comando runcython , hace que el proceso de usar Cython sea extremadamente fácil. Si necesita pasar datos estructurados a C ++, eche un vistazo a la biblioteca protobuf de Google, es muy conveniente.

Aquí hay unos ejemplos mínimos que hice que usan ambas herramientas:

https://github.com/nicodjimenez/python2cpp

Espero que pueda ser un punto de partida útil.


Deberías echar un vistazo a Boost.Python . Aquí está la breve introducción tomada de su sitio web:

La biblioteca Boost Python es un marco para la interconexión de Python y C ++. Le permite exponer de forma rápida y sin problemas las funciones y objetos de las clases de C ++ a Python, y viceversa, sin usar herramientas especiales, solo su compilador de C ++. Está diseñado para envolver las interfaces de C ++ de manera no intrusiva, de modo que no tenga que cambiar el código de C ++ para envolverlo, por lo que Boost.Python es ideal para exponer bibliotecas de terceros a Python. El uso de técnicas avanzadas de metaprogramación de la biblioteca simplifica su sintaxis para los usuarios, de modo que el código de ajuste toma la apariencia de un tipo de lenguaje de definición de interfaz declarativa (IDL).


Echa un vistazo a pyrex o Cython . Son lenguajes similares a los de Python para interactuar entre C / C ++ y Python.


La forma más rápida de hacerlo es utilizar swig .

Ejemplo de tutorial SWIG:

/* File : example.c */ int fact(int n) { if (n <= 1) return 1; else return n*fact(n-1); }

Archivo de interfaz:

/* example.i */ %module example %{ /* Put header files here or function declarations like below */ extern int fact(int n); %} extern int fact(int n);

Construyendo un módulo Python en Unix:

swig -python example.i gcc -fPIC -c example.c example_wrap.c -I/usr/local/include/python2.7 gcc -shared example.o example_wrap.o -o _example.so

Uso:

>>> import example >>> example.fact(5) 120

Ten en cuenta que debes tener python-dev. También en algunos sistemas, los archivos de encabezado de Python estarán en /usr/include/python2.7 según la forma en que lo haya instalado.

Desde el tutorial:

SWIG es un compilador de C ++ bastante completo con soporte para casi todas las características de idioma. Esto incluye preprocesamiento, punteros, clases, herencia e incluso plantillas de C ++. SWIG también se puede utilizar para empaquetar estructuras y clases en clases de proxy en el idioma de destino, exponiendo la funcionalidad subyacente de una manera muy natural.


La pregunta es cómo llamar a una función C desde Python, si entendí correctamente. Entonces, las mejores opciones son Ctypes (BTW portátil en todas las variantes de Python).

>>> from ctypes import * >>> libc = cdll.msvcrt >>> print libc.time(None) 1438069008 >>> printf = libc.printf >>> printf("Hello, %s/n", "World!") Hello, World! 14 >>> printf("%d bottles of beer/n", 42) 42 bottles of beer 19

Para obtener una guía detallada, puede consultar el artículo de mi blog .


Nunca lo he usado, pero he oído cosas buenas sobre ctypes . Si está intentando usarlo con C ++, asegúrese de evadir la manipulación de nombres a través de la extern "C" . Gracias por el comentario, Florian Bösch.


Para C ++ moderno, use cppyy: http://cppyy.readthedocs.io/en/latest/

Se basa en Cling, el intérprete de C ++ para Clang / LLVM. Los enlaces están en tiempo de ejecución y no es necesario ningún idioma intermedio adicional. Gracias a Clang, soporta C ++ 17.

Instálalo usando pip:

$ pip install cppyy

Para proyectos pequeños, simplemente cargue la biblioteca relevante y los encabezados que le interesan. Por ejemplo, tome el código del ejemplo de ctypes en este hilo, pero divídalo en las secciones de encabezado y código:

$ cat foo.h class Foo { public: void bar(); }; $ cat foo.cpp #include "foo.h" #include <iostream> void Foo::bar() { std::cout << "Hello" << std::endl; }

Compilarlo

$ g++ -c -fPIC foo.cpp -o foo.o $ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o

y úsalo:

$ python >>> import cppyy >>> cppyy.include("foo.h") >>> cppyy.load_library("foo") >>> from cppyy.gbl import Foo >>> f = Foo() >>> f.bar() Hello >>>

Los proyectos grandes son compatibles con la carga automática de información de reflexión preparada y los fragmentos de cmake para crearlos, de modo que los usuarios de paquetes instalados puedan simplemente ejecutar:

$ python >>> import cppyy >>> f = cppyy.gbl.Foo() >>> f.bar() Hello >>>

Gracias a LLVM, son posibles funciones avanzadas, como la creación automática de instancias de plantillas. Para continuar con el ejemplo:

>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]() >>> v.push_back(f) >>> len(v) 1 >>> v[0].bar() Hello >>>

Nota: Soy el autor de cppyy.


Primero debes decidir cuál es tu propósito particular. La documentación oficial de Python sobre la extensión e incrustación del intérprete de Python se mencionó anteriormente, puedo agregar una buena descripción general de las extensiones binarias . Los casos de uso se pueden dividir en 3 categorías:

  • módulos aceleradores : para ejecutarse más rápido que el código Python puro equivalente se ejecuta en CPython.
  • Módulos de envoltura : para exponer las interfaces C existentes al código Python.
  • acceso al sistema de bajo nivel : para acceder a las funciones de nivel inferior del tiempo de ejecución de CPython, el sistema operativo o el hardware subyacente.

Con el fin de ofrecer una perspectiva más amplia para otros interesados ​​y, dado que su pregunta inicial es un poco vaga ("para una biblioteca C o C ++"), creo que esta información podría ser interesante para usted. En el enlace anterior puede leer sobre las desventajas de usar extensiones binarias y sus alternativas.

Aparte de las otras respuestas sugeridas, si desea un módulo acelerador, puede probar Numba . Funciona "al generar un código de máquina optimizado utilizando la infraestructura del compilador LLVM en el momento de la importación, el tiempo de ejecución o estáticamente (utilizando la herramienta pycc incluida)".




ctypes es parte de la biblioteca estándar y, por lo tanto, es más estable y está más disponible que el swig , que siempre me ha dado problems .

Con ctypes, debe satisfacer cualquier dependencia de tiempo de compilación en python, y su enlace funcionará en cualquier python que tenga ctypes, no solo contra el que se compiló.

Supongamos que tiene una clase de ejemplo de C ++ simple con la que desea hablar en un archivo llamado foo.cpp:

#include <iostream> class Foo{ public: void bar(){ std::cout << "Hello" << std::endl; } };

Como los ctypes solo pueden comunicarse con las funciones de C, debe proporcionarles las que las declaran como "C" externas

extern "C" { Foo* Foo_new(){ return new Foo(); } void Foo_bar(Foo* foo){ foo->bar(); } }

Luego tienes que compilar esto en una biblioteca compartida.

g++ -c -fPIC foo.cpp -o foo.o g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o

Y finalmente tienes que escribir tu envoltorio de python (por ejemplo, en fooWrapper.py)

from ctypes import cdll lib = cdll.LoadLibrary(''./libfoo.so'') class Foo(object): def __init__(self): self.obj = lib.Foo_new() def bar(self): lib.Foo_bar(self.obj)

Una vez que tengas eso puedes llamarlo como

f = Foo() f.bar() #and you will see "Hello" on the screen