From: ptkwt@... (Phil Tomson) Date: 2004-02-11T16:10:01+09:00 Subject: Re: Swig: Extra objects being created In article <40279508.7050100@knology.net>, Lyle Johnson 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: > > > >> 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