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.
- Change the parameter type to
SV *
(pointer to an SV
).
- Use the API to check if this particular
SV
is a reference as opposed to some other kind of scalar
- Check the RV to make sure it's pointing at an array and not something else.
- Dereference the
RV
to get the SV
that it points to.
- Since we know that
SV
is an array, cast it to AV
and start working with it.
- Lookup the third element of that array, which is another
SV
.
- Check that the
SV
we got from the array is a numerical value suitable for C printf
- Extract the actual numerical out of the
SV
.
- Print the message
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:
- The Perlguts Illustrated PDF, then
- The
perlguts
manpage, and then
- The Inline::C Cookbook, while consulting
- The
perlapi
manpage.