Serialisierung mit C++
-
Hallo,
ich habe ein paar Fragen zur Serialisierung mit C++. Ich möchte gerne Daten serialisieren, um sie über das Netzwerk zu schicken. Dabei sollen alle möglichen numerischen Datentypen (int, float, etc.) aus C++ serialsiert werden.
Was ist der beste Weg das zu tun, ohne auf externe Libs, wie Boost, zurückgreifen zu müssen?
-
Entweder selber schreiben, Boost.Serialization oder Google Protocol Buffers verwenden. Ich persönlich würde zu Google Protocol Buffers tendieren, da es Libraries in verschiedenen Sprachen gibt.
Edit:
Oh... erst lesen dann schreiben... tja, wie gesagt, selber schreiben...
Normalerweise mache ich das so, dass ein Klasse fürs schreiben und eine fürs lesen zuständig ist, jeweils mit den entsprechenden Überladungen.Bsp. für einen Writer/Reader:
typedef std::vector<unsigned char> ProtocolBuffer; class ProtocolWriter { public: ProtocolWriter(ProtocolBuffer& buffer); // Gibt die Anzahl geschriebenen Bytes zurück std::size_t write(int value); std::size_t write(double value); std::size_t write(const std::string& value); // ... }; class ProtocolReader { public: ProtocolReader(ProtocolBuffer& buffer); // Gibt die Anzahl gelesenen Bytes zurück std::size_t read(int& value); std::size_t read(double& value); std::size_t read(std::string& value); };Typischerweise mache ich dann einen Header mit fester Länge, z.B. Payload Länge und Message Typ - danach folgt das spezifische Message Layout. Strings codiere ich typischer weise Length Prefixed.
Einige Überladungen kann man auch sparen und dafür ein Funktions-Template machen.
-
theta schrieb:
Entweder selber schreiben, Boost.Serialization oder Google Protocol Buffers verwenden. Ich persönlich würde zu Google Protocol Buffers tendieren, da es Libraries in verschiedenen Sprachen gibt.
Ja, ich verwende auf der einen Seite QDataStream für die Serialisierung und Deserialisierung.
Auf der anderen Seite steht mir aber kein Qt zur Verfügung und externe Libs sind nicht möglich.
theta schrieb:
tja, wie gesagt, selber schreiben...
Normalerweise mache ich das so, dass ein Klasse fürs schreiben und eine fürs lesen zuständig ist, jeweils mit den entsprechenden Überladungen.Ja, so in etwa habe ich mir das auch gedacht. Ich habe nur keine Lust jetzt großartige eine Serializer-Klasse zu schreiben mit Byte-Order/Endianness.
Aber es bleibt mir wohl nichts anderes übrig, als das Rad neu zu erfinden.

-
Du kannst mal schauen, wie SFML 2 seine
sf::Packet-Klasse aufgebaut hat.
-
Nexus schrieb:
Du kannst mal schauen, wie SFML 2 seine
sf::Packet-Klasse aufgebaut hat.Ahh, Danke! Dieses SFML scheint eine recht beschauliche kleine Packet-Klasse zu haben. Die Stream-Operatoren operator<< undoperator>> sind für die wichtigsten C++-Datentypen überladen. Die Klasse scheint eine feste Byte-Order zu verwenden.
Sieht ziemlich schlicht, aber sauber aus. Darauf kann man aufbauen.

-
Stefan6355 schrieb:
Sieht ziemlich schlicht, aber sauber aus. Darauf kann man aufbauen.

Gilt für ganz SFML. Das Design ist wirklich sehr schön und intuitiv, sowas sieht man sehr selten in C++.
-
Stefan6355 schrieb:
Ja, so in etwa habe ich mir das auch gedacht. Ich habe nur keine Lust jetzt großartige eine Serializer-Klasse zu schreiben mit Byte-Order/Endianness.
Aber es bleibt mir wohl nichts anderes übrig, als das Rad neu zu erfinden.

Ich verstehe Dein Problem nicht. Du willst kein anderes Serialisierungsframework verwenden aber Du willst es auch nicht selbst schreiben
. Also mir fällt beim besten Willen kein anderer Weg ein, als entweder selbst machen oder eben nicht. Das ist doch eine rein binäre Frage.
-
tntnet schrieb:
Stefan6355 schrieb:
Ja, so in etwa habe ich mir das auch gedacht. Ich habe nur keine Lust jetzt großartige eine Serializer-Klasse zu schreiben mit Byte-Order/Endianness.
Aber es bleibt mir wohl nichts anderes übrig, als das Rad neu zu erfinden.

Ich verstehe Dein Problem nicht. Du willst kein anderes Serialisierungsframework verwenden aber Du willst es auch nicht selbst schreiben
. Also mir fällt beim besten Willen kein anderer Weg ein, als entweder selbst machen oder eben nicht. Das ist doch eine rein binäre Frage.Ich habe nicht gesagt das ich kein anderes Framework verwenden will, sondern das ich nicht auf externe Libs, wie Boost, zurückgreifen kann. Ich hatte gehofft das es ein paar fertige Klassen (Open Source) dazu bereits gibt.
Das spielt aber keine Rolle, weil ich mir jetzt ein auf Templates basierendes eigenes Serialisierungs-Framework geschrieben habe. Es ist den Streams von Java (OutputStream, InputStream, ByteArrayOutputStream, ByteArray etc.) nachempfunden und bietet dieselben Schnittstellen, wie QDataStream.
/** * Base class for all input streams. */ class InputStream : public Stream { public: //////////////////////////////////////////////////////////// /// Functions to read data from the stream /// //////////////////////////////////////////////////////////// virtual void read(bool& data, int bit = Bits<bool>::size ) = 0; virtual void read(int8_t& data, int bit = Bits<int8_t>::size ) = 0; virtual void read(uint8_t& data, int bit = Bits<uint8_t>::size ) = 0; virtual void read(int16_t& data, int bit = Bits<int16_t>::size ) = 0; virtual void read(uint16_t& data, int bit = Bits<uint16_t>::size) = 0; virtual void read(int32_t& data, int bit = Bits<int32_t>::size ) = 0; virtual void read(uint32_t& data, int bit = Bits<uint32_t>::size) = 0; virtual void read(int64_t& data, int bit = Bits<int64_t>::size ) = 0; virtual void read(uint64_t& data, int bit = Bits<uint64_t>::size) = 0; virtual void read(float& data, int bit = Bits<int32_t>::size ) = 0; virtual void read(double& data, int bit = Bits<int64_t>::size ) = 0; //virtual void read(const char* data, int bit = Bits<int8_t>::size ) = 0; //virtual void read(const wchar_t* data, int bit = Bits<int16_t>::size ) = 0; virtual void read(std::string& data, size_t maxLength, int bit ) = 0; virtual void read(std::wstring& data, size_t maxLength, int bit ) = 0; virtual void read(void* data, uint16_t length ) = 0; virtual int read(uint8_t* b, int off, int len) = 0; //////////////////////////////////////////////////////////// /// Operator >> overloads to read data from the stream /// //////////////////////////////////////////////////////////// virtual InputStream& operator>>(bool& data) = 0; virtual InputStream& operator>>(int8_t& data) = 0; virtual InputStream& operator>>(uint8_t& data) = 0; virtual InputStream& operator>>(int16_t& data) = 0; virtual InputStream& operator>>(uint16_t& data) = 0; virtual InputStream& operator>>(int32_t& data) = 0; virtual InputStream& operator>>(uint32_t& data) = 0; virtual InputStream& operator>>(int64_t& data) = 0; virtual InputStream& operator>>(uint64_t& data) = 0; virtual InputStream& operator>>(float& data) = 0; virtual InputStream& operator>>(double& data) = 0; //virtual InputStream& operator>>(const char* data) = 0; //virtual InputStream& operator>>(const wchar_t* data) = 0; virtual InputStream& operator>>(std::string& data) = 0; virtual InputStream& operator>>(std::wstring& data) = 0; struct true_type { enum { value = true }; }; struct false_type { enum { value = false }; }; template <typename T> void do_read(T& data, int bit, const false_type&) { read(data, bit); } template <typename T> void do_read(T& data, int bit, const true_type&) { int32_t temp; read(temp, bit); data = static_cast<T>(temp); } };Ich bin zufrieden, ich kann die Byte-Order einstellen, etc.
