=encoding iso-8859-1 =head1 NAME/NOM perlxs - Manuel de référence du langage XS =head1 DESCRIPTION =head2 Introduction XS est un langage utilisé pour réaliser une interface d'extension entre Perl et une librairie C qu'on souhaite utiliser depuis Perl. L'interface XS est combinée avec la librairie pour produire une nouvelle librairie susceptible d'être liée avec Perl. Une B est une fonction écrite dans le langage XS ; c'est le composant central de l'interface de l'application Perl. Le compilateur XS s'appelle B. Ce compilateur insère les constructions nécessaires pour permettre à une XSUB, qui n'est autre qu'une fonction C déguisée, de manipuler des valeurs Perl, et crée la colle nécessaire pour permettre à Perl d'accéder à la XSUB. Le compilateur utilise des B pour faire la correspondance entre les variables et les paramètres de fonction C d'une part, et les valeurs Perl de l'autre. Le typemap par défaut gère de nombreux types C parmi les plus courants. Un typemap supplémentaire doit être créé pour traiter les structures et les types spécifiques à la librairie que l'on veut lier dans Perl. Consultez L pour un guide d'apprentissage du processus de création d'extensions dans son ensemble. Note : pour beaucoup d'extensions, le système SWIG de Dave Beazley fournit un mécanisme nettement plus pratique pour réaliser le code de liaison XS. Voyez http://www.cs.utah.edu/~beazley/SWIG pour plus d'information. =head2 Mise en route La plupart des exemples qui suivent portent sur la création d'une interface entre Perl et les fonctions de la librairie de connexion ONC+ RPC. La fonction rpcb_gettime() sera utilisée pour illustrer de nombreuses caractéristiques du langage XS. Cette fonction accepte deux paramètres ; le premier est une donnée en entrée et le second une donnée en sortie. La fonction renvoie aussi une valeur d'état. bool_t rpcb_gettime(const char *host, time_t *timep); Depuis C, cette fonction est appelée avec les instructions suivantes. #include bool_t status; time_t timep; status = rpcb_gettime( "localhost", &timep ); Si on réalise une XSUB offrant une traduction directe entre cette fonction et Perl, cette XSUB sera utilisée depuis Perl avec le code suivant. Les variables $status et $timep contiendront la sortie de cette fonction. use RPC; $status = rpcb_gettime( "localhost", $timep ); Le fichier XS suivant présente une sous-routine XS, ou XSUB, proposant un exemple d'interface avec la fonction rpcb_gettime(). Cette XSUB représente une traduction directe entre C et Perl et préserve donc l'interface même depuis Perl. Cette XSUB sera appelée depuis Perl de la même manière que ci-dessus. Notez que les trois premières instructions #include, pour C, C et C, seront tout le temps présentes au début d'un fichier XS. On élargira plus tard cette approche, et on en présentera d'autres. #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include MODULE = RPC PACKAGE = RPC bool_t rpcb_gettime(host,timep) char *host time_t &timep OUTPUT: timep Toutes les extensions à Perl, y compris celles qui contiennent des XSUB, doivent être accompagnées d'un module Perl d'amorçage (angl. bootstrap) qui assure l'intégration de l'extension dans Perl. Ce module exporte les fonctions et les variables de l'extension vers le programme Perl et entraîne la liaison entre les XSUB de l'extension et Perl. Le module qui suit sera utilisé dans la plupart des exemples de ce document ; il doit être utilisé depuis Perl avec la commande C comme on l'a vu précédemment. On approfondira l'étude des modules Perl plus loin dans ce document. package RPC; require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT = qw( rpcb_gettime ); bootstrap RPC; 1; Tout au long de ce document, diverses interfaces à la XSUB rpcb_gettime() seront explorées. L'ordre ou le nombre des paramètres pris par les XSUB variera. Dans chaque cas, la XSUB est une abstraction entre Perl et la vraie fonction C rpcb_gettime(), et la XSUB doit toujours s'assurer que la vraie fonction rpcb_gettime() est appelée avec les bons paramètres. Cette abstraction permettra au programmeur de réaliser une interface avec la fonction C plus proche de Perl. =head2 Anatomie d'une XSUB La XSUB qui suit permet à un programme Perl d'accéder à une fonction de librairie C dont le nom est sin(). La XSUB fait comme la fonction C, qui prend un seul paramètre et renvoie une valeur unique. double sin(x) double x Lorsqu'on utilise des pointeurs C, l'opérateur d'indirection C<*> devrait être considéré comme une partie du type et l'opérateur d'addressage C<&> comme une partie de la variable, comme on l'a fait dans la fonction rpcb_gettime() ci-dessus. Consultez la section sur les typemaps pour avoir plus de détails sur l'utilisation des qualificateurs et des opérateurs unaires dans les types C. Le nom de la fonction et le type de retour doivent être placés sur des lignes séparées. INCORRECT CORRECT double sin(x) double double x sin(x) double x Le corps de la fonction peut être mis en retrait ou ajusté à gauche. L'exemple suivant montre une fonction dont le corps est ajusté à gauche. On utilisera un retrait dans la plupart des exemples de ce document. CORRECT double sin(x) double =head2 La pile des arguments La pile des arguments est utilisée pour stocker les valeurs qui sont envoyées à la XSUB en tant que paramètres, ainsi que la valeur de retour de la XSUB. En fait, toutes les fonctions Perl mettent leurs valeurs sur cette pile en même temps, chacune étant limitée à son propre intervalle de positions sur la pile. Dans ce document, la première position dans la pile appartenant à la fonction active sera désignée comme la position 0 pour cette fonction. Les XSUB se réfèrent à leurs arguments de la pile avec la macro B, où I désigne une position dans l'intervalle appartenant à la XSUB sur la pile. La position 0 pour cette fonction est connue de la XSUB comme ST(0). Les paramètres en entrée et les valeurs de retour de la XSUB commencent toujours à ST(0). Dans de nombreux cas simples, le compilateur B générera le code nécessaire à la manipulation de la pile des arguments en insérant des morceaux de code trouvés dans les typemaps. Dans les cas plus complexes, le programmeur devra fournir le code. =head2 La variable RETVAL La variable RETVAL est une variable magique qui est toujours du type retourné par la fonction de la librairie C. Le compilateur B fournit cette variable dans chaque XSUB et l'utilise par défaut pour y mettre la valeur de retour de la fonction C appelée. Dans les cas simples, la valeur de RETVAL sera mise dans ST(0) sur la pile d'arguments, où Perl le recevra comme valeur de retour de la XSUB. Si la XSUB a un type de retour égal à C, le compilateur ne fournira pas de variable RETVAL pour cette fonction. Lorsqu'on utilise la directive PPCODE:, la variable RETVAL n'est plus nécessaire, sauf si on l'utilise explicitement. Si la directive PPCODE: n'est pas utilisée, la valeur de retour C devrait être utilisée uniquement pour des sous-routines qui ne renvoient pas de valeur, I la directive CODE: est utilisée pour positionner ST(0) explicitement. Dans des versions plus anciennes de ce document, on conseillait d'utiliser la valeur de retour C dans de tels cas. On a découvert que cela pouvait mener à des segfaults lorsque la XSUB était I C. Cette pratique est maintenant désapprouvée, et risque de ne plus être supportée dans une version future. Utilisez la valeur de retour C dans ces cas-là (C contient actuellement du code heuristique qui tente de faire la différence entre les fonctions "vraiment-void" et celles qui sont "déclarées-void-suivant-l'ancienne-pratique" ; votre code est donc à la merci de l'heuristique si vous n'utilisez pas C comme valeur de retour). =head2 Le mot-clé MODULE Le mot-clé MODULE est utilisé pour marquer le début du code XS et spécifier un paquetage pour les fonctions que l'on est en train de définir. Tout le texte qui précède le premier mot-clé MODULE est considéré comme du code C et sera transféré dans la sortie du compilateur sans modification. Tout module XS aura une fonction d'initialisation utilisée pour brancher la XSUB dans Perl. Le nom du paquetage de cette fonction d'initialisation correspondra à la valeur de la dernière instruction MODULE dans les fichiers source XS. La valeur de MODULE devrait toujours rester constante à l'intérieur d'un même fichier XS, même si cela n'est pas obligatoire. L'exemple suivant démarre le code XS et place toutes les fonctions dans un paquetage nommé RPC. MODULE = RPC =head2 Le mot-clé PACKAGE Lorsque les fonctions, à l'intérieur d'un fichier source XS, doivent être réparties dans des paquetages, il faut utiliser le mot-clé PACKAGE. Ce mot-clé est utilisé avec le mot-clé MODULE et doit être spécifié immédiatement après lui. MODULE = RPC PACKAGE = RPC [ code XS dans le paquetage RPC ] MODULE = RPC PACKAGE = RPCB [ code XS dans le paquetage RPCB ] MODULE = RPC PACKAGE = RPC [ code XS dans le paquetage RPC ] Bien que ce mot-clé soit optionnel et qu'il fournisse des informations redondantes dans certains cas, il devrait toujours être utilisé. Il permet de garantir que la XSUB apparaîtra dans le paquetage désiré. =head2 Le mot-clé PREFIX Le mot-clé PREFIX désigne des préfixes à supprimer dans les noms de fonction Perl. Si la fonction C est C et que la valeur de PREFIX est C, Perl devra voir cette fonction comme C. Ce mot-clé suit le mot-clé PACKAGE lorsqu'il est utilisé. Si PACKAGE n'est pas utilisé, alors PREFIX suit le mot-clé MODULE. MODULE = RPC PREFIX = rpc_ MODULE = RPC PACKAGE = RPCB PREFIX = rpcb_ =head2 Le mot-clé OUTPUT Le mot-clé OUTPUT: indique qu'il faut mettre à jour certains paramètres de fonction (en rendant les nouvelles valeurs accessibles à Perl) lorsque la XSUB se termine, ou que certaines valeurs doivent être renvoyées à la fonction Perl. Pour des fonctions simples, comme la fonction sin() ci-dessus, la variable RETVAL est désignée automatiquement comme une valeur en sortie. Dans des fonctions plus complexes, le compilateur B aura besoin d'aide pour déterminer quelles variables sont des variables en sortie. Ce mot-clé sera utilisé normalement en complément du mot-clé CODE:. La variable RETVAL n'est pas reconnue comme une variable en sortie lorsque le mot-clé CODE: est présent. Le mot-clé OUTPUT: est utilisé dans cette situation pour dire au compilateur que RETVAL est réellement une variable en sortie. Le mot-clé OUTPUT: peut aussi être utilisé pour indiquer que des paramètres de fonction sont des variables en sortie. Cela peut être nécessaire lorsqu'un paramètre a été modifié à l'intérieur d'une fonction et que le programmeur veut que cette modification soit visible depuis Perl. bool_t rpcb_gettime(host,timep) char *host time_t &timep OUTPUT: timep Le mot-clé OUTPUT: permet aussi à un paramètre en sortie d'être relié à un morceau de code correspondant plutôt qu'à un typemap. bool_t rpcb_gettime(host,timep) char *host time_t &timep OUTPUT: timep sv_setnv(ST(1), (double)timep); B rajoute automatiquement un C pour tous les paramètres de la section OUTPUT de la XSUB, sauf pour RETVAL. C'est le comportement désiré la plupart du temps, car il se charge d'invoquer convenablement l'effet 'set' (angl. "set magic") sur les données en sortie (ce qui est nécessaire pour les éléments de tableau simple ou de tableau associatif passés en paramètre, qui doivent être créés s'ils n'existent pas). Si, pour une raison donnée, ce comportement n'est pas désiré, la section OUTPUT peut contenir une ligne C qui le désactive pour le restant des paramètres de la section OUTPUT:. De même, C peut être utilisé pour le réactiver pour le restant de la section OUTPUT. Voyez L pour plus de détails sur l'effet 'set'. =head2 Le mot-clé CODE Ce mot-clé est utilisé dans des XSUB plus complexes qui nécessitent un traitement spécial pour la fonction C. La variable RETVAL est disponible, mais elle ne sera pas retournée, sauf si elle est spécifiée sous le mot-clé OUTPUT:. La XSUB qui suit correspond à une fonction C qui requiert un traitement spécial de ses paramètres. L'utilisation depuis Perl est donnée en premier. $status = rpcb_gettime( "localhost", $timep ); Voici la XSUB. bool_t rpcb_gettime(host,timep) char *host time_t timep CODE: RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL =head2 Le mot-clé INIT Le mot-clé INIT: permet d'insérer du code d'initialisation dans la XSUB avant que le compilateur ne génère l'appel à la fonction C. Contrairement au mot-clé CODE: ci-dessus, celui-ci n'affecte pas la manière dont le compilateur traite RETVAL. bool_t rpcb_gettime(host,timep) char *host time_t &timep INIT: printf("# l'hote est %s\n", host ); OUTPUT: timep =head2 Le mot-clé NO_INIT Le mot-clé NO-INIT sert à indiquer qu'on utilise un paramètre de la fonction uniquement comme valeur en sortie. Le compilateur B génère normalement du code pour lire les valeurs de tous les paramètres de la fonction sur la pile des arguments, et pour les assigner à des variables C lors de l'entrée dans la fonction. NO_INIT dit au compilateur que certains paramètres seront utilisés en sortie plutôt qu'en entrée, et qu'ils seront traités avant la fin de la fonction. L'exemple suivant présente une variante de la fonction rpcb_gettime(). Cette fonction utilise la variable timep uniquement comme variable en sortie, et ne se préoccupe pas de son contenu initial. bool_t rpcb_gettime(host,timep) char *host time_t &timep = NO_INIT OUTPUT: timep =head2 Initialisation des arguments de fonction Normalement, les paramètres de fonction sont initialisés avec les valeurs qu'ils ont dans la pile des arguments. Les typemaps contiennent les portions de code utilisées pour transférer les valeurs Perl dans des paramètres C. Le programmeur, toutefois, est autorisé à surcharger les typemaps et à fournir un code d'initialisation différent (ou supplémentaire). Le code qui suit montre comment fournir du code d'initialisation pour les paramètres de fonction. Le code d'initialisation est évalué entre des guillemets par le compilateur (en utilisant eval()), avant d'être inséré dans le code généré, de sorte que toute expression devant être interprétée littéralement (essentiellement C<$>, C<@> ou C<\>) doit être protégée par des C<\>. Les variables C<$var>, C<$arg> et C<$type> peuvent être utilisées comme dans les typemaps. bool_t rpcb_gettime(host,timep) char *host = (char *)SvPV($arg,PL_na); time_t &timep = 0; OUTPUT: timep Ce procédé ne devrait pas être utilisé pour fournir des valeurs par défaut aux paramètres. Le cas normal d'utilisation est celui où un paramètre de fonction doit être traité par une autre fonction de la librairie avant d'être utilisé. Les paramètres par défaut font l'objet de la prochaine section. Si l'initialisation commence par C<=>, elle sera insérée sur la même ligne que la déclaration de la variable. Si elle commence par C<;> ou C<+>, elle sera insérée une fois que toutes les variables en entrée auront été déclarées. Les cas C<=> et C<;> remplacent l'initialisation fournie normalement par le typemap. Dans le cas C<+>, l'initialisation du typemap précédera le code d'initialisation qui suit le C<+>. Une variable globale, C<%v>, est disponible pour le cas vraiment rare où une initialisation a besoin d'une information venant d'une autre initialisation. bool_t rpcb_gettime(host,timep) time_t &timep ; /*\$v{time}=@{[$v{time}=$arg]}*/ char *host + SvOK($v{time}) ? SvPV($arg,PL_na) : NULL; OUTPUT: timep =head2 Valeurs de paramètres par défaut On peut donner des valeurs par défaut à des paramètres de fonction en rajoutant une instruction d'affectation dans la liste des paramètres. La valeur par défaut peut être un nombre ou une chaîne de caractères. Les valeurs par défaut doivent toujours être utilisées uniquement pour les paramètres les plus à droite. Afin de permettre à la XSUB de rpcb_gettime() d'avoir un hôte par défaut, les paramètres de la XSUB pourraient être réordonnés. La XSUB appellera alors la vraie fonction rpcb_gettime() avec les paramètres placés dans le bon ordre. Perl appellera cette XSUB avec l'une des instructions suivantes. $status = rpcb_gettime( $timep, $host ); $status = rpcb_gettime( $timep ); La XSUB ressemblera au code qui suit. Un bloc CODE: est utilisé pour appeler la vraie fonction rpcb_gettime() avec les paramètres remis dans l'ordre correct pour cette fonction. bool_t rpcb_gettime(timep,host="localhost") char *host time_t timep = NO_INIT CODE: RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL =head2 Le mot-clé PREINIT Le mot-clé PREINIT: permet de déclarer des variables supplémentaires avant que les typemaps soient utilisés. Si une variable est déclarée dans un bloc CODE:, elle suivra tout code généré par le typemap. Cela peut provoquer des fautes de syntaxe en C. Pour forcer la déclaration de la variable avant le code du typemap, placez-la dans un bloc PREINIT:. Le mot-clé PREINIT: peut être utilisé une ou plusieurs fois à l'intérieur d'une XSUB. Les exemples qui suivent sont équivalents, mais, si le code utilise des typemaps complexes, le premier exemple sera plus sûr. bool_t rpcb_gettime(timep) time_t timep = NO_INIT PREINIT: char *host = "localhost"; CODE: RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL Un exemple correct, mais avec des risques d'erreur. bool_t rpcb_gettime(timep) time_t timep = NO_INIT CODE: char *host = "localhost"; RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL =head2 Le mot-clé SCOPE Le mot-clé SCOPE: permet d'activer un niveau spécial de visibilité (angl. scoping) pour une XSUB donnée. Dans ce cas-là, la XSUB invoquera ENTER et LEAVE automatiquement (Note du Traducteur : les macros ENTER et LEAVE sont décrites dans L). Afin de supporter des correspondances de type complexes, ce niveau spécial sera activé automatiquement pour une XSUB si elle utilise une entrée de typemap contenant le commentaire C. Pour activer ce niveau spécial : SCOPE: ENABLE Pour le désactiver : SCOPE: DISABLE =head2 Le mot-clé INPUT Habituellement, les paramètres de XSUB sont évalués juste après l'entrée dans la XSUB. Le mot-clé INPUT: peut être utilisé pour forcer ces paramètres à être évalués un peu plus tard. On peut utiliser le mot-clé INPUT: plusieurs fois dans une XSUB, et ceci pour une ou plusieurs variables en entrée. Ce mot-clé accompagne le mot-clé PREINIT:. L'exemple suivant montre comment l'évaluation du paramètre en entrée C peut être retardée, après un PREINIT. bool_t rpcb_gettime(host,timep) char *host PREINIT: time_t tt; INPUT: time_t timep CODE: RETVAL = rpcb_gettime( host, &tt ); timep = tt; OUTPUT: timep RETVAL Dans cet autre exemple, chacun des paramètres en entrée voit son évaluation retardée. bool_t rpcb_gettime(host,timep) PREINIT: time_t tt; INPUT: char *host PREINIT: char *h; INPUT: time_t timep CODE: h = host; RETVAL = rpcb_gettime( h, &tt ); timep = tt; OUTPUT: timep RETVAL =head2 Listes de paramètres de longueur variable Les XSUB peuvent recevoir des listes de paramètres de longueur variable en spécifiant une ellipse C<...> dans la liste des paramètres. Cette utilisation de l'ellipse est similaire à celle que l'on trouve en C ANSI. Le programmeur peut déterminer le nombre d'arguments passés à la XSUB en examinant la variable C que le compilateur B fournit pour chaque XSUB. Grâce à ce mécanisme, on peut réaliser une XSUB qui accepte une liste de paramètres de longueur indéterminée. Le paramètre I de la XSUB rpcb_gettime() peut être optionnel, de manière à ce qu'on puisse utiliser l'ellipse pour indiquer que la XSUB prendra un nombre variable de paramètres. Perl devrait pouvoir appeler cette XSUB avec chacune des instructions suivantes. $status = rpcb_gettime( $timep, $host ); $status = rpcb_gettime( $timep ); Voici le code XS, avec l'ellipse : bool_t rpcb_gettime(timep, ...) time_t timep = NO_INIT PREINIT: char *host = "localhost"; CODE: if( items > 1 ) host = (char *)SvPV(ST(1), PL_na); RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL =head2 Le mot-clé C_ARGS Le mot-clé C_ARGS permet de réaliser des XSUB que l'on n'appelle pas de la même manière depuis Perl que depuis C, sans qu'il soit nécessaire d'écrire une section CODE: ou PPCODE:. Le contenu du paragraphe C_ARGS est passé comme argument à la fonction C, sans aucun changement. Supposons par exemple que la fonction C soit déclarée ainsi : symbolic nth_derivative(int n, symbolic function, int flags); et que la valeur par défaut de I soit contenue dans la variable C I. Supposons que vous souhaitiez réaliser une interface que l'on appellera de la manière suivante : $second_deriv = $function->nth_derivative(2); Pour cela, déclarez la XSUB ainsi : symbolic nth_derivative(function, n) symbolic function int n C_ARGS: n, function, default_flags =head2 Le mot-clé PPCODE Le mot-clé PPCODE: est une variante du mot-clé CODE: qui est utilisée pour indiquer au compilateur B que le programmeur fournit le code contrôlant la pile des arguments pour les valeurs de retour des XSUB. On souhaite parfois que la XSUB renvoie une liste de valeurs et non une valeur unique. Dans ce cas-là, il faut utiliser PPCODE: et rajouter de manière explicite la liste des valeurs sur la pile. Les mots-clés PPCODE: et CODE: ne sont pas utilisés simultanément dans la même XSUB. La XSUB suivante appelle la fonction C rpcb_gettime() et renvoie à Perl ses deux valeurs de sortie, timep et status, comme une seule liste. void rpcb_gettime(host) char *host PREINIT: time_t timep; bool_t status; PPCODE: status = rpcb_gettime( host, &timep ); EXTEND(SP, 2); PUSHs(sv_2mortal(newSViv(status))); PUSHs(sv_2mortal(newSViv(timep))); Remarquez que le programmeur doit fournir le code C assurant l'appel de la vraie fonction rpcb_gettime(), ainsi que le placement correct des valeurs de retour sur la pile des arguments. Le type de retour C pour cette fonction indique au compilateur B que la variable RETVAL n'est pas nécessaire, qu'elle n'est pas utlisée, et qu'elle ne devrait pas être créée. Dans la plupart des cas, il faut utiliser le type de retour void avec l'instruction PPCODE:. On utilise la macro EXTEND() afin de dégager de la place sur la pile des arguments pour les 2 valeurs de retour. L'instruction PPCODE: fait en sorte que le compilateur B crée un pointeur vers la pile dans la variable C ; c'est ce pointeur qui est utilisé dans la macro EXTEND(). Les valeurs sont ensuite rajoutées sur la pile avec les macros PUSHs(). A présent, la fonction rpcb_gettime() peut être utilisée depuis Perl avec l'instruction suivante. ($status, $timep) = rpcb_gettime("localhost"); Lorsque vous travaillez sur les paramètres en sortie avec une section PPCODE:, assurez-vous de traiter l'effet 'set' convenablement. Consultez L pour plus de détails sur cet effet 'set'. =head2 Renvoyer undef et des listes vides Le programmeur voudra parfois renvoyer simplement C ou une liste vide si la fonction échoue, plutôt qu'une valeur d'état à part. La fonction rpcb_gettime() présente justement cette situation. Nous aimerions que la fonction retourne l'heure si elle réussit, ou undef si elle échoue. Dans le code Perl suivant, la valeur de $timep sera soit undef, soit une heure valide; $timep = rpcb_gettime( "localhost" ); La XSUB suivante n'utilise le type de retour C qu'à titre d'aide-mémoire, et il utilise un bloc CODE: pour indiquer au compilateur que le programmeur a fourni tout le code nécessaire. L'appel de sv_newmortal() initialise la valeur de retour à undef, ce qui en fait la valeur de retour par défaut. SV * rpcb_gettime(host) char * host PREINIT: time_t timep; bool_t x; CODE: ST(0) = sv_newmortal(); if( rpcb_gettime( host, &timep ) ) sv_setnv( ST(0), (double)timep); Cet autre exemple montre comment on peut mettre un undef explicite dans la valeur de retour, au cas où le besoin s'en ferait ressentir. SV * rpcb_gettime(host) char * host PREINIT: time_t timep; bool_t x; CODE: ST(0) = sv_newmortal(); if( rpcb_gettime( host, &timep ) ){ sv_setnv( ST(0), (double)timep); } else{ ST(0) = &PL_sv_undef; } Pour renvoyer une liste vide, il faut utiliser un bloc PPCODE: et ne pas rajouter de valeur de retour sur la pile. void rpcb_gettime(host) char *host PREINIT: time_t timep; PPCODE: if( rpcb_gettime( host, &timep ) ) PUSHs(sv_2mortal(newSViv(timep))); else{ /* Rien n'est remis sur la pile, donc une */ /* liste vide est renvoyee implicitement */ } Certaines personnes préfèrent rajouter un C explicite dans la XSUB ci-dessus au lieu de laisser l'exécution se poursuivre jusqu'au bout. Dans ce cas-là, il convient plutôt d'utiliser C, ce qui garantira que la pile XSUB est ajustée correctement. D'autres macros sont décrites dans L. =head2 Le mot-clé REQUIRE Le mot-clé REQUIRE: sert à indiquer le numéro de version minimal du compilateur B requis pour compiler le module XS. Un module XS contenant l'instruction suivante ne pourra être compilé qu'avec une version de B égale ou supérieure à 1.922 : REQUIRE: 1.922 =head2 Le mot-clé CLEANUP Ce mot-clé peut être utilisé quand une XSUB doit exécuter des procédures de nettoyage spéciales avant de se terminer. Quand le mot-clé CLEANUP: est utilisé, il doit suivre tout bloc CODE:, PPCODE: ou OUTPUT: présent dans la XSUB. Les instructions spécifiées dans le bloc de nettoyage seront les dernières instructions de la XSUB. =head2 Le mot-clé BOOT Le mot-clé BOOT: permet de rajouter du code dans la fonction d'amorçage de l'extension. La fonction d'amorçage est générée par le compilateur B et contient normalement les instructions nécessaires pour enregistrer toute XSUB auprès de Perl. Avec le mot-clé BOOT:, le programmeur peut dire au compilateur de rajouter des instructions supplémentaires dans la fonction d'amorçage. Ce mot-clé peut être utilisé n'importe quand après le premier mot-clé MODULE, et doit être tout seul sur une ligne. La première ligne blanche après le mot-clé terminera le bloc de code. BOOT: # Le message suivant sera affiche lors de l'execution de la # fonction d'amorcage. printf("bonjour !\n"); =head2 Le mot-clé VERSIONCHECK Le mot-clé VERSIONCHECK: correspond aux options C<-versioncheck> et C<-noversioncheck> de B. Ce mot-clé prime sur les options de la ligne de commande. La vérification de version est activée par défaut. Lorsqu'elle est activée, le module XS essaie de vérifier que son numéro de version est compatible avec celui du module PM. Pour activer la vérification de version : VERSIONCHECK: ENABLE Pour la désactiver : VERSIONCHECK: DISABLE =head2 Le mot-clé PROTOTYPES Le mot-clé PROTOTYPES: correspond aux options C<-prototypes> et C<-noprototypes> de B. Ce mot-clé prime sur les options de la ligne de commande. Les prototypes sont activés par défaut. Lorsqu'ils sont activés, les XSUB reçoivent des prototypes Perl. Ce mot-clé peut être utilisé plusieurs fois dans un module XS pour activer et désactiver les prototypes dans différentes parties du module. Pour activer les prototypes : PROTOTYPES: ENABLE Pour les désactiver : PROTOTYPES: DISABLE =head2 Le mot-clé PROTOTYPE Ce mot-clé est proche du mot-clé PROTOTYPES: ci-dessus, mais peut être utilisé pour forcer B à utiliser un prototype donné pour la XSUB. Ce mot-clé prime sur toute autre option ou mot-clé relatif au prototypage, mais n'affecte que la XSUB courante. Consultez L pour plus d'informations sur les prototypes Perl. bool_t rpcb_gettime(timep, ...) time_t timep = NO_INIT PROTOTYPE: $;$ PREINIT: char *host = "localhost"; CODE: if( items > 1 ) host = (char *)SvPV(ST(1), PL_na); RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL =head2 Le mot-clé ALIAS Le mot-clé ALIAS: permet à une XSUB de recevoir plusieurs noms en Perl, et de déterminer lequel de ces noms a été utilisé pour l'invoquer. Les noms en Perl peuvent être complets avec le nom du paquetage. Chaque alias reçoit un numéro. Le compilateur crée une variable nommée C qui contient le numéro de l'alias utilisé. Lorsque le nom utilisé pour invoquer la XSUB est celui avec lequel il a été déclaré, C est égal à 0. L'exemple suivant crée des alias C et C pour cette fonction. bool_t rpcb_gettime(host,timep) char *host time_t &timep ALIAS: FOO::gettime = 1 BAR::getit = 2 INIT: printf("# ix = %d\n", ix ); OUTPUT: timep =head2 Le mot-clé INTERFACE Ce mot-clé déclare que la XSUB courante sert à garder en réserve la signature de fonction indiquée. S'il est suivi par du texte, ce texte est considéré comme une liste de fonctions qui ont cette signature, et qui doivent être attachées à des XSUB. Par exemple, si vous avez 4 fonctions multiply(), divide(), add() et subtract(), toutes avec la signature symbolic f(symbolic, symbolic); vous pouvez les implémenter toutes à la fois avec la XSUB symbolic interface_s_ss(arg1, arg2) symbolic arg1 symbolic arg2 INTERFACE: multiply divide add subtract L'avantage de cette approche par rapport au mot-clé ALIAS: est que l'on peut attacher une fonction supplémentaire remainder() lors de l'exécution en utilisant CV *mycv = newXSproto("Symbolic::remainder", XS_Symbolic_interface_s_ss, __FILE__, "$$"); XSINTERFACE_FUNC_SET(mycv, remainder); (on suppose dans cet exemple qu'il n'y a pas de section INTERFACE_MACRO:, car il faut alors utiliser autre chose que C) =head2 Le mot-clé INTERFACE_MACRO Ce mot-clé permet de définir une INTERFACE qui procède d'une manière différente pour récupérer dans la XSUB un pointeur vers la fonction C. Le texte qui suit ce mot-clé doit indiquer des noms de macros à utiliser pour récupérer le pointeur de fonction ou pour l'initialiser dans la XSUB. La macro de récupération reçoit le type de retour, C, et C pour ce C. La macro d'initialisation reçoit cv et le pointeur de fonction. Les valeurs par défaut de ces macros sont C et C. Le mot-clé INTERFACE avec une liste vide de fonctions peut être omis si INTERFACE_MACRO est utilisé. Supposons que, dans l'exemple qui précède, des pointeurs de fonction pour multiply(), divide(), add(), et subtract() soient conservés dans un tableau C global C, les positions de ces pointeurs par rapport au début du tableau étant contenues dans des variables nommées C, C, C, C. On peut alors utiliser #define XSINTERFACE_FUNC_BYOFFSET(ret,cv,f) \ ((XSINTERFACE_CVT(ret,))fp[CvXSUBANY(cv).any_i32]) #define XSINTERFACE_FUNC_BYOFFSET_set(cv,f) \ CvXSUBANY(cv).any_i32 = CAT2( f, _off ) dans la section C, et symbolic interface_s_ss(arg1, arg2) symbolic arg1 symbolic arg2 INTERFACE_MACRO: XSINTERFACE_FUNC_BYOFFSET XSINTERFACE_FUNC_BYOFFSET_set INTERFACE: multiply divide add subtract dans la section XSUB. =head2 Le mot-clé INCLUDE Ce mot-clé peut être utilisé pour insérer d'autres fichiers dans le module XS. Les autres fichiers peuvent contenir du code XS. INCLUDE: peut aussi être utilisé pour exécuter une commande générant du code XS à insérer dans le module. Le fichier F contient notre fonction C : bool_t rpcb_gettime(host,timep) char *host time_t &timep OUTPUT: timep Le module XS peut utiliser INCLUDE: pour insérer ce fichier. INCLUDE: Rpcb1.xsh Si les paramètres du mot-clé INCLUDE: sont suivis d'un trait vertical (C<|>), alors le compilateur interprétera les paramètres comme une commande. INCLUDE: cat Rpcb1.xsh | =head2 Le mot-clé CASE Le mot-clé CASE permet à une XSUB de se subdiviser en plusieurs parties distinctes, chacune se comportant comme une XSUB virtuelle. CASE: est global à la XSUB : quand on l'utilise, il faut mettre tous les autres mots-clés XS à l'intérieur d'un CASE:. Cela signifie que rien ne peut précéder le premier CASE: dans la XSUB, et que tout code suivant le dernier CASE: y est incorporé. Un CASE: peut être associé à une condition sur un paramètre de la XSUB en utilisant la variable-alias C (voir L<"Le mot-clé ALIAS:">), ou éventuellement la variable C (voir L<"Listes de paramètres de longueur variable">). Le dernier CASE correspond au mot-clé B en C s'il n'est associé à aucune condition. L'exemple suivant montre un CASE dépendant de C dans la fonction C, laquelle a pour alias C. Lorsque la fonction est invoquée sous le nom C, elle reçoit les paramètres habituels C<(char *host, time_t *timep)>, tandis que, lorsque elle est invoquée en tant que C, ses paramètres sont inversés, C<(time_t *timep, char *host)>. long rpcb_gettime(a,b) CASE: ix == 1 ALIAS: x_gettime = 1 INPUT: # 'a' est timep, 'b' est host char *b time_t a = NO_INIT CODE: RETVAL = rpcb_gettime( b, &a ); OUTPUT: a RETVAL CASE: # 'a' est host, 'b' est timep char *a time_t &b = NO_INIT OUTPUT: b RETVAL Cette fonction peut être appelée avec chacune des instructions suivantes. Notez la différence dans l'ordre des arguments. $status = rpcb_gettime( $host, $timep ); $status = x_gettime( $timep, $host ); =head2 L'opérateur unaire & L'opérateur unaire & est utilisé pour dire au compilateur qu'il doit déréférencer l'objet lors de l'appel à la fonction C. On utilise ce procédé lorsqu'on n'a pas mis de bloc CODE: et que l'objet n'est pas de type pointeur (l'objet est un C ou un C, mais pas un C ni un C). La XSUB suivante génère du code C incorrect. Le code généré par B appellera C avec les paramètres C<(char *host, time_t timep)>, mais le vrai C attend un paramètre C de type C et non C. bool_t rpcb_gettime(host,timep) char *host time_t timep OUTPUT: timep On résout ce problème en utilisant l'opérateur C<&>. Le compilateur B, à présent, traduira la XSUB en code qui appellera C de manière correcte, avec les paramètres C<(char *host, time_t *timep)>. Il le fait en conservant le C<&> tel quel, de sorte que l'appel de fonction est C. bool_t rpcb_gettime(host,timep) char *host time_t &timep OUTPUT: timep =head2 Insérer des commentaires et des directives de pré-processeur C Des directives de pré-processeur C peuvent prendre place à l'intérieur des blocs BOOT:, PREINIT:, INIT:, CODE:, PPCODE: et CLEANUP:, de même qu'en dehors des fonctions. Les commentaires sont autorisés partout après le mot-clé MODULE. Le compilateur transmet les directives de pré-processeur sans modification et supprime les lignes commentées. On peut rajouter des commentaires dans les XSUB en mettant un C<#> sur une ligne comme premier caractère non blanc. Attention, le commentaire ne doit pas ressembler à une directive de pré-processeur C, sinon il sera interprété comme tel. Le moyen le plus simple d'éviter cela consiste à mettre des caractères blancs devant le C<#>. Si vous utilisez des directives de pré-processeur pour choisir entre deux versions d'une fonction, utilisez #if ... version1 #else /* ... version2 */ #endif et non pas #if ... version1 #endif #if ... version2 #endif car, dans le second cas, xsubpp croira que vous avez défini la fonction deux fois. Par ailleurs, insérez une ligne blanche avant le C<#else> et le C<#endif> afin qu'ils ne soient pas considérés comme une partie intégrante du corps de la fonction. =head2 Utiliser XS avec C++ Si une fonction est définie comme une méthode C++, alors elle considère son premier argument comme un pointeur vers un objet. Le pointeur vers l'objet est stocké dans une variable nommée THIS. L'objet doit avoir été créé en C++ avec la fonction new(), et il doit être béni par Perl (comme avec la fonction bless() en Perl pur) avec la macro sv_setref_pv(). La I d'un objet par Perl peut être effectuée par le typemap. Un exemple de typemap est donné à la fin de cette section. Si la méthode est définie comme statique, elle appellera la fonction C++ en utilisant la syntaxe classe::methode(). Si la méthode n'est pas statique, la fonction sera appelée avec la syntaxe THIS-Emethod(). Les exemples qui suivent utiliseront la classe C++ que voici : class color { public: color(); ~color(); int blue(); void set_blue( int ); private: int c_blue; }; Les XSUB pour les méthodes blue() et set_blue() sont définies avec le nom de la classe, mais le paramètre pour l'objet (THIS, ou "self") est implicite et il n'est pas mentionné dans la liste. int color::blue() void color::set_blue( val ) int val Les deux fonctions attendent un objet en premier paramètre. Le compilateur xsubpp donne à cet objet le nom C, et l'utilise pour appeler la méthode spécifiée. Ainsi, dans le code C++ généré, les méthodes blue() et set_blue() seront appelées de la façon suivante : RETVAL = THIS->blue(); THIS->set_blue( val ); Si le nom de la fonction est B, alors la fonction C++ C sera appelée avec C comme paramètre. void color::DESTROY() Le code C++ appellera C. delete THIS; Si le nom de la fonction est B, la fonction C++ C sera appelée pour créer un objet C++ dynamique. La XSUB s'attendra à recevoir comme premier argument le nom de la classe, qui sera stocké dans une variable nommée C. color * color::new() Le code C++ appellera C : RETVAL = new color(); Voici maintenant un exemple de typemap qui pourrait être utilisé dans cet exemple C++. TYPEMAP color * O_OBJECT OUTPUT # L'objet Perl est beni dans 'CLASS', qui devrait etre un char* # contenant le nom du paquetage servant a le benir. O_OBJECT sv_setref_pv( $arg, CLASS, (void*)$var ); INPUT O_OBJECT if( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) ) $var = ($type)SvIV((SV*)SvRV( $arg )); else{ warn( \"${Package}::$func_name() -- $var n'est pas une reference de SV benie\" ); XSRETURN_UNDEF; } =head2 Méthode de construction d'interfaces Lorsque vous concevez une interface entre Perl et une librairie C, dans de nombreux cas une traduction directe de C vers XS est suffisante. L'interface sera souvent très proche de C et parfois non intuitive, surtout lorsque la fonction C modifie l'un de ses paramètres. Si le programmeur souhaite réaliser une interface de style plus proche de Perl, la méthode suivante peut l'aider à repérer les parties les plus importantes de l'interface. Repérez les fonctions C qui modifient leurs paramètres. Les XSUB de ces fonctions peuvent retourner à Perl des listes, ou bien, en cas d'échec, undef ou une liste vide. Repérez les valeurs qui sont utilisées uniquement par les fonctions C et les XSUB. Si le code Perl lui-même n'a pas besoin d'accéder au contenu d'une valeur, alors il n'est peut-être pas nécessaire de fournir une traduction de cette valeur de C en Perl. Repérez les pointeurs dans la liste de paramètres de la fonction C et dans les valeurs de retour. Certains pointeurs peuvent être traités en XS avec l'opérateur unaire & sur le nom de la variable, tandis que d'autres nécessitent l'utilisation de l'opérateur * sur le nom du type. En général, il est plus facile de travailler avec l'opérateur &. Repérez les structures utilisées par les fonctions C. On peut souvent utiliser le typemap T_PTROBJ pour faire en sorte que ces structures puissent être manipulées par Perl comme des objets bénis. =head2 Objets Perl et structures C Lorsqu'on a affaire à des structures C, il faut choisir soit B, soit B pour le type XS. Ces deux types sont conçus pour manipuler des pointeurs vers des objets complexes. Le type T_PTRREF peut être utilisé avec un objet Perl non béni, tandis que le type T_PTROBJ impose que l'objet soit béni. En utilisant T_PTROBJ, on peut obtenir une sorte de vérification de type, car la XSUB essaiera de vérifier que l'objet Perl possède le type attendu. Le code XS qui suit montre la fonction getnetconfigent() utilisée avec ONC+ TIRPC. Cette fonction retourne un pointeur vers une structure C, et son prototype C est donné plus bas. Cet exemple montrera comment le pointeur C devient une référence Perl. Perl considérera cette référence comme un pointeur sur l'objet béni, et cherchera à appeler un destructeur pour cet objet. Un destructeur sera fourni dans le code XS pour libérer la mémoire utilisée par getnetconfigent(). En XS, les destructeurs peuvent être créés en spécifiant une fonction XSUB dont le nom se termine avec le mot B. Les destructeurs XS peuvent être utilisés pour libérer de la mémoire qui aurait été allouée par malloc() dans une autre XSUB. struct netconfig *getnetconfigent(const char *netid); Un C peut être créé pour C. L'objet Perl sera béni dans une classe dont le nom correspondra au type C, avec un suffixe C ; le nom ne doit pas contenir de blanc s'il doit devenir un nom de paquetage Perl. Le destructeur de l'objet sera mis dans une classe correspondant à la classe de l'objet, et le mot-clé PREFIX sera utilisé pour réduire le nom au seul mot DESTROY, comme prévu dans Perl. typedef struct netconfig Netconfig; MODULE = RPC PACKAGE = RPC Netconfig * getnetconfigent(netid) char *netid MODULE = RPC PACKAGE = NetconfigPtr PREFIX = rpcb_ void rpcb_DESTROY(netconf) Netconfig *netconf CODE: printf("Maintenant dans NetconfigPtr::DESTROY\n"); free( netconf ); Cet exemple requiert l'entrée de typemap suivante. Consultez la section sur les typemaps pour plus d'informations sur l'ajout de nouveaux typemaps dans une extension. TYPEMAP Netconfig * T_PTROBJ Cet exemple sera utilisé avec l'instruction Perl suivante. use RPC; $netconf = getnetconfigent("udp"); Lorsque Perl détruit l'objet référencé par $netconf, il envoie l'objet à la fonction XSUB DESTROY fournie. Perl ne peut pas déterminer, et ça ne l'intéresse pas, que cet objet est une structure C et non un objet Perl. En ce sens, il n'y a pas de différence entre l'objet créé par la XSUB getnetconfigent() et un objet créé par une sous-routine Perl normale. =head2 Le typemap Le typemap est un ensemble de fragments de code utilisés par le compilateur B pour relier les paramètres de fonction et les valeurs C à des valeurs Perl. Le fichier typemap peut contenir trois sections dénommées C, C et C. La section C indique au compilateur comment convertir des valeurs Perl en valeurs de certains types C. La section OUTPUT indique au compilateur comment traduire les valeurs de certains types C en des valeurs accessibles à Perl. La section TYPEMAP indique au compilateur quels fragments de code, dans les sections INPUT et OUTPUT, doivent être utilisés pour faire correspondre un type C donné à une valeur Perl. Chacune des sections du typemap doit être précédée de l'un des mots-clés TYPEMAP, INPUT et OUTPUT. Le typemap par défaut, dans le répertoire C du code source Perl, contient un grand nombre de types utiles qui sont mis à disposition des extensions Perl. Certaines extensions définissent des typemaps supplémentaires qu'elles peuvent conserver dans leur propre répertoire. Ces typemaps supplémentaires peuvent se référer aux tables de correspondance INPUT et OUTPUT du typemap principal. Le compilateur B permet au typemap d'une extension de redéfinir des correspondances présentes dans le typemap par défaut. La plupart des extensions qui nécessitent un typemap personnalisé n'ont besoin que de la section TYPEMAP du fichier typemap. Le typemap personnalisé utilisé dans l'exemple getnetconfigent() présenté précédemment montre ce qui est peut-être l'utilisation typique des typemaps dans les extensions. Ce typemap est utilisé pour faire correspondre une structure C au typemap T_PTROBJ. Le typemap utilisé par getnetconfigent() est présenté ici. Remarquez que le type C est séparé du type XS avec une tabulation, et que l'opérateur unaire C<*> de C est considéré comme une partie du nom du type C. TYPEMAP Netconfig *T_PTROBJ Voici un exemple plus compliqué. Supposons que vous vouliez que C soit béni dans la classe C. Une manière de le faire est d'utiliser de la manière suivante le caractère souligné (_) pour séparer les noms de paquetage : typedef struct netconfig * Net_Config; et de fournir ensuite une entrée de typemap C qui relie le souligné au quadruple point (::), puis de déclarer C avec ce type : TYPEMAP Net_Config T_PTROBJ_SPECIAL INPUT T_PTROBJ_SPECIAL if (sv_derived_from($arg, \"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\")) { IV tmp = SvIV((SV*)SvRV($arg)); $var = ($type) tmp; } else croak(\"$var n'est pas de type ${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\") OUTPUT T_PTROBJ_SPECIAL sv_setref_pv($arg, \"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\", (void*)$var); Les sections INPUT et OUTPUT substituent à la volée le souligné au quadruple point, ce qui produit l'effet désiré. Cet exemple donne une idée du pouvoir et de la souplesse du mécanisme des typemaps. =head1 EXEMPLES Fichier C : interface avec certaines fonctions de la librairie de connexion ONC+ RPC. #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include typedef struct netconfig Netconfig; MODULE = RPC PACKAGE = RPC SV * rpcb_gettime(host="localhost") char *host PREINIT: time_t timep; CODE: ST(0) = sv_newmortal(); if( rpcb_gettime( host, &timep ) ) sv_setnv( ST(0), (double)timep ); Netconfig * getnetconfigent(netid="udp") char *netid MODULE = RPC PACKAGE = NetconfigPtr PREFIX = rpcb_ void rpcb_DESTROY(netconf) Netconfig *netconf CODE: printf("NetconfigPtr::DESTROY\n"); free( netconf ); Fichier C : typemap personnalisé pour RPC.xs. TYPEMAP Netconfig * T_PTROBJ Fichier C : module Perl pour l'extension RPC. package RPC; require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT = qw(rpcb_gettime getnetconfigent); bootstrap RPC; 1; Fichier C : programme de test Perl pour l'extension RPC. use RPC; $netconf = getnetconfigent(); $a = rpcb_gettime(); print "time = $a\n"; print "netconf = $netconf\n"; $netconf = getnetconfigent("tcp"); $a = rpcb_gettime("poplar"); print "time = $a\n"; print "netconf = $netconf\n"; =head1 VERSION DE XS Ce document couvre les fonctionnalités supportées par C 1.935. =head1 AUTEUR Dean Roehrich > Jul 8, 1996. =head1 TRADUCTION =head2 Version Cette traduction française correspond à la version anglaise distribuée avec perl 5.005_02. Pour en savoir plus concernant ces traductions, consultez L. =head2 Traducteur Thierry Bézecourt > =head2 Relecture Régis Julié > =cut