使用Perl XS的64位整数

时间:2015-07-18 10:18:56

标签: c perl xs

我想使用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

foo()有C原型:

uint64_t foo(uint64_t);

我在perlxs文档和stackoverflow.com中找不到任何有用的东西。

3 个答案:

答案 0 :(得分:5)

Perl分发版附带的默认uint64_t中未定义typemap类型。根据{{​​3}}:

  

在更实际的术语中,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);

我不确定这些映射,特别是SvUVUV部分,因此您需要在任何实际代码中仔细测试它。我怀疑他们只是"普通"整数,因此uint64_t仅在内部使用时完全起作用,即在函数的定义范围内。

请注意unsigned long long类型至少 64位宽,根据C标准(自C99起),但它在我知道的每个实现上都是64位

test.xs为:

#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

test.pl定义为:

#!/usr/bin/perl
use ExtUtils::testlib;
use test;

print test::u64_mul_high(2147483647, 1000), "\n";

你得到的结果是:

499 (0001 1111 0011)

这是32-bit x 32-bit乘法的32位高,导致64位整数。

我在带有sizeof(long) = 4和Perl 5.10的32位GNU / Linux上进行了检查。

答案 1 :(得分:4)

$ perl -MConfig -le 'print $Config{use64bitint}'

将显示您的perl是否已编译为使用64位整数。如果是这样,IV是64位。

另见perldoc perlguts

  

什么是“IV”?

     

Perl使用一个特殊的typedef IV,它是一个简单的有符号整数类型,保证足够大以容纳指针(以及整数)。此外,还有UV,它只是一个无符号的IV。

     

Perl还使用两个特殊的typedef,I32和I16,它们总是至少分别为32位和16位。 (同样,还有U32和U16。)它们通常只有32位和16位长,但在Crays上它们都是64位。

另见Math::Int64

  

该模块增加了对Perl的64位整数(有符号和无符号整数)的支持。

     

...

     

如果可用,则回退到原生64位支持

     

如果程序中使用了词法编译指示Math::Int64::native_if_available,并且所使用的perl版本本身支持64位整数,则从创建64位整数的模块导入的函数(即uint64,{ {1}},int64string_to_int64等)将返回常规的perl标量。

答案 2 :(得分:4)

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.
}

如果你不能忍受精度损失并且想要支持32位UV,你可以使用Math::Int64的{​​{3}}(另见C API):

#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

使用类似

的类型图
TYPEMAP
uint64_t T_UINT64

INPUT
T_UINT64
    $var = SvU64($arg)

OUTPUT
T_UINT64
    $arg = newSVu64($var);

您可以直接在XS中使用uint64_t

uint64_t
foo(arg)
    uint64_t arg