This is a tutorial as much as it is a request for guidance from experienced XS/C/perlguts folks, as TIMTOWTDI, and in this case, likely, a better way.

This will show you how to pass a Perl array reference (aref) into a C function, convert the aref into a C array, work on it, then push it back onto the stack so the C function returns it as a Perl array.

It'll also show that although we bite off of Inline::C, the XS code it generates can be used in your distribution, even without the end-user needing Inline installed.

First, straight to the code. Comments inline for what's happening (or, at least, what I think is happening... feedback welcomed):

use warnings; use strict; use feature 'say'; use Inline 'Noclean'; use Inline 'C'; my $aref = [qw(1 2 3 4 5)]; # overwrite the existing aref to # minimize memory usage @$aref = aref_to_array($aref); say $_ for @$aref; __END__ __C__ void aref_to_array(SV* aref){ // check if the param is an array reference... // die() if not if (! SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV){ croak("not an aref

"); } // convert the array reference into a Perl array AV* chars = (AV*)SvRV(aref); // allocate for a C array, with the same number of // elements the Perl array has unsigned char buf[av_len(chars)+1]; // convert the Perl array to a C array int i; for (i=0; i<sizeof(buf); i++){ SV** elem = av_fetch(chars, i, 0); buf[i] = (unsigned char)SvNV(*elem); } // prepare the stack inline_stack_vars; inline_stack_reset; int x; for (x=0; x<sizeof(buf); x++){ // extract elem, do stuff with it, // then push to stack char* elem = buf[x]; elem++; inline_stack_push(sv_2mortal(newSViv(elem))); } // done! inline_stack_done; }

We now get an _Inline directory created within the current working directory, which has a build/ dir and then a sub directory (or multiple, just look at the one with the most recent timestamp). Peek in there, and you'll see a file with an .xs extention. This is the file you want if you want to include your work into a real Perl distribution. This essentially allows one to utilize my favourite feature of Inline::C , which is to build XS code for us, without having to know any XS (or little XS) at all.

After I run the above example, I get this in the XS file (my comments removed):

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "INLINE.h" void aref_to_array(SV* aref){ if (! SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV){ croak("not an aref

"); } AV* chars = (AV*)SvRV(aref); unsigned char buf[av_len(chars)+1]; int i; for (i=0; i<sizeof(buf); i++){ SV** elem = av_fetch(chars, i, 0); buf[i] = (unsigned char)SvNV(*elem); } inline_stack_vars; inline_stack_reset; int x; for (x=0; x<sizeof(buf); x++){ char* elem = buf[x]; elem++; inline_stack_push(sv_2mortal(newSViv(elem))); } inline_stack_done; } MODULE = c_and_back_pl_f8ff PACKAGE = main PROTOTYPES: DISABLE void aref_to_array (aref) SV * aref PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; aref_to_array(aref); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */

To note is the following line:

MODULE = c_and_back_pl_f8ff PACKAGE = main

That dictates the name of the module you're creating the XS for. You'll want to change it to something like:

MODULE = My::Module PACKAGE = My::Module

...then put that file in the root of your distribution, and add, into your distributions primary .pm module file:

require XSLoader; XSLoader::load('My::Module', $VERSION);