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