Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call subroutine in perl using variable name [duplicate]

Let say I have one array that contains all subroutine name and I want to call all one by one.

foreach $sub (@arr){
      print "Calling $sub\n";
       #---How to call $sub?----
       &$sub;  ## will not work
}
like image 588
Raj Avatar asked Jan 05 '17 09:01

Raj


2 Answers

Your code is correct in general, but you need to turn off strict 'refs' to make Perl allow you to use variable content as code refs.

use strict;
use warnings;

sub foo { print "foo" }
sub bar { print "bar" }

my @arr = qw/foo bar/;
foreach my $sub (@arr) {
    no strict 'refs';
    print "Calling $sub\n";

    &$sub();
}

The output here is:

Calling foo
fooCalling bar
bar

I've also added parenthesis () after the call. That way we pass no arguments to %$sub. If we do not those, the @_ argument list of the current subroutine will be used.


However, you should probably not do this. Especially if @arr contains user input, this is a big problem. Your user can inject code. Consider this:

my @arr = qw/CORE::die/;

Now we get the following output:

Calling CORE::die
Died at /home/code/scratch.pl line 1492.

Oops. You don't want to do this. The die example is not very bad, but like this you could easily call code in some different package that wasn't intended.

It's probably better to make a dispatch table. There is a whole chapter about those in Higher Order Perl by Mark Jason Dominus, which you can download for free on his website.

It basically means you put all the subs into a hash as code references, and then call those in your loop. That way you can control which ones are allowed.

use strict;
use warnings;

sub baz { print "baz" }

my %dispatch = (
    foo => sub { print "foo" },
    bar => sub { print "bar" },
    baz => \&baz,
);

my @arr = qw/foo bar baz wrong_entry/;
foreach my $sub ( @arr ) {
    die "$sub is not allowed" 
        unless exists $dispatch{$sub};

    $dispatch{$sub}->();
}

This outputs:

foobarbaz
wrong_entry is not allowed at /home/code/scratch.pl line 1494.
like image 50
simbabque Avatar answered Sep 21 '22 23:09

simbabque


You want to do that using code references.

foreach my $sub (@arr) 
{
    $sub->();
}

where @arr contains scalars such as

my $rc = sub { print "Anonymous subroutine\n" };

or

sub func { print "Named sub\n" }
my $rc = \&func;

You can manipulate these scalars as you would any other, to form your array. However, it is more common and useful to use them as values in a hash, creating a dispatch table.

See perlref and perlsub, and (for example) this post and links in it for comments and details.

like image 28
zdim Avatar answered Sep 23 '22 23:09

zdim