Có thể tuần tự hóa và giải tuần tự hóa một lớp trong C ++ không?


138

Có thể tuần tự hóa và giải tuần tự hóa một lớp trong C ++ không?

Tôi đã sử dụng Java được 3 năm rồi và việc tuần tự hóa / giải tuần tự hóa khá tầm thường trong ngôn ngữ đó. C ++ có các tính năng tương tự không? Có thư viện riêng xử lý tuần tự hóa?

Một ví dụ sẽ hữu ích.


2
không chắc ý của bạn là "bản địa", bạn có nghĩa là C ++ bản địa (như Boost.Serialization) không? Bạn có nghĩa là chỉ sử dụng Thư viện chuẩn C ++? Bạn có ý gì khác?
jwfearn

1
tôi có nghĩa là "không phải là một thư viện phần mềm bên ngoài". Và xin lỗi tiếng anh của tôi không tốt lắm: S. Tôi đến từ Argentina
Agusti-N

3
Không có cách riêng để tuần tự hóa một đối tượng (bạn vẫn có thể kết xuất dữ liệu nhị phân từ POD, nhưng bạn sẽ không nhận được những gì bạn muốn). Tuy nhiên, Boost, trong khi không phải là "thư viện nội bộ", là thư viện bên ngoài đầu tiên bạn nên xem xét để thêm vào trình biên dịch của mình. Boost có chất lượng STL (tức là Top Gun C ++)
paercebal

Câu trả lời:


95

Các Boost::serializationthư viện xử lý này khá thanh lịch. Tôi đã sử dụng nó trong một số dự án. Có một chương trình ví dụ, cho thấy cách sử dụng nó ở đây .

Cách duy nhất để làm điều đó là sử dụng các luồng. Về cơ bản, tất cả các Boost::serializationthư viện đều làm như vậy, nó mở rộng phương thức truyền phát bằng cách thiết lập một khung để viết các đối tượng thành định dạng giống như văn bản và đọc chúng từ cùng định dạng.

Đối với các loại tích hợp hoặc các loại của riêng bạn operator<<operator>>được xác định đúng, điều đó khá đơn giản; xem C ++ FAQ để biết thêm thông tin.


Dường như với tôi rằng boost :: serialization yêu cầu người gọi theo dõi thứ tự các đối tượng được viết và đọc. Đúng không? Vì vậy, nếu có sự thay đổi theo thứ tự hai trường được viết giữa các phiên bản của một chương trình thì chúng ta có sự không tương thích. Thê nay đung không?
Agnel Kurian

1
Điều đó có thể là do các chức năng tuần tự hóa, chứ không phải chính mã tuần tự Boost ::.
Head Geek

1
@ 0xDEADBEEF: Điều đó có thể xảy ra khi sử dụng kho lưu trữ binary_ (i | o), giới thiệu các "vấn đề" khác như endian-ness. Hãy thử lưu trữ text_ (i | o), đó là bất khả tri về nền tảng.
Ela782

2
Một giải pháp thư viện / thư viện cụ thể không nên là câu trả lời được chấp nhận.
Andrea

3
@Andrea: Thư viện Boost là một trường hợp đặc biệt. Cho đến khi C ++ 11 được hoàn thành, không thể viết mã C ++ hiện đại mà không có nó, vì vậy nó gần với STL thứ cấp hơn là một thư viện riêng biệt.
Head Geek

52

Tôi nhận ra đây là một bài viết cũ nhưng nó là một trong những bài đầu tiên xuất hiện khi tìm kiếm c++ serialization.

Tôi khuyến khích bất cứ ai có quyền truy cập vào C ++ 11 hãy xem ngũ cốc , thư viện chỉ dành cho tiêu đề C ++ 11 để tuần tự hóa hỗ trợ nhị phân, JSON và XML. ngũ cốc được thiết kế để dễ dàng mở rộng và sử dụng và có cú pháp tương tự như Boost.


4
Điểm hay của ngũ cốc là không giống như boost, nó có siêu dữ liệu tối thiểu (hầu như không có). boost :: serialization trở nên thực sự khó chịu khi mỗi khi bạn mở một kho lưu trữ, nó sẽ ghi phiên bản lib của nó vào luồng, điều này khiến cho việc thêm vào một tập tin là không thể.
CyberSnoopy

@CyberSnoopy - có một cờ để loại bỏ tính năng này khi lưu trữ được tạo - tất nhiên bạn cũng phải nhớ nó khi đọc lưu trữ.
Robert Ramey

16

Boost là một gợi ý tốt. Nhưng nếu bạn muốn tự lăn, nó không quá khó.

Về cơ bản, bạn chỉ cần một cách để xây dựng một biểu đồ các đối tượng và sau đó xuất chúng sang một số định dạng lưu trữ có cấu trúc (JSON, XML, YAML, bất cứ điều gì). Xây dựng biểu đồ đơn giản như sử dụng thuật toán đối tượng đệ quy đánh dấu và sau đó xuất ra tất cả các đối tượng được đánh dấu.

Tôi đã viết một bài báo mô tả một hệ thống tuần tự thô sơ (nhưng vẫn mạnh mẽ). Bạn có thể thấy thú vị: Sử dụng SQLite làm Định dạng tệp trên đĩa, Phần 2 .


14

Theo như các thư viện "tích hợp", <<>>đã được dành riêng để tuần tự hóa.

Bạn nên ghi đè <<để xuất đối tượng của mình sang một số bối cảnh tuần tự hóa (thường là một iostream) và >>để đọc lại dữ liệu từ bối cảnh đó. Mỗi đối tượng chịu trách nhiệm xuất ra các đối tượng con tổng hợp của nó.

Phương pháp này hoạt động tốt miễn là đồ thị đối tượng của bạn không chứa chu kỳ.

Nếu vậy, bạn sẽ phải sử dụng một thư viện để đối phó với các chu kỳ đó.


3
Chắc chắn, điều đó không thể đúng ... các <<toán tử được triển khai được sử dụng để in các biểu diễn văn bản có thể đọc được của con người, thường không phải là thứ bạn muốn để tuần tự hóa.
einpoklum

1
@einpoklum Thay vì xác định <<cho chung chung ostream, hãy thử xác định nó cho một luồng tệp.
Carcigenicate

1
@Carcigenicate: Một tệp nhật ký lấy văn bản có thể đọc được của con người là một luồng tệp.
einpoklum

1
@einpoklum Tôi không chắc ý của bạn là gì. Mặc dù vậy, quyền của Frank có thể được sử dụng để tuần tự hóa. Tôi chỉ định nghĩa chúng để tuần tự hóa / giải tuần tự hóa một vectơ.
Carcigenicate

2
Tôi nghĩ rằng sản phẩm khai thác ở đây, bạn nên ghi đè <<để xuất đối tượng của mình sang một số bối cảnh tuần tự hóa. Mỗi đối tượng chịu trách nhiệm xuất ra trò chơi của mình - câu hỏi là làm thế nào để tránh phải viết ra một cách tốn công cho mỗi đối tượng: bao nhiêu có thể ngôn ngữ hay thư viện giúp?
ShreevatsaR

14

Tôi khuyên dùng bộ đệm giao thức Google . Tôi đã có cơ hội lái thử thư viện trong một dự án mới và nó rất dễ sử dụng. Thư viện được tối ưu hóa rất nhiều cho hiệu suất.

Protobuf khác với các giải pháp tuần tự hóa khác được đề cập ở đây theo nghĩa là nó không tuần tự hóa các đối tượng của bạn, mà tạo ra mã cho các đối tượng được tuần tự hóa theo đặc điểm kỹ thuật của bạn.


2
Bạn đã có kinh nghiệm tuần tự hóa các đối tượng có kích thước khoảng 10-50 MB bằng cách này chưa? Tài liệu dường như nói rằng bộ đệm giao thức phù hợp nhất cho các đối tượng có kích thước MB.
Agnel Kurian

Tôi đã tự tạo ra lib của mình, không sử dụng các luồng (chưa), vì vậy nó thực sự dành cho những thứ nhỏ nhặt: gist.github.com/earonesty/5ba3a93f391ea03ef90884840f068767
Erik Aronesty


4

Bạn có thể kiểm tra giao thức amef , một ví dụ về mã hóa C ++ trong amef sẽ như thế nào,

    //Create a new AMEF object
    AMEFObject *object = new AMEFObject();

    //Add a child string object
    object->addPacket("This is the Automated Message Exchange Format Object property!!","adasd");   

    //Add a child integer object
    object->addPacket(21213);

    //Add a child boolean object
    object->addPacket(true);

    AMEFObject *object2 = new AMEFObject();
    string j = "This is the property of a nested Automated Message Exchange Format Object";
    object2->addPacket(j);
    object2->addPacket(134123);
    object2->addPacket(false);

    //Add a child character object
    object2->addPacket('d');

    //Add a child AMEF Object
    object->addPacket(object2);

    //Encode the AMEF obejct
    string str = new AMEFEncoder()->encode(object,false);

Giải mã trong java sẽ như thế nào,

    string arr = amef encoded byte array value;
    AMEFDecoder decoder = new AMEFDecoder()
    AMEFObject object1 = AMEFDecoder.decode(arr,true);

Việc triển khai Giao thức có codec cho cả C ++ và Java, phần thú vị là nó có thể giữ lại biểu diễn lớp đối tượng dưới dạng các cặp giá trị tên, tôi yêu cầu một giao thức tương tự trong dự án cuối cùng của tôi, khi tôi tình cờ gặp giao thức này, tôi thực sự đã tình cờ sửa đổi thư viện cơ sở theo yêu cầu của tôi. Hy vọng điều này sẽ giúp bạn.




2

Tôi đề nghị xem xét các nhà máy trừu tượng thường được sử dụng làm cơ sở cho việc tuần tự hóa

Tôi đã trả lời trong một câu hỏi SO khác về các nhà máy C ++. Xin vui lòng xem ở đó nếu một nhà máy linh hoạt được quan tâm. Tôi cố gắng mô tả một cách cũ từ ET ++ để sử dụng các macro đã hoạt động rất tốt cho tôi.

ET ++ là một dự án chuyển MacApp cũ sang C ++ và X11. Trong nỗ lực của nó, Eric Gamma v.v ... bắt đầu nghĩ về các mẫu thiết kế . ET ++ chứa các cách tự động để tuần tự hóa và hướng nội khi chạy.


0

Nếu bạn muốn hiệu suất đơn giản và tốt nhất và không quan tâm đến khả năng tương thích dữ liệu lạc hậu, hãy thử HPS , nó nhẹ, nhanh hơn nhiều so với Boost, v.v. và dễ sử dụng hơn nhiều so với Protobuf, v.v.

Thí dụ:

std::vector<int> data({22, 333, -4444});
std::string serialized = hps::serialize_to_string(data);
auto parsed = hps::parse_from_string<std::vector<int>>(serialized);

0

Đây là một thư viện serializer đơn giản tôi gõ lên. Đó chỉ là tiêu đề, c11 và có các ví dụ để tuần tự hóa các loại cơ bản. Đây là một bản đồ cho lớp.

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

#include "c_plus_plus_serializer.h"

class Custom {
public:
    int a;
    std::string b;
    std::vector c;

    friend std::ostream& operator<<(std::ostream &out, 
                                    Bits my)
    {
        out << bits(my.t.a) << bits(my.t.b) << bits(my.t.c);
        return (out);
    }

    friend std::istream& operator>>(std::istream &in, 
                                    Bits my)
    {
        in >> bits(my.t.a) >> bits(my.t.b) >> bits(my.t.c);
        return (in);
    }

    friend std::ostream& operator<<(std::ostream &out, 
                                    class Custom &my)
    {
        out << "a:" << my.a << " b:" << my.b;

        out << " c:[" << my.c.size() << " elems]:";
        for (auto v : my.c) {
            out << v << " ";
        }
        out << std::endl;

        return (out);
    }
};

static void save_map_key_string_value_custom (const std::string filename)
{
    std::cout << "save to " << filename << std::endl;
    std::ofstream out(filename, std::ios::binary );

    std::map< std::string, class Custom > m;

    auto c1 = Custom();
    c1.a = 1;
    c1.b = "hello";
    std::initializer_list L1 = {"vec-elem1", "vec-elem2"};
    std::vector l1(L1);
    c1.c = l1;

    auto c2 = Custom();
    c2.a = 2;
    c2.b = "there";
    std::initializer_list L2 = {"vec-elem3", "vec-elem4"};
    std::vector l2(L2);
    c2.c = l2;

    m.insert(std::make_pair(std::string("key1"), c1));
    m.insert(std::make_pair(std::string("key2"), c2));

    out << bits(m);
}

static void load_map_key_string_value_custom (const std::string filename)
{
    std::cout << "read from " << filename << std::endl;
    std::ifstream in(filename);

    std::map< std::string, class Custom > m;

    in >> bits(m);
    std::cout << std::endl;

    std::cout << "m = " << m.size() << " list-elems { " << std::endl;
    for (auto i : m) {
        std::cout << "    [" << i.first << "] = " << i.second;
    }
    std::cout << "}" << std::endl;
}

void map_custom_class_example (void)
{
    std::cout << "map key string, value class" << std::endl;
    std::cout << "============================" << std::endl;
    save_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
    load_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
    std::cout << std::endl;
}

Đầu ra:

map key string, value class
============================
save to map_of_custom_class.bin
read from map_of_custom_class.bin

m = 2 list-elems {
    [key1] = a:1 b:hello c:[2 elems]:vec-elem1 vec-elem2
    [key2] = a:2 b:there c:[2 elems]:vec-elem3 vec-elem4
}

0

Tôi đang sử dụng mẫu sau để thực hiện tuần tự hóa:

template <class T, class Mode = void> struct Serializer
{
    template <class OutputCharIterator>
    static void serializeImpl(const T &object, OutputCharIterator &&it)
    {
        object.template serializeThis<Mode>(it);
    }

    template <class InputCharIterator>
    static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
    {
        return T::template deserializeFrom<Mode>(it, end);
    }
};

template <class Mode = void, class T, class OutputCharIterator>
void serialize(const T &object, OutputCharIterator &&it)
{
    Serializer<T, Mode>::serializeImpl(object, it);
}

template <class T, class Mode = void, class InputCharIterator>
T deserialize(InputCharIterator &&it, InputCharIterator &&end)
{
    return Serializer<T, Mode>::deserializeImpl(it, end);
}

template <class Mode = void, class T, class InputCharIterator>
void deserialize(T &result, InputCharIterator &&it, InputCharIterator &&end)
{
    result = Serializer<T, Mode>::deserializeImpl(it, end);
}

Đây Tlà loại bạn muốn tuần tự hóa Modelà một loại giả để phân biệt giữa các loại tuần tự khác nhau, ví dụ. cùng một số nguyên có thể được tuần tự hóa như endian nhỏ, endian lớn, varint, v.v.

Theo mặc định, các Serializerđại biểu nhiệm vụ cho đối tượng được tuần tự hóa. Đối với các loại được xây dựng, bạn nên tạo một chuyên môn mẫu của Serializer.

Mẫu chức năng thuận tiện cũng được cung cấp.

Ví dụ: tuần tự hóa endian nhỏ của số nguyên không dấu:

struct LittleEndianMode
{
};

template <class T>
struct Serializer<
    T, std::enable_if_t<std::is_unsigned<T>::value, LittleEndianMode>>
{
    template <class InputCharIterator>
    static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
    {
        T res = 0;

        for (size_t i = 0; i < sizeof(T); i++)
        {
            if (it == end) break;
            res |= static_cast<T>(*it) << (CHAR_BIT * i);
            it++;
        }

        return res;
    }

    template <class OutputCharIterator>
    static void serializeImpl(T number, OutputCharIterator &&it)
    {
        for (size_t i = 0; i < sizeof(T); i++)
        {
            *it = (number >> (CHAR_BIT * i)) & 0xFF;
            it++;
        }
    }
};

Sau đó để tuần tự hóa:

std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));

Để giải trừ:

uint32_t val;
deserialize(val, serialized.begin(), serialized.end());

Do logic iterator trừu tượng, nó sẽ hoạt động với bất kỳ iterator nào (ví dụ: iterator stream), con trỏ, v.v.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.