EDIT: neue Antwort unten hinzugefügt, um das eigentliche Problem zu adressieren.
Ihre Frage impliziert, dass Sie mehrmals auf dasselbe Objekt deserialisieren. Es ist umständlich, ob das sauber ist oder nicht. Wenn Sie zum Beispiel ein Schachbrett haben, möchten Sie die Anfangsposition der Stücke synchronisieren (um vom letzten gespeicherten Spiel fortzusetzen). Um die Züge zu kommunizieren, während das Spiel gespielt wird, kann es sinnvoller sein, die einzelnen Züge als separate Objekte zu senden (die dann nach Erhalt auf das Brettobjekt angewendet werden), anstatt das gesamte Brettobjekt zu übertragen, das nur das überträgt geändert, wenn es bereits 'initialisiert' ist. Auf diese Weise können Sie die Eingabe zuerst validieren und ungültige Bewegungen ignorieren. Jedenfalls wollte ich das nur erwähnen, lass uns weitergehen.
Wenn Sie ein Objekt haben, das mehrfach synchronisiert werden kann und nur einmal übertragen werden muss, lassen Sie das Objekt entscheiden, ob es 'initialisiert' ist oder nicht (und folglich, wenn es alles übertragen muss oder nur eine Untermenge) mit einem Flag (das nicht serialisiert ist).
Dann können Sie das Flag im Serialisierungscode des Objekts überprüfen, genau wie in dem von Ihnen geposteten Code (mit der Ausnahme, dass das Flag kein Parameter für die Serialisierungsmethode ist, sondern eine Elementvariable des Objekts, das Sie sind de/serialisieren).Wenn das Flag gesetzt ist, de/serialisieren Sie alles und setzen Sie das Flag zurück. Sowohl der Client als auch der Server müssen denselben Status des Flags haben oder die Serialisierung bricht ab.
Alternativ könnten Sie zuerst das Flag serialisieren, um dem Empfänger mitzuteilen, wie die Deserialisierung durchgeführt werden muss (z. B. ein Bit für jede Mitgliedsdatengruppe).
Beachten Sie, dass die Deserialisierung mit der Serialisierung übereinstimmen muss; Sie müssen dieselben Objekte in derselben Reihenfolge extrahieren, in der sie serialisiert wurden. Sie können jedoch polymorphe Klassen serialisieren, vorausgesetzt, sie werden auf derselben Ebene in der Klassenhierarchie serialisiert wie sie deserialisiert werden (im Zweifelsfall wird auch beim Senden und Deserialisieren durch den Basiszeiger zum Basiszeiger gewandelt)).
In Bezug auf Ihre zweite Frage, was Sie suchen, ist non-intrusive serialization. Die nicht-intrusive Serialisierung ruft freistehende Funktionen auf und übergibt das zu serialisierende Objekt als Parameter (so werden std :: vector und boost :: shared_ptr serialisiert). Sie können BOOST_SERIALIZATION_SPLIT_FREE
verwenden, um die freistehende Funktion in save()
und load()
aufzuteilen. Für intrusive Serialisierung ist es BOOST_SERIALIZATION_SPLIT_MEMBER
.
Um ein verallgemeinerte de/Serialisierungsfunktion (das überträgt ein Objekt über das Netzwerk zum Beispiel) schreiben Sie Vorlagen verwenden können:
template<typename T>
void transmit(const T& data) {
// ...
archive << data
socket << archive_stream;
}
Die Einschränkung bei dieser Methode ist, dass der Empfänger wissen müssen, welche Art von Objekt wurde geschickt. Wenn Sie zufällige Objekte senden möchten, machen sie polymorphe:
IData* data = 0;
archive >> data;
switch(data->type()) {
case TYPE_INIT:
return dispatch(static_cast<Board*>(data));
case TYPE_MOVE:
return dispatch(static_cast<Move*>(data));
case TYPE_CHAT:
return dispatch(static_cast<ChatMsg*>(data));
}
UPDATE: Wenn Sie steuern müssen, wie Ihre (individuelle) Serialisierungsmethoden/Funktionen verhalten, basierend auf einem Zustand unbekannten Typen Wenn Sie serialisiert werden, können Sie eine eigene Archivklasse implementieren, die den Status enthält. Die Serialisierungsfunktionen können dann den Status abfragen und entsprechend handeln.
Dieser Status (oder eine entsprechende Ersetzung) muss ebenfalls serialisiert werden, um anzugeben, wie die Daten deserialisiert werden müssen. Zum Beispiel könnte dieses "unterschiedliche Verhalten" der Serialisierungsfunktionen eine Art von Komprimierung sein, und der Status ist die Art der verwendeten Komprimierung.
Hier ist ein minimales Beispiel für ein benutzerdefiniertes Ausgabearchiv. Für weitere Informationen können Sie Derivation from an Existing Archive lesen und durch die Boost-Quellen graben.
eine Klasse Da Sie nicht ändern können:
struct Foo {
Foo() : i(42), s("foo") {}
int i;
std::string s;
};
Sie möchten unbekannt in der Klasse auf einem Zustand serialisiert i
und/oder s
basiert. Sie könnten einen Wrapper erstellen, um ihn zu serialisieren und den Status hinzuzufügen. Dies funktioniert jedoch nicht, wenn das Objekt in einem Vektor (oder einer anderen Klasse) liegt.
Es kann einfacher sein, um das Archiv bewusst zu machen den Staat statt:
#include <boost/archive/text_oarchive.hpp>
// using struct to omit a bunch of friend declarations
struct oarchive : boost::archive::text_oarchive_impl<oarchive>
{
oarchive(std::ostream& os, unsigned flags=0)
: boost::archive::text_oarchive_impl<oarchive>(os,flags),mask(0){}
// forward to base class
template<class T> void save(T& t) {
boost::archive::text_oarchive_impl<oarchive>::save(t);
}
// this is the 'state' that can be set on the archive
// and queried by the serialization functions
unsigned get_mask() const { return mask; }
void set_mask(unsigned m) { mask = m; }
void clear_mask() { mask = 0; }
private:
unsigned mask;
};
// explicit instantiation of class templates involved
namespace boost { namespace archive {
template class basic_text_oarchive<oarchive>;
template class text_oarchive_impl<oarchive>;
template class detail::archive_serializer_map<oarchive>;
} }
// template implementations (should go to the .cpp)
#include <boost/archive/impl/basic_text_oarchive.ipp>
#include <boost/archive/impl/text_oarchive_impl.ipp>
#include <boost/archive/impl/archive_serializer_map.ipp>
Jetzt ist der Zustand zu setzen und Abfrage:
enum state { FULL=0x10, PARTIAL=0x20 };
und ein Verfahren um den Zustand zu setzen (dies ist nur ein sehr einfaches Beispiel):
oarchive& operator<<(oarchive& ar, state mask) {
ar.set_mask(ar.get_mask()|mask);
return ar;
}
Schließlich wird der (nicht intrusiv) Serialisierung Funktion:
namespace boost { namespace serialization {
template<class Archive>
void save(Archive & ar, const Foo& foo, const unsigned int version)
{
int mask = ar.get_mask(); // get state from the archive
ar << mask; // serialize the state! when deserializing,
// read the state first and extract the data accordingly
if(mask & FULL)
ar << foo.s; // only serialize s if FULL is set
ar << foo.i; // otherwise serialize i only
ar.clear_mask(); // reset the state
}
} } // boost::serialization
BOOST_SERIALIZATION_SPLIT_FREE(Foo)
Und dies kann wie folgt verwendet werden:
int main() {
std::stringstream strm;
oarchive ar(strm);
Foo f;
ar << PARTIAL << f << FULL << f;
std::cout << strm.str();
}
Der Zweck dieses Beispiels ist es nur um das Prinzip zu veranschaulichen. Es ist zu einfach für den Produktionscode.
Das Problem, das ich mit Boost-haben ist, dass ich die Mitglieder einer Klasse serialisiert werden müssen, was ich in einigen Fällen tun möchte, ist nur Teile einer Klasse serialisiert, Teile, die möglicherweise nicht einmal Membervariablen sind, sondern Daten, die aus Teilen von Membervariablen erstellt werden. Ich werde nicht alle Daten jedes Mal vollständig serialisieren, ich werde nur die Daten serialisieren, die benötigt werden, um festzustellen, was sich geändert hat. Nur die anfängliche Serialisierung enthält alle Daten. –
Basierend auf meinen allgemeinen Bedürfnissen habe ich mich entschieden, die Boost-Serialisierung nicht zu verwenden. Meistens wollte ich nur eine Bibliothek, die gewöhnliche Datentypen zu/von Binärdateien serialisiert, aber die Art und Weise, wie Boost dies tat, war nicht genau das, wonach ich suchte. Ich werde wahrscheinlich meine eigenen Methoden schreiben, um Typen in/aus binär zu konvertieren und eine separate Bibliothek zu verwenden, um die Bitströme für mich zu komprimieren/dekomprimieren. Ihre Antwort nähert sich jedoch aufgrund meiner Frage dem, was ich brauchte, daher werde ich sie als die Antwort auf meine Frage markieren. Vielen Dank. –
@NicFoster Ich war im Begriff, die Antwort zu entfernen, da ich Ihre Frage missverstanden habe. fühlen Sie sich frei, es zu inakzeptabel und unupvote, bis ich (oder jemand anderes) mit einer richtigen Lösung kommt. Sie möchten eine Klasse serialisieren, die Sie beispielsweise in einem Vektor nicht ändern können, und Sie möchten steuern, wie sie serialisiert wird, ohne zu einer globalen Variablen zurückzukehren. –