2010-07-28 9 views
6

me he encontrado con el siguiente patrón de un par de veces, mientras que el desarrollo de módulos de Perl que utilizan AUTOLOAD u otras técnicas subrutina de despacho:¿Cómo localizar una variable en un ámbito superior en Perl?

sub AUTOLOAD { 
    my $self = $_[0]; 

    my $code = $self->figure_out_code_ref($AUTOLOAD); 

    goto &$code; 
} 

Esto funciona bien, y caller ve el alcance correcto.

Ahora lo que me gustaría hacer es establecer $_ localmente igual a $self durante la ejecución de &$code. ¿Cuál sería algo como esto:

sub AUTOLOAD { 
    my $self = $_[0]; 

    my $code = $self->figure_out_code_ref($AUTOLOAD); 

    local *_ = \$self; 

    # and now the question is how to call &$code 

    # goto &$code; # wont work since local scope changes will 
        # be unrolled before the goto 

    # &$code; # will preserve the local, but caller will report an 
       # additional stack frame 
} 

Soluciones que conlleva incluir caller no son aceptables debido a problemas de rendimiento y de dependencia. Entonces eso parece descartar la segunda opción.

Volviendo a la primera, la única manera de evitar que el nuevo valor de $_ de ir fuera de alcance durante el goto sería o bien no localizar el cambio (no es una opción viable) o para implementar algún tipo de uplevel_local o goto_with_local.

He jugado un poco con todo tipo de permutaciones que implican PadWalker, Sub::Uplevel, Scope::Upper, B::Hooks::EndOfScope y otros, pero no han sido capaces de llegar a una solución robusta que limpia $_ en el momento adecuado, y no se ajusta caller .

¿Alguien ha encontrado un patrón que funcione en este caso?

(la pregunta SO: How can I localize Perl variables in a different stack frame? está relacionado, pero conservando caller no era un requisito, y en última instancia la respuesta que había que usar un enfoque diferente, por lo que la solución no es útil en este caso)

+0

Seré honesto que estoy un poco perdido aquí. pero podrías hacer nuestro $ _; ? así que tienes acceso a $ _ localmente pero también acceso a ...$ Package :: $ _ en serio aunque esto está por encima de mi alcance del cerebro – xenoterracide

+0

@xenoterracide => No estoy seguro de a dónde va con la idea del alcance del paquete, pero '$ _' (y la mayoría de las otras variables de puntuación) son comunes en todos los paquetes y no pueden ser del mismo tamaño con 'our'. el léxico '$ _' (' my $ _; ') es una bestia completamente diferente, pero eso está más allá del alcance de esta pregunta, perdón por el juego de palabras –

+0

, no estoy seguro de ir a ninguna parte con él. pero Perl no grita acerca de "nuestro $ _;' así que estoy asumiendo que $ _ puede verse afectado con eso también; tal vez asumiendo mal sin embargo. Si tuviera una respuesta real, habría respondido de esa manera. – xenoterracide

Respuesta

1

Sub :: uplevel parece funcionar - al menos por un simple caso que no impliquen AUTOLOAD:

use strict; 
use warnings; 
use Sub::Uplevel; 

$_ = 1; 
bar(); 

sub foo { 
    printf "%s %s %d - %s\n", caller, $_ 
} 

sub bar { 
    my $code = \&foo; 
    my $x = 2; 
    local *_ = \$x; 
    uplevel 1, $code; 
} 

La salida es:

main c:\temp\foo.pl 6 - 2 

De acuerdo, esto no localiza realmente una variable en el alcance principal, pero no creo que realmente quiera hacer eso aunque pueda. Solo desea localizar $_ durante la duración de la llamada.

+0

'Sub :: Uplevel' hará el trabajo de engañar a 'quien llama' pero yo no estoy loco por la forma en que lo hace (envolviendo y localizando 'CORE :: GLOBAL :: caller'). Vea la sección BUGS/CAVEATS de 'Sub :: Uplevel' para más detalles. –

+0

Ah, he (mal) leí tu pregunta como "Sub :: Uplevel no funcionó" en lugar de que la hubieras descalificado debido a cómo funcionó. –

1

La documentación perlfunc para goto señala (énfasis añadido)

La forma goto-&NAME es bastante diferente de las otras formas de "goto". De hecho, no es un goto en el sentido normal en absoluto, y no tiene el estigma asociado con otros gotos. En cambio, cuando sale de la subrutina actual (perdiendo los cambios establecidos por local ...)

¿Qué tipo de problemas de rendimiento permiten el direccionamiento indirecto a través de la auto-carga, pero no a través de un envoltorio?

+0

mis preocupaciones sobre el rendimiento son más para el código que no estoy escribiendo (usuarios del módulo). No puedo decir si usarán 'calller' o código que use' calller' extensivamente, donde el impacto en el rendimiento de ir de un C incorporado a un sub de userland puede ser significativo. Dado que el objetivo final es solo la sutileza sintáctica de tener '$ _' establecido para ti, es difícil justificar los cambios en el espacio de nombres' CORE :: GLOBAL' que podría causar regresiones de rendimiento. –

+0

Supongo que mi problema final es que no estoy de acuerdo con la decisión de diseño de que los locales deben desenrollarse antes del 'goto', porque parece reducir innecesariamente la potencia de la construcción' goto- & NAME'. Yo preferiría que 'goto- & NAME' adoptara el pad local del sitio de llamadas, y luego revierte los locales al final de la ejecución de' & NAME'. Probablemente también sea más rápido que hacer estallar y empujar la pila del osciloscopio cada vez. –

Cuestiones relacionadas