On Fri, Apr 30, 2004 at 05:44:25PM -0700 Bill Moseley wrote: > I'm wondering how best to carry along a reference to a parent object so > I can decrement its refcount as child objects are destroyed. And I'm > wondering if I can use a typemap to help keep my XS methods simple. > > > I have a C structure that I return as a Perl object. Part of that > struct contains a list of pointers to other structures. I want to > provide methods on those individual list items. So, ... > > In my perl code I want to do this: > > my $parent_object = Foo->new; # create the Foo object. > > # Return a list of Foo::List objects. > my @list = $parent_object->return_list; > > for my $item ( @list ) { > print $item->name, $item->whatever; > } > > Now, the problem is that the Foo::List objects are just a reference to > to the real items in the list located in the parent object. So I don't > want the parent object destroyed before ALL of the Foo::List objects are > destroyed. > > So, I want to bump the ref count on $parent_object for each > item in the list. But, then I need to be able to get back to the parent > object when destroying each item. > > All I can think of is to return a list of arrays where one of the > elements points back the the SV of the parent so I can do this in my > destroy method: > > SvREFCNT_dec( parent ); > > I would think this a common problem, so I'm wondering how it's, well, > commonly solved.
FWIW, I also have this situation in one of my XS modules. So it's probably not totally uncommon. > My current methods are very simple and map directly to the C API of my > library. That is, I can do simple things like: > > > const char * > name( item ) > ITEM item > > And let the typemap do all the work. > > Is there a way to do a typemap that will pull out my pointer from the > complex data so my individual methods can remain simple? Storing a pointer to the parent object (which would have to be the Perl scalar and not the underlying C struct) is probably the point where you will have to leave the typemap trail locally. You might need both the Perl scalar and the C structure referencing it and therefore do the conversion manually. In my module I have: void emails (object) SV *object; PREINIT: DBX *self; int i; PP_CODE: self = (DBX*)SvIV((SV*)SvRV(object)); if (self->type != DBX_TYPE_EMAIL || self->indexCount == 0) XSRETURN_EMPTY; for (i = 0; i < self->indexCount; i++) { SV *o = sv_newmortal(); void *item = dbx_get(self, i, 0); DBX_EMAIL *ret = (DBX_EMAIL*) safemalloc(sizeof(DBX_EMAIL)); ret->dbx = object; ret->email = (DBXEMAIL*) item; ret->header = NULL; ret->body = NULL; SvREFCNT_inc(object); o = sv_setref_pv(o, "Mail::Transport::Dbx::Email", (void*)ret); XPUSHs(o); } XSRETURN(i); So it's one of the few methods where the input typemap is not used, otherwise the signature would read void emails (self) DBX *self; As I need the C structure nonetheless, I convert it by hand: self = (DBX*)SvIV((SV*)SvRV(object)); and store a pointer to 'object' in the child object, additionally incrementing the ref-count of the parent scalar: ret->dbx = object; ... SvREFCNT_inc(object); So, yes, it is more work than just using the typemaps, but at least it is locally restricted to the particular method. Another thing that you could possibly do is adding a field to the C structure that contains a pointer to the Perl scalar that is used to bless the reference to this structure. Then you can use the typemap and retrieve the Perl object from the C struct: void method (self) C_STRUCT *self; CODE: { C_STRUCT_2 *child; SV *obj = self->objptr; /* now put a pointer to 'obj' into the child structure * and increment obj's ref-count */ ... } Tassilo -- $_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({ pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus})!JAPH!qq(rehtona{tsuJbus#; $_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval