20
Jul/09
3

XS Hits Newb; Bystanders Watch in Dismay

Last year I wrote GIS::Distance::Fast to provide some much needed speedups to the pure-perl GIS::Distance. This ::Fast version used Inline::C which isn’t all that optimal and was just a scapegoat for doing the right thing.

So, this evening I rolled up my sleeves and went to work. I first looked at a module I already knew to use XS, Cache::Memcached::Fast. That was overwelming.

Then I went on over and read through the perlxs and perlxstut docs. I was STILL floundering about not sure what to do. I then took a whack at using SWIG (Simplified Wrapper and Interface Generator). OhMyGodz I’m feeling overwhelmed now and still nothing works. I’m not going to even document my attempts up until this point.

I then found this document that goes in to more detail about using SWIG with Perl. The thing about this document that helped me so much is that in section 2, Perl Extension Building, it had the simplest example I had yet run in to, and it made sense! Its kinda funny that a SWIG document was what taught me how to write my own XS without using SWIG. :)

An XS distribution can contain as little as two files (well, beyond the usual Makefile.PL, etc). A single .pm file and a .xs file is enough. You can get more complex, if you want, of course. Once you’ve written your Module.xs file you can run it through xssubpp Module.xs > Module.c to see what the .c file looks like and to verify that your .xs file is correct.

Note that xssubpp may complain about you using types that aren’t in your typemap. This is because xssubpp doesn’t know how to find your system’s typemap file. You can specify a typemap file using the -typemap switch. The file is located somewhere in your perl lib path as ExtUtils/typemap.

So, let’s go for an example. Let’s make an XS module that convert hours to minutes. I structure all my code and files as if they would be distributed as a CPAN module. This has added benefits as you’ll see in a moment.

convert-hoursminutes/lib/Convert/HoursMins.pm:

package Convert::HoursMins;
use strict;
use warnings;

our $VERSION = 0.01;

use XSLoader;

XSLoader::load('Convert::HoursMins', $VERSION);

1;

convert-hoursminutes/HoursMins.xs:

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

double convert_hours_mins( double hours ) {
    return hours * 60;
}

MODULE = Convert::HoursMins  PACKAGE = Convert::HoursMins

PROTOTYPES: DISABLE

double
convert_hours_mins (hours)
    double hours

convert-hoursmins/Makefile.PL

use inc::Module::Install;

name     'Convert-HoursMins';
all_from 'lib/Convert/HoursMins.pm';
requires 'XSLoader';

WriteAll;

convert-hoursmins/t/00_basic.t

use strict;
use warnings;
use Test::More tests => 2;

BEGIN{ use_ok('Convert::HoursMins'); }

is(
    Convert::HoursMins::convert_hours_minutes(1.5),
    90,
    '1.5 hours is 90 minutes',
);

Now lets see if it worked:

perl Makefile.PL
make
make test

Bottom line for me is XS isn’t all that hard when your C code isn’t all that complex. SWIG is overkill for small stuff. But for large swaths of C code I bet SWIG saves lives. Also, XS is a little arcane, but its not too bad.

Tagged as:
Comments (3) Trackbacks (0)
  1. Steffen Mueller
    12:26 am on July 21st, 2009

    I always found that learning XS, examples help loads. Unfortunately, it’s not always easy to find simple examples. Nowadays, I know of a bunch of simple XS modules because I wrote them. You could, maybe, have a look at Parse::ExuberantCTags or Class::XSAccessor (maybe less simple). I’ve never had any success with SWIG. As an alternative for wrapping C++, have a look at Mattia Barbon’s ExtUtils::XSpp.

  2. Alexandr Ciornii
    3:52 am on July 21st, 2009

    Just require XSLoader in your Makefile.PL and it would work even in ancient (pre-5.006) perls without DynaLoader fallback. And since you use ‘our’, you don’t even need it.

  3. bluefeet
    7:05 am on July 21st, 2009

    Alexandr – good point, thanks. Post updated.

Leave a comment

No trackbacks yet.