How to Serialize an Object in C++

How do you serialize an object in C++?

Talking about serialization, the boost serialization API comes to my mind. As for transmitting the serialized data over the net, I'd either use Berkeley sockets or the asio library.

If you want to serialize your objects to a byte array, you can use the boost serializer in the following way (taken from the tutorial site):

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

Actual serialization is then pretty easy:

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

Deserialization works in an analogous manner.

There are also mechanisms which let you handle serialization of pointers (complex data structures like tress etc are no problem), derived classes and you can choose between binary and text serialization. Besides all STL containers are supported out of the box.

C++: how to serialize/deserialize objects without the use of libraries?

One pattern is to implement an abstract class the defines functions for serialization and the class defines what goes into the serializer and what comes out. An example would be:

class Serializable
{
public:
Serializable(){}
virtual ~Serializable(){}

virtual void serialize(std::ostream& stream) = 0;
virtual void deserialize(std::istream& stream) = 0;
};

You then implement Serializable interface for the class/struct that you want to serialize:

struct PersonInfo : public Serializable // Yes! It's possible
{
unsigned int age_;
string name_;
enum { undef, man, woman } sex_;

virtual void serialize(std::ostream& stream)
{
// Serialization code
stream << age_ << name_ << sex_;
}

virtual void deserialize(std::istream& stream)
{
// Deserialization code
stream >> age_ >> name_ >> sex_;
}
};

Rest I believe you know. Here's a few hurdles to pass though and can be done in your leisure:

  1. When you write a string to the stream with spaces in it and try to read it back, you will get only one portion of it and rest of the string 'corrupts' the values read after that.
  2. How can you program it such that it's cross-platform (little-endian vs big-endian)
  3. How can your program automatically detect, which class to create when deserializing.

Clues:

  1. Use custom serializer that has functions to write bool, int, float, strings, etc.
  2. Use a string to represent the object type being serialized and use factory to create an instance of that object when deserializing.
  3. Use predefined macros to determine which platform your code is being compiled.
  4. Always write files in a fixed endian and make the platforms that use the other endianess adjust to that.

Preferred way to serialize an object with C++

If you only have a single, very simple class to serialise out, it's not that hard to implement a serialisation function that writes out the handful of members that you need to save. You don't give a code example that shows your class, but with a handful of members it should be comparatively easy as long as there are no pointers involved. If you write out the number of objects serialised followed by the data contained therein, that would probably be good enough for your purposes.

If you want to implement the serialisation yourself, I would have an external function (possibly a friend of your class) handle the serialisation of the Task array rather than trying to put the array serialisation into your class. What you can do is add a serialiseObject() function to your class that serialises a single object, then call it repeatedly from the array serialisation function. That's a much cleaner design than having the array serialisation also bolted onto the class itself.

Once you get into serialising C++ object that are a little more complex, especially ones containing references and pointers, serialisation very quickly becomes a hard problem and you really, really want to use an existing, third-party mechanism that has been well tested.

That said, as someone who does C++ development for a living, I consider a dependency on boost as normal, not a third party library I would want to avoid. Boost gives you so much additional functionality that I consider it part of "my standard library".

how to serialize a struct in c?

This answer is besides the problems with your malloc.

Unfortunately, you cannot find a nice trick that would still be compatible with the standard. The only way of properly serializing a structure is to separately dissect each element into bytes, write them to an unsigned char array, send them over the network and put the pieces back together on the other end. In short, you would need a lot of shifting and bitwise operations.

In certain cases you would need to define a kind of protocol. In your case for example, you need to be sure you always put the object p is pointing to right after struct A, so once recovered, you can set the pointer properly. Did everyone say enough already that you can't send pointers through network?

Another protocolish thing you may want to do is to write the size allocated for the flexible array member s in struct B. Whatever layout for your serialized data you choose, obviously both sides should respect.

It is important to note that you cannot rely on anything machine specific such as order of bytes, structure paddings or size of basic types. This means that you should serialize each field of the element separately and assign them fixed number of bytes.

c++ Serializing an object containing string and pointer to another object

You have best answers from ISO CPP standard.

I can not explain better than that.

Please walk through question numbers (4,9,10,11) for answers to your specific question.

https://isocpp.org/wiki/faq/serialization

How to serialize an object to send over network

I got it!

I used strinstream to serialize objects and I sent it as a message using the stringstream's method str() and so string's c_str().

Look.

class Object {
public:
int a;
string b;

void methodSample1 ();
void methosSample2 ();

friend ostream& operator<< (ostream& out, Object& object) {
out << object.a << " " << object.b; //The space (" ") is necessari for separete elements
return out;
}

friend istream& operator>> (istream& in, Object& object) {
in >> object.a;
in >> object.b;
return in;
}
};

/* Server side */
int main () {
Object o;
stringstream ss;
o.a = 1;
o.b = 2;
ss << o; //serialize

write (socket, ss.str().c_str(), 20); //send - the buffer size must be adjusted, it's a sample
}

/* Client side */
int main () {
Object o2;
stringstream ss2;
char buffer[20];
string temp;

read (socket, buffer, 20); //receive
temp.assign(buffer);
ss << temp;
ss >> o2; //unserialize
}

I'm not sure if is necessary convert to string before to serialize (ss << o), maybe is possible directly from char.

C - serialization techniques

For each data structure, have a serialize_X function (where X is the struct name) which takes a pointer to an X and a pointer to an opaque buffer structure and calls the appropriate serializing functions. You should supply some primitives such as serialize_int which write to the buffer and update the output index.
The primitives will have to call something like reserve_space(N) where N is the number of bytes that are required before writing any data. reserve_space() will realloc the void* buffer to make it at least as big as it's current size plus N bytes.
To make this possible, the buffer structure will need to contain a pointer to the actual data, the index to write the next byte to (output index) and the size that is allocated for the data.
With this system, all of your serialize_X functions should be pretty straightforward, for example:

struct X {
int n, m;
char *string;
}

void serialize_X(struct X *x, struct Buffer *output) {
serialize_int(x->n, output);
serialize_int(x->m, output);
serialize_string(x->string, output);
}

And the framework code will be something like:

#define INITIAL_SIZE 32

struct Buffer {
void *data;
size_t next;
size_t size;
}

struct Buffer *new_buffer() {
struct Buffer *b = malloc(sizeof(Buffer));

b->data = malloc(INITIAL_SIZE);
b->size = INITIAL_SIZE;
b->next = 0;

return b;
}

void reserve_space(Buffer *b, size_t bytes) {
if((b->next + bytes) > b->size) {
/* double size to enforce O(lg N) reallocs */
b->data = realloc(b->data, b->size * 2);
b->size *= 2;
}
}

From this, it should be pretty simple to implement all of the serialize_() functions you need.

EDIT:
For example:

void serialize_int(int x, Buffer *b) {
/* assume int == long; how can this be done better? */
x = htonl(x);

reserve_space(b, sizeof(int));

memcpy(((char *)b->data) + b->next, &x, sizeof(int));
b->next += sizeof(int);
}

EDIT:
Also note that my code has some potential bugs. There is no provision for error handling and no function to free the Buffer after you're done so you'll have to do this yourself. I was just giving a demonstration of the basic architecture that I would use.

is it possible to simply serialize C++ objects

Can you cast an object to a string of hex data (similar to how packets
are sent) and then store that and then cast the object back?

No.

(or perhaps I do not know how.)

Note - tcp packets are not hex, or otherwise formatted, either.

Casting the pointer to your data buffer does nothing to the data, nothing to the binary contents of an array of bytes. No conversion. No formatting.

So the c-style cast to (unsigned char*) will NOT convert the contents to hex text.

If you want to translate to/from hex format, you have to write code (i.e.operator>>() and operator<<()) to translate each byte into two characters. This is easy but processor expensive. (You can find many examples on the net.)


is it possible to simply serialize C++ objects

Yes.

Many would emphasize the 'serialize' in your question, and worry about endian-ness, and other issues. 'Serialize' has specific meaning in certain contexts - persistent storage is where I first ran into these items.

If, on the other hand, binary is ok, and you just want to send a binary packet to/from the file system, or over tcp/ip socket streams, all you need to do is use write/read to store/retrieve the object's data into/out of a (binary) file, or send/receive over stream socket.

Consider the following:

 class Something
{
public:
Something(void) { clear(); }
~Something (void) { clear(); }

void clear(void){for (int i=0; i<100; i+=1) m_data[i] = 0;}

void init(void) {for (int i=0; i<100; i+=1) m_data[i] = char(i); }

const char* data_GetAddr() { return m_data; }
char* data_PutAddr() { return m_data; }

// show 3 bytes:
void show(void) { std::cout << "m_data: "
<< m_data[0] << " "
<< m_data[1] << " "
<< m_data[2] << "\n"
<< std::endl; }
private:
char m_data[100];
// and various other POD here
};



int main (int, char**)
{
auto obj1 = new Something();
obj1->init();
obj1->show(); // show initialized data

// cast does not convert from binary to text
// so the following does not help
// auto obj1Hex = (unsigned char*)obj1;

// but we can store obj1 to a file in binary
std::stringstream ss; // a ram-base 'file'

// store data to file using write.
ss.write(obj1->data_GetAddr(), sizeof(Something));

// now we allocate a receive buffer just as you have suggested
auto obj2 = new Something(); // allocate space for another instance
obj2->show(); // show this has 0's

// retrieve obj data from file, installing it into obj2 working buffer.
ss.read(obj2->data_PutAddr(), sizeof(Something));

obj2->show(); // show results

return(0);
}

The output looks similar to the following (emacs presents the binary 0, 1, 2 as the keystrokes needed to achieve them, i.e. control-@, control-A, control-B

m_data: ^@ ^A ^B <<< obj1 after init()

m_data: ^@ ^@ ^@ <<< uninitialized obj2 (all 0's)

m_data: ^@ ^A ^B <<< obj2 after read, no init()

Is it possible to Serialize and Deserialize objects in C++?

Check this out:

http://www.functionx.com/cpp/articles/serialization.htm

or use

Boost:Serialization
http://www.boost.org/doc/libs/1_36_0/libs/serialization/doc/index.html



Related Topics



Leave a reply



Submit