Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Das Gurke

Community-Fossil

Beiträge: 1 996

Wohnort: Pinneberg

Beruf: Schüler

  • Private Nachricht senden

11

02.11.2007, 20:26

Nullpointer Exceptions gehören bei mir auch zur absoluten Ausnahme und sollten dafacto nie geworfen werden. Sie sind keinesfalls Normalität, sondern immer kritische Fehler, die in vielen Fällen zum sofortigen Programmabbruch führen.

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

12

02.11.2007, 20:29

Zitat von »"Das Gurke"«

Nullpointer Exceptions gehören bei mir auch zur absoluten Ausnahme und sollten dafacto nie geworfen werden. Sie sind keinesfalls Normalität, sondern immer kritische Fehler, die in vielen Fällen zum sofortigen Programmabbruch führen.


Wenn du dir so sicher bist das die Ausnahme ohnehin kaum auftritt, wieso behandelst du sie dann explizit? Und wieso führt sowas zum Programmabbruch?
Ich glaub du hast was ganz grundlegend missverstanden beim Exceptionhandling...
@D13_Dreinig

Osram

Alter Hase

Beiträge: 889

Wohnort: Weissenthurm

Beruf: SW Entwickler

  • Private Nachricht senden

13

03.11.2007, 09:33

Der ursprüngliche Code KANN absolut ok sein, nämlich wenn pPointer==NULL erlaubt ist. z.B.

Zitat


void SetUpEmissiveAttributes(CGfxObject *pGfxObject, CGfxAttributes *pGfxAttribs)
// Setze die Grafik Attribute je nach Object.
// pGfxObject darf NULL sein, pGfxAttribs jedoch nicht
{
assert(pGfxAttribs);
if(pGfxObject && pGfxObject->IsTracer())
{
// Ist Leuchtspurmuni...
pGfxAttribs->eEmissiveType = EMISSIVE_TRACER;
.... hier sind weitere Berechnungen der anderen Attribute...
}
else
pGfxAttribs->eEmissiveType = EMISSIVE_NONE;

}


Tatsächlich gibt es den Code so ähnlich in BoB.

pGfxObject == NULL ist zugelassen. Z.B. könnte es sein dass für jeden Frame ein grosses Array von potentiell sichtbaren Objekten gibt, aber vor dem Rendern diese nochmal auf Überdeckung getestet werden und völlig unsichtbare auf NULL gesetzt werden.

Ich habe absichtlich die Attribute als Pointer übergeben, um zu zeigen wie ich einen Pointer behandele der nicht NULL sein darf: Ich mache einfach ein assert. Eine Exception ist IMO in einem Projekt, was sowieso Exceptions schmeisst, auch ok. Wichtig ist aber dass der Fehler überhaupt extra behandelt wird, sodass ich eine vernünftige Fehlermeldung zurück bekomme. Ich weiss dann also z.B. dass der Pointer NULL ist, während wenn ich eine Access Violation bekomme und selbst wenn ich genau weiss wo, kann ich nur spekulieren, dass der Pointer kaput ist, aber nicht, welchen Wert er hat. Ein assert sollte so früh wie möglich stehen. Z.B. wenn in der Routine erst mal eine grosse Berechnung gemacht wird, eher der Pointer benutzt wird, so weiss ich bei einer Access Violation nicht ob in der Berechnung durch einen anderen Fehler der Pointer überschrieben wurde. Mit einem Assert am Anfang der Routine oder gar direkt vor Aufruf der Routine bin ich dem eigentlichen Fehler schon etwas näher.

Übrigens gibt es so etwas ähnliches als Sprachfeature bei Eiffel: Mit sogenannten Vor- Nach- und Klassen-bedingungen kann man die Annahmen beim Programmieren aufschreiben, also z.B. dass "NoOfVisibleObjects" mindestens eins sein muss. Diese Bedingungen werden dann zur Laufzeit geprüft (ich glaube nur in Debug builds - schon wieder zu lange her :( ). In C(++) gibt es so etwas von der Sprache her leider nicht, daher muss man "manuell" so viel wie möglich machen, um Fehler möglichst schnell nachdem man sie eingebaut hat, zu entdecken.
"Games are algorithmic entertainment."

Osram

Alter Hase

Beiträge: 889

Wohnort: Weissenthurm

Beruf: SW Entwickler

  • Private Nachricht senden

14

03.11.2007, 10:07

Zitat von »"David_pb"«


Wenn du dir so sicher bist das die Ausnahme ohnehin kaum auftritt, wieso behandelst du sie dann explizit?


Weil man aus Erfahrung weiss dass man praktisch nie 100% sicher ist sondern höchstens 99%.

Zitat


Und wieso führt sowas zum Programmabbruch?


Weil sich alles aufwändigere nicht lohnt. In den meisten Fällen hat man ja keinen Fehler gemacht und der Pointer ist wirklich IMMER ungleich NULL. Und auch wenn ein assert / eine Exception die zum Programmabbruch führt keine 100% Lösung ist, ist es doch 80% und mit 10% des Aufwands.

Die Programmierer testen sowieso öfters ein Debug Build. Und die Tester sollten es auch ab und zu machen, auch wenn sie stöhnen :badgrin:. Daher werden viele solche Fehler gefunden und behoben bevor das Programm / der Patch überhaupt die Kunden erreicht. Und wenn es beim Testen nicht auffällt passiert es meist nur in sehr speziellen Kombinationen - z.B. Flugmodel auf "Novice" UND Flugzeug Ju87 ausgewählt UND "starte aus Pen" eingeschaltet.

Erfahrungsgemäß ist so etwas zumindest in einem Patch kein wirklich großes Problem - die Kunden hören im Forum dass es für alle anderen Kunden läuft. Vielleicht ist auch sofort klar, welche Einstellungen das Problem sind, sodass sie das Spiel weiter nutzen können während wir das Problem lösen, oder aber wenn es nicht klar ist woran es liegt, sind die Kunden auch öfters bereit wenigstens etwas mitzuhelfen, z.B. indem sie die Datei mit den Optionen senden. Und wenn man, wie ich, ein assert nimmt, schlägt es ja sowieso nur in Debug Builds zu.

Asserts machen auch manchmal ein Problem offensichtlich was sonst nicht offensichtlich ist. Daher geht das Programm besser getestet an die Kunden raus.

Und noch etwas Prinzipielles zur Entwicklung von Spielen und auch den meisten anderen Programmen: Was (grob) vorgegeben ist, ist die "man power", also wie viel Geld bzw wie viele Mann Monate man reinstecken kann. Wenn ich also an einer Stelle mit nur geringem Verlust an "Qualität"/Features/"Polish" so viel Zeit sparen kann, dass es an anderer Stelle mehr Vorteile bringt, dann ist das eine gute Sache. Ich weiss, das ist trivial, aber ich glaube viele haben sich da noch nie drüber Gedanken gemacht. Und der Unterschied von der tatsächlichen "Man Power" zu der die man sinnvoll einsetzen könnte ist zum Teil astronomisch.
"Games are algorithmic entertainment."

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

15

03.11.2007, 11:04

Zitat von »"Osram"«

Zitat von »"David_pb"«


Wenn du dir so sicher bist das die Ausnahme ohnehin kaum auftritt, wieso behandelst du sie dann explizit?


Weil man aus Erfahrung weiss dass man praktisch nie 100% sicher ist sondern höchstens 99%.


Genau. Und eine Ausnahme zu werfen ist für sowas totaler overkill... Eine Annahme reicht und fällt bei der Release nicht weiter ins Gewicht.

Zitat von »"Osram"«


Zitat


Und wieso führt sowas zum Programmabbruch?


Weil sich alles aufwändigere nicht lohnt. In den meisten Fällen hat man ja keinen Fehler gemacht und der Pointer ist wirklich IMMER ungleich NULL. Und auch wenn ein assert / eine Exception die zum Programmabbruch führt keine 100% Lösung ist, ist es doch 80% und mit 10% des Aufwands.


Die Endversion sollte möglichst überhaupt nicht abbrechen. Das kann bei Debugversionen vorkommen, aber die Version die an den Kunden geht hat nicht so einfach beendet zu werden.
@D13_Dreinig

Das Gurke

Community-Fossil

Beiträge: 1 996

Wohnort: Pinneberg

Beruf: Schüler

  • Private Nachricht senden

16

03.11.2007, 11:39

Naja, durch diese Exception kann ich mir immerhin vom "Kunden" einen Stacktrace zusenden lassen, zumindest sofern ich die pdbs mitliefere.

Und Osram hatte das ja auch schon angeschnitten: In dem von mir konkreten Fall sind Nullpointer einfach mit die heftigsten Fehler die auftreten können und kein Normalfall. Daher ist imho das Werfen einer Exception gerechtfertigt.

Aber ich denke, hier prallen einfach Welten aufeinander. Bei gewissen kontroversen Themen, wird man einfach nie zu einem wirklichen Konsens kommen.

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

17

03.11.2007, 12:35

Also wenn du sinnvolle Daten vom Kunden haben willst=>Minidumps! Mit entsprechender Einstellung haben die alle Infos, die man braucht und die Benutzer bekommen die obj und pdb Dateien erst garnicht in die Finger.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
LONG CALLBACK CreateMiniDumpFile(EXCEPTION_POINTERS* pExceptionPointers)
{
    MessageBoxW(NULL, L"Es ist ein Fehler aufgetreten und das Programm wird beendet.", L"Fehlerbericht", 0);

    static int x = 0;
    Atomic_Inc(&x);
    if(x == 1)
    {  
        MINIDUMP_EXCEPTION_INFORMATION ExpParam;

        char FileName[40];
        sprintf(FileName, "FiregalaxyDump_Version %i.dmp", GVERSION);
        HANDLE hDumpFile = CreateFile(FileName, GENERIC_READ|GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);

        ExpParam.ThreadId           = GetCurrentThreadId();
        ExpParam.ExceptionPointers  = pExceptionPointers;
        ExpParam.ClientPointers     = false;

        int mdt       = MiniDumpWithPrivateReadWriteMemory |
                        MiniDumpWithDataSegs;

        if(!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithPrivateReadWriteMemory, &ExpParam, 0, 0))
        {
            int errorcode = GetLastError();

            char ecode[10];
            sprintf(ecode, "%i", errorcode);

            MessageBoxA(NULL, ecode, "Fehlerbericht", 0);
        }
        
        CloseHandle(hDumpFile);
    }
    return EXCEPTION_EXECUTE_HANDLER;
}


C-/C++-Quelltext

1
2
3
4
5
6
7
nt main(int argc, char* argv[])
{
#ifdef _CRTDBG_MAP_ALLOC
   _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); //memleakdetection

#endif
    SetUnhandledExceptionFilter(CreateMiniDumpFile);
}


Die Dumps sind mit den Einstellungen zwar ziemlich groß, aber gezippt sind die auf der Größe von ein paar MBs. Zum Lesen diese Dumps braucht man die zugehörige pdb, weil ohne die kann man die Symbole nicht auflösen. Sprich die Dumps erleichtern es Reversfreaks nicht wirklich. Allerdings hat das ganze Einschränkungen:
1. Der Callstack wird nur dann korrekt erstellt wenn man den Exceptionpointer mit übergibt oder die Funktion aus einem Workerthread aufruf(kA was sich die Programmierer da gedacht haben)
2. Ist nicht kompatibel mit c++ Exception (wegen dem Exceptionpointer)
3. SetUnhandledExceptionFilter wird bei "systemkritischen" Fehlern übergangen, sprich der Dump nie erstellt. Dazu gehören Bufferoverflows und noch ein paar andere Sachen

Wer eine Lösung hat, wie man den Exceptionpointer umgehen kann, also auch ein MiniDump per C++ Exception erstellen kann, immer her damit!
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

18

03.11.2007, 15:52

Zitat von »"Das Gurke"«

Naja, durch diese Exception kann ich mir immerhin vom "Kunden" einen Stacktrace zusenden lassen, zumindest sofern ich die pdbs mitliefere.

Und Osram hatte das ja auch schon angeschnitten: In dem von mir konkreten Fall sind Nullpointer einfach mit die heftigsten Fehler die auftreten können und kein Normalfall. Daher ist imho das Werfen einer Exception gerechtfertigt.

Aber ich denke, hier prallen einfach Welten aufeinander. Bei gewissen kontroversen Themen, wird man einfach nie zu einem wirklichen Konsens kommen.


Wie ich bereits erwähnt, hängt es stark von der jeweiligen Situation ab wie kritisch der Fehler gewichtet wird. Meist reicht es aber einen Fehler einfach zu verhindern indem man Abfragen á la if ( ptr && ptr->... ) macht und nicht unnötige Resourcen verbrät indem man Ausnahmen wirft.

Für Debugausgaben (Stacktrace & Co) hat ja Nox einen guten Lösungsansatz aufgezeigt.
@D13_Dreinig

Werbeanzeige