Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden)



  • @SeppJ sagte in Welche Sprachfeatures sollte man vermeiden?:

    @DocShoe sagte in Welche Sprachfeatures sollte man vermeiden?:

    PS:
    Der Beweis/use case, dass Schleifen/goto performanter sind als Schleifen/Lambdas/Funktionen steht immer noch aus.

    Mehrere Sprungmarken kannst du überhaupt gar nicht mit Funktionen emulieren. Da kannst du nichts vergleichen. Und es hat doch auch niemand gefragt. Und wenn überhaupt, dann wäre die Schuldigkeit doch bei den Funktionen-als-Ablaufsterung-Vertretern, zu zeigen, dass es keine Nachteile hätte.

    Sehe ich anders. C++ best practices und Guidelines raten von goto ab. Dann kommt jemand daher und sagt: "Ich muss das hier benutzen, weil mir sonst Performancenachteile entstehen." Dieser jemand sollte jetzt begründen, warum er goto benutzt und welche Nachteile ihm entstehen, wenn´s er nicht tut. Oder anders gesagt: goto sollte die Ausnahme sein, und dann muss man auch begründen, warum die Ausnahme gerechtfertigt ist.

    Edit:
    Thread Fork?


  • Mod

    Das goto harmful ist, bestreitet doch keiner. Aber hier geht es um den einen typischen Fall, wo es gut und nötig ist. Die Begründungen dafür wurden oben doch ausführlich gegeben.



  • @john-0 sagte in Welche Sprachfeatures sollte man vermeiden?:

    @DocShoe sagte in Welche Sprachfeatures sollte man vermeiden?:

    In diesem Bereich bist du.

    Das ist der Bereich in dem man goto nutzt, und es ist eine legitime Nutzung von goto. Mit der Begründung das wird ja nur von wenigen genutzt könnte man in C++ etliches herauswerfen.

    Um wieviel Prozent ist denn

    for ( int i = 0; i < 100; ++i )
    {
         for ( int j = 0; < j < 100; ++j )
         {
              if ( Abbruch ) goto exitlabel:
         }
    }
    exitabel:
    

    performanter als

    bool exit_calc = false;
    for ( int i = 0; i < 100 && !exit_calc; ++i )
    {
         for ( int j = 0; < j < 100 && !exit_calc; ++j )
         {
              if ( Abbruch ) exit_calc = true;
         }
    }
    

    ich hab da ernsthafte Zweifel, dass man da überhaupt einen Unterschied messen kann.



  • @SeppJ sagte in Welche Sprachfeatures sollte man vermeiden?:

    Wer seine verschachtelten Loop nur zwecks Ablaufsteuerung in Unterfunktionen verteilt, gehört ebenfalls nicht an ein Computerprogramm.

    Und wer seine Funktionen bis zur Unleserlichkeit tief verschachtelt, damit sie auf gar keinen Fall mehr intuitiv sind, gehört standrechtlich erschossen 😛



  • @DocShoe sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    Sehe ich anders. C++ best practices und Guidelines raten von goto ab.

    C++ Core Guidelines ES.76: Avoid goto

    Exception

    Breaking out of a nested loop. In that case, always jump forwards.



  • @It0101 sagte in Welche Sprachfeatures sollte man vermeiden?:

    exit_calc

    Es geht weniger darum ob und wie viel die exit_calc Variante langsamer ist. Es geht darum dass sie viel schwerer zu lesen ist. In echt sind das ja keine leeren Schleifen. Da ist noch Code, meist in jeder Schleife (also z.B. Code der nach der inneren Schleife noch in der äusseren Schleife ausgeführt wird). Und das wird dann ganz schnell ganz unübersichtlich.

    Die goto Variante ist dagegen quasi immer trivial zu lesen.



  • @hustbaer sagte in Welche Sprachfeatures sollte man vermeiden?:

    @It0101 sagte in Welche Sprachfeatures sollte man vermeiden?:

    exit_calc

    Es geht weniger darum ob und wie viel die exit_calc Variante langsamer ist. Es geht darum dass sie viel schwerer zu lesen ist. In echt sind das ja keine leeren Schleifen. Da ist noch Code, meist in jeder Schleife (also z.B. Code der nach der inneren Schleife noch in der äusseren Schleife ausgeführt wird). Und das wird dann ganz schnell ganz unübersichtlich.

    Die goto Variante ist dagegen quasi immer trivial zu lesen.

    Das mag sein, aber das war nicht das Argument. @john-0 hat explizit mit der Performance argumentiert.

    Die Goto-Variante ist auch nur dann besser zu lesen, wenn das Sprungziel nicht auf der nächsten Seite ist. Und wenn hier, wie man so hört, die tief-verschachtelten Schleifen hart durchgezogen werden, und im inneren der Schleifen irgendwo auch nur ein bisschen sinnvoller Sourcecode drin ist, dann ist der Spaß mindestens eine Seite groß. Viel Spaß beim Suchen des Sprungziels 😃

    Ich bin aber ehrlich: ich stehe überhaupt nicht auf Verschachtelungstiefen. Im allgemeinen habe ich selten mehr als zwei Ebenen innnerhalb einer Funktion/Methode. Die vier verschachtelten Schleifen triggern mich also hart 😛



  • @It0101 Ah, OK. Die Beiträge bestimmter Leute sehe ich ja nicht 🙂

    Viel Spaß beim Suchen des Sprungziels

    F12/Alt-G/was auch immer der Shortcut für "go to definition" ist. Bzw. ohne Tool-Support einfach ein "find" mit dem Namen des Labels machen.
    Das Suchen des Sprungziels ist ein triviales Problem.
    Den Code Zeile für Zeile durchzugehen um zu sehen was noch alles ausgeführt wird nachdem die Hilfsvariable gesetzt wurde ist dagegen nicht trivial.
    Ich hab da Fälle gesehen wo alle verschachtelten Schleifen auf eine Bildschirmseite gepasst haben, und es einem trotzdem das Hirn zerbröselt hat beim Versuch zu verstehen wie der Abbruch da genau funktioniert. Super "lustig" ist dann auch das Fehlersuchen in solchem Code.



  • @manni66 sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    @DocShoe sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    Sehe ich anders. C++ best practices und Guidelines raten von goto ab.

    C++ Core Guidelines ES.76: Avoid goto

    Exception

    Breaking out of a nested loop. In that case, always jump forwards.

    Dann nehmen wir mal das Beispiel aus den Guidelines:

    void foo()
    {
       for (int i = 0; i < imax; ++i)
          for (int j = 0; j < jmax; ++j) {
             if (a[i][j] > elem_max) goto finished;
               // ...
          }
       finished:
       // ...
    }
    

    geht auch als

    void bar()
    {
       [&] {
          for (int i = 0; i < imax; ++i)
             for (int j = 0; j < jmax; ++j) {
                if (a[i][j] > elem_max) return;
                // ...
             }
       } ();
       //...
    }
    


  • @DocShoe sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    geht auch als

    Es mag sein, dass das Beispiel dort ungeschickt gewählt ist. Es ging mir aber auch nur darum, dass zumindest die Core Guidelines explizit eine Ausnahem vom kein goto machen.



  • @DocShoe sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    geht auch als [Version mit Lambda]

    findest du das lesbarer?

    Lambdas schön und gut, aber man siehst bei dem if das return und denkt sich, da wird aus bar rausgesprungen. Dann fällt irgendwann das unscheinbare [&] auf. Für mich ist so eine anonymes, direkt ausgeführtes Lambda, das dann noch mehr als 1-2 Zeilen Code hat, schlecht lesbar.



  • @wob
    Ne, finde ich nicht lesbarer. Man kann das auch benennen und explizit aufrufen. Oder als Funktionsaufruf ausgliedern, mir ging´s da nur um´s goto. Aus meiner Sicht ist goto immer noch überflüssig.



  • @DocShoe sagte in [Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden)](/forum

    geht auch als

    void bar()
    {
       [&] {
          for (int i = 0; i < imax; ++i)
             for (int j = 0; j < jmax; ++j) {
                if (a[i][j] > elem_max) return;
                // ...
             }
       } ();
       //...
    }
    

    Das sehe ich als extrem schlechtes Design an, weil die Syntax von C++ sehr gut kennen muss, um überhaupt zu verstehen, was da passiert. Lambdas sind in dieser Hinsicht eine deutliche Verschlechterung der Lesbarkeit von Programmcode. Lambdas führen zu recht hässlichem Code, der zar kompakt ist und für den C++-Profi geht zu verstehen ist, aber für alle anderen nicht.

    goto ist an dieser Stelle ohne hässlichen Seiteneffekte und man sieht sofort was passiert. Das ist deutlich besseres Design.



  • @*john-0
    @DocShoe sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    Ne, finde ich nicht lesbarer. Man kann das auch benennen und explizit aufrufen. Oder als Funktionsaufruf ausgliedern, mir ging´s da nur um´s goto. Aus meiner Sicht ist goto immer noch überflüssig.


  • Mod

    Dieser Beitrag wurde gelöscht!

  • Mod

    @DocShoe sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    @*john-0
    @DocShoe sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    Ne, finde ich nicht lesbarer. Man kann das auch benennen und explizit aufrufen. Oder als Funktionsaufruf ausgliedern, mir ging´s da nur um´s goto. Aus meiner Sicht ist goto immer noch überflüssig.

    Aber den variablen Sprung kannst du prinzipiell immer noch nicht damit emulieren, egal wie du dich verrenkst. Und voraussichtlich wird keine der goto-Vermeidungsstrategien hier zu einem besser lesbaren Code führen. Es gibt schon einen Grund, wieso dieses Konstrukt immer als das Beispiel für das gute goto herangezogen wird. Ist auch so ungefähr das einzige Beispiel für ein gutes goto, aber es ist ein gutes Beispiel.

    Hier, zum Austoben

    for (i…)
    {
      for (j…) 
      {
        for(k…)
        {
          rechnung(i, j, k);
          if (i ist sinnlos) goto i_continue;
          if (j ist sinnlos) goto j_continue;
        }
        j_continue:
      }
      i_continue:
    }
    


  • for( i.... )
    {
       for( j... )
       {
          for( k... )
          {
             rechnung( i, j, k );
             if( i ist sinnlos || j ist sinnlos ) break;
          }
          if( i ist sinnlos ) break;
       }
     }
    

    Aber ich sehe deinen Punkt. Bei drei Verschachtelungstiefen geht das noch, aber bei mehreren werden die if-Abfragen unübersichtlich und die Abbruchbedingungen verteilen sich auf mehrere Zeilen Quelltext.



  • @DocShoe

    Warum nicht so, sofern es algorithmisch möglich ist?

    int MeineRechnung(i, j, k)
    {
      for(k…)
      {
        rechnung(i, j, k);
        if (i ist sinnlos) 
          return i_continue;
        if (j ist sinnlos)
          return;
      }
    }
    
    for (i…)
    {
      for (j…) 
      { 
    	if (MeineRechnung(i, j, k) == i_continue)
    		break;
      }
      i_continue:
    }
    

    Meine is, js uns ks werden schnell groß. Von daher mag ich kleine Funktionen, welche ich seperat testen kann, bevor ich eine O(n^3) Operation starte.


  • Mod

    @Quiche-Lorraine : Das ist ja genau der Lösungsansatz, den ich und andere kritisiert haben.

    • Du hast die Logik des Algorithmus, die vorher auf einen Blick direkt erfassbar war, auf mehrere Stellen im Quellcode verteilt.
    • Man muss genau das Verhalten der Unterfunktion kennen, um den Code der aufrufenden Ebene verstehen zu können. Was ein Zeichen für eine schlecht definierte Funktion ist.
    • Die Funktion ist fest daran gebunden, dass der aufrufende Kontext genau so aussieht wie hier. Ein weiteres Zeichen für eine schlecht definierte Funktion.
    • Du hast auch noch eine Conditionvariable und deren Abfrage eingebaut, wo doch das eigentliche Ziel war, so etwas zu vermeiden

    Der Code ist in jeder Hinsicht schlechter gemacht als das Original. Das nur um dogmatisch das goto zu vermeiden, obwohl das vollständige Dogma doch sogar explizit eine Ausnahme macht für diesen Fall.

    @DocShoe : Du hast die Extrafunktionen zwar nicht, aber halt auch die Extracondition, die jetzt bei jedem Durchlauf gecheckt werden muss. Was vorher nicht da war. Wenn Laufzeit geopfert wird für Dogmatismus, dann ist das Dogma falsch (oder hier: Unvollständig, da es eigentlich hier die Ausnahme zulässt)



  • @DocShoe sagte in Goto considered harmful!? (Fork aus: Welche Sprachfeatures sollte man vermeiden):

    Wenn ich in verschiedenen Schleifenrümpfen gotos habe, die an Sprungmarken springen, die ich aufgrund der Anzahl der Zeilen nicht mal sehen kann, weil ich dazu scrollen muss?

    Dann kannst du zumindest auch innere Schleifen-Blocks in andere Funktionen verlagern.


Anmelden zum Antworten