Tomemos algo muy simple,
# Foo.pm
package Foo {
my $baz = bar();
sub bar { 42 }; ## Overwrite this
print $baz; ## Before this is executed
}
¿Hay alguna forma de que pueda test.pl
ejecutar el código que cambia lo que $baz
está configurado y hace Foo.pm
que se imprima algo más en la pantalla?
# maybe something here.
use Foo;
# maybe something here
¿Es posible con las fases del compilador forzar la impresión anterior 7
?
perl
compilation
Evan Carroll
fuente
fuente
Foo::bar
, perouse Foo
ejecutará tanto la fase de compilación (redefiniendo la barra si algo se definió previamente allí) como la fase de tiempo de ejecución de Foo. Lo único que se me ocurre sería un@INC
gancho profundamente hacky para modificar cómo se carga Foo.require
(y por louse
tanto) compila y ejecuta el módulo antes de regresar. Lo mismo vale paraeval
.eval
no se puede usar para compilar código sin ejecutarlo también.Respuestas:
Se requiere un hack porque
require
(y por louse
tanto) compila y ejecuta el módulo antes de regresar.Lo mismo vale para
eval
.eval
no se puede usar para compilar código sin ejecutarlo también.La solución menos intrusiva que he encontrado sería anular
DB::postponed
. Esto se llama antes de evaluar un archivo requerido compilado. Desafortunadamente, solo se llama cuando se depura (perl -d
).Otra solución sería leer el archivo, modificarlo y evaluar el archivo modificado, algo así como lo siguiente:
Lo anterior no se establece correctamente
%INC
, desordena el nombre de archivo utilizado por las advertencias y demás, no llamaDB::postponed
, etc. La siguiente es una solución más sólida:Usé
UNITCHECK
(que se llama después de la compilación pero antes de la ejecución) porque antepuse la anulación (usandounread
) en lugar de leer todo el archivo y agregar la nueva definición. Si desea utilizar ese enfoque, puede obtener un identificador de archivo para volver utilizandoFelicitaciones a @Grinnz por mencionar
@INC
ganchos.fuente
Dado que las únicas opciones aquí serán muy extravagantes, lo que realmente queremos aquí es ejecutar código después de que se haya agregado la subrutina al
%Foo::
alijo:fuente
Esto emitirá algunas advertencias, pero imprime 7:
Primero, definimos
Foo::bar
. Su valor será redefinido por la declaración en Foo.pm, pero se activará la advertencia "Subrutina Foo :: barra redefinida", que llamará al controlador de señal que redefine la subrutina nuevamente para devolver 7.fuente
perl -w
.Aquí hay una solución que combina el enganche del proceso de carga del módulo con las capacidades de creación de solo lectura del módulo Readonly:
fuente
He revisado mi solución aquí, para que ya no dependa de ella
Readonly.pm
, después de enterarme de que me había perdido una alternativa muy simple, basada en la respuesta de m-conrad , que he reelaborado en el enfoque modular que había comenzado aquí.Foo.pm ( igual que en la publicación de apertura )
OverrideSubs.pm Actualizado
test-run.pl
Ejecutar y salida:
fuente
Si el
sub bar
interiorFoo.pm
tiene un prototipo diferente que unaFoo::bar
función existente , ¿Perl no lo sobrescribirá? Ese parece ser el caso, y hace que la solución sea bastante simple:o algo parecido
Actualización: no, la razón por la que esto funciona es que Perl no redefinirá una subrutina "constante" (con prototipo
()
), por lo que esta es solo una solución viable si su función simulada es constante.fuente
BEGIN { *Foo::bar = sub () { 7 } }
está mejor escrito comosub Foo::bar() { 7 }
sub bar { 42 } my $baz = bar();
lugar demy $baz = bar(); sub bar { 42 }
, no funcionaría.Prototype mismatch: sub Foo::bar () vs none at Foo.pm line 5.
yConstant subroutine bar redefined at Foo.pm line 5.
)¡Hagamos un concurso de golf!
Esto solo antepone el código del módulo con un reemplazo del método, que será la primera línea de código que se ejecuta después de la fase de compilación y antes de la fase de ejecución.
Luego, complete la
%INC
entrada para que futuras cargas deuse Foo
no tiren del original.fuente