66

I keep the name of the subroutine I want to call at runtime in a variable called $action. Then I use this to call that sub at the right time:

&{\&{$action}}();

Works fine. The only thing I don't like is that it's ugly and every time I do it, I feel beholden to add a comment for the next developer:

# call the sub by the name of $action

Anyone know a prettier way of doing this?


UPDATE: The idea here was to avoid having to maintain a dispatch table every time I added a new callable sub, since I am the sole developer, I'm not worried about other programmers following or not following the 'rules'. Sacrificing a bit of security for my convenience. Instead my dispatch module would check $action to make sure that 1) it is the name of a defined subroutine and not malicious code to run with eval, and 2) that it wouldn't run any sub prefaced by an underscore, which would be marked as internal-only subs by this naming convention.

Any thoughts on this approach? Whitelisting subroutines in the dispatch table is something I will forget all the time, and my clients would rather me err on the side of "it works" than "it's wicked secure". (very limited time to develop apps)


FINAL UPDATE: I think I've decided on a dispatch table after all. Although I'd be curious if anyone who reads this question has ever tried to do away with one and how they did it, I have to bow to the collective wisdom here. Thanks to all, many great responses.

Marcus
  • 5,772
  • 8
  • 35
  • 60
  • 4
    Realize that when you take a reference to something to immediately dereference it, you can skip the middle steps. :) – brian d foy Dec 16 '09 at 19:58
  • 1
    @brian: usually (and definitely in this case), but see http://stackoverflow.com/questions/1836178/how-do-i-call-a-function-name-that-is-stored-in-a-hash-in-perl/1836356#1836356 for a counter-example. :) – Ether Dec 16 '09 at 22:52
  • 1
    A dispatch table also means you can `use strict`, which is generally a good thing. – OrangeDog Jan 09 '13 at 11:18

12 Answers12

90

Rather than storing subroutine names in a variable and calling them, a better way to do this is to use a hash of subroutine references (otherwise known as a dispatch table.)

my %actions = ( foo => \&foo,
                bar => \&bar,
                baz => sub { print 'baz!' } 
                ... 
              );

Then you can call the right one easily:

$actions{$action}->();

You can also add some checking to make sure $action is a valid key in the hash, and so forth.

In general, you should avoid symbolic references (what you're doing now) as they cause all kinds of problems. In addition, using real subroutine references will work with strict turned on.

LanX
  • 478
  • 3
  • 10
friedo
  • 65,762
  • 16
  • 114
  • 184
  • 1
    I've repeated my comment here from another answer below, as this answer has by far the highest score! in case it helps anyone, if you also want to pass parameters to the subroutine, you just put them in between the () - i.e. the same as you would to a normal sub. e.g. `$actions{$action}->($parmVariable,11,'a');` The parameters would have to be consistent across all the possible sub-routines being called, though the values can come from variables of course. – Andy Lorenz Jun 05 '14 at 09:47
23

Just &$action(), but usually it's nicer to use coderefs from the beginning, or use a dispatcher hash. For example:

my $disp = {foo => \&some_sub, bar => \&some_other_sub };
$disp->{'foo'}->();
phuclv
  • 37,963
  • 15
  • 156
  • 475
Harmen
  • 669
  • 3
  • 8
  • Using the &$action solution I get: Can't use string ("main::instructors") as a subroutine ref while "strict refs". So are the gymnastics I am doing only because I have strict turned on? – Marcus Dec 16 '09 at 16:15
  • 6
    Yes, this kind of usage is what 'use strict' prevents (among others). Use 'no strict "refs"' to allow it, but again, real references or dispatchers are ususally much clearer. – Harmen Dec 16 '09 at 16:16
  • 3
    The dispatch table answer, which uses real (as opposed to symbolic) references, will of course work with `strict` turned on, which is another advantage of doing it that way. – friedo Dec 16 '09 at 16:36
  • `use strict qw(vars subs)` is a shorter way to say `use strict; no strict 'refs';` – mob Dec 16 '09 at 20:27
  • 5
    @mobrule: until someone adds a fourth category of stricture, that is :) – Ether Dec 16 '09 at 22:53
17

Huh? You can just say

    $action->()

Example:

    sub f { return 11 }
    $action = 'f';
    print $action->();


    $ perl subfromscalar.pl
    11

Constructions like

    'f'->()     # equivalent to   &f()

also work.

mob
  • 117,087
  • 18
  • 149
  • 283
  • 11
    If you `use strict` (and of course you do), you'll need to say `no strict 'refs'` or just `use strict qw(vars subs)` to use this syntax. – mob Dec 16 '09 at 20:25
12

I'm not sure I understand what you mean. (I think this is another in a recent group of "How can I use a variable as a variable name?" questions, but maybe not.)

In any case, you should be able to assign an entire subroutine to a variable (as a reference), and then call it straightforwardly:

# create the $action variable - a reference to the subroutine
my $action = \&sing_out;
# later - perhaps much later - I call it
$action->();

sub sing_out {
    print "La, la, la, la, la!\n"
}
Telemachus
  • 19,459
  • 7
  • 57
  • 79
10

The most important thing is: why do you want to use variable as function name. What will happen if it will be 'eval'? Is there a list of functions that can be used? Or can it be any function? If list exists - how long it is?

Generally, the best way to handle such cases is to use dispatch tables:

my %dispatch = (
   'addition' => \&some_addition_function,
   'multiplication' => sub { $self->call_method( @_ ) },
);

And then just:

$dispatch{ $your_variable }->( 'any', 'args' );
Telemachus
  • 19,459
  • 7
  • 57
  • 79
9
__PACKAGE__->can($action)->(@args);

For more info on can(): http://perldoc.perl.org/UNIVERSAL.html

Nehal J Wani
  • 16,071
  • 3
  • 64
  • 89
6

I do something similar. I split it into two lines to make it slightly more identifiable, but it's not a lot prettier.

my $sub = \&{$action};
$sub->();

I do not know of a more correct or prettier way of doing it. For what it's worth, we have production code that does what you are doing, and it works without having to disable use strict.

Flimm
  • 136,138
  • 45
  • 251
  • 267
Wes
  • 1,834
  • 3
  • 15
  • 26
  • 1
    and in case it helps anyone, if you also want to pass parameters to the subroutine, you just put them in between the () - i.e. the same as you would to a normal sub. e.g. `$sub->($parmVariable,11,'a');` – Andy Lorenz Jun 05 '14 at 09:41
  • To call this reflexively from within a sub; my $this = \&{(caller(0))[3]}; $return = $this->(); $return .= $this->(\@flags,$arguments,\%foo); – Alexx Roche Aug 21 '17 at 19:48
4

Every package in Perl is already a hash table. You can add elements and reference them by the normal hash operations. In general it is not necessary to duplicate the functionality by an additional hash table.

#! /usr/bin/perl -T
use strict;
use warnings;

my $tag = 'HTML';

*::->{$tag} = sub { print '<html>', @_, '</html>', "\n" };

HTML("body1");

*::->{$tag}("body2");

The code prints:

<html>body1</html>
<html>body2</html>

If you need a separate name space, you can define a dedicated package.

See perlmod for further information.

Telemachus
  • 19,459
  • 7
  • 57
  • 79
ceving
  • 21,900
  • 13
  • 104
  • 178
  • That's a long page to link to, but I think you're referring to the [Symbol Tables](http://perldoc.perl.org/perlmod.html#Symbol-Tables) section. – Teepeemm Jul 23 '17 at 23:49
1

Either use

&{\&{$action}}();

Or use eval to execute the function:

eval("$action()");
Grolim
  • 29
  • 1
1

I did it in this way:

@func = qw(cpu mem net disk);
foreach my $item (@func){
    $ret .= &$item(1);
}
Putnik
  • 5,925
  • 7
  • 38
  • 58
0

If it's only in one program, write a function that calls a subroutine using a variable name, and only have to document it/apologize once?

Dean J
  • 39,360
  • 16
  • 67
  • 93
0

I used this: it works for me.

(\$action)->();

Or you can use 'do', quite similar with previous posts:

$p = do { \&$conn;}; 
$p->();
Yang
  • 1,285
  • 1
  • 10
  • 14