Apakah mungkin untuk membuat serial dan deserialisasi kelas di C ++?

141

Apakah mungkin untuk membuat serial dan deserialisasi kelas di C ++?

Saya telah menggunakan Java selama 3 tahun sekarang, dan serialisasi / deserialisasi cukup sepele dalam bahasa itu. Apakah C ++ memiliki fitur yang serupa? Apakah ada perpustakaan asli yang menangani serialisasi?

Sebuah contoh akan sangat membantu.

Agusti-N
sumber
2
tidak yakin apa yang Anda maksud dengan "native", maksud Anda native C ++ (seperti Boost.Serialization)? Apakah yang Anda maksud hanya menggunakan C ++ Standard Library? Apakah maksud Anda sesuatu yang lain?
jwfearn
1
maksud saya "bukan perpustakaan perangkat lunak eksternal". Dan maaf bahasa Inggris saya kurang baik: S. Saya dari Argentina
Agusti-N
3
Tidak ada cara asli untuk membuat serial objek (Anda masih dapat membuang data biner dari POD, tetapi Anda tidak akan mendapatkan apa yang Anda inginkan). Namun, Boost, meski bukan "pustaka internal", adalah pustaka eksternal pertama yang harus Anda pertimbangkan untuk ditambahkan ke kompiler Anda. Boost adalah kualitas STL (yaitu Top Gun C ++)
paercebal

Jawaban:

96

The Boost::serializationlibrary menangani ini agak elegan. Saya telah menggunakannya di beberapa proyek. Ada program contoh, yang menunjukkan cara menggunakannya, di sini .

Satu-satunya cara asli untuk melakukannya adalah dengan menggunakan aliran. Itu pada dasarnya semua yang Boost::serializationperpustakaan lakukan, itu memperluas metode aliran dengan menyiapkan kerangka kerja untuk menulis objek ke format seperti teks dan membacanya dari format yang sama.

Untuk tipe bawaan, atau tipe Anda sendiri dengan operator<<dan operator>>ditentukan dengan benar, itu cukup sederhana; lihat FAQ C ++ untuk informasi lebih lanjut.

Kepala Geek
sumber
Tampaknya bagi saya bahwa boost :: serialization mengharuskan pemanggil untuk melacak urutan penulisan dan pembacaan objek. Apakah itu benar? Jadi jika ada perubahan dalam urutan dua bidang yang ditulis di antara versi program, maka kami memiliki ketidakcocokan. Apakah ini benar?
Agnel Kurian
1
Itu mungkin karena fungsi serialisasi, bukan kode serialisasi Boost :: itu sendiri.
Kepala Geek
1
@ 0xDEADBEEF: Itu mungkin terjadi saat menggunakan arsip binary_ (i | o), yang memperkenalkan "masalah" lain seperti endian-ness. Coba arsip text_ (i | o), ini lebih agnostik platform.
Ela782
2
Solusi kerangka / pustaka tertentu tidak boleh menjadi jawaban yang diterima.
Andrea
3
@Andrea: Perpustakaan Boost adalah kasus khusus. Sampai C ++ 11 diselesaikan, sangat tidak mungkin untuk menulis kode C ++ modern tanpanya, jadi itu lebih dekat ke STL sekunder daripada pustaka terpisah.
Kepala Geek
55

Saya menyadari ini adalah postingan lama tetapi ini adalah salah satu postingan pertama yang muncul saat mencari c++ serialization.

Saya mendorong siapa pun yang memiliki akses ke C ++ 11 untuk melihat sereal , perpustakaan hanya header C ++ 11 untuk serialisasi yang mendukung biner, JSON, dan XML di luar kotak. sereal dirancang agar mudah diperluas dan digunakan serta memiliki sintaks yang mirip dengan Boost.

Azoth
sumber
4
Hal yang baik tentang sereal adalah tidak seperti boost, ia memiliki metadata yang minimal (hampir tidak ada). boost :: serialization menjadi sangat mengganggu ketika setiap kali Anda membuka arsip, ia menulis versi libnya ke aliran, yang membuat penambahan ke file tidak mungkin dilakukan.
CyberSnoopy
@CyberSnoopy - ada flag untuk menyembunyikan fitur ini saat arsip dibuat - tentu saja Anda harus mengingatnya saat membaca arsip juga.
Robert Ramey
17

Boost adalah saran yang bagus. Tetapi jika Anda ingin menggulung sendiri, itu tidak terlalu sulit.

Pada dasarnya Anda hanya perlu cara untuk membuat grafik objek dan kemudian menampilkannya ke beberapa format penyimpanan terstruktur (JSON, XML, YAML, apa pun). Membangun grafik semudah menggunakan algoritme objek rekursif penandaan yang layak dan kemudian mengeluarkan semua objek yang ditandai.

Saya menulis artikel yang menjelaskan sistem serialisasi yang belum sempurna (tapi tetap kuat). Anda mungkin merasa tertarik: Menggunakan SQLite sebagai Format File dalam Disk, Bagian 2 .

Frank Krueger
sumber
15

Saya merekomendasikan buffer protokol Google . Saya memiliki kesempatan untuk menguji coba pustaka pada proyek baru dan itu sangat mudah digunakan. Perpustakaan sangat dioptimalkan untuk kinerja.

Protobuf berbeda dari solusi serialisasi lain yang disebutkan di sini dalam arti bahwa itu tidak membuat serialisasi objek Anda, melainkan menghasilkan kode untuk objek yang serialisasi sesuai dengan spesifikasi Anda.

yoav.aviram
sumber
2
Pernahkah Anda mengalami serialisasi objek berukuran sekitar 10-50MB menggunakan ini? Dokumentasi tampaknya mengatakan bahwa buffer protokol paling cocok untuk objek berukuran sekitar MB.
Agnel Kurian
Saya memutar lib saya sendiri, belum menggunakan aliran (belum), jadi ini benar-benar untuk hal-hal kecil: gist.github.com/earonesty/5ba3a93f391ea03ef90884840f068767
Erik Aronesty
14

Sejauh perpustakaan "built-in" pergi, <<dan >>telah dipesan khusus untuk serialisasi.

Anda harus mengganti <<untuk mengeluarkan objek Anda ke beberapa konteks serialisasi (biasanya sebuah iostream) dan >>untuk membaca data kembali dari konteks itu. Setiap objek bertanggung jawab untuk mengeluarkan objek turunannya yang teragregasi.

Metode ini berfungsi dengan baik selama grafik objek Anda tidak berisi siklus.

Jika ya, maka Anda harus menggunakan perpustakaan untuk menangani siklus tersebut.

Frank Krueger
sumber
3
Tentunya, itu tidak benar ... <<operator yang diimplementasikan digunakan untuk mencetak representasi teks yang dapat dibaca manusia dari objek, yang seringkali bukan yang Anda inginkan untuk serialisasi.
einpoklum
1
@einpoklum Alih-alih menetapkan <<generik ostream, coba tentukan untuk aliran file.
Carcigenicate
1
@Carcigenicate: File log yang mengambil teks yang dapat dibaca manusia adalah aliran file.
einpoklum
1
@einpoklum Saya tidak begitu yakin apa yang Anda maksud. Frank benar, operator tersebut dapat digunakan untuk membuat serial. Saya baru saja mendefinisikannya untuk membuat serial / deserialisasi vektor.
Carcigenicate
2
Saya pikir tangkapannya ada di sini "Anda harus mengganti <<untuk mengeluarkan objek Anda ke beberapa konteks serialisasi ... Setiap objek bertanggung jawab untuk mengeluarkannya ..." - pertanyaannya adalah tentang bagaimana menghindari keharusan menulis itu dengan susah payah untuk setiap objek: berapa banyak yang bisa bahasa atau perpustakaan membantu?
ShreevatsaR
14

Boost :: serialization adalah pilihan yang bagus, tetapi saya menemukan proyek baru: Cereal yang menurut saya jauh lebih elegan! Saya sangat menyarankan untuk menyelidikinya.

M2tM
sumber
4

Anda dapat memeriksa protokol amef , contoh pengkodean C ++ di amef akan seperti,

    //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);

Decoding di java akan seperti,

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

Implementasi Protokol memiliki codec untuk C ++ dan Java, yang menarik adalah dapat mempertahankan representasi kelas objek dalam bentuk pasangan nilai nama, saya memerlukan protokol serupa di proyek terakhir saya, ketika saya kebetulan menemukan protokol ini, saya sebenarnya memodifikasi pustaka dasar sesuai dengan kebutuhan saya. Semoga ini bisa membantu Anda.

Dave
sumber
3

Sweet Persist adalah satu lagi.

Dimungkinkan untuk membuat serial ke dan dari stream dalam format XML, JSON, Lua, dan biner.

Vincent
sumber
Situs itu sepertinya tidak aktif
Janusz Syf
2

Saya sarankan melihat ke pabrik Abstrak yang sering digunakan sebagai dasar untuk serialisasi

Saya telah menjawab dalam pertanyaan SO lain tentang pabrik C ++. Silakan lihat di sana jika pabrik fleksibel menarik. Saya mencoba menjelaskan cara lama dari ET ++ untuk menggunakan makro yang telah bekerja sangat baik untuk saya.

ET ++ adalah proyek untuk mem-port MacApp lama ke C ++ dan X11. Dalam upaya itu Eric Gamma dll mulai memikirkan Pola Desain . ET ++ berisi cara otomatis untuk serialisasi dan introspeksi saat runtime.

epatel
sumber
0

Jika Anda menginginkan kinerja yang sederhana dan terbaik dan tidak peduli dengan kompatibilitas data mundur, coba HPS , ini ringan, jauh lebih cepat daripada Boost, dll, dan jauh lebih mudah digunakan daripada Protobuf, dll.

Contoh:

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);
streaver91.dll
sumber
0

Ini adalah pustaka serializer sederhana yang saya buat. Ini hanya header, c11 dan memiliki contoh untuk membuat serial tipe dasar. Ini satu untuk peta ke kelas.

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;
}

Keluaran:

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
}
Neil McGill
sumber
0

Saya menggunakan template berikut untuk mengimplementasikan serialisasi:

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);
}

Berikut Tadalah tipe yang ingin Anda serialisasi Modeadalah tipe dummy untuk membedakan antara berbagai jenis serialisasi, mis. integer yang sama dapat diserialkan sebagai little endian, big endian, varint, dll.

Secara default, Serializermendelegasikan tugas ke objek yang diserialisasi. Untuk tipe bawaan, Anda harus membuat spesialisasi template dari Serializer.

Templat fungsi kenyamanan juga disediakan.

Misalnya serialisasi little endian dari unsigned integers:

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++;
        }
    }
};

Kemudian untuk membuat serial:

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

Untuk deserialisasi:

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

Karena logika iterator abstrak, ia harus bekerja dengan iterator apa pun (misalnya, iterator aliran), penunjuk, dll.

Calmarius
sumber