Quiero portar una función C usando enteros de 64 bits al código Perl. Para hacer eso, uso Perl XS.
Mi problema es que no hay enteros de 64 bits en los tipos Perl XS (solo U32, U16 y U8).
Entonces, ¿cuál es la mejor manera de usar números enteros de 64 bits en código Perl XS?
Aquí hay un ejemplo de código de lo que quiero hacer:
uint64_t foo(integer) uint64_t integer CODE: RETVAL = foo(integer); OUTPUT: RETVALfoo() tiene el prototipo C:
uint64_t foo(uint64_t);No encontré nada útil en la documentación de perlxs y en stackoverflow.com.
El tipo interno de Perl para enteros sin signo es UV . Si su plataforma tiene UV de 64 bits, debería estar bien.
Con UV de 32 bits (SO de 32 bits y Perl se compiló sin use64bitint), puede convertir números enteros grandes hacia y desde números de coma flotante ( NV , que generalmente es un double ). Pero dado que los dobles IEEE solo tienen 53 bits de mantisa, esto provocará una pérdida de precisión para números grandes.
uint64_t integer; // Convert uint64_t to SV. if (integer <= UV_MAX) { RETVAL = newSVuv((UV)integer); } else { RETVAL = newSVnv((NV)integer); } // Convert SV to uint64_t with range checks. if (SvUOK(sv)) { integer = (uint64_t)SvUV(sv); } else if (SvIOK(sv)) { IV iv = SvIV(sv); if (iv < 0) croak(...); integer = (uint64_t)iv; } else if (SvNOK(sv)) { NV nv = SvNV(sv); if (nv < 0.0 || nv >= 18446744073709551616.0) croak(...); integer = (uint64_t)nv; } else { // Parse a uint64_t from the string value, or whatever. } Si no puede vivir con la pérdida de precisión y desea admitir UV de 32 bits, puede usar la API C de Math::Int64 (consulte también Module::CAPIMaker ):
#define MATH_INT64_NATIVE_IF_AVAILABLE #include "perl_math_int64.h" MODULE = ... BOOT: PERL_MATH_INT64_LOAD_OR_CROAK; SV* foo(args...) CODE: uint64_t integer = ... RETVAL = newSVu64(integer); OUTPUT: RETVALCon un mapa tipográfico como
TYPEMAP uint64_t T_UINT64 INPUT T_UINT64 $var = SvU64($arg) OUTPUT T_UINT64 $arg = newSVu64($var); puedes usar uint64_t directamente en tu XS:
uint64_t foo(arg) uint64_t argEl tipo uint64_t no está definido en el typemap de tipos predeterminado, que viene con la distribución de Perl. Según perlxstypemap :
En términos más prácticos, el mapa de tipos es una colección de fragmentos de código que utiliza el compilador
xsubpppara asignar parámetros y valores de funciones de C a valores de Perl.
Puede definir su propio mapa de tipos (en el mismo directorio que .xs typemap . Podría verse como:
TYPEMAP unsigned long long T_U_LONG_LONG uint64_t T_U_LONG_LONG # equivalent to typedef unsigned long long uint64_t; INPUT T_U_LONG_LONG $var = (unsigned long long)SvUV($arg) OUTPUT T_U_LONG_LONG sv_setuv($arg, (UV)$var); No estoy seguro acerca de estas asignaciones, especialmente SvUV y parte UV , por lo que debe probarlo cuidadosamente en cualquier código real. Sospecho que son solo números enteros "simples", por lo que uint64_t es completamente funcional solo para uso interno, es decir, dentro de la definición de la función.
Tenga en cuenta que el tipo unsigned long long tiene al menos 64 bits de ancho según el estándar C (desde C99), pero es de 64 bits en todas las implementaciones que conozco.
Con test.xs como:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = test PACKAGE = test unsigned int u64_mul_high(a, b) unsigned int a unsigned int b CODE: RETVAL = ((uint64_t) a * b) >> 32; OUTPUT: RETVAL y test.pl definido como:
#!/usr/bin/perl use ExtUtils::testlib; use test; print test::u64_mul_high(2147483647, 1000), "\n";obtienes un resultado de:
499 (0001 1111 0011) que es el miembro superior de 32 bits de una multiplicación de 32-bit x 32-bit que da como resultado un entero de 64 bits.
Verifiqué esto en GNU/Linux de 32 bits con sizeof(long) = 4 y Perl 5.10.
$ perl -MConfig -le 'print $Config{use64bitint}' mostrará si su perl fue compilado para usar números enteros de 64 bits. Si es así, IV es de 64 bits.
Véase también perldoc perlguts :
¿Qué es un "IV"?
Perl utiliza un typedef IV especial que es un tipo de entero con signo simple que se garantiza que es lo suficientemente grande como para contener un puntero (así como un número entero). Además, está el UV, que es simplemente un IV sin firmar.
Perl también utiliza dos definiciones de tipos especiales, I32 e I16, que siempre tendrán una longitud mínima de 32 y 16 bits, respectivamente. (Nuevamente, también hay U32 y U16). Por lo general, tendrán exactamente 32 y 16 bits de largo, pero en Crays ambos serán de 64 bits.
Véase también Matemáticas::Int64 :
Este módulo agrega soporte para enteros de 64 bits, con y sin signo, a Perl.
...
Respaldo al soporte nativo de 64 bits si está disponible
Si el pragma léxico
Math::Int64::native_if_availablese usa en su programa y la versión de Perl en uso tiene soporte nativo para enteros de 64 bits, las funciones importadas del módulo que crean enteros de 64 bits (es decir,uint64,int64,string_to_int64,native_to_int64, etc.) devolverá escalares perl regulares.