Ist goto xy wirklich so böse?



  • goto ist manchmal ganz praktisch für clean-up Code oder Fehlerbehandlung.



  • goto ist auch praktisch um verschachtelte Schleifen zu verlassen



  • goto ist auch praktisch um verschachtelte Schleifen zu verlassen

    Daran hatte ich noch gar nicht gedacht. Ich hatte mich aber immer darüber geärgert, dass ich dafür immer Zustandsflags mit schleppen musste.

    Und bei Fehlerbehandlung fällt mir noch etwas ein. Ich hatte vor einiger Zeit in eines meiner Linux Programme libpng (eine C Lib) eingebunden. Die arbeiten mit longjump für die Exception Behandlung und der Beispielcode im Tutorial ist auch voller goto's, etwa so:

    ...
    	// Initialize info structure
    	info_ptr = png_create_info_struct(png_ptr);
    	if (info_ptr == NULL) {
    		fprintf(stderr, "Could not allocate info struct\n");
    		code = 1;
    		goto finalise;
    	}
    ...
    

    In meinen eigenen C Programmen habe ich goto in den letzten 20 Jahren nur 2 mal eingesetzt und immer nur zur Fehlerbehandlung (bei C++ bisher nicht).



  • goto in C ist IMO etwas völlig anderes also goto in C++.
    Auf Grund fehlender Destruktoren in C gibt es da dauernd Situationen wo man goto gut brauchen kann - das klassische goto error/exit/cleanup halt.

    @zufallswert

    zufallswert schrieb:

    goto ist manchmal ganz praktisch für clean-up Code oder Fehlerbehandlung.

    In C++?



  • Ja. Wenn ich in bestimmten Fällen entscheide, dass es eleganter ist, zum clean-up mit goto zu verzweigen statt Exceptions zu nutzen, bedarf dies keinerlei Rechtfertigung oder gar Belehrung. Sorry ... 😃



  • zufallswert schrieb:

    Ja. Wenn ich in bestimmten Fällen entscheide, dass es eleganter ist, zum clean-up mit goto zu verzweigen statt Exceptions zu nutzen, bedarf dies keinerlei Rechtfertigung oder gar Belehrung. Sorry ... 😃

    Dazu gibt's jetzt nicht viel zu sagen. Wenn du es nicht für nötig befindest irgendwas sachliches zu antworten, dann sage ich einfach mal: nein, da liegst du völlig falsch. Sorry.


  • Mod

    einwegflasche schrieb:

    Daran hatte ich noch gar nicht gedacht. Ich hatte mich aber immer darüber geärgert, dass ich dafür immer Zustandsflags mit schleppen musste.

    Das ist tatsächlich nicht elegant lösbar. Es gibt ein Paper, an dem noch gearbeitet wird: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0082r2.pdf

    Multiple breaks
    The early termination block also provides for graceful multiple breaks. Suppose you want to iterate over a three-dimensional table and choose a particular cell. Today you might write something like this:

    vector<vector<vector<. . .>>> table = . . .;
    for (auto& x : table)
      for (auto& y : x)
        for (auto& z : y)
          if (some_condition(z))
          {
            do_something(z);
            goto DONE;
          }
    DONE:
    

    This isn’t too bad, but it gets worse if you have different exit situations. This particular problem could be solved by putting the loops in a function, then returning from the innermost loop, but not all algorithms are so easily encapsulated. With early termination blocks, you can do this:

    for (auto& x : table)
      for (auto& y : x)
        for (auto& z : y)
          if (some_condition(z))
          {
             do_something(z);
             break;
          }
          on_break break;
        on_break break;
    

    This scales well to more complicated cases since you can either continue or break on either termination condition. I would expect that the compiler could collapse the repeated breaks into a single jump, so the efficiency of the goto solution would be preserved.

    Ich vermute allerdings, dass viele solcher Fälle von verschachtelten "inner-break" for-Schleifen tatsächlich eine nicht verschachtelte, flache Iteration imitieren sollen. Im obigen Fall sollte demnach eine Schleife über eine "flat view" stattfinden. Kennt jemand ein Gegenbeispiel?



  • hustbaer schrieb:

    zufallswert schrieb:

    Ja. Wenn ich in bestimmten Fällen entscheide, dass es eleganter ist, zum clean-up mit goto zu verzweigen statt Exceptions zu nutzen, bedarf dies keinerlei Rechtfertigung oder gar Belehrung. Sorry ... 😃

    Dazu gibt's jetzt nicht viel zu sagen. Wenn du es nicht für nötig befindest irgendwas sachliches zu antworten, dann sage ich einfach mal: nein, da liegst du völlig falsch. Sorry.

    wenn du Streit suchst, hast du bei mir Pech. Ooooh 😞



  • Ich suche keinen Streit. Ich hatte gehofft dass das ganze zu einem sachlichen Argument führt. Du scheinst aber nur daran interssiert zu sein dich über andere lustig zu machen. Auch gut, kann ich nicht ändern.



  • Arcoth schrieb:

    einwegflasche schrieb:

    Daran hatte ich noch gar nicht gedacht. Ich hatte mich aber immer darüber geärgert, dass ich dafür immer Zustandsflags mit schleppen musste.

    Das ist tatsächlich nicht elegant lösbar.

    Tatsächlich ist das Problem in PHP sehr elegant gelöst. Dort kann man break einfach einen Parameter übergeben, für wieviele Schleifenschichten es gilt (ein break(2) beendet eben die Innerste und darauf Folgende). Ich weiß gerade nicht aus dem Kopf, ob das auch für continue gilt, was aber die logische Konsequenz wäre.



  • Zum Thema "double break" (bzw. "multiple break")...
    Man kann hier oft return statt break bzw. goto verwenden. Ist naheliegend und auch keine universelle Lösung, aber sollte IMO trotzdem erwähnt werden.

    Und falls nicht, ist meine Meinung dass ein goto zum Verlassen mehrerer Schleifen meist ganz klar besser ist als zusätzliche Zustandsvariablen einzuführen.

    Wobei ich finde dass hier noch eines meiner Lieblingspatterns erwähnt werden sollte, nämlich das Method-Object Pattern. Dadurch dass man damit lokale Variablen zu Membervariablen machen kann, lässt sich "return statt break" zum Abbrechen von verschachtelten Schleifen in sehr vielen Fällen anwenden, wo es mit "normalen" Methoden/Funktionen ... umständlich werden würde.



  • @ProGoTo
    Naja, also besonders schön finde ich das nicht. Dann noch lieber die Variante wo man bei break ein Label angeben kann - wobei die Einschränkung gilt dass das Label direkt auf das Ende eines "aktiven" Loops folgen muss. Ist im Prinzip das selbe, nur mMn. leichter zu lesen und weniger fehleranfällig.

    Auf continue lässt sich das ganze im Prinzip genau so anwenden, nur dass das Label dann direkt vor dem Ende eines aktiven Loops stehen muss.


  • Mod

    hustbaer schrieb:

    Wobei ich finde dass hier noch eines meiner Lieblingspatterns erwähnt werden sollte, nämlich das Method-Object Pattern. Dadurch dass man damit lokale Variablen zu Membervariablen machen kann, lässt sich "return statt break" zum Abbrechen von verschachtelten Schleifen in sehr vielen Fällen anwenden, wo es mit "normalen" Methoden/Funktionen ... umständlich werden würde.

    Könntest du das erläutern? Ich suche stets nach einer eleganten Lösung für folgendes Problem: Tief verschachtelte, hochkomplexe Rechnung (die man auch am liebsten in Unterfunktionen aufteilen würde), bei der aber unter gewissen Fällen vorzeitig klar wird, was das Ergebnis sein wird. Wie bekommt man dann schnell und sauber dieses Ergebnis heraus, ohne im Rest der Rechnung Laufzeitkompromisse einzugehen?



  • SeppJ schrieb:

    Tief verschachtelte, hochkomplexe Rechnung (die man auch am liebsten in Unterfunktionen aufteilen würde), bei der aber unter gewissen Fällen vorzeitig klar wird, was das Ergebnis sein wird. Wie bekommt man dann schnell und sauber dieses Ergebnis heraus, ohne im Rest der Rechnung Laufzeitkompromisse einzugehen?

    Wenn die Rechnung komplex und lange dauert und Ergebnisse nur selten rauskommen:

    throw ComplexCalculationResult(result);
    

    😉

    (Nicht ernst gemeint!)



  • SeppJ schrieb:

    Tief verschachtelte, hochkomplexe Rechnung (die man auch am liebsten in Unterfunktionen aufteilen würde), bei der aber unter gewissen Fällen vorzeitig klar wird, was das Ergebnis sein wird. Wie bekommt man dann schnell und sauber dieses Ergebnis heraus, ohne im Rest der Rechnung Laufzeitkompromisse einzugehen?

    ohne goto mit

    break (bei einer Schleifenebene)
    return (bei mehreren)
    

  • Mod

    wob schrieb:

    Wenn die Rechnung komplex und lange dauert und Ergebnisse nur selten rauskommen:

    throw ComplexCalculationResult(result);
    

    😉

    (Nicht ernst gemeint!)

    Wutz schrieb:

    break (bei einer Schleifenebene)
    return (bei mehreren)
    

    Das sind halt beides die Lösungen, die nicht so optimal sind. Auch wenn wobs Vorschlag nicht ernst gemeint ist, ist das doch genau das, was ich möchte: Beliebig viele Ebenen sauber abwickeln. Aber das ist halt ein Missbrauch der Exceptions und Exceptions sind im throw-Fall auch arg langsam.

    break und return sind ja gerade die Mechanismen, die nicht ausreichen, denn damit komme ich nicht aus tief verschachtelten Rechnungen heraus, außer ich verzichte auf Unterfunktionen und schreibe stattdessen die gesamte Rechnung in einer einzigen Funktion. Was doof ist. Das ist ja gerade das Thema dieses Threads, dass so etwas nicht immer ausreicht. In diesem Fall ist es aber sogar noch schlimmer, weil die Alternative kein goto, sondern ein longjmp ist, was erst recht Spagetticode ist und auch voraussetzt, dass man ohne sauberes Stackaufräumen auskommt.



  • on-topic:
    Mach ´ne Funktion draus. Wenn es mehr als zwei Typen gibt geht auch der ternäre Operator wie ihn Arcoth vorgeschlagen hat nicht mehr. Bzw. wird länger und unleserlich.

    const char* label_name( unsigned int Type )
    {
       if( fdat.type == ft_IIRLP )  return "IIR low-pass"; 
       else                         return "IIR high-pass"; 
    }
    
    Filter * FilterFactory::create( InData_t * inp ) { 
        Filter *    fi = 0; 
        Fdata_t     fdat; // input to the concrete constructor 
    ...                   // copy some data to fdat 
        switch( inp->type ) { 
    ... 
            case  ft_IIRLP: 
            case  ft_IIRHP: 
                create_IIR( inp, &fdat );            // calculate the parameters 
                fdat.inbuf = get_inbuf( fdat.taps ); // request the input buffer 
                fi = new F_iir_df2( &fdat );         // create the filter 
                fi->label( label_name( fdat.type );
                break; 
    ... 
            default: 
                break; 
        } 
        return fi; 
    }
    


  • SeppJ schrieb:

    Tief verschachtelte, hochkomplexe Rechnung (die man auch am liebsten in Unterfunktionen aufteilen würde), bei der aber unter gewissen Fällen vorzeitig klar wird, was das Ergebnis sein wird. Wie bekommt man dann schnell und sauber dieses Ergebnis heraus, ohne im Rest der Rechnung Laufzeitkompromisse einzugehen?

    Ohne jetzt Trollen zu wollen. Es wird immer von einer tief verschachtelten, komplexen Berechnung erzählt. So eine ist mir bisher noch nie vor die Flinte gekommen. Kannst du da mal ein Beispiel aus der echten Welt geben?


  • Mod

    sowas schrieb:

    Ohne jetzt Trollen zu wollen. Es wird immer von einer tief verschachtelten, komplexen Berechnung erzählt. So eine ist mir bisher noch nie vor die Flinte gekommen. Kannst du da mal ein Beispiel aus der echten Welt geben?

    Ja, aber wieso sollte ich mir dermaßen viel Mühe machen, hier anonymisierten Beispielcode zu erstellen? Da säße ich mindestens 40 Minuten dran, bloß um deine Neugierde zu befriedigen. Kannst du dir nicht vorstellen, dass es auch Leute gibt, die Programme schreiben, die wirklich etwas tun?

    Die Kurzvariante, ohne konkreten Code:
    Laufe über eine Menge. Rufe für jedes Element mehrere Funktionen auf, die jeweils wieder über eine andere Funktion von allen anderen Elementen der Menge abhängen. Zähle alle Zahlen zusammen. Nun kann es aber ganz regulär vorkommen, dass eine der Zahlen ∞ ist, d.h. man kann sich auch den Rest der Rechnung sparen, weil klar ist, dass die Summe aller Zahlen auch ∞ sein wird. Daher will man in dem Fall aus der Unterfunktion der Unterfunktion heraus in die Gesamtrechenfunktion springen und dort mit ∞ aussteigen.



  • @ DocShoe

    Mach ´ne Funktion draus.

    Toll, warum bin ich da nicht selber drauf gekommen? Allerdings werde ich das setzen des Labels ans Ende der create Methode setzen. Damit ist denn auch mein eigentliches Ziel erreicht, nämlich den Switch nicht zu lang werden zu lassen.

    Und eine Funktion hat ja auch den Vortiel, dass man alle Verdächtigen zusammen hat und diese bei Änderungen nicht erst suchen muss.

    Im Übrigen finde ich die Meinungen zum Einsatz von goto interessant.


Anmelden zum Antworten