Calocybe: perl 5.6: [functionname] called too early to check prototype

Hi folks!

Ich teste gerade ein kleines Programm auf verschiedenen Betriebssystemen. Dabei sind mir auch verschiedene Versionen des Perl-Interpreters untergekommen. Waehrend ich meistens eine 5.005-Version habe, laeuft auf einer Suse-Box Perl 5.6. Und so wunderte ich, als ich ploetzlich von diesem Interpreter die Warnung
  main::GetFilenames() called too early to check prototype at ./xren.pl line 296vorgesetzt bekam.

Nachdem ich ein bisschen auf perldoc.com herumgesucht habe, fand ich in perldelta folgenden Text:
  %s() called too early to check prototype
  (W prototype) You've called a function that has a prototype before the parser saw a definition
  or declaration for it, and Perl could not check that the call conforms to the prototype. You need
  to either add an early prototype declaration for the subroutine in question, or move the subroutine
  definition ahead of the call to get proper prototype checking. Alternatively, if you are certain that
  you're calling the function correctly, you may put an ampersand before the name to avoid the
  warning. See perlsub.
Mmh tja, was will er mir wohl damit sagen? Zunaechst sei gesagt, dass ich alle mein Funktionen prototype, soweit das moeglich ist, und es da auch keine Probleme gibt. Die Funktion, ueber die sich Perl beschwert, hat scheinbar nur die Besonderheit, dass sie sich selbst rekursiv aufruft. Sie sieht so aus:

sub GetFilenames($$$$;$) {
    ...
    push(@entries, @{GetFilenames($scanpattern, 1, $basevol, $basedir, $_)}) for (@subdirs);
    ...
  }

Offensichtlich steht die Deklaration weit VOR dem Aufruf. Damit ist doch der Prototyp bekannt, und Perl kann den Aufruf dagegen testen. Ueberhaupt frage ich mich, wie Perl wissen kann, dass eine Funktion ge-prototyped ist, wenn es noch keine Deklaration oder Definition davon gesehen hat.

Wie ich jetzt festgestellt habe, funktioniert der Check in diesem Fall tatsaechlich auch mit aelteren Versionen nicht, nur dass es dort halt keine Warnung gibt. Schade, schade. Da werde ich wohl ein & vor den Aufruf schreiben muessen, damit Perl die Warnung weglaesst. Aber eine Frage bleibt: Wer kann mir die Logik dabei erklaeren?

So long

  1. Tach,

    [Prototyp-Deklaration]

    [...] See perlsub
    Wer kann mir die Logik dabei erklaeren?

    Wer lesen kann, ist klar im Vorteil.
    perldoc perlsub.

    Synopsis

    Prototype declaration:
    [...]
    sub NAME(PROTO) BLOCK

    merkst du was?

    Die Lösung für dein Problem steht übrigens auch bereits in der Synopsis.

    Jens

    1. Auch Tach!

      Synopsis
      Prototype declaration:
      [...]
      sub NAME(PROTO) BLOCK
      merkst du was?

      Noe. Und Du?

      Die Lösung für dein Problem steht übrigens auch bereits in der Synopsis.

      Jetzt bin ich ja mal gespannt, ob Du das noch erklaeren kannst, oder ob Du nur wieder mal eines Deiner sinnlosen Ich-bin-Jens-und-keiner-hat-mich-lieb-Postings loswerden wolltest. Bis jetzt hast Du's zumindest noch nicht mal geschafft, richtig aus perlsub zu zitieren.

      So long

      1. Auch Tach!

        Tach auch,

        Synopsis
        Prototype declaration:
        [...]
        sub NAME(PROTO) BLOCK
        merkst du was?

        Noe. Und Du?

        schade, ich hätte mehr erwartet.
        Die Prototyp-Deklaration ist erst *nach* dem Block vollzogen. Steht doch da!

        Die Lösung für dein Problem steht übrigens auch bereits in der Synopsis.

        Forward-Deklaration wurde ja bereits genannt. Du enttäuscht mich, wirklich.

        Jens

        1. schade, ich hätte mehr erwartet.
          Die Prototyp-Deklaration ist erst *nach* dem Block vollzogen. Steht doch da!

          Naja, steht in der Zeile aus Deinem Posting, aber nicht in perlsub. Dort steht:

          To declare subroutines:
              [...]
              sub NAME BLOCK                # A declaration and a definition.
              sub NAME(PROTO) BLOCK         #  ditto, but with prototypes

          Diese Zeile beschreibt also die Deklaration und Definition einer Funktion mit Prototyp. Da steht aber nicht, dass der Prototyp innerhalb des Blocks noch nicht gueltig sein darf.
          Weiss nicht wo Du das her hast, was Du gepostet hast. Ist das ok fuer Dich, wenn ich mich lieber auf perlsub beziehe als auf das, was Du Dir ausgedacht hast?

          Die Lösung für dein Problem steht übrigens auch bereits in der Synopsis.
          Forward-Deklaration wurde ja bereits genannt. Du enttäuscht mich, wirklich.

          Komisch, gerade jetzt faellt mir wieder dieser Spruch ein - wer lesen kann, ist klar im Vorteil. Wo hab ich den nur zuletzt gesehen ...? Also Du enttaeuschst mich jedenfalls nicht.

          So long

  2. Hoi Calo,

    [... function called to early to check prototype ...]

    Kannst du mal etwas Code posten?

    Gruesse,
     CK

    1. Holla!

      [... function called to early to check prototype ...]
      Kannst du mal etwas Code posten?

      Mmh, eigentlich dachte ich, das waere der relevante Teil gewesen. Aber gut, kann ja nebenbei gleich ein bisschen Werbung machen, hehe *g*. Also das ganze Script findet sich auf http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/xren/xren/xren.pl?rev=1.3&content-type=text/vnd.viewcvs-markup
      Mmh, Slashs hinter dem Fragezeichen erlaubt? Sicherheitshalber gleich nochmal:
      http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/xren/xren/xren.pl?rev=1.3&content-type=text%2Fvnd.viewcvs-markup

      So long

      1. Hoi Calo,

        Mmh, eigentlich dachte ich, das waere der relevante Teil gewesen.

        Nee, wars nicht ;-) Der relevante Teil war im Grunde der Head.

        Aber gut, kann ja nebenbei gleich ein bisschen Werbung machen, hehe
        *g*.

        Danke.

        Mmh, Slashs hinter dem Fragezeichen erlaubt? Sicherheitshalber
        gleich nochmal:
        http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/xren/xren/xren.pl?rev=1.3&content-type=text%2Fvnd.viewcvs-markup

        Ah, der Fehler ist klar. Auch Perl arbeitet Zeilenweise; wenn du eine
        Funktion aufrufst, bei der ein Prototype-Check gemacht werden muss,
        und sie wurde vorher noch nicht definiert (durch eine
        Forward-Deklaration), kann der Prototyp eben nicht geprueft werden.
        Das ist in C ganz aehnlich (wobei da noch viel extremer: der
        Compiler geht standardmaessig davon aus, dass die Funktionsargumente
        einer nicht deklarierten Funktion int-Parameter sind und dass die
        Funktion int zurueck gibt). Da du die Funktion (in sich selbst)
        aufrufst, bevor sie komplett analysiert wurde, wird halt dieser
        Fehler erzeugt.

        Die Loesung ist auch ganz einfach: oben am Script (am besten direkt
        nach dem Einbinden eventueller Module) forward-Deklarationen
        benutzen.

        Gruesse,
         CK

        1. Hi Christian!
          (Schoen, dass sich doch noch jemand mit Perlkenntnissen eingeschaltet hat. ;-))

          Nee, wars nicht ;-) Der relevante Teil war im Grunde der Head.

          Mmh? Den Funktionskopf mit Prototyp hatte ich doch gepostet.

          Ah, der Fehler ist klar.

          Find ich noch nicht. ;-)

          Auch Perl arbeitet Zeilenweise; wenn du eine
          Funktion aufrufst, bei der ein Prototype-Check gemacht werden muss,
          und sie wurde vorher noch nicht definiert (durch eine
          Forward-Deklaration), kann der Prototyp eben nicht geprueft werden.

          Soweit hab ich mir das ja auch gedacht.

          Da du die Funktion (in sich selbst)
          aufrufst, bevor sie komplett analysiert wurde, wird halt dieser
          Fehler erzeugt.

          Hier bin ich nicht einverstanden. Dort wo der Aufruf steht, hat Perl den Prototyp bereits gesehen, also kann es ihn auch pruefen. Die Semantik des Prototyps aendert sich doch nicht mehr durch die Definition der Funktion. Und wenn es so waere, wie Du sagst, dann duerfte Perl ja auch nicht wissen, dass es ueberhaupt einen Prototypen gibt, gegen den es pruefen muss. Also entweder es hat ihn gesehen und weiss dass es einen gibt und kann dagegen pruefen, oder es hat ihn nicht gesehen, kann dann auch nicht pruefen, aber kann auch keine Warnung ausgeben, weil nicht bekannt ist, dass es einen Prototypen gibt.

          Die Loesung ist auch ganz einfach: oben am Script (am besten direkt
          nach dem Einbinden eventueller Module) forward-Deklarationen
          benutzen.

          Naja, Workarounds hab ich genug in der Hosentasche, daran soll's nicht scheitern. ;-) Habe mich nun doch fuer die Forwarddeklaration entschieden und direkt davor hingeschrieben, aber sinnvoll finde ich das nicht. ;-)

          So long

          1. Hoi Roland,

            Nee, wars nicht ;-) Der relevante Teil war im Grunde der Head.

            Mmh? Den Funktionskopf mit Prototyp hatte ich doch gepostet.

            Aber nicht den Head des Scriptes.

            Da du die Funktion (in sich selbst)
            aufrufst, bevor sie komplett analysiert wurde, wird halt dieser
            Fehler erzeugt.

            Hier bin ich nicht einverstanden. Dort wo der Aufruf steht, hat
            Perl den Prototyp bereits gesehen, also kann es ihn auch pruefen.

            Ja, aber der Syntax-Baum fuer die Sub wurde noch nicht erstellt. Ich
            denke, erst dann wird der Prototyp definiert; zumindest faende ich
            das logisch: so kann erst auf syntaktische Korrektheit geprueft
            werden.

            Die Semantik des Prototyps aendert sich doch nicht mehr durch
            die Definition der Funktion. Und wenn es so waere, wie Du sagst,
            dann duerfte Perl ja auch nicht wissen, dass es ueberhaupt einen
            Prototypen gibt, gegen den es pruefen muss. Also entweder es hat
            ihn gesehen und weiss dass es einen gibt und kann dagegen pruefen,
            oder es hat ihn nicht gesehen, kann dann auch nicht pruefen, aber
            kann auch keine Warnung ausgeben, weil nicht bekannt ist, dass es
            einen Prototypen gibt.

            Allgemein sind eigentlich die Prototypen abgeraten (ich benutze sie
            allerdings trotzdem :-), weil sie wiederum Bugs oder Gefahren mit
            sich bringen. Sie scheinen nicht ganz sauber implementiert zu sein.

            Naja, Workarounds hab ich genug in der Hosentasche, daran soll's
            nicht scheitern. ;-)

            Das ist eigentlich kein Wuergaround ;-), sondern eine ganz normale
            Methode; Funktionen mit Prototypen haben im Normalfall auch
            Forward-Deklarationen.

            Gruesse,
             CK

            1. Re!

              Mmh? Den Funktionskopf mit Prototyp hatte ich doch gepostet.
              Aber nicht den Head des Scriptes.

              Ach so, Du wolltest nur wissen, ob ich auch wirklich nirgendwo eine Forward declaration habe?

              Hier bin ich nicht einverstanden. Dort wo der Aufruf steht, hat
              Perl den Prototyp bereits gesehen, also kann es ihn auch pruefen.

              Ja, aber der Syntax-Baum fuer die Sub wurde noch nicht erstellt.

              Wurde er im Fall einer Forward declaration aber auch nicht.

              Ich
              denke, erst dann wird der Prototyp definiert; zumindest faende ich
              das logisch: so kann erst auf syntaktische Korrektheit geprueft
              werden.

              Finde ich ueberhaupt nicht logisch. Was hat denn die syntaktische Korrektheit der Funktionsdefinition fuer eine Relevanz fuer den Prototypen?

              Die Semantik des Prototyps aendert sich doch nicht mehr durch
              die Definition der Funktion.

              Allgemein sind eigentlich die Prototypen abgeraten (ich benutze sie
              allerdings trotzdem :-), weil sie wiederum Bugs oder Gefahren mit
              sich bringen.

              Ich benutze sie auch schon seit Anfang an, schliesslich komme ich von C. ;-)
              Aber dass davon abgeraten wird, habe ich noch nicht gehoert. Lediglich, dass man nicht wild und quer bestehende aeltere Funktionen prototypen soll, auf deren Verhalten sich jemand an anderer Stelle verlaesst (siehe dazu perlsub).

              Sie scheinen nicht ganz sauber implementiert zu sein.

              Das wuerde zumindest dieses seltsame Verhalten ... also nicht erklaeren, aber weniger verwunderlich machen.

              Das ist eigentlich kein Wuergaround ;-),

              Solange mir das Verhalten keiner zweifelsfrei logisch erklaeren kann, ist das fuer mich ein Bug. ;-)

              sondern eine ganz normale
              Methode; Funktionen mit Prototypen haben im Normalfall auch
              Forward-Deklarationen.

              Wieso das denn? Ich benutze Forwarddeklarationen praktisch nie, Prototypen dagegen soweit moeglich immer. Eigentlich braucht man sie (FwdDecl) nur, wenn zwei Funktionen sich gegenseitig aufrufen. Evtl. noch, wenn man eine Funktion verwendet, die erst zur Laufzeit nachgeladen wird und daher nicht einfach vorher mit use aus einem Modul geholt werden kann. Oder halt, wenn man diesem seltsamen Programmierstil froehnt, alle Funktionen erst nach dem Hauptprogramm zu definieren. Aber wer macht das schon ausser ein paar Unix-Hackern ... ;-)

              So long

              1. Hoi Roland,

                Ach so, Du wolltest nur wissen, ob ich auch wirklich nirgendwo
                eine Forward declaration habe?

                Janz jenau ;-)

                Wurde er im Fall einer Forward declaration aber auch nicht.

                Nein, aber da wurde die Forward-Deklaration gemacht ;-)

                Finde ich ueberhaupt nicht logisch. Was hat denn die syntaktische
                Korrektheit der Funktionsdefinition fuer eine Relevanz fuer den
                Prototypen?

                Keine. Aber Perl wird in Schichten validiert, Prototypen-Fehler
                muessen nicht zwingend Compile-Time-Fehler sein. Syntaktische Fehler
                schon.

                Ich benutze sie auch schon seit Anfang an, schliesslich komme ich
                von C. ;-)

                Dito.

                Aber dass davon abgeraten wird, habe ich noch nicht gehoert.
                Lediglich, dass man nicht wild und quer bestehende aeltere
                Funktionen prototypen soll, auf deren Verhalten sich jemand an
                anderer Stelle verlaesst (siehe dazu perlsub).

                http://www.perl.com/pub/a/language/misc/fmproto.html
                http://www.perl.com/pub/a/language/misc/bunce.html

                Sie scheinen nicht ganz sauber implementiert zu sein.

                Das wuerde zumindest dieses seltsame Verhalten ... also nicht
                erklaeren, aber weniger verwunderlich machen.

                Sie sind nachgeruestet worden, aus dem folgenden Grund:

                Angenommen, man hat die Funktion sub x($). Und man ruft sie wie ein
                Perl-Builtin auf:

                @var = (x 1,1);

                Mit einem Prototypen wird daraus

                @var = (x(1),1);

                Ohne wuerde daraus

                @var = (x(1,1));

                Das ist eigentlich kein Wuergaround ;-),

                Solange mir das Verhalten keiner zweifelsfrei logisch erklaeren
                kann, ist das fuer mich ein Bug. ;-)

                Ich meine die Forward-Deklarationen. Sie sind kein Wuergaround. Sie
                sind ein normales Sprachmittel.

                sondern eine ganz normale
                Methode; Funktionen mit Prototypen haben im Normalfall auch
                Forward-Deklarationen.

                Wieso das denn? Ich benutze Forwarddeklarationen praktisch nie,
                Prototypen dagegen soweit moeglich immer.

                Ich benutze Forward-Deklarationen *immer*.

                Eigentlich braucht man sie (FwdDecl) nur, wenn zwei Funktionen
                sich gegenseitig aufrufen. Evtl. noch, wenn man eine Funktion
                verwendet, die erst zur Laufzeit nachgeladen wird und daher nicht
                einfach vorher mit use aus einem Modul geholt werden kann. Oder
                halt, wenn man diesem seltsamen Programmierstil froehnt, alle
                Funktionen erst nach dem Hauptprogramm zu definieren. Aber wer
                macht das schon ausser ein paar Unix-Hackern ... ;-)

                So ziemlich viele Leute ;-)

                Gruesse,
                 CK

                1. Re-Hi!

                  Wurde er im Fall einer Forward declaration aber auch nicht.
                  Nein, aber da wurde die Forward-Deklaration gemacht ;-)

                  Ja, was willst Du mir denn jetzt sagen?

                  Finde ich ueberhaupt nicht logisch. Was hat denn die syntaktische
                  Korrektheit der Funktionsdefinition fuer eine Relevanz fuer den
                  Prototypen?
                  Keine.

                  Eben. Und damit gibt es keinen Grund, die "Inbetriebnahme" eines Prototyps von einem voellig peripheren Ereignis wie der Fertigstellung der Compilierung der Funktion abhaengig zu machen. In C geht's ja nu auch (weiss nich, ob der Vergleich Sinn macht).

                  Aber Perl wird in Schichten validiert, Prototypen-Fehler
                  muessen nicht zwingend Compile-Time-Fehler sein. Syntaktische Fehler
                  schon.

                  Gibt es Prototypenfehler, die zur Runtime festgestellt werden? Ich glaube eher nicht. Wie auch immer, ich verstehe nicht, was Du mir mit dem Satz sagen willst. Bin momentan aber auch ein bisschen muede.

                  http://www.perl.com/pub/a/language/misc/fmproto.html
                  http://www.perl.com/pub/a/language/misc/bunce.html

                  Na sieh an. Ich stimme zu, dass Prototypen in Perl in der jetzigen Form nicht gerade der grosse Wurf sind. Aber den Artikel von Tom Christiansen finde ich uebertrieben. Er scheint davon auszugehen, das Perl-Programmierer Volldeppen sind.

                  Sie sind nachgeruestet worden, aus dem folgenden Grund:

                  Bei Perl ist *alles* nachgeruestet. Dem entsprechend ist es jetzt ein ziemlicher Kraut- und Ruebenhaufen.

                  Ich meine die Forward-Deklarationen. Sie sind kein Wuergaround. Sie
                  sind ein normales Sprachmittel.

                  Na das ist mir klar, aber sie fuer den Fall einen ge-prototypten rekursiven Funktion anwenden zu muessen, das ist ein Workaround.

                  Ich benutze Forward-Deklarationen *immer*.

                  Wozu?

                  So ziemlich viele Leute ;-)

                  So ziemlich viele Leute ... benutzen den IE. ;-)

                  So long

              2. Hi Calocybe,

                Wieso das denn? Ich benutze Forwarddeklarationen praktisch nie,
                Prototypen dagegen soweit moeglich immer.
                Eigentlich braucht man sie (FwdDecl) nur, wenn zwei Funktionen
                sich gegenseitig aufrufen.

                ich komme von Pascal, nicht von C, bin aber exakt Deine Vorgehensweise gewohnt (und fände einen forward-Zwang für jede rekursive Funktion häßlich).

                Und ich halte auch es für guten Stil, zuerst die aufgerufenen und danach die aufrufenden Funktionen zu deklarieren (also auf "forward" wo immer möglich zu verzichten). Ich möchte einen Quelltext gerne von vorne nach hinten lesen können, und unnötige Vorwärtsverweise erschweren das Verständnis m. E. enorm.

                Viele Grüße
                      Michael