Is it OK to assign to $! on an error in Perl?
E.g.,
if( ! (-e $inputfile))
{
$! = "Input file $inputfile appears to be non-existent\n";
return undef;
}
This way I can handle all errors at the top-level.
Thanks.
Is it OK to assign to $! on an error in Perl?
E.g.,
if( ! (-e $inputfile))
{
$! = "Input file $inputfile appears to be non-existent\n";
return undef;
}
This way I can handle all errors at the top-level.
Thanks.
Well, the documentation says it's an indicator for system errors, basically. So I wouldn't assign to it, you just make your lib's users mad.
Use exceptions instead:
eval { # this ain't the evil eval
# some code
die $something;
}
if (my $err = $@) {
# exception handling
}
Note that you can "throw", or die, with about anything you need..
If you assign to $!, it is placed in the system errno variable, which only takes numbers. So you can in fact do:
use Errno "EEXIST";
$! = EEXIST;
print $!;
and get the string value for a defined system error number, but you can't do what you want - setting it to an arbitrary string. Such a string will get you a Argument "..." isn't numeric in scalar assignment warning and leave errno set to 0.
The other problem is that $! may be changed by any system call. So you can only trust it to have the value you set until you do a print or just about anything else. You probably want your very own error variable.
Setting $!
is fine, but:
Something to keep in mind is that die uses the value in $! for its exit code (so long as it is not zero).
If you only have one variable to store errors, you'll have problems if you have more than one error occurring in your program before checking the status of your error variable. That's worth avoiding if you can help it.
Thankfully in Perl you can help it. A really nice solution is to use object-oriented exception handling from Error.pm. This module will allow you to write try/catch blocks, like this:
try {
some code;
code that might thrown an exception;
more code;
return;
}
catch Error with {
my $ex = shift; # Get hold of the exception object
# handle the exception;
};
The CPAN documentation for the module is quite good, and there is a Perl.com article on the subject too.
$! has so many caveats, being a global variable which lots of functions assign to (some of them C functions which Perl calls), that I would simply throw an exception (which in Perl means dying) and let the user trap it if they care. So instead of writing:
$obj->foo or die $!;
$obj->bar or die $!;
$obj->baz or die $!;
or even
$obj->foo or die $obj->error;
$obj->bar or die $obj->error;
$obj->baz or die $obj->error;
you can just write
$obj->foo;
$obj->bar;
$obj->baz;
and know that if there is an error you'll be informed of it. Also anyone above you will be informed and can trap it. Since that's the most common case make it happen without the user needing to remember it and type it over and over and over again.
Should you want to ignore or recover from the error, simply use eval BLOCK
.
eval { $obj->foo }; # don't care if it works or not
eval { $obj->bar } or do { ...something when it doesn't work... };
Since this is the exceptional case it should be where the user has to remember to add more code and does more work.
Examples of this approach include DBI's RaiseError flag, which is only off by default for backwards compatibility, and the wonderful autodie module.