|Just another Perl shrine|
Re: Perl XSby ambrus (Abbot)
|on Nov 18, 2013 at 23:52 UTC||Need Help??|
The easiest way to work with XS is to put it in a full-blown perl module built with ExtUtils::MakeMaker. I'll show you how to do that.
Meanwhile, we'll also have to fix your XS file, because the declaration of print_array_int doesn't make sense as is: XS won't know how to pass an array of ints to your print_array_int function. I've taken the liberty to rename and modify some other parts of your code too.
First, create a directory which I'll call PrintArrays, this will be the directory where your perl module exists. You'll need five files in this directory.
First, your library which does the heavy work, written in C, and a header for it. Write this to the file printfuncs.h to define the interface of your C code.
Put the implementation into printfuncs.c.
Now we have to write the Makefile.PL, which is the customary name for the script that calls ExtUtils::MakeMaker. Here, we'll ask MakeMaker to build your C code for you, with the same compiler as your perl is normally built. This has the advantage that your module is easy to deploy, because MakeMaker will automatically compile it together with the XS code, so you don't need an external dependency. Now if your C code were a large library used in places other than this perl module, you could of course compile it separately and require it as a separately installed dependency for this perl module, but we won't do that now. So write this to Makefile.PL
Note the OBJECT clause which tells MakeMaker that printfuncs.c must be compiled to compile this module. This also refers to the XS part of the code which I'll show next.
By default, MakeMaker will automatically scan your module directory (recursively) for .xs and .pm files, and knows how to build and install them automatically when you build and install the module. So our XS code will reside in a file called PrintArrays.xs. Here's how it starts (the easy part).
The print_array_char function will Just Work, because the XS compiler knows how to pass a perl string to a C function that expects a const char * argument. The print_array_int function is more complicated though, because it expects an array of C ints, which the XS compiler doesn't know how to create from your perl code. There are two ways around this, and I'll show the easy way first. We'll pack the integers to a C array in a wrapper function we write in perl, represented in a perl string, and pass that (plus a length) to an XS function called print_array_int2_impl. This makes the XS function quite simple. Append this to PrintArrays.xs
We then have to write the perl part of the code. This will do two things: load the XS extension, and provide the wrapper function called print_array_int2 that will do the above mentioned packing. The perl part of the code is in the file called PrintArrays.pm and looks like this:
The XSLoader part loads the XS extension (this module is an alternative to Dynaloader, you can use whichever you prefer). The print_array_int2 function expects a single argument: a reference to an array of integers, which it will pack and pass to the XS function, which in turn calls the C function.
Build this module by running perl Makefile.PL && make
Now, if you wanted to use this module, you would install it by make install, which would copy both PrintArrays.pm and shared the library built from the xs and C code to wherever perl modules normally reside in your perl installation. But now, we'll now just do a quick check in place. As the compiled module isn't yet installed to the normal location, but built in the ./blib directory, we'll have to tell perl to find it there. Run the following.
This should give the following output, all of which is printed directly from the C file.
I'll show a different solution for wrapping the perl_array_int C function under the fold.
Recall that as the C function expects a C array of ints, we had to pack the perl array of integers to such a structure. In the first solution, we did this from perl code. Now we'll do it from XS code, which is more complicated because we have to access a perl array directly.
Append this to PrintArrays.xs
The new XS function called perl_array_int needs no perl wrapper, because it takes a perl array reference. (Note that I've sneakily added this XS function to the export list of PrintArrays.pm earlier, even when the XS function didn't yet exist.)
Now rebuild the module by running make, then retest with this command.
This will print the following:
You can see that the two perl functions print_array_int and print_array_int2 give the same output.
For my previous XS example on perlmonks, see also Re: Opposite of strtol?.