lhp@nereida:~/Lperl/src/XSUB/cpanexamples/Scalar-Util-Numeric-0.02$ perl -de 0 main::(-e:1): 0 DB<1> use Scalar::Util::Numeric qw(:all) DB<2> x isint('432') 0 1 DB<3> x isint('432b') 0 0 DB<4> x isneg('-53') 0 1 DB<5> x isneg('53') 0 0 DB<15> x isbig ((2.0)**32) 0 1 DB<16> x isbig ((2.0)**31) 0 0Para proporcionar estas funciones el módulo provee via XS una función
is_num
la cual utiliza la función de la Perl API
looks_like_number
para estudiar el tipo del número.
lhp@nereida:~/Lperl/src/perlcompilerssource/perl-5.8.8$ grep 'looks_like_number(' embed.h #define looks_like_number(a) Perl_looks_like_number(aTHX_ a) lhp@nereida:~/Lperl/src/perlcompilerssource/perl-5.8.8$ sed -ne '1918,1932p' sv.c Perl_looks_like_number(pTHX_ SV *sv) { register const char *sbegin; STRLEN len; if (SvPOK(sv)) { sbegin = SvPVX_const(sv); len = SvCUR(sv); } else if (SvPOKp(sv)) sbegin = SvPV_const(sv, len); else return SvFLAGS(sv) & (SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK); return grok_number(sbegin, len, NULL); }Las funciones en el código Perl del módulo usan un sistema de flags para determinar el tipo del número. Las banderas funciona según la siguiente tabla:
Valor | Nombre | Descripción |
0x01 |
IS_NUMBER_IN_UV |
Número dentro del rango UV . No necesariametne un entero |
0x02 |
IS_NUMBER_GREATER_THAN_UV_MAX |
El número es mayor que UV_MAX ) |
0x04 |
IS_NUMBER_NOT_INT |
Por ej . o E |
0x08 |
IS_NUMBER_NEG |
Signo menos |
0x10 |
IS_NUMBER_INFINITY |
Infinito |
0x20 |
IS_NUMBER_NAN |
NaN not a number |
Veamos el código Perl:
lhp@nereida:~/Lperl/src/XSUB/cpanexamples/Scalar-Util-Numeric-0.02$ cat -n \ lib/Scalar/Util/Numeric.pm 1 package Scalar::Util::Numeric; 2 .. .... 24 1; 25 26 __END__ .. ........ 91 sub isnum ($) { 92 return 0 unless defined (my $val = shift); 93 # stringify - ironically, looks_like_number always returns 1 unless 94 # arg is a string 95 return is_num($val . ''); 96 } 97 98 sub isint ($) { 99 my $isnum = isnum(shift()); 100 return ($isnum == 1) ? 1 : ($isnum == 9) ? -1 : 0; 101 } 102 103 sub isuv ($) { 104 return (isnum(shift()) & 1) ? 1 : 0; 105 } 106 107 sub isbig ($) { 108 return (isnum(shift()) & 2) ? 1 : 0; 109 } 110 111 sub isfloat ($) { 112 return (isnum(shift()) & 4) ? 1 : 0; 113 } 114 115 sub isneg ($) { 116 return (isnum(shift()) & 8) ? 1 : 0; 117 } 118 119 sub isinf ($) { 120 return (isnum(shift()) & 16) ? 1 : 0; 121 } 122 123 sub isnan ($) { 124 return (isnum(shift()) & 32) ? 1 : 0; 125 }Pasemos a estudiar el código XS:
lhp@nereida:~/Lperl/src/XSUB/cpanexamples/Scalar-Util-Numeric-0.02$ cat -n Numeric.xs 1 #include "EXTERN.h" 2 #include "perl.h" 3 #include "XSUB.h" 4 #include "ppport.h" 5 6 MODULE = Scalar::Util::Numeric PACKAGE = Scalar::Util::Numeric 7 8 void 9 is_num(sv) 10 SV *sv 11 PROTOTYPE: $ 12 PREINIT: 13 I32 num = 0; 14 CODE: 15 16 if (!(SvROK(sv) || (sv == (SV *)&PL_sv_undef))) { 17 num = looks_like_number(sv); 18 } 19 20 XSRETURN_IV(num); 21 22 void 23 uvmax() 24 PROTOTYPE: 25 CODE: 26 XSRETURN_UV(UV_MAX);
La directiva PREINIT: permite la declaración adicional de variables.
Si una variable es declarada dentro de una sección
CODE:
su declaración ocurre después de la emisión
del código typemap
para los parámetros de entrada.
Ello podría dar lugar a un error sintáctico. Una sección
PREINIT:
permite forzar
una correcta declaración de variables.
Es posible usar mas de una sección PREINIT:
dentro
de una XSUB.
Podemos hacer que las declaraciones e inicializaciones PREINIT:
precedan al código typemap
para los parámetros de entrada.
Podríamos reorganizar el ejemplo anterior como sigue
lhp@nereida:~/Lperl/src/XSUB/cpanexamples/Scalar-Util-Numeric-0.02$ sed -ne '8,21p' Numeric.xs | cat -n 1 void 2 is_num(sv) 3 PREINIT: 4 I32 num = 0; 5 INPUT: 6 SV *sv 7 PROTOTYPE: $ 8 CODE: 9 10 if (!(SvROK(sv) || (sv == (SV *)&PL_sv_undef))) { 11 num = looks_like_number(sv); 12 } 13 14 XSRETURN_IV(num);El código generado muestra que la declaración
I32 num = 0
ocurre antes del
código typemap
:
lhp@nereida:~/Lperl/src/XSUB/cpanexamples/Scalar-Util-Numeric-0.02$ sed -ne '18,37p' Numeric.c | cat -n 1 XS(XS_Scalar__Util__Numeric_is_num) 2 { 3 dXSARGS; 4 if (items != 1) 5 Perl_croak(aTHX_ "Usage: Scalar::Util::Numeric::is_num(sv)"); 6 { 7 #line 11 "Numeric.xs" 8 I32 num = 0; 9 #line 27 "Numeric.c" 10 SV * sv = ST(0); 11 #line 17 "Numeric.xs" 12 if (!(SvROK(sv) || (sv == (SV *)&PL_sv_undef))) { 13 num = looks_like_number(sv); 14 } 15 16 XSRETURN_IV(num); 17 #line 35 "Numeric.c" 18 } 19 XSRETURN_EMPTY; 20 }Esta capacidad para añadir declaraciones antes de la ejecución del código
typemap
-cuando se combina con la directiva
CLEANUP:
-
puede ser útil
para salvar el estado de variables globales que sean
modificadas por el código typemap
(si es el caso):
typetutu myfun(x) PREINIT: saveglob = globalvar; INPUT: typex x; CODE: ... CLEANUP: globalvar = saveglob;
Normalmente los parámetros de una XSUB son evaluados
tan pronto como se entra en la XSUB.
Como se muestra en el ejemplo,
la directiva
INPUT:
puede usarse conjuntamente
con PREINIT:
para forzar
el retraso en la evaluación de los parámetros.
La directiva
CLEANUP:
permite la introducción de
código que se ejecutará antes que la XSUB termine.
Debe colocarse después de cualesquiera de las directivas
CODE:
, PPCODE:
y OUTPUT:
que
Casiano Rodríguez León