I've been working on a generic serialization class to use in my game(s). My main design goal was for the class to be multipurpose - meaning that I want to use it for serializing messages for network transport or just for saving the games state to disk. I have went through a couple of iterations so far...
The first required me to provide the size of the object and that worked but wasn't really generic. The second used the sizof() operator to determine the size of the memory required for the class, this was generic enough but the size of memory was not directly representational of the data member size.
So, what I finally decided on was to use the STL's deque like a dynamic array to store the data members of the class. Here's the class interface:
class CArchive
{
public:
typedef std::deque<BYTE*> ArchiveArray;
typedef std::deque<BYTE*>::iterator ArchiveArrayIt;
CArchive();
~CArchive(){}
// insertion operations
CArchive& operator<<(BYTE by);
CArchive& operator<<(WORD w);
CArchive& operator<<(LONG l);
CArchive& operator<<(DWORD dw);
CArchive& operator<<(std::string str);
CArchive& operator<<(LARGE_INTEGER li);
// extraction operations
CArchive& operator>>(BYTE& by);
CArchive& operator>>(WORD& w);
CArchive& operator>>(LONG& l);
CArchive& operator>>(DWORD& dw);
CArchive& operator>>(std::string& str);
CArchive& operator>>(LARGE_INTEGER& li);
protected:
private:
ArchiveArray m_Archive;
ArchiveArrayIt m_Ierator;
size_t size;
};
And here's an example of the WORD insertion and extraction operators in action.
CArchive& CArchive::operator<<(WORD w)
{
m_Archive.push_back((BYTE*)w);
size += sizeof(WORD);
return *this;
}
CArchive& CArchive::operator>>(WORD& w)
{
w = (WORD)m_Archive.front();
m_Archive.pop_front();
return *this;
}
And just for GP here's the string insertion implementation:
CArchive& CArchive::operator<<(std::string str)
{
DWORD slen = (DWORD)str.length();
*this << slen;
for(DWORD loop=0; loop <slen; loop++)
{
m_Archive.push_back((BYTE*)str[loop] );
size += sizeof(BYTE);
}
return *this;
}
EDIT FOLLOWS
------------
So, I didn't get any responses with the above info... here's some added text to further detail the system.
The CGameObject class contains a protected member of type CArchive (m_ar) and as usual all game objects derive from CGameObject. This is a condensed look at CGameObject:
class CGameObject
{
public:
CGameObject()
{
m_ObjectVersion = MAKEWORD( eMajorVersion, eMinorVersion );
}
~CGameObject(){};
virtual void Serialize()=0;
virtual void DeSerialize()=0;
void Display()=0;
private:
// objects cannot be copied or assigned
EPObject(const EPObject& obj);
void operator=(const EPObject& obj);
protected:
CArchive m_ar;
WORD m_ObjectVersion;
};
Pretty simple stuff, right? The CGameObject has a few pure virtual functions that the derived classes define, here's a test class named CTest.
class CTest : public CGameObject
{
public:
CTest();
~CTest(){};
void Serialize();
void DeSerialize();
void SaveToDisk();
private:
// For testing...
DWORD m_msgID;
BYTE z;
WORD zz;
LONG lo;
std::string* str;
WORD w;
};
Here's the implementation of the Serialize() and DeSerialize() methods.
void CTest::Serialize()
{
// to serialize a class, place the memberdata that you want to save into the
// archive object in the order that you want it to place. Careful to remove the
// data in the same order.
m_ar << m_msgID;
m_ar << z;
m_ar << zz;
m_ar << lo;
m_ar << *str;
m_ar << w;
}
void CTest::DeSerialize()
{
m_ar >> m_msgID;
m_ar >> z;
m_ar >> zz;
m_ar >> lo;
m_ar >> *str;
m_ar >> w;
}
I might mention that I left out the code that saves and the code that stamps the version number into the serialized data but that is pretty straight forward.
I suppose my question is this: How safe is it to use the std::deque in the manner that I am using it? During my testing I have not noticed any peculiarities, but I myself am a bit hesitant to call this iteration "good." My concern deals with how I am casting the data into the deque. Anyone else have a comment or caution regarding this usage?
Thanks,
[edited by - Dak Lozar on May 4, 2003 10:05:43 AM]