overhaul serialization code

The implementation of each class' serialization/deserialization is no longer
passed within a macro. The implementation now lies within a template of form:

template <typename T, typename Stream, typename Operation>
inline static size_t SerializationOp(T thisPtr, Stream& s, Operation ser_action, int nType, int nVersion) {
    size_t nSerSize = 0;
    /* CODE */
    return nSerSize;
}

In cases when codepath should depend on whether or not we are just deserializing
(old fGetSize, fWrite, fRead flags) an additional clause can be used:
bool fRead = boost::is_same<Operation, CSerActionUnserialize>();

The IMPLEMENT_SERIALIZE macro will now be a freestanding clause added within
class' body (similiar to Qt's Q_OBJECT) to implement GetSerializeSize,
Serialize and Unserialize. These are now wrappers around
the "SerializationOp" template.
This commit is contained in:
Kamil Domanski
2014-08-20 08:42:31 +02:00
parent 9f3d476779
commit 3d796f8996
13 changed files with 462 additions and 273 deletions

View File

@@ -22,6 +22,7 @@
#include <boost/tuple/tuple.hpp>
#include <boost/type_traits/is_fundamental.hpp>
#include <boost/typeof/typeof.hpp>
class CAutoFile;
class CDataStream;
@@ -37,6 +38,14 @@ inline T& REF(const T& val)
return const_cast<T&>(val);
}
// Used to acquire a const pointer "this" and generate a const
// serialization operation from a template
template<typename T>
inline T MAKE_CONST(T val)
{
return const_cast<const T>(val);
}
/** Get begin pointer of vector (non-const version).
* @note These functions avoid the undefined case of indexing into an empty
* vector, as well as that of indexing after the end of the vector.
@@ -79,48 +88,27 @@ enum
SER_GETHASH = (1 << 2),
};
#define IMPLEMENT_SERIALIZE(statements) \
unsigned int GetSerializeSize(int nType, int nVersion) const \
{ \
CSerActionGetSerializeSize ser_action; \
const bool fGetSize = true; \
const bool fWrite = false; \
const bool fRead = false; \
unsigned int nSerSize = 0; \
ser_streamplaceholder s; \
assert(fGetSize||fWrite||fRead); /* suppress warning */ \
s.nType = nType; \
s.nVersion = nVersion; \
{statements} \
return nSerSize; \
} \
template<typename Stream> \
void Serialize(Stream& s, int nType, int nVersion) const \
{ \
CSerActionSerialize ser_action; \
const bool fGetSize = false; \
const bool fWrite = true; \
const bool fRead = false; \
unsigned int nSerSize = 0; \
assert(fGetSize||fWrite||fRead); /* suppress warning */ \
{statements} \
} \
template<typename Stream> \
void Unserialize(Stream& s, int nType, int nVersion) \
{ \
CSerActionUnserialize ser_action; \
const bool fGetSize = false; \
const bool fWrite = false; \
const bool fRead = true; \
unsigned int nSerSize = 0; \
assert(fGetSize||fWrite||fRead); /* suppress warning */ \
{statements} \
}
#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action))
/* Implement three methods for serializable objects. These are actually wrappers over
* "SerializationOp" template, which implements the body of each class' serialization
* code. Adding "IMPLEMENT_SERIALIZE" in the body of the class causes these wrappers to be
* added as members. */
#define IMPLEMENT_SERIALIZE \
size_t GetSerializeSize(int nType, int nVersion) const { \
ser_streamplaceholder s; \
s.nType = nType; \
s.nVersion = nVersion; \
return SerializationOp(MAKE_CONST(this), s, CSerActionGetSerializeSize(), nType, nVersion); \
} \
template<typename Stream> \
void Serialize(Stream& s, int nType, int nVersion) const { \
SerializationOp(MAKE_CONST(this), s, CSerActionSerialize(), nType, nVersion); \
} \
template<typename Stream> \
void Unserialize(Stream& s, int nType, int nVersion) { \
SerializationOp(this, s, CSerActionUnserialize(), nType, nVersion); \
}