La siguiente subrutina _hcf
calcula el máximo común divisor de dos números.
El código es ''puro cálculo'' por lo que la implementación
Perl es obviamente inferior en rendimiento a la correspondiente
versión C:
$ cat -n hcf.c 1 int hcf(int x, int y) { 2 int t; 3 4 if (y > x) { 5 t = x; x = y; y = t; 6 } 7 if (x == y) return x; 8 while (y) { 9 t = x; 10 x = y; 11 y = t % y; 12 } 13 return x; 14 }
Para poder llamar a la versión C de la subrutina desde Perl lo único que necesitamos es crear una librería dinámica:
$ cc -shared hcf.o -o libhcf.so $ ls -ltr | tail -1 -rwxr-xr-x 1 lhp lhp 5446 2007-05-29 16:52 libhcf.so $ nm libhcf.so # nm nos lista los símbolos en la librería 00001650 A __bss_start 000003c0 t call_gmon_start 00001650 b completed.4463 00001558 d __CTOR_END__ 00001554 d __CTOR_LIST__ w __cxa_finalize@@GLIBC_2.1.3 000004f0 t __do_global_ctors_aux 000003f0 t __do_global_dtors_aux 00001648 d __dso_handle 00001560 d __DTOR_END__ 0000155c d __DTOR_LIST__ 00001568 a _DYNAMIC 00001650 A _edata 00001654 A _end 00000534 T _fini 00000450 t frame_dummy 00000550 r __FRAME_END__ 00001634 a _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 0000048c T hcf # Nuestra función máximo común divisor 00000485 t __i686.get_pc_thunk.bx 0000036c T _init 00001564 d __JCR_END__ 00001564 d __JCR_LIST__ w _Jv_RegisterClasses 0000164c d p.4462
El módulo P5NCI permite cargar la librería desde Perl y llamar a las funciones:
$ cat -n usehcf.pl 1 #!/usr/local/bin/perl -w 2 use strict; 3 use P5NCI::Library; 4 5 my $lib = P5NCI::Library->new( library => './libhcf.so' ); 6 $lib->install_function( 'hcf', 'iii' ); 7 8 print hcf( 20, 10 ), "\n"; 9 print hcf( 12, 9 ), "\n"; 10 print hcf( 18, 12 ), "\n";La llamada a
P5NCI::Library->new
carga la librería.
La subsiguiente llamada al método install
busca la función en la librería
y crea una interfaz Perl para la especificación dada por la firma 'iii'
.
Esta firma indica que la función recibe dos enteros y devuelve un entero.
Observe que mediante P5NCI
es posible usar funciones cuyo
fuente no esta disponible. Tampoco importa en que lenguaje esté escrito.
Importa que dispongamos de la librería y que conozcamos el nombre y la
interfaz de la función.
El constructor admite la opción path
que permite especificar el
path de búsqueda para la librería. En este caso
podemos usar el ''nombre'' oficial de la librería (hcf
) y no
la especifciación completa. También dispone de una opción package
que
permite especificar el paquete en el que se aloja la función.
$ cat -n usehcf2.pl 1 #!/usr/local/bin/perl -w 2 use strict; 3 use P5NCI::Library; 4 5 my $lib = P5NCI::Library->new( library => 'hcf', path => '.' ); 6 print "Path de búsqueda:\n@DynaLoader::dl_library_path\n"; 7 $lib->install_function( 'hcf', 'iii' ); 8 9 print "hcf( 20, 10 ) = ",hcf( 20, 10 ), "\n"; 10 print "hcf( 12, 9 ) = ",hcf( 12, 9 ), "\n"; 11 print "hcf( 18, 12 ) = ",hcf( 18, 12 ), "\n";
La variable DynaLoader::dl_library_path contiene el camino de búsqueda de las librerías dinámicas.
Ahora al ejecutar el programa obtenemos el máximo común divisor para cada una de las tres parejas:
$ usehcf2.pl Path de búsqueda: . /usr/local/lib /lib /usr/lib hcf( 20, 10 ) = 10 hcf( 12, 9 ) = 3 hcf( 18, 12 ) = 6
La portabilidad de P5NCI es todavía insuficiente. El porcentaje de plataformas en las que funciona es bajo aún. Existen otros mecanismos para empotrar otros lenguajes en Perl: XS, Inline, etc. que son mas robustos.