MS Code Analyse



  • Kann ich so blöd sein? Wo soll ich denn noexcept hinschreiben?



  • @tormen_bln sagte in MS Code Analyse:

    1. C26455/C26439 ist keine harte Regel, sondern bloss was MS gut & richtig findet. Kann man so sehen, muss man aber nicht. Das "may not" im Text der Warning ist also übertrieben, die korrekte Formulierung wäre eher "should not".
    2. C26455/C26439 wird u.A. bei Default-Konstruktoren erzeugt wenn diese nicht noexcept markiert sind.
    3. Die Frage wo du - im Zusammenhang mit C26455/C26439 in Zeile 11 - in deinem Code noch noexcept hinschreiben sollte kann man mit "nirgends" beantworten. Denn: Der WinAPI Default-Konstruktor kann Exceptions werfen, da er einen std::string mit nicht-leerem Inhalt initialisieren muss. Was nicht noexcept geht.

    Wenn du die Warning vermeiden willst bleiben dir folgende Möglichkeiten:

    a) Entferne den WinAPI Default-Konstruktor. D.h. er muss nen Parameter bekommen der keinen Default-Wert hat. Der naheliegende Kandidat wäre der "last error" Wert. Den Aufruf von GetLastError im WinAPI Konstruktor halte ich sowieso für schlechten Stil. Grund: wenn man GetLastError nicht direkt sofort nach dem fehlgeschlagenen Aufruf macht, kann man kaum sicherstellen dass dazwischen nicht ein weiterer WinAPI Aufruf gemacht wurde, der evtl. den "last error" Wert ändert. D.h. man kann kaum sicher stellen dass man den richtigen Fehlercode bekommt. Den User zu zwingen GetLastError selbst aufzurufen könnte man also sogar als Verbesserung ansehen (ich sehe das so).
    Wenn du unbedingt einen Convenience-Helper möchtest der GetLastError aufruft und dir ein damit initialisiertes WinAPI Exception Objekt gibt kannst du das ja leicht machen, z.B. indem du WinAPI eine statische Funktion fromLastError gibst die ein WinAPI Objekt zurückgibt und ca. so aussehen könnte:

    class WinAPI ... {
        // ...
        static WinAPI fromLastError() {
            auto const errorCode = ::GetLastError(); // Das sollte auf jeden Fall die erste Anweisung in dieser
                                                     // Funktion bleiben, sonst besteht wieder die Gefahr dass du den falschen Wert bekommst
            return WinAPI{ errorCode };
        }
        // ...
    };
    

    b) Ändere deine Exception-Klasse so, dass die Fehlermeldung erst zusammengebaut wird wenn getMessage bzw. getDetailedDescription aufgerufen werden. Dann könntest du statt einen std::string zu initialisieren im WinAPI Default-Konstruktor einfach nur den Fehlercode im Objekt ablegen (was noexcept geht, ist ja bloss ein DWORD), und FormatMessage erst in getMessage bzw. getDetailedDescription aufrufen.

    Eine weitere Beobachtung: Exceptions sollten immer noexcept-kopierbar sein. Sonst kann es passieren dass beim Fangen einer Exception wieder eine Exception erzeugt wird, was dann aua ist. Die einfachste Möglichkeit das zu erreichen, ist alles was beim Kopieren Allokationen erfordert (bzw. allgemein alles was beim Kopieren Exceptions werfen kann) in eine kleine Hilfs-struct zu packen, und in der Exception-Klasse dann nur einen shared_ptr<HelperStruct const> zu halten. Da shared_ptr noexcept zu kopieren ist löst das das Problem. Bzw. wenn gar nichts gehalten werden muss was beim Kopieren werfen könnte (so wie hier wenn du Variante (b) wählst), gibt es sowieso kein Problem.

    Noch ein letzter Punkt: sei mit noexcept sehr vorsichtig. Wenn du an eine Funktion noexcept dranschreibst, dann garantierst du damit dass aus der Funktion keine Exceptions rausfliegen. Anders als bei const oder ähnlichen Dingen hilft dir der Compiler allerdings nicht dabei dieses Versprechen zu halten, indem er dir einen Fehler um die Ohren wirft wenn du noexcept wo dazuschreibst "wo es nicht hingehört". Statt dessen wird das Programm fehlerfrei übersetzt, und wenn während der Laufzeit dann eine Exception aus einer noexcept Funktion rausfliegen würde (weil du nicht aufgepasst hast), dann wird an der Stelle statt dessen einfach dein Programm abgebrochen - std::terminate. Schwupps-weg, einfach so.
    Möglicherweise wird dir der Static Analyzer bei solchen Fehlern eine Warnung geben (-> C26447), aber verlassen sollte man sich mMn. nicht unbedingt darauf.

    ps:
    c) Markiere deinen Default-Konstruktor als noexcept(false). Das sollte den Static Analyzer von Visual Studio wissen lassen dass du dir darüber im Klaren bist dass der Konstruktor nicht noexcept ist, und die Warnung sollte nicht mehr kommen. Zumindest steht das so hier https://developercommunity.visualstudio.com/content/problem/236762/c-code-analysis-warning-c26439-this-kind-of-functi.html beschrieben.



  • Dieser Beitrag wurde gelöscht!


  • @hustbaer Vielen, vielen Dank und sorry, dass meine Antwort so spät kommt. Das ist mal eine Aussage, mit der ich etwas anfangen kann. Auch die Tipps zum schlechten Stil, danke nochmals. Ich werde das alles umbauen und bin somit diese Warnungen erstmal los. Gibt ja noch weitere, die bearbeitet werden wollen 🙂

    Auch Dank an alle anderen, die versucht haben, mir zu helfen.

    MfG Torsten


Anmelden zum Antworten