Problem mit Software Architektur (C++)
-
Ishildur schrieb:
Hehe, da kann Abhilfe geschaffen werden:
... disable stupid and useless compiler - warningsViele der von dir auskommentierten Warnungen würde ich in einem realen Projekt niemals auskommentieren, und sehe ich in der Regel nicht nur als Warnung, sondern als Fehler an.
Ich hoffe jedenfalls nicht, das man diesen Block ohne nachzudenken einfach in seinen Code kopiert. Es gibt zwar Fälle, in denen ich Warnungen deaktiviere, aber dann so lokal wie möglich und nur nach vorheriger Prüfung - Jede Warnung sollte man sich erst einmal zu Herzen nehmen, und dann prüfen ob sie in diesem Fall berechtigt ist (Und möglichst maximal auf Dateiebene deaktivieren, wenn nicht gar auf Blockebene).
-
@Nukularfüsiker
Seit wann kann ein linker Pure Virtual Functions entfernen?
-
@asc
Welche Warnung meinst du konkret, die man nicht einfach ausschalten sollte?#pragma warning(disable:4100) // unreferenced formal parameter [unused method parameters]
Viele Interfaces erwarten Parameter, welche jedoch von einigen konkreten Implementierungen nicht genutzt werden.
#pragma warning(disable:4127) // conditional expression is constant [while(true)]
Oft hat man keine Ahnung, wie oft eine schleife durchlaufen wird, sie wird mit einem break verlassen.
#pragma warning(disable:4482) // nonstandard extension used: enum
Ja genau, ich darf keine enums benutzen
#pragma warning(disable:4706) // assignment within conditional expression
Muss ich die Methode wie strlen usw. 2mal aufrufen, einmal für den Test und dann noch einmal für die effektive Zuweisung (genau!)
#pragma warning(disable:4715) // not all control paths return a value
Methoden, welche einen return value != void haben und eine exception werfen, werden nicht mehr kompiliert.
#pragma warning(disable:4701) // potentially uninitialized local variable used
Vielfach hat man variablen, welche man mit Werten aus einer Datei oder anderen Datenquellen füllt, diese dann vorher noch explizit mit null zu füllen, nur um sich gleich anschliessend auf der nächsten Zeile wieder zu überschreiben. Naja....
#pragma warning(disable:4238) // nonstandard extension used : class rvalue used as lvalue
SetVector(&Vector3(0.0f,0.0f,0.0f))
Was soll daran falsch sein?#pragma warning(disable:4996) // 'stricmp': The POSIX name for this item is deprecated.
Und wie lautet der richtige Name? Habe ich nirgends gefunden...
#pragma warning(disable:4250) // inheritance via dominance (diamond of death with interfaces)
Hier sage ich mal nichts, weil habe diese Zeile erst vorhin hinzugefügt und weiss noch nicht, was das für Konsequenzen haben wird...
-
OMG
-
OMG?
-
Ishildur schrieb:
#pragma warning(disable:4100) // unreferenced formal parameter [unused method parameters]
Viele Interfaces erwarten Parameter, welche jedoch von einigen konkreten Implementierungen nicht genutzt werden.
Parameter aus dem Interface rausschmeißen, wenn diese nicht gebraucht werden.
Ishildur schrieb:
#pragma warning(disable:4127) // conditional expression is constant [while(true)]
Oft hat man keine Ahnung, wie oft eine schleife durchlaufen wird, sie wird mit einem break verlassen.
Schlechter Stil. Stattdessen eine bool'sche Variable nutzen.
Ishildur schrieb:
#pragma warning(disable:4482) // nonstandard extension used: enum
Ja genau, ich darf keine enums benutzen
Warum nicht?
Ishildur schrieb:
#pragma warning(disable:4706) // assignment within conditional expression
Muss ich die Methode wie strlen usw. 2mal aufrufen, einmal für den Test und dann noch einmal für die effektive Zuweisung (genau!)
Schlechter Stil. Variable deklarieren und dort zuweisen, dann diese Variable nutzen.
Ishildur schrieb:
#pragma warning(disable:4715) // not all control paths return a value
Methoden, welche einen return value != void haben und eine exception werfen, werden nicht mehr kompiliert.
Schlechter Stil. Immer komplette Pfade haben.
Ishildur schrieb:
#pragma warning(disable:4701) // potentially uninitialized local variable used
Vielfach hat man variablen, welche man mit Werten aus einer Datei oder anderen Datenquellen füllt, diese dann vorher noch explizit mit null zu füllen, nur um sich gleich anschliessend auf der nächsten Zeile wieder zu überschreiben. Naja....
Initialisieren. Wird es nicht benutzt, wird es eh wegoptimiert. Und wird es benutzt, beugt man so Fehler vor.
Ishildur schrieb:
#pragma warning(disable:4238) // nonstandard extension used : class rvalue used as lvalue
SetVector(&Vector3(0.0f,0.0f,0.0f))
Was soll daran falsch sein?Es ist einfach falsch. Erwartet das Ding einen Zeiger? Dann übergib einen Zeiger auf ein richtiges Objekt, kein temporäres. Sonst übergib konkrete Kopien oder eine const Referenz.
Ishildur schrieb:
#pragma warning(disable:4996) // 'stricmp': The POSIX name for this item is deprecated.
Und wie lautet der richtige Name? Habe ich nirgends gefunden...
Welch wunderbar portablen Code du schreibst. :> Schonmal von std::string gehört?
Ishildur schrieb:
#pragma warning(disable:4250) // inheritance via dominance (diamond of death with interfaces)
Hier sage ich mal nichts, weil habe diese Zeile erst vorhin hinzugefügt und weiss noch nicht, was das für Konsequenzen haben wird...
Du entfernst ernsthaft Warnungen, von denen du keine Ahnung hast? Autsch.
-
Ishildur schrieb:
@Nukularfüsiker
Seit wann kann ein linker Pure Virtual Functions entfernen?Das Warum überblicke ich gerade auch nicht, aber das ist es, was die Meldung besagt.
Ishildur schrieb:
@asc
Welche Warnung meinst du konkret, die man nicht einfach ausschalten sollte?Also ich würde ja sagen man sollte zumindest die nicht ausschalten, deren Sinn man nicht genau verstanden hat. Und das ist bei dir bei einigen der genannten der Fall.
Ishildur schrieb:
#pragma warning(disable:4127) // conditional expression is constant [while(true)]
Oft hat man keine Ahnung, wie oft eine schleife durchlaufen wird, sie wird mit einem break verlassen.
for(;;)
bringt keine Warnung.#pragma warning(disable:4482) // nonstandard extension used: enum
Ja genau, ich darf keine enums benutzen
Du hast sicher sowas gemacht:
int foo = Enum::Wert
Es ist aber nicht erlaubt,
Enum::
zu spezifizieren.#pragma warning(disable:4706) // assignment within conditional expression
Muss ich die Methode wie strlen usw. 2mal aufrufen, einmal für den Test und dann noch einmal für die effektive Zuweisung (genau!)
Die Erklärung verstehe ich nicht. Und auch nicht, was strlen in einem C++-Program verloren hat.
#pragma warning(disable:4715) // not all control paths return a value
Methoden, welche einen return value != void haben und eine exception werfen, werden nicht mehr kompiliert.
Zeig mal ein Beispiel.
#pragma warning(disable:4701) // potentially uninitialized local variable used
Vielfach hat man variablen, welche man mit Werten aus einer Datei oder anderen Datenquellen füllt, diese dann vorher noch explizit mit null zu füllen, nur um sich gleich anschliessend auf der nächsten Zeile wieder zu überschreiben.
Das sollte eigentlich keine Warnung verursachen. Zeig mal ein Beispiel.
#pragma warning(disable:4238) // nonstandard extension used : class rvalue used as lvalue
SetVector(&Vector3(0.0f,0.0f,0.0f))
Was soll daran falsch sein?Ein rvalue hat nun mal keine Adresse.
#pragma warning(disable:4996) // 'stricmp': The POSIX name for this item is deprecated.
Und wie lautet der richtige Name? Habe ich nirgends gefunden...
Eigentliches Problem: du benutzt C-Funktionen in C++.
-
okok, ich glaube wir schweifen ein wenig ab. Guter Stiel / Schlechter Stiel, C in C++. Also nur ganz kurz, oft hat man ja bestehenden Code oder auch externe Dinge im Source vorliegen und direkt hineinkompiliert werden müssen. Zu den rvalues. Jeder der schon mal mit der DirectX Extension Library zu tun hatte, wird das Problem kennen
Nein viel interessanter ist nun glaube ich schon der Linkerfehler...
-
Ishildur schrieb:
okok, ich glaube wir schweifen ein wenig ab. Guter Stiel / Schlechter Stiel, C in C++.
Also da geht es ja nicht nur um Stilfragen, sondern um handfeste Fehler, die du kurzerhand mit
disable stupid and useless compiler - warnings
abgewürgt hast. Die kommen irgendwann zurück und beißen dich in den Allerwertesten. Wird sich noch zeigen, wer stupid und useless war
Zu den rvalues. Jeder der schon mal mit der DirectX Extension Library zu tun hatte, wird das Problem kennen
Nee, erklär mal.
-
Ishildur schrieb:
@Nukularfüsiker
Seit wann kann ein linker Pure Virtual Functions entfernen?Wenn sie nie aufgerufen werden - warum nicht?
Ein minimales kompilierbares Beispiel wäre halt super. Die Warnung heisst ja nur, dass dead code eliminiert wurde. Jetzt müsste man rausfinden warum es dead code ist
und zu dem deaktivieren der warnungen sag ich mal lieber nix...
-
Zu den rvalues. Jeder der schon mal mit der DirectX Extension Library zu tun hatte, wird das Problem kennen
Viele DirectX Funktionen nehmen einen Parameter als Zeiger, welcher dann aber innerhalb der Funktion kopiert wird. Eine Referenz wäre hier IMHO eben schöner, aber eben, man verwendet ja auch Code, der nicht von einem selbst ist:
Beispiel:
D3DXVECTOR3 out; D3DXMatrixLookAtLH(&out,&D3DXVECTOR3(0.0f,1.0f,0.0),&D3DXVECTOR3(0.0f,0.0f,0.0f),&D3DXVECTOR3(0.0f,1.0f,0.0f));
Mit dieser Warnung aktiviert muss man dies dann folgendermassen umschreiben:
D3DXVECTOR3 out; D3DXVECTOR3 eye; D3DXVECTOR3 at; D3DXVECTOR3 up; D3DXMatrixLookAtLH(&out,&eye,&at,&up);
Das mag zwar auf den ersten Blick sogar schöner sein, aber nicht mehr, wenn man 100erte solcher Funktionen aufrufen muss.
Parameter aus dem Interface rausschmeißen, wenn diese nicht gebraucht werden.
Dazu sag ICH jetzt mal nichts....
Aber wie gesagt, ich möchte mich hier nicht streiten. Es wäre ja noch was ganz anderes gewesen, wenn einfach jemand freundlich nach dem WARUM gefragt hätte und sich anschliessend erst mal meine Argumentation angehört hätte, bevor drauf los gewettert wird, aber na gut...
-
Es ist aber schon so, dass Warnungen in der Regel gleich einem Fehler sind oder zumindest gleich einem potentiellen Fehler. Man schaltet eine Warnung nur dann bewusst aus, wenn man genau verstanden hat, warum diese Warnung ausgegeben wird und sich ganz sicher ist, dass diese Gründe in diesem besonderen Fall (jeden Fall einzeln prüfen!) nicht vorliegen.
Dein DirectX Interface ist ein schönes Beispiel, wo man die Warnung ruhigen Gewissens ausschalten darf, sofern man ganz sicher weiß, dass in der Funktion eine Kopie des Pointees erstellt wird, so dass man hinterher nicht auf nicht-existierende Objekte zeigt. In jedem anderen Fall deutet die Warnung aber auf einen schweren Fehler hin und man sollte unverzüglich den Grund für die Warnung abstellen und nicht die Warnung selbst.
-
Ishildur schrieb:
Parameter aus dem Interface rausschmeißen, wenn diese nicht gebraucht werden.
Dazu sag ICH jetzt mal nichts....
Den Namen des Parameters rauswerfen:
int foo(int a, char) { return a; }
und schon ist die warnung weg...
-
@SeppJ
Ja ich stimme dir bei diesem Beitrag bei, aus diesem Grund habe ich dem Compiler auch gesagt, das Warnungen als Fehler geanded werden sollen. Aber du kannst dir vorstellen, dass es einfach extrem nervig ist, wenn der Code dann ständig wegen solchen Sachen (wo man ganz genau weiss, dass es in diesem Fall kein Problem ist) nicht mehr kompiliert. Daher habe ich diese ausgeschaltet und schalte sie erst wieder ein, wenn ein Modul abgeschlossen ist (und prüfe da jeden einzelen Fall noch einmal).@Shade of Mine
Danek für den Tipp, das habe ich nicht gewusst.
-
@Shade of Mine
Habe das schnell ausprobiert, funktioniert, aber findest du das wirklich schön?void MyInterfaceMethod(float32 A,float32,float32,uint16,int64 E){ }
Vielfach hat man ja auch die Situation, dass man eine Funktion (bspw. fürs Prototyping noch nicht vollständig implementiert (spricht noch nicht alle Parameter interpretiert) oder dass man sich einen zusätzlichen Parameter definiert (reserviert), bei dem man noch nicht weis, wofür man ihn irgendwann benutzen könnte, aber dass man die Funktion eben erweitern könnte, ohne die Interfaces verändern zu müssen.
-
Schlechter Stil. Immer komplette Pfade haben.
float32 divide(float32 A,float32 B){ if(B == 0.0f) throw DivisionException(); else return A/B; }
Kannst du mir zeigen, wie du das so machst, dass da keine C4715 Warnung kommt?
-
Ishildur schrieb:
Vielfach hat man ja auch die Situation, dass man eine Funktion (bspw. fürs Prototyping noch nicht vollständig implementiert (spricht noch nicht alle Parameter interpretiert) oder dass man sich einen zusätzlichen Parameter definiert (reserviert), bei dem man noch nicht weis, wofür man ihn irgendwann benutzen könnte, aber dass man die Funktion eben erweitern könnte, ohne die Interfaces verändern zu müssen.
Und in so einen Fall weisst du schon dass der Parameter numberOfElements oder so heissen wird?
Du lässt den Namen ja in der Implementierung weg - in der Interface Definition darf ja ruhig ein Name stehen...
Und was du in der Implementierung machst ist ja dein privat vergnügen...
-
Ishildur schrieb:
Schlechter Stil. Immer komplette Pfade haben.
float32 divide(float32 A,float32 B){ if(B == 0.0f) throw DivisionException(); else return A/B; }
Kannst du mir zeigen, wie du das so machst, dass da keine C4715 Warnung kommt?
float32 divide(float32 A,float32 B){ if(B == 0.0f) throw DivisionException(); return A/B; }
-
Ishildur schrieb:
Schlechter Stil. Immer komplette Pfade haben.
float32 divide(float32 A,float32 B){ if(B == 0.0f) throw DivisionException(); else return A/B; }
Kannst du mir zeigen, wie du das so machst, dass da keine C4715 Warnung kommt?
float32 divide(float32 A,float32 B) { if(std::fabs(B) == 0.0f) throw DivisionException(); return A/B; }
-
Wo ist der Unterschied? Ausser dass das Beispiel ohne "else" meiner Meinung nach weniger intuitiv lesbar ist?