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

When writing an AUTLOAD function it commonly looks like
sub AUOTLOAD { my $fn = something_clever_returning_fn_reference; goto &$fn; }
this "magic" goto makes it look like the AUTOLOAD never happened by overwriting the call stack. My question is how to do this for methods. You could do
sub AUOTLOAD { my $self = shift; my $m = something_clever_returning_method_name(); $self->$m(@_); }
but the call stack would still show the AUTOLOAD So, I thought of this
sub AUTOLOAD { my $self = $_[0]; my $m = something_clever_returning_method_name(); my $sub = $_[0]->can($m) || $_[0]->can("AUTOLOAD"); die "Can't call $m on $_[0]" unless defined($sub); goto &$sub; }
I don't see why this would be wrong. Are there any drawbacks to this method? I've never seen it used before. Why do all the tutorials show how to use the magic goto &$sub for functional programming but never for OO?

Replies are listed 'Best First'.
Re: goto and AUTOLOADed methods
by broquaint (Abbot) on Aug 01, 2003 at 11:14 UTC
    That should be fine, although the assignment of $sub looks broken as the calling object doesn't have the $m method defined (unless the $m method is created in the AUTOLOAD call), otherwise AUTOLOAD wouldn't have been called. Also unless you're doing something really clever with $m that will loop forever as the calling object has AUTOLOAD defined, and without the $m method defined $sub will always end up pointing to the AUTOLOAD method and calling itself in the goto. Here's my take on AUTOLOADing methods
    sub AUTOLOAD { no strict; (my $m = $AUTOLOAD) =~ s{.*::}(); *$m = make_method_here() if $m =~ /fulfils some condition/; croak "AUTOLOAD: can't access method $m"; unless defined *$m{CODE} and $_[0]->can($m); goto &$m; }

    But as far as calling goto with a method name having left @_ intact, that nicely emulates a method call. The drawbacks would be that it'll be slow, and it's always a bit contentious dynamically creating/accessing methods via AUTOLOAD when quite often the likes of Class::Accessor will suffice (but as always, it's somewhat of a rule-of-thumb decision).
    HTH

    _________
    broquaint

      Good point about the infinte loop thingy, although for my application it's not relevant. It's a pity you can't do
      $self->can("SUPER::method_name");

      I suppose I should explain my idea. I'm not doing autloaded accessors or anything like that. Here's a very rough idea of what I'm doing

      package UnloadedObject; use Class::MethodMaker ( new_hash_init => 'new', get_set => [qw( Table Class ID )] ) sub AUTOLOAD { my $method = $AUTOLOAD; $method =~ s/.*:://; my $table = $self->Table; my $id = $self->ID; my $class = $self->Class; my $self = $_[0]; my $sub = $class->can($method) || $class->can("AUTOLOAD"); die "Can't call $method on $class" unless defined($sub); bless $self, $class; %$self = (); $table->fillFromID($self, $id); goto &$sub; }
      So you pull some object out of your database and it has links to other objects but you obviously don't want to pull those objects out because that would pull out more sub objects and pretty soon, your whole database is in memory. So instead you just leave a "fake" object wherever there would be a sub object and you end up with something like
      my $me = $people->getByName("fergal"); my $name = $me->Name; # nothing special my $boss = $me->Boss; # still nothing special my $boss_name = $boss->Name; # bang! magic things happen and suddenly +$boss has all it's data loaded
      It's actually a little more complicated than that but that's the basic idea. I don't have to worry about AUTOLOAD loops because I'm not looking in the same class.

      UpdatedI changed $self to $class in the ->can sutff

        I guess the real point of most of the responses to your original question is that there isn't really that much difference between OO methods and non-OO subs. The "method" syntax is just that -- a bit of syntactic sugar to help people think objectly (object-oriently?)

        If you want to use goto in your AUTOLOAD and have your method think the method was called by the caller of AUTOLOAD, just do it and it will DWYM.

      In general, I think it is good practice for AUTOLOAD to instantiate the sub (or method), then goto() it, so that next time it gets called directly instead of going through AUTOLOAD.

      If the instantiation is 'clean', there should be no difference between the first call and subsequent ones.

      That should offer the "least suprise" to the caller.

Re: goto and AUTOLOADed methods
by ctilmes (Vicar) on Aug 01, 2003 at 11:07 UTC
    Just combine your first two examples and use the goto:
    sub AUTOLOAD { my $self = $_[0]; my $method = $self->something_clever_returning_method_ref(); # or my $method = something_clever_returning_method_ref($self); # or whatever goto &$method; }

    Update: Change "shift" to $_[0].

    Also add recommendation here: something_clever_returning_method_ref() should instantiate the method as a sub so that next time it gets called directly.

      That breaks because you've shifted $self off the stack so it's no longer the first argument when you goto $method.

      The question is really about, how to avoid leaving an entry for the AUTOLOAD subroutine on the call stack. There's a well established way of doing it when you're AUTOLOADing functions but I've never seen an example of AUTOLOADing methods used the magic stack fixing goto. Is this because there's some subtle problem I've missed or is it just because nobody has bothered?

        Ok, I fixed that. Just grab the first argument as $_[0], do whatever you want with it to figure out what method you need to call, require other modules, build a new sub, etc., then just use goto to call the method you want with the arguments (including the object) intact. The goto will mimic a method call just like you want it to.

      First argument should be object reference when you call object method:

      sub AUTOLOAD { my $self = shift; my $method = $self->something_clever_returning_method_ref(); # or my $method = something_clever_returning_method_ref($self); # or whatever goto &$method($self); }

      _ _ _ _ _ _
        M i c h a e l

        But you haven't passed any of the other arguments now.

        The whole point of the magic goto &$sub is that you don't pass any arguments at all, the subroutine gets the current @_. It makes it look like the AUTOLOAD never happened, even if the method uses the caller() function to look at who called it, it won't see the AUTOLOAD routine, it will see the code that called the AUTOLOADer.

      I'm not doing what you think I'm doing with AUTOLOAD, so instantiating sub routines doesn't come into it. Have a look below to see some more detail on why I'm AUTOLOADing.
Re: goto and AUTOLOADed methods
by dragonchild (Archbishop) on Aug 01, 2003 at 13:35 UTC
    I'd like to make a quiet protest on behalf of all the programmers who come into a project 4 years into it and who have to deal with the abysmal dreck that is AUTOLOAD. I have never once, and I mean never, have come across an AUTOLOAD whose existence was justified. Ever. Most of the time, it's an attempt to circumvent proper encapsulation and is a perfect example of false Laziness. Something like:
    sub AUTOLOAD { my ($self, $method_name) = @_; $self->{$method_name} = $_[0] @_; return $self->{$method_name}; }
    Why is this bad? Oh, let me count the ways!
    1. No checking, either compile or runtime, for typos.
    2. You can create members on the fly.
    3. $self->FooBar and $self->foobar are different.
    4. There is a much cleaner WTDI, using a base class that will receive a list of attributes to use and auto-generate your getters/setters/mutators for you. This option even has the benefit of being self-documenting and provides runtime checking for typos.
    Would someone please give me a place, in production code, where AUTOLOAD shines over proper decomposition?

    ------
    We are the carpenters and bricklayers of the Information Age.

    The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      I frequently use it for "plugins" where I have a directory full of files with subroutines, and AUTOLOAD just runs 'require' to pull it in, but the subroutines in the files look normal, and everything is easy to use/debug.
        Couldn't you be better served by making those files into modules/objects and choosing which one(s) you want at the beginning, making it clearer to your maintainer(s) (which often is you) exactly what's going on?

        ------
        We are the carpenters and bricklayers of the Information Age.

        The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      I agree, autoloaded accessor are probably a bad idea but that's not what I was doing. Did you read my comment on what I was actually doing with AUTOLOAD?

      Another use I had was an object proxy. Basically you connect to an object server, get an ID from that server, it gets wrapped in an object on the client side which looks like

      package ProxyObject; sub AUTOLOAD { my $self = shift; $method = $AUTOLOAD; $method =~ s/.*:://; return $self->{HomeServer}->call_method($method, $self->{ID}, \@_); }
      the HomeServer object sends the method, the id and the args back to another server which calls the method on the correct object, passing in the args and then passes the result back across the network.

      In use it looked like

      my $server = Server->connect( host => auth.domain.com user => $user, pass => $pass ); my $root = $server->getRoot; # this is a proxied object my $users = $root->getUsers; # another proxied object my $fergal = $users->getUser("fergal"); $fergal->setPasswd("wibble"); $fergal->setShell("/bin/zoidberg"); $fergal->commit;

      Your Perl code neither knows nor cares that some of the objects it's playing with doesn't actually exist on this machine.

        Why wouldn't you use something like SOAP::Lite here? (Please note that I'm a SOAP novice, but it seems like it would do the trick here ...)

        ------
        We are the carpenters and bricklayers of the Information Age.

        The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: goto and AUTOLOADed methods
by ctilmes (Vicar) on Aug 01, 2003 at 11:16 UTC
    BTW, if you alias $_[0] with something nice like $self, you might as well use it:
    sub AUTOLOAD { my $self = $_[0]; my $m = something_clever_returning_method_name(); my $sub = $self->can($m) || $self->can("AUTOLOAD"); die "Can't call $m on $self" unless defined($sub); goto &$sub; }
    Though your die stringify's the object, which might look weird to some.
      Except that you're not aliasing anything ;) You're making a copy of $_[0]. If you modify $self, the variable aliased by $_[0] will not be modified.
      local $\="\n"; my $roy = 4; print $roy; print asdf($roy); print $roy; sub asdf { my $self = $_[0]; $_[0] = 66; $self = 0; return "yoda"; } __END__ 4 yoda 66
      Isn't perl fun ;)
Re: goto and AUTOLOADed methods
by adamk (Chaplain) on Aug 03, 2003 at 04:45 UTC
    There seems to be a lot of argument here

    Go get the module Class::Autouse and have a look at it.

    It has both a working example of AUTOLOADing methods AND a reason for doing it ( in this case, saving memory by not loading classes in large class trees until a method is called on a class ).

    It does a fair bit of stuff to achieve it, manually walking the ISA tree, anti-infinite loop protection code, etc.
Re: goto and AUTOLOADed methods
by bsb (Priest) on Aug 02, 2003 at 06:22 UTC
    1) You may want to assign the result of something_clever_ret­urning_fn_reference() to *{$AUTOLOAD} so that you only run the something_clever... function the first time you need the sub, not every time. (I'm assuming that it'll return the same thing each time since you don't give it arguments)

    2) If you've got an inheritence hierarchy then you may want to stick the sub into base class so that sibling classes don't have to do the AUTOLOAD thang either.

    See AUTOLOAD & mod_perl memory for my ponderings and perrin's wisdom.