• How to use AUTOLOAD to generate accessor methods at runtime

    From Rainer Weikusat@21:1/5 to All on Thu Oct 29 14:48:51 2020
    Motivated by the horrible example in the "DOWNLOAD.OUR.STUFF.NOW"
    document wrongly referred to as "perl OO tutorial" (the similarity to MAKE.MONEY.FAST is intentional).

    This is an example for a class hierachy using anonymous arrays as representation as that's more sensible when single inheritance is
    sufficient.

    [Comments inserted for the example]

    ---------------
    sub AUTOLOAD
    {
    my $self = $_[0];
    my ($attr, $ndx, $sub, $kwm, $code);

    our $AUTOLOAD;

    $AUTOLOAD =~ /([^:]+)$/, $sub = $1; # determine subroutine name

    if ($sub =~ /^set_(.+)/) {

    # if it's set_xxx, a method for setting attribute xxx
    # is being asked for

    $attr = $1;
    $code = 'sub { $_[0][%u] = $_[1] }';
    } else {

    # otherwise, generate a method for reading the value

    $attr = $sub;
    $code = 'sub { $_[0][%u] }';
    }

    #
    # Determine attribute index in the array by
    # calling a method returning a hash mapping
    # names to he corresponding numbers and looking
    # it up there.
    #

    $kwm = $self->kw_map();
    $ndx = $$kwm{$attr};
    err('class %s has no attribute %s', ref($self), $attr) # throws an exception
    unless defined($ndx);

    # compile the code
    #
    $sub = eval(sprintf($code, $ndx));

    # install in the symbol table of the class of
    # the invocant under the proper name
    #
    *{$AUTOLOAD} = $sub;

    # pass control to the generated code
    #
    goto &$sub;
    }
    -----------------

    Assuming the code is complicated enough that the accessor will be called
    again in future, this will just be a 'normal' method call, ie, it won't
    go through AUTOLOAD again.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Henry Law@21:1/5 to Rainer Weikusat on Thu Oct 29 19:19:27 2020
    On 29/10/2020 14:48, Rainer Weikusat wrote:
    # install in the symbol table of the class of
    # the invocant under the proper name
    #
    *{$AUTOLOAD} = $sub;

    This is Perl witchcraft, but I followed it and am much better informed;
    it's really clever.

    One thing, though: in the statement above you're telling Perl that
    instead of the AUTOLOAD subroutine it should go straight to the sub that
    you built and compiled: I get that.

    But I thought $AUTOLOAD was the variable (defined in all the examples
    I've seen with "our $AUTOLOAD") not the subroutine? I'm wrong, plainly,
    but why?

    --
    Henry Law n e w s @ l a w s h o u s e . o r g
    Manchester, England

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Henry Law on Thu Oct 29 21:13:09 2020
    Henry Law <news@lawshouse.org> writes:
    On 29/10/2020 14:48, Rainer Weikusat wrote:
    # install in the symbol table of the class of
    # the invocant under the proper name
    #
    *{$AUTOLOAD} = $sub;

    This is Perl witchcraft, but I followed it and am much better
    informed; it's really clever.

    One thing, though: in the statement above you're telling Perl that
    instead of the AUTOLOAD subroutine it should go straight to the sub
    that you built and compiled: I get that.

    But I thought $AUTOLOAD was the variable (defined in all the examples
    I've seen with "our $AUTOLOAD") not the subroutine? I'm wrong,
    plainly, but why?

    $AUTOLOAD is a package variable whose value will be the fully qualified
    name of the subroutine/ method which was actualy called, eg,

    Top::Middle::set_height

    This means

    *$AUTOLOAD

    (the {} aren't really needed)

    accesses the glob Top::Middle::set_height via symbolic
    reference. Assigning a reference to something to a glob changes the
    object in the slot corresponding with the type of the object the
    reference refers to. Eg,

    [rw@doppelsaurus]/tmp#perl -E '*array = [1,2,3]; say @array'
    123

    This construct (the $AUTOLOAD one) will not work under 'use
    strict'. It'll need a

    no strict 'refs';

    there (possibly in a block to limit the scope of that).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Randal L. Schwartz@21:1/5 to All on Sat Oct 31 08:51:10 2020
    "Rainer" == Rainer Weikusat <rweikusat@talktalk.net> writes:

    Rainer> $AUTOLOAD =~ /([^:]+)$/, $sub = $1; # determine subroutine
    Rainer> name

    This is bad style and potentionally dangerous. If the match fails, $1
    is the *previous* $1, leading to bugs I've seen in the wild. Do this
    instead:

    my ($sub) = $AUTOLOAD =~ /([^:]+)$/ or die "bad $AUTOLOAD value\n";

    --
    Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 <merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
    Perl/Unix/Dart consulting, Technical writing, Comedy, etc. etc.
    Still trying to think of something clever for the fourth line of this .sig

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Randal L. Schwartz on Mon Nov 2 16:49:18 2020
    merlyn@stonehenge.com (Randal L. Schwartz) writes:
    "Rainer" == Rainer Weikusat <rweikusat@talktalk.net> writes:

    Rainer> $AUTOLOAD =~ /([^:]+)$/, $sub = $1; # determine subroutine Rainer> name

    This is bad style and potentionally dangerous. If the match fails, $1
    is the *previous* $1, leading to bugs I've seen in the wild. Do this instead:

    my ($sub) = $AUTOLOAD =~ /([^:]+)$/ or die "bad $AUTOLOAD value\n";

    It's consistent with the documented behaviour:

    ,----
    | The fully qualified name of the original subroutine magically appears in
    | the global $AUTOLOAD variable of the same package as the "AUTOLOAD"
    | routine.
    `----

    Granted, one could write something like

    $AUTOLOAD =~ /([^:]+)$/ or die('Your perl is broken!');

    :-)

    But I don't really see a need to "defend" against that. If "broken perl"
    causes a problem in practice, I'll fix it (at least for the version I'm
    using).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)