Re: goto superclass method
by Ovid (Cardinal) on Dec 22, 2004 at 18:01 UTC
|
Well, I don't know if this is necessarily the best way to go about it, but then, I don't know what problem you're trying to solve. It sounds like a design issue rather than a syntax issue (i.e., are you sure you don't have a design flaw?) In any event, here's a rough way to do it:
sub dostuff {
# you'll want to walk your inheritance tree rather than hardcode thi
+s
foreach my $super (search_isa()) {# <-- you implement :)
if (my $method = UNIVERSAL::can($super, 'dostuff')) {
goto $method;
}
}
}
I feel dirty just telling you this :)
| [reply] [d/l] |
|
use strict;
use warnings;
package AA;
sub dostuff { print(scalar(caller()), $/); }
package BB;
package CC;
BEGIN { our @ISA = 'AA'; }
package DD;
use vars qw(@ISA);
BEGIN { @ISA = qw(BB CC); }
sub dostuff {
foreach my $super (@ISA) {
#if (my $method = $super->can('dostuff')) {
# -or-
if (my $method = UNIVERSAL::can($super, 'dostuff')) {
#print($super, $/);
#print($method, $/);
goto $method;
}
}
}
package main;
#print(\&AA::dostuff, $/);
bless({},'DD')->dostuff(); # Prints "main"
| [reply] [d/l] [select] |
|
search_isa() is simply @ISA, because can searches the whole inheritance tree of the supplied class.
D'oh! I knew that. Silly me :)
| [reply] |
|
Here's an alternative that doesn't use a loop:
You probably know that dostuff exists in a parent, you can simplify it down to:
package MyModule;
use vars qw(@ISA);
BEGIN {
@ISA = ...;
*MyModule::Super::ISA = \@ISA;
}
sub dostuff {
goto(MyModule::Super->can('dostuff')) if (...condition...);
...
}
Don't put any methods (or at least not 'dostuff') in the MyModule::Super package (namespace) for this to work.
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
Well, calling can directly can break if you have something in @ISA that isn't a class (such as a coderef: Class::Dynamic). Out of habit I try to call UNIVERSAL::can unless the $object->can('can');
Perhaps I'm being overly paranoid.
| [reply] |
|
|
|
|
|
Hi Ovid,
Aha - You can feel less dirty as I promise not to actually implement your code for anything important!
Considering your code example cleared up my misunderstanding regarding the $obj->SUPER syntax, and I have cleaned up my code.
Thanks - TIM
--
#Tip: use 'no strict' to make those nasty errors vanish.
| [reply] |
|
I feel dirty just telling you this
And so you should.
This method of searching is flawed, since if perl does the work, it will scan the whole isa tree looking for a declared "dostuff", and then fall back on searching the whole tree for "AUTOLOAD". Your code will find an AUTOLOAD in a package with no declared "dostuff" sometimes when it shouldn't.
This breaks superclasses that are using AUTOLOAD correctly, i.e. with a declaration of each function that may be autoloaded.
| [reply] |
Re: goto superclass method
by steves (Curate) on Dec 22, 2004 at 19:02 UTC
|
Since we're all feeling dirty, you could also do what you
want by temporarily faking the symbol table out so there is,
for an instant, no child class method. Playing with Perl
symbol tables will put hair on your chest, but you may also
go blind.
use strict;
package Super;
sub new
{
return bless {}, shift;
}
sub dostuff
{
print "In Parent dostuff -- caller: ", join('|', caller()), "\n";
}
package Child;
our @ISA = qw(Super);
sub dostuff
{
my $self = @_[0];
print "In Child dostuff -- caller: ", join('|', caller()), "\n";
local *SAVE = *Child::dostuff;
undef *Child::dostuff;
my $method = $self->SUPER::can('dostuff');
*Child::dostuff = *SAVE;
goto(&$method);
}
package main;
my $x = Child->new();
$x->dostuff();
$x->dostuff();
| [reply] [d/l] |
Re: goto superclass method
by broquaint (Abbot) on Dec 23, 2004 at 06:48 UTC
|
You were almost there, just give that goto code dereference a code reference as produced by can e.g
sub dostuff {
goto &{ $_[0]->can('SUPER::dostuff') }
if $condition;
# ...
}
Sorry if this repeats what was said above but all those replies were making me cross-eyed.
| [reply] [d/l] |
|
goto $_[0]->can( 'SUPER::dostuff' );
Makeshifts last the longest. | [reply] [d/l] |
|
Sorry if this repeats what was said above
Yes, and unfortunately, you cut-n-pasted the wrong code. You want can("SUPER::dostuff"), not SUPER::can("dostuff"). {sigh}
| [reply] |
|
Heaven forbid someone makes a mistake. {{{sigh}}}
| [reply] |
Re: goto superclass method
by ikegami (Patriarch) on Dec 22, 2004 at 17:57 UTC
|
Why? The latter sounds like a useless "optimization" to me, especially since you say the first one works. If there is a reason, let us know and maybe we can address the real problem.
And no, there's no easy way that wouldn't require heavy symbol table manipulation. Well, maybe a module, but I find that unlikely.
| [reply] |
|
Actually, it's very easy to do, as shown in my reply below. can returns a code ref and you can goto a code reference. The only tricky part is writing code that pulls in the entire inheritance heirarchy in the correct order so it can be searched. I suspect the original poster may find that less fast than "optimizing" the stack (if that's what was intended.)
| [reply] |
|
| [reply] [d/l] |
|
I never said I thought it was a good idea - I just want to know how it can be done!
TIM
--
#Tip: use 'no strict' to make those nasty errors vanish.
| [reply] |
Re: goto superclass method
by Aristotle (Chancellor) on Dec 22, 2004 at 20:58 UTC
|
Unfortunately, this doesn't work in vanilla Perl:
#!/usr/bin/perl
use strict;
use warnings;
sub Foo::method { return "Foo" }
BEGIN { @Bar::ISA = qw( Foo ); }
sub Bar::method { goto $_[ 0 ]->SUPER::can( 'method' ); }
print Bar->method(), "\n";
This is an infinite loop, because it dispatches to UNIVERSAL::can with $_[ 0 ] as the first parameter. This means that adorning the call with SUPER:: doesn't make a difference here. And since the package for $_[ 0 ] is Bar, can() finds Bar::method, so round and round we go…
But we can easily make this work. I came up with this before reading steves' reply, but it is just a cleaner, more polished version of that trick. Stick this somewhere in the code:
sub SUPER::can {
my $caller = ( caller 1 )[ 3 ];
no strict 'refs';
local *$caller;
return UNIVERSAL::can( @_ );
}
Tiny as it is, maybe this ought to be on CPAN?
See replies.
Makeshifts last the longest. | [reply] [d/l] [select] |
|
package Base;
sub do_me {
my $self = shift;
print "Base do_me\n";
}
package Derived;
@ISA = qw(Base);
sub do_me {
my ($self) = @_; # no shift, so we can goto
print "About to jump...\n";
goto &{$self->can("SUPER::do_me")};
}
package main;
Derived->do_me;
| [reply] [d/l] |
|
SUPER:: is always relative to __PACKAGE__, and when you start saying sub Foo::bar, the __PACKAGE__ doesn't change to Foo, so you have a problem with SUPER.
That's wrong.
#!/usr/bin/perl
use strict;
use warnings;
package Foo;
sub bar { }
package Baz;
our @ISA = qw( Foo );
sub bar {
my $self = shift;
print $self->SUPER::can( "bar" ), "\n";
print $self->can( "SUPER::bar" ), "\n";
};
package main;
print UNIVERSAL::can( Baz => "bar" ), "\n";
Baz->bar;
__END__
CODE(0x815a230)
CODE(0x815a230)
CODE(0x813bc4c)
Interestingly enough, I thought I had tried the $self->can( "SUPER::foo" ) combination, but apparently I didn't. Huh.
Makeshifts last the longest. | [reply] [d/l] |
|
|
|
|
| [reply] |
|
|
package Base;
sub do_me {
my $self = shift;
print "Base do_me\n";
}
package Derived;
@ISA = qw(Base);
package main;
Derived->do_me;
It doesn't use goto but the result is the same as far as I can tell.
Update: Okay, ignore me. I got distracted from the original question of how to conditionally jump to a super class. | [reply] [d/l] |