Wednesday, January 20, 2010

Object Serializing in C++

Why till date we do not have any standard way serialize/deserialize C++ like Java.This is some of the disadvantages C++ have apart from Reflection(as in Java).So I had to find a crude way of doing that which most of you will not like.

Part Zero

This technique is a cliche in C++ object serializing topic.Implement a seialize() function in the class with putting of attributes to the ostringstream.For deserializing read from istringstream and update the variables.

Example:

Part1

memcpy is faster than istringstream read or ostringstream write.
Why dont we have functions written on top of memcpy to provide the interface to serialize/deserialize various variables?
say serializeInt(int i) or serializeBool(bool b) .. etc as follows.
string serializeInt(int i)
{
string p = "1111";
char* ch = (char*)p.c_str();
memcpy(ch, &i, sizeof( i ));
string temp;
temp.assign(ch, sizeof( i ));
return temp;
}

So we use it straight away as

string serialize()
{
_string str;
str.append(serializeInt(10));
str.append(serializeBool(true);
.....and so on
}

Deserialize a bit crooked and looks like this

int deserializeInt(char** ch)
{
int num;
memcpy(&num, *ch, sizeof(num));
*ch += sizeof(num);
return num;
}

So we need to pass the address of string to the deserialize function which will after getting the value modify it to the next item to be deserialized.
So, the deserialize function of the classes look as follows.
void deserialize(string str)
{
char* ch = (char*)str.c_str();
int num = deserializeInt(&ch);
bool f = deserializeBool(&ch);
string str1 = deserializeString(&ch);
}

Part2

Now we need to come up with one class that can handle serializing of any class.Lets name it Serializer.Serializer needs to know the type and value of the attribute that it has to serialize.So, it has a multimap as a member variable. Why Multimap and not Map, because a class can have two integers as attributes or more than one float attribute.Those cannot be entered in map as the key i.e type is not unique. Serializer class also provides a function to insert the type and value to the map.

Now how should the value be entered ??As we are using the multimap we cannot have different types for the value. So, we define the map with a pointer to char as multimap. Following is what the class looks like

class Serializer
{
private:
multimap<string,char> m_typeMap;
public:
void insert(string type, char* ptr)
{
m_typeMap.insert(pair<string,char*>(type, ptr));
}

string serialize()
{
ostringstream str;
char *ptr = str.c_str();
for(multimap<string,char*>::iterator iter = m_typeMap.begin(); iter != m_typeMap.end(); iter++)
{
if(iter->first == "int")
{
int val = *(int*)iter->second;
str.write(reinterpret_cast<char*>(&val),sizeof(val));
}
}
}

void deserialize(string str)
{
//Deserialize and assign the value to the address Here
istringstream str(str);
for(multimap<string,char*>::iterator iter = m_typeMap.begin(); iter != m_typeMap.end(); iter++)
{
if(iter->first == "int")
{
int val;
str.read(reinterpret_cast<char*>(&val), sizeof(val));
*(int*)iter->second = val
}
}
}

};

Now the Serializer class is having code to serialize integer data type only. This can be extended to support any data type.Now lets see how can we use this class.

Following is the class A, which needs to be serialized.We inherit this class from our Serializer class and register the variables that we want to serialize.

class A : public Serializer
{
private:
int i;
public:
A()
{
insert("int", (char*)&i);
}
int getVal()
{
return i;
}
void setVal(int j)
{
i = j;
}
};

Now lets use this class in our main executable.

int main()
{
A obj;
obj.setVal(10);
string str = obj.serialize();

B obj1;
obj1.deserialize();
cout<<obj1.getValue();}

Hope this helps in making serialize/deserialize of c++ objects in a more generic way instead of putting the serialize/deserialize functions in each n every class.

More solutions are welcome.