in reply to Re^3: Autoloading tie routines
in thread Autoloading tie routines

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

Replies are listed 'Best First'.
Re^5: Autoloading tie routines
by tilly (Archbishop) on Feb 03, 2009 at 16:28 UTC
    Are you aware of everything that the AUTOLOAD has done with those 6 packages? Every constant in ABC, every method in ABC, all are inherited by XYZ::ABC::Scalar. Is this really what you want? Furthermore ABC looks like an example of the God module anti-pattern. That is a code smell right there.

    Finally you are obviously confused about how this works because you don't know whether you need to both import AUTOLOAD and inherit. The answer is that if you don't have an import method in ABC.pm, then the use statement does nothing and should be removed. If you have an import that will export AUTOLOAD, then the AUTOLOAD written for ABC.pm will fail to work in another package because it will be looking at $ABC::AUTOLOAD for the requested function, but Perl will put it in $XYZ::ABC::Scalar::AUTOLOAD instead and you won't find it properly. Therefore the use should definitely be removed.

    A much cleaner solution is at the end to have something that looks like this:

    # Presumably you have 6 packages listed in this list for my $package (qw( XYZ::ABC::Scalar )) { $package =~ /(Scalar|Array|Hash)(::NoLock)?/ or die "Tie type of '$package' not understood"; my $kind = $1; my $nolock = $2; no strict 'refs'; *{$package . uc("::TIE$kind")} = sub { my $class = shift; my $ssh = shift; # Scalar/Hash/Array return bless $ssh, $class; }; # These two do nothing. *{"$package\::DESTROY"} = sub {}; *{"$package\::UNTIE"} = sub {}; # And now the rest. my @functions = qw(FETCH STORE); if ($kind eq "Array") { push @functions, qw( FETCHSIZE STORESIZE EXTEND CLEAR POP PUSH SHIFT UNSHIFT SPLICE ); } elsif ($kind eq "Hash") { push @functions, qw( STORE DELETE CLEAR EXISTS FIRSTKEY NEXTKEY SCALAR ); } for my $function (@functions) { my $subname = join "_", ($nolock ? "abca" : "abc"), lc($kind) +. lc($function); *{"$package\::$function"} = \&$subname; } }
    This will create the 6 packages, set all of the right methods, not create any unwanted additional methods, and gets rid of the overhead of running the AUTOLOAD every time you want to access one of the tied methods. Also by moving logic out of the AUTOLOAD you make the logic that remains in the AUTOLOAD clearer. Plus if anyone has code that uses the can method for introspection, it will work on those packages.

    The moral? AUTOLOAD is a big sledgehammer. Don't swing it unless you need to, and then swing it carefully.

      Please explain "Furthermore ABC looks like an example of the God module anti-pattern. That is a code smell right there." Colorful language!

      Obviously there are is a lot of stuff in XYZ::ABC, but I'm working toward a successor module to an existing CPAN module, which has the same Scalar, Hash, and BTree stuff. I admit to making it worse by adding the Array stuff. But hopefully the linker will treat the ABC.so file as a library, and only load what's called for.

      "everything that the AUTOLOAD has done with those 6 packages"? (There are now 8 because I added BTree as an alias for Hash, to be more compatible with the predecessor module.) Far as I know, AUTOLOAD has done nothing but be there to field attempts to use unknown constants and subroutines. The subsidiary package names cannot be use'd, and exist only to be used in tie statements.

      There is no "use AutoLoad" in ABC.pm any more. ABC.pm's AUTOLOAD is free-standing, and passes the tie calls to subs in ABC.xs that are also available as direct calls to users of XYZ::ABC.

      Rather than define 82 names in the symbol table at the start of execution, the compromise course between your routine and the direct-through AUTOLOAD that for the moment is still shown below, is for AUTOLOAD to put each name for which it is called in the symbol table (thus eliminating future calls to AUTOLOAD for this name) and then go to the newly defined sub. It will need to make an anonymous sub that dereferences the first operand from perl to match what the destination XS routine expects.

      If I wasn't "confused about how this works" I wouldn't be posting to perlmonks. ABC.pm does not have an import method. It has an export method that used to include AUTOLOAD in @EXPORT_OK. But I took the use's out of the subsidiary packages, and no longer export AUTOLOAD. Thank you for this!

      We must be careful about commenting about the clarity of each others' code. I find your routine above quite readable, until I come to *name = thing; which I have not used. Based on your code I made several improvements to my AUTOLOAD routine, which looks like this PENDING THE "STORE THE NAME" IMPROVEMENT NOTED ABOVE:
      # 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 { # make the base name (without the "package::") (my $constname = $AUTOLOAD) =~ s/.*:://; # call the constant lookup routine in ABC.xs my $val = constant($constname, 0); if ($!) { # the name in $AUTOLOAD is not a constant defined by XYZ::ABC # sah = scalar/array/hash if (my ($abcx, $sah, $function) = $AUTOLOAD =~ /^XYZ::(ABCA?)::(Scalar|Array|Hash|BTree)::([ +A-Z]+)$/) { if ($sah eq 'BTree') {$sah = 'Hash'} if ($function eq uc("TIE$sah")) { my $self = shift; my $base_sah = shift; # sah = scalar/array/hash return bless \$base_sah, $self; # Scalar or Array or Hash } elsif ($function eq 'FETCH' || $function eq 'STORE' || $sah ne 'Scalar' # Array or Hash && $function =~ /^(DELETE|EXISTS|CLEAR)$/ || $sah eq 'Array' && $function =~ /^(FETCHSIZE|STORESIZE|EXTEND|POP|PU +SH|SHIFT|UNSHIFT|SPLICE)$/ || $sah eq 'Hash' && $function =~ /^(FIRSTKEY|NEXTKEY|SCALAR)$/) { $function =~ s/KEY$/_KEY/; my $subname = lc($abcx) . '_' . lc($sah) . '_' . lc($f +unction); my $base_sah_ref = shift; unshift @_, $$base_sah_ref; # dereference the base sca +lar/array/hash goto &$subname; } elsif ($function eq 'UNTIE' || $function eq 'DESTROY') { return; # do nothing } } 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? }
      I would appreciate comments on the last two questions in its comments.

      Thanks for being there,
      cmac
      www.animalhead.com
        For more on the God antipattern see http://en.wikipedia.org/wiki/God_object. It refers to the case when one class or module tries to take on too many jobs at once. This defeats the entire point of modularity, which is to enable code to be broken into modular pieces, each of which has reasonable complexity.

        If you do not know what benefits modularity is supposed to bring, or what good modularity looks like, please pick up a book like Code Complete 2. It is a large and important topic, and one I certainly don't have time to go into in any depth today.

        Moving on, it sounds like you are micro-optimizing on memory usage. The vast majority of mod_perl sites should just use a reverse proxy in accelerator mode and then not concern themselves overly much with memory needs. See this guide for an overview of what that is, and how to do it. (Yes, it is a 1.0 specific guide. But the advice still applies pretty well to 2.0. BTW I really, really hope you're using pre-fork mode with 2.0...)

        That said, I don't know the exact amount of memory needed to have 100 entries in a symbol table, but it is going to be at most a few K. (The functions themselves take up no space because they already exist.) If you are having trouble with running out of memory, this is not where you should look for improvements.

        Moving on, the syntax *foo = $coderef; is the standard way to manipulate the package table and install a function. See perlmod for more examples. It is certainly better than the eval solution that you are using in your AUTOLOAD. Better in what way, you ask? Well for a start, if the code you are generating is wrong, then the eval will silently fail and you won't get a good error message. Real example. If one of your constants is supposed to be "Hello, world" then your eval will fail, no error will be reported, and your AUTOLOAD is going to blow up. So compare:

        # This might break badly depending on $value eval "sub $AUTOLOAD { $val }"; # This will work no matter what $val happens to be. no strict 'refs'; *$AUTOLOAD = sub { $val };
        Which means that this is a piece of syntax you really should learn.

        Finally we come to your two questions. Yes, you can replace $AUTOLOAD with $constname if you are in the right package. Secondly the goto will go to the new function you just defined if it was properly created by eval, and it just returns a value. So you can replace one by the other. However as your code currently stands, that goto is the only sign you are given that your code broke if it broke.