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 0
Para 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
