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

Greetings, Monks!

Let us assume I have a class that manufactures methods, like so:

package Maker; sub make { my $pkg = caller; my $sub = sub { my $self = shift; print "w00t\n"; $self->SUPER::go; }; no strict 'refs'; *{ $pkg . '::go' } = $sub; }

And let us further assume I have parent and child classes like this:

package Foo; sub go { print 42; } package Bar; use base 'Foo'; sub new { return bless { }, $_[0] } Maker->make;

And let us even further assume that I have a script like this:

package main; my $o = Bar->new; $o->go;

Which when run, produces:

w00t Can't locate object method "go" via package "Maker" at mm.pl line 11.

So SUPER is looking for the parent class of Maker, not Bar. That makes sense, since Maker is the package in which it was compiled, but I want it to work for the package where it is installed. Preferably, I would like to do this without resorting to a string eval, if it's possible. Any ideas?

Replies are listed 'Best First'.
Re: Getting SUPER to DWIM
by chromatic (Archbishop) on Mar 29, 2006 at 04:32 UTC

    You could use the SUPER module, which has a nicer syntax as well.

      Thanks, chromatic! I think that will do perfectly.
Re: Getting SUPER to DWIM
by merlyn (Sage) on Mar 29, 2006 at 04:47 UTC
    Notwithstanding chromatic's comments, the reason this doesn't work is that SUPER respects __PACKAGE__: the compile-time package setting, not the runtime package. Consider this:
    package Foo; sub Bar::bletch { ... $self->SUPER::bletch(@_); ... }
    That's right... SUPER:: goes to @Foo::ISA, not @Bar::ISA. Oops.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: Getting SUPER to DWIM
by ikegami (Patriarch) on Mar 29, 2006 at 04:49 UTC
    That makes sense, since Maker is the package in which it was compiled

    So compile it in the right package:

    package Maker; sub make { my $pkg = caller; my $sub = eval <<" __EOI__" or die $@; package $pkg; sub { my \$self = shift; print "w00t\\n"; \$self->SUPER::go; } __EOI__ no strict 'refs'; *{ $pkg . '::go' } = $sub; }

    Update: or easier (since only doubled slashes need to be escaped):

    package Maker; sub make { my $pkg = caller; my $sub_body = <<' __EOI__'; sub { my $self = shift; print "w00t\n"; $self->SUPER::go; } __EOI__ my $sub = eval "package $pkg; $sub_body" or die $@; no strict 'refs'; *{ $pkg . '::go' } = $sub; }

    Update: Oops, just noted your comment about eval. The only alternative is to play with the ref($self)'s @ISA, which is what the aformentioned SUPER module does.