Lucifers,

This is just a braindump on how the callback mechanism could be changed to be interface-based without the need to fully support interfaces in Clownfish. With regard to the Perl bindings, this would also allow to use arbitrary Perl classes that don't inherit from Clownfish::Obj. So there's no need for inside-out instance variables which would be a big plus.

I take the Analyzer class as an example. Currently, custom analyzers are implemented by subclassing and overriding the `Transform` method. With an interface-based approach, we'd introduce a purely abstract class `Transformer`

    public abstract class Transformer {
        public abstract incremented Inversion*
        Transform(Transformer *self, Inversion *inv);
    }

and add a transformer variable to Analyzer:

    public class Analyzer {
        Transformer *transformer;

        public inert Analyzer*
        init(Analyzer *self, Transformer *transformer);
    }

So my approach would be to add a modifier to the Transformer class

    public abstract *create_host_interface* class Transformer

which would autogenerate a class equivalent to

    public class HostTransformer extends Transformer {
        void *host_interface_obj; // SV* for Perl
    }

and set a new property `host_interface` in the `TRANSFORMER` class singleton pointing to `HOSTRANSFORMER`.

When Analyzer_init is called from Perl, the following piece of code in XSBind_maybe_sv_to_cfish_obj would handle the conversion to a HostTransformer:

    Obj*
    XSBind_maybe_sv_to_cfish_obj(SV *sv, Class *klass) {
        Obj *retval;

        // ...

        if (klass->host_interface
            && sv_isobject(sv)
            && !sv_derived_from(...)
        ) {
            HV *stash = SvSTASH(SvRV(sv));
            StackString *class_name
                = SSTR_WRAP_UTF8(HvNAME(stash), NvNAMELEN(stash));
            Class *singleton
                = Class_singleton((String*)class_name,
                                  klass->host_interface);
            retval = Class_Make_Obj(singleton);
            retval->host_interface_obj = sv;
            SvREFCNT_inc(sv);
            return retval;
        }

        // ...
    }

Additionally, the following functions would be autogenerated:

    Inversion*
    HostTransformer_Transform_OVERRIDE(HostTransformer *self,
                                       Inversion *inv) {
        // Autogenerated callback using self->host_interface_obj
    }

    void
    HostTransformer_Destroy_IMP(HostTransformer *self) {
        SvREFCNT_dec((SV*)self->host_interface_obj);
        SUPER_DESTROY(...);
    }

This means that every time a callback object is passed from Perl to Clownfish, a new HostTransformer object is created, i.e. the HostTransformer is not cached. But I don't think this would cause a performance problem.

Another benefit is that the number of generated OVERRIDE functions would be reduced drastically. Also, the Perl constructor bindings could be simplified because they wouldn't have to check for Perl subclasses anymore.

I'm probably missing some things but I wanted share my initial thoughts with the list.

Nick

Reply via email to