Prenons quelque chose de très simple,
# Foo.pm
package Foo {
my $baz = bar();
sub bar { 42 }; ## Overwrite this
print $baz; ## Before this is executed
}
Existe-t-il de toute façon que je peux test.pl
exécuter du code qui modifie ce qui $baz
est défini et provoque l' Foo.pm
impression de quelque chose d'autre à l'écran?
# maybe something here.
use Foo;
# maybe something here
Est-il possible avec les phases du compilateur de forcer l'impression ci-dessus 7
?
perl
compilation
Evan Carroll
la source
la source
Foo::bar
, maisuse Foo
elle exécutera à la fois la phase de compilation (barre de redéfinition si quelque chose a déjà été définie là-bas) et la phase d'exécution de Foo. La seule chose à laquelle je peux penser serait un@INC
crochet profondément hacky pour modifier la façon dont Foo est chargé.require
(et doncuse
) compile et exécute le module avant de revenir. Il en va de mêmeeval
.eval
ne peut pas être utilisé pour compiler du code sans l'exécuter également.Réponses:
Un hack est nécessaire car
require
(et doncuse
) à la fois compile et exécute le module avant de revenir.Il en va de même
eval
.eval
ne peut pas être utilisé pour compiler du code sans l'exécuter également.La solution la moins intrusive que j'ai trouvée serait de passer outre
DB::postponed
. Ceci est appelé avant d'évaluer un fichier requis compilé. Malheureusement, il n'est appelé que lors du débogage (perl -d
).Une autre solution serait de lire le fichier, de le modifier et d'évaluer le fichier modifié, un peu comme ce qui suit:
Ce qui précède n'est pas correctement défini
%INC
, il gâche le nom de fichier utilisé par les avertissements et autres, il n'appelle pasDB::postponed
, etc. La solution suivante est plus robuste:J'ai utilisé
UNITCHECK
(qui est appelé après la compilation mais avant l'exécution) parce que j'ai ajouté le remplacement (à l'aideunread
) plutôt que de lire le fichier entier et d'ajouter la nouvelle définition. Si vous souhaitez utiliser cette approche, vous pouvez obtenir un descripteur de fichier à retourner en utilisantFélicitations à @Grinnz pour avoir mentionné les
@INC
crochets.la source
Puisque les seules options ici vont être profondément hacky, ce que nous voulons vraiment ici, c'est exécuter du code après que le sous-programme a été ajouté à la
%Foo::
cachette:la source
Cela émettra quelques avertissements, mais affiche 7:
Tout d'abord, nous définissons
Foo::bar
. Sa valeur sera redéfinie par la déclaration dans Foo.pm, mais l'avertissement "Subroutine Foo :: bar redefined" sera déclenché, qui appellera le gestionnaire de signaux qui redéfinit la sous-routine pour retourner 7.la source
perl -w
.Voici une solution qui combine le raccordement du processus de chargement du module avec les capacités de création en lecture seule du module en lecture seule:
la source
J'ai révisé ma solution ici, afin qu'elle ne repose plus
Readonly.pm
, après avoir appris que j'avais manqué une alternative très simple, basée sur la réponse de m-conrad , que j'ai retravaillée dans l'approche modulaire que j'avais commencée ici.Foo.pm ( comme dans le message d'ouverture )
OverrideSubs.pm mis à jour
test-run.pl
Exécuter et sortir:
la source
Si l'
sub bar
intérieurFoo.pm
a un prototype différent d'uneFoo::bar
fonction existante , Perl ne le remplacera pas? Cela semble être le cas et rend la solution assez simple:ou un peu la même chose
Mise à jour: non, la raison pour laquelle cela fonctionne est que Perl ne redéfinira pas un sous-programme "constant" (avec prototype
()
), donc ce n'est une solution viable que si votre fonction de simulation est constante.la source
BEGIN { *Foo::bar = sub () { 7 } }
est mieux écrit commesub Foo::bar() { 7 }
sub bar { 42 } my $baz = bar();
au lieu demy $baz = bar(); sub bar { 42 }
, cela ne fonctionnerait pas.Prototype mismatch: sub Foo::bar () vs none at Foo.pm line 5.
etConstant subroutine bar redefined at Foo.pm line 5.
)Laisse avoir un concours de golf!
Cela préfixe simplement le code du module avec un remplacement de la méthode, qui sera la première ligne de code qui s'exécute après la phase de compilation et avant la phase d'exécution.
Ensuite, remplissez l'
%INC
entrée afin que les futures charges deuse Foo
ne tirent pas l'original.la source