leocharre has asked for the wisdom of the Perl Monks concerning the following question:

Some functions set the $! error code, like

open(FILE,"</this/file") or die($!);

How would I get my own sub to populate '$!'? Is it any printing to STDERR that ends up in '$!'- no.. right? (I searched the docs but this is throwing me off) Is the special variable '$!' only set by C libraries or something spooky like that?

How would I get the following to work? (uncommenting the line doesn't do what I would like).

for(qw(1 4 haha)){ usenumbers($_) or die($!); } sub usenumbers { my $var = shift; $var=~/^\d+$/ or # ( $! = 'these were not digits' ) and return; print "Yes, [$var] has numbers."; return 1; }

Replies are listed 'Best First'.
Re: How do I return false and set an error in special variable $!
by RMGir (Prior) on May 17, 2007 at 17:48 UTC
    $! is magic.

    Its magic is documented in perlvar, but basically, what $! contains is the "errno" C runtime library value for the last system-related (I don't want to say "system" call, since I don't only mean that function) call that failed.

    You CAN assign to errno, but only integer values, which you can find in your system's documentation, or in the errno.h C header file for your platform (if it's unix-ish, check /usr/include/errno.h).

    The "string" value of $! is whatever string your system decides goes with a given integer, so none of them are going to be "these were not digits". Your best bets in that case might be 22 (EINVAL "Invalid argument") or 33 (EDOM, "Domain error in Math Function). At least, those are valid for AIX, Solaris, and Linux, and probably for other Unices, as well as for Windows. (A quick search finds that EDOM is actually required to exist per the C standard - of course, it's likely that nothing says it has to be 33 on your platform...)

    To test, you can do:

    perl -e'foreach(1..33){$!=$_; print "$_: $!\n"}'

    Mike
Re: How do I return false and set an error in special variable $!
by ikegami (Patriarch) on May 17, 2007 at 17:54 UTC

    RMGir explained the problem. Here's a solution:

    for(qw(1 4 haha)){ eval { usenumbers($_) } or die($@); } # Returns true on success. # Throws an exception on error. sub usenumbers { my $var = shift; $var=~/^\d+$/ or die('Invalid argument: Expecting digits'); print "Yes, [$var] has numbers."; return 1; }

    A variation of the above where the function doesn't need to return true:

    for(qw(1 4 haha)){ eval { usenumbers($_) }; die($@) if $@; } # Throws an exception on error. sub usenumbers { my $var = shift; $var=~/^\d+$/ or die('Invalid argument: Expecting digits'); print "Yes, [$var] has numbers."; }
Re: How do I return false and set an error in special variable $!
by eric256 (Parson) on May 17, 2007 at 18:10 UTC

    Though you can't use $! you can use $@. It might be considered bastardizing it.

    use strict; use warnings; for(qw(1 4 haha)){ usenumbers($_) or die($@); } sub usenumbers { my $var = shift; unless($var =~ /^\d+$/) { $@ = "Invalid Number"; return; } print "Yes, [$var] has numbers.\n"; return 1; }

    ___________
    Eric Hodges
Re: How do I return false and set an error in special variable $!
by kyle (Abbot) on May 17, 2007 at 17:55 UTC

    My reading of perlvar is that you can't set $! to a particular string. You can set it to a particular number so that it will stringify to a particular error message, but that's it.

    use strict; use warnings; $! = 'foo'; print "bang: $!\n"; $! = 3; print "bang: $!\n"; __END__ Argument "foo" isn't numeric in scalar assignment at perlmonks.pl line + 8. bang: bang: No such process

    I'd suggest you use a package variable to pass your error messages or throw full blown exceptions (see Exception::Class) with as much detail as you like.

Re: How do I return false and set an error in special variable $!
by naikonta (Curate) on May 18, 2007 at 02:14 UTC
    The Errno module provides interface to the system error constants with magic %!. Since it's readonly, you can't just introduce new constants at will, unless perhaps, you subclass the module. But, you can use any predefined constant that is close to the error string you want to throw.
    #!/usr/bin/perl use strict; use warnings; use Errno; sub usenumbers { my $var = shift; $var =~ /^\d+$/ or ($! = &Errno::EINVAL) and return; print "Yes, [$var] has numbers.\n"; return 1; } for (qw(1 4 haha)) { usenumbers($_) or die "$_: $!"; }
    Results,
    Yes, [1] has numbers. Yes, [4] has numbers. haha: Invalid argument at error.pl line 15.
    However, I'm not sure this is a good practice. The problem is error constants are different across OSes.

    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!