0

I'm creating a dispatch table:

  my $dispatch = { 
    'do_this' => \&do_this,
    'do_that' => \&do_that,
    'do_something' => \&do_something,
    'do_something_else' => \&do_something_else,
  };

Instead of typing in the same string of chars for the key and the value, I'd like to do this:

my $dispatch_values = ['do_this', 'do_that', 'do_something', 'do_something_else'];
my $dispatch = generate_dispatch_table($dispatch_values);

sub generate_dispatch_table {
  my $values = shift;
  my $table = {};
  foreach $value (@$values) {
    $table{$value} = #WHAT GOES HERE?
  }
  return $table;  
}

I don't know how to generate a subroutine reference from a string, though.

2 Answers 2

4

Just use \&{ $sub_name }:

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

sub hi  { print "Hi\n"  }
sub bye { print "Bye\n" }

my %dispatch = map { $_, \&{$_} } qw(hi bye);

chomp(my $action = <>);
$dispatch{$action}->();
Sign up to request clarification or add additional context in comments.

Comments

1

Alternatives include:

  • use an object.
  • use a package.

For an object, it's pretty much exactly what you're used to:

#! /usr/bin/perl

package Foo;
use warnings;
use strict;

sub hi  { print "Hi\n"  }
sub bye { print "Bye\n" }

sub new { bless {} }

package main;

my $dispatcher = Foo->new;
chomp(my $action = <>);
$dispatcher->$action();

Of course, one should check if you can do the action, but we're omitting some basic checks here.

Another good check is to not use the action as is, but to use a prefix that indicates it's dispatchable in case you have other non-dispatch methods in the object:

#! /usr/bin/perl

package Foo;
use warnings;
use strict;

sub do_hi  { print "Hi\n"  }
sub do_bye { print "Bye\n" }

sub new { bless {} }

package main;

my $dispatcher = Foo->new;
chomp(my $action = <>);
$action = "do_" . $action;
$dispatcher->$action();

The only difference is the do_ prefix, but now the caller can't call new through the dispatcher. Otherwise, it's the same - this dispatcher will dispatch hi and bye like choroba's answer.

Remember, of course, that $self is the first parameter, if you're passing in parameters at all.

Doing this via packages is almost the same:

#! /usr/bin/perl

package Foo;
use warnings;
use strict;

sub do_hi  { print "Hi\n"  }
sub do_bye { print "Bye\n" }

package main;

chomp(my $action = <>);
$action = 'do_' . $action;
Foo->$action();

Here, the first parameter is, of course, "Foo". We also don't need an object, so no constructor required.

However, you can take this and apply it directly to your original question and avoid some of the extra sigils. Just remove the package declarations, and change Foo->$action() to __PACKAGE__->$action() even in the default (main) package. But, if you don't want to have the package name being passed in, we take this just a tiny step further:

sub do_hi  { print "Hi\n"  }
sub do_bye { print "Bye\n" }

chomp(my $action = <>);
$action = 'do_' . $action;
__PACKAGE__->can($action)->();

TMTOWTDI. Pick the one that makes the most sense to you and your code layout. Sometimes I use the object model, sometimes another one.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.