Pertanyaan Bagaimana Anda membuat serial objek dalam C ++?


Saya memiliki hierarki kecil objek yang saya perlukan untuk membuat serial dan mengirimkan melalui koneksi soket. Saya perlu kedua cerita bersambung objek, kemudian deserialize berdasarkan jenis apa itu. Apakah ada cara mudah untuk melakukan ini di C ++ (seperti yang ada di Java)?

Apakah ada contoh kode atau tutorial serialisasi online C ++?

EDIT: Hanya untuk memperjelas, saya mencari metode untuk mengubah objek menjadi larik byte, lalu kembali ke objek. Saya bisa menangani transmisi socket.


75
2018-02-07 14:51


asal


Jawaban:


Berbicara tentang serialisasi, yang meningkatkan API serialisasi datang ke pikiran saya. Untuk mentransmisikan data serial melalui internet, saya akan menggunakan soket Berkeley atau perpustakaan asio.

Edit:
Jika Anda ingin membuat serial objek Anda ke array byte, Anda dapat menggunakan boost serializer dengan cara berikut (diambil dari situs tutorial):

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

Serialisasi yang sebenarnya kemudian cukup mudah:

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

Deserialisasi bekerja secara analog.

Ada juga mekanisme yang memungkinkan Anda menangani serialisasi pointer (struktur data yang kompleks seperti ikal dll tidak ada masalah), kelas turunan dan Anda dapat memilih antara serialisasi biner dan teks. Selain itu semua kontainer STL didukung di luar kotak.


50
2018-02-07 14:57



Dalam beberapa kasus, ketika berhadapan dengan tipe sederhana, Anda dapat melakukan:

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

Tidak apa-apa sebagai bukti konsep atau konsep pertama, sehingga anggota tim Anda yang lain dapat terus mengerjakan bagian lain.

Tapi cepat atau lambat, biasanya lebih cepat, ini akan membuatmu terluka!

Anda mengalami masalah dengan:

  • Tabel pointer virtual akan rusak.
  • Pointer (ke data / anggota / fungsi) akan rusak.
  • Perbedaan padding / alignment pada mesin yang berbeda.
  • Masalah pemesanan byte Big / Little-Endian.
  • Variasi dalam penerapan float / double.

(Plus Anda perlu tahu apa yang sedang Anda buka di sisi penerima.)

Anda dapat meningkatkan ini dengan mengembangkan metode marshalling / unmarshalling Anda sendiri untuk setiap kelas. (Idealnya virtual, sehingga mereka dapat diperpanjang dalam subclass.) Beberapa macro sederhana akan memungkinkan Anda untuk menulis tipe dasar yang berbeda dengan cukup cepat dalam urutan besar / sedikit-endian-netral.

Tapi pekerjaan kasar semacam itu jauh lebih baik, dan lebih mudah ditangani melalui meningkatkan perpustakaan serialisasi.


13
2018-02-07 15:17



Serialisasi berarti mengubah objek Anda menjadi data biner. Sedangkan deserialization berarti membuat ulang objek dari data.

Ketika serialisasi Anda mendorong byte menjadi uint8_t vektor. Ketika unserializing Anda sedang membaca byte dari uint8_t vektor.

Tentu saja ada pola yang bisa Anda gunakan saat membuat serial.

Setiap kelas serializable harus memiliki serialize(std::vector<uint8_t> &binaryData) atau fungsi signatured serupa yang akan menulis representasi biner ke dalam vektor yang disediakan. Maka fungsi ini dapat meneruskan vektor ini ke fungsi serialisasi anggota sehingga mereka dapat menulis barang-barang mereka ke dalamnya juga.

Karena representasi data dapat berbeda pada arsitektur yang berbeda. Anda perlu mencari tahu bagaimana cara merepresentasikan data.

Mari kita mulai dari dasar:

Serialisasi data bilangan bulat

Tulis saja byte dalam urutan little endian. Atau gunakan representasi varint jika ukurannya penting.

Serialisasi dalam urutan little endian:

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

Deserialization dari little endian order:

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

Serialisasi data titik apung

Sejauh yang saya tahu IEEE 754 memiliki monopoli di sini. Saya tidak tahu ada arsitektur utama yang akan menggunakan sesuatu yang lain untuk mengapung. Satu-satunya hal yang bisa berbeda adalah urutan byte. Beberapa arsitektur menggunakan sedikit endian, yang lain menggunakan perintah byte endian besar. Ini berarti Anda harus berhati-hati agar Anda mengeraskan bita pada ujung penerima. Perbedaan lain dapat menangani nilai denormal dan infinity dan NAN. Tetapi selama Anda menghindari nilai-nilai ini, Anda seharusnya baik-baik saja.

Serialisasi:

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

Deserialization melakukannya ke belakang. Pikiran urutan byte arsitektur Anda!

Serialisasi string

Pertama Anda harus menyetujui pengkodean. UTF-8 adalah hal biasa. Kemudian simpan sebagai cara awalan panjang: pertama Anda menyimpan panjang string menggunakan metode yang saya sebutkan di atas, kemudian tulis string byte-by-byte.

Serialisasi array.

Mereka sama dengan string. Pertama-tama Anda membuat serial integer yang mewakili ukuran larik, lalu serialize setiap objek di dalamnya.

Serialisasi seluruh objek

Seperti yang saya katakan sebelumnya mereka harus memiliki serialize metode yang menambahkan konten ke vektor. Untuk unserialize suatu objek, ia harus memiliki konstruktor yang mengambil aliran byte. Itu bisa menjadi sebuah istream tetapi dalam kasus yang paling sederhana itu hanya bisa menjadi referensi uint8_t penunjuk. Konstruktor membaca byte yang diinginkan dari aliran dan mengatur bidang di objek. Jika sistem dirancang dengan baik dan membuat serial bidang dalam urutan bidang objek, Anda bisa meneruskan streaming ke konstruktor bidang dalam daftar penginisialisasi dan minta mereka deserialized dalam urutan yang benar.

Serial objek grafik

Pertama, Anda perlu memastikan apakah objek-objek ini benar-benar sesuatu yang Anda inginkan untuk bersambung. Anda tidak perlu membuat serialisasi jika ada benda-benda yang ada di tempat tujuan.

Sekarang Anda tahu bahwa Anda perlu membuat serial objek yang ditunjuk oleh pointer. Masalah pointer bahwa mereka hanya berlaku dalam program yang menggunakan mereka. Anda tidak dapat membuat serial pointer, Anda harus berhenti menggunakannya dalam objek. Sebaliknya, buat kumpulan objek. Kolam objek ini pada dasarnya adalah array dinamis yang berisi "kotak". Kotak-kotak ini memiliki jumlah referensi. Jumlah referensi non-nol menunjukkan objek langsung, nol menunjukkan slot kosong. Kemudian Anda membuat pointer pintar mirip dengan shared_ptr yang tidak menyimpan pointer ke objek, tetapi indeks dalam array. Anda juga harus menyetujui indeks yang menunjukkan pointer null, misalnya. -1.

Pada dasarnya apa yang kami lakukan di sini adalah mengganti pointer dengan indeks array. Sekarang ketika serialisasi Anda dapat membuat serialisasi indeks array ini seperti biasa. Anda tidak perlu khawatir tentang di mana objek akan berada dalam memori pada sistem tujuan. Pastikan saja mereka memiliki kolam objek yang sama juga.

Jadi kita perlu membuat serial objek objek. Tapi yang mana? Baik ketika Anda membuat serialisasi grafik objek, Anda tidak membuat serialisasi hanya objek, Anda membuat serialisasi seluruh sistem. Ini berarti serialisasi sistem seharusnya tidak dimulai dari bagian-bagian sistem. Benda-benda itu seharusnya tidak mengkhawatirkan sisa sistem, mereka hanya perlu membuat serial indeks array dan hanya itu. Anda harus memiliki rutin serialisasi sistem yang mengatur serialisasi sistem dan berjalan melalui kumpulan objek yang relevan dan membuat serial semua dari mereka.

Pada bagian penerima, semua array dan objek dalam deserialized, menciptakan grafik objek yang diinginkan.

Serial fungsi pointer

Jangan simpan pointer di objek. Memiliki array statis yang berisi pointer ke fungsi-fungsi ini dan menyimpan indeks dalam objek.

Karena kedua program memiliki tabel ini yang dikompilasi ke dalamnya, gunakan hanya indeks yang seharusnya berfungsi.

Serialisasi jenis polimorfik

Karena saya katakan Anda harus menghindari pointer dalam jenis serializable dan Anda harus menggunakan indeks array sebagai gantinya, polimorfisme tidak bisa bekerja, karena memerlukan pointer.

Anda harus menyelesaikan ini dengan tag dan serikat jenis.

Versi

Di atas semua hal di atas. Anda mungkin ingin berbagai versi perangkat lunak saling beroperasi.

Dalam hal ini setiap objek harus menulis nomor versi pada awal serialisasi mereka untuk menunjukkan versi.

Saat memuat objek di sisi lain, objek yang lebih baru mungkin dapat menangani representasi yang lebih lama tetapi yang lebih tua tidak dapat menangani yang lebih baru sehingga mereka harus membuang pengecualian tentang hal ini.

Setiap kali sesuatu berubah, Anda harus menabrak nomor versi.


Jadi untuk membungkus ini, serialisasi bisa rumit. Tapi untungnya Anda tidak perlu membuat serialisasi segala sesuatu dalam program Anda, paling sering hanya pesan protokol yang diserialkan, yang sering kali merupakan struct lama. Jadi Anda tidak perlu trik rumit yang saya sebutkan di atas terlalu sering.


0
2018-05-06 23:10