I can't get arrayrefs passed into a C function using Inline C. I would like some help, please.
First, just to prove I can get Inline C to work, I'll pass a scalar value to a C function:
#!/usr/bin/perl -I.
#
# try1.pl
#
use Inline C;
my $c = 3.8;
foo( $c );
__END__
__C__
void foo( double c )
{
printf( "C = %f\n", c );
}
And run it:
% ./try1.pl
C = 3.800000
Now do the same thing, but with an arrayref:
#!/usr/bin/perl -I.
#
# try2.pl
#
use Inline C;
my @abc = (1.9, 2.3, 3.8);
foo( \@abc );
__END__
__C__
void foo( double *abc )
{
printf( "C = %f\n", abc[2] );
}
Run it:
% ./try2.pl
Undefined subroutine &main::foo called at ./try1.pl line 7.
Any ideas what I'm doing wrong? Help greatly appreciated!
Inline::C is smart enough to extract values from SV
's based on your C function's type signature. But if you want to pass complex Perl structures to C functions you'll need to use the Perl API to extract the values. So, here's what you need to know for this problem:
An array is an instance of a C struct
called AV
. A reference is implemented by a struct
called an RV
. All of these are "subtypes" (kinda) of a base struct
called SV
.
So to make this function work we need to do a few things.
SV *
(pointer to an SV
). SV
is a reference as opposed to some other kind of scalarRV
to get the SV
that it points to.SV
is an array, cast it to AV
and start working with it.SV
.SV
we got from the array is a numerical value suitable for C printf
SV
.So putting that all together, we get something like this:
use Inline C;
my @abc = (1.9, 2.3, 3.8);
foo( \@abc );
__END__
__C__
void foo( SV *abc )
{
AV *array; /* this will hold our actual array */
SV **value; /* this will hold the value we extract, note that it is a double pointer */
double num; /* the actual underlying number in the SV */
if ( !SvROK( abc ) ) croak( "param is not a reference" );
if ( SvTYPE( SvRV( abc ) ) != SVt_PVAV ) croak( "param is not an array reference" );
/* if we got this far, then we have an array ref */
/* now dereference it to get the AV */
array = (AV *)SvRV( abc );
/* look up the 3rd element, which is yet another SV */
value = av_fetch( array, 2, 0 );
if ( value == NULL ) croak( "Failed array lookup" );
if ( !SvNOK( *value ) ) croak( "Array element is not a number" );
/* extract the actual number from the SV */
num = SvNV( *value );
printf( "C = %f\n", num );
}
Kinda makes you appreciate how much work Perl does under-the-hood. :)
Now, you don't have to be as super-explicit as that example. You could get rid of some of the temp variables by doing things inline, e.g.
printf( "C = %f\n", SvNV( *value ) );
would eliminate the need to declare num
. But I wanted to make it clear how much dereferencing and type-checking is needed to traverse a Perl structure in C.
And as @mob points out below, you don't actually have to do all that work (though it's a good idea to be familiar with how it works.)
Inline::C is smart enough that if you declare your function as
void foo( AV *abc ) {
...
}
It will automatically unwrap the AV
for you and you can go straight to the av_fetch
step.
If all of that seems baffling to you, I highly recommend taking a look at:
perlguts
manpage, and thenperlapi
manpage.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