note
lodin
<p><blockquote>Don't rely on someone else's module. The area is too complicated.</blockquote>
Interesting. This is exactly why I think a module is needed. It is too complicated to be reimplemented and at least my first version was flawed or incomplete. I'd be more inclined to trust a module if the documentation and test suite give the impression of the module being robust.</p>
<blockquote>This means you have to craft your own solution, but it is fairly simple.</blockquote>
That sounds a bit contradictory. :-)
<blockquote>Define a DESTROY method as bad stuff happens otherwise.</blockquote>
How about <c>return if $method eq 'DESTROY';</c> in <c>AUTOLOAD</c>?
<blockquote>Define a semantically correct "can" function that either returns undef or a CODE ref.</blockquote>
Would that include looking at <c>UNIVERSAL::can</c> to find "real" methods?
<blockquote>Define the AUTOLOAD function in the fairly standard way based upon the "can" function.</blockquote>
Someone might decide to overload <c>can</c> and use <c>caller</c> arbitrarily, which may break (since there is no <c>uplevel</c> function in Perl). One might do this to remove some methods in <c>can</c> from the public (they can of course still be called). Good idea or not; I'm not to judge. I think <c>caller</c> was the reason I moved the logic out from <c>can</c> and into a common routine shared between <c>can</c> and <c>AUTOLOAD</c>. That way if anyone overloads <c>can</c> they do not have to think about my <c>AUTOLOAD</c> implementation since <c>AUTOLOAD</c> does not call <c>can</c> and in <c>can</c> I can use <c>goto &$next</c> to make it fully transparent.
<blockquote>I avoid multiple inheritence so I would make autoloading in a multiple inheritence scenario an absolute no.</blockquote>
It's not harder to use <c>next::method</c>/<c>next::can</c> and <c>mro::get_linear_isa</c>/<c>Algorithm::C3::merge</c> than it is to use <c>SUPER::foo</c>/<c>can('SUPER::foo')</c> and <c>Class::ISA::super_path</c> (mro/Algorithm::C3/Class::ISA is only needed (?) when overloading <c>AUTOLOAD</c>). <c>mro.pm</c> is core since Perl 5.9.5.</p>
<p>If you wanted to allow subroutine stubs in your classes (including any subclasses) you'd need to take some special care, I believe. This may be to break your first rule above, but still. The person who subclasses your class might not agree so if it can be solved I favor solving it. The problem is that <c>UNIVERSAL::can</c> returns a reference, but the subroutine it references is undefined, so <c>AUTOLOAD</c> will be invoked. So to avoid fatal recursion <c>can</c> may not return a reference to an undefined subroutine (i.e. stub). I think you may run into trouble with <c>$self->SUPER::foo</c> and/or <c>$self->can('SUPER::foo')</c> as well. (<c>$self->SUPER::foo</c> invokes <c>AUTOLOAD</c> only if it's a stub.)</p>
<p>The reciprocal perspective is that you want to overload a method and use <c>SUPER</c> in a subclass to a class (that perhaps someone else wrote) that uses stubs to lazily provide methods. If <c>AUTOLOAD</c> would query <c>can</c> then you'd end up with a fatal recursion.</p>
<p>Coincidentally, the design of <c>Class::AUTOCAN</c> implicitly handles stubs seamlessly. :-) If you want to lazily provide a method but still want to give it the precedence of a real method then you just do
<code>
{
package Foo;
sub foo;
sub bar { __PACKAGE__ };
# Handles &foo, and fallback for everything.
AUTOLOAD { __PACKAGE__ }
## or, if it should can() everything:
#use Class::AUTOCAN sub {
# return sub { __PACKAGE__ };
#};
}
{
package Foo::Bar;
our @ISA = Foo::;
sub bar; # Lazily overload bar.
# Handle &bar and provide fallback for &foo and &BAR.
# &foo should not be called here since &foo is declared
# and should be handled by Foo::AUTOLOAD. If &foo should
# be handled here then a stub should have been declared
# here.
use Class::AUTOCAN sub {
my $self = shift;
my ($method) = @_;
return sub { __PACKAGE__ } if $method eq 'foo'; # Beware: stub.
return sub { __PACKAGE__ } if $method eq 'bar';
return sub { __PACKAGE__ } if $method eq 'BAR';
return; # Let Foo worry about other methods.
};
}
my $o = Foo::Bar::;
for my $method (qw/ foo bar BAR abc /) {
my $code = $o->can($method);
printf "%s => %-8s (can: %-8s)\n",
$method,
$o->$method,
($code ? $o->$code : ''),
;
}
__END__
foo => Foo (can: Foo )
bar => Foo::Bar (can: Foo::Bar)
BAR => Foo::Bar (can: Foo::Bar)
abc => Foo (can: )
</code>
Maybe I should upload <c>Class::AUTOCAN</c> somewhere so that you can try it out and see if you find any bugs in it. I would appreciate any fire your or anyone else could put it under. :-) If it's not bullet proof then it's not worth having on CPAN.</p>
<p><i>lodin</i></p>
<p><b>Update</b>: improved the example.</p>
792087
792547