Unerlaubte Argumente anzeigen



  • Hi!

    Ich würde gerne wissen, wie ich ein nicht erlaubtes Argument (z.B. negatives Monat) "anzeigen" sollte.
    In Java werfe ich einfach eine IllegalArgumentException.
    Sollte ich in C++ eine invalid_argument Exception werfen?
    Ich habe gelesen, dass man in Konstruktoren nicht unbedingt Exceptions werfen sollte, stimmt das?

    LG!



  • AndreasLeeb schrieb:

    Ich habe gelesen, dass man in Konstruktoren nicht unbedingt Exceptions werfen sollte, stimmt das?

    Das stimmt nicht. Wenn das Object mit dem falschen Parameter nicht sinnvoll Konstruierbar ist bleibt dir nichts anderes übrig.



  • tkausl schrieb:

    AndreasLeeb schrieb:

    Ich habe gelesen, dass man in Konstruktoren nicht unbedingt Exceptions werfen sollte, stimmt das?

    Das stimmt nicht. Wenn das Object mit dem falschen Parameter nicht sinnvoll Konstruierbar ist bleibt dir nichts anderes übrig.

    Ok, danke für die Antwort!
    Also in solchen Fällen immer invalid_argument werfen.



  • Nein! invalid_argument ist keine gute Lösung.

    Negativer Monat vermeidet man durch unsigned. Zu hoher Monat prüft man mit assert. Exceptions sind für Ausnahmefälle, nicht Logikfehler!



  • brassert schrieb:

    Nein! invalid_argument ist keine gute Lösung.

    Negativer Monat vermeidet man durch unsigned. Zu hoher Monat prüft man mit assert. Exceptions sind für Ausnahmefälle, nicht Logikfehler!

    Ok, danke!



  • brassert schrieb:

    Nein! invalid_argument ist keine gute Lösung.

    Negativer Monat vermeidet man durch unsigned. Zu hoher Monat prüft man mit assert. Exceptions sind für Ausnahmefälle, nicht Logikfehler!

    Pauschale Aussagen sind immer falsch.

    In der Standradlib wird zwar in der Regel so verfahren, vector.at prüft aber z.B. dennoch, ob der Index gültig ist.



  • manni66 schrieb:

    Pauschale Aussagen sind immer falsch.

    Das war die Regel, wie man verfahren sollte und als Anfänger ist man gut damit geraten sich daran ausnahmslos zu halten.

    manni66 schrieb:

    In der Standradlib wird zwar in der Regel so verfahren, vector.at prüft aber z.B. dennoch, ob der Index gültig ist.

    Das ist auch einzige Daseinsberechtigung von vector::at . Bei so allgemeinen Funktionen wie at() will man das manchmal.



  • Ich mach manchmal das hier:

    struct month
    {
    	month() noexcept
    		: from_zero(0)
    	{
    	}
    
    	static boost::optional<month> create_checked(std::uint8_t from_zero) noexcept
    	{
    		if (from_zero >= 12)
    		{
    			return boost::none;
    		}
    		return month(from_zero);
    	}
    
    	std::uint8_t index_from_zero() const noexcept
    	{
    		return from_zero;
    	}
    
    private:
    
    	std::uint8_t from_zero;
    
    	explicit month(std::uint8_t from_zero) noexcept
    		: from_zero(from_zero)
    	{
    	}
    };
    


  • brassert schrieb:

    Nein! invalid_argument ist keine gute Lösung.

    Negativer Monat vermeidet man durch unsigned. Zu hoher Monat prüft man mit assert. Exceptions sind für Ausnahmefälle, nicht Logikfehler!

    brassert schrieb:

    Das war die Regel, wie man verfahren sollte und als Anfänger ist man gut damit geraten sich daran ausnahmslos zu halten.

    Was macht man wenn der Monat aus einer User-Eingabe kommt (bitte geben Sie ihr Geburtsdatum ein)? Dann bringt einem das Assert nix und wenn ich vor dem konstruieren prüfe hab ich den check zweimal drinnen...



  • happystudent schrieb:

    brassert schrieb:

    Nein! invalid_argument ist keine gute Lösung.

    Negativer Monat vermeidet man durch unsigned. Zu hoher Monat prüft man mit assert. Exceptions sind für Ausnahmefälle, nicht Logikfehler!

    brassert schrieb:

    Das war die Regel, wie man verfahren sollte und als Anfänger ist man gut damit geraten sich daran ausnahmslos zu halten.

    Was macht man wenn der Monat aus einer User-Eingabe kommt (bitte geben Sie ihr Geburtsdatum ein)? Dann bringt einem das Assert nix und wenn ich vor dem konstruieren prüfe hab ich den check zweimal drinnen...

    Assert im Konstruktor und Validierung der Eingabe.
    ➡ Im Release ist das Assert weg aber die Eingabe muss immer noch geprüft werden, darum muss die bestehen bleiben.
    Und wenn du keine Code Duplizierung haben möchtest schreibe dir eine Validierung Funktion.

    bool validateMonth(std::uint8_t month)
    {
    // ...
    }
    
    std::uint8_t userInput = //...
    if (!validateMonth(userInput))
    {
       std::cout << "Kauf dir einen Kalender!";
    }
    
    struct month
    {
       month(std::uint8_t from_zero) : from_zero(from_zero)
       {
          assert(validateMonth(m));
          // ...
       }
       // ...
    }
    


  • C Newbie schrieb:

    happystudent schrieb:

    brassert schrieb:

    Nein! invalid_argument ist keine gute Lösung.

    Negativer Monat vermeidet man durch unsigned. Zu hoher Monat prüft man mit assert. Exceptions sind für Ausnahmefälle, nicht Logikfehler!

    brassert schrieb:

    Das war die Regel, wie man verfahren sollte und als Anfänger ist man gut damit geraten sich daran ausnahmslos zu halten.

    Was macht man wenn der Monat aus einer User-Eingabe kommt (bitte geben Sie ihr Geburtsdatum ein)? Dann bringt einem das Assert nix und wenn ich vor dem konstruieren prüfe hab ich den check zweimal drinnen...

    Assert im Konstruktor und Validierung der Eingabe.
    ➡ Im Release ist das Assert weg aber die Eingabe muss immer noch geprüft werden, darum muss die bestehen bleiben.
    Und wenn du keine Code Duplizierung haben möchtest schreibe dir eine Validierung Funktion.

    bool validateMonth(std::uint8_t month)
    {
    // ...
    }
    
    std::uint8_t userInput = //...
    if (!validateMonth(userInput))
    {
       std::cout << "Kauf dir einen Kalender!";
    }
    
    struct month
    {
       month(std::uint8_t from_zero) : from_zero(from_zero)
       {
          assert(validateMonth(m));
          // ...
       }
       // ...
    }
    

    Wenn man eine validateMonth Methode macht, wie soll man dann in der Methode verfahren, wenn das Monat < 1 oder > 12 ist?



  • AndreasLeeb schrieb:

    C Newbie schrieb:

    happystudent schrieb:

    brassert schrieb:

    Nein! invalid_argument ist keine gute Lösung.

    Negativer Monat vermeidet man durch unsigned. Zu hoher Monat prüft man mit assert. Exceptions sind für Ausnahmefälle, nicht Logikfehler!

    brassert schrieb:

    Das war die Regel, wie man verfahren sollte und als Anfänger ist man gut damit geraten sich daran ausnahmslos zu halten.

    Was macht man wenn der Monat aus einer User-Eingabe kommt (bitte geben Sie ihr Geburtsdatum ein)? Dann bringt einem das Assert nix und wenn ich vor dem konstruieren prüfe hab ich den check zweimal drinnen...

    Assert im Konstruktor und Validierung der Eingabe.
    ➡ Im Release ist das Assert weg aber die Eingabe muss immer noch geprüft werden, darum muss die bestehen bleiben.
    Und wenn du keine Code Duplizierung haben möchtest schreibe dir eine Validierung Funktion.

    bool validateMonth(std::uint8_t month)
    {
    // ...
    }
    
    std::uint8_t userInput = //...
    if (!validateMonth(userInput))
    {
       std::cout << "Kauf dir einen Kalender!";
    }
    
    struct month
    {
       month(std::uint8_t from_zero) : from_zero(from_zero)
       {
          assert(validateMonth(m));
          // ...
       }
       // ...
    }
    

    Wenn man eine validateMonth Methode macht, wie soll man dann in der Methode verfahren, wenn das Monat < 1 oder > 12 ist?

    😕

    bool validateMonth(std::uint8_t month)
    {
       return month >= 1 && month <= 12;
    }
    

    Die Behandlung von falschen month werten passiert natürlich beim Aufrufer.



  • Ok danke!



  • @brassert
    Du hast halt deine Meinung hier presentiert als ob es ein Fakt wäre, und als ob es dazu nicht mehr zu sagen gäbe.

    Es gibt aber oft gute Gründe nicht "garbage in garbage out" zu machen. Was mMn. auch der Grund dafür ist dass es vector::at überhaupt gibt.
    Und der vermutlich wichtigste Grund nicht "garbage in garbage out" zu machen ist, dass es hilft den Effekt von Fehlern einzugrenzen. Klar, Fehler sollten nicht im ausgelieferten Produkt drinnen sein. Das fehlerfreie (nicht triviale) Programm ist aber eher eine Wunschvorstellung, auf jeden Fall nichts wovon man ausgehen kann.

    Und dann muss man sich fragen: was ist besser, ein "garbage in garbage out" Programm das im Falle des Falles gröbstens Mist baut (z.B. alle Daten löscht), oder ein Programm das im Falle des Falles mit einer mehr oder weniger verständlichen Fehlermeldung abbricht?

    Das ganze ist natürlich auch oft ein Kompromiss zwischen Sicherheit und Performance.
    An Stellen wo Laufzeitprüfungen wirklich wichtig sind, ist assert() aber auf jeden Fall der falsche Weg, da im ausgelieferten Programm nix mehr davon übrig bleibt.
    Wenn man den Aufrufer "dazu ermuntern will" auf seiner Seite bereits dafür zu sorgen dass alles OK ist, kann man statt assert(exp) immer noch eine eigene Hilfsfunktion verwenden, die im Fall !exp eine Meldung ausgibt und danach abort() aufruft. Und die eben auch in einem Release-Build drinnen bleibt.



  • @C Newbie
    Für eine Funktion die true zurückliefert wenn ihr Argument eine gültige Monatsnummer ist, ist validateMonth mMn. ein schlechter Name.

    Erstmal ist validateXxx(arg) nicht klar/eindeutig, es könnten mehrere Dinge damit gemeint sein. Folgende Verhalten wären denkbar:

    1. Die Methode validiert (im Sinn von: erklärt für gültig) arg als xxx. z.B. indem der übergebene Wert in einer Liste gültiger xxx-Werte eingetragen wird, wo andere Funktionen dann nachgucken können ob sie ihrerseits einen gültigen xxx-Wert übergeben bekommen haben.
    2. Die Methode selbst ist als Assertion zu verstehen, macht also intern ein assert(arg is a valid xxx) .
    3. Ähnlich (2) nur dass die Methode intern ein if (arg is NOT a valid xxx) throw ... macht.
    4. Die Methode prüft die Validität von arg und gibt dann true bzw. false zurück.
    5. Die Methode biegt arg so zurecht dass dabei ein gültiges xxx rauskommt. z.B. durch Clamping, Modulo-Arithmetik oder Ersetzen von ungültigen Werten mit einem Default.

    Deine validateXxx(arg) macht jetzt (4). Und das ist doppelt schlimm, denn für (4) gibt es einen super einfachen, oft verwendeten und auch 100% unmisverständlichen anderen Namen: isValidXxx(arg)

    (Und auch für (1), (2), (3) und (5) gibt es mMn. viel bessere Namen, wobei diese Fälle schon eine Spur schwieriger sind als (4))



  • Was ist eigentlich an meinem Ansatz mit dem statischen Pseudokonstruktor und optional so schlimm, dass der gänzlich ignoriert wird?



  • TyRoXx schrieb:

    Was ist eigentlich an meinem Ansatz mit dem statischen Pseudokonstruktor und optional so schlimm, dass der gänzlich ignoriert wird?

    Viel zu umständlich?



  • hustbaer schrieb:

    @C Newbie
    Für eine Funktion die true zurückliefert wenn ihr Argument eine gültige Monatsnummer ist, ist validateMonth mMn. ein schlechter Name.

    Erstmal ist validateXxx(arg) nicht klar/eindeutig, es könnten mehrere Dinge damit gemeint sein. Folgende Verhalten wären denkbar:

    1. Die Methode validiert (im Sinn von: erklärt für gültig) arg als xxx. z.B. indem der übergebene Wert in einer Liste gültiger xxx-Werte eingetragen wird, wo andere Funktionen dann nachgucken können ob sie ihrerseits einen gültigen xxx-Wert übergeben bekommen haben.
    2. Die Methode selbst ist als Assertion zu verstehen, macht also intern ein assert(arg is a valid xxx) .
    3. Ähnlich (2) nur dass die Methode intern ein if (arg is NOT a valid xxx) throw ... macht.
    4. Die Methode prüft die Validität von arg und gibt dann true bzw. false zurück.
    5. Die Methode biegt arg so zurecht dass dabei ein gültiges xxx rauskommt. z.B. durch Clamping, Modulo-Arithmetik oder Ersetzen von ungültigen Werten mit einem Default.

    Deine validateXxx(arg) macht jetzt (4). Und das ist doppelt schlimm, denn für (4) gibt es einen super einfachen, oft verwendeten und auch 100% unmisverständlichen anderen Namen: isValidXxx(arg)

    (Und auch für (1), (2), (3) und (5) gibt es mMn. viel bessere Namen, wobei diese Fälle schon eine Spur schwieriger sind als (4))

    Ja ich gebe dir recht das ich den Name suboptimal gewählt habe,
    jetzt würde mich noch interesieren welche Namen du für (1), (2), (3) und (5) nehmen würdest? (Und wie man bei der Signatur überhaupt auf (5) kommen kann 🤡 )
    (1) declareValid?
    (2) assertValid?
    (3) assureValid 😞
    (5) fitToValidValues 😞



    1. markValidXxx, tagValidXxx, rememberValidXxxValue - etwas in der Art
    2. Ja, ganz klar assertValidXxx
    3. Schwierig wenn man es von (2) abgrenzen will (was ich nicht unbedingt nötig finde). "Assure" finde ich nicht ganz passend. requireValidXxx vielleicht.
    4. makeValidXxx, forceValidXxx oder substituteInvalidXxxValues . Bzw. auch spezifischer clampToValidXxx oder substituteInvalidXxxWithDefault .

    C Newbie schrieb:

    (Und wie man bei der Signatur überhaupt auf (5) kommen kann 🤡 )

    Das ist ne gute Frage. Müsste ich denjenigen fragen der den Code wo ich es gesehen habe geschrieben hat - dummerweise arbeitet der nicht mehr bei uns. Wobei ich zugeben muss dass ich mir beim Funktionsnamen gerade nicht 100% sicher bin. Ich glaube es war validateXxx , kann aber auch verifyXxx gewesen sein (was aber mMn. noch weniger Sinn machen würde).


Log in to reply