in reply to Autoloading tie routines

Piece of advice. If you know the names of all of the routines, and they are repetitive enough to AUTOLOAD, why not create them in advance by assigning closures to the symbol table? That is rather than something like this:
sub AUTOLOAD { my @name_pieces = split /::/, $AUTOLOAD; my $function = pop @name_pieces; my $package = join "::", @name_pieces; no strict 'refs'; *{"$package\::$function"} = get_sub($package, $function); goto &{"$package\::$function"}; }
You write something like this:
for my $package (@packages) { for my $function (@functions) { no strict 'refs'; *{"$package\::$function"} = get_sub($package, $function); } }
and now there is no need to write an AUTOLOAD. Which means that chromatic doesn't complain that can doesn't do the right thing, your AUTOLOAD doesn't break when it sees functions you didn't mean to catch, you don't blow the method cache a bunch of times, and life is generally better.

Replies are listed 'Best First'.
Re^2: Autoloading tie routines
by cmac (Monk) on Feb 02, 2009 at 20:43 UTC
    Tilly:
    Thers is an AUTOLOAD in ABC.pm that loads constants using the constant routine in ABC.xs. ABC.pm exports the direct-call sub names (for those who don't want to use the tied interface) and the constants. Here is the AUTOLOAD routine in ABC.pm:
    our $AUTOLOAD; sub AUTOLOAD { # this is used to 'autoload' constants from the constant() # XS function. If a constant is not found then control is passed # to the AUTOLOAD in AutoLoader. my $constname; ($constname = $AUTOLOAD) =~ s/.*:://; croak "& not defined" if $constname eq 'constant'; my $val = constant($constname, @_ ? $_[0] : 0); if ($! != 0) { if ($! =~ /Invalid/ || $!{EINVAL}) { $AutoLoader::AUTOLOAD = $AUTOLOAD; goto &AutoLoader::AUTOLOAD; } else { croak "Your vendor has not defined $constname"; } } eval "sub $AUTOLOAD { $val }"; goto &$AUTOLOAD; }
    Your solution is not clear to me. Does it provide autoloading, or is it them same as putting all of the tie routines as preloaded (before the __END__)? Can you please re-cast it for use with the existing AUTOLOAD?
      This would be an example of why moving more and more dispatch logic into AUTOLOAD leads to problems.

      You have a complex AUTOLOAD that already does two very different things. It defines a series of constants. And you have a set of functions that are only loaded if they are used. Unless you've written out all of the tie methods and AutoSplit them for AutoLoader to find, you haven't implemented a tie interface. So making this do that is not just a question of making a small modification to this AUTOLOAD routine.

      Making things worse, ABC is not inherited by the classes that you want to be able to tie to. So if you want to use AUTOLOAD, you have to write a new AUTOLOAD that is in that class, or in a superclass that is loaded from that class. Which lead to your original question that you didn't know how to write said AUTOLOAD.

      So show me how to write the AUTOLOAD you want to write, and I'll show you how to convert it. Otherwise look at the template I wrote, and try to write get_sub which will, for instance, take "XYZ::ABC::Array" and "STORE" as arguments, then returns an anonymous subroutine.

        Based on the the statement "the AUTOLOAD sub is called with the arguments that the unknown sub would be called with", here is my AUTOLOAD in ABC.pm.
        # AUTOLOAD is used to # 1) 'autoload' constants from the constant() function in ABC.xs # If the name is not a constant then it's parsed for # 2) a tie package-name::function-name, which if matched is executed our $AUTOLOAD; # implicit argument of AUTOLOAD sub AUTOLOAD { my $constname; ($constname = $AUTOLOAD) =~ s/.*:://; my $val = constant($constname, 0); if ($!) { # the name in $AUTOLOAD is not a constant defined by XYZ::ABC if (my ($kind, $nolock, $function) = $AUTOLOAD =~ /^XYZ::ABC::(Scalar|Array|Hash)::(NoLock::)?( +[A-Z]+)$/) { if ($function =~/^TIE$kind$/i) { my $self = shift; my $sah = shift; # sah = scalar/array/hash return bless \$sah, $self; # vv Scalar or Array or Hash } elsif ($function =~ /^(FETCH|STORE)$/ || $kind !~ /^S/ # vv Array or Hash && $function =~ /^(DELETE|EXISTS|CLEAR)$/ || $kind =~ /^A/ # vv Array && $function =~ /^(FETCHSIZE|STORESIZE|EXTEND|POP|PU +SH|SHIFT|UNSHIFT|SPLICE)$/ || $kind =~ /^H/ # vv Hash && $function =~ /^(FIRSTKEY|NEXTKEY|SCALAR)$/) { my $subname = 'abc' . ($nolock ? 'a_' : '_') . lc($kin +d) . '_' . lc($function); my $sah_ref = shift; { no strict 'refs'; # differentiate subs with and without return value +s if ($function =~ /^(ST|CL|PU|UN|EXT)/) { &$subname ($$sah_ref, @_); return; } else { return &$subname ($$sah_ref, @_); } } } elsif ($function =~ /^(UNTIE|DESTROY)$/) { # how do we do nothing? return; # ?? } } croak "$AUTOLOAD is not a defined constant or subroutine for X +YZ::ABC"; } # the name in $AUTOLOAD is a constant defined by XYZ::ABC: define +it for perl eval "sub $AUTOLOAD { $val }"; # can this be $constname rather tha +n $AUTOLOAD? goto &$AUTOLOAD; # can this just be 'return' if all of the defined + names really are constants? }
        The routines that implement most of the 'tie' calls already exist in ABC.xs, with the same names (lowercased). It pleases me very much to get rid of 52 silly wrapper routines.

        At the end of ABC.pm are six packages like this:
        package XYZ::ABC::Scalar; use XYZ::ABC qw(AUTOLOAD); our @ISA = qw(XYZ::ABC); # are this and the use both needed? 1;
        Suggestions and corrections for AUTOLOAD and these packages are very welcome. In its first test AUTOLOAD allowed perl to do a tie statement to XYZ::ABC::Scalar.

        Thanks for being there,
        cmac
        www.animalhead.com