c-使用64位整数与Perl XS



我想将一个使用64位整数的C函数移植到Perl代码中。为此,我使用PerlXS。

我的问题是在PerlXS类型中没有64位整数(只有U32、U16和U8(。

那么,在PerlXS代码中使用64位整数的最佳方式是什么呢?

下面是我想做的一个代码示例:

uint64_t
foo(integer)
   uint64_t integer
   CODE:
      RETVAL = foo(integer);
   OUTPUT:
      RETVAL

foo((具有C原型:

uint64_t foo(uint64_t);

我在perlxs文档和stackoverflow.com.

中找不到任何有用的东西Perl对无符号整数的内部类型是UV。如果您的平台具有64位UV,则应该可以。

使用32位UV(32位操作系统和Perl是在不使用64bitint的情况下编译的(,可以将大整数转换为浮点数(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的C API(另请参见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

使用类似的类型图

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

默认typemap中没有定义uint64_t类型,这是Perl发行版附带的。根据perlxstypemap:

更实际的说法是,typemap是代码片段的集合CCD_ 10编译器用来映射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是完全函数的,仅用于内部使用,也就是在函数的定义中。

注意,根据C标准(自C99以来(,unsigned long long类型至少为64位宽,但在我所知的每个实现中都是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

CCD_ 18定义为:

#!/usr/bin/perl
use ExtUtils::testlib;
use test;
print test::u64_mul_high(2147483647, 1000), "n";

你得到的结果是:

499 (0001 1111 0011)

即CCD_ 19乘法的高32位分支,其结果为64位整数。

我用sizeof(long) = 4和Perl 5.10在32位GNU/Linux上检查了这一点。

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

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

另请参阅perldoc perlguts:

什么是";IV">

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

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

另请参阅数学::Int64:

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

回退到本机64位支持(如果可用(

如果程序中使用了词法杂注Math::Int64::native_if_available,并且使用的perl版本本机支持64位整数,则从创建64位整数的模块导入的函数(即uint64int64string_to_int64native_to_int64等(将返回常规perl标量。

相关内容

  • 没有找到相关文章

最新更新