MS Code Analyse



  • Hallo,

    ich würde gerne unter Visual Studio 2017 die MS Code Analyse nutzen, wobei mein Anspruch ist, dass ALLE Warnungen beseitigt werden.

    Ich habe eine Klasse Exception mit verschiedenen Konstruktoren. Die Klasse besitzt unter anderem zwei Member der Klasse std::string. Die MS Code Analyse ist für alle MS Regeln aktiviert.

    Ich erhalte beim Move Konstruktor die Warnung: "Warning C26439 This kind of function may not throw. Declare it 'noexcept' (f.6)."

    Exception::Exception(Exception&& _exception)
    	: message(_exception.message)
    {
    }
    

    Gut, denke ich mir, also deklariere ich den Konstruktor mit noexcept:

    Exception::Exception(Exception&& _exception) noexcept
    	: message(_exception.message)
    {
    }
    

    Nun erhalte ich die folgende Warnung: "Warning C26447 The function is declared 'noexcept' but calls function 'allocator<char> >()' which may throw exceptions (f.6)."

    Ich stehe gerade irgendwie auf dem Schlauch. Was soll ich nun tun? Gibt es dafür eine logische Erklärung? Mache ich etwas falsch?

    MfG Torsten



  • @tormen_bln sagte in MS Code Analyse:

    Was soll ich nun tun?

    : message{ std::move(_exception.message) }
    

    @manni66 sagt dir den Rest. 😉

    @tormen_bln sagte in MS Code Analyse:

    Gibt es dafür eine logische Erklärung?

    : message{ _exception.message }
    

    nimmt den copy-ctor von std::string der seinerseits Speicher alloziiert was nicht nothrow geht.



  • Warum schreibst du den Konstruktor selbst?



  • @manni66 Habe ich mir so angewöhnt (Rule of five). Ich weiß, dass es in dem Fall nicht notwendig ist. Vielleicht nehme ich das auch noch mal raus. Ich probiere euren Vorschlag heute Abend aus.

    Was mich allerdings wundert ist, dass die Warnung beim Kopier Konstruktor nicht auftritt. Ist das tatsächlich so geplant, bzw. wird das anders behandelt (abgesehen vom Unterschied zwischen Copy und Move)?

    MfG Torsten



  • Welche Warnung? Welcher Kopierkonstruktor? Code?



  • @tormen_bln

    Habe ich mir so angewöhnt (Rule of five).

    Gewöhne dir Rule of 0 an.



  • Copy Con:

    Exception::Exception(const Exception& _exception)
    	: message(_exception.message)
    {
    }
    

    Move Con:

    Exception::Exception(Exception&& _exception)
    	: message(_exception.message)
    {
    }
    

    Während ich die Warnung C26439 beim Move Constructor bekomme, wird sie beim Copy Constructor nicht angezeigt. Zum Einen frage ich mich, wo da der Unterschied ist, zum anderen möchte ich die Warnung natürlich beheben.

    @manni66 Gibt es Probleme, wenn ich die Rule of five anwende?

    MfG Torsten



  • Überleg dir mal was der unterschied sein könnte zwischen den beiden Begriffen

    • Copy-Konstruktor
    • Move-Konstruktor

    Dann sollte es eventuell klar werden.



  • @tormen_bln sagte in MS Code Analyse:

    @manni66 Gibt es Probleme, wenn ich die Rule of five anwende?

    Du nutzt jede Chance, einen Fehler zu programmieren 😉
    Ein Leser des Codes nimmt an, du hättest dir etwas besonderts intelligentes überlegt, obwohl du nur versuchst, den automatisch generierten "Code" selbst zu programmieren..

    Deim Movekonstruktor kopiert z.B. den String. Deswegen kommt dann bei noexcept eine Warnung.



  • Um es noch expliziter zu sagen:

    Exception::Exception(Exception&& _exception) // <--- move hier bei Parameterübergabe
    	: message(_exception.message)        // Kopie hier, da ohne std::move. Ist das beabsichtigt?
    


  • @manni66 und @wob Vielen Dank für eure Tips, das werde ich heute Abend ausprobieren.

    MfG Torsten



  • @manni66 sagte in MS Code Analyse:

    Gewöhne dir Rule of 0 an.

    QFT!



  • Selbststudium ist halt sehr schwierig 😞

    Erstmal vielen Dank für die Tips und die Links. Ich muss mir das im Detail mal anschauen, um es endlich zu verstehen.

    Ich habe den Code nun so weit abgespeckt:

    namespace StdLibs
    {
    	class Exception
    	{
    		public:
    			Exception() = default;
    			Exception(const Exception& _exception) = default;
    			Exception(Exception&& _exception) = default;
    			Exception(const std::string& _message);
    
    			virtual ~Exception() = default;
    
    			Exception& operator=(const Exception& _exception) = default;
    			Exception& operator=(Exception&& _exception) = default;
    
    			virtual const std::string& getMessage() const noexcept;
    
    		private:
    			std::string message;
    	};
    }
    

    und

    #include "exception.hpp"
    
    namespace StdLibs
    {
    	Exception::Exception(const std::string& _message)
    		: message(_message)
    	{
    	}
    
    	const std::string& Exception::getMessage() const noexcept
    	{
    		return message;
    	}
    }
    

    Die Warnung sind alle weg. Habt ihr jetzt noch weitere Ratschläge für mich?

    Kurze Frage noch zu unit tests. Die Tests für automatisch generierte Konstruktoren und Operatoren kann ich dann getrost weglassen, richtig?

    MfG Torsten



  • Naja. Wenn Du testen möchtest dass sich alles "automatisch richtig konstruiert" können die auch Sinn machen!



  • Die Dateiendung .hpp benutzt man häufig als Indikator, dass in der entsprechenden Datei funktionaler Code steht, z.B. bei templates. Gebräuchlich für normale Headerdateien ist die Endung .h.



  • @DocShoe Danke, für deinen Tipp.

    Sorry, dass ich nochmal stören muss. Aber ich habe es offensichtlich noch immer nicht ganz verstanden. Ich habe meine Klasse Exception, welche auch als Basisklasse dienen soll, wie folgt erweitert:

    #pragma once
    
    #include <string>
    
    namespace StdLibs
    {
    	class Exception
    	{
    		protected:
    			Exception() = default;
    
    		public:
    			Exception(const Exception& _exception) = default;
    			Exception(Exception&& _exception) = default;
    			Exception(const std::string& _message);
    
    			virtual ~Exception() = default;
    
    			Exception& operator=(const Exception& _exception) = default;
    			Exception& operator=(Exception&& _exception) = default;
    
    			virtual void setDetailedDescription(const std::string& _detailedDescription);
    
    			virtual const std::string& getMessage() const noexcept;
    			virtual const std::string& getDetailedDescription() const noexcept;
    
    		private:
    			std::string message;
    			std::string detailedDescription;
    	};
    }
    

    mit

    #include "exception.hpp"
    
    namespace StdLibs
    {
    	Exception::Exception(const std::string& _message)
    		: message(_message)
    	{
    	}
    
    	void Exception::setDetailedDescription(const std::string& _detailedDescription)
    	{
    		detailedDescription = _detailedDescription;
    	}
    
    	const std::string& Exception::getMessage() const noexcept
    	{
    		return message;
    	}
    
    	const std::string& Exception::getDetailedDescription() const noexcept
    	{
    		return detailedDescription;
    	}
    }
    

    In meinem Framework möchte ich nun eine WinAPI Exception implementieren:

    #pragma once
    
    #include "../../Standard libraries/Exception/exception.hpp"
    
    namespace Framework
    {
    	namespace Exceptions
    	{
    		class WinAPI : public StdLibs::Exception
    		{
    			public:
    				WinAPI();
    				WinAPI(const WinAPI& _winAPI) = default;
    				WinAPI(WinAPI&& _winAPI) = default;
    
    			public:
    				~WinAPI() = default;
    
    			private:
    				WinAPI& operator=(const WinAPI& _winAPI) = delete;
    				WinAPI& operator=(WinAPI&& _winAPI) = delete;
    		};
    	}
    }
    

    mit

    #include "winapi.hpp"
    
    #include <Windows.h>
    
    #include <string>
    
    namespace Framework
    {
    	namespace Exceptions
    	{
    		WinAPI::WinAPI()
    			: Exception("Something goes wrong with WinAPI")
    		{
    			//const DWORD _lastErrorId = GetLastError();
    			//LPSTR _lastErrorMessage = nullptr;
    			//const size_t _size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    			//	nullptr, _lastErrorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&_lastErrorMessage, 0, nullptr);
    
    			//setDetailedDescription(std::string(_lastErrorMessage, _size));
    			//LocalFree(_lastErrorMessage);
    		}
    	}
    }
    

    Die auskommentierten Zeilen bitte erstmal ignorieren.

    Ich bekomme beim Aufruf des Konstruktors Exception nun auch hier die Warnung C26455 Default constructor may not throw. Declare it 'noexcept' (f.6).

    Ich gehe doch recht in der Annahme, dass der Konstruktor Exception(const std::string& _message) der Basisklasse aufgerufen wird? Dieser kann eine Exception werfen, da die Zeichenkette kopiert wird. Ist das soweit richtig? Denn wenn ich den Konstruktor WinAPI() mit noexcept deklariere, bekomme ich erneut die Warnung, ich soll das noexcept entfernen, da der std::allocator() aufgerufen wird, der ja nun mal eine Exception werfen kann?

    Was mache ich denn hier falsch?

    MfG Torsten



  • @tormen_bln

    Die Warnung bezieht sich doch auf Exception::Exception(), nicht auf Exception::Exception(const std::string& _message), welcher von WinAPI aufgerufen wird.



  • This post is deleted!


  • @Jockelx Hm... komme ich jetzt nicht mit. Die Warnung wird in Zeile 11 in der Quelltextdatei winapi.cpp angezeigt.

    MfG Torsten



  • @tormen_bln

    Ja und da wird doch offensichtlich der Konstruktor von Exception(std::string) aufgerufen und nicht Exception(). Letztere kann noexcept sein.


Log in to reply