> That happens because the method pointer stored in the vtable is a callback
> which calls the XSUB which calls the callback which calls the XSUB which calls
> the callback…

Thanks again for the explanation. I think this is third time you explained this 
to me, but I keep forgetting about it. I’ll add a more detailed explanation to 
the code comment.

> PS: While researching this post and preparing an example using SUPER::DESTROY,
>    I discovered that the implementation for DESTROY is a little wacky right
>    now.  When a Perl subclass implements DESTROY, the method pointer for
>    Destroy in the subclass vtable doesn't get overridden (possibly because
>    "destroy" doesn't match "DESTROY" after
>    https://github.com/apache/lucy-clownfish/commit/64cf00e28a2 ) but stuff
>    still works because under the Perl bindings cfish_dec_refcount calls
>    SvREFCNT_dec if there's a cached host object.

The commit is what made overriding aliases methods from Perl work. But if you 
were testing with the latest code from the master branch, I just removed the 
alias for `Destroy` and added a custom `DESTROY` XSUB to fix the global 
destruction issue:

    https://github.com/apache/lucy-clownfish/commit/4aea9977c3

It’s tricky to make overriding custom XSUBs from Perl work. The custom 
implementation would have to use static dispatch and be implemented for every 
subclass which overrides the method because of the issue mentioned above. This 
isn't really needed for Destroy but it might be useful in other cases (also for 
default interface methods).

The key idea is to reuse the XSUB of a novel method for all overridden methods, 
but without dynamic dispatch. If you have a look at how the ALIAS and INTERFACE 
keywords work in XS, you’ll see that there’s a slot in CV that can be used to 
store additional information for XSUBs. We could use it to store a pointer to 
the subclass:

    // Single XSUB for a method (novel in MyClass)
    XS_INTERNAL(XS_MyClass_method) {
        ...
        // Get (sub)class with CvXSUBANY.
        klass = (Class*)CvXSUBANY(cv).any_ptr;
        // Static dispatch.
        method = CFISH_METHOD_PTR(klass, MyClass_Method);
        ...
    }

    // Register XSUB for novel methods.
    CV *cv = newXS(“MyClass::method", XS_MyClass_method, filename);
    CvXSUBANY(cv).any_ptr = MYCLASS;

    // Reuse XSUB for overridden method.
    CV *cv = newXS(“MySubclass::method", XS_MyClass_method, filename);
    CvXSUBANY(cv).any_ptr = MYSUBCLASS;

    // Or, for methods from a different parcel.
    CV *parent_cv = get_cv(“MyClass::method”, 0);
    CV *cv = newXS(“MySubclass::method", CvXSUB(parent_cv), filename);
    CvXSUBANY(cv).any_ptr = MYSUBCLASS;

Then we’ll only need a way to tell CFC that a there’s a custom XSUB for a 
method (which can also have an alias) and to suppress the autogenerated XSUB. 
Custom XSUBs have to use the same static dispatch mechanism, of course. 

Nick

Reply via email to