assert() vs std::invalid_argument



  • Ist es legal, assert() auf ein Funktionsargument anzuwenden, oder sind dafür Exceptions gedacht?

    assert() find ich schöner, weil das kann ich später abstellen. Wenn das Programm fertig ist und fehlerfrei läuft, braucht es den Test nicht mehr und dann läuft das Programm schneller.



  • Was heisst denn "legal"? Natürlich darf man das machen und oft ist es sogar gute Praxis. Nämlich meist dann, wenn es sich um Logikfehler handelt -- d.h. Fehler, die bei korrekter Programmierung nicht mehr auftreten. Da diese gar nie auftreten dürften, kann man sie selten sinnvoll behandeln, daher ist assert() mit sofortigem Halt eine gute Sache.

    Im Gegensatz dazu stehen Laufzeitfehler wie z.B. durch Benutzereingaben oder Ressourcenverwaltung verursacht.



  • Was heißt "legal"? Das ist eine Designentscheidung. assert dient einfach nur für Debugzwecke. Damit kannst du eben überprüfen, ob im normalen Programmablauf sich alles an deine Vorgaben hält. Eine Exception zu werfen ist eher für den normalen Progammablauf. Dies bietet dem Programmierer die Möglichkeit auf den Fehler zu reagieren und du bist abgesichert gegen Fehler, die du beim Debuggen nicht gefunden hast (zB durch fehlerhafte Nutzereingaben). Dafür haben die Überprüfungen/Exceptions einen teilweise recht enormen Overhead.



  • Steht assert() nicht in direkter Konkurrenz zu std::logic_error und seinen Ableitungen?



  • Eigentlich schon.
    Was ich aber ganz nett finde ist ein Makro zu schreiben, dass sich je nach Flags wie Assert oder Exceptionwurf verhält.

    zb

    #define ASSERT_USE_EXCEPTION
    
    MY_ASSERT(1 == 2); // Exception fliegt.
    
    #undef ASSERT_USE_EXCEPTION
    
    MY_ASSERT(1 == 2); // Normales Assert
    

    Benutzt hab ich das aber eigentlich nur in Bibliotheken, für die ich Python-Wrapper schreibe. Ist nämlich unschön wenn assert den Python-Interpreter abschießt.



  • Ethon schrieb:

    Was ich aber ganz nett finde ist ein Makro zu schreiben, dass sich je nach Flags wie Assert oder Exceptionwurf verhält.

    Und im Usercode steht dann abhängig von diesem Makro ein catch , oder wie? Wäre es nicht klüger, für Python direkt ein spezifisches Assert-Makro zu schreiben als eines zu verwenden, das sich je nach Kontext anders verhält?



  • Nexus schrieb:

    Ethon schrieb:

    Was ich aber ganz nett finde ist ein Makro zu schreiben, dass sich je nach Flags wie Assert oder Exceptionwurf verhält.

    Und im Usercode steht dann abhängig von diesem Makro ein catch , oder wie? Wäre es nicht klüger, für Python direkt ein spezifisches Assert-Makro zu schreiben als eines zu verwenden, das sich je nach Kontext anders verhält?

    Naja, bei mir fliegt die Exception statt dem Assert nur wenn "BUILD_PYTHON_MODULE" definiert ist, das passiert nicht ausversehen. 😉
    Im C++-Code gehe ich nicht davon aus, dass das jemand abfängt.



  • Ich habe früher assert benutzt, bis ich irgendwann raus gefunden hab, dass das total unkonfortabel ist. assert bringt dir nur etwas, wenn der Debugger an ist. Eine schöne Exception kann dir direkt Fehlermeldung + Position im Code mit raus geben.



  • otze schrieb:

    Position im Code

    Wie das? Das wär ja mal genial, hab ich aber noch nicht endeckt.



  • Hacker schrieb:

    otze schrieb:

    Position im Code

    Wie das? Das wär ja mal genial, hab ich aber noch nicht endeckt.

    Macros.



  • struct assertion_failure : std::logic_error
    {
        assertion_failure(std::string const& what)
            : logic_error(what)
        {}
    };
    
    #define TO_STRING_I(x) #x
    #define TO_STRING(x) TO_STRING_I(x)
    #define ASSERT(cond) if(!(cond)) throw assertion_failure(__FILE__ "(" TO_STRING(__LINE__) "): ASSERT(" #cond ")");
    


  • otze schrieb:

    Hacker schrieb:

    otze schrieb:

    Position im Code

    Wie das? Das wär ja mal genial, hab ich aber noch nicht endeckt.

    Macros.

    Ich hab manchmal sowas gemacht (nicht heulen):

    void check_pos_throw(float x, const char* n_x, float y, const char* n_y)
    {
        if(x < 0
        || y < 0
        || x > SCREEN_W
        || y > SCREEN_H)
            throw std::out_of_range(string("\ncheck_pos_throw: invalid arguments for position: X = ") + itostr(x) + string(", name of X = ") + string(n_x) + string(" || Y = ") + itostr(y) + string(", name of Y = ") + n_y);
    }
    #define check_pos_throw(x,y) check_pos_throw(x, #x, y, #y)
    


  • otze schrieb:

    Ich habe früher assert benutzt, bis ich irgendwann raus gefunden hab, dass das total unkonfortabel ist. assert bringt dir nur etwas, wenn der Debugger an ist. Eine schöne Exception kann dir direkt Fehlermeldung + Position im Code mit raus geben.

    Versteh ich nicht. Asserts sind doch eigentlich nur im Debugbuild aktiv. Machst du die Exceptions auch nur im Debugbuild rein? Und bei Windowsanwendungen öffnet sich beim Assert auch immer eine Messagebox und ich kann den VS-Debugger attachen.



  • Zwei Sachen versteh ich an PI's Idee nicht.

    #define TO_STRING_I(x) #x
    #define TO_STRING(x) TO_STRING_I(x)
    

    Wieso nicht gleich

    #define TO_STRING(x) #x
    

    Und wozu brauchst du die Struktur assertion_failure , wenn sie sich sowieso wie std::logic_error verhält?
    Oder findest du den Namen besser?

    Edit: Wäre das nicht dasselbe?

    #define ASSERT(cond) if(!(cond)) \
                             throw std::logic_error(__FILE__ "("# __LINE__ "): ASSERT(" #cond ")")
    


  • Hacker schrieb:

    Zwei Sachen versteh ich an PI's Idee nicht.

    #define TO_STRING_I(x) #x
    #define TO_STRING(x) TO_STRING_I(x)
    

    Wieso nicht gleich

    #define TO_STRING(x) #x
    

    Probiers aus. 😉

    Hacker schrieb:

    Und wozu brauchst du die Struktur assertion_failure , wenn sie sich sowieso wie std::logic_error verhält?

    Weil std::logic_error irgendwas ist und keine Assertion-Failure.



  • 314159265358979 schrieb:

    Hacker schrieb:

    Zwei Sachen versteh ich an PI's Idee nicht.

    #define TO_STRING_I(x) #x
    #define TO_STRING(x) TO_STRING_I(x)
    

    Wieso nicht gleich

    #define TO_STRING(x) #x
    

    Probiers aus. 😉

    Hab ich. 😕



  • Dann wirst du doch den Unterschied sehen, oder? 🙄



  • 314159265358979 schrieb:

    Dann wirst du doch den Unterschied sehen, oder? 🙄

    N-Ö?

    #define TO_STRING_I(x) #x
    #define TO_STRING(x) TO_STRING_I(x)
    
    int main()
    {
        std::cout << string() + TO_STRING_I(5) << '\n'
                  << string() +  TO_STRING(6) << '\n';
    }
    


  • Dann probiers mal mit __LINE__. 😡



  • otze schrieb:

    Ich habe früher assert benutzt, bis ich irgendwann raus gefunden hab, dass das total unkonfortabel ist. assert bringt dir nur etwas, wenn der Debugger an ist. Eine schöne Exception kann dir direkt Fehlermeldung + Position im Code mit raus geben.

    Assertions benutzt man ja auch nur um Programmierfehler abzufangen.

    assert(ptr != nullptr)
    

    Gibt mir die Information darüber dass hier ein Pointer null ist und auch die Position im Code. Die Exception bringt hier keine Vorteile, ohne Macros aber Runtime-Overhead.


Anmelden zum Antworten