http://qs321.pair.com?node_id=792092


in reply to A working strategy to handle AUTOLOAD, can(), and (multiple) inheritance without touching UNIVERSAL?

If I want to have can awareness I would have to overload can.

You can also predeclare the subs you want to AUTOLOAD.

That can be tricky business.

Not really. If your AUTOLOAD has some logic to determine whether it should generate a subroutine, you can factor that logic into a subroutine shared between AUTOLOAD and an overridden can.

You don't even have to go through gyrations in can to return a reference. Perl 5 autovivifies subroutines if you take references to them in the appropriate way. Of course, you can also factor out the code which installs the reference and share it between can and AUTOLOAD.

  • Comment on Re: A working strategy to handle AUTOLOAD, can(), and (multiple) inheritance without touching UNIVERSAL?
  • Select or Download Code

Replies are listed 'Best First'.
Re^2: A working strategy to handle AUTOLOAD, can(), and (multiple) inheritance without touching UNIVERSAL?
by lodin (Hermit) on Aug 29, 2009 at 23:58 UTC
    You can also predeclare the subs you want to AUTOLOAD.

    If I know the names of the subroutines I AUTOLOAD then I don't have a problem. The problem is when you do not know the methods during compilation.

    If your AUTOLOAD has some logic to determine whether it should generate a subroutine, you can factor that logic into a subroutine shared between AUTOLOAD and an overridden can.

    Indeed, that is the problem I'm trying to solve and the solution that I use.

    You don't even have to go through gyrations in can to return a reference. Perl 5 autovivifies subroutines if you take references to them in the appropriate way. Of course, you can also factor out the code which installs the reference and share it between can and AUTOLOAD.

    I don't quite understand this paragraph. What subroutines will be autovivified? Maybe this is just wording, but I'm not installing any references. I get the impression that you think I am trying to lazily load subroutines to e.g. save memory. That is not the problem I try to solve. The major problem is how to provide fallback methods dynamically during runtime that shows up in can. Maybe that should have been clearer. I have updated the root node to make this clearer, I hope.

    lodin

      All credit to lodin for working on a subject that I just happened to be thinking about. I have quickly found that it is quite an old subject however - for example: Why breaking can() is acceptable and Class::AutoloadCAN.

      Also I am actually none the wiser. The order is as follows:

      1. If you stop using AUTOLOAD the Perl::Critic shuts up.
      2. If you must use AUTOLOAD you generally have to overload 'can'. sub can {return 1;} seems to work for me.
      3. Actually the above is wrong since 'can' is supposed to return a reference to the subroutine. But how do I do that when the subroutine is AUTOLOAD?
      4. I see a lot of modules offering solutions but I don't feel inclined to trust them. I think I would rather write code that I understand and depends on modules that look plausible.

      Edit: This seems to work.

      # In class implementing AUTOLOAD for everything. sub can { my $self = shift; my $method = shift; return sub { my $self = shift; .......... return ....; }; } sub AUTOLOAD { my $self = shift; my @method = split /::/, $AUTOLOAD; my $param = pop @method; my $c = $self->can($param); return &$c($self); }

        As the thread here on PM and the two CPAN modules (written by notable Perl people) suggests, this is not as easy as it looks. I think the best way to get it right each time is to find an approach that always acts as "expected", and then put it in a module. I trust a module to be more consistent than me when faced with a repeated problem. :-)

        Regarding your suggestions; it fails to behave like I would expect when it is inherited (and upon object destruction, but to be fair you do not have a constructor).

        { package Foo; sub can { my $self = shift; my $method = shift; return sub { print __PACKAGE__ . ": autoloaded $method\n" }; } sub AUTOLOAD { my $code = $_[0]->can(our($AUTOLOAD) =~ /.*::(.*)/s); goto &$code; } } { package Bar; our @ISA = Foo::; sub new { bless {} => shift } sub bar { print __PACKAGE__ . ": bar\n" } } my $o = Bar::->new; my $m = $o->can('bar'); $o->$m; $o->bar; __END__ Foo: autoloaded bar Bar: bar Foo: autoloaded DESTROY
        In my solution outlined in the root node I handle it by moving the logic that generates the autoloaded subroutine reference to a common routine that both AUTOLOAD and can use. Then I see if UNIVERSAL::can returns anything. If it does it means that AUTOLOAD will not be invoked for $o->bar, so the return value of UNIVERSAL::can should be used instead. (This of course assumes that UNIVERSAL::can is not overloaded to do something else.)

        The remaining logic in the root node is to handle when you use AUTOLOAD in a child class and the parent class also uses AUTOLOAD. This is even rarer, but it is still possible. As you say, it is important to trust a module to handle all edge cases so that you don't end up spending your evening debugging due to the module.

        lodin