2012-06-05 11 views
5

Dada una variable que contiene una cadena que representa el nombre de un paquete, ¿cómo puedo llamar a una subrutina específica del paquete?¿Cómo se llama una subrutina de un paquete, dado el nombre del paquete en Perl?

Aquí es lo más cercano que he descubierto:

package MyPackage; 

sub echo { 
    print shift; 
} 

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

1; 

El problema con este código es la subrutina se llama como un método de clase; el nombre del paquete se pasa como el primer argumento. Deseo invocar la subrutina desde el nombre del paquete sin que se haya pasado implícitamente un primer argumento especial.

+1

Si no quiere pasar a un invocante, entonces lo que quiere es no llamar a un * método *. – hobbs

+0

@hobbs: Gracias. Actualicé la pregunta para usar una terminología más correcta. – Sam

Respuesta

4

Perl llamadas a métodos son subprogramas simplemente regulares, que obtienen las invocaciones como el primer valor.

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) 

Si desea llamar a una subrutina sin invocaciones, tendrá que llamar de otra manera.

{ 
    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'); 
  • El método can devuelve una referencia a la subrutina que se llamaría si hubiera sido llamado a las invocaciones. El coderef puede usarse por separado.

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

    hay algunas advertencias para el uso de can:

    • can puede ser anulado por el paquete, o cualquier clase de la que hereda.
    • El paquete que define un método puede ser diferente al invocante.


    Este puede ser el comportamiento que estás buscando.

  • Otro enfoque es utilizar algo llamado symbolic reference.

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

    No se recomienda el uso de referencias simbólicas. Parte del problema es que es posible utilizar accidentalmente una referencia simbólica en la que no tenía la intención de usarla. Es por eso que no puede tener use strict 'refs'; vigente.

    Esta puede ser la forma más sencilla de hacer lo que quiera hacer.

  • Si no desea utilizar una referencia simbólica, puede utilizar el 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'); 
    

    El único problema con esto es que usted tiene que dividir en $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'); 
    

    Esto no es tan grande de un problema. Después de un rápido vistazo al CPAN, encontrará Package::Stash.

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

    (La versión Pure Perl de Package::Stash utiliza referencias simbólicas, no el alijo)


Es incluso posible hacer un alias de la subrutina/método, como si había sido importado de un módulo que estaba usando Exporter:

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

lo haría r ecommend limitar el alcance del alias embargo:

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

Ésta es una excepción, donde se puede utilizar una referencia simbólica con strict 'refs' habilitado.

+0

Gracias por la respuesta completa. Lo acepté porque me brindó una variedad de formas de hacer lo que le pedí. Me gusta particularmente el enfoque 'puedo'. – Sam

+1

@Sam, 'can' es incorrecto. Comprueba el árbol de herencia. 'can' es para métodos. – ikegami

8

Parece que no quiere realmente llamarlo como un método, sino como una subrutina normal. En ese caso, se puede utilizar una referencia simbólica:

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

+1 para "no estrictas 'referencias';" precision – DVK

+2

Si usa '$ :: {$ package_name. '::'} {echo}' no necesita usar 'no strict 'refs;;' –

1

utilizar la sintaxis &{ <EXPRESSION> }() de llamar a un sub cuyo nombre es la expresión, como se discutió en perldoc perlref cuando se enumeran los operadores de eliminación de referencias:

Obviamente, es un poco tonto para utilizar el curlies en este caso, pero el bloque puede contener cualquier expresión arbitraria, en particular, expresiones subindicadas:

&{ $dispatch{$index} }(1,2,3); # call correct routine 

aleatoria ejemplo práctico:

# Note no "use strict"! 
use File::Slurp; 
my $p="File::Slurp"; 
@a=&{"${p}::read_file"}(".profile"); 
print $a[0]; 
Cuestiones relacionadas