bluefeet

LinkedIn | GitHub | Twitter

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.

Authoring a CPAN Module

I’ve been authoring CPAN distribution since early 2003 with my first module being Geo::Distance. Since then I’ve authored several other distributions and do my best to keep my RT bug list small and fix FAILs. This process of authoring and maintaining modules on CPAN is in my top-10 list of all-time most educational experiences of my career.

I strongly encourage anyone that wants to become a better developer to take on the task and create a useful distribution for CPAN and/or work on an existing distro. There are plenty of projects out there (such as Moose, DBIx::Class, and Catalyst, to name a few) that need help and can use all the hands that they can get.

Now, if you would instead like to create your own distribution on CPAN, go for it! Its really quite easy and fun. But, it is VERY important that you think long and hard about what you want to release on CPAN. It may seem that every possible task that someone might want to accomplish in Perl is already available on CPAN - this is false. I regularly find myself needing tools that aren’t yet on CPAN. Don’t just write something for the sake of getting something on CPAN - develop something that is useful, of high quality, has a decent suite of automated tests, and doesn’t duplicate a module that is already on CPAN.

Once you developed a module or two, and you are ready to get it out the door, you’ll want to do a couple things. First, create a directory to hold your distribution. Assuming your module is named Acme::Yesterday:

mkdir Acme-Yesterday
cd Acme-Yesterday
vi Makefile.PL

You’ll want to install Module::Install first, before you go any further as it provides a much easier to configure build/test/install process than the old-school ExtUtils::MakeMaker.

Now, your Makefile.PL will look something like this:

use inc::Module::Install::DSL 0.91;
all_from lib/Acme/Yesterday.pm
requires DateTime
test_requires Test::More 0.42

(Notice the first line DOES have a semicolon at the end, while the rest do not)

Next, create a directory to put your module(s):

mkdir lib
mkdir lib/Acme
vi lib/Acme/Yesterday.pm

Now, your module will need some minimum requirements such as declaring the version of the distribution, the license type, and a name. Also, most CPAN modules follow a common convention for documentation using POD. Here is the most minimal of a module:

package Acme::Yesterday;
use strict;
use warnings;

our $VERSION = 0.01;

=head1 NAME

Acme::Yesterday - Make time() return the same hour,
second, and even minute as exactly 24 hours ago!

=head1 SYNOPSIS

    use Acme::Yesterday;

=head1 DESCRIPTION

I'm an interesting introduction to this module.

=cut

use DateTime;

=head1 METHODS

=head2 some_method

    some_method();

Description of some_method().

=cut

sub some_method { ... }

1;

=head1 AUTHOR

Your Name <your@email>

=head1 COPYRIGHT

This program is free software; you can redistribute it
and/or modify it under the same terms as Perl itself.

Alternatively you can use Module::Starter to create a shell of a module for you.

Now for automated tests. At a minimum you should write a test that verifies that your module can even be loaded. Here’s how to do that:

mkdir t
vi t/00_use.t

The content of 00_use.t should be something like:

#!/usr/bin/perl -w
use strict;
use warnings;
use Test::More tests => 1;
BEGIN{ use_ok('Acme::Yesterday'); }

You’ll need to make a couple more files:

echo 'perldoc Acme::Yesterday' > README
echo '0.01 - First release.' > Changes

OK, now just sit back and let Module::Install do its work:

perl Makefile.PL
make dist
make disttest

Assuming your tests pass when you run make disttest, you’re set to put your module on CPAN. The above steps would have now created for you a Acme-Yesterday-0.01.tar.gz. This is the file that you need to upload to CPAN. In order to upload a distribution to CPAN you’ll need to get a pause account. Once your request has been approved you can login to pause, go to the upload page, and upload your tarball. Within an hour or two your new distribution will be availabe on CPAN.

Once you’ve done this you’ll want to clean up your distribution directory as running the various make steps leaves around some files you don’t need:

make distclean

Good luck!

I’m in the Top 100!

So, I’ve been hearing about this CPAN Top 100 list but nobody seems to want to provide a link to it in their posts. Well, I dug that out and come to find that one of my very own modules is in the top 100… FAIL list. Woot! Anyways, I fixed it up and all should be well in an hour or two when everything gets mirrored out/re-indexed/etc by CPAN. There are 3 different top 100 lists on the site, all of them are interesting, so go take a look. It tickles me that Reaction is in the 100 Heavy list. Reaction is mst’s wrapper around Catalyst that is still in very early development. It looks promising.

The Perl is Dead, Long Live the Perl 5

Andy Lester recently wrote Promote Perl 6 by saying “Perl 5”. I think its a great idea, and as you may notice I’ve changed my blog in the few spots necessary to fit. Thanks for all the great work, Andy!

use autodie;

When writing perl code you’ll often find yourself using built-in functions that do not throw a fatal error when there is a problem. Instead you are expected to check the return value of the function, and if it is false, call die() yourself. Consider the following code:

open( my $fh, '>', 'testfile.txt' )
    or die('Unable to open testfile.txt for write');
print $fh "Hello world!\n";
close( $fh )
    or die('Unable to close testfile.txt');

This is a very cumbersome way to write code as you could easily forget to add these extra checks. If you’re lazy, which you should be, you’ll want fatal errors without having to write a bunch of extra code. This is where autodie steps in. The above code can then be rewritten as:

use autodie;

open( my $fh, '>', 'testfile.txt' );
print $fh "Hello world!\n";
close( $fh );

Much better. Generally you should want anything that has an unexpected problem to throw a fatal error. If you want your code to catch the error, handle it, and possibly continue running, wrap the offending code in an eval:

use autodie;

eval{
    open( my $fh, '>', 'testfile.txt' );
    print $fh "Hello world!\n";
    close( $fh );
};
if ($@) {
    unlink('testfile.txt') if -e 'testfile.txt';
    die($@);
}

On a side note: block evals, such as eval{ print ‘hi!’; } are very efficient and you can use them liberally. String evals, such as eval(“print ‘hi!’”), are not nearly as efficient and should be avoided.

Class::Measure 0.04 Released

I’ve just uploaded Class::Measure 0.04 to CPAN. Get it here. The changes for this release are optimizations and cleanups in preparation for using Moose in the next release. Class::Measure provides a simple and intuitive way to treat units of measure as basic data types with support for mathematical operations.

use Class::Measure::Length qw( length );
my $width = (length(2, 'feet') + length(3, 'yards')) * 2.8;
print 'Inches: ' . $width->inches() . "\n";

Currently the only supported units of measure are length. Sometime in the next release or two I’ll be adding support for density, frequency, volume, etc.

use Moose;

If you’re used to regular OO perl you’ll recognize this:

package Dog;
use strict;
use warnings;

sub new {
    my ($class, %args) = @_;
    $args{name} ||= 'Spot';
    return bless \%args, shift;
}

sub bark {
    my ($self) = @_;
    print $self->{name} . " barks!\n";
}

1;

With Moose this becomes drastically simpler and more powerful:

package Cat;
use Moose;

has 'name' => (is=>'ro', isa=>'Str', default=>'Fluffy');

sub meow {
    my ($self) = @_;
    print $self->name() . " meows!\n";
}

1;

You may hear that Moose is slow. Don’t let that throw you off, it only adds a fraction of a second to startup time, and is exceptionally fast during run-time. There are much more important things to be concerned about. Moose will make your code cleaner, easier to change and extend, and makes coding perl oo quite enjoyable.

Modern Perl Presentation

I wrote this for the TO.pm meeting on June 10th: