我想使用64位整数将一个C函数移植到Perl代码.为此,我使用Perl XS.
我的问题是Perl XS类型中没有64位整数(只有U32,U16和U8).
那么,在Perl XS代码中使用64位整数的最佳方法是什么?
这是我想要做的代码示例:
uint64_t
foo(integer)
uint64_t integer
CODE:
RETVAL = foo(integer);
OUTPUT:
RETVAL
Run Code Online (Sandbox Code Playgroud)
foo()有C原型:
uint64_t foo(uint64_t);
Run Code Online (Sandbox Code Playgroud)
我没有在perlxs文档和stackoverflow.com中找到任何有用的东西.
Perl 的无符号整数的内部类型是UV. 如果您的平台具有 64 位 UV,则应该没问题。
使用 32 位 UV(32 位操作系统和 Perl 是在没有 use64bitint 的情况下编译的),您可以将大整数与浮点数(NV通常是double)相互转换。但由于 IEEE 双打只有 53 位尾数,这将导致大数精度损失。
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.
}
Run Code Online (Sandbox Code Playgroud)
如果你不能住在一起的精度损失,并希望支持32位的UV,你可以使用C API的Math::Int64(见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:
RETVAL
Run Code Online (Sandbox Code Playgroud)
像这样的类型图
TYPEMAP
uint64_t T_UINT64
INPUT
T_UINT64
$var = SvU64($arg)
OUTPUT
T_UINT64
$arg = newSVu64($var);
Run Code Online (Sandbox Code Playgroud)
您可以uint64_t直接在您的 XS 中使用:
uint64_t
foo(arg)
uint64_t arg
Run Code Online (Sandbox Code Playgroud)
该uint64_t类型是不是在默认定义typemap,自带的Perl发行.根据perlxstypemap:
在更实际的术语中,typemap是代码片段的集合,
xsubpp编译器使用它们将C函数参数和值映射到Perl值.
您可以定义自己的typemap(与.xs文件位于同一目录中).它可能看起来像:
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);
Run Code Online (Sandbox Code Playgroud)
我不知道这些映射,尤其是SvUV和UV部分,所以你需要在任何实际的代码仔细测试它.我怀疑它们只是"普通"整数,因此uint64_t仅在内部使用时完全起作用,即在函数的定义范围内.
请注意,根据C标准(自C99起),unsigned long long类型至少为 64位宽,但在我知道的每个实现上它都是64位.
用test.xsas:
#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
Run Code Online (Sandbox Code Playgroud)
并test.pl定义为:
#!/usr/bin/perl
use ExtUtils::testlib;
use test;
print test::u64_mul_high(2147483647, 1000), "\n";
Run Code Online (Sandbox Code Playgroud)
你得到的结果是:
499 (0001 1111 0011)
Run Code Online (Sandbox Code Playgroud)
这是32-bit x 32-bit乘法的32位高,导致64位整数.
我在32位GNU/Linux sizeof(long) = 4和Perl 5.10 上检查过这个.
$ perl -MConfig -le 'print $Config{use64bitint}'
Run Code Online (Sandbox Code Playgroud)
将显示您的perl编译是否使用 64 位整数。如果是,则 IV 是 64 位。
另请参见perldoc perlguts:
什么是“IV”?
Perl 使用特殊的 typedef IV,它是一个简单的有符号整数类型,保证足够大以容纳指针(以及整数)。此外,还有 UV,它只是一个无符号的 IV。
Perl 还使用两个特殊的 typedef,I32 和 I16,它们的长度始终至少分别为 32 位和 16 位。(同样,还有 U32 和 U16。)它们通常正好是 32 和 16 位长,但在 Cray 上它们都是 64 位。
另请参见Math::Int64:
该模块向 Perl 添加了对 64 位整数(有符号和无符号)的支持。
...
如果可用,则回退到本机 64 位支持
如果程序中使用了词法编译指示
Math::Int64::native_if_available,并且所使用的 perl 版本本身支持 64 位整数,则从创建 64 位整数的模块导入的函数(即uint64、int64、string_to_int64、native_to_int64等)将返回常规 perl 标量。