I am trying to write a C function via Inline::C that will create and return an array reference to Perl... below is my script and output. Am I allocating the array and populating it properly? In particular, I create a temporary array of SV*
's and pass them to av_make
, and return a reference created with newRV_noinc
. The reference counts seem fine when I look at the returned array ref with Devel::Peek::Dump, which looks identical to the same data structure created in perl directly.
I do not yet understand what mortalization/sv_2mortal
are, or if I need it here. Apparently, Inline::C automatically calls sv_2mortal
on functions returning SV*
, which may or may not be relevant.
#!/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);
}
I get the following output.
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
Have you read perldoc perlguts? It should get you most of the way there. Inline::C
can handle an AV*
return value too. I might do something like:
#!/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;
Here is some code I have written which might help too.
I do not yet understand what mortalization/sv_2mortal are, or if I need it here. Apparently, Inline::C automatically calls sv_2mortal on functions returning SV*, which may or may not be relevant.
When you allocated the RV, you became the owner of it. If you relinquish your hold on it, you need to decrement its refcount. But that would free it on the spot since noone else holds a reference to it. Mortalising is a delayed refcount decrement. It gives the caller a chance to grab it (and increment its refcount) first.
So yes, the RV needs to be mortalised, and yes, the typemap will mortalise a returned SV* for you.
Your code is bug free, but as Joel Berger showed, you don't need to prebuild a C array. His code is also fine.
He also showed that if your return type is AV*
, the typemap will create a (mortal) reference to the array, but it will not mortalise the array itself. Your two primary return options are thus
// Return type = SV*
return newRV_noinc(arr);
and
// Return type = AV*
return MUTABLE_AV(sv_2mortal(MUTABLE_SV(newRV_noinc(arr)));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With