In article <40279508.7050100 / knology.net>,
Lyle Johnson  <lyle / knology.net> wrote:
>Phil Tomson wrote:
>
>> I'm finding that accessor methods on a wrapped C++ class end up creating 
>> extra objects on the Ruby side.  Here's an example to illustrate:
>
><snip>
>
>> So after calling the accessor methods 'start_node' and 'end_node' the 
>> number of Point objects is 4 instead of 2.  I think this is being caused 
>> by SWIG_NewPointerObj.  Maybe there's no way around it.  I would have 
>> expected that I would get a reference to an already existing Point object 
>> instead of a new Point object being created.
>
>No, you are correct that every time the accessor (or any other method 
>that returns a pointer to some object) is called, you'll get a new Ruby 
>instance that wraps that C++ object.
>
>> Is there any way to do this so that no new Point objects are created when 
>> calling Edge#end_node, Edge#start_node?  Since my script calls these 
>> methods a lot, lots of extra Point objects get created.
>
>I have a workaround for this in FXRuby, but it is a little complicated. 
>Generally speaking, I maintain a little hash table that maps the C++ 
>pointers to the corresponding Ruby objects. When it's time to return a 
>Ruby object from one of these accessors, I first check that hash table 
>to see if there's already a "live" Ruby instance corresponding to the 
>underlying C++ object and, if so, return that Ruby instance. Otherwise, 
>I call SWIG_NewPointerObj() as usual and store this new association in 
>the hash.
>
>It might be possible to extend SWIG to automate this process, I'm not 
>sure. We'd have to think about the implications for garbage collection 
>as well; for example, if the Ruby object gets garbage-collected, we need 
>to be sure to remove it from the hash table since it's no longer "alive".
>

Lyle,

Since a lot of my outgoing email seems to be bouncing today for whatever 
reason, I'll try posting here...

I hand edited the wrap file so that no new objects are being created.  
Basically, I just added some Ruby instance variables to shadow the C++ 
ones.  Here's the Edge constructor:

static VALUE
_wrap_new_Edge(int argc, VALUE *argv, VALUE self) {
    Point *arg1 = (Point *) 0 ;
    Point *arg2 = (Point *) 0 ;
    float arg3 ;
    float arg4 = (float) 1.0/(80*8000) ;
    Edge *result;
    
    if ((argc < 3) || (argc > 4))
    rb_raise(rb_eArgError, "wrong # of arguments(%d for 3)",argc);
    SWIG_ConvertPtr(argv[0], (void **) &arg1, SWIGTYPE_p_Point, 1);
    SWIG_ConvertPtr(argv[1], (void **) &arg2, SWIGTYPE_p_Point, 1);
    arg3 = (float) NUM2DBL(argv[2]);
    if (argc > 3) {
        arg4 = (float) NUM2DBL(argv[3]);
    }
    result = (Edge *)new Edge(arg1,arg2,arg3,arg4);
    DATA_PTR(self) = result;
    //added the following two lines:
    rb_iv_set(self,"@start_node",argv[0]);
    rb_iv_set(self,"@end_node",argv[1]);
    return self;
}


Here's one of the accessors:

static VALUE
_wrap_Edge_start_node(int argc, VALUE *argv, VALUE self) {
    Edge *arg1 = (Edge *) 0 ;
    Point *result;
    VALUE vresult = Qnil;
    
    if ((argc < 0) || (argc > 0))
    rb_raise(rb_eArgError, "wrong # of arguments(%d for 0)",argc);
    //replace these lines:
    //SWIG_ConvertPtr(self, (void **) &arg1, SWIGTYPE_p_Edge, 1);
    //result = (Point *)((Edge const *)arg1)->start_node();
    //vresult = SWIG_NewPointerObj((void *) result, SWIGTYPE_p_Point,0);
    
    //with this one:
    vresult = rb_iv_get(self,"@start_node");
    return vresult;
}


Works great.

Is there any way to change Swig's behavior so that I can automate this so 
I don't have to go in an hand-edit the _wrap file?

I imagine that I need to change some swg files under the SWIG/Lib/Ruby 
directory, but I'm not sure where to start.  Pointers would be much 
appreciated.

Phil