How to Create Persistent Objects

How to Create Persistent Objects

ames, distributed database systems, multimedia and graphic applications use persistent objects extensively. Yet presently, C++ doesn’t support persistence directly (there are proposals for adding persistence and reflection to C++ in the future). Persistence support is not as trivial as it may seem at first. The size and memory layout of the same object may vary from one platform to another. Different byte ordering, or endian-ness, complicate matters even further.

In this article, I will show how to implement persistence without resorting to third-party frameworks such as DCOM and CORBA. For small and portable applications, this is an effective and satisfactory solution.

To make an object persistent, we have to reserve its state in a non-volatile storage device. Consider an application that records and plays MP3 files. Every clip is represented as an object that contains the title, album, performer, time, bit-rate, recording date and the matching MP3 file. The application displays recently played tracks on the track list.



We need to to implement object persistence without resorting to third-party frameworks such as DCOM and CORBA.



Write a persistent object to retain its state outside the scope of the program in which it was created.

Step 1: Serializing Built-in Datatypes
Ultimately, every object consists of built-in data members such as int, bool, char [] and so on. Our first exercise is to write such datatypes to an ofstream (read more on file I/O here). We store the values in their binary format. For this purpose, we use the write() and read() member functions. write() takes the address and the size of a variable and writes its bit pattern to a file stream. read() takes two arguments of type char * and long which contain buffer’s address and its size in bytes, respectively. The following example demonstrates how to store two integers in an ofstream:
#include <:fstream>:using namespace std;int main(){ int x,y; //mouse coordinates //..assign values to x and y ofstream archive(“coord.dat”, ios::binary); archive.write(reinterpret_cast(&x), sizeof (x)); archive.write(reinterpret_cast(&x), sizeof (x)); archive.close();}

The use of reinterpret_cast is necessary because write() takes const char * as its first argument whereas &x and &y are of type int *.

We retrieve the previously stored values as follows:

#include using namespace std;int main(){ int x,y; ifstream archive(“coord.dat”); archive.read((reinterpret_cast(&x), sizeof(x)); archive.read((reinterpret_cast(&y), sizeof(y));}
Step 2: Serializing Objects
To serialize a complete object, we write its data members to a file:

class MP3_clip{private: std::time_t date; std::string name; int bitrate; bool stereo;public: void serialize(); void deserialize(); //..};void MP3_clip::serialize(){{int size=name.size();// store name’s length  //empty file if it already exists before writing new data ofstream arc(“mp3.dat”, ios::binary|ios::trunc); arc.write(reinterpret_cast(&date),sizeof(date)); arc.write(reinterpret_cast(&size),sizeof(size)); arc.write(name.c_str(), size+1); // write final ‘’ too arc.write(reinterpret_cast(&bitrate),  sizeof(bitrate)); arc.write(reinterpret_cast(&stereo),  sizeof(stereo));}

The implementation of deserialize() is a bit trickier, since we need to allocate a temporary buffer for the string:
void MP3_clip::deserialize(){ ifstream arce(“mp3.dat”); int len=0; char *p=0; arc.read(reinterpret_cast  (&date), sizeof(date)); arc.read(reinterpret_cast (&len), sizeof(len)); p=new char [len+1]; // allocate temp buffer for name arc.read(p, len+1); // copy name to temp, including ‘’ name=p; // copy temp to data member delete[] p; arc.read(reinterpret_cast (&bitrate),  sizeof(bitrate)); arc.read(reinterpret_cast (&stereo),  sizeof(stereo));}

Performance Considerations
You might wonder why we didn’t dump the entire object to a file instead of serializing individual data members. In other words, couldn’t we implement serialize() as follows:

void MP3_clip::serialize(){ ofstream arc(“mp3.dat”, ios::binary|ios::trunc); arc.write(reinterpret_cast (this),sizeof(*this));}

No, we couldn’t. There are at least two problems with this approach. Usually, when the serialized object contains other objects, you can’t just dump it to a file and reconstitute a valid object subsequently. In our example, the enclosing object contains a “std::string member.” A shallow copy would archive transient “std::string” members whose values are transient, meaning that they may change every time you run the program. Worse yet, because “std::string” doesn’t actually contain a char array but a pointer, it would be impossible to reconstitute the original string if we used shallow copying. To overcome this problem we didn’t serialize the string object. Instead, we archived its characters and length. In general, pointers, arrays and handles should be treated similarly.

Another problem might arise with polymorphic objects. Every polymorphic object contains a vptr?a hidden pointer to a dispatch table that holds the addresses of its virtual functions. The vptr’s value is transient. If we dumped an entire polymorphic object to a file and then superimposed the archived data on a new object, that reconstituted object’s vptr might be invalid and cause undefined behavior. Here again, the solution is to serialize and deserialize only non-transient data members. Alternatively, you can calculate the exact offset of the vptr and leave it intact when you reconstitute an object from a file. Remember that the vptr’s position is implementation-dependent. Therefore, such code isn’t portable.

Share the Post:
XDR solutions

The Benefits of Using XDR Solutions

Cybercriminals constantly adapt their strategies, developing newer, more powerful, and intelligent ways to attack your network. Since security professionals must innovate as well, more conventional endpoint detection solutions have evolved

AI is revolutionizing fraud detection

How AI is Revolutionizing Fraud Detection

Artificial intelligence – commonly known as AI – means a form of technology with multiple uses. As a result, it has become extremely valuable to a number of businesses across

AI innovation

Companies Leading AI Innovation in 2023

Artificial intelligence (AI) has been transforming industries and revolutionizing business operations. AI’s potential to enhance efficiency and productivity has become crucial to many businesses. As we move into 2023, several

data fivetran pricing

Fivetran Pricing Explained

One of the biggest trends of the 21st century is the massive surge in analytics. Analytics is the process of utilizing data to drive future decision-making. With so much of

kubernetes logging

Kubernetes Logging: What You Need to Know

Kubernetes from Google is one of the most popular open-source and free container management solutions made to make managing and deploying applications easier. It has a solid architecture that makes

ransomware cyber attack

Why Is Ransomware Such a Major Threat?

One of the most significant cyber threats faced by modern organizations is a ransomware attack. Ransomware attacks have grown in both sophistication and frequency over the past few years, forcing

data dictionary

Tools You Need to Make a Data Dictionary

Data dictionaries are crucial for organizations of all sizes that deal with large amounts of data. they are centralized repositories of all the data in organizations, including metadata such as