【问题标题】:perl xs - return perl array from c arrayperl xs - 从 c 数组返回 perl 数组
【发布时间】:2018-03-24 22:46:59
【问题描述】:

使用 XS 我试图将值从 C 数组传递到可在脚本中使用的 Perl 数组。

这是我的 xs 文件中的代码:

AV *
DoubleArray::getPerlArray()
    CODE:
    r = newAV();
    for(size_t i=0; i < THIS->count; i++)
    {
        av_push(RETVAL,newSVnv(THIS->data[i]));
    }
    OUTPUT:
    RETVAL

它编译得很好,但是当我在 perl 中运行以下内容时:

my @d = $C->getPerlArray();
foreach(@d)
{
    print "$_\n";
}

当我期望它打印数字列表时,它只会打印ARRAY(0x1408cdc)。

如何修改我的代码以正确传回 perl 数组?

【问题讨论】:

标签: arrays perl xs


【解决方案1】:

Perl subs 只能返回(0 个或多个)标量。当您尝试返回一个数组时(不可能不使 Perl 崩溃!),默认类型映射会返回对该数组的引用。

请注意,您的程序也会泄漏内存(因为 AV* 的默认类型映射应该使您的数组死亡,但不会)。


返回引用,方法一

AV* /* Returns: sv_2mortal(newRV(RETVAL)) */
DoubleArray::getPerlArrayRef()
    PREINIT:
        size_t i;
    CODE:
        RETVAL = (AV*)sv_2mortal((SV*)newAV());
        for (i=0; i < THIS->count; ++i) {
            av_push(RETVAL, newSVnv(THIS->data[i]));
        }

    OUTPUT:
       RETVAL

内存泄漏检查:

  • 数组的引用:1 (newAV) -1[delayed] (sv_2mortal) +1 (newRV) = 1[delayed](拥有参考)
  • 引用的引用:1 (newRV) -1[延迟] (sv_2mortal) = 0[延迟]

Perl:

my $array = $C->getPerlArrayRef();
say for @$array;

返回引用,方法二

SV* /* Returns: sv_2mortal(RETVAL) */
DoubleArray::getPerlArrayRef()
    PREINIT:
        AV* av;
        size_t i;
    CODE:
        av = newAV();
        RETVAL = newRV_noinc((SV*)av);
        for (i=0; i < THIS->count; ++i) {
            av_push(av, newSVnv(THIS->data[i]));
        }

    OUTPUT:
       RETVAL

内存泄漏检查:

  • 数组的引用:1 (newAV) +0 (newRV_noinc) = 1(由引用拥有)
  • 引用的引用:1 (newRV_noinc) -1[延迟] (sv_2mortal) = 0[延迟]

Perl:


返回引用,方法 3

void
DoubleArray::getPerlArrayRef()
    PREINIT:
        AV* av;
        size_t i;
    PPCODE:
        av = newAV();
        mXPUSHs(newRV_noinc((SV*)av));
        for (i=0; i < THIS->count; ++i) {
            av_push(av, newSVnv(THIS->data[i]));
        }

内存泄漏检查:

  • 数组的引用:1 (newAV) +0 (newRV_noinc) = 1(由引用拥有)
  • 引用的引用:1 (newRV_noinc) -1[延迟] (mXPUSHs) = 0[延迟]

Perl:


返回标量

我们必须检查上下文,因为我们不能在列表上下文之外的堆栈上放置多个标量。

void
DoubleArray::getElements()
    PREINIT:
        size_t i;
        U8 gimme = GIMME_V;
    PPCODE:
        if (gimme == G_ARRAY) {
            EXTEND(SP, THIS->count);
            for (i=0; i < THIS->count; ++i) {
                mPUSHn(THIS->data[i]);
            }
        }
        else if (gimme == G_SCALAR) {
            mXPUSHu(THIS->count);
        }

Perl:

my $count = $C->getElements();
say $count;

my @array = $C->getElements();
say for @array;

注意:sv_2mortal 的 refcnt 减量被延迟,直到调用者有机会增加 refcnt。

【讨论】:

  • 优秀的答案!谢谢!
  • 很好的答案!注意 Perl 版本 >= 5.16,如果您不关心向后兼容性,也可以声明类型映射 AV* T_AVREF_REFCOUNT_FIXED,请参阅 perlxs 中的 “通过 RETVAL 返回 SV、AV 和 HV” 部分了解更多信息。
  • 是的,如果没有介绍,我认为答案已经足够长了。
猜你喜欢
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 2011-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多