¿Cuál sería la forma más rápida de construir un enlace de Python a una biblioteca C o C++?¿Llamar a C/C++ desde Python?
(estoy usando Windows si esto importa.)
¿Cuál sería la forma más rápida de construir un enlace de Python a una biblioteca C o C++?¿Llamar a C/C++ desde Python?
(estoy usando Windows si esto importa.)
Debería echar un vistazo a Boost.Python. Esta es la breve introducción tomado de su página web:
The Boost Python Library is a framework for interfacing Python and C++. It allows you to quickly and seamlessly expose C++ classes functions and objects to Python, and vice-versa, using no special tools -- just your C++ compiler. It is designed to wrap C++ interfaces non-intrusively, so that you should not have to change the C++ code at all in order to wrap it, making Boost.Python ideal for exposing 3rd-party libraries to Python. The library's use of advanced metaprogramming techniques simplifies its syntax for users, so that wrapping code takes on the look of a kind of declarative interface definition language (IDL).
La forma más rápida de hacerlo es utilizando SWIG.
Ejemplo de TRAGO tutorial: archivo
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
Interfaz:
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
La construcción de un módulo de 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
Tenga en cuenta que debe tener python-dev. También en algunos sistemas, los archivos de cabecera de Python estarán en /usr/include/python2.7 según la forma en que lo haya instalado.
Desde el tutorial:
SWIG is a fairly complete C++ compiler with support for nearly every language feature. This includes preprocessing, pointers, classes, inheritance, and even C++ templates. SWIG can also be used to package structures and classes into proxy classes in the target language — exposing the underlying functionality in a very natural manner.
nunca he usado, pero he oído cosas buenas sobre ctypes. Si intentas usarlo con C++, asegúrate de evadir el cambio de nombre a través del extern "C"
. Gracias por el comentario, Florian Bösch.
This paper, claiming Python to be all a scientist needs, básicamente dice: Primer prototipo de todo en Python. Luego, cuando necesite acelerar una parte, use SWIG y traduzca esta parte a C.
Uno de los documentos oficiales de Python contiene detalles sobre extending Python using C/C++. Incluso sin el uso de SWIG, es bastante sencillo y funciona perfectamente en Windows.
+1 para Cython! No he probado cffi, así que no puedo decir cuál es mejor, pero tuve muy buenas experiencias con Cython: todavía escribes el código de Python, pero puedes usar C en él. Fue un poco difícil para mí configurar el proceso de compilación con Cython, que luego expliqué en una publicación de blog: http://martinsosic.com/development/2016/02/08/wrapping-c-library-as-python- module.html – Martinsos
me gusta mucho ctypes, swig siempre tendían a darme problems. También ctypes tiene la ventaja de que no necesita satisfacer ninguna dependencia de tiempo de compilación en python, y su enlace funcionará en cualquier python que tenga ctypes, no solo aquel contra el que se compiló.
Supongamos que tiene una clase simple de ejemplo de C++ con la que desea hablar en un archivo llamado foo.CPP:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Desde ctypes sólo pueden hablar con funciones de C, es necesario proporcionar a los declarándolos como extern "C"
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
siguiente que tiene que compilar esto a una biblioteca compartida
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
Y por último hay que escribir su envoltorio pitón (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 tenga que se le puede llamar como
f = Foo()
f.bar() #and you will see "Hello" on the screen
Esto es más o menos lo que impulsa.python hace por usted en una sola llamada a función. –
ctypes está en la biblioteca estándar de Python, swig y boost no. Swig y boost dependen de módulos de extensión y, por lo tanto, están vinculados a versiones menores de python que los objetos compartidos independientes no tienen. construir un contenedor o aumentar las envolturas puede ser un problema, ctypes no necesita requisitos de compilación. –
boost se basa en la magia de la plantilla vudú y en un sistema de compilación completamente personalizado, ctypes se basa en la simplicidad. ctypes es dinámico, boost es estático. ctypes puede manejar diferentes versiones de bibliotecas. impulso no puede. –
creo CFFI para Python puede ser una opción.
The goal is to call C code from Python. You should be able to do so without learning a 3rd language: every alternative requires you to learn their own language (Cython, SWIG) or API (ctypes). So we tried to assume that you know Python and C and minimize the extra bits of API that you need to learn.
Creo que esto solo puede llamar c (no C++), aún +1 (realmente me gusta cffi). –
empecé mi viaje en el pitón < -> C++ vinculante de esta página, con el objetivo de vincular los tipos de datos de alto nivel (STL vectores multidimensionales con las listas de Python) :-)
Después de probar las soluciones basadas en ctypes y boost.python (y no ser un ingeniero de software) las encontré complejas cuando se requiere el enlace de tipos de datos de alto nivel, mientras que encontré SWIG mucho más simple para tales casos.
Este ejemplo utiliza, por lo tanto, SWIG, y se ha probado en Linux (pero SWIG está disponible y también se usa ampliamente en Windows).
El objetivo es hacer que una función C++ esté disponible para Python que toma una matriz en forma de un vector 2D STL y devuelve un promedio de cada fila (como un vector 1D STL).
El código en C++ ("code.cpp") es la 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;
}
La cabecera equivalente ("code.h") es:
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
primera Compilamos el C++ código para crear un archivo de objeto:
g++ -c -fPIC code.cpp
a continuación, definir un SWIG interface definition file ("code.i") para nuestro C++ funciones.
%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"
Utilizando SWIG, generamos un código fuente de interfaz C++ desde el archivo de definición de interfaz SWIG.
swig -c++ -python code.i
Finalmente compilar el archivo fuente de la interfaz generada C++ y enlace todo juntos para generar una biblioteca compartida que es importar directamente por el pitón (los asuntos "_"):
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 las secuencias de comandos 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
Una implementación de caso real donde en el código C++ los vectores stl se pasan como referencias no const y, por lo tanto, están disponibles por python como parámetros de salida: http://lobianco.org/antonello/personal:portfolio:portopt – Antonello
Primero debe decidir cuál es su propósito particular. La documentación oficial de Python en extending and embedding the Python interpreter se mencionó anteriormente, puedo agregar un buen overview of binary extensions. Los casos de uso se pueden dividir en 3 categorías:
Para dar una perspectiva más amplia para otros interesados y dado que su pregunta inicial es un poco vaga ("a una biblioteca C o C++"), creo que esta información podría serle interesante. En el enlace de arriba puede leer sobre las desventajas del uso de extensiones binarias y sus alternativas.
Aparte de las otras respuestas sugeridas, si quiere un módulo de acelerador, puede probar Numba. Funciona "generando código de máquina optimizado utilizando la infraestructura del compilador LLVM en tiempo de importación, tiempo de ejecución o estáticamente (utilizando la herramienta pycc incluida)".
La pregunta es cómo llamar a una función C desde Python, si entendí correctamente. Entonces, la mejor apuesta es Ctypes (por cierto, 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 una guía detallada que usted lo desea, puede hacer referencia a my blog article.
Puede valer la pena señalar que, aunque ctypes son portátiles, su código requiere una biblioteca de C específica de Windows. – Palec
Cython es definitivamente el camino a seguir, a menos que anticipe escribir envoltorios Java, en cuyo caso puede ser preferible SWIG.
Recomiendo usar la utilidad de línea de comandos runcython
, hace que el proceso de uso de Cython sea extremadamente fácil. Si necesita pasar datos estructurados a C++, eche un vistazo a la biblioteca de protobuf de Google, es muy conveniente.
que aquí hay una mínimos ejemplos que hice que utiliza ambas herramientas:
https://github.com/nicodjimenez/python2cpp
esperanza de que puede ser un punto de partida útil.
También hay pybind11
, que es como una versión ligera de Boost.Python y compatible con todos los modernos compiladores de C++:
Para C moderna ++, el uso 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 un lenguaje intermedio adicional. Gracias a Clang, es compatible con C++ 17.
instalarlo usando pip:
$ pip install cppyy
Para proyectos pequeños, sólo tiene que cargar la biblioteca relevante y los encabezados de los que está interesado en, por ejemplo. tomar el código del ejemplo ctypes es este hilo, pero dividida en las secciones de cabecera y de 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 lo utilizan:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
grandes proyectos son compatible con la carga automática de la información de reflexión preparada y los fragmentos de cmake para crearlos, de modo que los usuarios de los paquetes instalados simplemente puedan 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.
Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página vinculada cambia. - [De la crítica] (/ review/low-quality-posts/19014837) – duckmayr
Gracias por los comentarios. Actualicé la respuesta con las instrucciones de instalación y le mostré cómo usar el ejemplo presentado anteriormente en este hilo. –
Boost.Python es una de las librerías más fáciles de usar en Boost, para una API de llamada de función simple es bastante sencillo y proporciona un texto repetitivo que deberías escribir tú mismo. Es un poco más complicado si quieres exponer una API orientada a objetos. – jwfearn