From: Lyle Johnson Date: 2003-05-07T01:07:07+09:00 Subject: Re: SWIG and Ruby (Was Re: SWIG on Solaris problem) Jim Freeze wrote: > Ok, I have been reading the swig docs and I have > a few questions. Other than the code above looking > totally greek to me, the swig manual does its > best to convince me that I never want to write > a typemap. So, why do we have to write a typemap > for something so common as char **? Is it because > I said that I wanted to call dbInit as: > > dbInit(n, "some string") > > instead of > > dbInit(n, ["some string"])? We should probably add a standard typemap for converting a Ruby Array of Strings to a char ** (like the one I showed). Your case is a little unusual in that the first argument is a pointer-to-int, instead of just an int, but that's not too much of a problem. But after taking another look at what I sent you, it doesn't do quite what you asked for. (It also had a few bugs). For Ruby, it's sort-of redundant to pass in both the length of the Array and the Array itself, unless you wanted to specifically limit the number of elements used in the array (e.g. "Here's an array with 100 elements, but only do something with the first 15 of them"). The typemap I was shooting for just expects a single argument (an Array). I've further modified it so that it accepts either a single String, or an Array of Strings: %typemap(in) (int *pnArgs, String *pString) (int count, int i, VALUE tmp) { if (TYPE($input) == T_STRING) { count = 1; $1 = &count; $2 = (String *) malloc(sizeof(String)); $2[0] = StringValuePtr($input); } else if (TYPE($input) == T_ARRAY) { count = RARRAY($input)->len; $1 = &count; $2 = (String *) malloc(count*sizeof(String)); for (i = 0; i < count; i++) { tmp = rb_ary_entry($input, i); $2[i] = StringValuePtr(tmp); } } } %typemap(freearg) String *pString { free((void *) $1); } > Second question, do I need to learn how to write typemaps? For some fairly simple interfaces, you may not need to know how to write typemaps. But almost anything involving pointers usually requires at least some knowledge of how typemaps work. This also entails knowing something about Ruby's C API (e.g. the code above that deals with grabbing elements out of a Ruby Array). Note that even if you weren't using SWIG, the fundamental problems don't go away. If you want wrapper code to interface between Ruby and C/C++, someone's got to write it ;) For me, the big advantage of using SWIG is that for large interfaces, most of the tedious parts of generating that wrapper code are taken care of. As I said in my presentation at RubyConf, though, for small interfaces SWIG may be overkill and you may be better off just writing the wrapper code yourself, by hand. I will certainly not try to convince anyone that there isn't a learning curve associated with using SWIG ;) > Third question, what is the $1 and $2 in the above typemap. The first part of the typemap declaration: %typemap(in) (int *pnArgs, String *pString) { ... } shows what kinds of argument patterns (in function declarations) that SWIG is going to try to match for this typemap. In other words, if it sees *any* functions that have the arguments "int *pnArgs" and "String *pString", with those names, in that order, it's a match. So each of these functions would use that typemap: void dbInit(int *pnArgs, String *pString); void foo(float x, int *pnArgs, String *pString, long y, double z); but these wouldn't match: void goo(int *pnArgs); void boo(String *pString); void loo(String *pString, int *pnArgs); void bar(int *pnArgs, float y, String *pString); Inside the typemap code, then, $1 refers to the first value matched (the int * value) and $2 refers to the second one (the String * value). I think this is covered in the typemaps section of the SWIG/Ruby docs: http://www.swig.org/Doc1.3/Ruby.html as well as the (more comprehensive) chapter on SWIG typemaps: http://www.swig.org/Doc1.3/Typemaps.html Hope this helps, Lyle