C ++ 'da bir sınıfın serileştirilmesi ve serileştirilmesi mümkün müdür?


138

C ++ 'da bir sınıfın serileştirilmesi ve serileştirilmesi mümkün müdür?

Java'yı 3 yıldır kullanıyorum ve serileştirme / serileştirme bu dilde oldukça önemsiz. C ++ benzer özelliklere sahip mi? Serileştirmeyi işleyen yerel kütüphaneler var mı?

Bir örnek yardımcı olabilir.


2
"native" ile ne demek istediğinizden emin değil misiniz, native C ++ (Boost.Serialization gibi) demek istiyor musunuz? Sadece C ++ Standart Kütüphanesini mi kullanıyorsunuz? Başka bir şey mi demek istiyorsun?
jwfearn

1
"harici bir yazılım kütüphanesi değil" demek istiyorum. Ve üzgünüm ingilizcem çok iyi değil: S. Ben Arjantinliyim
Agusti-N

3
Bir nesneyi serileştirmenin yerel bir yolu yoktur (yine de ikili verileri bir POD'dan dökebilirsiniz, ancak istediğinizi elde edemezsiniz). Yine de, Boost, bir "dahili kütüphane" olmasa da, derleyicinize eklemeyi düşünmeniz gereken ilk harici kütüphanedir. Boost, STL kalitesinde (yani Top Gun C ++)
paercebal

Yanıtlar:


95

Boost::serializationKütüphane oldukça zarif bu işler. Birkaç projede kullandım. Burada nasıl kullanılacağını gösteren bir örnek program var .

Bunu yapmanın tek yerel yolu akışları kullanmaktır. Temelde Boost::serializationkütüphanenin yaptığı budur, nesneleri metin benzeri bir biçime yazmak ve bunları aynı formattan okumak için bir çerçeve oluşturarak akış yöntemini genişletir.

Yerleşik türler veya kendi türlerinizle operator<<ve operator>>düzgün bir şekilde tanımlanmış için bu oldukça basittir; daha fazla bilgi için C ++ SSS bölümüne bakın .


Bana öyle geliyor ki boost :: serialization arayan kişinin nesnelerin yazıldığı ve okunduğu sırayı izlemesini gerektirir. Bu doğru mu? Dolayısıyla, bir programın sürümleri arasında iki alanın yazıldığı sırada bir değişiklik olursa, uyumsuzluğumuz vardır. Bu doğru mu?
Agnel Kurian

1
Bunun nedeni, Boost :: serileştirme kodunun kendisinin değil, serileştirme işlevlerinden kaynaklanıyordur.
Kafa Geek

1
@ 0xDEADBEEF: Muhtemelen bir ikili dosya (i | o) arşivi kullanıldığında olur ve endian-ness gibi diğer "problemleri" ortaya koyar. Metin_ (i | o) arşivini deneyin, daha platform agnostik.
Ela782

2
Belirli bir çerçeve / kütüphane çözümü kabul edilen cevap olmamalıdır.
Andrea

3
@Andrea: Boost kütüphanesi özel bir durumdur. C ++ 11 sonlandırılıncaya kadar, modern C ++ kodunu onsuz yazmak imkansızdı, bu yüzden ikincil bir STL'ye ayrı bir kütüphaneden daha yakındı.
Baş Geek

52

Bunun eski bir gönderi olduğunu anlıyorum, ancak arama yaparken ortaya çıkan ilklerden biri c++ serialization.

C ++ 11'e erişimi olan herkesi tahıl , bir C ++ 11 üstbilgisi sadece ikili, JSON ve XML kutularını destekleyen serileştirme için bir kütüphaneye bakmaya teşvik ediyorum . tahıl genişletilmesi ve kullanımı kolay olacak şekilde tasarlanmıştır ve Boost ile benzer bir sözdizimine sahiptir.


4
Tahıl ile ilgili iyi olan şey, takviyenin aksine, minimum meta verilere sahip olmasıdır (neredeyse hiçbiri). boost :: serialization her arşivi açtığınızda gerçekten can sıkıcı hale gelir, lib versiyonunu akışa yazar, bu da bir dosyaya eklenmeyi imkansız hale getirir.
CyberSnoopy

@CyberSnoopy - bir arşiv oluşturulduğunda bu özelliği bastırmak için bir bayrak vardır - elbette arşivi okurken de hatırlamanız gerekir.
Robert Ramey

16

Boost iyi bir öneri. Ama eğer kendi başınızı döndürmek istiyorsanız, o kadar zor değil.

Temel olarak, nesnelerin bir grafiğini oluşturmak ve sonra bunları bazı yapılandırılmış depolama biçimine (JSON, XML, YAML, her neyse) çıkarmak için bir yola ihtiyacınız vardır. Grafiği oluşturmak, bir işaretleme özyinelemeli düzgün nesne algoritması kullanmak ve daha sonra tüm işaretli nesneleri çıkarmak kadar basittir.

İlkel (ama yine de güçlü) bir serileştirme sistemini açıklayan bir makale yazdım. İlginç bulabilirsiniz: SQLite'ı Disk Üzerinde Dosya Biçimi olarak kullanma, Bölüm 2 .


14

Bildiğim kadarıyla "yerleşik" kütüphaneler gidin <<ve >>serileştirme için özel olarak ayrılmıştır.

<<Nesnenizi bazı serileştirme bağlamına (genellikle an iostream) >>çıkarmak ve bu bağlamdan veri okumak için geçersiz kılmalısınız . Her nesne toplanmış alt nesnelerini çıktılamaktan sorumludur.

Nesne grafiğiniz döngü içermediği sürece bu yöntem iyi çalışır.

Varsa, bu döngülerle başa çıkmak için bir kütüphane kullanmanız gerekecektir.


3
Elbette, bu doğru olamaz ... uygulanan <<operatörler, nesnelerin insan tarafından okunabilir metin temsillerini yazdırmak için kullanılır, bu da serileştirme için istediğiniz şey değildir.
einpoklum

1
@einpoklum <<Genel için tanımlamak yerine, ostreamonu bir dosya akışı için tanımlamayı deneyin.
Carcigenicate

1
@Carcigenicate: İnsan tarafından okunabilir metin alan bir günlük dosyası bir dosya akışıdır.
einpoklum

1
@einpoklum Ne demek istediğinden emin değilim. Frank doğru olsa da, bu operatörler serileştirmek için kullanılabilir. Sadece bir vektörü serileştirmek / serisini kaldırmak için tanımladım.
Carcigenicate

2
Bence yakalama burada <<Nesnenizi bazı serileştirme bağlamına çıkarmak için geçersiz kılmalısınız… Her nesne kendi çıktısını almaktan sorumludur…” - soru, bunu her nesne için zahmetli bir şekilde yazmaktan kaçınmakla ilgilidir: dil veya kütüphaneler yardım?
ShreevatsaR

14

Google protokol tamponlarını öneriyorum . Kütüphaneyi yeni bir projede test etme şansım oldu ve kullanımı oldukça kolay. Kütüphane performans için büyük ölçüde optimize edilmiştir.

Protobuf, nesnelerinizi serileştirmemesi yerine spesifikasyonunuza göre serileştirme yapan nesneler için kod üretmesi açısından burada belirtilen diğer serileştirme çözümlerinden farklıdır.


2
Bunu kullanarak yaklaşık 10-50 MB boyutunda nesneleri serileştirme deneyiminiz oldu mu? Belgeler, protokol arabelleklerinin MB boyutundaki nesneler için en uygun olduğunu söylüyor.
Agnel Kurian



4

Amef protokolünü kontrol edebilirsiniz, amef'deki C ++ kodlama örneği şöyle olur,

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

Java'da kod çözme,

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

Protokol uygulaması hem C ++ hem de Java için kodeklere sahiptir, ilginç kısmı, nesne değeri gösterimini ad değeri çiftleri biçiminde tutabilmesidir, bu projeye rastlantısal olarak tökezlediğimde, son projemde benzer bir protokole ihtiyacım vardı, aslında temel kütüphaneyi gereksinimlerime göre değiştirdi. Umarım bu size yardımcı olur.




2

Serileştirme için temel olarak kullanılan Soyut fabrikalara bakmanızı öneririm

C ++ fabrikaları hakkında başka bir SO sorusuna cevap verdim. Bakınız orada esnek fabrika ilgi ise. Benim için harika çalıştı makroları kullanmak için ET ++ eski bir yol tarif etmeye çalışın.

ET ++ , eski MacApp'ı C ++ ve X11'e taşıyan bir projeydi. Bunun için Eric Gamma vs Tasarım Desenleri üzerine düşünmeye başladı . ET ++ çalışma zamanında serileştirme ve içgözlem için otomatik yollar içeriyordu.


0

Basit istiyorum ve en iyi performans ve geriye doğru veri uyumluluğu hakkında önemli değilse, denemek HPS çok daha hızlı, daha vb Boost, vb Protobuf, daha kullanımı çok daha kolaydır hafiftir,

Misal:

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

İşte devirdiğim basit bir serileştirici kütüphanesi. Sadece başlık, c11 ve temel türleri serileştirmek için örnekleri var. İşte bir sınıf haritası için.

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

Çıktı:

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

Serileştirmeyi uygulamak için aşağıdaki şablonu kullanıyorum:

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

İşte Tsize serialize istediğiniz tip olduğu Modegibi serileştirme farklı türde birbirinden ayırt edebilmek için kukla türüdür. aynı tamsayı küçük endian, büyük endian, varint vb.

Varsayılan olarak Serializer, görevi serileştirilen nesneye delege eder. Yerleşik türler için bir şablon uzmanlığı yapmalısınız Serializer.

Kolaylık fonksiyonu şablonları da sunulmaktadır.

Örneğin, işaretsiz tam sayıların küçük endian serileştirmesi:

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

Sonra serileştirmek için:

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

Serisini kaldırmak için:

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

Soyut yineleyici mantığı nedeniyle, herhangi bir yineleyici (örn. Akış yineleyicileri), işaretçi vb.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.