Du kannst assertions auch mit Exceptions kombinieren indem du dein eigenes assert Makro schreibst, welches eine beliebige Exception wirft.
Das hatte ich auch mal probiert. Aber dabei die fehlgeschlagene Prüfbedingung als Fehlergrund in eine Logfile zu schreiben und dann die Exception zu werfen, hat mir nicht gefallen.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
|
void Grid::spawn(ObjectID id, Vector2u position) {
if (id > MAX_OBJECTS) {
throw MyException("Invalid object #" + std::to_string(id));
}
// ...
}
|
vs.
|
C-/C++-Quelltext
|
1
2
3
4
|
void Grid::spawn(ObjectID id, Vector2u position) {
MyAssertion(id <= MAX_OBJECTS);
// ...
}
|
Beim Assertion-Ansatz fehlen mir dann irgendwie zusätzliche Daten, z.B. die ObjectID, die das ganze verursache hat. An was ich (in der Makro-Implementierung des Asserts) rankomme, ist der String
"id > MAX_OBJECTS" und die Tatsache, dass der Ausdruck
false ist. Von daher würde ichd en Exception-Ansatz verfolgen wollen.
Alternativ gibt es in den meisten Test Frameworks auch ne moeglichkeit auf fehlgeschlagene asserts zu testen (z.B. EXPECT_DEATH in Google Test):
http://stackoverflow.com/questions/37564…ith-google-test
Ok, dann verwende ich entweder die falsche Testsuite oder ich bin zu blöd das in meiner zu finden xD
Gibt bei Exceptions immer sinnvolle Informationen aus, z.B. welche Reichweite von Werten denn gültig wäre.
Dabei stehe ich nun vor der Frage: Wirklich
eine Exception-Klasse, für deren Werfen ich den String "mühsam" (d.h. oft mit
std::to_string() und damit einigen Stringverknüpfungen) zusammenbauen muss - oder
mehrere Exception-Klassen, die ich mit den Einzeldaten "füttere" und dann später den die Daten "einfach" z.B. mit
std::cout << bla << foo zusammenbaue. z.B. habe ich eine Logging-Klasse, die verschiedene
operator<<()-Überladungen für im Endeffekt alle Typen hat, die ich verwende (d.h. z.B. auch um Vektoren "einfach" zu printen ohne mit selber noch mit
std::to_string() arbeiten zu müssen.
Wegen der Logger-Implementierung überlege ich, ob ich mehrere Exception-Typen baue, die im ctor die Daten direkt bekommen, z.B.
|
C-/C++-Quelltext
|
1
2
3
|
throw ObjectNotFound(scene_id, object_id);
throw PositionNotFound(scene_id, tile_pos);
// usw.
|
Das blöde ist nur, dass ich dann viele Exception-Klassen brauche. Einfach eine mit verschiedenen ctor's klappt nicht gut, wenn ich verschiedene Fehlerfälle habe, die identische Variablentypen und -anzahl mit sich bringen. z.B. könnte
InvalidObject(int) eine Ausgabe a la
|
Quellcode
|
1
|
ObjectID #17 is not used.
|
in die Log-File schreiben. Eine Exception
InvalidScene(int) wäre (beim Überladen des ctor's) nicht von der ersteren Unterscheidbar, so dass ich nur noch
|
Quellcode
|
1
|
Invalid id #17.
|
ausgeben könnte, aber nicht mehr wüsste: ObjectID? SceneID? ItemID? foo?
Die aussagekräftigen Logging-Einträge sollen ja beim Identifizieren von Fehlern suchen ...
![:)](wcf/images/smilies/smile.png.pagespeed.ce.cw1CRsMB0z.png)
Habt ihr da einen möglichen Ausweg für mich?
Und noch eine zweite Überlegung: Wenn ich die Exceptions im Programm (z.B. im mainloop) abfange (um z.B. noch den Spielstand zu speichern) könnte ich die Logfile auch schreiben lassen. Denkbar wäre dann sogar die Exceptions in folgendem Stile zu bauen (auch wenn es keine wirklichen C++-Exceptions mehr sind, aber ich könnte ja sogar einen int werfen xD)
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class GameError {
public:
virtual void dump(Logger& logger) = 0;
};
class ObjectNotFound: public GameError {
private:
int scene_id, object_id;
public:
ObjectNotFound(int scene_id, int object_id)
: GameError{}
, scene_id{scene_id}
, object_id{object_id} {
}
void dump(Logger& logger) override {
logger << "Cannot find object #" << object_id << " at scene # " << scene_id << "\n";
}
};
// etc.
|
So dass ich beim Auffangen der Exception nur
e.dump(error_logger); und einen Rethrow durchführe.... Könnte ich direkt mal probieren, ob das klappt
LG Glocke
/EDIT: Klappt, nur geht der Callstack flöten