Présentation de SWIG - Linux Magazine France No 8

Présentation de SWIG

Ce document est une rapide présentation de SWIG, un outil qui permet d'étendre facilement des langages de commandes comme Perl, Tcl, Python à l'aide de fonctions C ou d'objets C++.

 

1 Position du problème

1.1 Les langages de commandes

Unix en général et Linux en particulier sont pourvus de nombreux "langages de commandes" (ou LC), sortes de "super-shell" : Perl, Python, Tcl, Guile... Ces langages interprétés sont très agréables grâce à leurs structures de données puissantes (listes dynamiques, tableaux associatifs...) qui permettent d'écrire des algorithmes complexes de façon générique et concise. Ils fournissent des interfaces avec le système d'exploitation, ce qui permet de les utiliser pour des scripts autrefois réservés au shell. Ils offrent également accès à des librairies de widget (le plus souvent Tk), ce qui permet de construire rapidement et facilement de petites interfaces graphiques. Ils disposent également de riches bibliothèques permettant de toucher à tous les domaines d'application.

Dans la suite, je parlerai surtout de Python, qui est mon préféré, mais tout ce qui sera dit pourra s'appliquer (quasi) directement à Tcl, Perl ou encore Guile.

1.2 Oui, mais....

Ce tableau idyllique a cependant ses limites. On peut au moins en recenser deux : la première est l'absence de typage fort et de déclaration des variables, ce qui rend délicate la structuration de grands logiciels. L'autre limitation est la vitesse de ces langa ges interprétés, qui est rédhibitoire dès que des calculs un peu importants sont en jeu. Ceci fait que ces langages sont plus souvent cantonnés à de petites applications ou à des surcouches graphiques de logiciels existants (ce qui n'enlève rien à leur valeur !).

1.3 Le meilleur des deux mondes

Pour remédier aux inconvénients que je viens de citer, les LC offrent (presque) tous la possibilité d'être étendus par des fonctions C, voire par des objets C++. Ceci permet de concevoir un logiciel en deux temps : les fonctions de base sont implémentées en C ou C++, puis les fonctions et structures de données ainsi réalisées sont liées entre elles au moyen du LC. Cette approche permet de construire des applications rapides et souples à la fois, les parties critiques du code étant implémentées en C/C++, les entrées/sorties (voire une interface graphique) étant gérées avec la souplesse du LC choisi.

A contrario, on peut développer intégralement en LC, pour ensuite accélérer l'application en implémentant les fonctions critiques en C ou C++. C'est la philosophie des outils de développement rapide.

Dans tous les cas, cette approche oblige à une bonne structuration de l'application et, si l'on va au bout du raisonnement, l'application se résume à un nouveau module du LC, appelant des fonctions C. Un éventuel fichier de données est remplacé par un script en LC, ce qui rend l'application totalement souple pour son utilisateur.

Ces systèmes à deux langages sont particulièrement utiles dans les codes de calcul scientifiques, où les routines de calcul sont relativement figées, mais où les données d'entrées varient presque à chaque étude en fonction des paramètres explorés. Il devient alors impossible de figer la routine d'entrée des données. L'intégration du code dans un LC permet de résoudre de manière élégante ce problème.

2 SWIG entre en jeu

Pour mettre en jeu cette stratégie, il faut rentrer profondément dans la structure du LC choisi et, même si cet aspect est en général bien documenté, écrire le code qui lie les fonctions C/C++ et l'interpréteur : une tâche lourde et pénible. En pratique, il faut d'une part envelopper chaque fonction par une fonction "wrapper", qui traduit ses arguments d'entrées et sa valeur de retour. Ensuite, il faut structurer un module qui déclare l'ensemble des fonctions que l'on souhaite appeler depuis l'interpréteur. Il faut enfin compiler tout cela pour construire soit un nouvel interpréteur étendu, soit une librairie dynamique qui pourra être chargée pendant l'exécution de l'interpréteur.

C'est ici que SWIG entre en jeu. SWIG, écrit par David Beazley, signifie "Simplified Wrapper Interface Generator". Ce logiciel permet d'automatiser le processus décrit ci-dessus : SWIG lit les fichiers d'en-tête de votre code (ou une version simplifiée de ceux-ci) et en déduit automatiquement le code C nécessaire pour appeler votre code depuis le LC. Ceci permet d'automatiser le processus dans un Makefile qui, à partir des sources C, construira automatiquement une librairie dynamique (.so), chargeable à l'exécution dans l'interpréteur. Il devient ainsi extrêmement rapide et simple d'étendre votre langage préféré pour qu'il dispose d'une librairie C/C++ écrite par ailleurs : c'est le meilleur des deux mondes sans efforts !

Il faut noter que cette stratégie peut être utilisée au cours du développement d'un logiciel : dès qu'une routine ou un objet sont écrits, on les intègre dans le LC, avec lequel il est très facile de rédiger des scripts de test. On peut ainsi tester indépendamment chaque élément du code sans avoir à mettre au point et compiler un "main" spécifique. Le point fort de SWIG est de permettre d'utiliser cette stratégie à coût nul : dès que le bon Makefile est au point, il n'y a plus qu'à travailler sur le code C/C++ et rarement sur les problèmes d'interfaçage avec le LC.

2.1 Un exemple en C/Python

Prenons l'exemple d'une fonction C calculant le pgcd de deux entiers positifs (dont je laisse la rédaction au lecteur !). Son prototype, placé dans le fichier pgcd.h, est le suivant :

int pgcd(int a, int b);

Il suffit de "mouliner" ce fichier avec SWIG :

$ swig -python -module pgcd pgcd.h

pour générer le fichier pgcd[lowbar]wrap.c qui contient le code nécessaire pour lier notre fonction pgcd dans un module Python nommé "pgcd". Il faut noter que SWIG génère également une documentation pour le module que l'on est en train de créer. SWIG requiert au moins un argument, qui est le nom du langage auquel on souhaite se lier (perl, tcl, python...). Le second argument -module pgcd sert ici à spécifier le nom du module Python que l'on va générer.

Le fichier pgcd[lowbar]wrap.c qui a été créé doit être compilé en incluant les en-têtes Python, ce qui donne sur ma machine :

$ gcc -c -I/usr/include/python1.5 pgcd_wrap.c

Enfin, on peut créer une librairie dynamique contenant le module Python par la commande :

$ gcc -shared -o pgcdmodule.so pgcd.o pgcd_wrap.o

Et le tour est joué. Il suffit de lancer Python, d'importer le module pgcd et d'utiliser la fonction pgcd. Par exemple, le script suivant :

#! /usr/bin/env python

import pgcd

print pgcd.pgcd(10,3)

affichera le résultat désiré. On voit qu'il suffit d'inclure les trois commandes de compilation dans un Makefile pour obtenir un environnement de développement opérationnel.

Bien des variantes sont possibles : si l'on se trouve dans un environnement où les librairies dynamiques sont d'un emploi plus délicat, on pourra reconstruire un interpréteur Python contenant la nouvelle fonction. D'autre part, si le fichier .h est un peu complexe, SWIG risque de ne pas s'y retrouver. Il faut alors écrire un fichier .i qui appelle le .h et qui reprend seulement les déclarations utiles dans Python. Dans notre cas, cela donnerait :

%module pgcd

%

#include"pgcd.h"

%

int pgcd(int a, int b);

qu'il faudrait traiter par

$ swig -python pgcd.i

pour obtenir pgcd[lowbar]wrap.c. Le reste de la procédure est identique.

L'intérêt du fichier .i (outre qu'il déclare le nom du module) est de permettre de développer la partie C du code, sans se soucier de Python, et de ne retenir dans l'interface Python que les fonctions nécessaires. Il permet par ailleurs de définir des traitements spéciaux pour certains types d'arguments (par exemple, des listes Python que l'on souhaite transformer en tableaux C) par un mécanisme nommé "typemap", que je n'aborderai pas davantage dans cette présentation succincte.

La construction du fichier .i est en fait très rapide : partant du fichier d'en-tête d'une librairie existante, il ne faut que quelques minutes pour élaguer ce qui peut gêner SWIG. J'ai ainsi pu utiliser la librairie GNU plotutil avec Python en moins d'une demi-heure !

Autre remarque : les Makefile nécessaires à l'utilisation de SWIG sont fournis dans la distribution !

2.2 Un exemple en C++/Python

SWIG permet également de lier du code C++ et même de faire en sorte que des objets C++ soient vus comme des objets Python, ce qui permet d'utiliser très naturellement les extensions C++. Attention : pour pouvoir utiliser une librairie dynamique C++, il faudra employer le même compilateur que celui qui a servi à construire l'interpréteur.

Prenons pour exemple la classe Point, qui est définie intégralement dans le fichier Point.h :

class Point

public:

Point(double x,double y)

cx=x;cy=y;

void translate(double a,double b)

cx+=a;cy+=b;

double x() return cx;

double y() return cy;

private:

double cx,cy;

;

Pour lier cette classe à Python, il faut rédiger le fichier point.i qui reprend, en la simplifiant, la définition de la classe Point. En particulier, on élimine tout ce qui est privé et qui, par définition, ne sera pas exporté vers le langage de commande et les implémentations des fonctions membres qui sont inutiles.

%module point

%

#include"point.h"

%

class Point

public:

Point(double x,double y);

void translate(double a,double b);

double x();

double y();

;

La compilation du module se fait par

$ swig -shadow -c++ -python point.i

$ g++ -c -I/usr/include/python1.5 point_wrap.c

$ g++ -shared -o pointcmodule.so point_wrap.o

où il faut noter les options passées à SWIG : -c++ est clair, on traite du c++. Cette option signale à SWIG qu'il faut traiter les mots clés spécifiques du C++. L'autre option, -shadow, signifie que l'on souhaite que les objets C++ soient représentés par des objets Python et non par une structure de données et un ensemble de fonctions. Pour cela, SWIG va cette fois générer deux fichiers (en plus de la documentation) : le fichier point[lowbar]wrap.c , qui fonctionne comme précédemment, sauf qu'il définit le module pointc (et non point !) et un fichier point.py , qui définit la classe Point. Ceci explique que la librairie dynamique est appelée pointcmodule.so. Le "c" est important. En pratique, on importe le fichier point.py , qui fait appel au module pointc qui, lui, se trouve dans la librairie partagée.

La classe Point , ainsi rendue accessible dans Python, s'utilise ainsi :

#! /usr/bin/env python

from point import *

c=Point(2,2)

print c.x(),c.y()

c.translate(-2,-2)

print c.x(),c.y()

où l'on voit que l'extension ainsi définie est très facile à utiliser dans Python. En pratique, l'utilisateur ne peut pas voir s'il s'agit d'un module Python ou d'un module C++.

3 Pour en savoir plus

3.1 Installation

SWIG s'installe comme tout produit GNU, une fois l'archive extraite, par

$ ./configure

$ make

$ make install

L'installation nécessite un compilateur C++.

3.2 Ressources

"Programming Python" par Mark Lutz, chez O'Reilly et http://www.python.org : pour tout savoir sur Python.

Le site de SWIG (http://www.swig.org) permet de télécharger la dernière version de SWIG ainsi que l'abondante documentation qui l'accompagne.

4 Conclusion

SWIG est un outil très performant pour lier des librairies C/C++ aux principaux langages de commandes du monde UNIX. SWIG permet donc de créer facilement des modules d'extension pour ces langages, voire de concevoir des applications sous forme de tels modules.

SWIG possède bien plus de possibilités que celles décrites dans cette rapide présentation :

La génération automatique de documentation : SWIG permet de générer automatiquement une documentation de l'API de la librairie qui est en train d'être liée à Python, sous forme de fichier texte, latex...

Les "typemaps" permettent de convertir des types de données complexes en entrées ou en sorties des fonctions. Par exemple, il est possible de convertir des listes Python en tableaux C. Ces "typemaps" permettent également d'insérer des contrôles sur les arguments (vérification de la positivité d'un argument).

Il est même possible, grâce à ce mécanisme, de définir des callbacks Python qui seront appelés par le code C.

Voilà, j'espère que cette rapide présentation de SWIG vous aura mis l'eau à la bouche et que vous explorerez plus avant cet outil passionnant.

Bruno Bodin, bodinb@club-internet.fr

v1.0, 28 mai 1999

 

 


© Copyright 2000 Diamond Editions/Linux magazine France. - Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1or any later version published by the Free Software Foundation; A copy of the license is included in the section entitled "GNU Free Documentation License".