On Fri, Jul 26, 2013 at 3:39 PM, Nick Wellnhofer <[email protected]> wrote:
> On Jul 24, 2013, at 12:30 , Nick Wellnhofer <[email protected]> wrote:
>
>> On Jul 24, 2013, at 01:09 , Marvin Humphrey <[email protected]> wrote:
>>
>>> A more robust approach might be to default to generating bindings whenever
>>> possible for every class and every method in a parcel.  (C types which 
>>> cannot
>>> be mapped automatically, such as `int*`, prevent some methods from being 
>>> bound
>>> automatically.)  This will cost us in terms of compiled object size and 
>>> build
>>> time, but it's conceptually simpler and will cut down on weird edge cases 
>>> like
>>> the one we have to disentangle now.
>>
>> I think that's the best solution. We've been generating bindings for most 
>> methods anyway.
>
> This is now implemented in branch 'perl-method-bindings'.

Cool!  I'm looking forward to deleting a bunch of code from
perl/buildlib/Lucy/Build/Binding/*.pm. :)  It would be great if stuff like
this was no longer necessary, because all classes were fully bound by default:

    sub bind_andmatcher {
        my $binding = Clownfish::CFC::Binding::Perl::Class->new(
            parcel     => "Lucy",
            class_name => "Lucy::Search::ANDMatcher",
        );
        Clownfish::CFC::Binding::Perl::Class->register($binding);
    }

We're close, but not quite there.  When I make this change to
perl/buildlib/Lucy/Build/Binding/Search.pm, we lose the autogenerated
constructor binding `XS_Lucy_Search_ANDMatcher_new` from Lucy.xs.

    --- a/perl/buildlib/Lucy/Build/Binding/Search.pm
    +++ b/perl/buildlib/Lucy/Build/Binding/Search.pm
    @@ -21,7 +21,7 @@ $VERSION = eval $VERSION;

     sub bind_all {
         my $class = shift;
    -    $class->bind_andmatcher;
    +    #$class->bind_andmatcher;
         $class->bind_andquery;

> Looking at the code in CFCPerlClass_method_bindings, another question turned
> up. Currently, the Perl bindings generate XSubs for every fresh method to
> support SUPER:: invocations from Perl. But I can't see a legitimate use case
> where this would be needed.

Perhaps there's a misleading comment in the source code, but that's not why we
need to avoid XSUBs like this:

    void
    serialize(self, outstream)
        lucy_Query *self;
        lucy_OutStream *outstream;
    PPCODE:
        Lucy_Query_Serialize(self, outstream); // <--- vtable dispatch

> In my opinion, it only makes sense to invoke a
> method of a superclass on the 'self' object from a subclass. In this case,
> it would be enough to have a single XSub per novel method which uses dynamic
> dispatch.

Consider a `MyQuery` subclass of Query which adds a member variable "num" that
must be serialized.

    package MyQuery;
    use base qw( Lucy::Search::Query );

    sub serialize {
        my ($self, $outstream) = @_;
        $self->SUPER::serialize($outstream);
        $outstream->write_c32($self->get_num);
    }

When Clownfish creates the VTable for MyQuery, it will detect that MyQuery has
implemented Serialize() in Perl-space and will install a callback stub
`Lucy_Query_Serialize_OVERRIDE` at `Lucy_Query_Serialize_OFFSET` in the
newly created vtable for MyQuery.

If the XSUB binding for Serialize() were implemented as above, we will wind up
in an infinite loop when we invoke `$my_query->serialize($outstream)`:

    1.  Enter Perl subroutine `MyQuery::serialize`.
    2.  Enter XSUB `Lucy::Search::Query::serialize`.
    3.  Enter callback stub `Lucy_Query_Serialize_OVERRIDE`
    4.  Goto 1.

To stop the infinite loop, we need to implement the XSUB for
`Lucy::Search::Query::serialize` so that it is fixed to always use the
implementation from Query rather than the implementation extracted from self's
VTable.

    void
    serialize(self, outstream)
        lucy_Query *self;
        lucy_OutStream *outstream;
    PPCODE:
        Lucy_Query_Serialize_t method
            = (Lucy_Query_Serialize_t)CFISH_METHOD_PTR(LUCY_QUERY,
                                                       Lucy_Query_Serialize);
        method(self, outstream);

In order for SUPER invocations to work properly for all methods, we need to
generate one XSUB for each "fresh" method implementation.  That's a lot of XS
code, but I don't see a way around it.

Does that make sense?

Marvin Humphrey

Reply via email to