
Code funktioniert nur in unterschiedlichen Packages
Hallo,
ich hoffe, ich mache mich hier nicht allzu unbeliebt mit meiner vielen
Fragerei... falls doch, bitte um entsprechende Hinweise.
Ich habe jetzt mal versucht, das, was die Pragmas "use vars" und "use subs"
tun, in einem Skript nachzuvollziehen. So entspricht
use vars $x;
in etwas diesem Code:
BEGIN {
*main::x = \$main::x;
}
Danach sollte man $x auch mit "use strict" unqualifiziert ansprechen können.
Funktioniert aber nicht - ich bekomme, wenn ich obigem Code noch "$x = 2;"
anschließe, die Fehler
Variable "$x" not imported at line 7
Global symbol "$x" requires explicit package name at line 7
wobei line 7 die Zeile mit dem "$x = 2" ist.
Und jetzt kommt es: füge ich in den BEGIN Block eine "package"-Anweisung ein
, dann funktioniert es! Und auch dann, wenn ich außerhalb des BEGIN Blocks
eine "package" Anweisung einbaue und danach wieder "package main" angebe.
Auch wenn ich die packages vertausche (BEGIN Block in package main, anderer
Code in fremder package) geht es. Nur wenn der BEGIN Block und der restliche
Code im selben package ablaufen, kommt es zu dem Fehler.
Warum???
Liebe Grüße aus Wien,
Ferry
Re: Code funktioniert nur in unterschiedlichen Packages
Ferry Bolhar wrote:
> BEGIN {
> *main::x = \$main::x;
> }
[...]
> Variable "$x" not imported at line 7
> Global symbol "$x" requires explicit package name at line 7
[...]
> Und jetzt kommt es: füge ich in den BEGIN Block eine "package"-Anweisung ein
> , dann funktioniert es! Und auch dann, wenn ich außerhalb des BEGIN Blocks
> eine "package" Anweisung einbaue und danach wieder "package main" angebe.
> Auch wenn ich die packages vertausche (BEGIN Block in package main, anderer
> Code in fremder package) geht es. Nur wenn der BEGIN Block und der restliche
> Code im selben package ablaufen, kommt es zu dem Fehler.
>
> Warum???
Perl sagt es selbst, denke ich: "Variable "$x" not imported at line 7".
Ein Symbol importieren heißt ja, dass aus einem Package A heraus
ein Symbol zur Symboltabelle eines Package B hinzugefügt wird.
Das ist oben nicht der Fall, da manipuliert das Package main seine
Symboltabelle selbst. In dem Fall wird auf den Seiteneffekt des
Anlegens eines lokalen Bezeichners von Perl
offenbar verzichtet.
Grüße
Frank
--
Dipl.-Inform. Frank Seitz; http://www.fseitz.de/
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel
Re: Code funktioniert nur in unterschiedlichen Packages
Frank Seitz:
> Perl sagt es selbst, denke ich: "Variable "$x" not imported at line 7".
Naja, nach der Beschreibung dieser Meldung in perldiag:
(F) While "use strict" in effect, you referred to a global
variable
that you apparently thought was imported from another module,
because something else of the same name (usually a subroutine) is
exported by that module. It usually means you put the wrong
funny
character on the front of your variable.
hätte ich das so verstanden, dass im aktuellen Package zB. ein "sub x"
durchgeführt, aber kein Skalar $x deklariert wurde (anders gesagt: es
gibt den Typeglob *x, aber keine Skalarreferenz darin).
> Ein Symbol importieren heißt ja, dass aus einem Package A heraus
> ein Symbol zur Symboltabelle eines Package B hinzugefügt wird.
> Das ist oben nicht der Fall, da manipuliert das Package main seine
> Symboltabelle selbst. In dem Fall wird auf den Seiteneffekt des
> Anlegens eines lokalen Bezeichners von Perl offenbar verzichtet.
Du hast mich mal wieder mit der Nasenspitze darauf stoßen müssen
und ich habe es jetzt selbst ausprobiert: der Zieltypeglob bekommt
beim Anlegen, wenn sein Packagenamen _nicht_ mit dem aktuellen
Packagenamen übereinstimmt, eines der Flags GvIMPORTED_SV,
GvIMPORTED_AV, GvIMPORTED_HV oder GvIMPORTED_CV
gesetzt, je nachdem, ob eine Scalar-, Array-, Hash- oder Codereferenz
zugewiesen wird (bzw. alle Flags, wenn gleich eine Globref selber
zugewiesen wird). Wird dann später der Typeglob angesprochen,
prüft der Compiler nach, ob das jeweilige Flag gesetzt ist (dh., wenn
$x angesprochen wird, muss GvIMPORTED_SV im betreffenden
Typeglob gesetzt sein), und bricht mit der "Variable not imported"
Fehlermeldung ab, wenn das nicht der Fall ist.
Das heißt, es ist wirklich gewollt, dass ein Aliasing mittels Typeglobs
nur funktioniert, wenn Quell- und Zielpackage unterschiedlich sind.
Ansonsten wird es zwar toleriert, aber das strenge "use strict" wird
darüber stolpern.
Danke für deine Hinweis und schöne Grüße,
Ferry
--
Ing. Ferry Bolhar
Municipality of Vienna, Department 14
A-1010 Vienna / AUSTRIA
E-mail: bol [at] adv.magwien.gv.at
Re: Code funktioniert nur in unterschiedlichen Packages
Ferry Bolhar wrote:
> Du hast mich mal wieder mit der Nasenspitze darauf stoßen müssen
> und ich habe es jetzt selbst ausprobiert: der Zieltypeglob bekommt
> beim Anlegen, wenn sein Packagenamen _nicht_ mit dem aktuellen
> Packagenamen übereinstimmt, eines der Flags GvIMPORTED_SV,
> GvIMPORTED_AV, GvIMPORTED_HV oder GvIMPORTED_CV
> gesetzt, je nachdem, ob eine Scalar-, Array-, Hash- oder Codereferenz
> zugewiesen wird (bzw. alle Flags, wenn gleich eine Globref selber
> zugewiesen wird). Wird dann später der Typeglob angesprochen,
> prüft der Compiler nach, ob das jeweilige Flag gesetzt ist (dh., wenn
> $x angesprochen wird, muss GvIMPORTED_SV im betreffenden
> Typeglob gesetzt sein), und bricht mit der "Variable not imported"
> Fehlermeldung ab, wenn das nicht der Fall ist.
Und was geschieht im Falle von our und local?
Offen gestanden ist mir völlig unklar, wie Perl Variablennamen auflöst.
Weißt Du da näheres?
Grüße
Frank
--
Dipl.-Inform. Frank Seitz; http://www.fseitz.de/
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel
Re: Code funktioniert nur in unterschiedlichen Packages
Frank Seitz:
> Und was geschieht im Falle von our und local?
Beginnen wir mit dem Leichteren von den beiden - local.
"local" ist keine Deklaration, sondern es ist eigentlich eine
Anweisung - ich halte den Name für ausgesprochen unglücklich
gewählt, "save" wäre wesentlich besser und passender gewesen.
Dabei passiert eigentlich nur folgendes - stößt der Compiler
auf ein "local" vor einer Variable, setzt er in dem OP, der die
Variable namensmäßig adressiert ("gvsv" oder "rv2<x>v",
wobei das <x> für 'a', 'h', 'c' oder 'g' steht), das Flag LVINTRO.
Zur Laufzeit, wenn der OP abgearbeitet wird, veranlasst dieses
Flag nur, dass der aktuelle SV/AV/HV/CV oder GV (denn man
kann ja auch "local *x;" schreiben; das führt bei manchen von
Perls Spezialvariablen zu interessanten Effekten) in einen Bereich
verschoben wird, dessen Elemente beim Verlassen des Blocks
automatisch wieder hergestellt werden. Mehr ist es nicht.
Auch wenn der Name es vermuten läßt, ist "local" keine
Deklaration. Mit "use strict" müssen daher auch "local"-isierte
Variable vorher genauso deklariert werden:
use strict;
.....
{
local $test = 2;
}
führt genauso zu der bekannten "Global symbol "$test" requires
explicit package name..." Meldung, wie wenn man das "local"
weggelassen hätte. Weil eben "local" auf die Compilerphase,
während der solche Dinge geprüft werden, keinen Einfluss hat.
Im Gegensatz zu "our". "our" ist eine Deklaration, und hat
nur beim Compilieren einen Einfluss. Dabei wird ein Trick
verwendet: wird mit "our" eine Variable deklariert, so wird
wie mit "my" eine lexikalische Variable angelegt. Die Variable
selbst (dh. den SV/AV/HV) gibt's aber nur einmal als globale
Variable, die lexikalische Variable "zeigt" nur auf die global
Version. Die lexikalische Variable sorgt aber dafür, dass der
Compiler auch mit "use stricts" eine Bezeichnung ohne package
Namen (eben wie wenn die Variable mit "my" deklariert
worden wäre) erlaubt.
Beim Anlegen des lexikalischen Aliases wird dieser besonders
gekennzeichnet. Stößt der Compiler später auf die Variable,
so erkennt er anhand dieser Kennzeichnung, dass es sich nur
um einen Alias, der auf eine globale Variable zeigt, handelt
und generiert die entsprechenden gvsv/rv2<x>v OPs anstatt
jene für den Zugriff auf lexikalische Variable (pad<x>v).
Daher kann man auch Typeglobs so nicht deklarieren ("our *test"
ist nicht erlaubt), weil es keine lexikalischen Typeglobs gibt, die
als Alias verwendet werden könnten (Typeglobs dürfen daher
auch mit "use strict" unqualifiziert angegeben werden).
Aus dieser Implementierung erklärt sich auch, warum ein "our",
obwohl es eine _globale_ Variable deklariert, nur eine _lexika-
lische_ Gültigkeit hat - wie eine Variable mit "my" hat auch der
lexikalische Alias von "our" nur innerhalb des Blocks, in dem er
deklariert wurde, Gültigkeit. Über die Sinnhaftigkeit einer
solchen Deklaration läßt sich streiten...
Alles in allem ist "our" IMHO ein eher fragwürdiges Konstrukt
und ein gutes Beispiel, dass man es auch übertreiben kann. Was
nützt mir eine Deklaration einer globalen Variable, die nur in
einem Block Gültigkeit hat. Darüber hinaus führt es dazu, dass
sich die Deklaration einer gleichnamigen lexikalischen und
globalen Variable innerhalb eines Blocks ausschließen, was
besonders dann unangenehm ist, wenn es irrtümlich doch
einmal passiert (wie mir selber!) und Perl dann nicht einmal
eine entsprechende Warnung ausgibt:
use warnings;
use strict;
my $x = 2;
....
our $x = 3;
print $x;
gibt bis einschließlich Perl 5.8.8 den Wert 3 (ohne jegliche
Warnung) aus, der vorherige ist verloren. OK, das ist, wie
mir von der p5p - Group versichert wurde, ab Perl 5.9 behoben.
Es führt aber auch in Zusammehang mit Packages, wie ich hier
erst kürzlich gezeigt habe, zu dem interessanten Resultat, dass
die Variable $x des Packetes B im Packet A unqualifiziert
angesprochen wird:
package A;
our $x = 2;
package B;
our $x = 3;
package A;
print $x; # Gibt 3 (d.h, $B::x) aus
Auch wenn dieses Verhalten mehr oder weniger verständlich
dokumentiert ist, ist es sicher nicht das, was man erwartet.
Wir - dh, ich und mein Team - verwenden daher "our" grund-
sätzlich nicht, sondern begnügen uns - wie bis Perl 5.005 -
mit "my" und "use vars". Und ich denke, dass wir dabei nicht
viel schlechter fahren, aber einige Fehlerquellen ausschließen.
So, ich hoffe, ich konnte mich mit dieser Erklärung einmal für
die vielen guten Tipps von dir revanchieren!
LG, Ferry
--
Ing. Ferry Bolhar
Municipality of Vienna, Department 14
A-1010 Vienna / AUSTRIA
E-mail: bol [at] adv.magwien.gv.at
Re: Code funktioniert nur in unterschiedlichen Packages
Ich schrieb:
> Dabei passiert eigentlich nur folgendes - stößt der Compiler
> auf ein "local" vor einer Variable, setzt er in dem OP, der die
> Variable namensmäßig adressiert ("gvsv" oder "rv2<x>v",
> wobei das <x> für 'a', 'h', 'c' oder 'g' steht), das Flag LVINTRO.
> Zur Laufzeit, wenn der OP abgearbeitet wird, veranlasst dieses
> Flag nur, dass der aktuelle SV/AV/HV/CV oder GV...
Kleine Ergänzung bzw. Korrektur: CV stimmt natürlich nicht,
denn eine Subroutine kann man aus demselben Grund wie einen
Typeglob nicht lokalisieren - es gibt das hierzu notwendinge
lexikalische Pendant nicht. Bei Typeglobs ist das klar, da diese
per Definition nur globale Variable ansprechen, bei Subroutinen
könnte es eines Tages (Perl 6?) soweit sein, da sowohl Lexer
als auch Parser schon jetzt (seit Perl 5.6) ein "my sub ..."
interpretieren können, der Compiler allerdings winkt dann mit
einem
"my sub" not yet implemented
ab.
Auch noch zu "our" eine kleine Ergänzung - zur Laufzeit hat es
keine Bedeutung mehr (es dient ja nur zur Deklaration), mit einer
Ausnahme: es setzt im betreffenden OP ein Flag OUR_INTRO,
das von B::Deparse verwendet wird, in seiner Ausgabe wieder
das "our" am richtigen Platz auszugeben. Das ist also einer der
Fälle, wo der Core eine Funktionalität bereitstellt, die nur von
externen Modulen genutzt wird - wie das auch mit CHECK und
INIT Blocks der Fall ist (die eigentlich nur wegen des "Perl-
Compilers" implementiert wurden, auch wenn sie mittlerweile
schon von anderen Modulen verwendet werden).
LG, Ferry
--
Ing. Ferry Bolhar
Municipality of Vienna, Department 14
A-1010 Vienna / AUSTRIA
E-mail: bol [at] adv.magwien.gv.at