Design Serializer

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Design Serializer

Beitrag von snoob741 »

Aktuell implementiere ich für meine Engine einen Serialisierungsmechanismus. Klappt soweit gut. Bisher habe ich folgende Struktur:

DataBlop
Logischer Cntainer zum Halten der Serialisierungsdaten. Hat eine none unique Id und
Diverse Infos zu den Daten. ZB. Typ, Size usw.

Chunk
Logischer Container zum Gruppieren der Blops. Enthält 1-n Blops.
Zudem hat dieser eine none unique Id und alle Daten zu den Blops, d.h. Size usw.
Hält die Blops in einem std:vector.

Archiv
Generischer Container zum Verwalten der Chunks und zum
Laden und Speichern je nach Implementation. Z.B. Filestream.
Hält die Chunks in einem std:vector.

Soweit so gut. Damit bin ich auch zufrieden. Nun zerbreche ich mir den Kopf für die Umsetzung. Und wollte mal hier auf Eure Erfahrung zurückgreifen. Würdet Ihr eher einen Serializierer implementieren der alles abhandelt und diverse lese und schreib Methoden für ein Objekt z.b. Modell anbietet oder eher dies über entsprechende Methoden an den Objekten selbst machen? Was ist Eurer Meinung nach der bessere Ansatz auch in Hinblick auf Wartbarkeit und Erweiterung? Würdet Ihr generell etwas anders machen bzw. zusätzlich berücksichtigen?
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Design Serializer

Beitrag von gdsWizard »

Ich habe bisher die Serializierung in meinen Datenobjekten gemacht. Anders wäre der Aufwand wohl höher. Du solltest aber darauf achten das die Chunk-Struktur sich nicht zu sehr an deinen Klassen orientiert und du sonst Probleme bekommst wenn deine Klassen-Struktur sich ändert. Wenn du nur wenig Daten zu speichern hast würde ich auch über ein XML Dateiformat nachdenken. Die sind einfach zu lesen und zu schreiben. Auch sind sie gut wartbar und man bekommt schnell eine eigene Struktur die unabhängig von den Datenklassen macht. Zumindest war es bei mir so.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Design Serializer

Beitrag von snoob741 »

Danke für Deine Informationen, dann werde ich das auch direkt über die Datenobjekte machen. Denke ist sowieso besser, da man hier den direkten Zusammenhang zum Aufbau der Daten hat, die persistiert/geladen werden sollen. Das mit dem XML Dateiformat klingt gut, über so etwas hab ich auch schon nachgedacht, würde halt nur JSON bevorzugen.

Eine Frage hätte ich noch, würdest Du die Chunks flach halten oder ggfs. auch verschachteln, so dass eine Baumstruktur entstehen kann? Denke grad darüber nach, z.B. bei einer Mesh, so hätte man ein Chunk für die reinen Mesh-Daten und dann Subchunks für die Mesh-Geometry, Materials usw ...
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Design Serializer

Beitrag von gdsWizard »

Eine Baumstruktur bei Chunks ist kein Problem. Das habe ich auch immer gemacht und man findet sie sowieso bei komplexeren Formaten. Beim Amiga gab es die iffparse.library die einen dabei unterstützt hat. Also sowas wie BeginChunk / CreateChunk und EndChunk empfehlen sich. Bei Chunk-Formaten ist aber nicht offensichtlich ob der Chunk andere Chunks beinhaltet, deshalb hatte ich XML erwähnt, dort sieht man ja direkt ob eine Node noch andere Nodes beinhaltet. Es ist wie schon gesagt gängige Praxis Chunks zu schachteln.
Benutzeravatar
TGGC
Establishment
Beiträge: 569
Registriert: 15.05.2009, 18:14
Benutzertext: Ich _bin_ es.
Alter Benutzername: TGGC
Echter Name: Ich _bin_ es.
Wohnort: Mainz
Kontaktdaten:

Re: Design Serializer

Beitrag von TGGC »

Bau einfach ein das in einem Blob auch ein oder mehrere Blobs drin sein koennen. Dann brauchst du den Rest nicht mehr. Ausserdem kannst du alles schoen strukturieren. Im Grunde brauchst du dann nur ein paar Funktionen, die einen Typ in einen Blob umwandeln. Wie wandelt man dann Die Klasse A in einen Blop um? Alle relevanten Member in ein Blob umwandeln und die in einen neuen Blob tun. Wie macht man einen std::vector<A> oder std::list<A> zu einem Blob? Alle As zu Blobs machen und in einen Blob tun. Eigentlich ziemlich einfach.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4258
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Design Serializer

Beitrag von Chromanoid »

als Alternative kannst Du mit einer der gängigen Bibliotheken/Systeme wie protobuf arbeiten und Deine Daten entsprechend in einer Definitionsdatei definieren und dann die entsprechenden Klassen generieren lassen.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2369
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Design Serializer

Beitrag von Jonathan »

(Ich habe früher boost::serialization benutzt. Im Grunde sehr hübsch zu benutzen, aber alles was über "ich schreibe einfach ein paar Daten weg" hinausgeht, wird dann doch kompliziert und die Fehlermeldungen die man beim Compilieren bekommt sind absolut grässlich.)


Ich habe mir eine handvoll Hilfsfunktionen geschrieben, aber letztendlich bleibt es doch viel Handarbeit. Für jede Klasse eine Lade- und Speicherfunktion. Selbst wenn C++ Reflections unterstützen würde, müsste man immer noch entscheiden, welche Werte man davon jetzt wirklich in die Datei schreiben möchte.
Letztendlich ist es aber eigentlich noch erträglich. Ich habe den |-operator (in Anlehnung an Pipes, naja) überladen und muss so im Grunde in jeder Funktion nur alle Variablen aufzählen, die ich Speichern möchte. Ein kleines Makro bastelt mir aus Klassenmembern die entsprechenden Operatorenüberladungen, wodurch der Code letztendlich sehr hübsch aussieht. Bei mir sind übrigens alle Speicherfunktionen const, was leider dazu führt, dass ich es noch nicht geschafft habe, dass man nur eine einzige Funktion fürs Laden und Speichern benötigt (weil ja im Grunde nur die Elemente aufgelistet werden), aber der Aufwand hält sich auch so in Grenzen.
Das ganze ist übrigens binär, was einfach den Grund hat, dass man damit auch mal ganze Vektoren am Stück schreiben kann. Und die Dateien bleiben halt recht kompakt.

Chunks/Blobs habe ich natürlich auch, das sieht ungefähr so aus:

Code: Alles auswählen

void Entity::Save(OArchive& a) const
{
	a.StartChunk("Entity");
	a | m_Position | m_Direction | m_Prototype | m_RenderOffset
		 | m_RenderScale | m_ModelFilename | m_Components;
	a.EndChunk("Entity");
}
Im Grunde fügen die nur ein paar Meta-Informationen hinzu, die beim Auslesen der Dateien für Fehlerüberprüfungen genutzt werden (bei Binärdateien können sonst ja die lustigsten Probleme auftreten). Das ganze ist aber komplett optional, größere Objekte bilden eben einen Chunk, kleine werden direkt so geschrieben, um Platz und Abfragen zu sparen. Desweiteren habe ich noch einen Mechanismus, der vor jede gespeicherte Variable einen Typ-Identifier schreibt, was auch sehr nützlich ist, um Fehler zu finden. Auch das kann man an- oder ausschalten, im Grunde also einfach Debuginformationen.

Sehr sehr hübsch ist in diesem Zusammenhang übrigens der neue Unique-ptr. Wenn man den konsequent benutzt, kann man Objekte per unique_ptr einfach so speichern, wenn man aber einen normalen Zeiger bekommt, weiß man, dass es eine Referenz sein muss und man das Objekt eben nicht nocheinmal in die Datei schreiben sollte. Auf dieser Basis kann man dann ein System schreiben, um Referenzen beim Laden wiederherstellen zu können, aber das habe ich momentan noch nicht implementiert. (nicht temporäre Referenzen auf andere Objekte sind auch schon zur Laufzeit eklig, weil man sich einfach immer Gedanken machen muss, was passieren soll, wenn dieses Objekt einmal gelöscht werden soll. Beim Serialisieren wird es dann nochmal interessanter, weswegen ich versuche, so etwas wo es geht zu vermeiden).

Oh, übrigens: Es geht nicht immer darum, einfach alle Attribute in eine Datei zu schreiben. Beispielsweise habe ich Entities, die normalerweise in einem SceneManager liegen. Diese haben natürlich eine Referenz auf den SM, aber wie oben erwähnt, macht es nicht Spaß, so etwas zu speichern.
Meine Lösung ist einfach, dass Entities ihre SM-Referenz bei speichern ignorieren. Der SceneManager lädt später alle Entities und setzt danach in jedem Entity die SceneManager Referenz. Das lässt sich äußerst leicht programmieren und funktioniert sehr gut, und man muss sich im Entity überhaupt keine Gedanken mehr über die dusselige Referenz machen. Da Entities auch sowieso alleine existieren können (und sei es nur in der Factory für den Editor), ist der Mechanismus beim "zur Laufzeit neu erstellen" und Laden derselbe: Erst das Objekt erzeugen, dann in den SceneManager einsortieren (wobei auch die Rückreferenz gesetzt wird).
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Design Serializer

Beitrag von snoob741 »

Danke für Eure Anregungen. Externe Abhängigkeiten wollte ich vermeiden (hab u.A. deshalb auch Boost enfernt) um auch mit der Android Entwicklung weniger Probleme zu haben. Zudem ist ja ein großer Teil von Boost endlich in den C++11 Standard eingeflossen.

Den vielen Input muss ich erst einmal sacken lassen ;) Werde dann mal prüfen ob ich die Chunks verschachtel oder eher wie TGGC es empfohlen hat ganz auf diese zu verzichten und lieber die Blops zu verschachteln. Was Referenzen angeht, habe ich die gleichen Ideen gehabt wie Jonathan: D.h. Referenzen ignorieren und später Zentral alles über die Szene laden und zusammensetzen. Na dann will ich mal die Tasten zum Glühen bringen :P
Benutzeravatar
Jonathan
Establishment
Beiträge: 2369
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Design Serializer

Beitrag von Jonathan »

Was ist den bitte der Unterschied zwischen Chunks und Blobs? Das Blobs keinen Header haben?
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
TGGC
Establishment
Beiträge: 569
Registriert: 15.05.2009, 18:14
Benutzertext: Ich _bin_ es.
Alter Benutzername: TGGC
Echter Name: Ich _bin_ es.
Wohnort: Mainz
Kontaktdaten:

Re: Design Serializer

Beitrag von TGGC »

Da ich den genauso nicht sehe, scheint mir das reine Codedoppelung zu sein.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Design Serializer

Beitrag von snoob741 »

Die Blops sind substrukturen des Chunks und dienen der Datenkapselung. Sie enthalten neben den Rohdaten Informationen um sie wiederherstellen zu können. Chunks sind Container mit Header die 1-n Blops halten. Für den Nutzer sind die Blops eigentlich nicht relevant sie arbeiten mit dem Chunk und adden hier ihre Daten bzw. fragen sie ab.
joggel

Re: Design Serializer

Beitrag von joggel »

Die Blops sind substrukturen des Chunks und dienen der Datenkapselung. Sie enthalten neben den Rohdaten Informationen um sie wiederherstellen zu können.
Wieso noch zusätzliche Informationen? Welche zum Beispiel?
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Design Serializer

Beitrag von snoob741 »

Die Infos sind: Id, Datentyp, Länge, Size des Typs. Die Infos gebe ich beim Hinzufügen der Daten am Chunk mit an, intern wird dann der Blop erzeugt und im std:vector gehalten.
joggel

Re: Design Serializer

Beitrag von joggel »

Mir kommt es so vor, als kannst Du da entweder Chunk, oder Blob weglassen... aber nur so mit einer Hälfte des Gehirns nachgedacht...
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Design Serializer

Beitrag von snoob741 »

Stimmt. Je mehr ich drüber poste und nachdenke desto fraglicher wird die Chunk Funktionalität. Nur logisch gruppieren ist wohl zu wenig um langfristig bestand zu haben. Da hat sich das Posting ja schon gelohnt ;)
Antworten