Ich habe eine kleine Hierarchie von Objekten, die ich serialisieren und über eine Socket-Verbindung übertragen muss. Ich muss das Objekt sowohl serialisieren als auch basierend auf dem Typ deserialisieren. Gibt es eine einfache Möglichkeit, dies in C ++ zu tun (wie es in Java der Fall ist)?
Gibt es Online-Codebeispiele oder Tutorials für die C ++ - Serialisierung?
EDIT: Um ganz klar zu sein, suche ich nach Methoden zum Konvertieren eines Objekts in ein Array von Bytes und dann zurück in ein Objekt. Ich kann mit der Steckdosenübertragung umgehen.
c++
serialization
marshalling
c++-faq
Bill die Eidechse
quelle
quelle
Antworten:
Wenn ich über Serialisierung spreche, fällt mir die Boost-Serialisierungs-API ein . Für die Übertragung der serialisierten Daten über das Internet würde ich entweder Berkeley-Sockets oder die Asio-Bibliothek verwenden .
Bearbeiten:
Wenn Sie Ihre Objekte in ein Byte-Array serialisieren möchten, können Sie den Boost-Serializer folgendermaßen verwenden (entnommen aus der Tutorial-Site):
Die eigentliche Serialisierung ist dann ziemlich einfach:
Die Deserialisierung funktioniert analog.
Es gibt auch Mechanismen, mit denen Sie die Serialisierung von Zeigern handhaben können (komplexe Datenstrukturen wie Locken usw. sind kein Problem), abgeleitete Klassen und Sie können zwischen Binär- und Textserialisierung wählen. Außerdem werden alle STL-Container sofort unterstützt.
quelle
In einigen Fällen können Sie beim Umgang mit einfachen Typen Folgendes tun:
Das ist als Proof-of-Concept oder First-Draft in Ordnung, damit andere Mitglieder Ihres Teams weiter an anderen Teilen arbeiten können.
Aber früher oder später, normalerweise früher , werden Sie verletzt!
Sie stoßen auf Probleme mit:
(Außerdem müssen Sie wissen, in was Sie auf der Empfangsseite auspacken.)
Sie können dies verbessern, indem Sie für jede Klasse Ihre eigenen Marshalling- / Unmarshalling-Methoden entwickeln. (Idealerweise virtuell, damit sie in Unterklassen erweitert werden können.) Mit ein paar einfachen Makros können Sie verschiedene Basistypen recht schnell in einer großen / kleinen endian-neutralen Reihenfolge schreiben.
Diese Art von Grunzarbeit ist jedoch viel besser und einfacher über die Serialisierungsbibliothek von boost zu erledigen .
quelle
Es gibt ein generisches Muster, mit dem Sie Objekte serialisieren können. Das grundlegende Grundelement sind diese beiden Funktionen, die Sie von Iteratoren lesen und schreiben können:
Dann folgen Serialisierungs- und Deserialisierungsfunktionen dem Muster:
Für Klassen können Sie das Friend-Funktionsmuster verwenden, damit die Überladung mithilfe von ADL gefunden werden kann:
Die in Ihrem Programm können Sie serialisieren und in eine Datei wie diese objektivieren:
Dann lies:
Meine alte Antwort hier:
Serialisierung bedeutet, dass Sie Ihr Objekt in Binärdaten umwandeln. Während Deserialisierung bedeutet, ein Objekt aus den Daten neu zu erstellen.
Bei der Serialisierung werden Bytes in einen
uint8_t
Vektor verschoben. Beim Unserialisieren lesen Sie Bytes aus einemuint8_t
Vektor.Es gibt sicherlich Muster, die Sie beim Serialisieren von Inhalten verwenden können.
Jede serialisierbare Klasse sollte eine
serialize(std::vector<uint8_t> &binaryData)
oder eine ähnliche signierte Funktion haben, die ihre binäre Darstellung in den bereitgestellten Vektor schreibt. Dann kann diese Funktion diesen Vektor an die Serialisierungsfunktionen des Mitglieds weitergeben, damit diese auch ihre Daten darin schreiben können.Da die Datendarstellung auf verschiedenen Architekturen unterschiedlich sein kann. Sie müssen ein Schema finden, wie die Daten dargestellt werden.
Beginnen wir mit den Grundlagen:
Ganzzahlige Daten serialisieren
Schreiben Sie einfach die Bytes in Little-Endian-Reihenfolge. Oder verwenden Sie die Varint-Darstellung, wenn es auf die Größe ankommt.
Serialisierung in Little-Endian-Reihenfolge:
Deserialisierung aus Little Endian Order:
Gleitkommadaten serialisieren
Soweit ich weiß, hat der IEEE 754 hier ein Monopol. Ich kenne keine Mainstream-Architektur, die etwas anderes für Floats verwenden würde. Das einzige, was anders sein kann, ist die Bytereihenfolge. Einige Architekturen verwenden Little Endian, andere die Big Endian-Bytereihenfolge. Dies bedeutet, dass Sie vorsichtig sein müssen, in welcher Reihenfolge die Bytes auf der Empfangsseite laut werden. Ein weiterer Unterschied kann die Handhabung der Denormal- und Unendlichkeits- und NAN-Werte sein. Solange Sie diese Werte vermeiden, sollten Sie in Ordnung sein.
Serialisierung:
Deserialisierung macht es rückwärts. Beachten Sie die Bytereihenfolge Ihrer Architektur!
Strings serialisieren
Zuerst müssen Sie sich auf eine Kodierung einigen. UTF-8 ist üblich. Speichern Sie es dann als Länge vorangestellt: Zuerst speichern Sie die Länge der Zeichenfolge mit einer oben erwähnten Methode, dann schreiben Sie die Zeichenfolge Byte für Byte.
Arrays serialisieren.
Sie sind die gleichen wie eine Saite. Sie serialisieren zuerst eine Ganzzahl, die die Größe des Arrays darstellt, und serialisieren dann jedes Objekt darin.
Ganze Objekte serialisieren
Wie ich bereits sagte, sollten sie eine
serialize
Methode haben, die einem Vektor Inhalt hinzufügt. Um ein Objekt unserialisieren zu können, sollte es einen Konstruktor haben, der Byte-Streams verwendet. Es kannistream
ein Referenzzeiger sein, aber im einfachsten Fall kann es nur ein Referenzzeiger seinuint8_t
. Der Konstruktor liest die gewünschten Bytes aus dem Stream und richtet die Felder im Objekt ein. Wenn das System gut entworfen ist und die Felder in der Reihenfolge der Objektfelder serialisiert, können Sie den Stream einfach in einer Initialisierungsliste an die Konstruktoren des Felds übergeben und sie in der richtigen Reihenfolge deserialisieren lassen.Objektdiagramme serialisieren
Zuerst müssen Sie sicherstellen, dass diese Objekte wirklich etwas sind, das Sie serialisieren möchten. Sie müssen sie nicht serialisieren, wenn Instanzen dieser Objekte auf dem Ziel vorhanden sind.
Jetzt haben Sie herausgefunden, dass Sie das Objekt, auf das ein Zeiger zeigt, serialisieren müssen. Das Problem der Zeiger, dass sie nur in dem Programm gültig sind, das sie verwendet. Sie können Zeiger nicht serialisieren, Sie sollten sie nicht mehr in Objekten verwenden. Erstellen Sie stattdessen Objektpools. Dieser Objektpool ist im Grunde ein dynamisches Array, das "Boxen" enthält. Diese Felder haben eine Referenzanzahl. Ein Referenzzähler ungleich Null zeigt ein lebendes Objekt an, Null zeigt einen leeren Steckplatz an. Anschließend erstellen Sie einen intelligenten Zeiger, der dem shared_ptr ähnelt und nicht den Zeiger auf das Objekt, sondern den Index im Array speichert. Sie müssen sich auch auf einen Index einigen, der den Nullzeiger kennzeichnet, z. -1.
Grundsätzlich haben wir hier die Zeiger durch Array-Indizes ersetzt. Jetzt können Sie beim Serialisieren diesen Array-Index wie gewohnt serialisieren. Sie müssen sich keine Gedanken darüber machen, wo sich das Objekt auf dem Zielsystem im Speicher befindet. Stellen Sie einfach sicher, dass sie auch denselben Objektpool haben.
Wir müssen also die Objektpools serialisieren. Aber welche? Wenn Sie ein Objektdiagramm serialisieren, serialisieren Sie nicht nur ein Objekt, sondern ein gesamtes System. Dies bedeutet, dass die Serialisierung des Systems nicht von Teilen des Systems ausgehen sollte. Diese Objekte sollten sich nicht um den Rest des Systems kümmern, sie müssen nur die Array-Indizes serialisieren und das wars. Sie sollten über eine System-Serializer-Routine verfügen, die die Serialisierung des Systems koordiniert, die relevanten Objektpools durchläuft und alle serialisiert.
Auf der Empfangsseite werden alle Arrays und die darin enthaltenen Objekte deserialisiert, wodurch der gewünschte Objektgraph neu erstellt wird.
Funktionszeiger serialisieren
Speichern Sie keine Zeiger im Objekt. Haben Sie ein statisches Array, das die Zeiger auf diese Funktionen enthält, und speichern Sie den Index im Objekt.
Da diese Programme in beiden Programmen in die Regale kompiliert sind, sollte es funktionieren, nur den Index zu verwenden.
Serialisierung polymorpher Typen
Da ich sagte, Sie sollten Zeiger in serialisierbaren Typen vermeiden und stattdessen Array-Indizes verwenden, kann Polymorphismus einfach nicht funktionieren, da Zeiger erforderlich sind.
Sie müssen dies mit Typ-Tags und Gewerkschaften umgehen.
Versionierung
Darüber hinaus. Möglicherweise möchten Sie, dass verschiedene Versionen der Software zusammenarbeiten.
In diesem Fall sollte jedes Objekt zu Beginn seiner Serialisierung eine Versionsnummer schreiben, um die Version anzugeben.
Wenn Sie das Objekt auf der anderen Seite laden, können neuere Objekte möglicherweise die älteren Darstellungen verarbeiten, aber die älteren können die neueren nicht verarbeiten, sodass sie eine Ausnahme auslösen sollten.
Jedes Mal, wenn sich etwas ändert, sollten Sie die Versionsnummer erhöhen.
Zum Abschluss kann die Serialisierung komplex sein. Glücklicherweise müssen Sie nicht alles in Ihrem Programm serialisieren. Meistens werden nur die Protokollnachrichten serialisiert, bei denen es sich häufig um einfache alte Strukturen handelt. Sie brauchen also die komplexen Tricks, die ich oben erwähnt habe, nicht zu oft.
quelle
Zum Lernen habe ich einen einfachen C ++ 11-Serializer geschrieben. Ich hatte verschiedene andere schwerere Angebote ausprobiert, wollte aber etwas, das ich tatsächlich verstehen konnte, wenn es schief ging oder nicht mit dem neuesten g ++ kompiliert werden konnte (was für mich mit Cereal passiert ist; eine wirklich schöne Bibliothek, aber komplex und ich konnte nicht groken die Fehler, die der Compiler beim Upgrade gemeldet hat.) Wie auch immer, es ist nur ein Header und verarbeitet POD-Typen, Container, Maps usw. Keine Versionierung und es werden nur Dateien aus demselben Bogen geladen, in dem es gespeichert wurde.
https://github.com/goblinhack/simple-c-plus-plus-serializer
Anwendungsbeispiel:
quelle