Il s'agit de la commande perlcall qui peut être exécutée dans le fournisseur d'hébergement gratuit OnWorks en utilisant l'un de nos multiples postes de travail en ligne gratuits tels que Ubuntu Online, Fedora Online, l'émulateur en ligne Windows ou l'émulateur en ligne MAC OS
PROGRAMME:
Nom
perlcall - Conventions d'appel Perl à partir du C
DESCRIPTION
Le but de ce document est de vous montrer comment appeler des sous-routines Perl directement depuis C,
c'est-à-dire comment écrire rappels.
En plus de discuter de l'interface C fournie par Perl pour écrire des rappels, le document
utilise une série d'exemples pour montrer comment l'interface fonctionne réellement dans la pratique. Dans
De plus, certaines techniques de codage des rappels sont couvertes.
Les exemples où les rappels sont nécessaires incluent
· Un gestionnaire d'erreurs
Vous avez créé une interface XSUB vers l'API C d'une application.
Une fonctionnalité assez courante dans les applications est de vous permettre de définir une fonction C qui
sera appelé chaque fois que quelque chose de désagréable se produit. Ce que nous voudrions, c'est pouvoir
spécifiez un sous-programme Perl qui sera appelé à la place.
· Un programme événementiel
L'exemple classique d'utilisation des rappels est lors de l'écriture d'un événement piloté
programme, comme pour une application X11. Dans ce cas, vous enregistrez les fonctions à
appelé chaque fois que des événements spécifiques se produisent, par exemple, un bouton de la souris est enfoncé, le curseur
se déplace dans une fenêtre ou un élément de menu est sélectionné.
Bien que les techniques décrites ici soient applicables lors de l'intégration de Perl dans un programme C,
ce n'est pas le but premier de ce document. Il y a d'autres détails qui doivent être
considérés et sont spécifiques à l'intégration de Perl. Pour plus de détails sur l'intégration de Perl dans C, reportez-vous à
perlé.
Avant de vous lancer tête baissée dans la suite de ce document, ce serait une bonne
idée d'avoir lu les deux documents suivants--perlxs et perlguts.
L' APPELER_ FONCTIONS
Bien que ce truc soit plus facile à expliquer à l'aide d'exemples, vous devez d'abord être conscient de quelques
définitions importantes.
Perl possède un certain nombre de fonctions C qui vous permettent d'appeler des sous-routines Perl. Ils sont
I32 call_sv(SV* sv, indicateurs I32);
I32 call_pv(char *sous-nom, indicateurs I32);
I32 call_method(char *methname, indicateurs I32);
I32 call_argv(char *sous-nom, indicateurs I32, char **argv);
La fonction clé est appel_sv. Toutes les autres fonctions sont des wrappers assez simples qui
facilitent l'appel des sous-routines Perl dans des cas particuliers. À la fin de la journée, ils seront
tous appellent appel_sv pour invoquer le sous-programme Perl.
Tous les appeler_* les fonctions ont un paramètre "flags" qui est utilisé pour passer un masque de bits de
options à Perl. Ce masque de bits fonctionne de manière identique pour chacune des fonctions. le
les paramètres disponibles dans le masque de bits sont décrits dans "VALEURS DE DRAPEAU".
Chacune des fonctions sera maintenant discutée tour à tour.
appel_sv
appel_sv prend deux paramètres. Le premier, "sv", est un SV*. Cela vous permet de spécifier
la sous-routine Perl à appeler soit comme une chaîne C (qui a d'abord été convertie
à un SV) ou une référence à un sous-programme. La section, En utilisant appel_sv, montre comment vous
peut utiliser appel_sv.
appel_pv
La fonction, appel_pv, est similaire à appel_sv sauf qu'il s'attend à ce que son premier paramètre
être un caractère C* qui identifie le sous-programme Perl que vous souhaitez appeler, par exemple,
"call_pv("fred", 0)". Si le sous-programme que vous souhaitez appeler se trouve dans un autre package, il suffit de
inclure le nom du package dans la chaîne, par exemple, "pkg::fred".
méthode_appel
La fonction méthode_appel est utilisé pour appeler une méthode d'une classe Perl. Le paramètre
"methname" correspond au nom de la méthode à appeler. A noter que la classe
à laquelle appartient la méthode est passé sur la pile Perl plutôt que dans le paramètre
liste. Cette classe peut être soit le nom de la classe (pour une méthode statique) soit un
référence à un objet (pour une méthode virtuelle). Voir perlobj pour plus d'informations sur
méthodes statiques et virtuelles et "Using call_method" pour un exemple d'utilisation
méthode_appel.
appel_argv
appel_argv appelle le sous-programme Perl spécifié par la chaîne C stockée dans le "sous-nom"
paramètre. Il prend également le paramètre habituel « drapeaux ». Le dernier paramètre, "argv",
consiste en une liste terminée par NULL de chaînes C à transmettre en tant que paramètres au
Sous-routine Perl. Voir En utilisant appel_argv.
Toutes les fonctions renvoient un entier. Il s'agit du nombre d'articles retournés par
le sous-programme Perl. Les éléments réels renvoyés par la sous-routine sont stockés sur le Perl
association.
En règle générale, vous devez toujours vérifier la valeur de retour de ces fonctions. Même si
vous vous attendez à ce qu'un nombre particulier de valeurs soit renvoyé par Perl
sous-programme, rien n'empêche quelqu'un de faire quelque chose d'inattendu - ne dites pas
vous n'avez pas été prévenu.
FLAG VALEURS
Le paramètre « drapeaux » dans tous les appeler_* est l'une des fonctions G_VOID, G_SCALAR ou G_ARRAY,
qui indiquent le contexte de l'appel, OR avec un masque de bits de n'importe quelle combinaison des
autres symboles G_* définis ci-dessous.
G_VOID
Appelle la sous-routine Perl dans un contexte vide.
Ce drapeau a 2 effets :
1. Il indique au sous-programme appelé qu'il s'exécute dans un contexte vide
(s'il exécute désirer un tableau le résultat sera la valeur indéfinie).
2. Il garantit que rien n'est réellement renvoyé par le sous-programme.
La valeur renvoyée par le appeler_* fonction indique combien d'articles ont été retournés par
la sous-routine Perl - dans ce cas, ce sera 0.
G_SCALAIRE
Appelle la sous-routine Perl dans un contexte scalaire. Il s'agit du paramètre d'indicateur de contexte par défaut
pour tous les appeler_* fonctions.
Ce drapeau a 2 effets :
1. Il indique au sous-programme appelé qu'il s'exécute dans un contexte scalaire
(s'il exécute désirer un tableau le résultat sera faux).
2. Il garantit que seul un scalaire est réellement renvoyé du sous-programme. le
le sous-programme peut, bien sûr, ignorer le désirer un tableau et retourner une liste de toute façon. Le cas échéant,
alors seul le dernier élément de la liste sera renvoyé.
La valeur renvoyée par le appeler_* fonction indique combien d'articles ont été retournés par
la sous-routine Perl - dans ce cas, ce sera 0 ou 1.
Si 0, alors vous avez spécifié l'indicateur G_DISCARD.
Si 1, alors l'élément réellement renvoyé par le sous-programme Perl sera stocké sur le Perl
pile - la section Retour a Scalaire montre comment accéder à cette valeur sur la pile.
N'oubliez pas que quel que soit le nombre d'éléments renvoyés par le sous-programme Perl, seul le dernier
sera accessible depuis la pile - pensez au cas où une seule valeur est renvoyée comme
étant une liste avec un seul élément. Tous les autres articles qui ont été retournés n'existeront pas par
le contrôle horaire revient de la appeler_* une fonction. La section Retour a liste in a
scalaire aux contextes montre un exemple de ce comportement.
G_ARRAY
Appelle la sous-routine Perl dans un contexte de liste.
Comme avec G_SCALAR, ce drapeau a 2 effets :
1. Il indique au sous-programme appelé qu'il s'exécute dans un contexte de liste
(s'il exécute désirer un tableau le résultat sera vrai).
2. Il garantit que tous les éléments renvoyés par le sous-programme seront accessibles lorsque
les retours de contrôle du appeler_* la fonction.
La valeur renvoyée par le appeler_* fonction indique combien d'articles ont été retournés par
le sous-programme Perl.
Si 0, alors vous avez spécifié l'indicateur G_DISCARD.
Si ce n'est pas 0, il s'agira du nombre d'éléments renvoyés par le sous-programme. Ces
les éléments seront stockés sur la pile Perl. La section Retour a liste of valeurs donne un
exemple d'utilisation de l'indicateur G_ARRAY et de la mécanique d'accès aux éléments retournés à partir de
la pile Perl.
G_DISCARD
Par défaut, le appeler_* les fonctions placent les éléments renvoyés par la sous-routine Perl sur
la pile. Si vous n'êtes pas intéressé par ces éléments, la définition de ce drapeau fera
Perl les supprime automatiquement pour vous. A noter qu'il est toujours possible d'indiquer un
contexte au sous-programme Perl en utilisant G_SCALAR ou G_ARRAY.
Si vous ne définissez pas cet indicateur, il est très important que vous vous assuriez que tout
temporaires (c'est-à-dire les paramètres passés au sous-programme Perl et les valeurs renvoyées par le
sous-routine) sont éliminés vous-même. La section Retour a Scalaire donne des détails sur la façon dont
de disposer de ces temporaires explicitement et la section En utilisant Perl à disposer of
temporaires discute des circonstances spécifiques où vous pouvez ignorer le problème et laisser
Perl s'en occupe pour vous.
G_NOARGS
Chaque fois qu'un sous-programme Perl est appelé en utilisant l'un des appeler_* fonctions, il est supposé par
par défaut, les paramètres doivent être passés au sous-programme. Si vous ne passez aucun
paramètres à la sous-routine Perl, vous pouvez gagner un peu de temps en définissant ce drapeau. Ce
a pour effet de ne pas créer le tableau @_ pour le sous-programme Perl.
Bien que la fonctionnalité fournie par cet indicateur puisse sembler simple, elle devrait être
utilisé que s'il y a une bonne raison de le faire. La raison d'être prudent est que, même
si vous avez spécifié le drapeau G_NOARGS, il est toujours possible pour le sous-programme Perl que
a été appelé pour penser que vous lui avez passé des paramètres.
En fait, ce qui peut arriver, c'est que le sous-programme Perl que vous avez appelé puisse accéder au @_
tableau d'un sous-programme Perl précédent. Cela se produira lorsque le code qui s'exécute
le appeler_* fonction a elle-même été appelée à partir d'un autre sous-programme Perl. Le code ci-dessous
illustre ceci
sous fred
{ imprimer "@_\n" }
sous joe
{ &fred }
&joe(1,2,3);
Cela imprimera
1 2 3
Ce qui s'est passé, c'est que "fred" accède au tableau @_ qui appartient à "joe".
G_EVAL
Il est possible que le sous-programme Perl que vous appelez se termine anormalement, par exemple en
appel la explicitement ou en n'existant pas réellement. Par défaut, lorsque l'un de ces
se produit, le processus se terminera immédiatement. Si vous voulez piéger ce type de
événement, spécifiez l'indicateur G_EVAL. Cela mettra un eval { } autour de l'appel de sous-programme.
Chaque fois que le contrôle revient du appeler_* fonction, vous devez vérifier la variable $@ pendant que vous
serait dans un script Perl normal.
La valeur renvoyée par le appeler_* la fonction dépend de ce que les autres drapeaux ont été
spécifié et si une erreur s'est produite. Voici tous les différents cas qui peuvent
se produire:
· Si la appeler_* la fonction retourne normalement, alors la valeur retournée est comme spécifié dans
les sections précédentes.
· Si G_DISCARD est spécifié, la valeur de retour sera toujours 0.
· Si G_ARRAY est spécifié et une erreur s'est produite, la valeur de retour sera toujours 0.
· Si G_SCALAIRE est spécifié et une erreur s'est produite, la valeur de retour sera 1 et
la valeur en haut de la pile sera indéfini. Cela signifie que si vous avez déjà
détecté l'erreur en vérifiant $@ et vous voulez que le programme continue, vous devez
n'oubliez pas de faire sauter le indéfini de la pile.
See En utilisant G_EVAL pour plus de détails sur l'utilisation de G_EVAL.
G_KEEPERR
L'utilisation du drapeau G_EVAL décrit ci-dessus définira toujours $@ : l'effacer s'il n'y avait pas
error, et le configurer pour décrire l'erreur s'il y avait une erreur dans le code appelé.
C'est ce que vous voulez si votre intention est de gérer d'éventuelles erreurs, mais parfois vous
Je veux juste piéger les erreurs et les empêcher d'interférer avec le reste du programme.
Ce scénario s'appliquera principalement au code destiné à être appelé depuis l'intérieur
destructeurs, rappels asynchrones et gestionnaires de signaux. Dans de telles situations, où le
le code appelé a peu de rapport avec le contexte dynamique environnant, le programme principal
doit être isolé des erreurs dans le code appelé, même si elles ne peuvent pas être traitées
intelligemment. Il peut également être utile de le faire avec le code pour "__DIE__" ou "__WARN__"
crochets et fonctions « attaches ».
L'indicateur G_KEEPERR est destiné à être utilisé en conjonction avec G_EVAL dans appeler_* fonctions qui
sont utilisés pour implémenter un tel code, ou avec "eval_sv". Ce drapeau n'a aucun effet sur le
"call_*" fonctionne lorsque G_EVAL n'est pas utilisé.
Lorsque G_KEEPERR est utilisé, toute erreur dans le code appelé terminera l'appel comme d'habitude, et
l'erreur ne se propagera pas au-delà de l'appel (comme d'habitude pour G_EVAL), mais elle n'ira pas
en $@. Au lieu de cela, l'erreur sera convertie en un avertissement, précédé de la chaîne
"\t(en cours de nettoyage)". Cela peut être désactivé en utilisant "pas d'avertissements 'misc'". S'il n'y a pas d'erreur,
$@ ne sera pas effacé.
Notez que le drapeau G_KEEPERR ne se propage pas dans les évaluations internes ; ceux-ci peuvent toujours définir $@.
Le drapeau G_KEEPERR a été introduit dans Perl version 5.002.
See En utilisant G_KEEPERR pour un exemple de situation qui justifie l'utilisation de ce drapeau.
Détermination le Contexte
Comme mentionné ci-dessus, vous pouvez déterminer le contexte du sous-programme en cours d'exécution dans
Perl avec désirer un tableau. Le test équivalent peut être fait en C en utilisant la macro "GIMME_V",
qui renvoie "G_ARRAY" si vous avez été appelé dans un contexte de liste, "G_SCALAR" si dans un
contexte scalaire, ou "G_VOID" si dans un contexte vide (c'est-à-dire que la valeur de retour ne sera pas
utilisé). Une ancienne version de cette macro s'appelle "GIMME" ; dans un contexte vide, il renvoie
"G_SCALAIRE" au lieu de "G_VOID". Un exemple d'utilisation de la macro "GIMME_V" est montré dans
En utilisant GIMME_V.
EXEMPLES
Assez parlé de définition ! Ayons quelques exemples.
Perl fournit de nombreuses macros pour aider à accéder à la pile Perl. Dans la mesure du possible, ces
les macros doivent toujours être utilisées lors de l'interfaçage avec les composants internes de Perl. Nous espérons que cela devrait faire
le code moins vulnérable à toute modification apportée à Perl à l'avenir.
Un autre point à noter est que dans la première série d'exemples je n'ai utilisé que
le appel_pv une fonction. Cela a été fait pour garder le code plus simple et vous faciliter la tâche
sujet. Dans la mesure du possible, si le choix est entre utiliser appel_pv et appel_sv, tu devrais
essayez toujours d'utiliser appel_sv. Voir En utilisant appel_sv pour en savoir plus.
Non Paramètres, le Rien Revenu
Ce premier exemple trivial appellera un sous-programme Perl, ImprimerUID, pour imprimer l'UID de
le processus.
sous-PrintUID
{
print "UID est $<\n" ;
}
et voici une fonction C pour l'appeler
vide statique
appel_PrintUID()
{
dSP ;
POUSSOIR(SP);
call_pv("PrintUID", G_DISCARD|G_NOARGS);
}
Simple, hein ?
Quelques points à noter sur cet exemple :
1. Ignorez "dSP" et "PUSHMARK(SP)" pour le moment. Ils seront discutés dans l'exemple suivant.
2. Nous ne transmettons aucun paramètre à ImprimerUID donc G_NOARGS peut être spécifié.
3. Nous ne sommes pas intéressés par les retours de ImprimerUID, donc G_DISCARD est spécifié.
Même si ImprimerUID a été modifié pour renvoyer une ou plusieurs valeurs, après avoir spécifié G_DISCARD
signifient qu'ils seront effacés par les retours de contrôle horaire de appel_pv.
4. Comme appel_pv est utilisé, le sous-programme Perl est spécifié comme une chaîne C. Dans ce
dans le cas où le nom du sous-programme a été « câblé » dans le code.
5. Étant donné que nous avons spécifié G_DISCARD, il n'est pas nécessaire de vérifier la valeur renvoyée par
appel_pv. Ce sera toujours 0.
En passant Paramètres
Faisons maintenant un exemple un peu plus complexe. Cette fois, nous voulons appeler un Perl
sous-programme, "LeftString", qui prendra 2 paramètres--une chaîne ($s) et un entier ($n).
Le sous-programme imprimera simplement les premiers $n caractères de la chaîne.
Ainsi, le sous-programme Perl ressemblerait à ceci :
sous chaîne gauche
{
mon($s, $n) = @_;
print substr($s, 0, $n), "\n" ;
}
La fonction C requise pour appeler Chaîne gauche ressemblerait à ceci:
vide statique
call_LeftString (a, b)
caractère * a;
int b;
{
dSP ;
ENTRER;
SAVETMPS ;
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSVpv(a, 0)));
XPUSHs(sv_2mortal(newSViv(b)));
RETOUR ;
call_pv("LeftString", G_DISCARD);
FREETMPS ;
LAISSER;
}
Voici quelques notes sur la fonction C appel_LeftString.
1. Les paramètres sont passés à la sous-routine Perl en utilisant la pile Perl. C'est le
but du code commençant par la ligne "dSP" et se terminant par la ligne "PUTBACK".
Le "dSP" déclare une copie locale du pointeur de pile. Cette copie locale doit toujours
être accessible en tant que "SP".
2. Si vous allez mettre quelque chose sur la pile Perl, vous devez savoir où mettre
ce. C'est le but de la macro "dSP" : elle déclare et initialise un locales copier
du pointeur de pile Perl.
Toutes les autres macros qui seront utilisées dans cet exemple nécessitent que vous ayez utilisé cette
macro.
L'exception à cette règle est si vous appelez un sous-programme Perl directement à partir d'un
Fonction XSUB. Dans ce cas, il n'est pas nécessaire d'utiliser explicitement la macro "dSP" - elle
sera déclaré pour vous automatiquement.
3. Tous les paramètres à pousser sur la pile doivent être encadrés par le "PUSHMARK" et
Macros « PUTBACK ». Le but de ces deux macros, dans ce contexte, est de compter les
nombre de paramètres que vous poussez automatiquement. Ensuite, chaque fois que Perl crée
le tableau @_ pour le sous-programme, il sait quelle taille il faut.
La macro "PUSHMARK" dit à Perl de noter mentalement le pointeur de pile actuel.
Même si vous ne passez aucun paramètre (comme dans l'exemple présenté dans la section Non
Paramètres, le Rien Revenu) vous devez toujours appeler la macro "PUSHMARK" avant de pouvoir
appeler l'un des appeler_* fonctions--Perl a encore besoin de savoir qu'il n'y a pas
paramètres.
La macro "PUTBACK" définit la copie globale du pointeur de pile pour qu'elle soit la même que notre
copie locale. Si nous ne l'avons pas fait, appel_pv ne saurait pas où les deux paramètres que nous
poussé étaient - rappelez-vous que jusqu'à présent, toutes les manipulations de pointeur de pile que nous avons effectuées
est avec notre copie locale, pas la copie globale.
4. Ensuite, nous arrivons aux XPUSH. C'est là que les paramètres sont réellement poussés sur le
empiler. Dans ce cas, nous poussons une chaîne et un entier.
Voir « XSUB et la pile d'arguments » dans perlguts pour plus de détails sur la façon dont les macros XPUSH
.
5. Parce que nous avons créé des valeurs temporaires (au moyen de sv_2mortel() appels) nous devrons
rangez la pile Perl et éliminez les SV mortels.
Tel est le but de
ENTRER;
SAVETMPS ;
au début de la fonction, et
FREETMPS ;
LAISSER;
à la fin. La paire "ENTER"/"SAVETMPS" crée une limite pour tous les temporaires que nous
créer. Cela signifie que les temporaires dont nous nous débarrassons seront limités à ceux qui
ont été créés après ces appels.
La paire "FREETMPS"/"LEAVE" se débarrassera de toutes les valeurs renvoyées par Perl
sous-routine (voir l'exemple suivant), et il videra également les SV mortels que nous avons créés.
Avoir "ENTER"/"SAVETMPS" au début du code garantit qu'aucun autre
les mortels sont détruits.
Considérez ces macros comme fonctionnant un peu comme "{" et "}" en Perl pour limiter la portée de
variables locales.
Voir la section En utilisant Perl à disposer of Temporaires pour plus de détails sur une alternative à
en utilisant ces macros.
6. Finalement, Chaîne gauche peut maintenant être appelé via le appel_pv une fonction. Le seul drapeau
spécifié cette fois est G_DISCARD. Parce que nous passons 2 paramètres au Perl
sous-programme cette fois, nous n'avons pas spécifié G_NOARGS.
Retour a Scalaire
Maintenant, un exemple de traitement des éléments renvoyés par un sous-programme Perl.
Voici un sous-programme Perl, additionneur, qui prend 2 paramètres entiers et renvoie simplement leur
somme.
sous-additionneur
{
mon($a, $b) = @_;
$a + $b ;
}
Parce que nous sommes maintenant concernés par la valeur de retour de additionneur, la fonction C requise pour
l'appeler est maintenant un peu plus complexe.
vide statique
appel_Adder(a, b)
int a;
int b;
{
dSP ;
nombre int;
ENTRER;
SAVETMPS ;
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
RETOUR ;
count = call_pv("Adder", G_SCALAR);
ESPAGNE ;
si (compte != 1)
croak("Gros problème\n");
printf ("La somme de %d et %d est %d\n", a, b, POPi);
RETOUR ;
FREETMPS ;
LAISSER;
}
Les points à noter cette fois sont
1. Le seul indicateur spécifié cette fois était G_SCALAR. Cela signifie que le tableau @_ sera
créé et que la valeur renvoyée par additionneur existera toujours après l'appel à
appel_pv.
2. Le but de la macro "SPAGAIN" est de rafraîchir la copie locale du pointeur de pile.
Ceci est nécessaire car il est possible que la mémoire allouée à la pile Perl
a été réaffecté au cours de la appel_pv appel.
Si vous utilisez le pointeur de pile Perl dans votre code, vous devez toujours actualiser
la copie locale en utilisant SPAGAIN chaque fois que vous utilisez le appeler_* fonctions ou tout
autre fonction interne de Perl.
3. Bien qu'une seule valeur soit censée être renvoyée de additionneur, c'est toujours bon
pratique pour vérifier le code de retour de appel_pv de toute façon.
Attendre une valeur unique n'est pas tout à fait la même chose que de savoir qu'il y en aura une. Si
quelqu'un a modifié additionneur pour retourner une liste et nous n'avons pas vérifié cette possibilité et
prendre les mesures appropriées, la pile Perl se retrouverait dans un état incohérent. C'est-à-dire
quelque chose que tu vraiment ne veux jamais arriver.
4. La macro "POPi" est utilisée ici pour extraire la valeur de retour de la pile. Dans ce cas
nous voulions un entier, donc "POPi" a été utilisé.
Voici la liste complète des macros POP disponibles, ainsi que les types qu'elles renvoient.
POP SV
pointeur POPp
POP double
POPi entier
POP longue
5. Le "PUTBACK" final est utilisé pour laisser la pile Perl dans un état cohérent avant
quitter la fonction. Ceci est nécessaire car lorsque nous avons extrait la valeur de retour de
la pile avec "POPi", il n'a mis à jour que notre copie locale du pointeur de pile. Rappelles toi,
"PUTBACK" définit le pointeur de pile global pour qu'il soit le même que notre copie locale.
Retour a Liste of VALEURS
Maintenant, étendons l'exemple précédent pour renvoyer à la fois la somme des paramètres et le
différence.
Voici le sous-programme Perl
sous-AjouterSoustraire
{
mon($a, $b) = @_;
($a+$b, $a-$b);
}
et c'est la fonction C
vide statique
call_AddSubtract(a, b)
int a;
int b;
{
dSP ;
nombre int;
ENTRER;
SAVETMPS ;
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
RETOUR ;
count = call_pv("AjouterSoustraction", G_ARRAY);
ESPAGNE ;
si (compte != 2)
croak("Gros problème\n");
printf ("%d - %d = %d\n", a, b, POPi);
printf ("%d + %d = %d\n", a, b, POPi);
RETOUR ;
FREETMPS ;
LAISSER;
}
If call_AddSubtract s'appelle comme ça
call_AddSubtract(7, 4);
alors voici la sortie
7 - 4 = 3
7 + 4 = 11
Remarques
1. Nous voulions un contexte de liste, donc G_ARRAY a été utilisé.
2. Sans surprise, "POPi" est utilisé deux fois cette fois car nous récupérons 2 valeurs
de la pile. La chose importante à noter est que lors de l'utilisation des macros "POP*", elles
sortir de la pile dans inverser ordre.
Retour a Liste in a Scalaire Contexte
Supposons que le sous-programme Perl de la section précédente a été appelé dans un contexte scalaire, comme celui-ci
vide statique
call_AddSubScalar(a, b)
int a;
int b;
{
dSP ;
nombre int;
int i;
ENTRER;
SAVETMPS ;
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
RETOUR ;
count = call_pv("AjouterSoustraction", G_SCALAIRE);
ESPAGNE ;
printf ("Éléments renvoyés = %d\n", count);
pour (i = 1; i <= compter; ++i)
printf ("Valeur %d = %d\n", i, POPi);
RETOUR ;
FREETMPS ;
LAISSER;
}
L'autre modification apportée est que call_AddSubScalar imprimera le nombre d'articles
renvoyés par le sous-programme Perl et leur valeur (pour plus de simplicité, il suppose qu'ils sont
entier). Donc si call_AddSubScalar est appelé
call_AddSubScalar (7, 4);
alors la sortie sera
Articles retournés = 1
Valeur 1 = 3
Dans ce cas, le point principal à noter est que seul le dernier élément de la liste est renvoyé
du sous-programme. AjouterSoustraire en fait, je suis revenu à call_AddSubScalar.
Retour Sauvegarde de Perl via le Paramètre Liste
Il est également possible de retourner des valeurs directement via la liste des paramètres - que ce soit
réellement souhaitable de le faire est une tout autre affaire.
Le sous-programme Perl, Inc, ci-dessous prend 2 paramètres et incrémente chacun directement.
sous Inc
{
++ $_[0] ;
++ $_[1] ;
}
et voici une fonction C pour l'appeler.
vide statique
appel_Inc(a, b)
int a;
int b;
{
dSP ;
nombre int;
SV * sva;
SV * svb ;
ENTRER;
SAVETMPS ;
sva = sv_2mortel(nouveauSViv(a));
svb = sv_2mortel(nouveauSViv(b));
POUSSOIR(SP);
XPUSH(sva);
XPUSH(svb);
RETOUR ;
count = call_pv("Inc", G_DISCARD);
si (compte != 0)
croak ("call_Inc : 0 valeurs attendues de 'Inc', obtenu %d\n",
compter);
printf ("%d + 1 = %d\n", a, SvIV(sva));
printf ("%d + 1 = %d\n", b, SvIV(svb));
FREETMPS ;
LAISSER;
}
Pour pouvoir accéder aux deux paramètres qui ont été poussés sur la pile après leur retour
appel_pv il est nécessaire de noter leurs adresses - ainsi les deux variables
"sva" et "svb".
La raison pour laquelle cela est nécessaire est que la zone de la pile Perl qui les contenait sera très
ont probablement été écrasés par quelque chose d'autre par les retours de contrôle de temps de appel_pv.
En utilisant G_EVAL
Maintenant un exemple utilisant G_EVAL. Ci-dessous se trouve un sous-programme Perl qui calcule la différence de
ses 2 paramètres. Si cela aboutit à un résultat négatif, le sous-programme appelle la.
soustraire soustraire
{
mon ($a, $b) = @_;
die "la mort peut être fatale\n" si $a < $b ;
$a - $b ;
}
et du C pour l'appeler
vide statique
appeler_Subtract(a, b)
int a;
int b;
{
dSP ;
nombre int;
ENTRER;
SAVETMPS ;
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
RETOUR ;
count = call_pv("Soustraire", G_EVAL|G_SCALAIRE);
ESPAGNE ;
/* Vérifier d'abord l'évaluation */
si (SvVRAI(ERRSV))
{
printf ("Euh oh - %s\n", SvPV_nolen(ERRSV));
POP ;
}
d'autre
{
si (compte != 1)
croak("call_Subtract : voulait 1 valeur de 'Soustract', a obtenu %d\n",
compter);
printf ("%d - %d = %d\n", a, b, POPi);
}
RETOUR ;
FREETMPS ;
LAISSER;
}
If appeler_Soustraire s'appelle ainsi
appeler_Subtract(4, 5)
ce qui suit sera imprimé
Uh oh - la mort peut être fatale
Remarques
1. Nous voulons être en mesure d'attraper le la nous avons donc utilisé le drapeau G_EVAL. Ne pas préciser
ce drapeau signifierait que le programme se terminerait immédiatement à la la
instruction dans le sous-programme Soustraire.
2. Le code
si (SvVRAI(ERRSV))
{
printf ("Euh oh - %s\n", SvPV_nolen(ERRSV));
POP ;
}
est l'équivalent direct de ce bit de Perl
affiche "Euh oh - $@\n" si $@ ;
"PL_errgv" est un perl global de type "GV *" qui pointe vers l'entrée de la table des symboles
contenant l'erreur. "ERRSV" fait donc référence à l'équivalent en C de $@.
3. Notez que la pile est éclatée à l'aide de "POPs" dans le bloc où "SvTRUE(ERRSV)" est
vrai. Ceci est nécessaire car chaque fois qu'un appeler_* fonction invoquée avec
G_EVAL|G_SCALAR renvoie une erreur, le haut de la pile contient la valeur indéfini. Car
nous voulons que le programme continue après avoir détecté cette erreur, il est essentiel que le
pile être rangé en enlevant le indéfini.
En utilisant G_KEEPERR
Considérez cet exemple plutôt facétieux, où nous avons utilisé une version XS du
call_Subtract exemple ci-dessus à l'intérieur d'un destructeur :
paquet Foo;
sous-nouveau { bénir {}, $_[0] }
soustraire {
mon($a,$b) = @_;
die "la mort peut être fatale" si $a < $b;
$a - $b ;
}
sub DESTROY { call_Subtract (5, 4); }
sub foo { die "foo dies"; }
paquet principal;
{
mon $foo = Foo->nouveau ;
eval { $foo->foo } ;
}
print "Saw : $@" if $@ ; # devrait être, mais ne l'est pas
Cet exemple ne reconnaîtra pas qu'une erreur s'est produite à l'intérieur de "eval {}". Voici
pourquoi : le code call_Subtract a été exécuté pendant que perl nettoyait les temporaires quand
sortant du bloc accolade externe, et parce que call_Subtract est implémenté avec appel_pv
en utilisant le drapeau G_EVAL, il réinitialise rapidement $@. Cela se traduit par l'échec de la
test pour $@, et donc l'échec du piège d'erreur.
L'ajout du drapeau G_KEEPERR, de sorte que le appel_pv call in call_Subtract lit :
count = call_pv("Soustraire", G_EVAL|G_SCALAR|G_KEEPERR);
préservera l'erreur et restaurera une gestion fiable des erreurs.
En utilisant appel_sv
Dans tous les exemples précédents, j'ai 'câblé' le nom du sous-programme Perl à
appelé depuis C. La plupart du temps cependant, il est plus pratique de pouvoir spécifier le
nom de la sous-routine Perl à partir du script Perl.
Considérez le code Perl ci-dessous
sous fred
{
print "Bonjour\n" ;
}
CallSubPV("fred");
Voici un extrait de XSUB qui définit AppelSousPV.
annuler
CallSubPV(nom)
caractère * nom
CODE:
POUSSOIR(SP);
call_pv(nom, G_DISCARD|G_NOARGS);
C'est très bien dans la mesure où il va. Le fait est que le sous-programme Perl peut être spécifié comme
une chaîne, cependant, Perl autorise les références à des sous-programmes et des sous-programmes anonymes. Cette
est ou appel_sv est utile.
Le code ci-dessous pour AppelSousSV est identique à AppelSousPV sauf que le paramètre "name" est
maintenant défini comme un SV* et nous utilisons appel_sv au lieu de appel_pv.
annuler
CallSubSV(nom)
SV * nom
CODE:
POUSSOIR(SP);
call_sv(nom, G_DISCARD|G_NOARGS);
Parce que nous utilisons un SV pour appeler Paix les éléments suivants peuvent tous être utilisés :
CallSubSV("fred");
CallSubSV(\&fred);
$ref = \&fred;
AppelSubSV($ref);
CallSubSV( sub { print "Bonjour\n" } );
Comme vous pouvez le voir, appel_sv vous donne beaucoup plus de flexibilité dans la façon dont vous pouvez spécifier le Perl
sous-programme.
A noter que, s'il est nécessaire de stocker le SV ("nom" dans l'exemple ci-dessus)
qui correspond au sous-programme Perl pour qu'il puisse être utilisé plus tard dans le programme, il
pas assez juste pour stocker une copie du pointeur vers le SV. Disons que le code ci-dessus était comme
ce:
statique SV * RememberSub;
annuler
SaveSub1(nom)
SV * nom
CODE:
souvenirSub = nom;
annuler
AppelSavedSub1()
CODE:
POUSSOIR(SP);
call_sv(rememberSub, G_DISCARD|G_NOARGS);
La raison pour laquelle c'est faux est que, au moment où vous utilisez le pointeur "rememberSub" dans
"CallSavedSub1", il peut ou non encore faire référence au sous-programme Perl qui a été enregistré dans
"EnregistrerSub1". Ceci est particulièrement vrai pour ces cas :
SaveSub1(\&fred);
AppelSavedSub1();
SaveSub1( sub { print "Bonjour\n" } );
AppelSavedSub1();
Au moment où chacune des instructions "SaveSub1" ci-dessus a été exécutée, les SV*s qui
correspondait aux paramètres n'existera plus. Attendez-vous à un message d'erreur de Perl de
la forme
Impossible d'utiliser une valeur non définie comme référence de sous-programme à ...
pour chacune des lignes "CallSavedSub1".
De même, avec ce code
$ref = \&fred;
SaveSub1($réf);
$réf = 47 ;
AppelSavedSub1();
vous pouvez vous attendre à l'un de ces messages (ce que vous obtenez en fait dépend de la version
de Perl que vous utilisez)
Pas une référence CODE à ...
Sous-routine non définie &main::47 appelée ...
La variable $ref peut avoir fait référence au sous-programme "fred" chaque fois que l'appel à
"SaveSub1" a été créé mais au moment où "CallSavedSub1" est appelé, il contient maintenant le numéro
47. Parce que nous n'avons enregistré qu'un pointeur vers le SV d'origine dans "SaveSub1", toute modification de $ref
sera suivi par le pointeur "rememberSub". Cela signifie que chaque fois que "CallSavedSub1"
est appelé, il tentera d'exécuter le code référencé par le SV*
"se souvenirSub". Dans ce cas cependant, il fait maintenant référence à l'entier 47, alors attendez-vous à ce que Perl
plaindre bruyamment.
Un problème similaire mais plus subtil est illustré avec ce code :
$ref = \&fred;
SaveSub1($réf);
$ref = \&joe;
AppelSavedSub1();
Cette fois, chaque fois que "CallSavedSub1" est appelé, il exécutera le sous-programme Perl "joe"
(en supposant qu'il existe) plutôt que "fred" comme cela a été demandé à l'origine dans l'appel à
"EnregistrerSub1".
Pour contourner ces problèmes, il est nécessaire de prendre une copie complète du SV. Le code
ci-dessous montre "SaveSub2" modifié pour ce faire.
statique SV * keepSub = (SV*)NULL;
annuler
SaveSub2(nom)
SV * nom
CODE:
/* Faire une copie du rappel */
si (keepSub == (SV*)NULL)
/* Première fois, donc créez un nouveau SV */
keepSub = newSVsv(nom);
d'autre
/* Été ici avant, alors écrasez */
SvSetSV(keepSub, nom);
annuler
AppelSavedSub2()
CODE:
POUSSOIR(SP);
call_sv(keepSub, G_DISCARD|G_NOARGS);
Pour éviter de créer un nouveau SV à chaque appel de "SaveSub2", la fonction vérifie d'abord
voir s'il a déjà été appelé. Si ce n'est pas le cas, l'espace pour un nouveau SV est alloué et le
référence à la sous-routine Perl "nom" est copiée dans la variable "keepSub" dans un
opération en utilisant "newSVsv". Par la suite, chaque fois que "SaveSub2" est appelé, le SV existant,
"keepSub", est écrasé par la nouvelle valeur à l'aide de "SvSetSV".
En utilisant appel_argv
Voici une sous-routine Perl qui imprime tous les paramètres qui lui sont passés.
sous-liste d'impression
{
ma(@liste) = @_;
foreach (@list) { print "$_\n" }
}
Et voici un exemple de appel_argv qui appellera Imprimer la liste.
static char * mots[] = {"alpha", "beta", "gamma", "delta", NULL} ;
vide statique
call_PrintList()
{
dSP ;
call_argv("PrintList", G_DISCARD, mots);
}
Notez qu'il n'est pas nécessaire d'appeler "PUSHMARK" dans ce cas. Ceci est dû au fait
appel_argv le fera pour vous.
En utilisant méthode_appel
Considérez le code Perl suivant :
{
paquet Mine;
sous nouveau
{
mon($type) = décalage;
bénir [@_]
}
sous-affichage
{
mon ($self, $index) = @_;
print "$index : $$self[$index]\n" ;
}
sous ID d'impression
{
ma($classe) = @_;
print "Ceci est la classe $class version 1.0\n" ;
}
}
Il implémente juste une classe très simple pour gérer un tableau. Outre le constructeur,
"new", il déclare des méthodes, une statique et une virtuelle. La méthode statique, "PrintID",
imprime simplement le nom de la classe et un numéro de version. La méthode virtuelle, "Affichage",
imprime un seul élément du tableau. Voici un exemple d'utilisation entièrement Perl.
$a = Mine->new('rouge', 'vert', 'bleu');
$a->Présentoir(1);
Mine->ImprimerID ;
va imprimer
1 : vert
Ceci est la version 1.0 de Class Mine
Appeler une méthode Perl depuis C est assez simple. Les éléments suivants sont requis :
· Une référence à l'objet pour une méthode virtuelle ou le nom de la classe pour une statique
méthode
· Le nom de la méthode
· Tout autre paramètre spécifique à la méthode
Voici un XSUB simple qui illustre les mécanismes d'appel à la fois du "PrintID" et
Les méthodes "Affichage" de C.
annuler
call_Method(ref, méthode, index)
SV * réf
méthode char *
indice entier
CODE:
POUSSOIR(SP);
XPUSHs(réf);
XPUSHs(sv_2mortal(newSViv(index)));
RETOUR ;
call_method(méthode, G_DISCARD);
annuler
call_PrintID(classe, méthode)
caractère * classe
méthode char *
CODE:
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSVpv(class, 0)));
RETOUR ;
call_method(méthode, G_DISCARD);
Ainsi, les méthodes "PrintID" et "Display" peuvent être invoquées comme ceci :
$a = Mine->new('rouge', 'vert', 'bleu');
call_Method($a, 'Affichage', 1);
call_PrintID('Mine', 'PrintID');
La seule chose à noter est que, dans les deux méthodes statiques et virtuelles, le nom de la méthode est
pas passé via la pile - il est utilisé comme premier paramètre pour méthode_appel.
En utilisant GIMME_V
Voici une XSUB triviale qui imprime le contexte dans lequel elle s'exécute actuellement.
annuler
ImprimerContexte()
CODE:
I32 donne moi = GIMME_V;
si (donne-moi == G_VOID)
printf ("Le contexte est vide\n");
sinon si (donne-moi == G_SCALAIRE)
printf ("Le contexte est scalaire\n");
d'autre
printf ("Le contexte est un tableau\n");
Et voici du Perl pour le tester.
Contexte d'impression ;
$a = PrintContext ;
@a = PrintContext ;
Le résultat sera
Le contexte est nul
Le contexte est scalaire
Le contexte est un tableau
En utilisant Perl à disposer of Temporaires
Dans les exemples donnés à ce jour, tous les temporaires créés dans le rappel (c'est-à-dire les paramètres
passé sur la pile au appeler_* fonction ou les valeurs renvoyées via la pile) ont été
libéré par l'une de ces méthodes :
· Spécification de l'indicateur G_DISCARD avec appeler_*
· Utilisation explicite du couplage "ENTER"/"SAVETMPS"--"FREETMPS"/"LEAVE"
Il existe une autre méthode qui peut être utilisée, à savoir laisser Perl le faire pour vous automatiquement
chaque fois qu'il reprend le contrôle après la fin du rappel. Cela se fait simplement en ne
en utilisant l'
ENTRER;
SAVETMPS ;
FREETMPS ;
LAISSER;
séquence dans le rappel (et non, bien sûr, en spécifiant le drapeau G_DISCARD).
Si vous envisagez d'utiliser cette méthode, vous devez être conscient d'une éventuelle fuite de mémoire qui
peut survenir dans des circonstances très particulières. Pour expliquer ces circonstances, vous devez
en savoir un peu plus sur le flux de contrôle entre Perl et la routine de rappel.
Les exemples donnés au début du document (un gestionnaire d'erreurs et un événement piloté
programme) sont typiques des deux principaux types de contrôle de flux que vous êtes susceptible de
rencontre avec des rappels. Il y a une distinction très importante entre eux, alors payez
attention.
Dans le premier exemple, un gestionnaire d'erreurs, le flux de contrôle pourrait être le suivant. Vous avez
créé une interface vers une bibliothèque externe. Le contrôle peut atteindre la bibliothèque externe comme
ceci.
perl --> XSUB --> bibliothèque externe
Pendant que le contrôle est dans la bibliothèque, une condition d'erreur se produit. Vous avez préalablement mis en place un
Un rappel Perl pour gérer cette situation, il sera donc exécuté. Une fois que le rappel a
terminé, le contrôle reviendra à Perl. Voici quel sera le flux de contrôle
comme dans cette situation
perl --> XSUB --> bibliothèque externe
erreur se produit
bibliothèque externe --> call_* --> perl
|
perl <-- XSUB <-- bibliothèque externe <-- call_* <----+
Après traitement de l'erreur à l'aide appeler_* est terminé, le contrôle revient à Perl plus
ou moins immédiatement.
Dans le diagramme, plus vous allez à droite, plus la portée est profondément imbriquée. C'est seulement
quand le contrôle est revenu avec perl à l'extrême gauche du schéma que vous aurez
retombé dans la portée englobante et tous les temporaires que vous avez laissés traîner seront
être libéré.
Dans le deuxième exemple, un programme événementiel, le flux de contrôle ressemblera davantage à ceci
perl --> XSUB --> gestionnaire d'événements
gestionnaire d'événements --> call_* --> perl
|
gestionnaire d'événements <-- call_* <----+
gestionnaire d'événements --> call_* --> perl
|
gestionnaire d'événements <-- call_* <----+
gestionnaire d'événements --> call_* --> perl
|
gestionnaire d'événements <-- call_* <----+
Dans ce cas, le flux de contrôle peut consister uniquement en la séquence répétée
gestionnaire d'événements --> call_* --> perl
pendant pratiquement toute la durée du programme. Cela signifie que le contrôle peut jamais
revenez à la portée environnante en Perl à l'extrême gauche.
Alors quel est le gros problème ? Eh bien, si vous vous attendez à ce que Perl range ces temporaires
pour vous, vous pourriez être dans une longue attente. Pour que Perl se débarrasse de vos temporaires,
le contrôle doit revenir à la portée englobante à un moment donné. Dans le scénario événementiel
cela peut ne jamais arriver. Cela signifie qu'avec le temps, votre programme créera plus
et plus de temporaires, dont aucun ne sera jamais libéré. Comme chacun de ces temporaires
consomme de la mémoire, votre programme finira par consommer toute la mémoire disponible dans votre
système--kapow!
Voici donc l'essentiel - si vous êtes sûr que le contrôle reviendra à l'enceinte
Portée Perl assez rapidement après la fin de votre rappel, alors ce n'est pas absolument
nécessaire de disposer explicitement de tous les temporaires que vous pourriez avoir créés. Remarquez, si vous
ne savent pas du tout quoi faire, cela ne fait aucun mal de ranger de toute façon.
Stratégies pour Stockage Rappel Contexte Info
Potentiellement l'un des problèmes les plus délicats à surmonter lors de la conception d'une interface de rappel
peut être de savoir comment stocker le mappage entre la fonction de rappel C et le Perl
équivalent.
Pour aider à comprendre pourquoi cela peut être un réel problème, examinez d'abord comment un rappel est configuré
dans un environnement tout C. Typiquement, une API C fournira une fonction pour enregistrer un
rappeler. Cela attendra un pointeur vers une fonction comme l'un de ses paramètres. Ci-dessous un
appel à une fonction hypothétique "register_fatal" qui enregistre la fonction C pour obtenir
appelé lorsqu'une erreur fatale se produit.
registre_fatal(cb1) ;
Le paramètre unique "cb1" est un pointeur vers une fonction, vous devez donc avoir défini "cb1" dans
ton code, dis quelque chose comme ça
vide statique
cb1()
{
printf ("Erreur fatale\n");
sortie(1);
}
Maintenant, changez cela pour appeler un sous-programme Perl à la place
SV statique * rappel = (SV*)NULL ;
vide statique
cb1()
{
dSP ;
POUSSOIR(SP);
/* Appeler le sous-système Perl pour traiter le rappel */
call_sv(rappel, G_DISCARD);
}
annuler
registre_fatal(fn)
SV * fn
CODE:
/* Rappelez-vous le sous-titre Perl */
if (rappel == (SV*)NULL)
rappel = newSVsv(fn);
d'autre
SvSetSV(rappel, fn);
/* enregistrer le rappel avec la bibliothèque externe */
registre_fatal(cb1) ;
où l'équivalent Perl de "register_fatal" et le rappel qu'il enregistre, "pcb1", pourraient
ressemble à ca
# Enregistrez le sous pcb1
register_fatal(\&pcb1);
sous pcb1
{
die "Je meurs...\n" ;
}
Le mappage entre le rappel C et l'équivalent Perl est stocké dans le fichier global
variable "rappel".
Ce sera suffisant si vous avez besoin d'avoir un seul rappel enregistré à la fois.
Un exemple pourrait être un gestionnaire d'erreurs comme le code esquissé ci-dessus. Rappelez-vous cependant,
les appels répétés à "register_fatal" remplaceront le rappel précédemment enregistré
fonctionner avec le nouveau.
Supposons, par exemple, que vous souhaitiez vous connecter à une bibliothèque qui permet des E/S de fichiers asynchrones. Dans
dans ce cas, vous pourrez peut-être enregistrer un rappel chaque fois qu'une opération de lecture est terminée.
Pour être utile, nous voulons pouvoir appeler des sous-routines Perl distinctes pour chaque fichier qui
est ouvert. Dans l'état actuel des choses, l'exemple de gestionnaire d'erreurs ci-dessus ne serait pas adéquat car il
permet de ne définir qu'un seul rappel à la fois. Ce dont nous avons besoin, c'est d'un moyen de
stocker le mappage entre le fichier ouvert et le sous-programme Perl que nous voulons appeler
pour ce fichier.
Disons que la bibliothèque i/o a une fonction "asynch_read" qui associe une fonction C
"ProcessRead" avec un descripteur de fichier "fh" - cela suppose qu'il a également fourni une routine
pour ouvrir le fichier et obtenir ainsi le descripteur de fichier.
async_read(fh, ProcessRead)
Cela peut s'attendre à ce que le C ProcessusLire fonction de cette forme
annuler
ProcessRead (fh, tampon)
int fh ;
char * tampon ;
{
}
Pour fournir une interface Perl à cette bibliothèque, nous devons pouvoir mapper entre le "fh"
et le sous-programme Perl que nous voulons appeler. Un hachage est un mécanisme pratique pour
stocker ce mappage. Le code ci-dessous montre une implémentation possible
HV statique * Mappage = (HV*)NULL ;
annuler
async_read(fh, rappel)
int fh
SV * rappel
CODE:
/* Si le hachage n'existe pas déjà, créez-le */
if (Mappage == (HV*)NULL)
Mappage = newHV();
/* Enregistrer le mappage fh -> callback */
hv_store(Mapping, (char*)&fh, sizeof(fh), newSVsv(callback), 0);
/* S'inscrire à la bibliothèque C */
async_read (fh, async_read_if);
et "asynch_read_if" pourrait ressembler à ceci
vide statique
asynch_read_if(fh, tampon)
int fh ;
char * tampon ;
{
dSP ;
SV**sv;
/* Récupère le rappel associé à fh */
sv = hv_fetch(Mapping, (char*)&fh , sizeof(fh), FALSE);
si (sv == (SV**)NULL)
croak("Erreur interne...\n");
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSViv(fh)));
XPUSHs(sv_2mortal(newSVpv(buffer, 0)));
RETOUR ;
/* Appeler le sous-titre Perl */
call_sv(*sv, G_DISCARD);
}
Pour être complet, voici "asynch_close". Cela montre comment supprimer l'entrée du
hachage "Mapping".
annuler
async_close(fh)
int fh
CODE:
/* Supprimer l'entrée du hachage */
(void) hv_delete(Mapping, (char*)&fh, sizeof(fh), G_DISCARD);
/* Appelez maintenant le vrai asynch_close */
async_close(fh);
Donc l'interface Perl ressemblerait à ceci
sous-rappel1
{
mon($handle, $buffer) = @_;
}
# Enregistrer le rappel Perl
async_read($fh, \&callback1) ;
async_close($fh);
Le mappage entre le rappel C et Perl est stocké dans le hachage global "Mapping" ce
temps. L'utilisation d'un hachage a le net avantage de permettre un nombre illimité de
rappels à enregistrer.
Que faire si l'interface fournie par le rappel C ne contient pas de paramètre qui permet
le descripteur de fichier au mappage de sous-routine Perl ? Disons que dans le package d'e/s asynchrones, le
la fonction de rappel ne reçoit que le paramètre "buffer" comme celui-ci
annuler
ProcessusLire (tampon)
char * tampon ;
{
}
Sans le descripteur de fichier, il n'y a pas de moyen simple de mapper du rappel C au
Sous-routine Perl.
Dans ce cas, un moyen possible de contourner ce problème est de prédéfinir une série de fonctions C à
agir comme l'interface avec Perl, ainsi
#définir MAX_CB 3
#définir NULL_HANDLE -1
typedef void (*FnMap)();
struct MapStruct {
Fonction FnMap ;
SV * PerlSub ;
int Poignée;
};
vide statique fn1();
vide statique fn2();
vide statique fn3();
struct statique MapStruct Map [MAX_CB] =
{
{ fn1, NULL, NULL_HANDLE },
{ fn2, NULL, NULL_HANDLE },
{ fn3, NULL, NULL_HANDLE }
};
vide statique
PCB (index, tampon)
index int;
char * tampon ;
{
dSP ;
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSVpv(buffer, 0)));
RETOUR ;
/* Appeler le sous-titre Perl */
call_sv(Carte[index].PerlSub, G_DISCARD);
}
vide statique
fn1(tampon)
char * tampon ;
{
PCB (0, tampon);
}
vide statique
fn2(tampon)
char * tampon ;
{
PCB (1, tampon);
}
vide statique
fn3(tampon)
char * tampon ;
{
PCB (2, tampon);
}
annuler
array_asynch_read(fh, rappel)
int fh
SV * rappel
CODE:
index int;
int null_index = MAX_CB ;
/* Trouver le même handle ou une entrée vide */
pour (index = 0 ; index < MAX_CB ; ++index)
{
si (Carte[index].Handle == fh)
break;
si (Carte[index].Handle == NULL_HANDLE)
index_null = index ;
}
if (index == MAX_CB && null_index == MAX_CB)
croak ("Trop de fonctions de rappel enregistrées\n");
si (indice == MAX_CB)
index = null_index ;
/* Enregistrer le descripteur de fichier */
Carte[index].Handle = fh;
/* Rappelez-vous le sous-titre Perl */
si (Carte[index].PerlSub == (SV*)NULL)
Map[index].PerlSub = newSVsv(callback);
d'autre
SvSetSV(Map[index].PerlSub, rappel);
async_read(fh, Map[index].Function);
annuler
array_asynch_close(fh)
int fh
CODE:
index int;
/* Trouver le descripteur de fichier */
pour (index = 0 ; index < MAX_CB ; ++ index)
si (Carte[index].Handle == fh)
break;
si (indice == MAX_CB)
croak ("impossible de fermer fh %d\n", fh);
Carte[index].Handle = NULL_HANDLE ;
SvREFCNT_dec(Carte[index].PerlSub);
Carte[index].PerlSub = (SV*)NULL ;
async_close(fh);
Dans ce cas, les fonctions "fn1", "fn2" et "fn3" sont utilisées pour mémoriser le Perl
sous-programme à appeler. Chacune des fonctions contient un index câblé distinct qui est
utilisé dans la fonction "Pcb" pour accéder au tableau "Map" et en fait appeler le Perl
sous-programme.
Cette technique présente des inconvénients évidents.
Premièrement, le code est considérablement plus complexe qu'avec l'exemple précédent.
Deuxièmement, il existe une limite fixe (dans ce cas 3) au nombre de rappels pouvant être
existent simultanément. La seule façon d'augmenter la limite est de modifier le code pour ajouter
plus de fonctions, puis recompilation. Néanmoins, tant que le nombre de fonctions est
choisi avec un certain soin, c'est toujours une solution viable et dans certains cas c'est la seule
disponible.
Pour résumer, voici un certain nombre de méthodes possibles à considérer pour stocker les
mappage entre C et le rappel Perl
1. Ignorez le problème - Autorisez un seul rappel
Pour de nombreuses situations, comme l'interfaçage avec un gestionnaire d'erreurs, cela peut être un
solution parfaitement adéquate.
2. Créez une séquence de rappels - limite câblée
S'il est impossible de dire à partir des paramètres renvoyés par le rappel C ce que
le contexte est, alors vous devrez peut-être créer une séquence d'interface de rappel C
fonctions et stocker des pointeurs vers chacun dans un tableau.
3. Utilisez un paramètre pour mapper au rappel Perl
Un hachage est un mécanisme idéal pour stocker le mappage entre C et Perl.
Autre Stack Manipulation
Bien que je n'aie utilisé que les macros "POP*" pour accéder aux valeurs renvoyées par Perl
sous-routines, il est également possible de contourner ces macros et de lire la pile en utilisant le "ST"
macro (Voir perlxs pour une description complète de la macro "ST").
La plupart du temps, les macros "POP*" devraient être adéquates ; le principal problème avec eux est que
ils vous obligent à traiter les valeurs renvoyées dans l'ordre. Ce n'est peut-être pas le plus
moyen approprié de traiter les valeurs dans certains cas. Ce que nous voulons, c'est pouvoir accéder au
empiler dans un ordre aléatoire. La macro "ST" utilisée lors du codage d'une XSUB est idéale pour cela
objectif.
Le code ci-dessous est l'exemple donné dans la section Retour a Liste of VALEURS recodé en
utilisez "ST" au lieu de "POP*".
vide statique
call_AddSubtract2(a, b)
int a;
int b;
{
dSP ;
hache I32 ;
nombre int;
ENTRER;
SAVETMPS ;
POUSSOIR(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
RETOUR ;
count = call_pv("AjouterSoustraction", G_ARRAY);
ESPAGNE ;
SP -= nombre ;
hache = (SP - PL_stack_base) + 1 ;
si (compte != 2)
croak("Gros problème\n");
printf ("%d + %d = %d\n", a, b, SvIV(ST(0, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX)));
printf ("%d - %d = %d\n", a, b, SvIV(ST(1, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX)));
RETOUR ;
FREETMPS ;
LAISSER;
}
Remarques
1. Notez qu'il était nécessaire de définir la variable "ax". C'est parce que le "ST"
macro s'attend à ce qu'il existe. Si nous étions dans un XSUB il ne serait pas nécessaire de définir
« hache » comme il est déjà défini pour nous.
2. Le code
ESPAGNE ;
SP -= nombre ;
hache = (SP - PL_stack_base) + 1 ;
configure la pile pour que nous puissions utiliser la macro "ST".
3. Contrairement au codage d'origine de cet exemple, les valeurs renvoyées ne sont pas accessibles dans
ordre inverse. Alors ST(0) fait référence à la première valeur renvoyée par le sous-programme Perl
et "ST(count-1)" se réfère au dernier.
La création et appel an Témoignages Sous-programme in C
Comme nous l'avons déjà montré, "call_sv" peut être utilisé pour invoquer un sous-programme anonyme. Pourtant,
notre exemple montrait un script Perl invoquant une XSUB pour effectuer cette opération. Voyons voir
comment cela peut être fait dans notre code C :
SV *cvrv = eval_pv("sub { print 'Vous ne me trouverez pas encombrant un espace de noms !' }", TRUE);
call_sv(cvrv, G_VOID|G_NOARGS);
"eval_pv" est utilisé pour compiler le sous-programme anonyme, qui sera la valeur de retour comme
bien (en savoir plus sur "eval_pv" dans "eval_pv" dans perlapi). Une fois cette référence de code dans
part, il peut être mélangé avec tous les exemples précédents que nous avons montrés.
POIDS LÉGER RAPPELS
Parfois, vous devez invoquer le même sous-programme à plusieurs reprises. Cela se produit généralement avec un
fonction qui agit sur une liste de valeurs, telle que la fonction intégrée de Perl sorte(). Vous pouvez passer un
fonction de comparaison à sorte(), qui sera ensuite invoqué pour chaque paire de valeurs qui
doit être comparé. le premier() et réduire() les fonctions de List::Util suivent une
motif.
Dans ce cas, il est possible d'accélérer la routine (souvent assez sensiblement) en utilisant
l'API de rappel légère. L'idée est que le contexte d'appel doit seulement être
créé et détruit une fois, et le sous-marin peut être appelé arbitrairement plusieurs fois entre les deux.
Il est habituel de passer des paramètres à l'aide de variables globales (généralement $_ pour un paramètre, ou
$a et $b pour deux paramètres) plutôt que via @_. (Il est possible d'utiliser le mécanisme @_
si vous savez ce que vous faites, bien qu'il n'y ait pas encore d'API prise en charge pour cela. C'est aussi
intrinsèquement plus lent.)
Le modèle des appels de macro est le suivant :
dMULTICALL; /* Déclarer les variables locales */
I32 donne moi = G_SCALAIRE; /* contexte de l'appel : G_SCALAIRE,
* G_ARRAY, ou G_VOID */
PUSH_MULTICALL(cv); /* Configurer le contexte pour appeler cv,
et définissez les variables locales de manière appropriée */
/* boucler */ {
/* définir la (les) valeur(s) de vos variables de paramètres */
APPEL MULTIPLE ; /* Faire l'appel réel */
} /* fin de boucle */
POP_MULTICALL; /* Détruire le contexte d'appel */
Pour quelques exemples concrets, voir la mise en œuvre du premier() et réduire() fonctions
de la liste::Util 1.18. Vous y trouverez également un fichier d'en-tête qui émule l'API multicall
sur les anciennes versions de perl.
Utiliser perlcall en ligne à l'aide des services onworks.net