Leviathan has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

I've written perl bindings for etk, (cpan). I'm currently writing the test suit and came across some inconsistencies:

Etk's objects mostly all extend the Etk::Widget object and the perl objects are simply blessed hash references that have an IV that points to the C object.

Most of the C functions that take objects take Etk_Widget and those that return them will also return an Etk_Widget, which the programmer has to cast to the appropriate type. Here's an example:

Etk_Bin * bin; Etk_Button * button; // assume the above initialized. etk_bin_child_set(bin, ETK_WIDGET(button); // and the get the button. button = ETK_BUTTON(etk_bin_child_get(bin));

Now in the perl version:

$bin->ChildSet($button); $button = $bin->ChildGet();

The problem is, passing $button to ChildSet works because of the inheritance, and $button is a widget, so it's cast correctly in the XS code, but ChildGet will return an Etk_Widget, and through the typemap the returned object will have Etk::Widget as a class, and therefore the programmer has to bless the object to the appropriate type, but that's not very perly.

I'm wondering on how best to keep track of what the objects were to return them. Here are the few options I can think of:

Any pointers or comments are greatly appreciated.

--
Leviathan.

Replies are listed 'Best First'.
Re: Keeping track of classes
by tye (Sage) on Sep 03, 2006 at 16:53 UTC

    I think that you will be happier if you implement this mapping in Perl rather than in XS.

    Writing less of your code in XS usually means fewer bugs and limitations, easier maintenance and extending, and easier to write.

    - tye        

Re: Keeping track of classes
by traveler (Parson) on Sep 03, 2006 at 17:44 UTC
    You might see how they do it at Gtk2-perl. It has a lot of similar issues and I know a lot of work was done on the design of the perl/xs portion. You could also ask on that list, the link to which is on that page.

    --traveler

Re: Keeping track of classes
by creamygoodness (Curate) on Sep 05, 2006 at 02:45 UTC

    If you want to spare the user the trouble of reblessing, do it yourself: make the class the return value should be blessed into an optional second argument to ChildGet().

    sub ChildGet { my ( $self, $class ) = @_; $class = "Etk::Widget" unless defined $class; my $child = $self->{_CHILD} ? $self->{_CHILD} : child_get($self); return bless $child, $class; }

    That's functionally equivalent to the C requirement that the user perform the casting. Since the information about what kind of Widget you've got can't be determined from the return value of the C function, if you want to make this DWIMmy Perl and not require that second argument, you're either going to have to add metadata to your C object or go with the global hash. You make the call. :)

    You can also do this in XS, if you want.

    void ChildGet(bin, ...) Etk_Bin * bin; PREINIT: char * class = "Etk::Widget"; Etk_Button * button; PPCODE: if (items > 1) class = SvPV_nolen( ST(1) ); button = TK_BUTTON(etk_bin_child_get(bin)); ST(0) = sv_newmortal(); sv_setref_pv(ST(0), class, (void*)button); XSRETURN(1);

    I'm not sure that's quite right, because after spelunking your typemap and your .xs file, I still don't grok how you're getting hash objects -- but you get the general idea.

    The fact that I'm a half-decent XS hacker and I can't figure out how to do this says something about its maintainability. :)

    --
    Marvin Humphrey
    Rectangular Research ― http://www.rectangular.com

      Thank you for your reply.

      I did some further digging around and found that the C objects keep track of the hierarchy, so that's how I solved that problem:

      I changed Etk_Widget in the typemap to call a function that takes the object, check the type through the C library and get a string representation of the type, I then use that to get the appropriate perl class and create the object like this.

      Latest cvs code is here. Object creation is now more simplified, so it should be obvious how it is being done.

      --
      Leviathan