Usando h2xs

Si le damos como entrada a h2xs el fichero coord.h sin cambio alguno obtenemos una salida errónea:
lhp@nereida:~/Lperl/src/XSUB/h2xsexample$ cp coordlib/include/coord.h .
lhp@nereida:~/Lperl/src/XSUB/h2xsexample$ h2xs -xn Coord coord.h
Defaulting to backwards compatibility with perl 5.8.8
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.

Writing Coord/ppport.h
Scanning typemaps...
 Scanning /usr/share/perl/5.8/ExtUtils/typemap
Scanning coord.h for functions...
Expecting parenth after identifier in `struct _IO_FILE_plus _IO_2_1_stdin_;
....
lhp@nereida:~/Lperl/src/XSUB/h2xsexample$ ls -ltr Coord/
total 120
-rw-r--r--  1 lhp lhp 118192 2006-07-08 11:50 ppport.h
-rw-r--r--  1 lhp lhp      0 2006-07-08 11:50 Coord.xs
Observe que el fichero generado Coord.xs tiene tamaño 0. El error es causado por las cabeceras de inclusión de los fichero estandar:
lhp@nereida:~/Lperl/src/XSUB/h2xsexample$ head -2 coord.h
#include <stdio.h>
#include <math.h>
Tambien el nombre format usado para el primer argumento de las funciones de conversión polar2str y rectangular2str produce problemas (en la sección 17.22.1 encontrará los contenidos de coord.h) Después de editar la copia de coord.h, eliminar las primeras líneas y hacer los cambios en el nombre de los parámetros nos quedamos con el siguiente fichero de descripción de la interfaz:
hp@nereida:~/projects/perl/src/XSUB/h2xsexample$ cat -n coord.h
 1  #define PI 3.14159265358979323846264338327
 2
 3  typedef struct {
 4    double mod;
 5    double arg;
 6  } polar;
 7
 8  polar getpolar();
 9  char * polar2str(const char * f, polar p);
10
11  typedef struct {
12    double x;
13    double y;
14  } rectangular;
15
16  rectangular getrectangular();
17  char * rectangular2str(const char * f, rectangular r);
18
19  polar rc2pl(rectangular r);
20  rectangular pl2rc(polar p);
Procedemos a compilar de nuevo con h2xs.
lhp@nereida:~/projects/perl/src/XSUB/h2xsexample$ h2xs -xa -n Coord coord.h
Defaulting to backwards compatibility with perl 5.8.8
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.

Writing Coord/ppport.h
Scanning typemaps...
 Scanning /usr/share/perl/5.8/ExtUtils/typemap
Scanning coord.h for functions...
Scanning coord.h for typedefs...
Writing Coord/lib/Coord.pm
Use of uninitialized value in concatenation (.) or string at /usr/bin/h2xs line 1275.
Use of uninitialized value in concatenation (.) or string at /usr/bin/h2xs line 1275.
Writing Coord/Coord.xs
Writing Coord/fallback/const-c.inc
Writing Coord/fallback/const-xs.inc
Writing Coord/typemap
Writing Coord/Makefile.PL
Writing Coord/README
Writing Coord/t/Coord.t
Writing Coord/Changes
Writing Coord/MANIFEST
lhp@nereida:~/projects/perl/src/XSUB/h2xsexample$
La opción -x hace que se genere el código XSUB a partir de las declaraciones en el fichero cabecera. Para ello, es necesario tener instalado el paquete C::Scan .

La opción -a hace que se generen funciones de acceso/mutación (geter-seters en la terminología) a los campos de las estructuras polar y rectangular.

Obsérvese que no hemos usado la opción -A habilitando por tanto la generación de código para el acceso desde Perl a las constantes declaradas en el fichero de cabecera (en nuestro ejemplo la definición de PI). Esta es la causa de la existencia de los fichero const-c.inc y const-xs.inc.

A continuación editamos Makefile.PL modificando las líneas 12 (entrada LIBS) y 14 (entrada INC) para que el Makefile generado pueda encontrar la librería:

lhp@nereida:~/projects/perl/src/XSUB/h2xsexample$ cd Coord/
lhp@nereida:~/projects/perl/src/XSUB/h2xsexample/Coord$ vi Makefile.PL 
....
lhp@nereida:~/projects/perl/src/XSUB/h2xsexample/Coord$ cat -n Makefile.PL
 1  use 5.008008;
 2  use ExtUtils::MakeMaker;
 3  # See lib/ExtUtils/MakeMaker.pm for details of how to influence
 4  # the contents of the Makefile that is written.
 5  WriteMakefile(
 6      NAME              => 'Coord',
 7      VERSION_FROM      => 'lib/Coord.pm', # finds $VERSION
 8      PREREQ_PM         => {}, # e.g., Module::Name => 1.1
 9      ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
10        (ABSTRACT_FROM  => 'lib/Coord.pm', # retrieve abstract from module
11         AUTHOR         => 'Lenguajes y Herramientas de Programacion <lhp@>') : ()),
12      LIBS              => ['-L../coordlib/lib/ -lpl', '-lm'], # e.g., '-lm'
13      DEFINE            => '', # e.g., '-DHAVE_SOMETHING'
14      INC               => '-I. -I../coordlib/include', # e.g., '-I. -I/usr/include/other'
15          # Un-comment this if you add C files to link with later:
16      # OBJECT            => '$(O_FILES)', # link all the C files too
17  );
18  if  (eval {require ExtUtils::Constant; 1}) {
19    # If you edit these definitions to change the constants used by this module,
20    # you will need to use the generated const-c.inc and const-xs.inc
21    # files to replace their "fallback" counterparts before distributing your
22    # changes.
23    my @names = (qw(PI));
24    ExtUtils::Constant::WriteConstants(
25                                       NAME         => 'Coord',
26                                       NAMES        => \@names,
27                                       DEFAULT_TYPE => 'IV',
28                                       C_FILE       => 'const-c.inc',
29                                       XS_FILE      => 'const-xs.inc',
30                                    );
31
32  }
33  else {
34    use File::Copy;
35    use File::Spec;
36    foreach my $file ('const-c.inc', 'const-xs.inc') {
37      my $fallback = File::Spec->catfile('fallback', $file);
38      copy ($fallback, $file) or die "Can't copy $fallback to $file: $!";
39    }
40  }
La compilación del módulo no da errores:
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord$ perl Makefile.PL
Checking if your kit is complete...
Looks good
Warning: -L../coordlib/lib/ changed to -L/home/lhp/projects/perl/src/XSUB/h2xsexample/Coord/../coordlib/lib/
Writing Makefile for Coord
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord$ make
cp lib/Coord.pm blib/lib/Coord.pm
AutoSplitting blib/lib/Coord.pm (blib/lib/auto/Coord)
/usr/bin/perl /usr/share/perl/5.8/ExtUtils/xsubpp  \
   -typemap /usr/share/perl/5.8/ExtUtils/typemap -typemap typemap  Coord.xs > Coord.xsc && mv Coord.xsc Coord.c
cc -c  -I. -I../coordlib/include -D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN \
   -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -O2 \
   -DVERSION=\"0.01\" -DXS_VERSION=\"0.01\" -fPIC "-I/usr/lib/perl/5.8/CORE"   Coord.c
Running Mkbootstrap for Coord ()
chmod 644 Coord.bs
rm -f blib/arch/auto/Coord/Coord.so
LD_RUN_PATH="/home/lhp/projects/perl/src/XSUB/h2xsexample/Coord/../coordlib/lib" \
  cc  -shared -L/usr/local/lib Coord.o  -o blib/arch/auto/Coord/Coord.so    \
           -L/home/lhp/projects/perl/src/XSUB/h2xsexample/Coord/../coordlib/lib -lpl    \

chmod 755 blib/arch/auto/Coord/Coord.so
cp Coord.bs blib/arch/auto/Coord/Coord.bs
chmod 644 blib/arch/auto/Coord/Coord.bs
Manifying blib/man3/Coord.3pm
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord$ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" \
   "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/Coord....ok
All tests successful.
Files=1, Tests=2,  0 wallclock secs ( 0.06 cusr +  0.00 csys =  0.06 CPU)
Por cada struct el programa h2xs produce dos clases. Así para la struct polar escribe una clase polar y otra polarPtr que se corresponde con el tipo polar *. La clase polar dispone de dos métodos: un constructor new() que crea un objeto polar y un método _to_ptr() que construye el objeto polarPtr que representa al puntero al objeto polar. La clase polarPtr dispone de métodos de acceso y mutación de los campos de la struct. Estos métodos tienen los mismos nombres que los campos: mod, arg. Lo mismo sucede con la struct rectangular. Además se provee de la clase Coord que contiene el resto de las subrutinas de la librería. En el módulo Perl generado (lib/Coord.pm) sólo las constantes son exportadas por defecto. El resto de símbolos es insertado en @EXPORT_OK:
lhp@nereida:~/projects/perl/src/XSUB/h2xsexample/Coord$ sed -ne '20,34p' lib/Coord.pm | cat -n
 1  our %EXPORT_TAGS = ( 'all' => [ qw(
 2          PI
 3          getpolar
 4          getrectangular
 5          pl2rc
 6          polar2str
 7          rc2pl
 8          rectangular2str
 9  ) ] );
10
11  our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
12
13  our @EXPORT = qw(
14          PI
15  );

No siempre el código generado por h2xs funciona. Lo habitual es que el programador tenga que intervenir para reajustar el código, pero esta vez ha habido suerte: tenemos un modo casi funcional como muestra el siguiente ejemplo:

lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord$ mkdir script
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord$ vi usecoord.pl
..... 
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord$ cat -n usecoord.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use blib;
 4  use Coord;
 5
 6  print PI(),"\n";
 7
 8  my $p = polar->new();
 9  my $pp = $p->_to_ptr;
10
11  $pp->mod(1);
12  $pp->arg(PI()/4);
13
14  my $m = $pp->mod;
15  my $a = $pp->arg;
16
17  print "($m, $a)\n";
18
19  my $r = Coord::pl2rc($p);
20  my $rp = $r->_to_ptr;
21  print "(".$rp->x.", ".$rp->y,")\n";
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord$ ./usecoord.pl
3
(1, 0.75)
(0.731688868873821, 0.681638760023334)
Nos damos cuenta que algo raro ocurre con la impresión de PI() en la línea 6. La constante esta siendo interpretada como entera. Reeditamos Makefile.PL y cambiamos la línea 27 que se refiere al tipo por defecto de las constantes (campo DEFAULT_TYPE para que contenga NV en vez de IV):
18 if  (eval {require ExtUtils::Constant; 1}) {
19   # If you edit these definitions to change the constants used by this module,
20   # you will need to use the generated const-c.inc and const-xs.inc
21   # files to replace their "fallback" counterparts before distributing your
22   # changes.
23   my @names = (qw(PI));
24   ExtUtils::Constant::WriteConstants(
25                                      NAME         => 'Coord',
26                                      NAMES        => \@names,
27                                      DEFAULT_TYPE => 'NV',
28                                      C_FILE       => 'const-c.inc',
29                                      XS_FILE      => 'const-xs.inc',
30                                   );
31
32 }
Después de rehacer el módulo (make veryclean; perlMakefile.PL; make) la ejecución del programa de ejemplo produce una salida correcta:
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord$ ./usecoord.pl
3.14159265358979
(1, 0.785398163397448)
(0.707106781186548, 0.707106781186547)

Casiano Rodríguez León
Licencia de Creative Commons
Programación Distribuida y Mejora del Rendimiento
por Casiano Rodríguez León is licensed under a Creative Commons Reconocimiento 3.0 Unported License.

Permissions beyond the scope of this license may be available at http://campusvirtual.ull.es/ocw/course/view.php?id=44.
2012-06-19