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

Reply via email to