Custom serialiser with object factory











up vote
2
down vote

favorite
1












A couple of years ago I wrote a serialisation library for my project. By the time it worked well, but recently I thought it could be modernised. Back then I needed something that looks similar to Boost's serialiser, but also support polymorphism. I need to serialise and deserialise different types of objects from the same inheriting tree. However I didn't wanted to include Boost library just for this. Also I needed to support different sources besides FILE* (I only included just one here.)



I came across with this solution, tweaked a bit, wrote a couple of tests around it, and been using. It's spread across multiple files in my repo, and I do have a couple more tests for it using GTest.



My overall questions are




  • How can I use std some more to make it more compatible with its containers? I think about supporting std::map first

  • What other possible way are there to improve the way I define and store factories of the different objects?

  • The most important thing is for me is support more efficiently the serialisation of the parents of the class instances. At this point I only have a terrible solution that is I define a non-virtual _Serialize() method that takes care of it via override. Is there a solution to collect all the parents serialiser method more seamlessly and invoke them?


Here's the source code, that I had concatenated together and modified a bit to work single handed. I used clang on MacOS via Cmake to test it.



/*
Dynamics API
*/

#ifndef __DYNAMICS_H__
#define __DYNAMICS_H__

#include <string>
#include <map>


namespace Grafkit
{
class Clonables;

/**
*/
class Clonable
{
friend class Clonables;

public:
virtual ~Clonable() = default;
virtual Clonable *CreateObj() const = 0;
};

/**
*/
class Clonables
{
std::map<std::string, const Clonable *> m_clonables;

Clonables() {}

Clonables(const Clonables &) = delete;
Clonables &operator=(const Clonables &) = delete;

virtual ~Clonables()
{
for (auto it = m_clonables.begin(); it != m_clonables.end(); ++it)
{
const Clonable *clone = it->second;
delete clone;
}
m_clonables.clear();
}

public:
static Clonables &Instance()
{
static Clonables instance;
return instance;
}

void AddClonable(const char *CLASS_NAME, Clonable *clone)
{
std::string name = CLASS_NAME;

auto it = m_clonables.find(name);
if (it == m_clonables.end())
{
m_clonables[name] = clone;
}
}

const Clonable *Find(const char *CLASS_NAME)
{
std::string name = CLASS_NAME;
auto it = m_clonables.find(name);
if (it == m_clonables.end())
return nullptr;
const Clonable *clone = it->second;
return clone;
}

Clonable *Create(const char *CLASS_NAME)
{
const Clonable *clone = Find(CLASS_NAME);
if (clone)
return clone->CreateObj();

return nullptr;
}
};

class AddClonable
{
public:
AddClonable(const char *CLASS_NAME, Clonable *clone)
{
Clonables::Instance().AddClonable(CLASS_NAME, clone);
}
};

} // namespace Grafkit

#define CLONEABLE_DECL(CLASS_NAME)
public:
virtual Grafkit::Clonable *CreateObj() const override
{
return new CLASS_NAME();
}

#define CLONEABLE_FACTORY_DECL(CLASS_NAME)
CLONEABLE_DECL(CLASS_NAME)
public:
class Factory : public Grafkit::Clonable
{
public:
virtual Grafkit::Clonable *CreateObj() const
{
return new CLASS_NAME();
}
};

private:
static Grafkit::AddClonable _addClonableFactory;

#define CLONEABLE_FACTORY_IMPL(CLASS_NAME)
Grafkit::AddClonable CLASS_NAME::_addClonableFactory(#CLASS_NAME, new CLASS_NAME::Factory());

#define CLONEABLE_FACTORY_LOCAL_IMPL(CLASS_NAME)
Grafkit::AddClonable CLASS_NAME##_addClonableFactory(#CLASS_NAME, new CLASS_NAME::Factory());

#endif //__DYNAMICS_H__

/*
Persistence API
*/

#ifndef __PERSISTENCE_H__
#define __PERSISTENCE_H__

#include <cstdlib>
#include <vector>

// Alrady included above
//#include "dynamics.h"

namespace Grafkit
{
class Archive;

class Persistent : public Clonable
{
public:
Persistent() {}
virtual ~Persistent() {}

static Persistent *Load(Archive &ar);
template <class C>
static C *LoadT(Archive &ar) { return dynamic_cast<C *>(Load(ar)); }
void Store(Archive &ar);

protected:
virtual void Serialize(Archive &ar) = 0;

virtual std::string GetClazzName() const = 0;
virtual int GetVersion() const { return 0; }
};

class Archive
{
public:
explicit Archive(int IsStoring = 0);
virtual ~Archive();

/* IO API */
protected:
virtual void Write(const void *buffer, size_t length) = 0;
virtual void Read(void *buffer, size_t length) = 0;

size_t WriteString(const char *input);
size_t ReadString(char *&output);

public:
template <typename T>
void PersistField(T &t)
{
if (m_isStoring)
{
Write(&t, sizeof(T));
}
else
{
Read(&t, sizeof(T));
}
}

template <typename T>
void PersistVector(T *&v, uint32_t &count)
{
if (m_isStoring)
{
Write(&count, sizeof(count));
Write(v, sizeof(T) * count);
}
else
{
uint32_t readCount = 0;
Read(&readCount, sizeof(readCount));

void *p = malloc(sizeof(T) * readCount);
Read(p, sizeof(T) * readCount);

v = reinterpret_cast<T *>(p);

count = readCount;
}
}

template <typename T>
void PersistStdVector(std::vector<T> &v)
{
if (m_isStoring)
{
uint32_t u32count = v.size(); //clamp it down to 32 bit
Write(&u32count, sizeof(u32count));
void *p = v.data();
Write(p, sizeof(T) * u32count);
}
else
{
uint32_t count = 0;
Read(&count, sizeof(count));

void *p = malloc(sizeof(T) * count);
Read(p, sizeof(T) * count);

v.clear();
v.assign(static_cast<T *>(p), static_cast<T *>(p) + count);
}
}

void PersistString(const char *&str);
void PersistString(std::string &str);

void StoreObject(Persistent *object);
Persistent *LoadObject();

int IsStoring() const { return m_isStoring; }
void SetDirection(bool IsStoring) { m_isStoring = IsStoring; }

private:
int m_isStoring;
};
} // namespace Grafkit

// ------------------

#define PERSISTENT_DECL(CLASS_NAME, VERSION_NO)
public:
CLONEABLE_FACTORY_DECL(CLASS_NAME)
public:
std::string GetClazzName() const override
{
return #CLASS_NAME;
}
int GetVersion() const override
{
return VERSION_NO;
}

#define PERSISTENT_IMPL(CLASS_NAME)
CLONEABLE_FACTORY_IMPL(CLASS_NAME)

// ---

#define PERSIST_FIELD(AR, FIELD) (AR.PersistField<decltype(FIELD)>((FIELD)))
#define PERSIST_VECTOR(AR, VECTOR, COUNT) (AR.PersistVector<std::remove_pointer<decltype(VECTOR)>::type>(VECTOR, COUNT))
#define PERSIST_STD_VECTOR(AR, VECTOR) (AR.PersistStdVector<decltype(VECTOR)::value_type>(VECTOR))
#define PERSIST_STRING(AR, FIELD) (AR.PersistString((FIELD)))

#define _PERSIST_OBJECT(AR, TYPE, IN_FIELD, OUT_FIELD)
{
if (AR.IsStoring())
{
AR.StoreObject(dynamic_cast<Persistent *>((IN_FIELD)));
}
else
{
(OUT_FIELD) = dynamic_cast<TYPE>(AR.LoadObject());
}
}

#define PERSIST_OBJECT(AR, OBJECT) _PERSIST_OBJECT(AR, decltype(OBJECT), OBJECT, OBJECT)
#define PERSIST_REFOBJECT(AR, REF) _PERSIST_OBJECT(AR, decltype(REF.Get()), REF.Get(), REF)

#endif //__PERSISTENCE_H__

/*
Archive IO layer
*/

#ifndef __ARCHIVE_H__
#define __ARCHIVE_H__

// #include "persistence.h"

namespace Grafkit
{

class ArchiveFile : public Archive
{
public:
ArchiveFile(FILE *stream, bool IsStoring = false);
virtual ~ArchiveFile();

void Write(const void *buffer, size_t length) override;
void Read(void *buffer, size_t length) override;

private:
FILE *_stream;
};
}; // namespace Grafkit

#endif //__ARCHIVE_H__

/***************************************************
Impl
****************************************************/

/*
Persistent layer impl: persistent.cpp
*/

#include <vector>
#include <cassert>

// Already pasted above
//#include "persistence.h"
//#include "dynamics.h"

using namespace Grafkit;
using namespace std;

void Persistent::Store(Archive &ar)
{
string CLASS_NAME = this->GetClazzName();
uint8_t ver = this->GetVersion();

PERSIST_STRING(ar, CLASS_NAME);
PERSIST_FIELD(ar, ver);

this->Serialize(ar);
}

Persistent *Persistent::Load(Archive &ar)
{
string CLASS_NAME;
uint8_t ver = 0;
PERSIST_STRING(ar, CLASS_NAME);

Clonable *clone = Clonables::Instance().Create(CLASS_NAME.c_str());
assert(clone);

Persistent *obj = dynamic_cast<Persistent *>(clone);
assert(obj);

PERSIST_FIELD(ar, ver);
assert(ver == obj->GetVersion());

obj->Serialize(ar);
return obj;
}

/**
Archive
*/

Archive::Archive(int IsStoring) : m_isStoring(IsStoring)
{
}

Archive::~Archive()
{
}

size_t Archive::WriteString(const char *input)
{
uint16_t slen = strlen(input);
this->Write(&slen, sizeof(slen));
this->Write(input, slen + 1);
return slen;
}

size_t Archive::ReadString(char *&output)
{
uint16_t slen = 0;
this->Read(&slen, sizeof(slen));
output = new char[slen + 1];
this->Read(output, slen + 1);
return slen;
}

void Archive::PersistString(const char *&str)
{
if (m_isStoring)
{
WriteString(str);
}
else
{
char *in = nullptr;
ReadString(in);
str = in;
}
}

void Archive::PersistString(string &str)
{
if (m_isStoring)
{
WriteString(str.c_str());
}
else
{
char *in = nullptr;
ReadString(in);
str = in;
delete in;
}
}

void Archive::StoreObject(Persistent *object)
{
uint8_t isNotNull = object != nullptr;
PersistField(isNotNull);

if (isNotNull)
object->Store(*this);
}

Persistent *Archive::LoadObject()
{
uint8_t isNotNull = 0;
PersistField(isNotNull);

if (isNotNull)
return Persistent::Load(*this);
return nullptr;
}

/*
Archive IO impl: archive.cpp
*/

// Already included above
// #include "archive.h"
#include <cassert>

using namespace Grafkit;

ArchiveFile::ArchiveFile(FILE *stream, bool IsStoring) : Archive(IsStoring),
_stream(stream)
{
assert(_stream);
}

ArchiveFile::~ArchiveFile()
{
}

void ArchiveFile::Write(const void *buffer, size_t length)
{
assert(_stream);
fwrite(buffer, length, 1, this->_stream);
}

void ArchiveFile::Read(void *buffer, size_t length)
{
assert(_stream);
fread(buffer, length, 1, this->_stream);
}

/**
Test classes, Main
*/

class SimpleClass : public Grafkit::Persistent
{
public:
SimpleClass() : Persistent(), m_i(0)
{
}

void SetParams(const int i, const std::string str)
{
m_i = i;
m_str = str;
}

int GetI() const { return m_i; }
std::string GetStr() const { return m_str; }

PERSISTENT_DECL(SimpleClass, 1);

protected:
void Serialize(Archive &ar) override
{
PERSIST_FIELD(ar, m_i);
PERSIST_STRING(ar, m_str);
}

private:
int m_i;
std::string m_str;
};

class SimpleBaseClass : public Grafkit::Persistent
{
public:
SimpleBaseClass() : Persistent(), m_i(0) {}

void SetParams(const int i, const std::string str)
{
m_i = i;
m_str = str;
}

int GetI() const { return m_i; }
std::string GetStr() const { return m_str; }

virtual std::string GetSomeIntern() const = 0;

protected:
void _Serialize(Archive &ar)
{
PERSIST_FIELD(ar, m_i);
PERSIST_STRING(ar, m_str);
}

private:
int m_i;
std::string m_str;
};

class DerivedClassA : public SimpleBaseClass
{
public:
DerivedClassA() : SimpleBaseClass(), m_str1("This is derived class A") {}
virtual std::string GetSomeIntern() const override { return m_str1; }

PERSISTENT_DECL(DerivedClassA, 1);

protected:
void Serialize(Archive &ar) override
{
SimpleBaseClass::_Serialize(ar);
PERSIST_STRING(ar, m_str1);
}

private:
std::string m_str1;
};

class DerivedClassB : public SimpleBaseClass
{
public:
DerivedClassB() : SimpleBaseClass(), m_str2("This is derived class B") {}
virtual std::string GetSomeIntern() const override { return m_str2; }

PERSISTENT_DECL(DerivedClassB, 1);

protected:
void Serialize(Archive &ar) override
{
SimpleBaseClass::_Serialize(ar);
PERSIST_STRING(ar, m_str2);
}

private:
std::string m_str2;
};

#include <cstdio>

PERSISTENT_IMPL(SimpleClass);
PERSISTENT_IMPL(DerivedClassA);
PERSISTENT_IMPL(DerivedClassB);

int main()
{
FILE *fp;

// -- 1. simple class
// given
SimpleClass *simpleObj = new SimpleClass();
simpleObj->SetParams(42, "Hello Serializer");

SimpleBaseClass *inheritedA = new DerivedClassA();
SimpleBaseClass *inheritedB = new DerivedClassB();

inheritedA->SetParams(314, "Hello Serializer w/ inherited classes");
inheritedA->SetParams(216, "Hello Serializer GLEJD");

// when
fp = fopen("archive.bin", "wb'");
assert(fp);

ArchiveFile arWrite(fp, true);
simpleObj->Store(arWrite);
inheritedA->Store(arWrite);
inheritedB->Store(arWrite);

fflush(fp);
fclose(fp);
fp = nullptr;

// then
fp = fopen("archive.bin", "rb");
assert(fp);

ArchiveFile arRead(fp, false);
SimpleClass *loadedObj = Persistent::LoadT<SimpleClass>(arRead);
SimpleBaseClass *loadedA = Persistent::LoadT<SimpleBaseClass>(arRead);
SimpleBaseClass *loadedB = Persistent::LoadT<SimpleBaseClass>(arRead);

assert(loadedObj);
assert(simpleObj->GetI() == loadedObj->GetI());
assert(simpleObj->GetStr().compare(loadedObj->GetStr()) == 0);

assert(loadedA);
assert(dynamic_cast<DerivedClassA *>(loadedA));
assert(inheritedA->GetI() == loadedA->GetI());
assert(inheritedA->GetStr().compare(loadedA->GetStr()) == 0);
assert(inheritedA->GetSomeIntern().compare(loadedA->GetSomeIntern()) == 0);

assert(loadedB);
assert(dynamic_cast<DerivedClassB *>(loadedB));
assert(inheritedB->GetI() == loadedB->GetI());
assert(inheritedB->GetStr().compare(loadedB->GetStr()) == 0);
assert(inheritedB->GetSomeIntern().compare(loadedB->GetSomeIntern()) == 0);

fclose(fp);

delete simpleObj;
delete inheritedA;
delete inheritedB;

delete loadedObj;
delete loadedA;
delete loadedB;

return 0;
}









share|improve this question
























  • What is the purpose of Cloneables? It is singleton map wrapper, but creates objects through Clonable::CreateObj which does not appear to do cloning. It would make sense in Factory which I guess is there for de-serialization to create empty/default objects before filling them, but you should really describe how is it supposed to work.
    – firda
    Sep 24 at 10:59















up vote
2
down vote

favorite
1












A couple of years ago I wrote a serialisation library for my project. By the time it worked well, but recently I thought it could be modernised. Back then I needed something that looks similar to Boost's serialiser, but also support polymorphism. I need to serialise and deserialise different types of objects from the same inheriting tree. However I didn't wanted to include Boost library just for this. Also I needed to support different sources besides FILE* (I only included just one here.)



I came across with this solution, tweaked a bit, wrote a couple of tests around it, and been using. It's spread across multiple files in my repo, and I do have a couple more tests for it using GTest.



My overall questions are




  • How can I use std some more to make it more compatible with its containers? I think about supporting std::map first

  • What other possible way are there to improve the way I define and store factories of the different objects?

  • The most important thing is for me is support more efficiently the serialisation of the parents of the class instances. At this point I only have a terrible solution that is I define a non-virtual _Serialize() method that takes care of it via override. Is there a solution to collect all the parents serialiser method more seamlessly and invoke them?


Here's the source code, that I had concatenated together and modified a bit to work single handed. I used clang on MacOS via Cmake to test it.



/*
Dynamics API
*/

#ifndef __DYNAMICS_H__
#define __DYNAMICS_H__

#include <string>
#include <map>


namespace Grafkit
{
class Clonables;

/**
*/
class Clonable
{
friend class Clonables;

public:
virtual ~Clonable() = default;
virtual Clonable *CreateObj() const = 0;
};

/**
*/
class Clonables
{
std::map<std::string, const Clonable *> m_clonables;

Clonables() {}

Clonables(const Clonables &) = delete;
Clonables &operator=(const Clonables &) = delete;

virtual ~Clonables()
{
for (auto it = m_clonables.begin(); it != m_clonables.end(); ++it)
{
const Clonable *clone = it->second;
delete clone;
}
m_clonables.clear();
}

public:
static Clonables &Instance()
{
static Clonables instance;
return instance;
}

void AddClonable(const char *CLASS_NAME, Clonable *clone)
{
std::string name = CLASS_NAME;

auto it = m_clonables.find(name);
if (it == m_clonables.end())
{
m_clonables[name] = clone;
}
}

const Clonable *Find(const char *CLASS_NAME)
{
std::string name = CLASS_NAME;
auto it = m_clonables.find(name);
if (it == m_clonables.end())
return nullptr;
const Clonable *clone = it->second;
return clone;
}

Clonable *Create(const char *CLASS_NAME)
{
const Clonable *clone = Find(CLASS_NAME);
if (clone)
return clone->CreateObj();

return nullptr;
}
};

class AddClonable
{
public:
AddClonable(const char *CLASS_NAME, Clonable *clone)
{
Clonables::Instance().AddClonable(CLASS_NAME, clone);
}
};

} // namespace Grafkit

#define CLONEABLE_DECL(CLASS_NAME)
public:
virtual Grafkit::Clonable *CreateObj() const override
{
return new CLASS_NAME();
}

#define CLONEABLE_FACTORY_DECL(CLASS_NAME)
CLONEABLE_DECL(CLASS_NAME)
public:
class Factory : public Grafkit::Clonable
{
public:
virtual Grafkit::Clonable *CreateObj() const
{
return new CLASS_NAME();
}
};

private:
static Grafkit::AddClonable _addClonableFactory;

#define CLONEABLE_FACTORY_IMPL(CLASS_NAME)
Grafkit::AddClonable CLASS_NAME::_addClonableFactory(#CLASS_NAME, new CLASS_NAME::Factory());

#define CLONEABLE_FACTORY_LOCAL_IMPL(CLASS_NAME)
Grafkit::AddClonable CLASS_NAME##_addClonableFactory(#CLASS_NAME, new CLASS_NAME::Factory());

#endif //__DYNAMICS_H__

/*
Persistence API
*/

#ifndef __PERSISTENCE_H__
#define __PERSISTENCE_H__

#include <cstdlib>
#include <vector>

// Alrady included above
//#include "dynamics.h"

namespace Grafkit
{
class Archive;

class Persistent : public Clonable
{
public:
Persistent() {}
virtual ~Persistent() {}

static Persistent *Load(Archive &ar);
template <class C>
static C *LoadT(Archive &ar) { return dynamic_cast<C *>(Load(ar)); }
void Store(Archive &ar);

protected:
virtual void Serialize(Archive &ar) = 0;

virtual std::string GetClazzName() const = 0;
virtual int GetVersion() const { return 0; }
};

class Archive
{
public:
explicit Archive(int IsStoring = 0);
virtual ~Archive();

/* IO API */
protected:
virtual void Write(const void *buffer, size_t length) = 0;
virtual void Read(void *buffer, size_t length) = 0;

size_t WriteString(const char *input);
size_t ReadString(char *&output);

public:
template <typename T>
void PersistField(T &t)
{
if (m_isStoring)
{
Write(&t, sizeof(T));
}
else
{
Read(&t, sizeof(T));
}
}

template <typename T>
void PersistVector(T *&v, uint32_t &count)
{
if (m_isStoring)
{
Write(&count, sizeof(count));
Write(v, sizeof(T) * count);
}
else
{
uint32_t readCount = 0;
Read(&readCount, sizeof(readCount));

void *p = malloc(sizeof(T) * readCount);
Read(p, sizeof(T) * readCount);

v = reinterpret_cast<T *>(p);

count = readCount;
}
}

template <typename T>
void PersistStdVector(std::vector<T> &v)
{
if (m_isStoring)
{
uint32_t u32count = v.size(); //clamp it down to 32 bit
Write(&u32count, sizeof(u32count));
void *p = v.data();
Write(p, sizeof(T) * u32count);
}
else
{
uint32_t count = 0;
Read(&count, sizeof(count));

void *p = malloc(sizeof(T) * count);
Read(p, sizeof(T) * count);

v.clear();
v.assign(static_cast<T *>(p), static_cast<T *>(p) + count);
}
}

void PersistString(const char *&str);
void PersistString(std::string &str);

void StoreObject(Persistent *object);
Persistent *LoadObject();

int IsStoring() const { return m_isStoring; }
void SetDirection(bool IsStoring) { m_isStoring = IsStoring; }

private:
int m_isStoring;
};
} // namespace Grafkit

// ------------------

#define PERSISTENT_DECL(CLASS_NAME, VERSION_NO)
public:
CLONEABLE_FACTORY_DECL(CLASS_NAME)
public:
std::string GetClazzName() const override
{
return #CLASS_NAME;
}
int GetVersion() const override
{
return VERSION_NO;
}

#define PERSISTENT_IMPL(CLASS_NAME)
CLONEABLE_FACTORY_IMPL(CLASS_NAME)

// ---

#define PERSIST_FIELD(AR, FIELD) (AR.PersistField<decltype(FIELD)>((FIELD)))
#define PERSIST_VECTOR(AR, VECTOR, COUNT) (AR.PersistVector<std::remove_pointer<decltype(VECTOR)>::type>(VECTOR, COUNT))
#define PERSIST_STD_VECTOR(AR, VECTOR) (AR.PersistStdVector<decltype(VECTOR)::value_type>(VECTOR))
#define PERSIST_STRING(AR, FIELD) (AR.PersistString((FIELD)))

#define _PERSIST_OBJECT(AR, TYPE, IN_FIELD, OUT_FIELD)
{
if (AR.IsStoring())
{
AR.StoreObject(dynamic_cast<Persistent *>((IN_FIELD)));
}
else
{
(OUT_FIELD) = dynamic_cast<TYPE>(AR.LoadObject());
}
}

#define PERSIST_OBJECT(AR, OBJECT) _PERSIST_OBJECT(AR, decltype(OBJECT), OBJECT, OBJECT)
#define PERSIST_REFOBJECT(AR, REF) _PERSIST_OBJECT(AR, decltype(REF.Get()), REF.Get(), REF)

#endif //__PERSISTENCE_H__

/*
Archive IO layer
*/

#ifndef __ARCHIVE_H__
#define __ARCHIVE_H__

// #include "persistence.h"

namespace Grafkit
{

class ArchiveFile : public Archive
{
public:
ArchiveFile(FILE *stream, bool IsStoring = false);
virtual ~ArchiveFile();

void Write(const void *buffer, size_t length) override;
void Read(void *buffer, size_t length) override;

private:
FILE *_stream;
};
}; // namespace Grafkit

#endif //__ARCHIVE_H__

/***************************************************
Impl
****************************************************/

/*
Persistent layer impl: persistent.cpp
*/

#include <vector>
#include <cassert>

// Already pasted above
//#include "persistence.h"
//#include "dynamics.h"

using namespace Grafkit;
using namespace std;

void Persistent::Store(Archive &ar)
{
string CLASS_NAME = this->GetClazzName();
uint8_t ver = this->GetVersion();

PERSIST_STRING(ar, CLASS_NAME);
PERSIST_FIELD(ar, ver);

this->Serialize(ar);
}

Persistent *Persistent::Load(Archive &ar)
{
string CLASS_NAME;
uint8_t ver = 0;
PERSIST_STRING(ar, CLASS_NAME);

Clonable *clone = Clonables::Instance().Create(CLASS_NAME.c_str());
assert(clone);

Persistent *obj = dynamic_cast<Persistent *>(clone);
assert(obj);

PERSIST_FIELD(ar, ver);
assert(ver == obj->GetVersion());

obj->Serialize(ar);
return obj;
}

/**
Archive
*/

Archive::Archive(int IsStoring) : m_isStoring(IsStoring)
{
}

Archive::~Archive()
{
}

size_t Archive::WriteString(const char *input)
{
uint16_t slen = strlen(input);
this->Write(&slen, sizeof(slen));
this->Write(input, slen + 1);
return slen;
}

size_t Archive::ReadString(char *&output)
{
uint16_t slen = 0;
this->Read(&slen, sizeof(slen));
output = new char[slen + 1];
this->Read(output, slen + 1);
return slen;
}

void Archive::PersistString(const char *&str)
{
if (m_isStoring)
{
WriteString(str);
}
else
{
char *in = nullptr;
ReadString(in);
str = in;
}
}

void Archive::PersistString(string &str)
{
if (m_isStoring)
{
WriteString(str.c_str());
}
else
{
char *in = nullptr;
ReadString(in);
str = in;
delete in;
}
}

void Archive::StoreObject(Persistent *object)
{
uint8_t isNotNull = object != nullptr;
PersistField(isNotNull);

if (isNotNull)
object->Store(*this);
}

Persistent *Archive::LoadObject()
{
uint8_t isNotNull = 0;
PersistField(isNotNull);

if (isNotNull)
return Persistent::Load(*this);
return nullptr;
}

/*
Archive IO impl: archive.cpp
*/

// Already included above
// #include "archive.h"
#include <cassert>

using namespace Grafkit;

ArchiveFile::ArchiveFile(FILE *stream, bool IsStoring) : Archive(IsStoring),
_stream(stream)
{
assert(_stream);
}

ArchiveFile::~ArchiveFile()
{
}

void ArchiveFile::Write(const void *buffer, size_t length)
{
assert(_stream);
fwrite(buffer, length, 1, this->_stream);
}

void ArchiveFile::Read(void *buffer, size_t length)
{
assert(_stream);
fread(buffer, length, 1, this->_stream);
}

/**
Test classes, Main
*/

class SimpleClass : public Grafkit::Persistent
{
public:
SimpleClass() : Persistent(), m_i(0)
{
}

void SetParams(const int i, const std::string str)
{
m_i = i;
m_str = str;
}

int GetI() const { return m_i; }
std::string GetStr() const { return m_str; }

PERSISTENT_DECL(SimpleClass, 1);

protected:
void Serialize(Archive &ar) override
{
PERSIST_FIELD(ar, m_i);
PERSIST_STRING(ar, m_str);
}

private:
int m_i;
std::string m_str;
};

class SimpleBaseClass : public Grafkit::Persistent
{
public:
SimpleBaseClass() : Persistent(), m_i(0) {}

void SetParams(const int i, const std::string str)
{
m_i = i;
m_str = str;
}

int GetI() const { return m_i; }
std::string GetStr() const { return m_str; }

virtual std::string GetSomeIntern() const = 0;

protected:
void _Serialize(Archive &ar)
{
PERSIST_FIELD(ar, m_i);
PERSIST_STRING(ar, m_str);
}

private:
int m_i;
std::string m_str;
};

class DerivedClassA : public SimpleBaseClass
{
public:
DerivedClassA() : SimpleBaseClass(), m_str1("This is derived class A") {}
virtual std::string GetSomeIntern() const override { return m_str1; }

PERSISTENT_DECL(DerivedClassA, 1);

protected:
void Serialize(Archive &ar) override
{
SimpleBaseClass::_Serialize(ar);
PERSIST_STRING(ar, m_str1);
}

private:
std::string m_str1;
};

class DerivedClassB : public SimpleBaseClass
{
public:
DerivedClassB() : SimpleBaseClass(), m_str2("This is derived class B") {}
virtual std::string GetSomeIntern() const override { return m_str2; }

PERSISTENT_DECL(DerivedClassB, 1);

protected:
void Serialize(Archive &ar) override
{
SimpleBaseClass::_Serialize(ar);
PERSIST_STRING(ar, m_str2);
}

private:
std::string m_str2;
};

#include <cstdio>

PERSISTENT_IMPL(SimpleClass);
PERSISTENT_IMPL(DerivedClassA);
PERSISTENT_IMPL(DerivedClassB);

int main()
{
FILE *fp;

// -- 1. simple class
// given
SimpleClass *simpleObj = new SimpleClass();
simpleObj->SetParams(42, "Hello Serializer");

SimpleBaseClass *inheritedA = new DerivedClassA();
SimpleBaseClass *inheritedB = new DerivedClassB();

inheritedA->SetParams(314, "Hello Serializer w/ inherited classes");
inheritedA->SetParams(216, "Hello Serializer GLEJD");

// when
fp = fopen("archive.bin", "wb'");
assert(fp);

ArchiveFile arWrite(fp, true);
simpleObj->Store(arWrite);
inheritedA->Store(arWrite);
inheritedB->Store(arWrite);

fflush(fp);
fclose(fp);
fp = nullptr;

// then
fp = fopen("archive.bin", "rb");
assert(fp);

ArchiveFile arRead(fp, false);
SimpleClass *loadedObj = Persistent::LoadT<SimpleClass>(arRead);
SimpleBaseClass *loadedA = Persistent::LoadT<SimpleBaseClass>(arRead);
SimpleBaseClass *loadedB = Persistent::LoadT<SimpleBaseClass>(arRead);

assert(loadedObj);
assert(simpleObj->GetI() == loadedObj->GetI());
assert(simpleObj->GetStr().compare(loadedObj->GetStr()) == 0);

assert(loadedA);
assert(dynamic_cast<DerivedClassA *>(loadedA));
assert(inheritedA->GetI() == loadedA->GetI());
assert(inheritedA->GetStr().compare(loadedA->GetStr()) == 0);
assert(inheritedA->GetSomeIntern().compare(loadedA->GetSomeIntern()) == 0);

assert(loadedB);
assert(dynamic_cast<DerivedClassB *>(loadedB));
assert(inheritedB->GetI() == loadedB->GetI());
assert(inheritedB->GetStr().compare(loadedB->GetStr()) == 0);
assert(inheritedB->GetSomeIntern().compare(loadedB->GetSomeIntern()) == 0);

fclose(fp);

delete simpleObj;
delete inheritedA;
delete inheritedB;

delete loadedObj;
delete loadedA;
delete loadedB;

return 0;
}









share|improve this question
























  • What is the purpose of Cloneables? It is singleton map wrapper, but creates objects through Clonable::CreateObj which does not appear to do cloning. It would make sense in Factory which I guess is there for de-serialization to create empty/default objects before filling them, but you should really describe how is it supposed to work.
    – firda
    Sep 24 at 10:59













up vote
2
down vote

favorite
1









up vote
2
down vote

favorite
1






1





A couple of years ago I wrote a serialisation library for my project. By the time it worked well, but recently I thought it could be modernised. Back then I needed something that looks similar to Boost's serialiser, but also support polymorphism. I need to serialise and deserialise different types of objects from the same inheriting tree. However I didn't wanted to include Boost library just for this. Also I needed to support different sources besides FILE* (I only included just one here.)



I came across with this solution, tweaked a bit, wrote a couple of tests around it, and been using. It's spread across multiple files in my repo, and I do have a couple more tests for it using GTest.



My overall questions are




  • How can I use std some more to make it more compatible with its containers? I think about supporting std::map first

  • What other possible way are there to improve the way I define and store factories of the different objects?

  • The most important thing is for me is support more efficiently the serialisation of the parents of the class instances. At this point I only have a terrible solution that is I define a non-virtual _Serialize() method that takes care of it via override. Is there a solution to collect all the parents serialiser method more seamlessly and invoke them?


Here's the source code, that I had concatenated together and modified a bit to work single handed. I used clang on MacOS via Cmake to test it.



/*
Dynamics API
*/

#ifndef __DYNAMICS_H__
#define __DYNAMICS_H__

#include <string>
#include <map>


namespace Grafkit
{
class Clonables;

/**
*/
class Clonable
{
friend class Clonables;

public:
virtual ~Clonable() = default;
virtual Clonable *CreateObj() const = 0;
};

/**
*/
class Clonables
{
std::map<std::string, const Clonable *> m_clonables;

Clonables() {}

Clonables(const Clonables &) = delete;
Clonables &operator=(const Clonables &) = delete;

virtual ~Clonables()
{
for (auto it = m_clonables.begin(); it != m_clonables.end(); ++it)
{
const Clonable *clone = it->second;
delete clone;
}
m_clonables.clear();
}

public:
static Clonables &Instance()
{
static Clonables instance;
return instance;
}

void AddClonable(const char *CLASS_NAME, Clonable *clone)
{
std::string name = CLASS_NAME;

auto it = m_clonables.find(name);
if (it == m_clonables.end())
{
m_clonables[name] = clone;
}
}

const Clonable *Find(const char *CLASS_NAME)
{
std::string name = CLASS_NAME;
auto it = m_clonables.find(name);
if (it == m_clonables.end())
return nullptr;
const Clonable *clone = it->second;
return clone;
}

Clonable *Create(const char *CLASS_NAME)
{
const Clonable *clone = Find(CLASS_NAME);
if (clone)
return clone->CreateObj();

return nullptr;
}
};

class AddClonable
{
public:
AddClonable(const char *CLASS_NAME, Clonable *clone)
{
Clonables::Instance().AddClonable(CLASS_NAME, clone);
}
};

} // namespace Grafkit

#define CLONEABLE_DECL(CLASS_NAME)
public:
virtual Grafkit::Clonable *CreateObj() const override
{
return new CLASS_NAME();
}

#define CLONEABLE_FACTORY_DECL(CLASS_NAME)
CLONEABLE_DECL(CLASS_NAME)
public:
class Factory : public Grafkit::Clonable
{
public:
virtual Grafkit::Clonable *CreateObj() const
{
return new CLASS_NAME();
}
};

private:
static Grafkit::AddClonable _addClonableFactory;

#define CLONEABLE_FACTORY_IMPL(CLASS_NAME)
Grafkit::AddClonable CLASS_NAME::_addClonableFactory(#CLASS_NAME, new CLASS_NAME::Factory());

#define CLONEABLE_FACTORY_LOCAL_IMPL(CLASS_NAME)
Grafkit::AddClonable CLASS_NAME##_addClonableFactory(#CLASS_NAME, new CLASS_NAME::Factory());

#endif //__DYNAMICS_H__

/*
Persistence API
*/

#ifndef __PERSISTENCE_H__
#define __PERSISTENCE_H__

#include <cstdlib>
#include <vector>

// Alrady included above
//#include "dynamics.h"

namespace Grafkit
{
class Archive;

class Persistent : public Clonable
{
public:
Persistent() {}
virtual ~Persistent() {}

static Persistent *Load(Archive &ar);
template <class C>
static C *LoadT(Archive &ar) { return dynamic_cast<C *>(Load(ar)); }
void Store(Archive &ar);

protected:
virtual void Serialize(Archive &ar) = 0;

virtual std::string GetClazzName() const = 0;
virtual int GetVersion() const { return 0; }
};

class Archive
{
public:
explicit Archive(int IsStoring = 0);
virtual ~Archive();

/* IO API */
protected:
virtual void Write(const void *buffer, size_t length) = 0;
virtual void Read(void *buffer, size_t length) = 0;

size_t WriteString(const char *input);
size_t ReadString(char *&output);

public:
template <typename T>
void PersistField(T &t)
{
if (m_isStoring)
{
Write(&t, sizeof(T));
}
else
{
Read(&t, sizeof(T));
}
}

template <typename T>
void PersistVector(T *&v, uint32_t &count)
{
if (m_isStoring)
{
Write(&count, sizeof(count));
Write(v, sizeof(T) * count);
}
else
{
uint32_t readCount = 0;
Read(&readCount, sizeof(readCount));

void *p = malloc(sizeof(T) * readCount);
Read(p, sizeof(T) * readCount);

v = reinterpret_cast<T *>(p);

count = readCount;
}
}

template <typename T>
void PersistStdVector(std::vector<T> &v)
{
if (m_isStoring)
{
uint32_t u32count = v.size(); //clamp it down to 32 bit
Write(&u32count, sizeof(u32count));
void *p = v.data();
Write(p, sizeof(T) * u32count);
}
else
{
uint32_t count = 0;
Read(&count, sizeof(count));

void *p = malloc(sizeof(T) * count);
Read(p, sizeof(T) * count);

v.clear();
v.assign(static_cast<T *>(p), static_cast<T *>(p) + count);
}
}

void PersistString(const char *&str);
void PersistString(std::string &str);

void StoreObject(Persistent *object);
Persistent *LoadObject();

int IsStoring() const { return m_isStoring; }
void SetDirection(bool IsStoring) { m_isStoring = IsStoring; }

private:
int m_isStoring;
};
} // namespace Grafkit

// ------------------

#define PERSISTENT_DECL(CLASS_NAME, VERSION_NO)
public:
CLONEABLE_FACTORY_DECL(CLASS_NAME)
public:
std::string GetClazzName() const override
{
return #CLASS_NAME;
}
int GetVersion() const override
{
return VERSION_NO;
}

#define PERSISTENT_IMPL(CLASS_NAME)
CLONEABLE_FACTORY_IMPL(CLASS_NAME)

// ---

#define PERSIST_FIELD(AR, FIELD) (AR.PersistField<decltype(FIELD)>((FIELD)))
#define PERSIST_VECTOR(AR, VECTOR, COUNT) (AR.PersistVector<std::remove_pointer<decltype(VECTOR)>::type>(VECTOR, COUNT))
#define PERSIST_STD_VECTOR(AR, VECTOR) (AR.PersistStdVector<decltype(VECTOR)::value_type>(VECTOR))
#define PERSIST_STRING(AR, FIELD) (AR.PersistString((FIELD)))

#define _PERSIST_OBJECT(AR, TYPE, IN_FIELD, OUT_FIELD)
{
if (AR.IsStoring())
{
AR.StoreObject(dynamic_cast<Persistent *>((IN_FIELD)));
}
else
{
(OUT_FIELD) = dynamic_cast<TYPE>(AR.LoadObject());
}
}

#define PERSIST_OBJECT(AR, OBJECT) _PERSIST_OBJECT(AR, decltype(OBJECT), OBJECT, OBJECT)
#define PERSIST_REFOBJECT(AR, REF) _PERSIST_OBJECT(AR, decltype(REF.Get()), REF.Get(), REF)

#endif //__PERSISTENCE_H__

/*
Archive IO layer
*/

#ifndef __ARCHIVE_H__
#define __ARCHIVE_H__

// #include "persistence.h"

namespace Grafkit
{

class ArchiveFile : public Archive
{
public:
ArchiveFile(FILE *stream, bool IsStoring = false);
virtual ~ArchiveFile();

void Write(const void *buffer, size_t length) override;
void Read(void *buffer, size_t length) override;

private:
FILE *_stream;
};
}; // namespace Grafkit

#endif //__ARCHIVE_H__

/***************************************************
Impl
****************************************************/

/*
Persistent layer impl: persistent.cpp
*/

#include <vector>
#include <cassert>

// Already pasted above
//#include "persistence.h"
//#include "dynamics.h"

using namespace Grafkit;
using namespace std;

void Persistent::Store(Archive &ar)
{
string CLASS_NAME = this->GetClazzName();
uint8_t ver = this->GetVersion();

PERSIST_STRING(ar, CLASS_NAME);
PERSIST_FIELD(ar, ver);

this->Serialize(ar);
}

Persistent *Persistent::Load(Archive &ar)
{
string CLASS_NAME;
uint8_t ver = 0;
PERSIST_STRING(ar, CLASS_NAME);

Clonable *clone = Clonables::Instance().Create(CLASS_NAME.c_str());
assert(clone);

Persistent *obj = dynamic_cast<Persistent *>(clone);
assert(obj);

PERSIST_FIELD(ar, ver);
assert(ver == obj->GetVersion());

obj->Serialize(ar);
return obj;
}

/**
Archive
*/

Archive::Archive(int IsStoring) : m_isStoring(IsStoring)
{
}

Archive::~Archive()
{
}

size_t Archive::WriteString(const char *input)
{
uint16_t slen = strlen(input);
this->Write(&slen, sizeof(slen));
this->Write(input, slen + 1);
return slen;
}

size_t Archive::ReadString(char *&output)
{
uint16_t slen = 0;
this->Read(&slen, sizeof(slen));
output = new char[slen + 1];
this->Read(output, slen + 1);
return slen;
}

void Archive::PersistString(const char *&str)
{
if (m_isStoring)
{
WriteString(str);
}
else
{
char *in = nullptr;
ReadString(in);
str = in;
}
}

void Archive::PersistString(string &str)
{
if (m_isStoring)
{
WriteString(str.c_str());
}
else
{
char *in = nullptr;
ReadString(in);
str = in;
delete in;
}
}

void Archive::StoreObject(Persistent *object)
{
uint8_t isNotNull = object != nullptr;
PersistField(isNotNull);

if (isNotNull)
object->Store(*this);
}

Persistent *Archive::LoadObject()
{
uint8_t isNotNull = 0;
PersistField(isNotNull);

if (isNotNull)
return Persistent::Load(*this);
return nullptr;
}

/*
Archive IO impl: archive.cpp
*/

// Already included above
// #include "archive.h"
#include <cassert>

using namespace Grafkit;

ArchiveFile::ArchiveFile(FILE *stream, bool IsStoring) : Archive(IsStoring),
_stream(stream)
{
assert(_stream);
}

ArchiveFile::~ArchiveFile()
{
}

void ArchiveFile::Write(const void *buffer, size_t length)
{
assert(_stream);
fwrite(buffer, length, 1, this->_stream);
}

void ArchiveFile::Read(void *buffer, size_t length)
{
assert(_stream);
fread(buffer, length, 1, this->_stream);
}

/**
Test classes, Main
*/

class SimpleClass : public Grafkit::Persistent
{
public:
SimpleClass() : Persistent(), m_i(0)
{
}

void SetParams(const int i, const std::string str)
{
m_i = i;
m_str = str;
}

int GetI() const { return m_i; }
std::string GetStr() const { return m_str; }

PERSISTENT_DECL(SimpleClass, 1);

protected:
void Serialize(Archive &ar) override
{
PERSIST_FIELD(ar, m_i);
PERSIST_STRING(ar, m_str);
}

private:
int m_i;
std::string m_str;
};

class SimpleBaseClass : public Grafkit::Persistent
{
public:
SimpleBaseClass() : Persistent(), m_i(0) {}

void SetParams(const int i, const std::string str)
{
m_i = i;
m_str = str;
}

int GetI() const { return m_i; }
std::string GetStr() const { return m_str; }

virtual std::string GetSomeIntern() const = 0;

protected:
void _Serialize(Archive &ar)
{
PERSIST_FIELD(ar, m_i);
PERSIST_STRING(ar, m_str);
}

private:
int m_i;
std::string m_str;
};

class DerivedClassA : public SimpleBaseClass
{
public:
DerivedClassA() : SimpleBaseClass(), m_str1("This is derived class A") {}
virtual std::string GetSomeIntern() const override { return m_str1; }

PERSISTENT_DECL(DerivedClassA, 1);

protected:
void Serialize(Archive &ar) override
{
SimpleBaseClass::_Serialize(ar);
PERSIST_STRING(ar, m_str1);
}

private:
std::string m_str1;
};

class DerivedClassB : public SimpleBaseClass
{
public:
DerivedClassB() : SimpleBaseClass(), m_str2("This is derived class B") {}
virtual std::string GetSomeIntern() const override { return m_str2; }

PERSISTENT_DECL(DerivedClassB, 1);

protected:
void Serialize(Archive &ar) override
{
SimpleBaseClass::_Serialize(ar);
PERSIST_STRING(ar, m_str2);
}

private:
std::string m_str2;
};

#include <cstdio>

PERSISTENT_IMPL(SimpleClass);
PERSISTENT_IMPL(DerivedClassA);
PERSISTENT_IMPL(DerivedClassB);

int main()
{
FILE *fp;

// -- 1. simple class
// given
SimpleClass *simpleObj = new SimpleClass();
simpleObj->SetParams(42, "Hello Serializer");

SimpleBaseClass *inheritedA = new DerivedClassA();
SimpleBaseClass *inheritedB = new DerivedClassB();

inheritedA->SetParams(314, "Hello Serializer w/ inherited classes");
inheritedA->SetParams(216, "Hello Serializer GLEJD");

// when
fp = fopen("archive.bin", "wb'");
assert(fp);

ArchiveFile arWrite(fp, true);
simpleObj->Store(arWrite);
inheritedA->Store(arWrite);
inheritedB->Store(arWrite);

fflush(fp);
fclose(fp);
fp = nullptr;

// then
fp = fopen("archive.bin", "rb");
assert(fp);

ArchiveFile arRead(fp, false);
SimpleClass *loadedObj = Persistent::LoadT<SimpleClass>(arRead);
SimpleBaseClass *loadedA = Persistent::LoadT<SimpleBaseClass>(arRead);
SimpleBaseClass *loadedB = Persistent::LoadT<SimpleBaseClass>(arRead);

assert(loadedObj);
assert(simpleObj->GetI() == loadedObj->GetI());
assert(simpleObj->GetStr().compare(loadedObj->GetStr()) == 0);

assert(loadedA);
assert(dynamic_cast<DerivedClassA *>(loadedA));
assert(inheritedA->GetI() == loadedA->GetI());
assert(inheritedA->GetStr().compare(loadedA->GetStr()) == 0);
assert(inheritedA->GetSomeIntern().compare(loadedA->GetSomeIntern()) == 0);

assert(loadedB);
assert(dynamic_cast<DerivedClassB *>(loadedB));
assert(inheritedB->GetI() == loadedB->GetI());
assert(inheritedB->GetStr().compare(loadedB->GetStr()) == 0);
assert(inheritedB->GetSomeIntern().compare(loadedB->GetSomeIntern()) == 0);

fclose(fp);

delete simpleObj;
delete inheritedA;
delete inheritedB;

delete loadedObj;
delete loadedA;
delete loadedB;

return 0;
}









share|improve this question















A couple of years ago I wrote a serialisation library for my project. By the time it worked well, but recently I thought it could be modernised. Back then I needed something that looks similar to Boost's serialiser, but also support polymorphism. I need to serialise and deserialise different types of objects from the same inheriting tree. However I didn't wanted to include Boost library just for this. Also I needed to support different sources besides FILE* (I only included just one here.)



I came across with this solution, tweaked a bit, wrote a couple of tests around it, and been using. It's spread across multiple files in my repo, and I do have a couple more tests for it using GTest.



My overall questions are




  • How can I use std some more to make it more compatible with its containers? I think about supporting std::map first

  • What other possible way are there to improve the way I define and store factories of the different objects?

  • The most important thing is for me is support more efficiently the serialisation of the parents of the class instances. At this point I only have a terrible solution that is I define a non-virtual _Serialize() method that takes care of it via override. Is there a solution to collect all the parents serialiser method more seamlessly and invoke them?


Here's the source code, that I had concatenated together and modified a bit to work single handed. I used clang on MacOS via Cmake to test it.



/*
Dynamics API
*/

#ifndef __DYNAMICS_H__
#define __DYNAMICS_H__

#include <string>
#include <map>


namespace Grafkit
{
class Clonables;

/**
*/
class Clonable
{
friend class Clonables;

public:
virtual ~Clonable() = default;
virtual Clonable *CreateObj() const = 0;
};

/**
*/
class Clonables
{
std::map<std::string, const Clonable *> m_clonables;

Clonables() {}

Clonables(const Clonables &) = delete;
Clonables &operator=(const Clonables &) = delete;

virtual ~Clonables()
{
for (auto it = m_clonables.begin(); it != m_clonables.end(); ++it)
{
const Clonable *clone = it->second;
delete clone;
}
m_clonables.clear();
}

public:
static Clonables &Instance()
{
static Clonables instance;
return instance;
}

void AddClonable(const char *CLASS_NAME, Clonable *clone)
{
std::string name = CLASS_NAME;

auto it = m_clonables.find(name);
if (it == m_clonables.end())
{
m_clonables[name] = clone;
}
}

const Clonable *Find(const char *CLASS_NAME)
{
std::string name = CLASS_NAME;
auto it = m_clonables.find(name);
if (it == m_clonables.end())
return nullptr;
const Clonable *clone = it->second;
return clone;
}

Clonable *Create(const char *CLASS_NAME)
{
const Clonable *clone = Find(CLASS_NAME);
if (clone)
return clone->CreateObj();

return nullptr;
}
};

class AddClonable
{
public:
AddClonable(const char *CLASS_NAME, Clonable *clone)
{
Clonables::Instance().AddClonable(CLASS_NAME, clone);
}
};

} // namespace Grafkit

#define CLONEABLE_DECL(CLASS_NAME)
public:
virtual Grafkit::Clonable *CreateObj() const override
{
return new CLASS_NAME();
}

#define CLONEABLE_FACTORY_DECL(CLASS_NAME)
CLONEABLE_DECL(CLASS_NAME)
public:
class Factory : public Grafkit::Clonable
{
public:
virtual Grafkit::Clonable *CreateObj() const
{
return new CLASS_NAME();
}
};

private:
static Grafkit::AddClonable _addClonableFactory;

#define CLONEABLE_FACTORY_IMPL(CLASS_NAME)
Grafkit::AddClonable CLASS_NAME::_addClonableFactory(#CLASS_NAME, new CLASS_NAME::Factory());

#define CLONEABLE_FACTORY_LOCAL_IMPL(CLASS_NAME)
Grafkit::AddClonable CLASS_NAME##_addClonableFactory(#CLASS_NAME, new CLASS_NAME::Factory());

#endif //__DYNAMICS_H__

/*
Persistence API
*/

#ifndef __PERSISTENCE_H__
#define __PERSISTENCE_H__

#include <cstdlib>
#include <vector>

// Alrady included above
//#include "dynamics.h"

namespace Grafkit
{
class Archive;

class Persistent : public Clonable
{
public:
Persistent() {}
virtual ~Persistent() {}

static Persistent *Load(Archive &ar);
template <class C>
static C *LoadT(Archive &ar) { return dynamic_cast<C *>(Load(ar)); }
void Store(Archive &ar);

protected:
virtual void Serialize(Archive &ar) = 0;

virtual std::string GetClazzName() const = 0;
virtual int GetVersion() const { return 0; }
};

class Archive
{
public:
explicit Archive(int IsStoring = 0);
virtual ~Archive();

/* IO API */
protected:
virtual void Write(const void *buffer, size_t length) = 0;
virtual void Read(void *buffer, size_t length) = 0;

size_t WriteString(const char *input);
size_t ReadString(char *&output);

public:
template <typename T>
void PersistField(T &t)
{
if (m_isStoring)
{
Write(&t, sizeof(T));
}
else
{
Read(&t, sizeof(T));
}
}

template <typename T>
void PersistVector(T *&v, uint32_t &count)
{
if (m_isStoring)
{
Write(&count, sizeof(count));
Write(v, sizeof(T) * count);
}
else
{
uint32_t readCount = 0;
Read(&readCount, sizeof(readCount));

void *p = malloc(sizeof(T) * readCount);
Read(p, sizeof(T) * readCount);

v = reinterpret_cast<T *>(p);

count = readCount;
}
}

template <typename T>
void PersistStdVector(std::vector<T> &v)
{
if (m_isStoring)
{
uint32_t u32count = v.size(); //clamp it down to 32 bit
Write(&u32count, sizeof(u32count));
void *p = v.data();
Write(p, sizeof(T) * u32count);
}
else
{
uint32_t count = 0;
Read(&count, sizeof(count));

void *p = malloc(sizeof(T) * count);
Read(p, sizeof(T) * count);

v.clear();
v.assign(static_cast<T *>(p), static_cast<T *>(p) + count);
}
}

void PersistString(const char *&str);
void PersistString(std::string &str);

void StoreObject(Persistent *object);
Persistent *LoadObject();

int IsStoring() const { return m_isStoring; }
void SetDirection(bool IsStoring) { m_isStoring = IsStoring; }

private:
int m_isStoring;
};
} // namespace Grafkit

// ------------------

#define PERSISTENT_DECL(CLASS_NAME, VERSION_NO)
public:
CLONEABLE_FACTORY_DECL(CLASS_NAME)
public:
std::string GetClazzName() const override
{
return #CLASS_NAME;
}
int GetVersion() const override
{
return VERSION_NO;
}

#define PERSISTENT_IMPL(CLASS_NAME)
CLONEABLE_FACTORY_IMPL(CLASS_NAME)

// ---

#define PERSIST_FIELD(AR, FIELD) (AR.PersistField<decltype(FIELD)>((FIELD)))
#define PERSIST_VECTOR(AR, VECTOR, COUNT) (AR.PersistVector<std::remove_pointer<decltype(VECTOR)>::type>(VECTOR, COUNT))
#define PERSIST_STD_VECTOR(AR, VECTOR) (AR.PersistStdVector<decltype(VECTOR)::value_type>(VECTOR))
#define PERSIST_STRING(AR, FIELD) (AR.PersistString((FIELD)))

#define _PERSIST_OBJECT(AR, TYPE, IN_FIELD, OUT_FIELD)
{
if (AR.IsStoring())
{
AR.StoreObject(dynamic_cast<Persistent *>((IN_FIELD)));
}
else
{
(OUT_FIELD) = dynamic_cast<TYPE>(AR.LoadObject());
}
}

#define PERSIST_OBJECT(AR, OBJECT) _PERSIST_OBJECT(AR, decltype(OBJECT), OBJECT, OBJECT)
#define PERSIST_REFOBJECT(AR, REF) _PERSIST_OBJECT(AR, decltype(REF.Get()), REF.Get(), REF)

#endif //__PERSISTENCE_H__

/*
Archive IO layer
*/

#ifndef __ARCHIVE_H__
#define __ARCHIVE_H__

// #include "persistence.h"

namespace Grafkit
{

class ArchiveFile : public Archive
{
public:
ArchiveFile(FILE *stream, bool IsStoring = false);
virtual ~ArchiveFile();

void Write(const void *buffer, size_t length) override;
void Read(void *buffer, size_t length) override;

private:
FILE *_stream;
};
}; // namespace Grafkit

#endif //__ARCHIVE_H__

/***************************************************
Impl
****************************************************/

/*
Persistent layer impl: persistent.cpp
*/

#include <vector>
#include <cassert>

// Already pasted above
//#include "persistence.h"
//#include "dynamics.h"

using namespace Grafkit;
using namespace std;

void Persistent::Store(Archive &ar)
{
string CLASS_NAME = this->GetClazzName();
uint8_t ver = this->GetVersion();

PERSIST_STRING(ar, CLASS_NAME);
PERSIST_FIELD(ar, ver);

this->Serialize(ar);
}

Persistent *Persistent::Load(Archive &ar)
{
string CLASS_NAME;
uint8_t ver = 0;
PERSIST_STRING(ar, CLASS_NAME);

Clonable *clone = Clonables::Instance().Create(CLASS_NAME.c_str());
assert(clone);

Persistent *obj = dynamic_cast<Persistent *>(clone);
assert(obj);

PERSIST_FIELD(ar, ver);
assert(ver == obj->GetVersion());

obj->Serialize(ar);
return obj;
}

/**
Archive
*/

Archive::Archive(int IsStoring) : m_isStoring(IsStoring)
{
}

Archive::~Archive()
{
}

size_t Archive::WriteString(const char *input)
{
uint16_t slen = strlen(input);
this->Write(&slen, sizeof(slen));
this->Write(input, slen + 1);
return slen;
}

size_t Archive::ReadString(char *&output)
{
uint16_t slen = 0;
this->Read(&slen, sizeof(slen));
output = new char[slen + 1];
this->Read(output, slen + 1);
return slen;
}

void Archive::PersistString(const char *&str)
{
if (m_isStoring)
{
WriteString(str);
}
else
{
char *in = nullptr;
ReadString(in);
str = in;
}
}

void Archive::PersistString(string &str)
{
if (m_isStoring)
{
WriteString(str.c_str());
}
else
{
char *in = nullptr;
ReadString(in);
str = in;
delete in;
}
}

void Archive::StoreObject(Persistent *object)
{
uint8_t isNotNull = object != nullptr;
PersistField(isNotNull);

if (isNotNull)
object->Store(*this);
}

Persistent *Archive::LoadObject()
{
uint8_t isNotNull = 0;
PersistField(isNotNull);

if (isNotNull)
return Persistent::Load(*this);
return nullptr;
}

/*
Archive IO impl: archive.cpp
*/

// Already included above
// #include "archive.h"
#include <cassert>

using namespace Grafkit;

ArchiveFile::ArchiveFile(FILE *stream, bool IsStoring) : Archive(IsStoring),
_stream(stream)
{
assert(_stream);
}

ArchiveFile::~ArchiveFile()
{
}

void ArchiveFile::Write(const void *buffer, size_t length)
{
assert(_stream);
fwrite(buffer, length, 1, this->_stream);
}

void ArchiveFile::Read(void *buffer, size_t length)
{
assert(_stream);
fread(buffer, length, 1, this->_stream);
}

/**
Test classes, Main
*/

class SimpleClass : public Grafkit::Persistent
{
public:
SimpleClass() : Persistent(), m_i(0)
{
}

void SetParams(const int i, const std::string str)
{
m_i = i;
m_str = str;
}

int GetI() const { return m_i; }
std::string GetStr() const { return m_str; }

PERSISTENT_DECL(SimpleClass, 1);

protected:
void Serialize(Archive &ar) override
{
PERSIST_FIELD(ar, m_i);
PERSIST_STRING(ar, m_str);
}

private:
int m_i;
std::string m_str;
};

class SimpleBaseClass : public Grafkit::Persistent
{
public:
SimpleBaseClass() : Persistent(), m_i(0) {}

void SetParams(const int i, const std::string str)
{
m_i = i;
m_str = str;
}

int GetI() const { return m_i; }
std::string GetStr() const { return m_str; }

virtual std::string GetSomeIntern() const = 0;

protected:
void _Serialize(Archive &ar)
{
PERSIST_FIELD(ar, m_i);
PERSIST_STRING(ar, m_str);
}

private:
int m_i;
std::string m_str;
};

class DerivedClassA : public SimpleBaseClass
{
public:
DerivedClassA() : SimpleBaseClass(), m_str1("This is derived class A") {}
virtual std::string GetSomeIntern() const override { return m_str1; }

PERSISTENT_DECL(DerivedClassA, 1);

protected:
void Serialize(Archive &ar) override
{
SimpleBaseClass::_Serialize(ar);
PERSIST_STRING(ar, m_str1);
}

private:
std::string m_str1;
};

class DerivedClassB : public SimpleBaseClass
{
public:
DerivedClassB() : SimpleBaseClass(), m_str2("This is derived class B") {}
virtual std::string GetSomeIntern() const override { return m_str2; }

PERSISTENT_DECL(DerivedClassB, 1);

protected:
void Serialize(Archive &ar) override
{
SimpleBaseClass::_Serialize(ar);
PERSIST_STRING(ar, m_str2);
}

private:
std::string m_str2;
};

#include <cstdio>

PERSISTENT_IMPL(SimpleClass);
PERSISTENT_IMPL(DerivedClassA);
PERSISTENT_IMPL(DerivedClassB);

int main()
{
FILE *fp;

// -- 1. simple class
// given
SimpleClass *simpleObj = new SimpleClass();
simpleObj->SetParams(42, "Hello Serializer");

SimpleBaseClass *inheritedA = new DerivedClassA();
SimpleBaseClass *inheritedB = new DerivedClassB();

inheritedA->SetParams(314, "Hello Serializer w/ inherited classes");
inheritedA->SetParams(216, "Hello Serializer GLEJD");

// when
fp = fopen("archive.bin", "wb'");
assert(fp);

ArchiveFile arWrite(fp, true);
simpleObj->Store(arWrite);
inheritedA->Store(arWrite);
inheritedB->Store(arWrite);

fflush(fp);
fclose(fp);
fp = nullptr;

// then
fp = fopen("archive.bin", "rb");
assert(fp);

ArchiveFile arRead(fp, false);
SimpleClass *loadedObj = Persistent::LoadT<SimpleClass>(arRead);
SimpleBaseClass *loadedA = Persistent::LoadT<SimpleBaseClass>(arRead);
SimpleBaseClass *loadedB = Persistent::LoadT<SimpleBaseClass>(arRead);

assert(loadedObj);
assert(simpleObj->GetI() == loadedObj->GetI());
assert(simpleObj->GetStr().compare(loadedObj->GetStr()) == 0);

assert(loadedA);
assert(dynamic_cast<DerivedClassA *>(loadedA));
assert(inheritedA->GetI() == loadedA->GetI());
assert(inheritedA->GetStr().compare(loadedA->GetStr()) == 0);
assert(inheritedA->GetSomeIntern().compare(loadedA->GetSomeIntern()) == 0);

assert(loadedB);
assert(dynamic_cast<DerivedClassB *>(loadedB));
assert(inheritedB->GetI() == loadedB->GetI());
assert(inheritedB->GetStr().compare(loadedB->GetStr()) == 0);
assert(inheritedB->GetSomeIntern().compare(loadedB->GetSomeIntern()) == 0);

fclose(fp);

delete simpleObj;
delete inheritedA;
delete inheritedB;

delete loadedObj;
delete loadedA;
delete loadedB;

return 0;
}






c++ object-oriented c++14 serialization






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 21 at 16:28









yuri

3,50021033




3,50021033










asked Sep 21 at 11:45









Caiwan

533




533












  • What is the purpose of Cloneables? It is singleton map wrapper, but creates objects through Clonable::CreateObj which does not appear to do cloning. It would make sense in Factory which I guess is there for de-serialization to create empty/default objects before filling them, but you should really describe how is it supposed to work.
    – firda
    Sep 24 at 10:59


















  • What is the purpose of Cloneables? It is singleton map wrapper, but creates objects through Clonable::CreateObj which does not appear to do cloning. It would make sense in Factory which I guess is there for de-serialization to create empty/default objects before filling them, but you should really describe how is it supposed to work.
    – firda
    Sep 24 at 10:59
















What is the purpose of Cloneables? It is singleton map wrapper, but creates objects through Clonable::CreateObj which does not appear to do cloning. It would make sense in Factory which I guess is there for de-serialization to create empty/default objects before filling them, but you should really describe how is it supposed to work.
– firda
Sep 24 at 10:59




What is the purpose of Cloneables? It is singleton map wrapper, but creates objects through Clonable::CreateObj which does not appear to do cloning. It would make sense in Factory which I guess is there for de-serialization to create empty/default objects before filling them, but you should really describe how is it supposed to work.
– firda
Sep 24 at 10:59










1 Answer
1






active

oldest

votes

















up vote
0
down vote













I started reading.



IMO Find should be private and const.



Once you have done this you can replace the map
with a map of std::string, unique_ptr.



Doing so you get rid of the destructor of Clonables.



Does this make sense?






share|improve this answer

















  • 1




    It would make sense if you elaborated on object ownership (and the lack of it in the code provided) and provide alternative solution using unique_ptr. Find could still be useful, but should not return raw pointer (describe why). Solution is up to you, but it should probably take inspiration in map::find as Clonables is essentialy singleton wrapper of a map.
    – firda
    Sep 24 at 10:34











Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204123%2fcustom-serialiser-with-object-factory%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
0
down vote













I started reading.



IMO Find should be private and const.



Once you have done this you can replace the map
with a map of std::string, unique_ptr.



Doing so you get rid of the destructor of Clonables.



Does this make sense?






share|improve this answer

















  • 1




    It would make sense if you elaborated on object ownership (and the lack of it in the code provided) and provide alternative solution using unique_ptr. Find could still be useful, but should not return raw pointer (describe why). Solution is up to you, but it should probably take inspiration in map::find as Clonables is essentialy singleton wrapper of a map.
    – firda
    Sep 24 at 10:34















up vote
0
down vote













I started reading.



IMO Find should be private and const.



Once you have done this you can replace the map
with a map of std::string, unique_ptr.



Doing so you get rid of the destructor of Clonables.



Does this make sense?






share|improve this answer

















  • 1




    It would make sense if you elaborated on object ownership (and the lack of it in the code provided) and provide alternative solution using unique_ptr. Find could still be useful, but should not return raw pointer (describe why). Solution is up to you, but it should probably take inspiration in map::find as Clonables is essentialy singleton wrapper of a map.
    – firda
    Sep 24 at 10:34













up vote
0
down vote










up vote
0
down vote









I started reading.



IMO Find should be private and const.



Once you have done this you can replace the map
with a map of std::string, unique_ptr.



Doing so you get rid of the destructor of Clonables.



Does this make sense?






share|improve this answer












I started reading.



IMO Find should be private and const.



Once you have done this you can replace the map
with a map of std::string, unique_ptr.



Doing so you get rid of the destructor of Clonables.



Does this make sense?







share|improve this answer












share|improve this answer



share|improve this answer










answered Sep 21 at 14:32









jimifiki

1314




1314








  • 1




    It would make sense if you elaborated on object ownership (and the lack of it in the code provided) and provide alternative solution using unique_ptr. Find could still be useful, but should not return raw pointer (describe why). Solution is up to you, but it should probably take inspiration in map::find as Clonables is essentialy singleton wrapper of a map.
    – firda
    Sep 24 at 10:34














  • 1




    It would make sense if you elaborated on object ownership (and the lack of it in the code provided) and provide alternative solution using unique_ptr. Find could still be useful, but should not return raw pointer (describe why). Solution is up to you, but it should probably take inspiration in map::find as Clonables is essentialy singleton wrapper of a map.
    – firda
    Sep 24 at 10:34








1




1




It would make sense if you elaborated on object ownership (and the lack of it in the code provided) and provide alternative solution using unique_ptr. Find could still be useful, but should not return raw pointer (describe why). Solution is up to you, but it should probably take inspiration in map::find as Clonables is essentialy singleton wrapper of a map.
– firda
Sep 24 at 10:34




It would make sense if you elaborated on object ownership (and the lack of it in the code provided) and provide alternative solution using unique_ptr. Find could still be useful, but should not return raw pointer (describe why). Solution is up to you, but it should probably take inspiration in map::find as Clonables is essentialy singleton wrapper of a map.
– firda
Sep 24 at 10:34


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204123%2fcustom-serialiser-with-object-factory%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Morgemoulin

Scott Moir

Souastre