มันเป็นไปได้ที่จะทำให้เป็นอันดับและ deserialize ชั้นใน C + +?


138

มันเป็นไปได้ที่จะทำให้เป็นอันดับและ deserialize ชั้นใน C + +?

ฉันใช้ Java มา 3 ปีแล้วและการซีเรียลไลซ์เซชั่น / ดีซีเรียลไลเซชันเป็นเรื่องเล็กน้อยในภาษานั้น C ++ มีคุณสมบัติที่คล้ายกันหรือไม่ มีไลบรารีดั้งเดิมที่จัดการการทำให้เป็นอันดับหรือไม่

ตัวอย่างจะเป็นประโยชน์


2
ไม่แน่ใจว่าคุณหมายถึงอะไรโดย "native" คุณหมายถึง native C ++ (เช่น Boost.Serialization) หรือไม่ คุณหมายถึงใช้เฉพาะไลบรารีมาตรฐาน C ++ หรือไม่ คุณหมายถึงอะไรอย่างอื่นหรือ
jwfearn

1
ฉันหมายถึง "ไม่ใช่ห้องสมุดซอฟต์แวร์ภายนอก" และขออภัยภาษาอังกฤษของฉันไม่ค่อยดี: S ฉันมาจากอาร์เจนตินา
Agusti-N

3
ไม่มีวิธีเนทิฟในการทำให้วัตถุเป็นอนุกรม (คุณยังสามารถถ่ายโอนข้อมูลไบนารีจาก POD ได้ แต่คุณจะไม่ได้สิ่งที่ต้องการ) ถึงกระนั้น Boost ในขณะที่ไม่ใช่ "ห้องสมุดภายใน" เป็นห้องสมุดภายนอกแห่งแรกที่คุณควรพิจารณาเพิ่มในคอมไพเลอร์ของคุณ การเพิ่มคุณภาพ STL (เช่น Top Gun C ++)
paercebal

คำตอบ:


95

Boost::serializationจับห้องสมุดนี้ค่อนข้างหรูหรา ฉันใช้มันในหลายโครงการ มีโปรแกรมตัวอย่างที่แสดงให้เห็นว่าจะใช้มันเป็นที่นี่

วิธีเดียวที่จะทำได้คือใช้ลำธาร นั่นคือทั้งหมดที่Boost::serializationห้องสมุดทำมันขยายวิธีการสตรีมด้วยการตั้งค่าเฟรมเวิร์กเพื่อเขียนออบเจ็กต์ในรูปแบบข้อความและอ่านจากรูปแบบเดียวกัน

สำหรับประเภทที่มีอยู่แล้วหรือประเภทของคุณเองที่มีoperator<<และoperator>>กำหนดไว้อย่างถูกต้องนั้นค่อนข้างง่าย ดูคำถามที่พบบ่อย C ++สำหรับข้อมูลเพิ่มเติม


สำหรับผมแล้วมันดูเหมือนว่า boost :: serialization ต้องการให้ผู้เรียกติดตามลำดับที่วัตถุถูกเขียนและอ่าน ถูกต้องหรือไม่ ดังนั้นหากมีการเปลี่ยนแปลงตามลำดับที่สองเขตข้อมูลถูกเขียนระหว่างเวอร์ชันของโปรแกรมดังนั้นเราจึงมีความไม่ลงรอยกัน ถูกต้องหรือไม่
Agnel Kurian

1
อาจเป็นเพราะฟังก์ชั่นการทำให้เป็นอันดับไม่ใช่รหัส Boost :: serialization เอง
Head Geek

1
@ 0xDEADBEEF: อาจเกิดขึ้นได้เมื่อใช้ binary_ (i | o) ไฟล์เก็บถาวรซึ่งแนะนำ "ปัญหา" อื่น ๆ เช่น endian-ness ลองเก็บข้อความ _ (i | o) มันเป็นแพลตฟอร์มที่ไม่เชื่อเรื่องพระเจ้ามากขึ้น
Ela782

2
โซลูชันเฟรมเวิร์ก / ไลบรารี specifc ไม่ควรเป็นคำตอบที่ยอมรับได้
Andrea

3
@Andrea: ไลบรารี Boost เป็นกรณีพิเศษ จนกว่า C ++ 11 จะได้รับการสรุปมันเป็นไปไม่ได้เลยที่จะเขียนรหัส C ++ ที่ทันสมัยโดยที่ไม่มีรหัสนี้ดังนั้นจึงใกล้เคียงกับ STL รองมากกว่าห้องสมุดแยกต่างหาก
Head Geek

52

ผมทราบดีว่านี่เป็นโพสต์เก่า c++ serializationแต่มันก็เป็นหนึ่งในคนแรกที่เกิดขึ้นเมื่อค้นหา

ฉันขอแนะนำให้ทุกคนที่สามารถเข้าถึง C ++ 11 เพื่อดู ซีเรียลซึ่งเป็นส่วนหัวของไลบรารี C ++ 11 เท่านั้นสำหรับการซีเรียลไลซ์เซชั่นที่รองรับไบนารี, JSON และ XML นอกกรอบ ธัญญาหารถูกออกแบบมาให้ง่ายต่อการขยายและใช้งานและมีไวยากรณ์คล้ายกับ Boost


4
สิ่งที่ดีเกี่ยวกับซีเรียลนั้นต่างจากการเพิ่มมันมีข้อมูลเมตาน้อยที่สุด (เกือบจะไม่มีเลย) boost :: การทำให้เป็นอนุกรมนั้นน่ารำคาญจริงๆเมื่อทุกครั้งที่คุณเปิดไฟล์เก็บถาวรมันจะเขียนเวอร์ชัน lib ลงในสตรีมซึ่งจะทำให้การผนวกไฟล์เป็นไปไม่ได้
CyberSnoopy

@CyberSnoopy - มีธงสำหรับระงับคุณลักษณะนี้เมื่อสร้างไฟล์เก็บถาวร - แน่นอนว่าคุณต้องจำไว้เมื่ออ่านไฟล์เก็บถาวรเช่นกัน
Robert Ramey

16

Boost เป็นคำแนะนำที่ดี แต่ถ้าคุณต้องการที่จะม้วนของคุณเองก็ไม่ยาก

โดยทั่วไปคุณเพียงแค่ต้องการสร้างกราฟของวัตถุแล้วส่งออกไปยังรูปแบบการจัดเก็บที่มีโครงสร้างบางอย่าง (JSON, XML, YAML, อะไรก็ตาม) การสร้างกราฟนั้นง่ายเหมือนการใช้การทำเครื่องหมายอัลกอริธึมอ็อบเจกต์ที่เหมาะสมแบบเรียกซ้ำแล้วส่งออกวัตถุที่ทำเครื่องหมายทั้งหมด

ฉันเขียนบทความที่อธิบายถึงระบบการทำให้เป็นอันดับพื้นฐาน (แต่ยังคงมีประสิทธิภาพ) คุณอาจพบว่ามันน่าสนใจ: การใช้ SQLite เป็นบนดิสก์รูปแบบไฟล์, ส่วนที่ 2


14

เท่าที่ห้องสมุด "ในตัว" ไปแล้ว<<และ>>ได้รับการสงวนไว้เป็นพิเศษสำหรับการทำให้เป็นอนุกรม

คุณควรแทนที่<<เพื่อส่งออกวัตถุของคุณไปยังบริบทการทำให้เป็นอันดับ (มักจะเป็นiostream) และ>>การอ่านข้อมูลกลับจากบริบทนั้น แต่ละวัตถุมีหน้าที่ในการแสดงผลวัตถุลูกรวมของมัน

วิธีนี้ใช้ได้ดีตราบใดที่กราฟวัตถุของคุณไม่มีรอบ

ถ้าเป็นเช่นนั้นคุณจะต้องใช้ห้องสมุดเพื่อจัดการกับวงจรเหล่านั้น


3
แน่นอนว่ามันไม่ถูกต้อง ... ตัวดำเนิน<<การที่นำมาใช้นั้นถูกใช้เพื่อพิมพ์การแทนข้อความที่มนุษย์สามารถอ่านได้ของวัตถุซึ่งมักจะไม่ใช่สิ่งที่คุณต้องการสำหรับการทำให้เป็นอนุกรม
einpoklum

1
@einpoklum แทนที่จะกำหนด<<ให้เป็นแบบทั่วไปostreamให้ลองกำหนดให้เป็นกระแสไฟล์
Carcigenicate

1
@Carcigenicate: ไฟล์บันทึกที่ใช้ข้อความที่มนุษย์อ่านได้คือไฟล์สตรีม
einpoklum

1
@einpoklum ฉันไม่แน่ใจว่าคุณหมายถึงอะไร แฟรงก์ถูกต้องแล้วโอเปอเรเตอร์เหล่านั้นสามารถใช้เพื่อทำให้เป็นอันดับ ฉันเพิ่งนิยามพวกมันให้เป็นอนุกรม / ดีซีเรียลไลซ์เวกเตอร์
Carcigenicate

2
ฉันคิดว่าการจับอยู่ที่นี่“ คุณควรแทนที่<<การส่งออกวัตถุของคุณไปยังบริบทบางอย่างอนุกรม ... แต่ละวัตถุมีหน้าที่ในการแสดงผลของมัน…” - คำถามคือเกี่ยวกับวิธีการหลีกเลี่ยงการเขียนที่ยากสำหรับแต่ละวัตถุ: ภาษาหรือห้องสมุดช่วยอะไร
ShreevatsaR

14

ฉันแนะนำบัฟเฟอร์โปรโตคอล Googleบัฟเฟอร์โปรโตคอลฉันมีโอกาสทดสอบไดรฟ์ไลบรารีในโครงการใหม่และใช้งานง่ายอย่างน่าทึ่ง ห้องสมุดได้รับการปรับแต่งเพื่อประสิทธิภาพอย่างมาก

Protobuf นั้นแตกต่างจากโซลูชันการทำให้เป็นอนุกรมอื่น ๆ ที่กล่าวถึงที่นี่ในแง่ที่ว่ามันไม่ได้เป็นวัตถุของคุณ แต่สร้างรหัสสำหรับวัตถุที่เป็นอนุกรมตามข้อกำหนดของคุณ


2
คุณเคยมีประสบการณ์การทำให้เป็นอันดับวัตถุประมาณ 10-50MB ในการใช้สิ่งนี้หรือไม่? เอกสารดูเหมือนว่าบัฟเฟอร์โปรโตคอลเหมาะที่สุดสำหรับวัตถุที่มีขนาดประมาณ MB
Agnel Kurian

ฉันรีด lib ของฉันเองไม่ได้ใช้สตรีม (ยัง) ดังนั้นมันจึงเป็นเรื่องเล็ก ๆ น้อย ๆ : gist.github.com/earonesty/5ba3a93f391ea03ef90884840f068767
Erik Aronesty


4

คุณสามารถตรวจสอบโปรโตคอลamefตัวอย่างของการเข้ารหัส C ++ ใน amef จะเป็นเช่นนั้น

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

การถอดรหัสในจาวาจะเป็นเช่นนั้น

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

การใช้โพรโทคอลมีตัวแปลงสัญญาณสำหรับทั้ง C ++ และ Java ส่วนที่น่าสนใจคือมันสามารถรักษาการแทนคลาสอ็อบเจ็กต์ในรูปแบบของชื่อค่าคู่ฉันต้องการโปรโตคอลที่คล้ายกันในโครงการสุดท้ายของฉันเมื่อฉันบังเอิญโปรโตคอลนี้ฉันจริง ๆ แก้ไขไลบรารีฐานตามความต้องการของฉัน หวังว่านี่จะช่วยคุณได้


3

ฉันขอแนะนำให้ใช้การเพิ่มการจัดลำดับตามที่อธิบายโดยผู้โพสต์อื่น ๆ นี่คือบทเรียนรายละเอียดที่ดีเกี่ยวกับวิธีการใช้งานซึ่งเสริมการสอนการเพิ่มทาง: http://www.ocoudert.com/blog/2011/07/09/a-practical-guide-to-c-serialization/


3

ยังคงหวานเป็นอีกสิ่งหนึ่ง

เป็นไปได้ที่จะทำให้เป็นอันดับจากและในสตรีมในรูปแบบ XML, JSON, Lua และไบนารี


ดูเหมือนว่าไซต์นั้นจะไม่สามารถใช้งานได้ทั้งหมดคือ repo เก่า: github.com/cwbaker/sweet_persist
Janusz Syf

2

ฉันขอแนะนำให้ดูโรงงานบทคัดย่อซึ่งมักใช้เป็นพื้นฐานสำหรับการทำให้เป็นอนุกรม

ฉันได้ตอบคำถาม SO อื่นเกี่ยวกับโรงงาน C ++ โปรดดูที่นั่นหากโรงงานที่ยืดหยุ่นนั้นเป็นที่สนใจ ฉันพยายามอธิบายวิธีเก่า ๆ จาก ET ++ เพื่อใช้มาโครที่ใช้งานได้ดีสำหรับฉัน

ET ++เป็นโครงการที่จะย้ายพอร์ต MacApp เก่าไปยัง C ++ และ X11 ในความพยายามของมันเอริคแกมมา ฯลฯ เริ่มที่จะคิดเกี่ยวกับรูปแบบการออกแบบ ET ++ มีวิธีอัตโนมัติสำหรับการทำให้เป็นอนุกรมและวิปัสสนา ณ รันไทม์


0

หากคุณต้องการประสิทธิภาพที่ง่ายและดีที่สุดและไม่สนใจความเข้ากันได้ของข้อมูลย้อนหลังลองใช้HPSมันมีน้ำหนักเบาเร็วกว่า Boost ฯลฯ มากและใช้งานง่ายกว่า Protobuf เป็นต้น

ตัวอย่าง:

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

นี่คือไลบรารี่อนุกรมที่เรียบง่ายที่ฉันทำ มันเป็นเพียงส่วนหัว, c11 และมีตัวอย่างสำหรับการทำให้เป็นประเภทพื้นฐาน นี่คือแผนที่สำหรับชั้นหนึ่ง

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

เอาท์พุท:

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

ฉันใช้แม่แบบต่อไปนี้เพื่อใช้การทำให้เป็นอันดับ:

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

นี่Tคือประเภทที่คุณต้องการทำให้Modeเป็นอนุกรมเป็นประเภทหุ่นที่จะแยกแยะความแตกต่างระหว่างชนิดของการทำให้เป็นอนุกรมเช่น จำนวนเต็มเดียวกันสามารถต่อเนื่องเป็น endian น้อย, endian ใหญ่, varint ฯลฯ

โดยค่าเริ่มต้นSerializerผู้รับมอบสิทธิ์งานไปยังวัตถุที่เป็นอนุกรม สำหรับประเภทบิวด์อินคุณควรสร้างเทมเพลตเฉพาะSerializerสำหรับ

มีฟังก์ชั่นแม่แบบอำนวยความสะดวก

ตัวอย่างเช่นการเรียงลำดับ endian เล็กน้อยของจำนวนเต็มที่ไม่ได้ลงนาม:

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

จากนั้นให้เป็นอันดับ:

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

ในการยกเลิกการย่อ:

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

เนื่องจากตรรกะตัววนซ้ำนามธรรมมันควรทำงานกับตัววนซ้ำใด ๆ (เช่นสตรีมตัววนซ้ำ) ตัวชี้ ฯลฯ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.