Given a variable containing a string that represents the name of a package, how do I call a specific subroutine of the package?
Here's the closest thing I have figured out:
package MyPackage;
sub echo {
print shift;
}
my $package_name = 'MyPackage';
$package_name->echo('Hello World');
1;
The problem with this code is the subroutine is called as a class method; the package name is passed in as the first argument. I want to invoke the subroutine from the package name without a special first argument being implicitly passed in.
It sounds like you don't want to actually call it as a method, but as a regular subroutine. In that case, you can use a symbolic reference:
my $package_name = 'MyPackage';
{
no strict 'refs';
&{ $package_name . '::echo' }( 'Hello World' );
}
Perl method calls are just regular subroutines, which get the invocant as the first value.
use strict;
use warnings;
use 5.10.1;
{
package MyPackage;
sub new{ bless {}, shift } # overly simplistic constructor (DO NOT REUSE)
sub echo{ say @_ }
}
my $package_name = 'MyPackage';
$package_name->echo;
my $object = $package_name->new();
$object->echo; # effectively the same as MyPackage::echo($object)
MyPackage
MyPackage=HASH(0x1e2a070)
If you want to call a subroutine without an invocant, you will need to call it differently.
{
no strict 'refs';
${$package_name.'::'}{echo}->('Hello World');
&{$package_name.'::echo'}('Hello World');
}
# only works for packages without :: in the name
$::{$package_name.'::'}{echo}->('Hello World');
$package_name->can('echo')->('Hello World');
The can
method returns a reference to the subroutine that would be called if it had been called on the invocant. The coderef can then be used separately.
my $code_ref = $package_name->can('echo');
$code_ref->('Hello World');
There are some caveats to using can
:
can
may be overridden by the package, or any class from which it inherits.
This may actually be the behaviour you're looking for though.
Another approach is to use something called a symbolic reference.
{
no strict 'refs';
&{ $package_name.'::echo' }('Hello World');
}
Using symbolic references is usually not recommended. Part of the problem is that it is possible to accidently use a symbolic reference where you didn't intend on using one. This is why you can't have use strict 'refs';
in effect.
This may be the simplest way to do what you want to do though.
If you don't want to use a symbolic reference you could use the Stash.
$MyPackage::{echo}->('Hello World');
$::{'MyPackage::'}{echo}->('Hello World');
$main::{'MyPackage::'}{echo}->('Hello World');
$main::{'main::'}{'MyPackage::'}{echo}->('Hello World');
$main::{'main::'}{'main::'}{'main::'}{'MyPackage::'}{echo}->('Hello World');
The only problem with this is that you would have to split $package_name
on ::
*Some::Long::Package::Name::echo = \&MyPackage::echo;
$::{'Some::'}{'Long::'}{'Package::'}{'Name::'}{echo}('Hello World');
sub get_package_stash{
my $package = shift.'::';
my @package = split /(?<=::)/, $package;
my $stash = \%:: ;
$stash = $stash->{$_} for @package;
return $stash;
}
get_package_stash('Some::Long::Package::Name')->{echo}('Hello World');
This isn't that big of a problem though. After a quick look on CPAN you find Package::Stash.
use Package::Stash;
my $stash = Package::Stash->new($package_name);
my $coderef = $stash->get_symbol('&echo');
$coderef->('Hello World');
(The Pure Perl version of Package::Stash uses symbolic references, not the Stash)
It's even possible to make an alias of the subroutine/method, as if had been imported from a module that was using Exporter:
*echo = \&{$package_name.'::echo'};
echo('Hello World');
I would recommend limiting the scope of the alias though:
{
local *echo = \&{$package_name.'::echo'};
echo('Hello World');
}
This is an exception, where you can use a symbolic reference with strict 'refs'
enabled.
Use the &{ <EXPRESSION> }()
syntax of calling a sub whose name is the expression, as discussed in perldoc perlref
when listing dereferencing operators:
Admittedly, it's a little silly to use the curlies in this case, but the BLOCK can contain any arbitrary expression, in particular, subscripted expressions:
&{ $dispatch{$index} }(1,2,3); # call correct routine
Random practical example:
# Note no "use strict"!
use File::Slurp;
my $p="File::Slurp";
@a=&{"${p}::read_file"}(".profile");
print $a[0];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With