Quel serait le moyen le plus rapide de construire une liaison Python vers une bibliothèque C ou C++ ?
(J'utilise Windows si cela a de l'importance).
Quel serait le moyen le plus rapide de construire une liaison Python vers une bibliothèque C ou C++ ?
(J'utilise Windows si cela a de l'importance).
ctypes fait partie de la bibliothèque standard, et est donc plus stable et plus largement disponible que le module gorgée qui a toujours eu tendance à me donner problèmes .
Avec ctypes, vous devez satisfaire toute dépendance de python au moment de la compilation, et votre liaison fonctionnera sur tout python possédant ctypes, et pas seulement sur celui contre lequel elle a été compilée.
Supposons que vous ayez une classe d'exemple C++ simple à laquelle vous voulez parler dans un fichier appelé foo.cpp :
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Puisque les ctypes ne peuvent parler qu'aux fonctions C, vous devez les fournir en les déclarant comme extern "C".
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
Ensuite, il faut compiler le tout dans une bibliothèque partagée
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
Et enfin, vous devez écrire votre wrapper python (par exemple dans 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)
Une fois que vous l'avez, vous pouvez l'appeler comme
f = Foo()
f.bar() #and you will see "Hello" on the screen
Ctypes est dans la bibliothèque standard de python, swig et boost ne le sont pas. Swig et boost s'appuient sur des modules d'extension et sont donc liés aux versions mineures de python, ce qui n'est pas le cas des objets partagés indépendants. La construction d'un wrapper de swig ou de boost peut s'avérer pénible, alors que ctypes n'impose aucune condition de construction.
Boost repose sur la magie voodoo des templates et un système de construction entièrement personnalisé, ctypes repose sur la simplicité. ctypes est dynamique, boost est statique. ctypes peut gérer différentes versions de bibliothèques. boost ne le peut pas.
Vous devriez jeter un coup d'œil à Boost.Python . Voici la courte introduction tirée de leur site web :
La bibliothèque Python de Boost est un cadre permettant d'interfacer Python et C++. Elle vous permet d'exposer rapidement et de manière transparente des classes C++. fonctions et objets C++ à Python, et vice-versa, sans utiliser d'outils spéciaux. outils spéciaux, mais simplement votre compilateur C++. Il est conçu pour envelopper les interfaces C de manière non intrusive, de sorte que vous n'ayez pas à modifier le code C++ pour l'envelopper. C++ pour l'envelopper, ce qui rend Boost.Python idéal pour exposer des bibliothèques bibliothèques tierces à Python. L'utilisation par la bibliothèque de techniques avancées de simplifie sa syntaxe pour les utilisateurs. l'utilisateur, de sorte que le code d'habillage ressemble à une sorte de langage de définition déclaratif (IDL).
Boost.Python est l'une des bibliothèques les plus conviviales de Boost. Pour une simple API d'appel de fonction, elle est assez simple et fournit un modèle que vous auriez dû écrire vous-même. C'est un peu plus compliqué si vous voulez exposer une API orientée objet.
Boost.Python est la pire chose imaginable. À chaque nouvelle machine et à chaque mise à jour, il s'accompagne de problèmes de liaison.
La façon la plus rapide de le faire est d'utiliser SWIG .
Exemple de SWIG tutoriel :
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
Fichier d'interface :
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
Construction d'un module Python sous 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
Utilisation :
>>> import example
>>> example.fact(5)
120
Notez que vous devez disposer de python-dev. De plus, sur certains systèmes, les fichiers d'en-tête de python se trouveront dans /usr/include/python2.7, selon la manière dont vous l'avez installé.
Du tutoriel :
SWIG est un compilateur C++ assez complet qui prend en charge presque toutes les fonctionnalités du langage. Cela inclut le prétraitement, les pointeurs, les classes, l'héritage et même les modèles C++. SWIG peut également être utilisé pour empaqueter des structures et des classes dans des classes proxy dans la langue cible - exposant la fonctionnalité sous-jacente d'une manière très naturelle.
J'ai commencé mon voyage dans la liaison Python <-> C++ à partir de cette page, avec pour objectif de lier des types de données de haut niveau (vecteurs STL multidimensionnels avec des listes Python) :-)
Ayant essayé les solutions basées sur les deux ctypes y boost.python (et n'étant pas ingénieur en logiciel), je les ai trouvés complexes lorsque la liaison de types de données de haut niveau est requise, tandis que j'ai trouvé SWIG beaucoup plus simple pour de tels cas.
Cet exemple utilise donc SWIG, et il a été testé sous Linux (mais SWIG est disponible et largement utilisé sous Windows également).
L'objectif est de rendre une fonction C++ disponible pour Python qui prend une matrice sous forme de vecteur STL 2D et renvoie une moyenne de chaque ligne (sous forme de vecteur STL 1D).
Le code en C++ ("code.cpp") est le suivant :
#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;
}
L'en-tête équivalent ("code.h") est :
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
Nous compilons d'abord le code C++ pour créer un fichier objet :
g++ -c -fPIC code.cpp
Nous définissons ensuite un Fichier de définition d'interface SWIG ("code.i") pour nos fonctions 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"
En utilisant SWIG, nous générons un code source d'interface C++ à partir du fichier de définition d'interface SWIG .
swig -c++ -python code.i
Enfin, nous compilons le fichier source de l'interface C++ générée et lions le tout pour générer une bibliothèque partagée directement importable par Python (le "_" importe) :
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
Nous pouvons maintenant utiliser la fonction dans les 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
Un cas réel d'implémentation où dans le code C++ les vecteurs stl sont passés comme des références non constantes et donc disponibles pour python comme paramètres de sortie : lobianco.org/antonello/personal:portfolio:portopt
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.