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

Hi monks, This is my code
#!/usr/local/bin/perl use strict; use warnings; my $s = "res"; $s(); sub res { print "hai"; }
Using the variable i need to call the subroutine how can i do this i am asking this for sample. And here i have an hash key with value names as subroutines. If any one help it's helpful for me to continue.

Replies are listed 'Best First'.
Re: How to call subroutines using variables ?
by Joost (Canon) on Mar 22, 2007 at 22:49 UTC
      However AM said that they had a hash of sub names, possibly from a external library. If you cannot convert the hash of subroutine names to a hash of code references  $s{key}=\&valsub; then use a bare BLOCK to turn strict off only when calling subs from that hash:
      { # Get an enclosing lexical scope no strict 'refs'; # allow symbolic refrence shenanigains &$SubHash{key}(); # Call sub } # Back to strict 'refs'
      or write a sub for this purpose (This assumes $SubHash is a global)
      CallFromHash { no strict 'refs'; # allow symbolic refrence shenanigains return &$SubHash{shift(@_)}(@_); #Call the sub in the first arg with + the rest of @_ as args and return }
      However a hash of sub names is probably a bad idea to start with, and you should reconsider your design to use hard refs (mentioned by Joost) or remove the hash all together if possible.
      Update: Forgot Amperstands

      I tried:

      my @all_subs = (\&sub_1,\&sub_2); $all_subs[1]->();

      which worked fine. But in CGI::Application I'm using $self->, as in:

      $self->$all_subs[1]->();

      but got a syntax error (doesn't like the 2nd arrow operator). How can I get around this? Thanks.

      Update: fixed some punctuation


      —Brad
      "The important work of moving the world forward does not wait to be done by perfect men." George Eliot

        which worked fine. But in CGI::Application I'm using $self->, as in:

        $self->$all_subs[1]->();

        Use:

        my $code=$all_subs[1]; $self->$code();

        This will call $code as a method on $self, which AIUI is what you want. If you would like to do the same without an intermediate variable, then I wondered too and it turns out to be possible, albeit in a somewhat convoluted way.

Re: How to call subroutines using variables ?
by Fletch (Bishop) on Mar 22, 2007 at 23:00 UTC

    And do a search or super search for "dispatch table".

Re: How to call subroutines using variables ?
by saintly (Scribe) on Mar 22, 2007 at 23:00 UTC
    I agree with Joost, you should probably use function references as he suggests. However, there's a couple ways to do what you want if you are stuck with a value that is a function name. Cheap way 1:
    eval "&$s()";
    Or you could try:
    &{ &UNIVERSAL::can('main', $s) }();
    To rewrite the second example to make it nicer:
    my $funcRef = &UNIVERSAL::can('main', $s); if( ref($funcRef) eq 'CODE' ) { &$funcRef(); } else { warn "Function $s not found in package main!"; }
    (update: should probably check the return from 'can' to ensure it returns a function reference)
      eval "&$s()";
      Oof. Don't do that. It's a lot safer to turn of strict refs instead:
      no strict 'refs'; $s->(); use strict;
      That should still be treated with care, though. It will run ANY subroutine (even in other packages) that can be reached by name. Using your eval STRING option will run ANY code that happens to be in $s. Like:

      $s = "print(); unlink '/etc/passwd'; s";
      updated: fixed quoting
        what if function $s is defined another perl module. Even though i include the perl module .I get this error Undefined subroutine &main::hello

      It's a lot cleaner to use:

      my $func_ref = main->can( $s );

      Checking that $func_ref is true should be sufficient. (Checking ref doesn't always work.)

Re: How to call subroutines using variables ?
by shigetsu (Hermit) on Mar 22, 2007 at 23:06 UTC
    I second what Joost said, but for the sake of completeness:

    Change

    $s();

    to

    no strict 'refs'; &$s();

    to have it work with symbolic references. Should you be yearning to dynamically dispatch subs, that may be of some use.