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

$ARGV[0] is my subroutine name, and $ARGV[1] it's arg. The sub is a method in a class and I blessed object $A with new(), that class has a sub with a name contained in $ARGV[0].

I researched using a variable as a sub name and found may examples with syntax like \$sub , &$sub , \&$sub, "do", "eval", even a seemingly unusual &{\&{$sub}}();

I can get some of these to work on a local sub, but not as part of my class object $A , trying syntax using those examples, like:

%$A->$ARGV[0]( $ARGV[1] ) $A->$ARGV[0]( $ARGV[1] ) %{$A->$ARGV[0]( $ARGV[1] ) } eval "$A->$ARGV[0]( $ARGV[1] )"; eval "{$A->$ARGV[0]( $ARGV[1] ) };" $A->\$ARGV[0]( $ARGV[1] )
and like 27 other varieties. Can you (Rolf of course) suggest a syntax that works here?

TY!

Replies are listed 'Best First'.
Re: How do I call a sub using a variable as its name, objectively
by choroba (Cardinal) on Oct 28, 2024 at 19:20 UTC
    It's in fact much easier. Use a variable to keep the method name:
    #!/usr/bin/perl use warnings; use strict; { package My; sub new { bless {}, shift } sub show { my ($self, $name) = @_; print "Hello $name!\n" } } my $A = 'My'->new; my $method = shift; $A->$method(@ARGV);

    Using it:

    $ 1.pl show World Hello World!

    Otherwise, you need to do a reference/dereference trick:

    $A->${\ $ARGV[0] }(@ARGV[1 .. $#ARGV]);

    Update: Fixed arguments for the second example. Thaks etj.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      To be exactly equivalent, that last one would need to be: $A->${\ $ARGV[0] }(@ARGV[1..$#ARGV]);
Re: How do I call a sub using a variable as its name, objectively
by GrandFather (Saint) on Oct 28, 2024 at 20:39 UTC

    Choroba showed you how to do it, however allowing calls to arbitrary functions can be a bit of a security hole. To get around that I usually decorate the sub name after the user has provided it:

    use strict; use warnings; package My; sub new { my ($class) = @_; return bless {}, $class; } sub ext_show { my ($self, $name) = @_; print "Hello $name!\n"; return 1; } package main; my $A = 'My'->new(); my $method = 'ext_' . shift; $A->$method(@ARGV);

    Prints:

    Hello Bob!

    when given the command line parameters show Bob. Because 'ext_' is added a prefix to the actual name it's not possible for the user to call an unintended sub.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      Or use a whitelist:
      my $A = My::->new(); my $method = $method_list{$ARGV[0]} or die "Invalid method $ARGV[0]."; $A->$method(@ARGV[1..$#ARGV]);
      --
      A math joke: r = | |csc(θ)|+|sec(θ)| |-| |csc(θ)|-|sec(θ)| |

        I have done that in the past, but the list, the implementation and the documentation all have to be updated. Using the prefix trick avoids the need to update the list. I admit, a fairly minor point, but it does reduce scope for error.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: How do I call a sub using a variable as its name, objectively
by ikegami (Patriarch) on Oct 28, 2024 at 22:48 UTC

    Calling a sub:

    my $sub = \&$name; $sub->( @args )

    Optionally, you can add the following:

    die "No sub named `$name`" if !defined( &$sub );

    Note that \&$name will create an undefined sub if it doesn't already exists.


    Calling a method:

    $invocant->$name( @args )

    Calling a method (alternative):

    my $method = $invocant->can( $name ); $invocant->$method( @args )

    Optionally, you can add the following to the alternative approach:

    die "No method named `$name`" if !$method;

    Strict refs prevents none of these.

Re: How do I call a sub using a variable as its name, objectively
by SankoR (Prior) on Oct 28, 2024 at 19:23 UTC
    use strict; use warnings; package Acme::Bad::Idea { sub new { bless {}, shift } sub do_not_do_this { warn shift; ...; } }; my $bad_idea = Acme::Bad::Idea->new(); # @ARGV = qw[do_not_do_this seriously]; $bad_idea->can( $ARGV[0] )->( $ARGV[1] );
    Yuck.