Heftige Reaktion bei kleinem Schnitzer...


  • Administrator

    SeppJ schrieb:

    cooky451 schrieb:

    Ob ein Programm das unter keinen Umständen terminiert so sinnvoll ist, ist eine andere Frage.

    Frag mal dein Betriebssystem, wann es sich beendet. (Zumindest bevor es Systeme gab, die den PC mittels Soft-Off tatsächlich ausschalten können)

    Du musst gar nicht so ein seltsames Beispiel nehmen. Nimm z.B. den Router oder sonst ein Embedded System. Sehr viele haben am Ende der main eine Endlosschleife.

    Aber davon abgesehen sollte man den Programmierer trotzdem dazu zwingen das return hinzuschreiben. Klar gibt es Ausnahmefälle, wo man es nicht bräuchte, aber in der Regel braucht man das return und es lohnt sich, darüber ein paar Gedanken zu verlieren. Lieber mal ein return zu viel hinschreiben als eines zu wenig.

    Grüssli



  • Schicke Diskussion...
    Ich weiß nur, dass ich mich in Zukunft erstmal um ein return kümmer, bevor ich anfange die Funktion zu debuggen, damit ich mich nicht ärgern muss herauszufinden, warum mein Programm am Ende immer wieder crasht.



  • SeppJ schrieb:

    cooky451 schrieb:

    Ob ein Programm das unter keinen Umständen terminiert so sinnvoll ist, ist eine andere Frage.

    Frag mal dein Betriebssystem, wann es sich beendet. (Zumindest bevor es Systeme gab, die den PC mittels Soft-Off tatsächlich ausschalten können)

    Da würde void main das Programm eh besser modellieren - wie ja offenbar bei den meisten Programmen, wie du ja erwähnt hast. Die Tatsache dass der Rückgabewert von main scheinbar sinnlos geworden ist, ändert aber nichts an der Argumentation. Das eine return schreibe ich dafür gerne hin. (Oder auch nicht, ist ja nicht mehr 1989).

    @ Tim06TR
    Oder du nimmst VS 2012 🤡


  • Mod

    cooky451 schrieb:

    Da würde void main das Programm eh besser modellieren

    Eben! Die Vorgabe ist aber int:

    Eisflamme schrieb:

    Hast Du ein praktisches Beispiel (muss ja kein Code sein), in welchem eine Rückgabetyp einer Funktion bei einer Schnittstelle gefordert wird, obwohl man guten Gewissens das return auslassen kann?



  • SeppJ schrieb:

    Eben! Die Vorgabe ist aber int:

    Aha, so war da gemeint. Also stimmst du mir zu, dass

    Jede Funktion die nicht auf jedem return path etwas zurück gibt, sollte nicht kompilieren. (Außer bei void natürlich.)

    Und außer main. (Was ja eh der Fall ist, da Sonderregelung.)



  • @SeppJ: Du hast kein Argument dafür, dass es kein Fehler sein soll, sondern nur ein paar Beispiele bei denen du dir einmal "return ..." tippen sparst.



  • Nur weil SeppJ nichts findet, heisst das ja nicht, dass es das nicht geben kann.
    Mal schnell im Antwortfenster gecoded:

    class parser {
      char *begin, *curr, *end;
    public:
      very_complicated_token get_token()
      {
        throwy_assert(curr < end);
        case (*curr++) {
          case '0': return make_me_a_token(0);
          case 'x': return make_me_a_token(42);
          default : throwy_assert(false); // kein return
        }
      }
    
      char parse_point_or_comma()
      {
        throwy_assert(curr < end);
        if (*curr == '.' || *curr == ',')
          return *curr++;
        else
          throw_helper("point or comma excepted"); // kein return
      }
    
    private:
      void throw_helper(std::string const& msg) const;
    
      void throwy_assert(bool condition) const
      {
        if (!condition)
          throw_helper("some condition has failed");
      }
    }
    
    // im cpp-file dann:
    void parser::throw_helper(std::string const& msg) const
    {
      throw an_exception("parser error at " +
                         std::to_string(curr - begin) +
                         ": " + msg);
    }
    

    Oder zum anderen Beispiel:

    bool button_clicked_handler(button&)
    {
      std::exit(0);
      // Returnwert gibt an, ob weitere Handler benachrichtigt werden sollen.
      // hier aber unnötig.
    }
    


  • Und? Was wäre jetzt so schlimm, wenn man ein unnötiges return schreiben muss? Die Programme funtionieren trotzdem. Wenn man bedenkt, was es kostet, bis ein Tester so ein undefiniertes Verhalten findet und bis der Entwickler die Codestelle findet*, an der versehentlich ein return vergessen wurde, ist das unnötige tippen eines returns vernachlässigbar.

    *Es gibt durchaus viele Programme die aus mehreren 1000000 Zeilen Code bestehen und unmengen an Warnings haben.


  • Mod

    enternight schrieb:

    Und? Was wäre jetzt so schlimm, wenn man ein unnötiges return schreiben muss?

    Ja! Da gehört einfach logisch keines hin! Welchen Vorteil hätte das? Wenn du grundlos gezwungen wärst, in jedem Programm einmal den Text von Hänschen Klein zu schreiben, würde dich das hoffentlich auch stören.



  • enternight schrieb:

    Und? Was wäre jetzt so schlimm, wenn man ein unnötiges return schreiben muss?

    Weil es ein Returnwert vom Typ very_complicated_token ist, von dem kein sinnvoller Defaultwert existiert. Also muss man eine Funktion schreiben, die ein halbwegs einfaches Token zurückgibt.

    Dann hat man aber wieder viel Boilerplate-Code geschrieben und das Binary wird aufgebläht. Ausserdem stört das return den Lesefluss weil vom Namen der Funktionen klar ist, dass dort eine Exception geworfen wird.



  • throwy_unreg schrieb:

    enternight schrieb:

    Und? Was wäre jetzt so schlimm, wenn man ein unnötiges return schreiben muss?

    Weil es ein Returnwert vom Typ very_complicated_token ist, von dem kein sinnvoller Defaultwert existiert. Also muss man eine Funktion schreiben, die ein halbwegs einfaches Token zurückgibt.

    Dann hat man aber wieder viel Boilerplate-Code geschrieben und das Binary wird aufgebläht. Ausserdem stört das return den Lesefluss weil vom Namen der Funktionen klar ist, dass dort eine Exception geworfen wird.

    Der Compiler sollte sowieso merken, dass dort immer ein throw kommt, wenn er inlined und die Konstanten sieht. Und ein throw zählt auch als return, also kannst du auch das immer bei deinem very_complicated_token hinschreiben, falls du wirklich mal einen Fall hättest der nicht immer einen gültigen Returnpfad hat.

    SeppJ schrieb:

    Welchen Vorteil hätte das?

    Bist du zu dumm zum lesen?

    enternight schrieb:

    Wenn man bedenkt, was es kostet, bis ein Tester so ein undefiniertes Verhalten findet und bis der Entwickler die Codestelle findet*, an der versehentlich ein return vergessen wurde, ist das unnötige tippen eines returns vernachlässigbar.

    *Es gibt durchaus viele Programme die aus mehreren 1000000 Zeilen Code bestehen und unmengen an Warnings haben.

    SeppJ schrieb:

    Wenn du grundlos gezwungen wärst, in jedem Programm einmal den Text von Hänschen Klein zu schreiben, würde dich das hoffentlich auch stören.

    So, ich hab jetzt keine Lust mehr auf so geniale Kleinschreibargumente, das wird mir einfach eine zu dämliche Internetdiskussion die nichts ändert, also tschüss.



  • enternight schrieb:

    Der Compiler sollte sowieso merken, dass dort immer ein throw kommt, wenn er inlined und die Konstanten sieht. Und ein throw zählt auch als return, also kannst du auch das immer bei deinem very_complicated_token hinschreiben, falls du wirklich mal einen Fall hättest der nicht immer einen gültigen Returnpfad hat.

    Kann er eben nicht merken, weil das throw in einer Funktion versteckt ist, die nicht im Header definiert ist.



  • SeppJ:
    War Dein unkommentiertes Zitat meiner Aussage ein Hinweis dazu mich zu äußern? Ich nehme das jetzt Mal so an.

    Ja, das sind einige Beispiele, das räume ich gerne ein. Und die kommen auch vor. Das ändert an meiner Argumentation dann, dass es halt selten (statt nie) Fälle gibt, in denen ein return tatsächlich unnötig ist, weil es nie vorkommt. Du kannst Dir jetzt selbst überlegen, ob Du Dich lieber auf seltene Einzelfälle oder den Regelfall fokussierst, wenn es um solch eine Regel geht.

    Und ja, Ausnahmen können natürlich die Regel allgemein bestimmen, aber da wir hier ja die gute Möglichkeit haben ein Dummy-return hinzuschreiben, sollten sie das meiner Meinung nach nicht tun.


  • Mod

    Eisflamme schrieb:

    Und ja, Ausnahmen können natürlich die Regel allgemein bestimmen, aber da wir hier ja die gute Möglichkeit haben ein Dummy-return hinzuschreiben, sollten sie das meiner Meinung nach nicht tun.

    throwy_unreg hat doch ein schönes Beispiel gebracht, warum ein dummy-return nicht immer in Frage kommt.



  • cooky451 schrieb:

    @Sone: Warnung. Ist halt wie eine Endlosschleife.

    Und wieso nicht FÄHLA, wo wir doch (und der Compiler hoffentlich auch 😃 ) sehen können, dass es ganz klar in UB resultiert?



  • Ja, weil da ein throw steht. Das ist in meinen Augen äquivalent zum return, weil man damit logischerweise ebenfalls die Funktion (auf einem anderen, garantiert nicht rückgabewert-prüfenden Pfad) verlässt. Zudem gibt es, und das ist ebenfalls wesentlich, andere Pfade, auf dem die Funktion eben doch über return verlässt.

    Sone: Wieso ist das undefiniert? Die Funktion entspricht einer Endlosschleife, deren Rückgabewert niemals geprüft wird, außer sie wird durch ein nicht-f()-aufrufendes return verlassen (bei Codeerweiterung), in welchem Fall dort ein Wert stehen würde. Der Rückgabewert wird sonst niemals verwendet werden und somit entsteht auch kein UB. (für die Argumentation nach einer while-Schleife in einer nicht-returnenden Funktion gäbe es nach der Begründung natürlich auch kein UB, aber darum ging's mir bei diesem Kommentar jetzt auch nicht)



  • Also immer ein return erzwingen, wenn das Ende einer Funktion erreicht wird, ist ja auch kein Problem für den Compiler. Fragt sich nur, warum das im Standard nicht ill-formed ist. Aber na ja, mit der Klausel dass UB auch nicht kompilieren darf, bleibt wohl: +1 für VS.



  • cooky451 schrieb:

    Aber na ja, mit der Klausel dass UB auch nicht kompilieren darf

    Oh! Wo denn das?



  • Sone schrieb:

    Oh! Wo denn das?

    Thread lesen? ipsec Seite 4.



  • Ich denke, man muss hier die Historie bedenken. Diese Dinge sind praktisch unverändert von C übernommen worden, und in C war es durchaus üblich,

    hello() {
      puts("Hello, world.");
    }
    
    main() {
      hello();
    
      return 0;
    }
    

    zu schreiben. Ob man jetzt die Deklaration eines Basisdatentyprückgabewertes, der unter Umständen nicht verwendet wird, im C++-Standard hätte verbieten sollen, ist eine spannende, aber auch etwas müßige Frage. Ich kann damit leben, wenn in solchen Fällen ein unspezifizierter Wert zurückkommt, und mit Nicht-PODs kriegt man da eh undefiniertes Verhalten. Vom Compiler zu erwarten, solche Dinge gar nicht erst zu akzeptieren, ist allerdings etwas schwieriger. Ich gebe zu bedenken, dass

    enum my_enum {
      FOO,
      BAR,
      BAZ
    };
    
    int some_function(my_enum x) {
      switch(x) {
      case FOO:
      case BAR:
        return 1;
    
      case BAZ:
        return 2;
      }
    }
    

    vom Compiler nicht für wasserdicht gehalten werden könnte, aber durchaus gerne mal vorkommt (auch wenn ich das für stilistisch nicht sehr sauber halte und es sehr begrüße, wenn ein Compiler da warnt). Zumal die Prüfung der Ausführungspfade insbesondere bei Switches, aber auch bei if-Blöcken, zu der Zeit, als C++ standardisiert wurde, alles andere als gang und gäbe war. Und die auch alles andere als trivial ist, weil ein Compiler sich auch um Sprungkonstrukte sorgen muss, die heute kein Programmierer, der etwas auf sich hält, noch in seine Versionskontrolle lassen würde.

    switch(foo) {
    case 1:
      if(bar) {
    
      } else {
    case 2:
      }
    }
    

    ist gültiges C++!


Anmelden zum Antworten