6

我正在尝试通过 Inline::C 编写一个 C 函数,该函数将创建并返回对 Perl 的数组引用......下面是我的脚本和输出。我是否正在分配数组并正确填充它?特别是,我创建了一个SV*'s 的临时数组并将它们传递给av_make,并返回一个使用newRV_noinc. 当我使用 Devel::Peek::Dump 查看返回的数组 ref 时,引用计数似乎很好,它看起来与直接在 perl 中创建的相同数据结构相同。

我还不明白什么是 mortalization/ sv_2mortal,或者我在这里是否需要它。显然, Inline::C 自动调用sv_2mortal返回的函数SV*,这可能相关也可能不相关。

#!/usr/bin/env perl
use strict;
use warnings FATAL => "all";
use 5.010_000;
use Data::Dumper;
use autodie;

use Inline ('C');
use Devel::Peek;

my $from_perl = [0 .. 9];
my $from_c = inline_array_maker(10); #same as above but in C

say Dumper $from_perl;
Dump($from_perl);

say Dumper $from_c;
Dump($from_c);


__END__
__C__
SV* (int len){
    int i;
    SV ** tmp_buffer;
    AV * arr;

    tmp_buffer  = malloc(sizeof(SV*) * len);

    printf("allocating tmp_buffer of size %d\n", len);
    for (i = 0; i < len; i++) {
        tmp_buffer[i] = newSViv(i);
    }

    // is av_make the most efficient way of doing this?  
    printf("av_make\n");
    arr = av_make(len, tmp_buffer);

    printf("freeing tmp_buffer\n");
    for (i = 0; i < len; i++) {
        sv_free(tmp_buffer[i]);
    }
    free(tmp_buffer);

    return newRV_noinc(arr);
}

我得到以下输出。

allocating tmp_buffer of size 10
av_make
freeing tmp_buffer
$VAR1 = [
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9
        ];

SV = IV(0x20c7520) at 0x20c7530
REFCNT = 1
FLAGS = (PADMY,ROK)
RV = 0x21c0fa8
SV = PVAV(0x25c7ec8) at 0x21c0fa8
    REFCNT = 1
    FLAGS = ()
    ARRAY = 0x25a0e80
    FILL = 9
    MAX = 9
    ARYLEN = 0x0
    FLAGS = (REAL)
    Elt No. 0
    SV = IV(0x20b2dd8) at 0x20b2de8
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 0
    Elt No. 1
    SV = IV(0x20b2fb8) at 0x20b2fc8
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 1
    Elt No. 2
    SV = IV(0x20c69f8) at 0x20c6a08
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 2
    Elt No. 3
    SV = IV(0x20c6a10) at 0x20c6a20
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 3
$VAR1 = [
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9
        ];

SV = IV(0x20d25c8) at 0x20d25d8
REFCNT = 1
FLAGS = (PADMY,ROK)
RV = 0x25ac6b8
SV = PVAV(0x25c7ea0) at 0x25ac6b8
    REFCNT = 1
    FLAGS = ()
    ARRAY = 0x25b9140
    FILL = 9
    MAX = 9
    ARYLEN = 0x0
    FLAGS = (REAL)
    Elt No. 0
    SV = IV(0x25aca80) at 0x25aca90
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 0
    Elt No. 1
    SV = IV(0x25ac750) at 0x25ac760
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 1
    Elt No. 2
    SV = IV(0x25ac5e8) at 0x25ac5f8
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 2
    Elt No. 3
    SV = IV(0x25ac930) at 0x25ac940
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 3
4

2 回答 2

7

你读过perldoc perlguts吗?它应该能让你大部分时间到达那里。Inline::C也可以处理AV*返回值。我可能会做类似的事情:

#!/usr/bin/env perl

use strict;
use warnings;

use Inline C => <<'END';
  AV* make_arrayref (int size) {
    int i;
    AV* ret = newAV();
    sv_2mortal((SV*)ret);
    for(i=0; i<size; i++) {
      av_push(ret, newSViv(i) );
    }
    return ret;
  }
END

my $arrayref = make_arrayref(10);
print "$_\n" for @$arrayref;

这是我编写的一些代码,可能也有帮助。

于 2013-03-11T22:02:45.727 回答
5

我还不明白 mortalization/sv_2mortal 是什么,或者我在这里是否需要它。显然,Inline::C 在返回 SV* 的函数上自动调用 sv_2mortal,这可能相关也可能不相关。

当您分配 RV 时,您就成为了它的所有者。如果你放弃对它的持有,你需要减少它的引用计数。但这会当场释放它,因为没有其他人持有它的引用。Mortalising 是一种延迟的 refcount 递减。它让调用者有机会首先抓住它(并增加它的引用计数)。

所以,是的,RV 需要死去,是的,类型图将为您死去返回的 SV*。

您的代码没有错误,但正如 Joel Berger 所示,您不需要预先构建 C 数组。他的代码也很好。

他还表明,如果您的返回类型是AV*,则类型映射将创建对数组的(致命的)引用,但它不会使数组本身致命。因此,您的两个主要回报选择是

// Return type = SV*
return newRV_noinc(arr);

// Return type = AV*
return MUTABLE_AV(sv_2mortal(MUTABLE_SV(newRV_noinc(arr)));
于 2013-03-11T22:33:09.093 回答