atemerev has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I'd like to call some named method on some Perl object via its normal direct method call syntax. Like this:

my $doomsday_weapon = Weapon::Doomsday->new; my $method = 'destroy_the_world_with'; my @args = qw(WMD, PHP, NSA); eval("\$doomsday_weapon->$method(" . join(', ', @args) .")");

Is there a way to do this somewhat more elegantly and without the unsafe eval()?

Replies are listed 'Best First'.
Re: Calling a method by name without eval()
by FunkyMonk (Bishop) on Mar 23, 2008 at 17:24 UTC
    Like this, you mean?
    package Foo; sub new { bless {}, shift }; sub method { shift; return shift() + shift() } package main; my $foo = Foo->new; my $method = "method"; my @args = ( 2, 3 ); print $foo->$method( @args );

    Or am I missing something?

      Yes, right! Thank you.
Re: Calling a method by name without eval()
by whereiskurt (Friar) on Mar 23, 2008 at 17:44 UTC

    atemerev

    I'm away from a perl interpreter right now so this little snippet (from the the Perl Cookbook, recipe 11.4) will just have to do:

    my %commands = ( "happy" => \&joy, "sad" => \&sullen, "done" => sub { die "See ya!" }, "mad" => \&angry, ); print "How are you? "; chomp($string = <STDIN>); if ($commands{$string}) { $commands{$string}->(); } else { print "No such command: $string\n"; }

    The idea here is to store your function/method references in a hash (%commands). Then you call the method by looking it up in the hash, and passing in the params. I don't remember the exact context for this, but, most of the time when you're using eval'd strings or "soft references" you can use a hash to achieve the same results.

    Good luck with it!

    Kurt

      That's definitely a useful technique, but beware that these aren't methods: they don't go through method dispatch at all. You don't get polymorphism this way, unless you use a double-dispatch scheme.

Re: Calling a method by name without eval()
by perrin (Chancellor) on Mar 23, 2008 at 18:27 UTC
    It should just work. This is supported: $doomsday_weapon->$method(@args);
Re: Calling a method by name without eval()
by ikegami (Patriarch) on Mar 23, 2008 at 23:11 UTC

    If you wanted to make sure the method exists you could trap errors with the other form of eval (called "try" in other languages):

    my $rv; if (!eval { $rv = $doomsday_weapon->$method(@args); 1 }) { die("Doomsday weapon failed: $@\n"); }

    Or you could use can:

    my $method_ref = $doomsday_weapon->can($method) or die("Method $method not supported by this doomsday weapon\n"); $method(@args); 1 }) { die("Doomsday weapon failed: $@\n"); } my $rv = $doomsday_weapon->$method_ref(@args);