10

How can I pass Perl array by reference to C XS module?

my @array = ( 1..20 );

XSTEST::test_array_passing(\@array);

What do I do in XS so it sees the array?

brian d foy
  • 129,424
  • 31
  • 207
  • 592
Avinash
  • 12,851
  • 32
  • 116
  • 186

2 Answers2

11

XS can receive a ref to ARRAY as either an AV* or an SV*. The latter would have to be dereferenced to an AV*.

use Inline C => DATA;
@array = (1 .. 20);
$r = sum_of_elements1(\@array);
$s = sum_of_elements2(\@array);
print "$r $s\n";  #  produces output: "210 210\n"
__END__
__C__
double sum_of_elements1(AV* array)
{
  int i;
  double sum = 0.0;
  for (i=0; i<=av_len(array); i++) {
    SV** elem = av_fetch(array, i, 0);
    if (elem != NULL)
      sum += SvNV(*elem);
  }
  return sum;
}

double sum_of_elements2(SV* array_ref)
{
  AV* array;
  if (!SvROK(array_ref) || SvTYPE(SvRV(array_ref)) != SVt_PVAV)
    croak("expected ARRAY ref");
  array = (AV*) SvRV(array_ref);
  return sum_of_elements1(array);
}

The .xs file produced by this code declares:

double
sum_of_elements1 (array_ref)
        SV *    array_ref

double
sum_of_elements2 (array)
        AV *    array

Edit: in sum_of_element2(), added the check that the *SV was a reference to an array.

mob
  • 117,087
  • 18
  • 149
  • 283
  • Thanks for answer, I will try this, But in this case I will have to provide operations in the XS file only, I cannot put my array operations in separate C file, as I will need AV/SV data-structure access there – Avinash Oct 01 '09 at 17:43
  • 1
    Avinash: You can put perlapi-using code in arbitrary c files. Just include the necesary perl headers. – tsee Oct 01 '09 at 17:48
  • You also want to check whether the reference is a reference to an array. – tsee Oct 01 '09 at 17:48
  • Got It, It is working for me in both cases, I guess same is the case for perl hashes also. – Avinash Oct 01 '09 at 17:55
  • 1
    Woah, mobrule, what's with the lack of `use strict;` and `use warnings;` in that code? – Chris Lutz Oct 01 '09 at 21:50
  • It happens. I try to keep example code short without many lines of code that don't address the current issue. If the question was more like "How can I find the bug in my code?" then `strict` and `warnings` would make it. – mob Oct 01 '09 at 22:12
  • 1
    `tmpSV` must be a typo (as that's an undefined var), and as of 2017, that line should be written as: `if (! SvROK(array_ref) || SvTYPE(SvRV(array_ref)) != SVt_PVAV){`. I wasn't about to go editing your answer though. This answer definitely helped me with a reasonably related issue I was having, so I thought I'd comment on what I found. – stevieb Jan 22 '17 at 02:38
8

You can't pass a Perl array and have it automagically converted to, say, a C array of ints. You will have to resort to XS and the perlapi to do this. The reason is quite simple: a perl array contains untyped scalars. A C array holds N items of the same type.

What you can do is have an XSUB that takes an SV*. SV stands for scalar value. This naturally includes references (RV) and thus also references to arrays (AV's).

Here's how you can check whether a given SV* source is a reference to an array:

SV* tmpSV;
AV* theArray;
if (SvROK(source)) {                /* it's a reference */
  tmpSV = (SV*)SvRV(source);        /* deref */
  if (SvTYPE(tmpSV) == SVt_PVAV) {  /* it's an array reference */
    theArray = (AV*)tmpSV;
    /* do stuff with the array here */
  }
}
zdim
  • 64,580
  • 5
  • 52
  • 81
tsee
  • 5,034
  • 1
  • 19
  • 27