http://www.perlmonks.org?node_id=742734

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

Hey Monks, Can someone give me some guidance as to a sound way to invoke a named sub without hardcoding it's name in the calling program? My situation is that I will have many subroutines each of which take the same input paramters and are arbitarily complex. I want these subs to be named, defined and stored externally to the calling program as perl modules. I will use them from other programs in the future. I will be reading the sub routine name to execute from a file. I don't even want my program to know the name of the sub as new subs may be added and I want this pgm to execute what it's been told to without change. I've been reading and I can see how to do it with anonymous subs but they seem more suited to short subs. My problem is that there seems to be enough different ways I'm confused and can't see the right path to find the spiritual fulfillment of indirectedness and avoid descent into the valley of hardcoding. Thanks

Replies are listed 'Best First'.
Re: dynamic perl calls
by Corion (Patriarch) on Feb 10, 2009 at 12:02 UTC

    It seems to me that you basically want something like App::Sequence. You could look at how it does what it does. Personally, I'd use a separate namespace for all user commands, but you can also use the main:: namespace and call the functions in it:

    #!perl -w use strict; sub hello { print "Hello $_\n" for @_ }; sub bye { print "Goodbye $_\n" for @_ }; my $namespace = \%main::; my ($command,@args) = @ARGV; if (! exists $namespace->{$command}) { warn "Unknown command '$command'. Valid commands are:\n"; for (sort keys %$namespace) { if (defined *{$namespace->{$_}}{CODE}) { warn "$_\n"; }; }; die "$0 stopped.\n"; } else { my $code = $namespace->{$command}; $code->(@args); }; __END__ Q:\>perl -w tmp.pl x Unknown command 'x'. Valid commands are: bye hello tmp.pl stopped. Q:\>perl -w tmp.pl hello Foo Hello Foo Q:\>perl -w tmp.pl bye Foo Goodbye Foo Q:\>
      Thanks Moritz and Corion. App::Sequence is very close to what I want. I'll study your suggestions and learn. It does help a lot to know the right way to head. Much appreciated.
Re: dynamic perl calls
by moritz (Cardinal) on Feb 10, 2009 at 11:55 UTC
    So you might want to use something like this:
    $ perl -wle 'sub f { print @_ }; my $x = "f"; $x->(3)' 3

    If you work under use strict; (which is always recommended), you need no strict 'refs'; in the scope in which you do this call.

Re: dynamic perl calls
by osunderdog (Deacon) on Feb 10, 2009 at 13:39 UTC

    It sounds like you want to do polymorphism. This is a quick example that sounds like a starting point for you.

    child1.pm

    package child1; sub polyexample { my $class = shift; my $param1 = shift; my $param2 = shift; print "polyexample (child1) called with [$param1] and [$param2]\n" +; } 1;

    child2.pm

    package child2; sub polyexample { my $class = shift; my $param1 = shift; my $param2 = shift; print "polyexample (child2) called with [$param1] and [$param2]\n" +; } 1;

    main.pl

    use strict; use Carp; my $class = shift; my $method = shift; my $param1 = shift; my $param2 = shift; eval("use $class"); if($@) { croak("Couldn't find package $class ($@) "); } eval($class->$method($param1, $param2)); if($@) { croak ("couldn't invoke method #method on $class with params ($@)" +) }

    Finally, here are the two example invocations:

    ~/src/perl/poly$ perl main.pl child1 polyexample 1 2 polyexample (child1) called with [1] and [2]
    ~/src/perl/poly$ perl main.pl child2 polyexample 1 2 polyexample (child2) called with [1] and [2]

    and an example failure

    ~/src/perl/poly$ perl main.pl child5 polyexample 1 2 Couldn't find package child5 (Can't locate child5.pm in @INC (@INC con +tains: /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.1 +0.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/per +l/5.10 /usr/local/lib/site_perl .) at (eval 1) line 2. BEGIN failed--compilation aborted at (eval 1) line 2.

    And many wonderful permutations of this exist.

    Still searching; Still seeking.

      That was my thought as well. This could also be combined with Module::Pluggable to deal with the external files aspect.

      Method calls are not subject to strict subs. So you can do:

      use strict; use warnings; my $sub = 'foo'; my @result_set = Dispatch->$sub( qw/bar baz/ ); package Dispatch; sub foo { shift; print @_,"\n"; }


      TGI says moo

Re: dynamic perl calls
by pileofrogs (Priest) on Feb 10, 2009 at 18:36 UTC

    If you use a hash of subroutine references you can then use the names as keys in the hash.

    #! /usr/bin/perl -w -T use strict; my %subs = ( foo => sub { print "Foo!\n" }, bar => sub { print "Bar!\n" }, ); while ( my $sub_name = <DATA> ) { chomp($sub_name); if ( defined($subs{$sub_name}) ) { &{$subs{$sub_name}}; } } __DATA__ foo bar
Re: dynamic perl calls
by romandas (Pilgrim) on Feb 11, 2009 at 09:36 UTC

    I cannot assist you with your question (I am not wise in the way you seek), but humbly suggest better formatting of your post next time; large blocks of text make for difficult reading.

    Please see "Long Paragraphs" in Writeup Formatting Tips.

Re: dynamic perl calls
by Anonymous Monk on Feb 11, 2009 at 13:28 UTC
    Maybe you're deliberately trying to avoid OO, in which case this won't help much, but would you be better off following a Factory class pattern? In other words, define your many subs as classes with a common entry method name (but which exhibit different behaviours), and you can have an action/class-type mapping in your config. See Class::Factory on the CPAN for an implementation..