Check if a routine exists in Perl

I would like to check if a subroutine exists before calling it. For instance:

use warnings;
use strict;

my @names=qw (A B);

for (@names) {
  my $sub=\&{"print$_"};
  if (exists &{$sub}) {
    &$sub();
  }
}

sub printA {
  print "hello A\n";
}

However, this code does not work. This gives an error:

Undefined subroutine &main::printB

I know that I can use eval,

for (@names) {
  my $sub=\&{"print$_"};
  eval {
    &$sub();
  };
  if ($@) {
    print "$_ does not exist..\n";
  }
}

This is great, but it would be nice to know why the first code didn't work?

+4
source share
4 answers

You are trying to make a link before you know if suba exists. Be sure to use the routine name in the call first exists.

use strict;
use warnings;

foreach (qw(A B)) {
    my $name = 'print' . $_;
    if (exists &{$name}) {
        my $s = \&{$name};
        $s->();
    }
}

sub printA {
    print "hello A"
}
+4
source
my $sub = main->can("print$_");
if ($sub) {
  $sub->();
}

From perldoc

, METHOD. , . , undef. , $obj, CLASS VAL.

+5

if you change the code to:

for (@names) {
  my $sub="print$_";
  no strict 'refs';
  if (exists &{$sub}) {
    &$sub;
  }
}

A method is called only if it exists.

+3
source

It is almost certainly better to avoid calling routines with a string this way. One easy way to do this is to use the refs routines.

my %prints = (
  'printA' => \&printA,
  'printB' => \&printB,
);
for (@names) {
  my $printName = "print$_";
  if($prints{$printName}) {
    $prints{$printName}->();
  }
  else {
    warn "Invalid print: $printName";
  }
}
+1
source

All Articles