0

Imagine I have this simple function

say_hi() || die "ERROR: sub say_hi had an issue: Details => $!\n";

sub say_hi{
   my ($name) = @_;
   return unless $name;
   print "Hi $name\n";
   return 1;
}

How can I set the content of $! in the sub so that it can be used after function returns. I'm trying to do something similar to what

open() || die "$!";

does. Thank you

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
JohnnyLoo
  • 877
  • 1
  • 11
  • 22

2 Answers2

3

perldoc -v $!:

When referenced, $! retrieves the current value of the C "errno" integer variable. If $! is assigned a numerical value, that value is stored in "errno". When referenced as a string, $! yields the system error string corresponding to "errno".

... Assignment to $! is similarly ephemeral. It can be used immediately before invoking the "die()" operator, to set the exit value, or to inspect the system error string corresponding to error n, or to restore $! to a meaningful state. (emphases mine)

That explains why you can't do the obvious:

#!/usr/bin/env perl

say_hi() or die "ERROR: sub say_hi had an issue: Details => $!\n";

sub say_hi{
   my ($name) = @_;
   unless ($name) {
       $! = 'no name';
       return;
   }
   print "Hi $name\n";
   return 1;
}

That results in:

ERROR: sub say_hi had an issue: Details =>

Consider using eval/$@:

#!/usr/bin/env perl

eval { say_hi() } or die "ERROR: sub say_hi had an issue: Details => $@\n";

sub say_hi{
   my ($name) = @_;
   $name or die "no name\n";
   print "Hi $name\n";
   return 1;
}

See also "Is Try::Tiny still recommended for exception handling in Perl 5.14 or later?"

If you follow brian's recommendation in this answer to that question, you can do something like:

#!/usr/bin/env perl

use strict;
use warnings;

my $r = say_hi();

$r->{success}
    or die sprintf "ERROR: sub say_hi had an issue: Details => %s\n", $r->{reason};

sub say_hi{
   my ($name) = @_;
   $name or return { success => 0, reason => 'Missing "name"' };
   print "Hi $name\n";
   return { success => 1 };
}

Or, you can use Params::Validate or Validate::Tiny etc.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
3

$! is a proxy for C's errno. The values errno can take are available as constants in Errno.

use Errno qw( EINVAL );

sub foo {
   my ($arg) = @_;
   if (!$arg) {
      $! = EINVAL;
      return 0;
   }

   ...

   return 1;
}

foo(...)
   or die($!);

This is rarely done, mostly because you are limited to error codes designed for system calls. It's far more common to throw exceptions or have a module-specific error variable (e.g. $DBI::err).

ikegami
  • 367,544
  • 15
  • 269
  • 518