SWIG, Ruby, and libjsw

Introduction

If you like Ruby, you probably enjoy metaprogramming and code generation. If you haven’t used SWIG, you’ll enjoy that special feeling when you write a couple of simple files and suddenly a C library Just Works in Ruby.

I have a sound synthesis engine in C that I use for live performance, and I would like to control it with a gamepad. The freely available Joystick Wrapper Library has worked well for me as a C library, but I’d like to be able to use it from Ruby to easily make changes on the fly while performing.

So I decided to try to generate a SWIG wrapper for it. I know from experience that some libraries are tricky to use to SWIG, but libjsw worked very well and serves as a nice illustration of some of SWIG’s basic features.

Preliminaries

You’ll have to have libjsw, SWIG, and Ruby installed. Note that your SWIG has to be built against the Ruby you wish to use.

The Interface file

The first thing is to write a SWIG interface file, which is language-neutral.

   1  %module jsw
   2  
   3  %{
   4  #include <jsw.h>
   5  %}
   6  
   7  %include cpointer.i
   8  %include "/usr/include/jsw.h"
   9  
  10  %pointer_functions(int,intp);

Line 1 specifies the name of the Ruby (or any other language) module that will be generated. Language-specific conventions will be respected; with Ruby’s capitalization rules this will become module Jsw.

Lines 3-5 will be passed to the compiler as-is when building the library. Here, we pass in the libjsw C header file.

This works fine for the usual call-by-value parameters, but C’s simulation of call by reference with pointers (see below for a note on what I mean by “simulation”) requires some special handling, which is why SWIG provides the cpointer.i library. I include it in line 7 create a pointer function in line 10. This reads “make functions with the name intp to handle pointer to int”. More on this below.

Line 8 is where the magic happens! This one statement says “wrap everything you find in this include file.”

The extconf.rb file

This is the standard Ruby way of creating extensions.

   1  require 'mkmf'
   2  
   3  have_library('jsw')
   4  create_makefile('jsw')

Line 1 required the mkmf library for generating extensions.

Line 3 makes sure to include libjsw library when the extension is compiled.

Line 4 writes out a Makefile.

The build

   1  % swig -ruby jsw.i  
   2  % ruby extconf.rb
   3  % make

Line 1 will generate a file jsw_wrap.c that will be used to build the Ruby extension. You might get warning like

/usr/include/jsw.h:112: Warning(801): Wrong class name (corrected to `Js_axis_struct')

This is telling you that Ruby can’t use the original C struct name for a Ruby class, and is changing it appropriately.

Line 2 generates a Makefile to build the extension out of jsw_wrap.c. A file mkmf.log is also created with some details on what mkmf did to create the Makefile.

Line 3 executes the build. You should see some gcc (or whatever compiler you’re using) lines go by, and you should end up with jsw.so. And that’s it! jsw.so is the extension!

Using the generated library

Now just require the Jsw module to use it:

   1  require 'jsw'
   2  include Jsw

The full source code is available below, but have a look at the C and Ruby versions of this snippet that fetches the current set of joystick attributes:

   1  const char * calib = JSDefaultCalibration;
   2  js_attribute_struct * js_attrib;
   3  int total_js_attribs;
   4  
   5  js_attrib = JSGetAttributesList(&total_js_attribs, calib);
   1  calib = JSDefaultCalibration
   2  js_attrib = Js_attribute_struct.new
   3  total_js_attribs = new_intp
   4  
   5  js_attrib = JSGetAttributesList(total_js_attribs, calib)
   6  
   7  pp intp_value(total_js_attribs)
   8  pp js_attrib

Line 1: The C-style string calib has become a Ruby String containing the string in the constant JSDefaultCalibration.

Line 2: The pointer to C struct js_attribute_struct has become the Ruby object of class Js_attribute_struct, which is instantiated with its new() method. Note that a pointer is used in C so that when it is passed into a function, the function can modify the referenced struct. This works similarly in Ruby because an object reference is passed in, and the function can modify the referenced object. SWIG refers to this as using “opaque objects” in the target language. The fields of the C struct become attributes of the target language object.

Line 3: But look at lines 3 and 5. There’s a problem here, in that in C an integer variable total_js_attribs is used, and then its address is taken as the argument to JSGetAttributesList so that the function can modify its value. It doesn’t make sense to use a Ruby object in this case, because a variable with a single value is wanted. But simply passing in a Ruby variable to the function means that variable cannot be written to; it will be passed by value.

This is where the cpointer.i library that was included in the interface file comes in. Line 3 of the Ruby version creates a special Ruby variable using the new_intp() method. This is passed in to the function, and when read with the intp_value() method, has worked like a C variable passed by reference. I haven’t peeked at the internals but I assume that a Ruby object is being used behind the scenes.

To sum up:

A C struct becomes a Ruby class

C variables passed “by reference” using pointers are handled using functions specified from the cpointer.i library.

The code

Source code is available as a tarball.