Using XS i am trying to pass values from a C array into a Perl array that can be used in the script.
Here is the code from my xs file:
AV *
DoubleArray::getPerlArray()
CODE:
r = newAV();
for(size_t i=0; i < THIS->count; i++)
{
av_push(RETVAL,newSVnv(THIS->data[i]));
}
OUTPUT:
RETVAL
It compiles fine but when I run the following in perl:
my @d = $C->getPerlArray();
foreach(@d)
{
print "$_\n";
}
It just prints ARRAY(0x1408cdc)
when I am expecting it to print a list of numbers.
How can I modify my code to correctly pass back a perl array?
Perl subs can only return (0 or more) scalars. When you tried to return an array (impossible without crashing Perl!), the default typemap returned a reference to that array instead.
Note that your program also leaks memory (because the default typemap for AV*
should mortalize your array but doesn't).
Returning a reference, method 1
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
Memory leak check:
newAV
) -1[delayed] (sv_2mortal
) +1 (newRV
) = 1[delayed] (owned by reference)newRV
) -1[delayed] (sv_2mortal
) = 0[delayed]Perl:
my $array = $C->getPerlArrayRef();
say for @$array;
Returning a reference, method 2
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
Memory leak check:
newAV
) +0 (newRV_noinc
) = 1 (owned by reference)newRV_noinc
) -1[delayed] (sv_2mortal
) = 0[delayed]Perl: <same as above>
Returning a reference, method 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]));
}
Memory leak check:
newAV
) +0 (newRV_noinc
) = 1 (owned by reference)newRV_noinc
) -1[delayed] (mXPUSHs
) = 0[delayed]Perl: <same as above>
Returning scalars
We have to check context because we can't place more than one scalar on the stack outside of list context.
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;
Note: The refcnt decrement by sv_2mortal
is delayed until the caller has a chance to increment the refcnt.