Heftige Reaktion bei kleinem Schnitzer...



  • SeppJ:
    Ja und wenn der Rückgabewert nicht von dem, der die Schnittstelle erzwingt, verwertet wird, ist die Schnittstelle wohl nicht besonders schön designed. Liegt das in eigener Hand, würde ich sie verbessern. Liegt das in fremder Hand kann man sich doch gar nicht so sicher sein, was mit dem Rückgabewert passiert. Und dann sollte man wohl dafür Sorge tragen, dass auch ein Wert (und sei es Dummy) zurückgegeben wird.

    Wenn ich eine Funktion mit Rückgabetyp habe, schreibe ich dort auch ein return rein. Und ich tue das auch dann, wenn ich weiß, dass der Rückgabewert nicht verwendet wird. Es hat doch keinen praktischen Vorteil es nicht zu tun, außer dass man höchstens unnötigen Code hinschreibt. Ist aber die Schnittstelle schon so entwickelt, dass Rückgabe erforderlich ist, so liegt der Designfehler hier nicht darin ein unnötiges return zu schreiben, sondern in meinen Augen in der Schnittstelle.

    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?



  • Dafuer gibts doch jetzt [noreturn]. Ergo gueltiges Programm.


  • Mod

    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?

    main.

    Kellerautomat schrieb:

    Dafuer gibts doch jetzt [noreturn]. Ergo gueltiges Programm.

    Danke, das bringt mich auf noch eine Idee:

    int does_not_return()
    {
     for (;;);
    }
    

    Warnung? Fehler? Gültiges Programm?

    (Dieser Fall ist insofern interessant, da der Compiler annehmen darf, dass nebeneffektfreie Endlosschleifen doch terminieren, aufgrund einer obskuren Klausel im Standard, die fast niemand kennt und die ich gerade nicht suchen mag. Falls das zu verwirrend ist, möge man sich einfach eine Ausgabe in der Schleife denken :p )



  • SeppJ schrieb:

    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?

    main.

    Ich verteidige Eisflammes Meinung ja nur ungern :p , aber das ist doch eine vom Standard definierte Ausnahme.
    Ich hab was zum streiten:

    int f()
    {
        return f();
    }
    

    Warnung, Fehler, ...?



  • SeppJ:
    Wenn Du schon main sagen musst, hast Du offensichtlich keins. 🙄

    Das Programm finde ich genau so sinnlos. Die Funktion hat offensichtlich einen Rückgabetypen, also ist irgendwo angedacht, dass sie etwas zurückgibt. Somit sollte sie das eben auch tun. Diese obskure und anscheinend auch versteckte Klausel bestärkt das doch nur.



  • ack Eisflamme. Sogar noch mehr. Jede Funktion die nicht auf jedem return path etwas zurück gibt, sollte nicht kompilieren. (Außer bei void natürlich.) Darüber dass "ich rufe die Funktion wissentlich nur mit bestimmten Werten auf" völliger Quatsch ist, müssen wir glaube ich gar nicht reden. Und selbst wenn das so wäre, tut ein dummy return nicht weh. Das ist im Prinzip genau die gleiche Situation wie const_cast. Sollte ein Programm das etwas über einen const void* verändert kompilieren? Nein, natürlich nicht. Und wenn man es doch mal aus irgendwelchen obskuren gründen braucht, nimmt man halt einen cast bzw. in diesem Fall ein dummy return.

    @Sone: Warnung. Ist halt wie eine Endlosschleife.


  • Mod

    Eisflamme schrieb:

    SeppJ:
    Wenn Du schon main sagen musst, hast Du offensichtlich keins. 🙄

    Wieso? Das ist ein exzellentes Beispiel, denn:

    Sone schrieb:

    Ich verteidige Eisflammes Meinung ja nur ungern :p , aber das ist doch eine vom Standard definierte Ausnahme.

    Wieso ist das wohl so?
    Weil der Rückgabewert von main schon seit 20 Jahren fast niemanden mehr interessiert.

    Das Programm finde ich genau so sinnlos. Die Funktion hat offensichtlich einen Rückgabetypen, also ist irgendwo angedacht, dass sie etwas zurückgibt. Somit sollte sie das eben auch tun.

    Und wenn es die main-Funktion wäre? (Nehmen wir mal C89, wo es in main kein implizites return 0 gibt):

    int main()
    {
     for(;;)
      puts("Dieses Programm läuft für immer\n");
    
     return 0; // Muss das hier also hin?
    }
    

    Dieses letzte Beispiel richtet sich auch an cooky451 mit seiner Meinung zum Dummy-Return.



  • SeppJ schrieb:

    Dieses letzte Beispiel richtet sich auch an cooky451 mit seiner Meinung zum Dummy-Return.

    Selbstverständlich wäre das dann so sinnvoll. Ob ein Programm das unter keinen Umständen terminiert so sinnvoll ist, ist eine andere Frage. Aber es macht natürlich mehr Sinn den Programmierer zu zwingen sich Gedanken über den return Wert zu machen, als diese eine Zeile zu sparen.



  • Für main, bei dem Rückgabewert definiert ist:
    Es ergibt doch jetzt überhaupt keinen Sinn sich die Frage zu stellen, ob der Compiler bei Weglassen des returns in der main meckern sollte oder nicht. Vom Standard ist explizit vorgeschrieben, dass das Weglassen nicht zu undefiniertem Verhalten führt, sondern das Verhalten ist definiert. Gäbe es default-Rückgabewerte, hätte ich auch nichts gegen das fehlende return.

    Für main, bei dem Rückgabewert undefiniert ist:
    Das ändert nichts an meiner Argumentation. In >99% der Fälle, wenn ein return dort nicht steht, obwohl der Rückgabetyp aussagt, dass es dort stehen sollte, ist das nicht eine Reaktion auf eine vorherige Endlosschleife und somit wohlüberlegt, sondern ein Fehler.

    Ich wiederhole nochmal, wieso ich finde, dass ein return erzwungen sein sollte. Das beruht auf den möglichen "Fehlern", die man mit return begehen kann:

    1. Lässt man das return weg, wenn ein Rückgabetyp angegeben ist und dieser weiter verwendet wird, so begeht man einen erheblichen Fehler; der Code ist syntaktisch korrekt, semantisch aber verheerend
    2. Lässt man das return nicht weg, wenn ein Rückgabetyp angegeben ist und dieser nicht weiter verwendet wird, so begeht man den Fehler, dass man eine Zeile zu viel Text geschrieben hat; gleichzeitig gibt man aber, falls der Rückgabewert potenziell irgendwann einmal benutzt wird, einen Wert an, der vielleicht später interessant sein könnte.

    Der Fehler bei 1 ist also fatal, der bei 2 in meinen Augen lächerlich gering, wenn überhaupt vorhanden. Und die Szenarien, in denen man 2) begeht, erscheinen mir nur im Kontext eines fehlerhaften Designs. Ob sich MSVC also mit dem Verhalten exakt an den Standard hält oder nicht, damit werden Fehler vermieden und realistische Einschränkungen nicht geschaffen (diese gestellten Beispiele, bei denen ich immer auch ein return zurückgeben würde, sind in meinen Augen alles andere als realistisch). Daher befürworte ich die Fehlermeldung.



  • @ Eisflamme
    Bitte ordentlich lesen

    SeppJ schrieb:

    (Nehmen wir mal C89, wo es in main kein implizites return 0 gibt)



  • Ändert in meinen Augen nichts, hab's aber ergänzt, danke.



  • Sone schrieb:

    out schrieb:

    Sone schrieb:

    GCC.

    Dann mal +1 für MSVC 😃

    Lol. Dafür das er mit dem Standard inkompatibel ist? 👎

    GCC tut nur seine Pflicht :p

    Undefiniertes Verhalten erlaubt auch, das Programm gar nicht erst zu übersetzen. Siehe Standard:

    1.3.24 [defns.undefined]
    […]
    behavior for which this International Standard imposes no requirements
    [ Note: […] Permissible undefined behavior
    ranges from […] to terminating a translation or execution (with the issuance of a diagnostic message).
    […].
    — end note ]


  • Mod

    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)


  • 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.
    }
    

Anmelden zum Antworten