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

2

5

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
}
Trounce answered 5/1, 2017 at 9:48 Comment(1)
(\&$name)->(@args)Glacialist
B
11

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.
Birkenhead answered 5/1, 2017 at 9:55 Comment(5)
I gave the man a fish, you taught him how to fish. +1Dynah
@Dynah oh, there are two more answers. I didn't see them yet. I was just busy hacking down half a blog post in here again... :DBirkenhead
HI simbabque. Thanks. I was not knowing that. In my case users are not providing subroutine name in arguments but good info.Trounce
@Dynah I assumed the OP does not have use strict because there was no my sub in the loop. In that case, the OP's code works. With strict turned on however, strict 'refs' needs to be disabled.Birkenhead
Am I getting downvoted because it's too much text?Birkenhead
R
8

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.

Renvoi answered 5/1, 2017 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.