1

I working with a bunch of packages that shares the same interface. I need to execute the same subroutine on different class names and want to make it dynamic, i.e. something like in this example:

#!/usr/bin/env perl

use Modern::Perl;
use Data::Dumper;

use Mod1;
use Mod2;

my $mod = $ARGV[0];
my $meth = $ARGV[1];

${mod}::some_sub;
${mod}::${meth};

I need to call exactly subroutine and not a class' method. How I can achieve this? When I'm executing script above from CLI with arguments 'Mod1 some_sub' I'm getting script execution error with next message:

Bad name after :: at ./test.pl line 13.

or

Bareword found where operator expected at ./test.pl line 12, near "${mod}::some_sub"
    (Missing operator before ::some_sub?)
syntax error at ./test.pl line 12, near "${mod}::some_sub"
Execution of ./test.pl aborted due to compilation errors.

for last 2 lines

Mod1.pm looks like this:

package Mod1;

use Modern::Perl;
use Data::Dumper;

sub some_sub {
    say Dumper(\@_);
    say 'in some_meth';
}

1;

And Mod2.pm code is next:

package Mod2;

use Modern::Perl;
use Data::Dumper;

sub other_meth {
    say Dumper(\@_);
    say 'other';
}

1;
  • You seem looking for [`eval`](http://perldoc.perl.org/functions/eval.html). With `eval` you can execute arbitrary strings as Perl code. – PerlDuck Mar 24 '16 at 17:42
  • Is it possible to do without eval? – Nikita Tropin Mar 24 '16 at 17:47
  • 2
    @PerlDog That would be pretty dangerous since the OP is accepting arguments from the command line. A dispatch table (and possibly AUTOLOAD) would be better. – ThisSuitIsBlackNot Mar 24 '16 at 17:47
  • @ThisSuitIsBlackNot Good point; I didn' think about that. I focused on how to run arbitrary code. I'm currently writing an answer. If I'll post it, I'll consider that. – PerlDuck Mar 24 '16 at 17:51
  • 2
    See [How can I elegantly call a Perl subroutine whose name is held in a variable?](http://stackoverflow.com/q/1915616/176646) – ThisSuitIsBlackNot Mar 24 '16 at 18:07
  • @ThisSuitIsBlackNot I can't build dispatch table because I can't get reference to sub in different package when package name is in variable. Is it possible without eval? – Nikita Tropin Mar 24 '16 at 18:53
  • You don't build a dispatch table dynamically with user input, so it doesn't matter that the package name is in a variable: `my %dispatch = ('Foo::bar' => \&Baz::qux);` – ThisSuitIsBlackNot Mar 24 '16 at 19:00

1 Answers1

0
$main::{"${mod}::"}{some_sub}();

$main::{"${mod}::"}{$meth}();

%Mod1:: is a top level hash but is also a key/value in %main:: so we just need to make up the key and index the hash. No eval() needed.

EDIT

I tried a deeply nested package example and the same basic idea seems to work:

% perl
use ExtUtils::CBuilder::Platform::darwin;
$nmz = 'darwin';
$ExtUtils::CBuilder::Platform::{"${nmz}::"}{compile}();
Can't call method "SUPER::compile" on unblessed reference at /System/Library/Perl/5.16/ExtUtils/CBuilder/Platform/darwin.pm line 18.

The error is due to my not calling the function with what it expects, but the point is I managed to call the function.

cdlane
  • 40,441
  • 5
  • 32
  • 81
  • Cool approach but that was an example script and I can't apply it in a real code. Can you help? I have such line of code in LendingClub package: `$nmzed->$field( $Wrapper::LendingClub::{"${nmz}::"}{from_string}($lc->{$lc_field}) );` and it fails to execute with message _"Can't use an undefined value as a subroutine reference at lib/Wrapper/LendingClub.pm line 140."_ $nmz contains correct package name. – Nikita Tropin Mar 24 '16 at 18:41
  • I got an idea, thanks – Nikita Tropin Mar 24 '16 at 19:18