Programing

C ++에서 객체를 어떻게 직렬화합니까?

lottogame 2020. 10. 13. 07:15
반응형

C ++에서 객체를 어떻게 직렬화합니까?


소켓 연결을 통해 직렬화하고 전송해야하는 작은 개체 계층 구조가 있습니다. 개체를 직렬화 한 다음 유형에 따라 역 직렬화해야합니다. C ++에서 쉽게 수행 할 수있는 방법이 있습니까 (Java에 있음)?

C ++ 직렬화 온라인 코드 샘플 또는 자습서가 있습니까?

편집 : 명확하게 말하면 객체를 바이트 배열로 변환 한 다음 객체로 다시 변환하는 방법을 찾고 있습니다. 소켓 전송을 처리 할 수 ​​있습니다.


직렬화에 대해 이야기하면 부스트 직렬화 API 가 떠 오릅니다. 직렬화 된 데이터를 네트워크를 통해 전송하려면 Berkeley 소켓이나 asio 라이브러리를 사용 합니다.

편집 :
객체를 바이트 배열로 직렬화하려는 경우 다음과 같은 방식으로 부스트 직렬 변환기를 사용할 수 있습니다 (자습서 사이트에서 가져옴).

#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
class gps_position
{
private:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & degrees;
        ar & minutes;
        ar & seconds;
    }
    int degrees;
    int minutes;
    float seconds;

public:
    gps_position(){};
    gps_position(int d, int m, float s) :
    degrees(d), minutes(m), seconds(s)
    {}
};

실제 직렬화는 매우 쉽습니다.

#include <fstream>
std::ofstream ofs("filename.dat", std::ios::binary);

    // create class instance
    const gps_position g(35, 59, 24.567f);

    // save data to archive
    {
        boost::archive::binary_oarchive oa(ofs);
        // write class instance to archive
        oa << g;
        // archive and stream closed when destructors are called
    }

역 직렬화는 유사한 방식으로 작동합니다.

포인터의 직렬화 (tress 등과 같은 복잡한 데이터 구조는 문제가 없음), 파생 클래스를 처리 할 수있는 메커니즘도 있으며 이진 및 텍스트 직렬화 중에서 선택할 수 있습니다. 게다가 모든 STL 컨테이너는 즉시 지원됩니다.


경우에 따라 단순 유형을 처리 할 때 다음을 수행 할 수 있습니다.

object o;
socket.write(&o, sizeof(o));

개념 증명 또는 초안으로 괜찮으므로 팀의 다른 구성원이 다른 부분에서 계속 작업 할 수 있습니다.

그러나 조만간, 일반적으로 조만간 이것은 당신을 다치게 할 것입니다!

다음과 같은 문제가 발생합니다.

  • 가상 포인터 테이블이 손상됩니다.
  • 포인터 (데이터 / 멤버 / 함수)가 손상됩니다.
  • 다른 기계에서 패딩 / 정렬의 차이.
  • Big / Little-Endian 바이트 순서 문제.
  • float / double 구현의 변형.

(또한 수신 측에서 무엇을 풀고 있는지 알아야합니다.)

모든 클래스에 대해 고유 한 마샬링 / 마샬링 해제 방법을 개발하여이를 개선 할 수 있습니다. (이상적으로는 가상이므로 하위 클래스에서 확장 할 수 있습니다.) 몇 가지 간단한 매크로를 사용하면 빅 / 리틀 엔디안 중립 순서로 다른 기본 유형을 매우 빠르게 작성할 수 있습니다.

그러나 그런 종류의 지저분한 작업은 boost의 직렬화 라이브러리 를 통해 훨씬 더 좋고 더 쉽게 처리됩니다 .


직렬화는 개체를 이진 데이터로 변환하는 것을 의미합니다. 역 직렬화는 데이터에서 개체를 다시 만드는 것을 의미합니다.

직렬화 할 때 바이트를 uint8_t벡터 로 푸시합니다 . 직렬화를 해제 할 때 uint8_t벡터 에서 바이트를 읽습니다 .

물건을 직렬화 할 때 사용할 수있는 패턴이 확실히 있습니다.

각 직렬화 가능한 클래스 serialize(std::vector<uint8_t> &binaryData)에는 제공된 벡터에 이진 표현을 쓰는 또는 유사한 서명 함수 가 있어야합니다 . 그런 다음이 함수는이 벡터를 멤버의 직렬화 함수로 전달할 수 있으므로 해당 벡터도 여기에 쓸 수 있습니다.

데이터 표현은 아키텍처마다 다를 수 있기 때문입니다. 데이터를 표현하는 방법을 찾아야합니다.

기본부터 시작하겠습니다.

정수 데이터 직렬화

리틀 엔디안 순서로 바이트를 작성하십시오. 또는 크기가 중요한 경우 varint 표현을 사용하십시오.

리틀 엔디안 순서로 직렬화 :

data.push_back(integer32 & 0xFF);
data.push_back((integer32 >> 8) & 0xFF);
data.push_back((integer32 >> 16) & 0xFF);
data.push_back((integer32 >> 24) & 0xFF);

리틀 엔디안 순서에서 역 직렬화 :

integer32 = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);

부동 소수점 데이터 직렬화

내가 아는 한 IEEE 754는 여기에서 독점권을 가지고 있습니다. 나는 수레에 대해 다른 것을 사용할 주류 아키텍처를 모릅니다. 다를 수있는 유일한 것은 바이트 순서입니다. 일부 아키텍처는 리틀 엔디안을 사용하고 다른 아키텍처는 빅 엔디안 바이트 순서를 사용합니다. 이것은 수신 측에서 바이트를 크게하는 순서에주의해야 함을 의미합니다. 또 다른 차이점은 비정규 및 무한대 및 NAN 값을 처리하는 것입니다. 그러나 이러한 값을 피하는 한 괜찮습니다.

직렬화 :

uint8_t mem[8];
memcpy(mem, doubleValue, 8);
data.push_back(mem[0]);
data.push_back(mem[1]);
...

역 직렬화는 역방향으로 수행합니다. 아키텍처의 바이트 순서에 유의하십시오!

문자열 직렬화

First you need to agree on an encoding. UTF-8 is common. Then store it as a length prefixed manner: first you store the length of the string using a method I mentioned above, then write the string byte-by-byte.

Serializing arrays.

They are the same as a strings. You first serialize an integer representing the size of the array then serialize each object in it.

Serializing whole objects

As I said before they should have a serialize method that add content to a vector. To unserialize an object, it should have a constructor that takes byte stream. It can be an istream but in the simplest case it can be just a reference uint8_t pointer. The constructor reads the bytes it wants from the stream and sets up the fields in the object. If the system is well designed and serialize the fields in object field order, you can just pass the stream to the field's constructors in an initializer list and have them deserialized in the right order.

Serializing object graphs

First you need to make sure if these objects are really something you want to serialize. You don't need to serialize them if instances of these objects present on the destination.

Now you found out you need to serialize that object pointed by a pointer. The problem of pointers that they are valid only the in the program that uses them. You cannot serialize pointer, you should stop using them in objects. Instead create object pools. This object pool is basically a dynamic array which contains "boxes". These boxes have a reference count. Non-zero reference count indicates a live object, zero indicates an empty slot. Then you create smart pointer akin to the shared_ptr that doesn't store the pointer to the object, but the index in the array. You also need to agree on an index that denotes the null pointer, eg. -1.

Basically what we did here is replaced the pointers with array indexes. Now when serializing you can serialize this array index as usual. You don't need to worry about where does the object will be in memory on the destination system. Just make sure they have the same object pool too.

So we need to serialize the object pools. But which ones? Well when you serialize an object graph you are not serializing just an object, you are serializing an entire system. This means the serialization of the system shouldn't start from parts of the system. Those objects shouldn't worry about the rest of the system, they only need to serialize the array indexes and that's it. You should have a system serializer routine that orchestrates the serialization of the system and walks through the relevant object pools and serialize all of them.

On the receiving end all the arrays an the objects within are deserialized, recreating the desired object graph.

Serializing function pointers

Don't store pointers in the object. Have a static array which contains the pointers to these functions and store the index in the object.

Since both programs have this table compiled into themshelves, using just the index should work.

Serializing polymorphic types

Since I said you should avoid pointers in serializable types and you should use array indexes instead, polymorphism just cannot work, because it requires pointers.

You need to work this around with type tags and unions.

Versioning

On top of all the above. You might want different versions of the software interoperate.

In this case each object should write a version number at the beginning of their serialization to indicate version.

When loading up the object at the other side the, newer objects maybe able to handle the older representations but the older ones cannot handle the newer so they should throw an exception about this.

Each time a something changes, you should bump the version number.


So to wrap this up, serialization can be complex. But fortunately you don't need to serialize everything in your program, most often only the protocol messages are serialized, which are often plain old structs. So you don't need the complex tricks I mentioned above too often.


By way of learning I wrote a simple C++11 serializer. I had tried various of the other more heavyweight offerings, but wanted something that I could actually understand when it went wrong or failed to compile with the latest g++ (which happened for me with Cereal; a really nice library but complex and I could not grok the errors the compiler threw up on upgrade.) Anyway, it's header only and handles POD types, containers, maps etc... No versioning and it will only load files from the same arch it was saved in.

https://github.com/goblinhack/simple-c-plus-plus-serializer

Example usage:

#include "c_plus_plus_serializer.h"

static void serialize (std::ofstream out)
{
    char a = 42;
    unsigned short b = 65535;
    int c = 123456;
    float d = std::numeric_limits<float>::max();
    double e = std::numeric_limits<double>::max();
    std::string f("hello");

    out << bits(a) << bits(b) << bits(c) << bits(d);
    out << bits(e) << bits(f);
}

static void deserialize (std::ifstream in)
{
    char a;
    unsigned short b;
    int c;
    float d;
    double e;
    std::string f;

    in >> bits(a) >> bits(b) >> bits(c) >> bits(d);
    in >> bits(e) >> bits(f);
}

참고URL : https://stackoverflow.com/questions/523872/how-do-you-serialize-an-object-in-c

반응형