2017-07-04 4 views
6

J'ai prochain programme:Pourquoi est-ce que je peux obtenir une adresse de sous-programme avant qu'elle ne soit déclarée sans erreur?

use warnings; 
use strict; 

BEGIN { 
    print \&mysub; 
} 


sub mysub {}; 

print \&mysub; 

Sa sortie:

CODE(0x118e890)CODE(0x118e890) 

Le bloc BEGIN est traité dans le temps de la compilation. À ce stade, la définition de sub mysub n'est pas encore vue par le compilateur. Mais le programme imprime toujours l'adresse de sous-routine correcte, ce qui sera le cas lorsqu'il est défini. Pourquoi je ne reçois pas d'erreur ici?

Est-ce une sorte d'autovivification?

+0

Vous pouvez supprimer les deux dernières lignes, et il fonctionne. Vous pouvez également supprimer 'BEGIN {}' et il imprime toujours l'adresse et aucun avertissement ou erreur. Cependant, si vous essayez d'appeler le sub après (sans le définir), vous obtenez le sous-programme Undefined subroutine & main :: mysub', donc il n'est pas autovivifié. On dirait un bug. –

+1

Qu'est-ce que cela fait est de créer un «stub» de sous-routine. Essentiellement, cela suffit pour permettre au code de continuer. Comme mentionné, vous ne pouvez pas appeler le sous-marin. Ce qui va casser ce code, c'est si vous essayez d'utiliser des prototypes. Il ne serait pas capable de comprendre la liste des arguments, de sorte que le code serait effectivement rompu. – stevieb

Répondre

4

Oui, c'est une forme d'autovivification. Un talon est créé lorsqu'une référence au sous-marin est requise et que le sous-marin n'existe pas.

use strict; 
use warnings qw(all); 
use feature qw(say); 

sub test { 
    say defined(&mysub) ? "defined (".\&mysub.")" 
     : exists(&mysub) ? "exists (".\&mysub.")" 
     :     "doesn't exist"; 
} 

test(); 
my $ref = \&mysub; 
test(); 
eval("sub mysub { } 1") or die([email protected]); 
test(); 

Sortie:

doesn't exist 
exists (CODE(0xab8cd8)) 
defined (CODE(0xab8cd8)) 
+0

Pourquoi cela existe (* {main :: mysub} {CODE}) 'ne fonctionne pas? –

+0

'exists' requiert un élément hash de' & something'. De plus, chaque slot d'un slot glob existe toujours (puisqu'il s'agit d'un C 'struct'), la vérification de l'existence n'est donc pas utile. Vous pouvez vérifier si l'emplacement contient quelque chose en vérifiant qu'il est défini (ou vrai). – ikegami

2

Question très intéressante. J'écris ceci comme une réponse à la place d'un commentaire parce que ce sera plutôt long, mais il y a encore quelques morceaux dont je ne suis pas entièrement sûr.

Je crois que votre intuition est correcte et que c'est une forme d'autovivification.

Devel::Peek peut répandre plus de lumière sur ce qui se passe.

j'ai changé votre code un peu:

use warnings; 
use strict; 
use Devel::Peek; 

$|++; 

BEGIN { 
    Dump(\&mysub); 
    print \&mysub; 
}; 

sub mysub {}; 

Dump(\&mysub); 
print \&mysub; 

I ajouté $|++ de sorte que tampon ne sera pas cause de confusions, et a ajouté des appels à Devel::Peek::Dump pour examiner la référence \&mysub. Ici, la sortie sur mon système:

SV = IV(0x2628628) at 0x2628638 
    REFCNT = 1 
    FLAGS = (TEMP,ROK) 
    RV = 0x26286e0 
    SV = PVCV(0x2640750) at 0x26286e0 
    REFCNT = 2 
    FLAGS = (DYNFILE) 
    COMP_STASH = 0x25ffdb0 "main" 
    ROOT = 0x0 
    GVGV::GV = 0x26287a0 "main" :: "mysub" 
    FILE = "/tmp/autov.pl" 
    DEPTH = 0 
    FLAGS = 0x1000 
    OUTSIDE_SEQ = 0 
    PADLIST = 0x0 
    OUTSIDE = 0x0 (null) 
CODE(0x26286e0)SV = IV(0x25fff20) at 0x25fff30 
    REFCNT = 1 
    FLAGS = (TEMP,ROK) 
    RV = 0x26286e0 
    SV = PVCV(0x2640750) at 0x26286e0 
    REFCNT = 2 
    FLAGS = (DYNFILE) 
    COMP_STASH = 0x25ffdb0 "main" 
    START = 0x262ea50 ===> 1 
    ROOT = 0x262ea10 
    GVGV::GV = 0x26287a0 "main" :: "mysub" 
    FILE = "/tmp/autov.pl" 
    DEPTH = 0 
    FLAGS = 0x1000 
    OUTSIDE_SEQ = 371 
    PADLIST = 0x2648620 
    PADNAME = 0x2630180(0x2667f70) PAD = 0x2628770(0x262f020) 
    OUTSIDE = 0x2600140 (MAIN) 
CODE(0x26286e0) 

Notez comment les changements de sortie de l » Dump entre les deux appels. La première fois que Dump est appelée, nous avons juste une référence à un scalaire vide. Le deuxième deuxième, après la définition réelle de la sous-routine, vous pouvez voir les détails qui se rapportent aux sous-programmes ont été étoffés: à savoir PADLIST (maintenant non nul), PADNAME et START (Je ne suis pas un expert de Perl guts mais je pense que ce est le "pointeur" réel à la sous-routine).

J'espère que cela aide. Je serais intéressé de savoir ce que vous découvrirez si vous approfondissez le problème.

+0

Vous pourriez vouloir ajouter un exemple de ce qui se passe si le sous-programme a des prototypes ... – stevieb