La préparation des objets C++ à leur intégration dans du code perl

C'est l'outil SWIG (Simplified Wrapper and Interface Generator) qui est utilisé pour faire cette préparation. Aussi bien pour la bibliothèque DIESE que pour le simulateur lui-même, elle se déroule selon les quatre étapes suivantes.

Installer le système de répertoires

Soit <rep> le répertoire d'accueil : c'est bdiese/ pour DIESE et toboggan/ pour le simulateur du toboggan. On a créé dans <rep> un répertoire pour les fichiers sources (source). Celui sous bdiese/ comprend un fichier de directives de compilation spécial compileModuleSWIG. Celui sous toboggan comprend un fichier de directives makeSimulatorSWIG.

On crée sous rep un répertoire swig ainsi structuré :

rep/
   source/
   swig/
      incl/
      doc/

Ecrire les fichiers d'informations pour SWIG
On crée dans incl/ un fichier de suffixe .i (i pour 'interface') pour chaque fichier d'entêtes (dans source) qui mentionne au moins un objet ou service qu'on souhaite équiper pour être reconnu par perl. En principe, le fichier débute par la déclaration d'un module perl dans lequel seront regroupés les objets et services équipés. Il se poursuit par les déclarations des objets à équiper. La déclaration se fait à l'identique du fichier d'entêtes pour les variables, structures et types énumérés. Pour les classes, on reprend la déclaration du fichier d'entêtes en excluant éventuellement les membres privés ou protégés et les services qu'on ne souhaite pas équiper. Il est important de noter que, pour chaque classe, un seul constructeur peut être déclaré dans le fichier .i.

En réalité, le répertoire <rep>/swig/incl/contient un fichier (bdiese.i et, pour l'application du toboggan, appli.i) qui réalise l'inclusion de tous les fichiers .i correspondant aux fichiers .h. Ainsi, le contenu de bdiese.i est il le suivant :

%module bdiese

%{
#include "CoreTypes.h"
#include "Entity.h"
...
%}

%include "../swig/incl/CoreTypes.i"
%include "../swig/incl/Entity.i"
...

Voici alors un extrait du fichier CoreTypes.i. Noter l'absence de ligne {%module :

extern Simulation* pCurrentSim;
typedef int UserDescriptorId;
enum ObjectTypeId {BD_BASIC_ENTITY, BD_DESCRIPTED_ENTITY, BD_ENTITY, BD_ENTITYSPEC, BD_EVENT, BD_MONITOR, BD_PROCESS};
...
... et un extrait du fichier Entity.i :
class Entity
{
 public:
  int         GetIntValue(UserDescriptorId);
  float       GetFloatValue(UserDescriptorId);
  char*       InstanceName() const;
  ...
};
...

Pour le simulateur du toboggan, le fichier appli.i est celui-ci :

%module appli

%{
#include "../source/UserTypes.h" 
#include "../source/UserEntity.h"
  ...
%}

%include "../swig/incl/UserTypes.i"
%include "../swig/incl/UserEntity.i"
  ...

Réaliser avec SWIG les versions intégrables dans perl

Il suffit d'invoquer la commande système swig en indiquant le langage intégrateur (perl), le type de langage à intégrer (C++) et le fichier des interfaces. Par exemple, pour intégrer les objets du toboggan :

swig -perl -C++ -dlatex appli.i
Cela génère automatiquement une documentation au format demandé (option -d, dans doc/) et un programme C qui doit être compilé en indiquant le chemin d'accès à la bibliothèque perl. L'étape suivante est la génération d'une librairie dynamique qui regroupe le fichier objet issu de cette compilation et les modules objets issus de la compilation des fichiers source C++. La confection de la librairie DIESE s'arrête là. La confection d'un simulateur nécessite en plus l'assemblage dans un programme exécutable (par édition des liens) du module objet du programme principal, de la librairie dynamique DIESE (créée à partir de bdiese.i) et de la librairie dynamique de l'application (créée à partir de appli.i).

Une procédure qui enchaîne toutes ces opérations est fournie dans les fichiers compileModuleSWIG et makeSimulatorSWIG.

Complément sur la création de l'interpréteur perl

Pour que l'interpréteur sache reconnaître et manipuler les objets C++ équipés, la fonction de création de l'interpréteur doit comporter une option spéciale, sous la forme d'une fonction de nom xs_init pour permettre à la directive use <module> de fonctionner correctement. Si on ne faisait jamais appel à des objets C++ dans le code perl, on utiliserait le pointeur de fonction NULL en lieu et place de xs_init dans le code de NewInterpreter. La fonction xs_init est déclarée dans Swig.h (voir ci-dessous) pour être référencée dans NewInterpreter. Elle est initialisée au pointeur NULL dans Swig.cc. Ce qui revient alors au développeur d'une application, c'est de surcharger la valeur de xs_init, en en plaçant une version appropriée de nom App_xs_init dans le code du simulateur, par exemple dans les fichiers UserTypes.* et en réalisant, typiquement dans le programme principal, l'affectation suivante entre fonctions :

      xs_init = App_xs_init;
App_xs_init comprend une ligne EXTERN_C et une ligne newXS pour chaque librairie dynamique incluse dans le simulateur. Une version standard en est la suivante :
#include <EXTERN.h>
#include <perl.h>

EXTERN_C void boot_bdiese _((CV* cv));
EXTERN_C void boot_appli _((CV* cv));  

void App_xs_init() {
  char* file = __FILE__;
  newXS("bdiese::bootstrap", boot_bdiese, file);
  newXS("appli::bootstrap", boot_appli, file);
}

This page was generated with the help of DOC++.