Para crear el esqueleto de un módulo que incluye código C usando XS
utilizaremos h2xs con las opción -A
.
1 lhp@nereida:~/Lperl/src/XSUB$ h2xs -A -n Example 2 Defaulting to backwards compatibility with perl 5.8.8 3 If you intend this module to be compatible with earlier perl versions, please 4 specify a minimum perl version with the -b option. 5 ! 6 Writing Example/ppport.h 7 Writing Example/lib/Example.pm ! 8 Writing Example/Example.xs 9 Writing Example/Makefile.PL 10 Writing Example/README 11 Writing Example/t/Example.t 12 Writing Example/Changes 13 Writing Example/MANIFEST 14 lhp@nereida:~/Lperl/src/XSUB$La opción
-A
indica que no se requiere carga automática
de constantes. En esta ocasión omitimos la habitual opción -X
para indicar que se trata de una extensión XS.
Un cambio que es apreciable de la salida es la creación
del fichero Example/Example.xs
.
El módulo generado Example.pm
también es un poco diferente
del que se obtiene cuando se usa -X
.
Veamos un extracto del fichero Example.pm
:
lhp@nereida:~/Lperl/src/XSUB$ cd Example/lib/ lhp@nereida:~/Lperl/src/XSUB/Example/lib$ cat -n Example.pm 1 package Example; 2 3 use 5.008008; 4 use strict; 5 use warnings; 6 7 require Exporter; 8 9 our @ISA = qw(Exporter); . ................. !30 require XSLoader; !31 XSLoader::load('Example', $VERSION); 32 33 # Preloaded methods go here. 34 35 1; 36 __END__ .. ........ lhp@nereida:~/Lperl/src/XSUB/Example/lib$Hay varios cambios. Primero, el módulo usa XSLoader . El módulo
XSLoader
proporciona el código necesario
para cargar librerías compartidas en Perl.
Las librerías compartidas serán creadas desde el código
XS
en aquellos sistemas operativos que permiten
el uso de librerías compartidas.
El segundo cambio está en la línea 31:
La función XSLoader::load
se encarga de cargar la librería dinámica
con nombre Example
y comprueba que su versión
es $VERSION
.
Una alternativa es usar el módulo DynaLoader.
El módulo DynaLoader proporciona un mecanismo mas potente
pero también una interfaz mas compleja.
El aspecto típico de un módulo XS construido con
DynaLoader
es similar:
package YourPackage; require DynaLoader; our @ISA = qw( OnePackage OtherPackage DynaLoader ); our $VERSION = '0.01'; bootstrap YourPackage $VERSION;
El fichero Example.xs
debe contener los ficheros
de cabecera que permiten el acceso a las funciones
internas de Perl (líneas 1-5).
lhp@nereida:~/Lperl/src/XSUB/Example$ cat -n Example.xs 1 #include "EXTERN.h" 2 #include "perl.h" 3 #include "XSUB.h" 4 5 #include "ppport.h" 6 7 8 MODULE = Example PACKAGE = Example 9 /* Aqui comienza la sección XS */
Para que un módulo XS sea portable en múltiples plataformas es necesario tener en cuenta
varios factores. Uno de ellos es que la API de Perl cambia con el tiempo.
Se añaden nuevas funciones y se eliminan otras.
El módulo Devel::PPPort tiene por objetivo proveer
compatibilidad a través de esos cambios de manera que un programador
XS no tenga que preocuparse del problema de las versiones de Perl.
Devel::PPPort
genera un fichero C de cabecera ppport.h
que es al mismo tiempo un programa Perl que nos da información
sobre la compatibilidad del código XS:
lhp@nereida:~/Lperl/src/XSUB/Example$ perl ppport.h Scanning ./Example.xs ... === Analyzing ./Example.xs === No need to include 'ppport.h' Suggested changes: --- ./Example.xs +++ ./Example.xs.patched @@ -2,7 +2,6 @@ #include "perl.h" #include "XSUB.h" -#include "ppport.h" MODULE = Example PACKAGE = ExamplePara generar
ppport.h
sin la ayuda de h2xs
podemos hacer.
perl -MDevel::PPPort -eDevel::PPPort::WriteFile
La inclusión de ppport.h
nos da acceso a una parte de la API de Perl
que no estaba disponible en versiones anteriores.
Ejecute perl ppport.h --list-provided
para obtener la lista de elementos
proveídos por ppport.h
.
Además se puede usar para obtener información sobre la portabilidad de las diferentes funciones, por ejemplo:
lhp@nereida:~/Lperl/src/XSUB/Example$ perl ppport.h --api-info=sv_magicext === sv_magicext === Supported at least starting from perl-5.7.3.
Para mas detalles consulta perldoc ppport.h
.
Un fichero XS comienza con una sección en lenguaje C que termina con
la primera aparición de la directiva MODULE (línea 8).
A partir de ahí pueden seguir otras directivas XS o
definiciones XSUB . Una definición XSUB proporciona la información
necesaria para establecer el puente entre los convenios de llamada
Perl y los de C para una función dada. Se proporciona
una definición XSUB
por cada función C que se desea hacer pública.
El lenguaje utilizado en este parte del fichero se conoce
como lenguaje XS. El traductor xsubpp reconoce los trozos
de documentación POD en ambas secciones.
La directiva MODULE declara el espacio de nombres del módulo y define el nombre de la librería que será creada. La palabra clave PACKAGE define el espacio de nombres Perl para las subrutinas. Después de esta línea todo lo que sigue es código XS. Añadamos el siguiente código XS:
lhp@nereida:~/Lperl/src/XSUB/Example$ cat -n Example.xs 1 #include "EXTERN.h" 2 #include "perl.h" 3 #include "XSUB.h" 4 5 #include "ppport.h" 6 7 double computepi(int id, int N, int np) { 8 double sum, left; 9 10 int i; 11 for(i=id, sum = 0; i<N; i+=np) { 12 double x = (i + 0.5)/N; 13 sum += 4/(1 + x*x); 14 } 15 sum /= N; 16 return (sum); 17 } 18 19 20 MODULE = Example PACKAGE = Example 21 22 double 23 computepi(id, N, np) 24 int id 25 int N 26 int np
Para funciones como computepi
que manejan tipos simples
la declaración de XSUB se limita a proporcionar el
prototipo de la función (líneas 22-26) en estilo KR.
La primera línea define el tipo de retorno, luego sigue una línea
con la forma de llamada a la función y después una línea por argumento,
especificando su tipo C.
! 1 lhp@nereida:~/Lperl/src/XSUB/Example$ perl Makefile.PL 2 Checking if your kit is complete... 3 Looks good 4 Writing Makefile for Example ! 5 lhp@nereida:~/Lperl/src/XSUB/Example$ make 6 7 cp lib/Example.pm blib/lib/Example.pm 8 cp useexample.pl blib/lib/useexample.pl ! 9 /usr/bin/perl /usr/share/perl/5.8/ExtUtils/xsubpp ! -typemap /usr/share/perl/5.8/ExtUtils/typemap ! Example.xs > Example.xsc && mv Example.xsc Example.c !10 Please specify prototyping behavior for Example.xs (see perlxs manual) !11 cc -c -I. -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" ! Example.c !12 Running Mkbootstrap for Example () !13 chmod 644 Example.bs !14 rm -f blib/arch/auto/Example/Example.so !15 cc -shared -L/usr/local/lib Example.o -o blib/arch/auto/Example/Example.so \ 16 \ 17 !18 chmod 755 blib/arch/auto/Example/Example.so !19 cp Example.bs blib/arch/auto/Example/Example.bs !20 chmod 644 blib/arch/auto/Example/Example.bs 21 Manifying blib/man3/Example.3pm
Expliquemos en mas detalle la secuencia anterior:
Makefile
(líneas 1-4) a partir de Makefile.PL
:
lhp@nereida:~/Lperl/src/XSUB/Example$ 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 => 'Example', 7 VERSION_FROM => 'lib/Example.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/Example.pm', # retrieve abstract from module 11 AUTHOR => 'Lenguajes y Herramientas de Programacion') : ()), 12 LIBS => [''], # e.g., '-lm' 13 DEFINE => '', # e.g., '-DHAVE_SOMETHING' 14 INC => '-I.', # 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 );La ejecución de
Makefile.PL
detecta la presencia de ficheros .xs
en el directorio y adapta el Makefile
para que dichos ficheros
sean procesados.
blib/lib/
(líneas 7-8).
El nombre blib
viene de Build Library.
.c
la salida se almacena en un fichero temporal Example.xsc
y luego
se renombra como Example.c
.
Ello se hace para evitar que ficheros en proceso de formación
puedan ser erróneamente tomados por código C válido.
PROTOTYPES: DISABLE
en el fichero XS justo después de la declaración
MODULE
:
lhp@nereida:~/Lperl/src/XSUB/Example$ cat -n Example.xs 1 #include "EXTERN.h" 2 #include "perl.h" 3 #include "XSUB.h" 4 5 #include "ppport.h" 6 .. ................................................. 20 MODULE = Example PACKAGE = Example 21 PROTOTYPES: DISABLE 22 23 double 24 computepi(id, N, np) 25 int id 26 int N 27 int np
Example.c
generado por xsubpp
(línea 11).
cc -c -I. -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" Example.cEl compilador y las opciones del compilador usadas son las mismas que se emplearon para construir la instalación actual del intérprete Perl. Los valores con los que la versión usada de Perl fue compilada pueden obtenerse por medio del módulo Config . Por ejemplo:
lhp@nereida:~/Lperl/src/XSUB$ perl -MConfig -e 'print Config::myconfig()' Summary of my perl5 (revision 5 version 8 subversion 8) configuration: Platform: osname=linux, osvers=2.6.15.4, archname=i486-linux-gnu-thread-multi uname='linux ninsei 2.6.15.4 #1 smp preempt mon feb 20 09:48:53 pst 2006\ i686 gnulinux ' config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN -Dcccdlflags=-fPIC -Darchname=i486-linux-gnu -Dprefix=/usr -Dprivlib=/usr/share/perl/5.8 -Darchlib=/usr/lib/perl/5.8 -Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5 -Dvendorarch=/usr/lib/perl5 -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl/5.8.8 -Dsitearch=/usr/local/lib/perl/5.8.8 -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1 -Dsiteman3dir=/usr/local/man/man3 -Dman1ext=1 -Dman3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Uusesfio -Uusenm -Duseshrplib -Dlibperl=libperl.so.5.8.8 -Dd_dosuid -des' hint=recommended, useposix=true, d_sigaction=define usethreads=define use5005threads=undef useithreads=define usemultiplicity=define useperlio=define d_sfio=undef uselargefiles=define usesocks=undef use64bitint=undef use64bitall=undef uselongdouble=undef usemymalloc=n, bincompat5005=undef ! Compiler: ! cc='cc', ccflags ='-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', ! optimize='-O2', cppflags='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN -fno-strict-aliasing -pipe -I/usr/local/include' ccversion='', gccversion='4.0.3 (Debian 4.0.3-1)', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, prototype=define Linker and Libraries: ld='cc', ldflags =' -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt perllibs=-ldl -lm -lpthread -lc -lcrypt libc=/lib/libc-2.3.6.so, so=so, useshrplib=true, libperl=libperl.so.5.8.8 gnulibc_version='2.3.6' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E' cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib'
Config
.
Observe que la librería se deja en el directorio
blib/arch/auto/Example/
(línea 18).
!13 chmod 644 Example.bs !14 rm -f blib/arch/auto/Example/Example.so !15 cc -shared -L/usr/local/lib Example.o -o blib/arch/auto/Example/Example.so \ 16 \ 17 !18 chmod 755 blib/arch/auto/Example/Example.so !19 cp Example.bs blib/arch/auto/Example/Example.bs !20 chmod 644 blib/arch/auto/Example/Example.bs 21 Manifying blib/man3/Example.3pm
Para probar el módulo escribimos un programa de prueba:
lhp@nereida:~/Lperl/src/XSUB/Example$ cat -n useexample.pl 1 #!/usr/bin/perl -w 2 use blib; # Para que encuentre el módulo 3 use strict; 4 use Example; 5 6 my $n = shift || 1000; 7 my $x = 0; 8 9 $x += Example::computepi(0,$n,4) for 0..3; 10 print "$x\n"; lhp@nereida:~/Lperl/src/XSUB/Example$ time ./useexample.pl 1000 3.14459174129814 real 0m0.032s user 0m0.020s sys 0m0.010s lhp@nereida:~/Lperl/src/XSUB/Example$ time ./useexample.pl 10000000 3.14159295358978 real 0m0.359s user 0m0.360s sys 0m0.000s