आप C ++ में किसी ऑब्जेक्ट को कैसे अनुक्रमित करते हैं?


85

मेरे पास वस्तुओं का एक छोटा पदानुक्रम है जिसे मुझे सॉकेट कनेक्शन के माध्यम से क्रमबद्ध और प्रसारित करने की आवश्यकता है। मुझे ऑब्जेक्ट को क्रमबद्ध करने की आवश्यकता है, फिर इसे किस प्रकार का है, इसके आधार पर डिसेरिएलाइज़ करें। क्या सी ++ में ऐसा करने का एक आसान तरीका है (जैसा कि जावा में है)?

क्या कोई C ++ सीरियललाइज़ेशन ऑनलाइन कोड नमूने या ट्यूटोरियल हैं?

संपादित करें: बस स्पष्ट होने के लिए, मैं एक वस्तु को बाइट्स के एक सरणी में परिवर्तित करने के तरीकों की तलाश कर रहा हूं, फिर एक वस्तु में वापस। मैं सॉकेट ट्रांसमिशन को संभाल सकता हूं।


3
की जाँच करें गूगल :: Protobuf , यह बहुत मजबूत और तेजी से बाइनरी क्रमांकन के लिए पुस्तकालय है। हमने इसे बढ़ावा देने के साथ सफलतापूर्वक उपयोग किया है :: asio आदि
केतन

[STLPLUS] [1] पर एक नज़र डालें, जो दृढ़ता के साथ लागू होता है। [१]: stlplus.sourceforge.net
lsalamon

4
प्रदान किए गए उत्तर वास्तव में व्याख्या नहीं करते हैं कि कैसे क्रमबद्ध किया जाए। एक बढ़ावा देने के क्रम पुस्तकालय प्रदान करता है, दूसरे बताते हैं कि एक भोली कार्यान्वयन में gotchas। चूँकि यह c ++ - faq प्रश्न है, क्या कोई वास्तव में इसका उत्तर दे सकता है?
अनाम

जवाबों:


55

सीरियलाइजेशन की बात करें तो बूस्ट सीरियलाइजेशन एपीआई मेरे दिमाग में आता है। नेट पर अनुक्रमित डेटा संचारित करने के लिए, मैं या तो बर्कले सॉकेट या एसियो लाइब्रेरी का उपयोग करूंगा ।

संपादित करें:
यदि आप अपनी वस्तुओं को एक बाइट सरणी में क्रमबद्ध करना चाहते हैं, तो आप निम्न तरीके से बूस्ट धारावाहिक का उपयोग कर सकते हैं (ट्यूटोरियल साइट से लिया गया):

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

वास्तविक क्रमांकन तब बहुत आसान है:

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

देशव्यापीकरण एक अनुरूप तरीके से काम करता है।

ऐसे तंत्र भी हैं जो आपको पॉइंटर्स के क्रमांकन (जटिल डेटा संरचनाएं जैसे कि ट्रेस आदि कोई समस्या नहीं हैं), व्युत्पन्न कक्षाओं को संभालते हैं और आप द्विआधारी और पाठ क्रमांकन के बीच चयन कर सकते हैं। इसके अलावा सभी एसटीएल कंटेनर बॉक्स से बाहर समर्थित हैं।


यह एक c ++ सवाल है, कैसे gps_position class ओवरलोडिंग है << संचालक। कोई भी फ्रेंड फंक्शन परिभाषित नहीं है
विसेंटा बोलिया

"फ्रेंड क्लास बूस्ट :: सीरियलाइजेशन :: एक्सेस" पर ध्यान दें। यह वर्ग सदस्यों को क्रमबद्ध पुस्तकालय कार्यों की पहुँच प्रदान करता है, भले ही वे निजी हों।
रॉबर्ट रमी

13

कुछ मामलों में, सरल प्रकारों के साथ काम करते समय, आप कर सकते हैं:

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

यह एक सबूत या अवधारणा के रूप में ठीक है, इसलिए आपकी टीम के अन्य सदस्य अन्य भागों में काम कर सकते हैं।

लेकिन जल्दी या बाद में, आमतौर पर जल्द ही , यह आपको चोट पहुंचाएगा!

आप इसके साथ समस्याओं में भाग लेते हैं:

  • वर्चुअल पॉइंटर टेबल दूषित हो जाएंगे।
  • पॉइंटर्स (डेटा / सदस्यों / कार्यों के लिए) दूषित हो जाएगा।
  • विभिन्न मशीनों पर पैडिंग / संरेखण में अंतर।
  • बिग / लिटिल-एंडियन बाइट के आदेश देने के मुद्दे।
  • फ्लोट / डबल के कार्यान्वयन में विविधताएं।

(इसके अलावा आपको यह जानना होगा कि आप प्राप्त पक्ष में क्या खोल रहे हैं।)

आप हर वर्ग के लिए अपने स्वयं के मार्शलिंग / अनमर्सहॉलिंग तरीकों को विकसित करके इस पर सुधार कर सकते हैं। (आदर्श रूप से आभासी, इसलिए उन्हें उपवर्गों में बढ़ाया जा सकता है।) कुछ सरल मैक्रोज़ आपको बड़े / छोटे-एंडियन-न्यूट्रल ऑर्डर में विभिन्न बुनियादी प्रकारों को बहुत तेज़ी से लिखने देंगे।

लेकिन उस तरह के ग्रंट काम बहुत बेहतर है, और अधिक आसानी से, बूस्ट की क्रमबद्धता पुस्तकालय के माध्यम से नियंत्रित किया जाता है ।


मैं कुछ सोच रहा था। लेकिन जैसा कि मैं एक नेटवर्क स्ट्रीम को क्रमबद्ध करना चाहता हूं, यह बिल्कुल भी काम नहीं करता है। अंत में और विभिन्न प्लेटफार्मों की वजह से। लेकिन मुझे नहीं पता था कि यह आभासी संकेत को भ्रष्ट करता है। धन्यवाद =)
Atmocreations

4

एक सामान्य पैटर्न है जिसका उपयोग आप वस्तुओं को क्रमबद्ध करने के लिए कर सकते हैं। अंतिम संस्कार आदिम वे दो कार्य हैं जिन्हें आप पुनरावृत्तियों से पढ़ और लिख सकते हैं:

template <class OutputCharIterator>
void putByte(char byte, OutputCharIterator &&it)
{
    *it = byte;
    ++it;
}


template <class InputCharIterator>
char getByte(InputCharIterator &&it, InputCharIterator &&end)
{
    if (it == end)
    {
        throw std::runtime_error{"Unexpected end of stream."};
    }

    char byte = *it;
    ++it;
    return byte;
}

फिर क्रमबद्धता और डीरियलाइज़ेशन फ़ंक्शन पैटर्न का पालन करते हैं:

template <class OutputCharIterator>
void serialize(const YourType &obj, OutputCharIterator &&it)
{
    // Call putbyte or other serialize overloads.
}

template <class InputCharIterator>
void deserialize(YourType &obj, InputCharIterator &&it, InputCharIterator &&end)
{
    // Call getByte or other deserialize overloads.
}

कक्षाओं के लिए आप ADL का उपयोग करके अधिभार की अनुमति देने के लिए मित्र फ़ंक्शन पैटर्न का उपयोग कर सकते हैं:

class Foo
{
    int internal1, internal2;

    // So it can be found using ADL and it accesses private parts.
    template <class OutputCharIterator>
    friend void serialize(const Foo &obj, OutputCharIterator &&it)
    {
        // Call putByte or other serialize overloads.
    }

    // Deserialize similar.
};

अपने कार्यक्रम में आप इस तरह एक फ़ाइल में क्रमबद्ध और ऑब्जेक्ट कर सकते हैं:

std::ofstream file("savestate.bin");
serialize(yourObject, std::ostreambuf_iterator<char>(file));

तब पढ़ें:

std::ifstream file("savestate.bin");
deserialize(yourObject, std::istreamBuf_iterator<char>(file), std::istreamBuf_iterator<char>());

मेरा पुराना जवाब यहाँ:

सीरियलाइजेशन का अर्थ है अपनी वस्तु को बाइनरी डेटा में बदलना। जबकि डिसेरिएलाइजेशन का मतलब डेटा से किसी ऑब्जेक्ट को फिर से बनाना है।

जब क्रमिक आप एक uint8_tवेक्टर में बाइट्स धकेल रहे हैं । जब अशुभ करते हुए आप एक uint8_tवेक्टर से बाइट्स पढ़ रहे हैं ।

निश्चित रूप से ऐसे पैटर्न हैं जिन्हें आप सामान को क्रमबद्ध करते समय नियोजित कर सकते हैं।

प्रत्येक क्रमिक श्रेणी में एक serialize(std::vector<uint8_t> &binaryData)या समान हस्ताक्षरित फ़ंक्शन होना चाहिए जो प्रदान किए गए वेक्टर में अपने द्विआधारी प्रतिनिधित्व को लिखेंगे। तब यह फ़ंक्शन इस वेक्टर को सदस्य के क्रमबद्ध कार्यों के लिए नीचे पारित कर सकता है ताकि वे इसमें अपना सामान भी लिख सकें।

चूंकि विभिन्न आर्किटेक्चर पर डेटा प्रतिनिधित्व अलग-अलग हो सकता है। आपको एक योजना का पता लगाने की आवश्यकता है कि डेटा का प्रतिनिधित्व कैसे करें।

आइए मूल बातों से शुरू करें:

पूर्णांक डेटा को सीरियल करना

बस बाइट्स को थोड़ा एंडियन क्रम में लिखें। या अगर आकार मायने रखता है तो varint प्रतिनिधित्व का उपयोग करें।

छोटे एंडियन क्रम में क्रमांकन:

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

छोटे एंडियन ऑर्डर से देशीकरण:

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

फ्लोटिंग पॉइंट डेटा को सीरियल करना

जहाँ तक मुझे पता है कि IEEE 754 का यहाँ एकाधिकार है। मैं किसी भी मुख्यधारा के आर्किटेक्चर के बारे में नहीं जानता, जो फ़्लोट के लिए कुछ और उपयोग करेगा। केवल एक चीज जो अलग हो सकती है वह है बाइट ऑर्डर। कुछ आर्किटेक्चर छोटे एंडियन का उपयोग करते हैं, अन्य बड़े एंडियन बाइट ऑर्डर का उपयोग करते हैं। इसका मतलब है कि आपको सावधान रहने की आवश्यकता है जो आपको प्राप्त अंत में बाइट्स का आदेश देता है। एक और अंतर असामान्य और अनंत और एनएएन मूल्यों से निपटने में हो सकता है। लेकिन जब तक आप इन मूल्यों से बचते हैं तब तक आपको ठीक होना चाहिए।

क्रमबद्धता:

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

देशव्यापीकरण इसे पिछड़ा कर रहा है। अपनी वास्तुकला के बाइट क्रम का ध्यान रखें!

सीरियल को तार-तार करना

पहले आपको एन्कोडिंग पर सहमत होने की आवश्यकता है। UTF-8 आम है। फिर इसे एक लंबी उपसर्ग विधि के रूप में संग्रहीत करें: पहले आप एक विधि का उपयोग करके स्ट्रिंग की लंबाई को स्टोर करें, जो मैंने ऊपर उल्लेख किया है, फिर स्ट्रिंग बाइट-बाइट लिखें।

सीरियल को व्यवस्थित करना।

वे एक तार के समान हैं। आप पहले एक पूर्णांक को क्रमबद्ध करते हैं और फिर सरणी के आकार का प्रतिनिधित्व करते हैं और फिर उसमें प्रत्येक ऑब्जेक्ट को क्रमबद्ध करते हैं।

संपूर्ण वस्तुओं को क्रमबद्ध करना

जैसा कि मैंने पहले कहा कि उनके पास एक ऐसी serializeविधि होनी चाहिए जो एक वेक्टर में सामग्री जोड़ दे। किसी ऑब्जेक्ट को अनसुनी करने के लिए, इसमें एक कंस्ट्रक्टर होना चाहिए जो बाइट स्ट्रीम लेता है। यह एक istreamसरलतम मामले में एक संदर्भ uint8_tसूचक हो सकता है । कंस्ट्रक्टर बाइट्स को स्ट्रीम से पढ़ता है और ऑब्जेक्ट में फ़ील्ड सेट करता है। यदि सिस्टम को अच्छी तरह से डिज़ाइन किया गया है और ऑब्जेक्ट फ़ील्ड ऑर्डर में फ़ील्ड्स को क्रमबद्ध करता है, तो आप केवल इनिशियलाइज़र सूची में फ़ील्ड के कंस्ट्रक्टर्स को स्ट्रीम पास कर सकते हैं और उन्हें सही क्रम में deserialized कर सकते हैं।

वस्तु रेखांकन को क्रमबद्ध करना

पहले आपको यह सुनिश्चित करने की आवश्यकता है कि क्या ये ऑब्जेक्ट वास्तव में कुछ हैं जो आप क्रमबद्ध करना चाहते हैं। यदि गंतव्य पर मौजूद इन वस्तुओं के उदाहरण आपको उन्हें अनुक्रमित करने की आवश्यकता नहीं है।

अब आपको पता चला कि आपको एक पॉइंटर द्वारा बताई गई वस्तु को क्रमबद्ध करना होगा। पॉइंटर्स की समस्या है कि वे केवल उस प्रोग्राम में मान्य हैं जो उनका उपयोग करता है। आप सूचक को अनुक्रमित नहीं कर सकते, आपको उन्हें वस्तुओं में उपयोग करना बंद कर देना चाहिए। इसके बजाय ऑब्जेक्ट पूल बनाएं। यह ऑब्जेक्ट पूल मूल रूप से एक गतिशील सरणी है जिसमें "बक्से" होते हैं। इन बॉक्स में एक संदर्भ गणना होती है। गैर-शून्य संदर्भ गणना एक जीवित वस्तु को इंगित करती है, शून्य एक खाली स्लॉट को इंगित करता है। फिर आप शेयर्ड_पार्ट के लिए स्मार्ट पॉइंटर को बनाते हैं जो पॉइंटर को ऑब्जेक्ट में स्टोर नहीं करता है, लेकिन एरे में इंडेक्स। आपको एक इंडेक्स पर सहमत होने की भी आवश्यकता है जो शून्य पॉइंटर को दर्शाता है, जैसे। -1।

मूल रूप से हमने यहां क्या किया, पॉइंटर्स को एरे इंडेक्स के साथ बदल दिया। अब क्रमबद्ध करते समय आप इस सरणी सूचकांक को हमेशा की तरह क्रमबद्ध कर सकते हैं। आपको इस बात की चिंता करने की आवश्यकता नहीं है कि गंतव्य सिस्टम पर ऑब्जेक्ट मेमोरी में कहां होगा। बस सुनिश्चित करें कि उनके पास समान ऑब्जेक्ट पूल भी है।

इसलिए हमें ऑब्जेक्ट पूल को क्रमबद्ध करने की आवश्यकता है। लेकिन कौन से? जब आप एक वस्तु ग्राफ को क्रमबद्ध करते हैं तो आप केवल एक वस्तु को क्रमबद्ध नहीं कर रहे हैं, आप एक संपूर्ण प्रणाली को क्रमबद्ध कर रहे हैं। इसका मतलब है कि सिस्टम का क्रमांकन सिस्टम के कुछ हिस्सों से शुरू नहीं होना चाहिए। उन वस्तुओं को बाकी प्रणाली के बारे में चिंता नहीं करनी चाहिए, उन्हें केवल सरणी अनुक्रमित को क्रमबद्ध करने की आवश्यकता है और यही वह है। आपके पास एक सिस्टम क्रमिक रुटीन होना चाहिए जो सिस्टम के क्रमांकन को ऑर्केस्ट्रेट करता है और संबंधित ऑब्जेक्ट पूल के माध्यम से चलता है और उन सभी को क्रमबद्ध करता है।

प्राप्त होने वाले अंत में सभी ऑब्जेक्ट को वांछित वस्तु ग्राफ को फिर से बनाने के लिए, ऑब्जेक्ट को डीरियलाइज़ किया जाता है।

फ़ंक्शन पॉइंटर्स को सीरियल करना

ऑब्जेक्ट में पॉइंटर्स स्टोर न करें। एक स्टैटिक ऐरे है जिसमें इन फंक्शन्स के पॉइंटर्स हैं और इंडेक्स को ऑब्जेक्ट में स्टोर करते हैं।

चूंकि दोनों कार्यक्रमों में यह तालिका थीशेल्स में संकलित है, बस सूचकांक का उपयोग करना चाहिए।

बहुरूपी प्रकारों को सीरियलाइज़ करना

चूँकि मैंने कहा था कि आपको क्रमिक प्रकारों में पॉइंटर्स से बचना चाहिए और आपको इसके बजाय एरे इंडेक्स का उपयोग करना चाहिए, बहुरूपता सिर्फ काम नहीं कर सकती है, क्योंकि इसके लिए पॉइंटर्स की आवश्यकता होती है।

आपको इसे टाइप टैग और यूनियनों के साथ काम करने की आवश्यकता है।

संस्करण

उपरोक्त सभी के ऊपर। आप सॉफ्टवेयर के अलग-अलग संस्करण चाहते हैं।

इस मामले में प्रत्येक ऑब्जेक्ट को संस्करण को इंगित करने के लिए उनके क्रमांकन की शुरुआत में एक संस्करण संख्या लिखना चाहिए।

जब दूसरी तरफ वस्तु को लोड किया जाता है, तो नई वस्तुएं शायद पुराने अभ्यावेदन को संभालने में सक्षम होती हैं, लेकिन पुराने लोग नए को संभाल नहीं सकते हैं, इसलिए उन्हें इस बारे में एक अपवाद फेंक देना चाहिए।

हर बार कुछ परिवर्तन होने पर, आपको संस्करण संख्या को टक्कर देनी चाहिए।


तो इसे लपेटने के लिए, क्रमबद्धता जटिल हो सकती है। लेकिन सौभाग्य से आपको अपने कार्यक्रम में सब कुछ क्रमबद्ध करने की आवश्यकता नहीं है, सबसे अधिक बार केवल प्रोटोकॉल संदेश क्रमबद्ध होते हैं, जो अक्सर सादे पुराने ढांचे होते हैं। इसलिए आपको उन जटिल ट्रिक्स की ज़रूरत नहीं है जिनका मैंने ऊपर उल्लेख किया है।


1
धन्यवाद। इस उत्तर में C ++ में संरचित डेटा को क्रमबद्ध करने के लिए प्रासंगिक अवधारणाओं का एक बड़ा अवलोकन है।
शॉन

0

सीखने के तरीके से मैंने एक सरल सी ++ 11 सीरियलाइज़र लिखा। मैंने अन्य अधिक भारी वजन वाले विभिन्न प्रकारों की कोशिश की थी, लेकिन कुछ ऐसा चाहता था, जिसे मैं वास्तव में समझ सकता था जब यह गलत हो गया था या नवीनतम जी ++ के साथ संकलन करने में विफल रहा था (जो कि मेरे साथ अनाज के लिए हुआ था; वास्तव में एक अच्छा पुस्तकालय लेकिन जटिल और मैं नहीं कर सका। संकलक अपग्रेड पर फेंक दिए गए हैं।) वैसे भी, यह केवल हेडर है और POD प्रकार, कंटेनर, नक्शे आदि को संभालता है ... कोई संस्करण नहीं है और यह केवल उसी आर्क से फ़ाइलों को लोड करेगा जो इसमें सहेजा गया था।

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

उदाहरण का उपयोग:

#include "c_plus_plus_serializer.h"

static void serialize (std::ofstream out)
{
    char a = 42;
    unsigned short b = 65535;
    int c = 123456;
    float d = std::numeric_limits<float>::max();
    double e = std::numeric_limits<double>::max();
    std::string f("hello");

    out << bits(a) << bits(b) << bits(c) << bits(d);
    out << bits(e) << bits(f);
}

static void deserialize (std::ifstream in)
{
    char a;
    unsigned short b;
    int c;
    float d;
    double e;
    std::string f;

    in >> bits(a) >> bits(b) >> bits(c) >> bits(d);
    in >> bits(e) >> bits(f);
}

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.