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

I'm trying to get 'super' to work in a prototype-based OO system and currently have this ugly mess. I'm wondering if there is a better way.

The system uses hash-based objects which can have CODE ref entries which are run to provide per-object behaviour. It has single inheritence so 'super' is not too complicated. I hope the intent is clear without all the entire class.

The ideal solution would have 'super' know which code ref called it and then find the next in the object_can_all list. Next best would be a syntactic improvement to make the second form below easier. Ideas? Suggestions?

=item super Call the prototype's/parent's version of a function. This is tricky because the when coding the $self->super call intends to target one thing but a subclass of this will provide a different $self that has the original as it's prototype. So super needs to discover when this has happened, seemingly impossible using just caller($n) and $self. More information is needed from 'new' time. That's why there's two interface variants: $self->super(check => @_); This asserts that there are only two "check" routines in the prototype chain for $self and it calls the last one (which assumes that only the first/sub-object uses super). The second variant requires a more complex declaration, permitting more than two subs in a hierarchy. You must pass super a coderef to the surrounding code so that it can find it in the list of potential versions. use WeakRef; my $this_sub; $this_sub = sub { my $self = shift; $self->super($this_sub, check => @_); } $object->{check} = $this_sub; weaken $this_sub; The weaken is important to avoid a circular reference as the closure captures $start_code_ref. This interface division means that objects must use the second form if they want to their sub-objects to be able to override a method using super. Requiring adjustments to the parent is ugly but seems to be the only solution. Devel::Caller provides caller_cx which would help but it is seems flak +y. =cut sub super { if(ref $_[1] ne 'CODE') { # No code ref, super must be unambiguous my ($self, $attr) = (shift, shift); # Find all versions of $attr for this object my @subs = $self->object_can_all($attr); die "Ambiguous super call for $attr on $self" if @subs != 2; # Super must be intended for the second (ie. last) one my $super_sub = pop @subs; return $self->$super_sub(@_); } else { # They gave us a reference to the intended start sub my ($self, $this_sub, $attr) = (shift, shift, shift); # Find where $this_sub is my @subs = $self->object_can_all($attr); my $n = 0; while ($subs[$n] != $this_sub) { $n++ } # Run the next highest one die "No super for $attr on $self" unless exists $subs[$n+1]; my $super_sub = $subs[$n+1]; return $self->$super_sub(@_); } }

Replies are listed 'Best First'.
Re: Sub getting it's own CODE ref? Proto-OO
by hv (Prior) on Jun 15, 2004 at 11:31 UTC

    I'm not sure I fully understand what you're doing here, but it may be possible to simplify the problem by modifying the dispatcher - something like (simplified):

    sub dispatch { my($self, $code) = (shift, shift); push @{ $self->{running} }, $code; my $result = eval { &$code($self, @_) }; pop @{ $self->{running} }; die if $@; return $result; }

    It gets slightly more complex when you properly allow for context using wantarray, but this will allow super() to inspect information in the object itself to determine whence it was called.

    Hugo

      Something like this could work. I wonder if I can avoid maintaining the stack in the majority of calls that don't use super, probably not.

      I also need to work out all the corner cases to be sure that it can't be confused. If an accessor calls another accessor via super, etc.

      Thank you

•Re: Sub getting it's own CODE ref? Proto-OO
by merlyn (Sage) on Jun 15, 2004 at 15:17 UTC
      Not yet, although since each object gets a package C::P's method may not be applicable. I'll check and see.

      Update:
      C::P uses the package from caller to know which object it's being called on and you have to mark the sub as "superable" so that the implementation can be stashed away and used later. Not really what I'm after but does suggest that there isn't a nicer way. You either pay by wrapping everything or somehow mark the subs that need super magic.