in reply to SUPER or NEXT from imported methods

The simplest solution is that instead of exporting the function, use eval to compile a copy in the right package. Now SUPER will know what to do. If you do this then for sanity sake look at the bottom of perlsyn to find out how to create a comment that will make errors in your generated code state clearly where the code was compiled from. Trust me, when you are debugging a crash this will be much, much more useful than "eval 53".

If you don't want to go there, then you'll need to write a function that walks the symbol tree yourself. And you'll need to know inside the function what package it is installed in. This could be done with an import method that looks like:

sub import { my $package = shift; my $methodname = shift; my $dest = caller; my $method = sub { return super($package, "blah", @_; }; my $symbol = $dest . '::' . $methodname; { no strict 'refs'; *$symbol = $method; } }
where super is your function that looks at @ISA, and goes looking to find where ${"$package\::$method"}{CODE} is defined, then calls it.

Honestly this is so much work that I really would advise the eval solution here.

Replies are listed 'Best First'.
Re^2: SUPER or NEXT from imported methods
by jsadusk (Acolyte) on Feb 20, 2009 at 23:24 UTC
    That function that walks the symbol tree is what NEXT is supposed to be. I don't know why it doesn't like this scenario. What exactly do you mean, use eval to compile it into the package? Something like this?
    sub import { my $package = shift; my $methodname = shift; my $dest = caller; eval <<END package $package; sub $methodname { return shift->SUPER::blah(); }; 1; END }
    That seems to work. I was hoping my imported symbol could make use of closure variables, but I guess I could just interpolate them into the eval.
      NEXT doesn't like this because it uses caller to find the calling package, and caller sees the package the function was compiled in, not the one that was called.

      You have the right idea with the eval approach, but I was suggesting something more like this:

      sub import { my $package = shift; my $methodname = shift; my $dest = caller; my $compiled_in = __PACKAGE__; # The initial comment improves error messages. eval qq{# line 1 "Loaded by $compiled_in\::import" package $package; sub $methodname { return shift->SUPER::blah(); } }; die $@ if $@; }
      Also note that your imported symbol can make use of closure variables without interpolating them. If you put \$foo in your code, that will be compiled as $foo, and that will pick up any lexical $foo that is in scope when the eval happens.
        Oh, I didn't realize runtime compiled evals would pick up closure variables, but in retrospect, they'd have to for a lot of things to work. Thanks a lot, I'll use this.