typemap facilita
el proceso de conversión entre arrays C y listas Perl. Este servicio
es útil si tenemos funciones de una librería que espera recibir o devolver
arrays. Si lo que se quiere
es simplemente manipular la lista Perl en C es mejor usar la 
técnica introducida en las secciones 
17.13
y
17.14.
Para ilustrar su uso estudiaremos el código de una subrutina que 
recibe y devuelve usando T_ARRAY un vector de dobles.
La subrutina realiza la suma de prefijos y su funcionamiento
queda ilustrado en la ejecución del siguiente programa de ejemplo:
lhp@nereida:~/Lperl/src/XSUB/Arrays$ cat -n usearrays.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use blib;
 4  use List::MoreUtils qw(all);
 5  use Regexp::Common;
 6  use Arrays;
 7  my @a = (@ARGV and all { /^$RE{num}{real}$/ } @ARGV)? @ARGV : 1..5;
 8  my @x = prefixsum(@a);
 9  print "Suma de prefijos de @a:\n  @x\n";
lhp@nereida:~/Lperl/src/XSUB/Arrays$ usearrays.pl
Suma de prefijos de 1 2 3 4 5:
  1 3 6 10 15
lhp@nereida:~/Lperl/src/XSUB/Arrays$ usearrays.pl 1 a b 3
Suma de prefijos de 1 2 3 4 5:
  1 3 6 10 15
lhp@nereida:~/Lperl/src/XSUB/Arrays$ usearrays.pl 5.2 3.1 -1 -2e1
Suma de prefijos de 5.2 3.1 -1 -2e1:
  5.2 8.3 7.3 -12.7
lhp@nereida:~/Lperl/src/XSUB/Arrays$ usearrays.pl 0
Suma de prefijos de 0:
  0
Para su funcionamiento, el código asociado con 
el typemap T_ARRAY requiere la colaboración
del programador en varios puntos.  
El primero es que el tipo de entrada y/o salida de la XSUB 
debe tener
un nombre que sea de la forma $subtipo."Array". Así deberá llamarse
doubleArray si
es un array de dobles, intArray si lo es de enteros, etc.
Por tanto, en el ejemplo que nos ocupa, el tipo se llamará
doubleArray y le asignamos la entrada T_ARRAY usando un 
fichero de typemap local al directorio de trabajo:
lhp@nereida:~/Lperl/src/XSUB/Arrays$ cat -nT typemap # La opción -T  muestra tabs
     1  doubleArray *^IT_ARRAY
lhp@nereida:~/Lperl/src/XSUB/Arrays$ cat -n typemap
     1  doubleArray *   T_ARRAY
El código de la XSUB prefixsum (líneas 30-41 del 
listado que sigue) comienza indicando mediante la elípsis
que la función recibirá un número variable de argumentos.
El primero argumento 
array no se corresponde con un argumento real
de la función y se usará para indicar el nombre del array C
que almacenará el vector C de números extraido de la lista 
Perl de escalares (observe la llamada en la línea 8
del código de usearrays.pl encima: no existe
un primer argumento especial). De hecho, es este identificador
array el que se corresponde con la variable $var del
código del typemap.
lhp@nereida:~/Lperl/src/XSUB/Arrays$ cat -n Arrays.xs
  1  #include "EXTERN.h"
  2  #include "perl.h"
  3  #include "XSUB.h"
  4  #include "ppport.h"
  5
  6  typedef double doubleArray;
  7
  8  doubleArray * doubleArrayPtr ( int num ) {
 ..    ... # asigna memoria para "num" elementos de tipo double
 14  }
 15
 16  doubleArray *
 17  prefixSum ( double num, doubleArray * array ) {
 ..    ... # retorna la suma de prefijos de array 
 26  }
 27
 28  MODULE = Arrays         PACKAGE = Arrays
 29
 30  doubleArray *
 31  prefixsum( array, ...)
 32    doubleArray * array
 33   PREINIT:
 34    int size_RETVAL;
 35   CODE:
 36    size_RETVAL = ix_array;
 37    RETVAL = prefixSum( ix_array, array );
 38   OUTPUT:
 39    RETVAL
 40   CLEANUP:
 41    XSRETURN(size_RETVAL);
El código de entrada asociado con T_ARRAY 
asigna memoria para el nuevo array y después procede 
a copiar cada uno de los elementos en la lista de escalares 
Perl en el array recién creado. Para poder llevar a
cabo dicha asignación de memoria el programador deberá
proveer una función de asignación cuyo nombre  debe ser igual
a la meta-variable $ntype (el nombre del tipo con los astericos
sustituidos por Ptr). Esa es la misión de
la función doubleArrayPtr que aparece en la línea 8
del código XS. Además el código de entrada asociado con T_ARRAY
declara la variable con nombre ix_$var (ix_array
en el ejemplo) la cual
contendrá el número de elementos en el array.
lhp@nereida:~/Lperl/src/XSUB/Arrays$ cat -n /usr/share/perl/5.8/ExtUtils/typemap
   1  # basic C types
   2  int                     T_IV
  ..  .............................................
  45  double                  T_DOUBLE
  ..  .............................................$
  55
  56  #############################################################################
  57  INPUT
 ... .............................................
 110  T_DOUBLE
 111          $var = (double)SvNV($arg)
 ... .............................................
 176  T_ARRAY
 177          U32 ix_$var = $argoff;
 178          $var = $ntype(items -= $argoff);
 179          while (items--) {
 180              DO_ARRAY_ELEM;
 181              ix_$var++;
 182          }
 183          /* this is the number of elements in the array */
 184          ix_$var -= $argoff
La entrada INPUT de T_ARRAY (líneas 176-184) del código
muestra en mas detalle las acciones que tienen lugar:
ix_$var (ix_array en el ejemplo) y se inicializa al desplaamiento
(offset) del argumento actual.
items - $argoff elementos del tipo del array
confiando en que el programador ha proveído una función de asignación con 
nombre $ntype. Observe que el uso de items implica que, para
que este código funcione, el array debe ser el último argumento de la lista.
De paso se cambia el valor de items que pasa a tener el número de 
elementos en el array.
Después se entra en un bucle en el que el campo numérico de
cada elemento de la lista Perl es copiado
en el array C. El traductor xsubpp sustituye la cadena 
   DO_ARRAY_ELEM
  
por:
                  $var[ix_$var - $argoff] = ($subtype)SvNV(ST(ix_$var));
que en el ejemplo acaba convertido en:
                  array[ix_array - 0] = (double)SvNV(ST(ix_array));
ix_$var es actualizada para que guarde el tamaño del 
array.
El código de salida del typemap realiza el proceso
inverso:
 194  OUTPUT
 ... .............................................
 240  T_DOUBLE
 241          sv_setnv($arg, (double)$var);
 ... .............................................
 273  T_ARRAY
 274          {
 275              U32 ix_$var;
 276              EXTEND(SP,size_$var);
 277              for (ix_$var = 0; ix_$var < size_$var; ix_$var++) {
 278                  ST(ix_$var) = sv_newmortal();
 279                  DO_ARRAY_ELEM
 280              }
 281          }
 ... .............................................
ix_$var. Puesto que $var es
el nombre de la variable siendo retornada, en la sección OUTPUT:,
la cadena ix_$var se convierte en ix_RETVAL.
size_$var
escalares. Por tanto el programador deberá proveer 
una variable con nombre size_$var que determine el
tamaño de la lista que se devuelve. En el ejemplo size_RETVAL.
DO_ARRAY_ELEM es expandida por xsubpp
a:
sv_setnv(ST(ix_$var), ($subtype)$var[ix_$var])
La función sv_setnv copia el valor numérico (segundo argumento) en el escalar. El texto anterior se transforma en el ejemplo en:
sv_setnv(ST(ix_RETVAL), (double)RETVAL[ix_RETVAL])
Finalmente el código generado por xsubpp para la XSUB prefixsum
queda:
lhp@nereida:~/Lperl/src/XSUB/Arrays$ cat -n Arrays.c
 .  ...................
39  XS(XS_Arrays_prefixsum); /* prototype to pass -Wmissing-prototypes */
40  XS(XS_Arrays_prefixsum)
41  {
42      dXSARGS;
43      if (items < 1)
44          Perl_croak(aTHX_ "Usage: Arrays::prefixsum(array, ...)");
45      {
46          doubleArray *   array;
47  #line 34 "Arrays.xs"
48    int size_RETVAL;
49  #line 50 "Arrays.c"
50          doubleArray *   RETVAL;
51
52          U32 ix_array = 0;
53          array = doubleArrayPtr(items -= 0);
54          while (items--) {
55                  array[ix_array - 0] = (double)SvNV(ST(ix_array));
56              ix_array++;
57          }
58          /* this is the number of elements in the array */
59          ix_array -= 0;
60  #line 36 "Arrays.xs"
61    size_RETVAL = ix_array;
62    RETVAL = prefixSum( ix_array, array );
63  #line 64 "Arrays.c"
64          {
65              U32 ix_RETVAL;
66              EXTEND(SP,size_RETVAL);
67              for (ix_RETVAL = 0; ix_RETVAL < size_RETVAL; ix_RETVAL++) {
68                  ST(ix_RETVAL) = sv_newmortal();
69                  sv_setnv(ST(ix_RETVAL), (double)RETVAL[ix_RETVAL]);
70              }
71          }
72  #line 41 "Arrays.xs"
73    XSRETURN(size_RETVAL);
74  #line 75 "Arrays.c"
75      }
76      XSRETURN(1);
77  }
78
..  ...................
Para completar, veamos el código de la subrutina de asignación 
de memoria (fichero Arrays.xs):
 8  doubleArray * doubleArrayPtr ( int num ) {
 9    doubleArray * array;
10    SV * mortal;
11    mortal = sv_2mortal( NEWSV(1, num * sizeof(doubleArray) ) );
12    array = (doubleArray *) SvPVX( mortal );
13    return array;
14  }
Casiano Rodríguez León
