doSimple Python et SWIG
SWIG pour interfacer une bibliothèque C avec Python
SWIG (Simplified Wrapper and Interface Generator) permet de connecter des langages interprétés comme Python, Perl ou Ruby avec C/C++. SWIG utilise les fichiers header de ces langages pour générer un emballage (wrapper) qui permet d’accéder aux méthodes, structures et objets des langages C/C++.
L’interêt de SWIG est de profiter de l’efficacité des langages compilés C/C++ et de la diversité des différentes bibliothèques construites dans ces langages.
Ce document montre par un exemple comment récupérer des stuctures de données du langage C.
Exemple
Prenons le cas d’une bibliotèque très simple qui manipule des vecteurs.
Télécharger les fichiers d’exemples.
Fichier header "biblio.h"
Le fichier header décris les structures de données et le prototype des fonctions de la bibliothèque
struct Vector
{
double x,y,z;
// ce tableau nous sera utile pour
// détécter des fuites de mémoire
double faketab[100000];
};
struct Vector * getUnitVector();
void deleteVector(struct Vector *v);
Fichier de la bibliothèque "biblio.c"
En suivant une syntaxe particulière, il est possible de définir
un constructeur et un destructeur pour notre structure. Si
on ne le fait pas SWIG les génère automatiquement à partir
du fichier header.
#include "biblio.h" #include <stdlib.h> // constructeur de la structure pour SWIG struct Vector * new_Vector() { struct Vector *v; v = (struct Vector *)malloc(sizeof(struct Vector)); v->x = v->y = v->z = 0; return v; } // destructeur de la structure pour SWIG void delete_Vector(struct Vector *v) { free(v); } // crée un vecteur unitaire struct Vector * getUnitVector() { struct Vector *v = new_Vector(); v->x=1; return v; } // supprime un vecteur donné void deleteVector(struct Vector *v) { free(v); }
Fichier d’interface "biblio.i"
Ce fichier d’interface indique à SWIG quel sera le nom du wrapper ainsi que les fichiers sources qu’il doit analyser.
%module biblio // le code suivant est simplement copié // au début de celui du "wrapper" généré %{ #include "biblio.h" %} // le fichier à analyser %include "biblio.h" // permet d’enregistrer le constructeur // et le destructeur auprès de SWIG %extend Vector { Vector(); ~Vector(); };
Le Makefile
Ce fichier de commande suivant génère les fichiers wrapper en C et en Python puis compile la bibliotèque.
SHELL = /bin/sh
WRAPPER = biblio
make:
swig -python ${WRAPPER}.i
gcc -c ${WRAPPER}.c ${WRAPPER}_wrap.c \
-I /usr/include/python2.4/
rm ${WRAPPER}_wrap.c
ld -shared ${WRAPPER}.o \
${WRAPPER}_wrap.o -o _${WRAPPER}.so
rm ${WRAPPER}.o ${WRAPPER}_wrap.o
Utilisation du wrapper avec Python
Ce script Python permet de montrer comment utiliser cette bibliotèque C afin d'éviter des fuites de mémoire.
Ce code crée 1000 vecteurs, une fois en utilisant la fonction getUnitVector, une fois en
utilisant le constructeur.
Si on utilise le constructeur de l’objet Python fourni par le wrapper le destructeur de l’objet Python appelera le destructeur de la bibliothèque Vector automatiquement. Par contre si on utilise la fonction getUnitVector il faut supprimer
explicitement la structure afin d'éviter une fuite de mémoire.
#!/usr/bin/python # -*- coding: utf8 -*- import sys import biblio import time def main(args): i=0 print 'boucle de test pour le ramasse-miettes' print 'controlez l\'etat de la memoire' while(i<1000): # sans utiliser le constructeur if i%2==0:v = biblio.getUnitVector() # en utilisant le constructeur else:v = biblio.Vector() time.sleep(0.01) # on supprime explicitement le vecteur # si il a n’a pas été créé avec le # constructeur, la propriété "thisown" # donne cette information if v.thisown==0:biblio.deleteVector(v) i=i+1 print 'boucle terminee' if __name__=="__main__": main(sys.argv)
Remarques
En utilisant ce script on constate que l'état de la mémoire est stable. Si la ligne biblio.deleteVector de suppression explicite est supprimée, on constate une augmentation rapide de l’utilisation mémoire qui ne sera pas rendue. Il y a donc une fuite mémoire.
Quand Python n’a plus de référence sur un objet le Garbage Collector supprime l’objet Python. Le destructeur de cet objet Python commande à la bibliothèque C/C++ la libération de la mémoire via le destructeur programmé en C.
On peut donc profiter pleinement du Garbage Collector de Python tant qu’on respecte la règle de ne pas commander la création d’une structure dans une fonction C/C++.
Liens connexes
- Auteur
- Batiste Bieler
- Licence
- Les exemples de code sont sous LGPL
- Vous pouvez distribuer librement l’ensemble de ces documents en respectant le droit d’auteur.