एक सामान्य पैटर्न है जिसका उपयोग आप वस्तुओं को क्रमबद्ध करने के लिए कर सकते हैं। अंतिम संस्कार आदिम वे दो कार्य हैं जिन्हें आप पुनरावृत्तियों से पढ़ और लिख सकते हैं:
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)
{
}
template <class InputCharIterator>
void deserialize(YourType &obj, InputCharIterator &&it, InputCharIterator &&end)
{
}
कक्षाओं के लिए आप ADL का उपयोग करके अधिभार की अनुमति देने के लिए मित्र फ़ंक्शन पैटर्न का उपयोग कर सकते हैं:
class Foo
{
int internal1, internal2;
template <class OutputCharIterator>
friend void serialize(const Foo &obj, OutputCharIterator &&it)
{
}
};
अपने कार्यक्रम में आप इस तरह एक फ़ाइल में क्रमबद्ध और ऑब्जेक्ट कर सकते हैं:
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।
मूल रूप से हमने यहां क्या किया, पॉइंटर्स को एरे इंडेक्स के साथ बदल दिया। अब क्रमबद्ध करते समय आप इस सरणी सूचकांक को हमेशा की तरह क्रमबद्ध कर सकते हैं। आपको इस बात की चिंता करने की आवश्यकता नहीं है कि गंतव्य सिस्टम पर ऑब्जेक्ट मेमोरी में कहां होगा। बस सुनिश्चित करें कि उनके पास समान ऑब्जेक्ट पूल भी है।
इसलिए हमें ऑब्जेक्ट पूल को क्रमबद्ध करने की आवश्यकता है। लेकिन कौन से? जब आप एक वस्तु ग्राफ को क्रमबद्ध करते हैं तो आप केवल एक वस्तु को क्रमबद्ध नहीं कर रहे हैं, आप एक संपूर्ण प्रणाली को क्रमबद्ध कर रहे हैं। इसका मतलब है कि सिस्टम का क्रमांकन सिस्टम के कुछ हिस्सों से शुरू नहीं होना चाहिए। उन वस्तुओं को बाकी प्रणाली के बारे में चिंता नहीं करनी चाहिए, उन्हें केवल सरणी अनुक्रमित को क्रमबद्ध करने की आवश्यकता है और यही वह है। आपके पास एक सिस्टम क्रमिक रुटीन होना चाहिए जो सिस्टम के क्रमांकन को ऑर्केस्ट्रेट करता है और संबंधित ऑब्जेक्ट पूल के माध्यम से चलता है और उन सभी को क्रमबद्ध करता है।
प्राप्त होने वाले अंत में सभी ऑब्जेक्ट को वांछित वस्तु ग्राफ को फिर से बनाने के लिए, ऑब्जेक्ट को डीरियलाइज़ किया जाता है।
फ़ंक्शन पॉइंटर्स को सीरियल करना
ऑब्जेक्ट में पॉइंटर्स स्टोर न करें। एक स्टैटिक ऐरे है जिसमें इन फंक्शन्स के पॉइंटर्स हैं और इंडेक्स को ऑब्जेक्ट में स्टोर करते हैं।
चूंकि दोनों कार्यक्रमों में यह तालिका थीशेल्स में संकलित है, बस सूचकांक का उपयोग करना चाहिए।
बहुरूपी प्रकारों को सीरियलाइज़ करना
चूँकि मैंने कहा था कि आपको क्रमिक प्रकारों में पॉइंटर्स से बचना चाहिए और आपको इसके बजाय एरे इंडेक्स का उपयोग करना चाहिए, बहुरूपता सिर्फ काम नहीं कर सकती है, क्योंकि इसके लिए पॉइंटर्स की आवश्यकता होती है।
आपको इसे टाइप टैग और यूनियनों के साथ काम करने की आवश्यकता है।
संस्करण
उपरोक्त सभी के ऊपर। आप सॉफ्टवेयर के अलग-अलग संस्करण चाहते हैं।
इस मामले में प्रत्येक ऑब्जेक्ट को संस्करण को इंगित करने के लिए उनके क्रमांकन की शुरुआत में एक संस्करण संख्या लिखना चाहिए।
जब दूसरी तरफ वस्तु को लोड किया जाता है, तो नई वस्तुएं शायद पुराने अभ्यावेदन को संभालने में सक्षम होती हैं, लेकिन पुराने लोग नए को संभाल नहीं सकते हैं, इसलिए उन्हें इस बारे में एक अपवाद फेंक देना चाहिए।
हर बार कुछ परिवर्तन होने पर, आपको संस्करण संख्या को टक्कर देनी चाहिए।
तो इसे लपेटने के लिए, क्रमबद्धता जटिल हो सकती है। लेकिन सौभाग्य से आपको अपने कार्यक्रम में सब कुछ क्रमबद्ध करने की आवश्यकता नहीं है, सबसे अधिक बार केवल प्रोटोकॉल संदेश क्रमबद्ध होते हैं, जो अक्सर सादे पुराने ढांचे होते हैं। इसलिए आपको उन जटिल ट्रिक्स की ज़रूरत नहीं है जिनका मैंने ऊपर उल्लेख किया है।