Typemaps

Un fichero typemap es un fichero usado por xsubpp en su labor de traducir dos pasos importantes:
  1. La llamada desde Perl a la función C
  2. El retorno de valores de la función C hacia el código Perl

Existe un fichero typemap por defecto el cuál describe el proceso de conversión para los tipos mas comunes. El fichero de typemap usado por defecto puede obtenerse ejecutando el siguiente inline:

pp2@nereida:~$ perl -MConfig -le 'print "$Config{installprivlib}/ExtUtils/typemap"'
/usr/share/perl/5.8/ExtUtils/typemap

Veamos un esquema del contenido del fichero typemap. Las elipsis corresponden a zonas suprimidas del fichero.

pp2@nereida:~$ cat -n /usr/share/perl/5.8/ExtUtils/typemap
   1  # basic C types
   2  int                     T_IV
   3  unsigned                T_UV
   4  unsigned int            T_UV
   5  long                    T_IV
   6  unsigned long           T_UV
   7  short                   T_IV
   8  unsigned short          T_UV
   9  char                    T_CHAR
  10  unsigned char           T_U_CHAR
  11  char *                  T_PV
  12  unsigned char *         T_PV
  13  const char *            T_PV
  14  caddr_t                 T_PV
  ..  .......                 .....
  43  Boolean                 T_BOOL
  44  float                   T_FLOAT
  45  double                  T_DOUBLE
  46  SysRet                  T_SYSRET
  47  SysRetLong              T_SYSRET
  48  FILE *                  T_STDIO
  49  PerlIO *                T_INOUT
  50  FileHandle              T_PTROBJ
  51  InputStream             T_IN
  52  InOutStream             T_INOUT
  53  OutputStream            T_OUT
  54  bool                    T_BOOL
  55
  56  #############################################################################
  57  INPUT
  58  T_SV
  59          $var = $arg
  60  T_SVREF
  61          if (SvROK($arg))
  62              $var = (SV*)SvRV($arg);
  63          else
  64              Perl_croak(aTHX_ \"$var is not a reference\")
  65  T_AVREF
  66          if (SvROK($arg) && SvTYPE(SvRV($arg))==SVt_PVAV)
  67              $var = (AV*)SvRV($arg);
  68          else
  69              Perl_croak(aTHX_ \"$var is not an array reference\")
  70  T_HVREF
  71          if (SvROK($arg) && SvTYPE(SvRV($arg))==SVt_PVHV)
  72              $var = (HV*)SvRV($arg);
  73          else
  74              Perl_croak(aTHX_ \"$var is not a hash reference\")
  75  T_CVREF
  76          if (SvROK($arg) && SvTYPE(SvRV($arg))==SVt_PVCV)
  77              $var = (CV*)SvRV($arg);
  78          else
  79              Perl_croak(aTHX_ \"$var is not a code reference\")
  80  T_SYSRET
  81          $var NOT IMPLEMENTED
  82  T_UV
  83          $var = ($type)SvUV($arg)
  84  T_IV
  85          $var = ($type)SvIV($arg)
  86  T_INT
  87          $var = (int)SvIV($arg)
  88  T_ENUM
  89          $var = ($type)SvIV($arg)
  90  T_BOOL
  91          $var = (bool)SvTRUE($arg)
  92  T_U_INT
  93          $var = (unsigned int)SvUV($arg)
 ...          ..............................
 110  T_DOUBLE
 111          $var = (double)SvNV($arg)
 112  T_PV
 113          $var = ($type)SvPV_nolen($arg)
 114  T_PTR
 115          $var = INT2PTR($type,SvIV($arg))
 ...          ................................
 193  #############################################################################
 194  OUTPUT
 195  T_SV
 196          $arg = $var;
 197  T_SVREF
 198          $arg = newRV((SV*)$var);
 199  T_AVREF
 200          $arg = newRV((SV*)$var);
 201  T_HVREF
 202          $arg = newRV((SV*)$var);
 203  T_CVREF
 204          $arg = newRV((SV*)$var);
 205  T_IV
 206          sv_setiv($arg, (IV)$var);
 207  T_UV
 208          sv_setuv($arg, (UV)$var);
 209  T_INT
 210          sv_setiv($arg, (IV)$var);
 211  T_SYSRET
 212          if ($var != -1) {
 213              if ($var == 0)
 214                  sv_setpvn($arg, "0 but true", 10);
 215              else
 216                  sv_setiv($arg, (IV)$var);
 217          }
 218  T_ENUM
 219          sv_setiv($arg, (IV)$var);
 220  T_BOOL
 221          $arg = boolSV($var);
 222  T_U_INT
 223          sv_setuv($arg, (UV)$var);
 224  T_SHORT
 225          sv_setiv($arg, (IV)$var);
 226  T_U_SHORT
 227          sv_setuv($arg, (UV)$var);
 228  T_LONG
 229          sv_setiv($arg, (IV)$var);
 230  T_U_LONG
 231          sv_setuv($arg, (UV)$var);
 232  T_CHAR
 233          sv_setpvn($arg, (char *)&$var, 1);
 234  T_U_CHAR
 235          sv_setuv($arg, (UV)$var);
 236  T_FLOAT
 237          sv_setnv($arg, (double)$var);
 238  T_NV
 239          sv_setnv($arg, (NV)$var);
 240  T_DOUBLE
 241          sv_setnv($arg, (double)$var);
 242  T_PV
 243          sv_setpv((SV*)$arg, $var);
 244  T_PTR
 245          sv_setiv($arg, PTR2IV($var));
 ...          .............................
 307  T_OUT
 308          {
 309              GV *gv = newGVgen("$Package");
 310              if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
 311                  sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1)));
 312              else
 313                  $arg = &PL_sv_undef;
 314          }
pp2@nereida:~$
  1. La primera sección (que opcionalmente puede llevar la etiqueta TYPEMAP) contiene una lista de tipos C básicos y una cadena que se asocia con el tipo. El tipo C y la cadena/token van separados por un tabulador. Obsérvese que varios tipos C pueden ser asignados a la misma cadena. Por ejemplo unsigned y unsigned long se corresponden con T_UV .

  2. La segunda sección se denomina INPUT. Es usado por xsubpp para convertir los parámetros de la llamada Perl a sus equivalentes C.

  3. La tercera sección se llama OUTPUT. Proporciona el código para traducir los valores retornados por la función C a sus equivalentes Perl.

Por ejemplo, el código para traducir entre un valor escalar SV y un entero C se obtiene en las entradas T_IV :

   1  # basic C types
   2  int                     T_IV
  ..  ...                     ....
  56  #############################################################################
  57  INPUT
  ..  ....    ........................
  84  T_IV
  85          $var = ($type)SvIV($arg)
 ...  ....    ........................
 193  #############################################################################
 194  OUTPUT
 ...  ....    ........................
 205  T_IV
 206          sv_setiv($arg, (IV)$var);
Lo que indica que, al traducir un argumento Perl (de ahí el nombre $arg) de una XSUB que espera un entero se llamará a SvIV17.1 sobre dicho argumento para obtener la variable c (denotada por $var). Recíprocamente se usará sv_setiv 17.2 para asignar la parte entera de la variable retornada al código Perl.

La notación $type se sustituye por el tipo C de la variable. debe ser uno de los tipos listados en la primera parte del fichero typemap.

Consideremos el siguiente ejemplo:

lhp@nereida:~/Lperl/src/XSUB/TypeMapExample$ cat -n TypeMapExample.xs
 1  #include "EXTERN.h"
 2  #include "perl.h"
 3  #include "XSUB.h"
 4
 5  #include "ppport.h"
 6
 7  void square(int x, int *x2) {
 8    *x2 = x*x;
 9  }
10
11  MODULE = TypeMapExample         PACKAGE = TypeMapExample
12
13  void
14  square(x, x2)
15    int x
16    int &x2 = NO_INIT
17  OUTPUT:
18    x2

La palabra clave NO_INIT usada en la línea 16 indica que el parámetro es usado sólo como parámetro de salida. Si no se pone se producirán mensajes de advertencia:

lhp@nereida:~/Lperl/src/XSUB/TypeMapExample$ cat -n ./useypeMapexample.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use blib;
     4  use TypeMapExample;
     5
     6  my @a;
     7  my $i = 0;
     8  @ARGV = 1..5 unless @ARGV;
     9  square($_, $a[$i++]) for @ARGV;
    10
    11  print "@a\n";
    12
lhp@nereida:~/Lperl/src/XSUB/TypeMapExample$ ./useypeMapexample.pl
Use of uninitialized value in subroutine entry at ./useypeMapexample.pl line 9.
Use of uninitialized value in subroutine entry at ./useypeMapexample.pl line 9.
Use of uninitialized value in subroutine entry at ./useypeMapexample.pl line 9.
Use of uninitialized value in subroutine entry at ./useypeMapexample.pl line 9.
Use of uninitialized value in subroutine entry at ./useypeMapexample.pl line 9.
1 4 9 16 25
El ampersand en la declaración de x2 en la línea 16 advierte al compilador que en la llamada a la función debe pasar la dirección del argumento. Hay una diferencia en XSUB entre estas dos declaraciones:
  int * x2;
  int  &x2;
La primera indica que x2 es un vector de enteros. El valor de x2 será pasado a la función. La segunda indica que x2 es un entero que será pasado por referencia: su dirección será pasada a la función.

La compilación con xsubpp de este fuente produce la salida:

lhp@nereida:~/Lperl/src/XSUB/TypeMapExample$ xsubpp -typemap /usr/share/perl/5.8/ExtUtils/typemap \
                                                     TypeMapExample.xs | cat -n
Please specify prototyping behavior for TypeMapExample.xs (see perlxs manual)
 1  /*
 2   * This file was generated automatically by xsubpp version 1.9508 from the
 3   * contents of TypeMapExample.xs. Do not edit this file, edit TypeMapExample.xs instead.
 4   *
 5   *      ANY CHANGES MADE HERE WILL BE LOST!
 6   *
 7   */
 8
 9  #line 1 "TypeMapExample.xs"
10  #include "EXTERN.h"
11  #include "perl.h"
12  #include "XSUB.h"
13
14  #include "ppport.h"
15
16  void square(int x, int *x2) {
17    *x2 = x*x;
18  }
19
20  #line 21 "TypeMapExample.c"
21
22  XS(XS_TypeMapExample_square); /* prototype to pass -Wmissing-prototypes */
23  XS(XS_TypeMapExample_square)
24  {
25      dXSARGS;
26      if (items != 2)
27          Perl_croak(aTHX_ "Usage: TypeMapExample::square(x, x2)");
28      {
29          int     x = (int)SvIV(ST(0));
30          int     x2;
31
32          square(x, &x2);
33          sv_setiv(ST(1), (IV)x2);
34          SvSETMAGIC(ST(1));
35      }
36      XSRETURN_EMPTY;
37  }
..  .................. código para C++

  1. Observe la línea 29: int x = (int)SvIV(ST(0)) y su correspondencia directa con el esqueleto que figura en el typemap: $var = ($type)SvIV($arg).
  2. En la línea 33 tenemos sv_setiv(ST(1), (IV)x2) que se corresponde con el código de OUTPUT que figura en el fichero typemap: sv_setiv($arg, (IV)$var)
  3. La macro dXSARGS (línea 25) establece los punteros de manejo de la pila y declara la variable items que contiene el número de argumentos.
    lhp@nereida:~/Lperl/src/perlcompilerssource/perl-5.8.8$ sed -ne '114,116p'  XSUB.h
    #  define dXSARGS \
            dSP; dAXMARK; dITEMS
    #endif
    lhp@nereida:~/Lperl/src/perlcompilerssource/perl-5.8.8$ grep -ni 'define dSP' *
    pp.h:76:#define dSP             register SV **sp = PL_stack_sp
    lhp@nereida:~/Lperl/src/perlcompilerssource/perl-5.8.8$ sed -ne '103,105p'  XSUB.h
    #define dAXMARK                         \
            I32 ax = POPMARK;       \
            register SV **mark = PL_stack_base + ax++
    lhp@nereida:~/Lperl/src/perlcompilerssource/perl-5.8.8$ grep -ni 'define ditems' *
    XSUB.h:107:#define dITEMS I32 items = SP - MARK
    

  4. La macro XSRETURN_EMPTY retorna una lista vacía.

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