Ruby-swig for C struct plus methods

We’ve been playing with testing a C-code via Ruby and testunit,
wrapping the C by hand. We’ve just recently tried using SWIG to
automate the wrapping, but so far we haven’t found the right combination
for our situation.

We have C-routines that look a lot like a C++ class, i.e., a struct
definition followed by associated methods where each method takes a
pointer to the associated struct as the first argument.

The trouble is the C SWIG generator creates a class for the struct
and global module methods for the functions, and we’ve been unable
to figure out how to get the methods mixed into the class created by
the struct.

Then again, maybe we’re asking too much from SWIG’s C-interface generator
and we should just embrace the latent object-oriented design of our curent
C code and migrate it to C++?

Note: we’d also like to do some prototyping and possibly high-level
orchestration in Ruby in addition to merely using the test framework.

···


Bil Kleb
NASA Langley Research Center
Hampton, Virginia, USA

i do not have any swig advice, but i have done exactly this by hand :

  • designed simple C ‘classes’ with member functions (f pointers)
  • map this onto a simple ruby class
  • use ruby classes for testing

one thing i thought i’d point out is that i follow this ‘pattern’ for my C
‘classes’

foo.h

···

On Fri, 21 Feb 2003, Bil Kleb wrote:

We’ve been playing with testing a C-code via Ruby and testunit,
wrapping the C by hand. We’ve just recently tried using SWIG to
automate the wrapping, but so far we haven’t found the right combination
for our situation.

We have C-routines that look a lot like a C++ class, i.e., a struct
definition followed by associated methods where each method takes a
pointer to the associated struct as the first argument.

The trouble is the C SWIG generator creates a class for the struct
and global module methods for the functions, and we’ve been unable
to figure out how to get the methods mixed into the class created by
the struct.

Then again, maybe we’re asking too much from SWIG’s C-interface generator
and we should just embrace the latent object-oriented design of our curent
C code and migrate it to C++?

Note: we’d also like to do some prototyping and possibly high-level
orchestration in Ruby in addition to merely using the test framework.


/* this is our class ‘foo’ /
struct foo
{
/
public */

/* methods */
int (*m) (struct foo *self, int x);

/* data */
int x;

  /* private */

/* methods */
int (*_m) (struct foo *self, int x);

/* data */
int _x;
};

/* public class ‘foo’ methods */
int foo_new (struct foo **object, int n, …);
int foo_delete (struct foo **object);

/* public class data */
int foo_x = 42;

foo.c

/* public class ‘foo’ methods */
int
foo_new (struct foo **object, int n, …)
{
struct foo *self = *object;
self = malloc(n);

self->m = m;
self->_m = _m;

self->x = foo_x;
self->_x = _foo_x;
}

int foo_delete (struct foo **object)
{
struct foo *self = *object;

free (self);

self = NULL;
}

/* private class data - note that they are NOT exportable */
static
int _foo_x = 42;

/* instance methods - note that they are NOT exportable */

/* public */
static
int m (struct foo *self, int x)
{

}

/* private */
static
int _m (struct foo *self, int x)
{

}

which exports nothing non-essential into the header file. in particular the
member methods should be static which would at least prevent SWIG from
exporting them! following this pattern the mapping to ruby is so easy i
simply do it by hand - err, guess i should be looking a SWIG too!

i would be very interested to hear your final methodology and experience with
SWIG as i find that developing in C first, then wrapping in ruby to be
infinitely more eloquent and easy to test than developing in C–. :wink:

-a

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

Bil Kleb wrote:

We’ve been playing with testing a C-code via Ruby and testunit,
wrapping the C by hand. We’ve just recently tried using SWIG to
automate the wrapping, but so far we haven’t found the right combination
for our situation.

We have C-routines that look a lot like a C++ class, i.e., a struct
definition followed by associated methods where each method takes a
pointer to the associated struct as the first argument.

OK.

The trouble is the C SWIG generator creates a class for the struct
and global module methods for the functions, and we’ve been unable
to figure out how to get the methods mixed into the class created by
the struct.

There’s no automatic way to get SWIG to recognize that your global C
functions should get mapped to instance methods for the Ruby class, but
the work involved is not too bad. For example, suppose your C code
consists of a struct:

 struct Foo {
     int x, y;
 };

and some function(s) that operate on Foo objects:

 int sum_xy(struct Foo *foo);

You could write a SWIG interface to this code as follows:

 %module bar

 %{
 #include "foo.h" /* header file with C declarations */
 %}

 %ignore sum_xy(struct Foo *foo);

 %include foo.h

 %extend Foo {
     int sum_xy() {
         return sum_xy(self);
     }
 }

Some highlights of the SWIG interface file:

  1. The %ignore directive tells SWIG to not generate
    a wrapper function for the global sum_xy() function
    it’s going to see declared in “foo.h”.

  2. The %extend directive tells SWIG to add a new
    instance method (or methods) to the Ruby class it
    is generating. IMO this is one of the most interesting
    features of SWIG. Within the body of an %extend-ed
    function, the special variable ‘self’ refers to the
    current object, much like the ‘this’ pointer in C++
    code.

If you were to run the above example through SWIG, e.g.

 swig -ruby bar.i

you’d end up with a Bar module that define a Foo class. The Foo class
has an initialize() method, accessors for the x & y members, and a
sum_xy() instance method that takes zero arguments and returns an integer.

For more information about %extend, see section 4.5.6 in the “SWIG
Basics” chapter of the SWIG documentation:

 http://www.swig.org/Doc1.3/SWIG.html

Hope this helps,

Lyle

In article 3E562785.1080608@NASA.Gov,

We’ve been playing with testing a C-code via Ruby and testunit,
wrapping the C by hand. We’ve just recently tried using SWIG to
automate the wrapping, but so far we haven’t found the right combination
for our situation.

I’ve done what you’re doing to unit test C++ classes. Swig worked great
in that case.

We have C-routines that look a lot like a C++ class, i.e., a struct
definition followed by associated methods where each method takes a
pointer to the associated struct as the first argument.

The trouble is the C SWIG generator creates a class for the struct
and global module methods for the functions, and we’ve been unable
to figure out how to get the methods mixed into the class created by
the struct.

Then again, maybe we’re asking too much from SWIG’s C-interface generator
and we should just embrace the latent object-oriented design of our curent
C code and migrate it to C++?

Swig’s a great tool, but you’re probably right: you’re asking a lot from
it in this case. I would suggest moving to C++. From my experience, Swig
will work very nicely for wrapping your C++ classes for unit testing in
Ruby.

Note: we’d also like to do some prototyping and possibly high-level
orchestration in Ruby in addition to merely using the test framework.

I did exactly that on a project last summer. We started coding in C++ and
I wanted to be able to setup a unit testing framework for our classes. I
tried Cppunit but ended up using swig, Ruby and test::unit. Since all of
our classes were available in Ruby, we also started using Ruby for the UI
and to parse input and configuration files - so we used C++ for the stuff
that had to go fast and Ruby for stuff that was a lot faster to develop
in Ruby than it would have been in C++. The mixed language approach
worked quite well.

Phil

···

Bil Kleb William.L.Kleb@NASA.Gov wrote: