How do you call a routine from a package, given the package name in Perl?

Given a variable containing a string that represents the name of the package, how do I call a specific package routine?

Here is the closest I found out:

package MyPackage;

sub echo {
    print shift;
}

my $package_name = 'MyPackage';
$package_name->echo('Hello World');

1;

The problem with this code is a subroutine called a class method; the package name is passed as the first argument. I want to call a routine from the package name without special input of the first argument.

+5
source share
3 answers

The Perl method call is just regular routines that get invocant as their 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 routine without 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');
  • can , , invocant. coderef .

    my $code_ref = $package_name->can('echo');
    $code_ref->('Hello World');
    

    can:

    • can , .
    • , , , invocant.


    , .

  • .

    {
      no strict 'refs';
      &{ $package_name.'::echo' }('Hello World');
    }
    

    . , , . use strict 'refs';.

    , , .

  • , 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');
    

    , $package_name ::

    *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');
    

    . CPAN Package:: Stash.

    use Package::Stash;
    my $stash = Package::Stash->new($package_name);
    my $coderef = $stash->get_symbol('&echo');
    $coderef->('Hello World');
    

    ( Pure Perl :: Stash , Stash)


/, , Exporter:

*echo = \&{$package_name.'::echo'};
echo('Hello World');

:

{
  local *echo = \&{$package_name.'::echo'};
  echo('Hello World');
}

, strict 'refs'.

+4

, , . :

my $package_name = 'MyPackage';
{ 
    no strict 'refs';
    &{ $package_name . '::echo' }( 'Hello World' );
}
+8

Use the syntax &{ <EXPRESSION> }()to call sub, whose name is an expression, as described in the perldoc perlreflisting of dereference operators:

Admittedly, it's a little silly to use curls in this case, but BLOCK can contain any arbitrary expression , in particular indexed 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];
+1
source

All Articles