mit einem großen zu einem Kommentar Reaktion ist eine schlechte Idee, so ist hier meine ausführliche Antwort auf den folgenden Themen:
Obwohl dies möglich ist, es ist ein Schmerz wäre, dass alle in definieren ist Testfalldateien. Auch dies ist nicht auf BOOST_REQUIRE beschränkt, sondern gilt auch für Assert, SDL_Assert oder jedes andere benutzerdefinierte Makro, das der Benutzer verwenden könnte.
Man sollte verstehen, dass es drei Arten von Testmakros gibt, die jeweils separat besprochen werden sollten.
Makros des ersten Typs warnen Sie einfach, dass in der Debug-Version etwas schief gelaufen ist. Ein typisches Beispiel ist assert
Makro. Der folgende Code wird dazu führen, PVS-Studio Analyzer eine Warnung zu generieren:
T* p = dynamic_cast<T *>(x);
assert(p);
p->foo();
Der Analysator wird einen möglichen Nullzeiger weist darauf hin, dereferencing hier und wird richtig sein. Ein Check, der assert
verwendet, ist nicht ausreichend, da er aus der Release-Version entfernt wird. Das heißt, es stellt sich heraus, dass es keine Überprüfung gibt. Ein besserer Weg, es zu implementieren ist, den Code in etwa wie folgt zu umschreiben:
T* p = dynamic_cast<T *>(x);
if (p == nullptr)
{
assert(false);
throw Error;
}
p->foo();
Dieser Code wird nicht die Warnung auslösen.
Sie können argumentieren, dass Sie 100% sicher sind, dass dynamic_cast
niemals nullptr
zurückgeben wird. Ich akzeptiere dieses Argument nicht. Wenn Sie absolut sicher sind, dass die Besetzung IMMER korrekt ist, sollten Sie die schnellere static_cast
verwenden. Wenn Sie nicht so sicher sind, müssen Sie den Zeiger testen, bevor Sie ihn dereferenzieren.
Nun, OK, ich verstehe Ihren Standpunkt. Sie sind sicher, dass der Code in Ordnung ist, aber Sie müssen diese Überprüfung mit dynamic_cast nur für den Fall haben. OK, verwenden Sie dann den folgenden Code:
assert(dynamic_cast<T *>(x) != nullptr);
T* p = static_cast<T *>(x);
p->foo();
Ich mag es nicht, aber zumindest ist es schneller, da der langsame dynamic_cast Operator in der Release-Version wird weggelassen wird, während der Analysator schweigen wird.
Weiter mit der nächsten Art von Makros.
Makros des zweiten Typs warnen Sie einfach, dass in der Debug-Version etwas schief gelaufen ist und in Tests verwendet wird. Sie unterscheiden sich vom vorherigen Typ dadurch, dass sie den zu testenden Algorithmus stoppen, wenn die Bedingung falsch ist und eine Fehlermeldung generiert.
Das grundlegende Problem mit diesen Makros besteht darin, dass die Funktionen nicht als nicht wiederkehrend gekennzeichnet sind. Hier ist ein Beispiel.
Angenommen, wir haben eine Funktion, die eine Fehlermeldung generiert, indem sie eine Ausnahme auslöst. Dies ist, was seine Erklärung wie folgt aussieht:
void Error(const char *message);
Und das ist, wie der Test Makro deklariert wird:
#define ENSURE(x) do { if (!x) Error("zzzz"); } while (0)
Mit dem Zeiger:
T* p = dynamic_cast<T *>(x);
ENSURE(p);
p->foo();
Der Analysator wird eine Warnung ausgeben eine mögliche Null-Zeiger-Dereferenzierung, aber der Code ist tatsächlich sicher. Wenn der Zeiger Null ist, löst die Funktion Error
eine Ausnahme aus und verhindert so die Dereferenzierung des Zeigers.
Wir müssen einfach den Analysator, darüber sagen durch eine der Funktions Annotation Mittel verwenden, zum Beispiel:
[[noreturn]] void Error(const char *message);
oder:
__declspec(noreturn) void Error(const char *message);
Dies wird helfen, die falsche Warnung zu beseitigen. Wie Sie sehen können, ist es in den meisten Fällen recht einfach, Dinge zu beheben, wenn Sie Ihre eigenen Makros verwenden.
Es könnte jedoch schwieriger sein, wenn Sie mit unvorsichtig implementierten Makros aus Bibliotheken von Drittanbietern umgehen.
Dies führt uns zu der dritten Art von Makros. Sie können sie nicht ändern, und der Analysator kann nicht herausfinden, wie genau sie funktionieren. Dies ist eine häufige Situation, da Makros auf ziemlich exotische Weise implementiert werden können.
Es gibt drei Möglichkeiten für Sie in diesem Fall links:
- unterdrücken die Warnung eines der falsch-positiven Suppression Mittel, das in den documentation beschrieben;
- Verwenden Sie die Technik, die ich in der vorherigen beschrieben answer;
- mailen Sie uns.
Wir fügen nach und nach Unterstützung für verschiedene knifflige Makros aus beliebten Bibliotheken hinzu.In der Tat ist der Analysator bereits mit den meisten spezifischen Makros vertraut, auf die Sie stoßen könnten, aber die Vorstellungskraft der Programmierer ist unerschöpflich, und wir können nicht jede mögliche Implementierung vorhersehen.
Danke für die Antwort. Obwohl dies möglich ist, wäre es sehr mühsam, dies in allen Testfalldateien zu definieren. Dies ist nicht nur auf "BOOST_REQUIRE" beschränkt, sondern gilt auch für "assert", "SDL_Assert" oder jedes andere benutzerdefinierte Makro, das der Benutzer verwenden könnte. Also sehe ich 2 Lösungen: Entweder lasse der Benutzer eine Liste von "assert" -macro-Namen angeben, nach denen die Bedingung als wahr angenommen wird (da dies der Grund für die Assert ist). – Flamefire
Oder mindestens eine globale, PVS-only-Datei mit Defines, die von PVS gelesen werden und angenommen werden, verwendet zu werden (ähnlich wie der 'cpp.hint'). Sie können also solche Definitionen an einem zentralen Ort platzieren. Ich denke jedoch, dass dies schwierig sein wird, wie es in jeder Datei möglich ist. – Flamefire