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. | [reply] |
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
| [reply] [d/l] [select] |
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. | [reply] [d/l] |