When you have a function that has quite a few arguments,
including a number of defaults, it is often much nicer to
use a hash instead of an array. However a risk with that
is that someone will mistype a name and their argument will
be silently ignored. So ideally instead of just using a
hash you want to do internal processing to make sure that
the function has been called appropriately.
This snippet takes care of that. It allows you to have
required arguments, optional arguments, and to set defaults
on some of the optional arguments. Just use it like this:
sub examp_func {
my %args = @_;
my ($foo, $bar, $baz)
= proc_args(\%args, ['foo'], ['bar', 'baz'], {bar => 'default'});
print "foo: $foo\n";
print "bar: $bar\n";
print "baz: $baz\n";
}
Unfortunately you still need to synchronize the order of
arguments to the function with the variables that they go
in. Barring using some sort of macro-preprocessing
facility that looks unavoidable if you want the protection
of lexical variables.
TIMTOWTDI, but if you are tempted to use this in a lot of
your code you probably should think about whether certain
collections of arguments logically belong together in an
object of some class...
UPDATE
I am not longer happy with this snippet. The approach
that I prefer as of Nov 2001 is in Re (tilly) 2: passing subroutine arguments directly into a hash. YMMV.
use Carp;
# Takes an anon hash of args, an anon array of required fields,
# an optional anon array of optional args, and an optional
# anon hash of defaults. Returns an array of the determined
# values
sub proc_args {
my $args = shift;
my $req = shift;
my $opt = shift || [];
my $default = shift || {};
my @res;
foreach my $arg (@$req) {
if (exists $args->{$arg}) {
push @res, $args->{$arg};
delete $args->{$arg};
}
else {
confess("Missing required argument $arg");
}
}
foreach my $arg (@$opt) {
if (exists $args->{$arg}) {
push @res, $args->{$arg};
delete $args->{$arg};
}
else {
push @res, $default->{$arg};
}
}
if (%$args) {
my $bad = join ", ", sort keys %$args;
confess("Unrecognized arguments: $bad\n");
}
else {
return @res;
}
}