New Module: XS::Writer

schwern on 2008-03-22T07:14:00

I have written a Perl module to write XS code which writes C code to wrap up more C code so that I can call it from Perl.

Follow?

Such is XS. I have a project to wrap up a big pile of undocumented, stinking C code in Perl in order to better test it. Because it's a ginormous pile and because the header files have all sorts of circular dependencies and because you need a big hairy autoconf generated pile of switches to compile it, h2xs goes into convulsions trying to deal with it. So I have to write the XS manually.

The basic XS for the basic functions wasn't that hard, after doing some puzzling out with "Extending and Embedding Perl" and the perlxs man page. Mostly it's just a matter of informing XS of the subroutine signature.

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

# Some magic from Devel::PPPort #define NEED_sv_2pv_flags #include "ppport.h"

MODULE = My::Thing PACKAGE = My::Thing

int check_file(char *file_name);

int isoption(char *option, int form);


Yes, you have to tell the routine to determine if something is an option if the option is going to be the short (s) or long (stupid) form. It doesn't just figure that out for itself with a strlen(), probably the rationale being that it would be a huge waste of resources! Wacky C programmers.

Did I mention the code does it's own argument processing? Did I mention that's a huge (and totally inconsequential) part of what it does?

Anyhow, the problem comes when you hit things that take structs. Like this struct to hold options.

typedef struct options_struct_t
{
  char *config_file_name;
  char *input_file_name;
  char *time_str;
  int test_mode;             /* flag set from command line or config value */
  int mail_input;            /* flag set from command line */
  int debug;                 /* flag set from command line */
  int save;                  /* flag set from command line */
  int debug_level;           /* output level for debug */
  ...and so on for about 20 lines...
} options_struct_t;


My first approach was to try and map this to a hash, but that required translating it back and forth from hash to struct on every function call which seemed inelegant. EEP suggested mapping it to an object, but its examples were simplistic. Tom Heady showed me an approach which worked and allowed me to have an accessor for each element of the struct.

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

#define NEED_sv_2pv_flags #include "ppport.h"

#include "option.h"

typedef options_struct_t * My::Option;

MODULE = My::Option PACKAGE = My::Option PREFIX=MY__Option_

My::Option My__Option_new(char* CLASS) CODE: /* my_option initializes an options_struct_t */ RETVAL = my_option(); if( RETVAL == NULL ) { warn( "unable to create new My::Option" ); } OUTPUT: RETVAL

int My__Option_test_mode( My::Option self, ... ) CODE: if( items > 1 ) self->test_mode = SvIV(ST(1)); RETVAL = self->test_mode; OUTPUT: RETVAL


Maybe not the best code, but it gives me a way to construct an object around the struct and access its elements. Trouble is I have to write an accessor for each element of the struct. There's no way to automate it. #define doesn't work in XS to make a macro. I hate cut & code, and there's plenty more structs to wrap.

I looked around for anything on CPAN that might make this easier. Inline::Struct looked promising but I couldn't get it to work and it has no facilities to deal with non-standard types. This code likes to use the Gnome lib types, GList and GHashTable. ExtUtils::XSBuilder looks really powerful and just what I'd need, except it looks just as complicated as XS itself.

So of course I wrote a module to write the code for me. XS::Writer is my first attempt. It will do some elementary parsing of a struct and write the XS code to wrap it up in an object. You can then INCLUDE: that XS file in another and add your own custom functions. It also allows you to write accessors to non-standard types.

And why keep it simple? It uses both Moose and autobox, neither of which I've used in production before! And Module::Build to put it all together, now I can see how it does with XS. Hey, when you're learning new things why not learn a whole pile of them?

You still need to know XS, but at least some of the drudge work can be taken care of. I don't know what else I'm going to put in other than structs, but I'm sure more is coming.

Next up, how to best deal with GList and GHashTable.


How about SWIG?

Bernhard on 2008-03-22T12:30:40

Have you looked at SWIG for achieving that?