Uso de la Pila de Argumentos

La macro ST permite acceder a la lista de argumentos. La expresión ST(0) denota al primero, ST(1) al segundo, etc. Esto es: ST(i) es equivalente a $_[$i].

El siguiente ejemplo provee una función sum que devuelve la suma de sus argumentos. Creamos la estructura de ficheros y directorios con h2xs -A -n Sum. Después de añadir el directorio script y el programa de prueba usesum.pl la estructura queda como sigue:

lhp@nereida:~/Lperl/src/XSUB/Sum$ tree
.
|-- Changes
|-- MANIFEST
|-- Makefile.PL
|-- README
|-- Sum.xs
|-- lib
|   `-- Sum.pm
|-- ppport.h
|-- script
|   `-- usesum.pl
`-- t
    `-- Sum.t

El programa de prueba ilustra el modo de uso:

lhp@nereida:~/Lperl/src/XSUB/Sum/script$ cat -n usesum.pl
 1  #!/usr/bin/perl -w
 2  use strict;
 3  use blib;
 4  use Sum;
 5
 6  die "Supply numbers\n" unless @ARGV;
 7  my $s = sum(@ARGV);
 8  print "$s\n";
En la ejecución que sigue nótese el control de tipos cuando la entrada es ilegal:
lhp@nereida:~/Lperl/src/XSUB/Sum/script$ usesum.pl 2 3 5
10
lhp@nereida:~/Lperl/src/XSUB/Sum/script$ usesum.pl 2 3 a 4
Argument "a" isn't numeric in subroutine entry at ./usesum.pl line 7.
9
lhp@nereida:~/Lperl/src/XSUB/Sum/script$ usesum.pl 2.5 3.4 2.1
8
lhp@nereida:~/Lperl/src/XSUB/Sum/script$

A continuación pasamos a comentar los contenidos de Sum.xs:

lhp@nereida:~/Lperl/src/XSUB/Sum$ cat -n Sum.xs
 1  #include "EXTERN.h"
 2  #include "perl.h"
 3  #include "XSUB.h"
 4
 5  #include "ppport.h"
 6
 7
 8  #ifdef SVf_IVisUV
 9  #  define slu_sv_value(sv) \
         (SvIOK(sv)) ? \
              (SvIOK_UV(sv)) ? (NV)(SvUVX(sv)) : (NV)(SvIVX(sv))\ 
            : (SvNV(sv))
10  #else
11  #  define slu_sv_value(sv) (SvIOK(sv)) ? (NV)(SvIVX(sv)) : (SvNV(sv))
12  #endif
13
14
15  MODULE = Sum            PACKAGE = Sum
16
17  NV
18  sum(...)
19  PROTOTYPE: @
20  CODE:
21  {
22      SV *sv;
23      int index;
24      if(!items) {
25          XSRETURN_UNDEF;
26      }
27      sv = ST(0);
28      RETVAL = slu_sv_value(sv);
29      for(index = 1 ; index < items ; index++) {
30          sv = ST(index);
31          RETVAL += slu_sv_value(sv);
32      }
33  }
34  OUTPUT:
35      RETVAL

  1. El tipo NV (Numerical Value) es usado para representar valores numéricos de tipo doble en Perl. Es el valor de retorno de la función sum (línea 17). Otros tipos posibles son: IV por Integer Value, PV para las cadenas (Pointer Value).

  2. La elipsis de la línea 18 indica que esta función recibe un número variable de argumentos.

  3. La directiva PROTOTYPE: indica al compilador xsubpp que debe generar un prototipo para la interface Perl a la subrutina.

  4. La directiva CODE: permite escribir el código de la función. Se usa cuando queremos tener acceso a algunos de los servicios facilitados por XS.

  5. En la línea 22 declaramos sv como puntero a un tipo escalar SV (por Scalar Value). El tipo SV es el tipo C usado por Perl para representar las variables Perl de tipo escalar. Analogamente los tipos AV y HV son las representaciones C de los tipos Perl Array Value y Hash Value.

  6. La variable XS items (línea 24) nos da el número de argumentos, esto es, la longitud de @_.

  7. La macro XSRETURN_UNDEF produce el equivalente C de return undef.

  8. La macro ST permite acceder a la lista de argumentos. ST(0) es el primero, ST(1) el segundo, etc. Esto es: ST(i) es equivalente a $_[$i].

  9. La variable RETVAL (línea 28) es declarada automáticamente por XS. El tipo de RETVAL casa con el tipo de retorno declarado para la función. Por defecto la función C generada usará RETVAL para guardar el valor de retorno. En los casos sencillos el valor de RETVAL será colocado en ST(0) para que sea recibido por Perl como el valor retornado por la XSUB. Sin embargo, cuando hay una sección CODE: como en este ejemplo, el valor en RETVAL no es devuelto automáticamente y es necesario explicitarlo en una sección OUTPUT:.

  10. Una sección OUTPUT: indica que ciertos parámetros de la función deben ser actualizados cuando la función termine.
Al compilar con xsubpp la XSUB sum es traducida asi:
lhp@nereida:~/Lperl/src/XSUB/Sum$ sed -ne '27,51p' Sum.c
XS(XS_Sum_sum)
{
    dXSARGS;
    {
        NV      RETVAL;
        dXSTARG;
#line 21 "Sum.xs"
{
    SV *sv;
    int index;
    if(!items) {
        XSRETURN_UNDEF;
    }
    sv = ST(0);
    RETVAL = slu_sv_value(sv);
    for(index = 1 ; index < items ; index++) {
        sv = ST(index);
        RETVAL += slu_sv_value(sv);
    }
}
#line 48 "Sum.c"
        XSprePUSH; PUSHn((NV)RETVAL);
    }
    XSRETURN(1);
}

Casiano Rodríguez León
2012-02-29