initial commit

This commit is contained in:
Anselme 2017-01-19 12:43:51 +01:00
commit 5d9bd6de45
9 changed files with 939 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.user
build*

14
CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
project(SparrowSerializer)
cmake_minimum_required(VERSION 2.8)
# choose source file
file(GLOB LIB_SRC_LIST src/property.cpp src/serializable.cpp src/serializationmanager.cpp)
file(GLOB LIB_HEAD_LIST src/*.h)
set(EXEC_SRC_LIST src/main.cpp)
#set compilation option
set(IS_LIBRARY True)
set(CMAKE_TEMPLATE_PATH "../CMakeTemplate")
include(${CMAKE_TEMPLATE_PATH}/template.cmake)

74
src/main.cpp Normal file
View File

@ -0,0 +1,74 @@
#include <iostream>
#include "serializationmanager.h"
#include "serializable.h"
#include <sstream>
#include <fstream>
class Foo : public Serializable
{
public:
INT (myInt)
STRING (myString)
VECTOR_FLOAT(myFloatArray)
VEC3 (myVec3)
REFERENCE (Foo, myFoo)
SERIALIZABLE(Foo,
CAST(myInt),
CAST(myString),
CAST(myFloatArray),
CAST(myVec3),
CAST(myFoo))
Foo()
{
myInt = 3;
myString = "plop";
myFloatArray.push_back(1.5);
myFloatArray.push_back(1.3);
myFloatArray.push_back(2.5);
myFloatArray.push_back(6.2);
myVec3 = glm::vec3(0.1, 0, 5.4);
myFoo = this;
}
};
INIT_SERIALIZABLE(Foo)
int main()
{
// init
Foo* f1 = new Foo();
f1->myString = "Fo1";
Foo* f2 = new Foo();
f2->myString = "Fo2";
f1->myFloatArray.clear();
f2->myVec3 = glm::vec3(1, 2, 3);
f1->myFoo = f2;
f2->myFoo = nullptr;
// serialization
ObjectSaver saver;
saver.addObject(f1);
std::fstream outFile;
outFile.open("test.bin", std::ios_base::out);
saver.saveBinary(outFile);
outFile.close();
ObjectLoader loader;
std::fstream inFile;
inFile.open("test.bin", std::ios_base::in);
loader.loadBinary(inFile);
inFile.close();
// print result
const std::vector<Foo*> fooVec = loader.getObjects<Foo>();
ObjectSaver printer;
for(Foo* f : fooVec)
printer.addObject(f);
printer.saveAscii(std::cout);
return 0;
}

145
src/property.cpp Normal file
View File

@ -0,0 +1,145 @@
#include "property.h"
#include "serializable.h"
// INTEGER PROPERTY
template <> std::ostream& Property<int>::saveBinary(std::ostream& os) { return os.write((char*)&value, sizeof(int)); }
template <> std::ostream& Property<int>::saveAscii(std::ostream& os) { return os << value; }
template <> std::istream& Property<int>::loadBinary(std::istream& is) { return is.read((char*)&value, sizeof(int)); }
template <> std::istream& Property<int>::loadAscii(std::istream& is) { return is >> value; }
template <> AbstractProperty::PropertyType Property<int>::getStaticPropertyType() { return AbstractProperty::INTEGER; }
// FLOAT PROPERTY
template <> std::ostream& Property<float>::saveBinary(std::ostream& os) { return os.write((char*)&value, sizeof(float)); }
template <> std::ostream& Property<float>::saveAscii(std::ostream& os) { return os << value; }
template <> std::istream& Property<float>::loadBinary(std::istream& is) { return is.read((char*)&value, sizeof(float)); }
template <> std::istream& Property<float>::loadAscii(std::istream& is) { return is >> value; }
template <> AbstractProperty::PropertyType Property<float>::getStaticPropertyType() { return AbstractProperty::FLOAT; }
// BOOLEAN PROPERTY
template <> std::ostream& Property<bool>::saveBinary(std::ostream& os) { return os.write((char*)&value, sizeof(bool)); }
template <> std::ostream& Property<bool>::saveAscii(std::ostream& os) { return os << value; }
template <> std::istream& Property<bool>::loadBinary(std::istream& is) { return is.read((char*)&value, sizeof(bool)); }
template <> std::istream& Property<bool>::loadAscii(std::istream& is) { return is >> value; }
template <> AbstractProperty::PropertyType Property<bool>::getStaticPropertyType() { return AbstractProperty::BOOLEAN; }
// VEC2 PROPERTY
template <> std::ostream& Property<glm::vec2>::saveBinary(std::ostream& os) { return os.write((char*)glm::value_ptr(value), sizeof(float)*value.length()); }
template <> std::ostream& Property<glm::vec2>::saveAscii(std::ostream& os) { os << "["; for(int i=0; i<value.length(); ++i) os << (glm::value_ptr(value)[i]) << (i == value.length()-1 ? "]" : ", "); return os; }
template <> std::istream& Property<glm::vec2>::loadBinary(std::istream& is) { return is.read((char*)glm::value_ptr(value), sizeof(float)*value.length()); }
template <> std::istream& Property<glm::vec2>::loadAscii(std::istream& is) { is.ignore(1); for(int i=0; i<value.length(); ++i) is >> (glm::value_ptr(value)[i]), is.ignore(1); return is; }
template <> AbstractProperty::PropertyType Property<glm::vec2>::getStaticPropertyType() { return AbstractProperty::VEC2; }
// VEC3 PROPERTY
template <> std::ostream& Property<glm::vec3>::saveBinary(std::ostream& os) { return os.write((char*)glm::value_ptr(value), sizeof(float)*value.length()); }
template <> std::ostream& Property<glm::vec3>::saveAscii(std::ostream& os) { os << "["; for(int i=0; i<value.length(); ++i) os << (glm::value_ptr(value)[i]) << (i == value.length()-1 ? "]" : ", "); return os; }
template <> std::istream& Property<glm::vec3>::loadBinary(std::istream& is) { return is.read((char*)glm::value_ptr(value), sizeof(float)*value.length()); }
template <> std::istream& Property<glm::vec3>::loadAscii(std::istream& is) { is.ignore(1); for(int i=0; i<value.length(); ++i) is >> (glm::value_ptr(value)[i]), is.ignore(1); return is; }
template <> AbstractProperty::PropertyType Property<glm::vec3>::getStaticPropertyType() { return AbstractProperty::VEC3; }
// MAT4 PROPERTY
template <> std::ostream& Property<glm::mat4>::saveBinary(std::ostream& os) { return os.write((char*)glm::value_ptr(value), sizeof(float)*value.length()); }
template <> std::ostream& Property<glm::mat4>::saveAscii(std::ostream& os) { os << "["; for(int i=0; i<value.length(); ++i) os << (glm::value_ptr(value)[i]) << (i == value.length()-1 ? "]" : ", "); return os; }
template <> std::istream& Property<glm::mat4>::loadBinary(std::istream& is) { return is.read((char*)glm::value_ptr(value), sizeof(float)*value.length()); }
template <> std::istream& Property<glm::mat4>::loadAscii(std::istream& is) { is.ignore(1); for(int i=0; i<value.length(); ++i) is >> (glm::value_ptr(value)[i]), is.ignore(1); return is; }
template <> AbstractProperty::PropertyType Property<glm::mat4>::getStaticPropertyType() { return AbstractProperty::MAT4; }
// STRING PROPERTY
template <> std::ostream& Property<std::string>::saveBinary(std::ostream& os)
{
static const char endChar = '\0';
os.write(value.data(), sizeof(char)*value.size());
return os.write(&endChar, sizeof(char));;
}
template <> std::ostream& Property<std::string>::saveAscii(std::ostream& os)
{
os << "\"";
for(char c : value)
{
switch(c)
{
case '\t' :
os << "\\t";
break;
case '\\' :
os << "\\\\";
break;
case '\n' :
os << "\\n";
break;
case '\r' :
os << "\\r";
break;
case '"' :
os << "\\\"";
break;
default:
os << c;
}
}
return os << "\"";
}
template <> std::istream& Property<std::string>::loadBinary(std::istream& is)
{
value = "";
for(char c = is.get(); c != '\0'; c = is.get())
value.push_back(c);
return is;
}
template <> std::istream& Property<std::string>::loadAscii(std::istream& is)
{
value = "";
is.ignore(1); // "
for(char c = is.get(); c != '"'; c = is.get())
{
if(c == '\\')
{
c = is.get();
switch(c)
{
case 't' :
value.push_back('\t');
break;
case '\\' :
value.push_back('\\');
break;
case 'n' :
value.push_back('\n');
break;
case 'r' :
value.push_back('\r');
break;
case '"' :
value.push_back('"');
c = 'c';
break;
default:
value.push_back(c);
}
}
else
value.push_back(c);
}
return is;
}
template <> AbstractProperty::PropertyType Property<std::string>::getStaticPropertyType() { return AbstractProperty::STRING; }
// ARRAY PROPERTY SPECIAL BOOL HANDLING
template <> std::ostream& ArrayProperty<bool>::saveBinary(std::ostream& os)
{
unsigned int size = value.size();
os.write((char*)&size, sizeof(unsigned int));
for(bool it : value)
Property<bool>(it).saveBinary(os);
return os;
}
template <> std::ostream& ArrayProperty<bool>::saveAscii(std::ostream& os)
{
os << "{ " << value.size() << ", [" << std::endl;
for(bool it : value)
{
Property<bool>(it).saveAscii(os);
os << "," << std::endl;
}
return os << "] }";
}

213
src/property.h Normal file
View File

@ -0,0 +1,213 @@
#ifndef PROPERTY_H
#define PROPERTY_H
#include <vector>
#include <iostream>
#include <glm/ext.hpp>
// PROPERTIES MACROS (used by Serializable class)
#define PROPERTY(type, var)\
Property<type> var##Property;\
type &var = var##Property.getValueRef();\
#define INT(var) PROPERTY(int, var)
#define FLOAT(var) PROPERTY(float, var)
#define BOOL(var) PROPERTY(bool, var)
#define VEC2(var) PROPERTY(glm::vec2, var)
#define VEC3(var) PROPERTY(glm::vec3, var)
#define MAT4(var) PROPERTY(glm::mat4, var)
#define STRING(var) PROPERTY(std::string, var)
// the provided type must be a class inheriting Serializable
#define REFERENCE(type, var)\
Property<Serializable*> var##Property;\
type*& var = (type*&)(var##Property.getValueRef());\
#define VECTOR_PROPERTY(type, var)\
ArrayProperty<type> var##Property;\
std::vector<type> &var = var##Property.getValueRef();\
#define VECTOR_INT(var) VECTOR_PROPERTY(int, var)
#define VECTOR_FLOAT(var) VECTOR_PROPERTY(float, var)
#define VECTOR_BOOL(var) VECTOR_PROPERTY(bool, var)
#define VECTOR_VEC2(var) VECTOR_PROPERTY(glm::vec2, var)
#define VECTOR_VEC3(var) VECTOR_PROPERTY(glm::vec3, var)
#define VECTOR_MAT4(var) VECTOR_PROPERTY(glm::mat4, var)
#define VECTOR_STRING(var) VECTOR_PROPERTY(std::string, var)
#define VECTOR_REFERENCE(type, var) VECTOR_PROPERTY(type*, var)
#define CAST(var) (AbstractProperty*)&var##Property
// FORWARD DECLARATIONS AND TYPES
class AbstractProperty;
class Serializable;
typedef std::vector<AbstractProperty*> PropertySet;
// PROPERTY INTERFACE
class AbstractProperty
{
public:
enum PropertyType
{
UNDEFINED,
INTEGER,
FLOAT,
BOOLEAN,
VEC2,
VEC3,
MAT4,
STRING,
REFERENCE,
ARRAY, // this is a bit special, an array must also have one of the other types
// keep last
NB_PROPERTY_TYPES
};
virtual ~AbstractProperty() {}
virtual std::ostream& saveBinary(std::ostream& os) = 0;
virtual std::ostream& saveAscii(std::ostream& os) = 0;
virtual std::istream& loadBinary(std::istream& is) = 0;
virtual std::istream& loadAscii(std::istream& is) = 0;
virtual PropertyType getPropertyType() = 0;
friend PropertySet& operator << (PropertySet &properties, AbstractProperty& p)
{ properties.push_back(&p); return properties; }
};
// GENERIC PROPERTY
template <typename T>
class Property : public AbstractProperty
{
T value;
public:
Property(T val) : value(val) {}
Property() {}
~Property() {}
std::ostream& saveBinary(std::ostream& os);
std::ostream& saveAscii(std::ostream& os);
std::istream& loadBinary(std::istream& is);
std::istream& loadAscii(std::istream& is);
PropertyType getPropertyType() { return getStaticPropertyType(); }
static PropertyType getStaticPropertyType();
T &getValueRef() { return value; }
};
/**
* Reference properties are implemented through partial specialization of Property,
* it can compile with any pointer,
* but will only work will pointers to a class that inherits Serializable
*/
template <typename T>
class Property<T*> : public AbstractProperty
{
T* value;
public:
Property(T* val) : value(val) {}
Property() {}
~Property() {}
// will not be used, serialization of references is entirely done by the serialization manager
std::ostream& saveBinary(std::ostream& os) { return os; }
std::ostream& saveAscii(std::ostream& os) { return os; }
std::istream& loadBinary(std::istream& is) { return is; }
std::istream& loadAscii(std::istream& is) { return is; }
PropertyType getPropertyType() { return getStaticPropertyType(); }
static PropertyType getStaticPropertyType() { return REFERENCE; }
T*& getValueRef() { return value; }
};
// GENERIC VECTOR PROPERTY
class AbstractArrayProperty : public AbstractProperty
{
public:
PropertyType getPropertyType() { return ARRAY; }
virtual PropertyType getDataType() { return UNDEFINED; }
};
template <typename T>
class ArrayProperty : public AbstractArrayProperty
{
std::vector<T> value;
public:
ArrayProperty(const std::vector<T> &val) : value(val) {}
ArrayProperty() {}
~ArrayProperty() {}
std::ostream& saveBinary(std::ostream& os)
{
Property<int> size(value.size());
size.saveBinary(os);
for(T &it : value)
Property<T>(it).saveBinary(os);
return os;
}
std::ostream& saveAscii(std::ostream& os)
{
os << "[";
int i = value.size();
for(T &it : value)
{
Property<T>(it).saveAscii(os);
if(--i) os << ", ";
}
return os << "]";
}
std::istream& loadBinary(std::istream& is)
{
Property<int> size;
size.loadBinary(is);
value.resize(size.getValueRef());
Property<T> p;
for(int i=0; i<size.getValueRef(); ++i)
{
p.loadBinary(is);
value[i] = p.getValueRef();
}
return is;
}
std::istream& loadAscii(std::istream& is)
{
value.clear();
is.ignore(1); // [
Property<T> p;
while(is.peek() != ']')
{
p.loadAscii(is);
value.push_back(p.getValueRef());
if(is.peek() == ',')
is.ignore(1); // ,
}
return is.ignore(1);
}
// array specific methods
PropertyType getDataType() { return Property<T>::getStaticPropertyType(); }
std::vector<T> &getValueRef() { return value; }
unsigned int getArraySize() { return value.size(); }
};
#endif // PROPERTY_H

16
src/serializable.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "serializable.h"
std::unordered_map<std::string, Serializable::Instancer>* Serializable::s_typeMap = nullptr;
bool Serializable::addType(const std::string &name, Instancer fun)
{
if(s_typeMap == nullptr)
s_typeMap = new std::unordered_map<std::string, Instancer>();
(*s_typeMap)[name] = fun;
return true;
}
PropertySet* Serializable::initProperties(PropertySet* properties, AbstractProperty* p) const
{
properties->push_back(p); return properties;
}

83
src/serializable.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef SERIALIZABLE_H
#define SERIALIZABLE_H
#include <unordered_map>
#include "property.h"
/**
// To use these macros, your class should inherit the abstract class "Serializable" :
class MyClass : public Serializable
// you should first declare properties like this :
private:
INT(myIntProperty)
REFERENCE(myPointer)
VECTOR_FLOAT(myFloatArray)
// and then, you'll be able to automatically save these properties after using the following macro :
public:
SERIALIZABLE(MyClass, CAST(myIntProperty), CAST(myPointer), CAST(myFloatArray))
// to be able to load your saved object, you will need to add the following declaration in the CPP file of your class :
INIT_SERIALIZABLE(MyClass)
// that's it ! now you can use the ObjectSaver and ObjectLoader classes to serialize your objects automatically.
*/
// ALTERNATIVE INITIALIZATION :
// use this if you really can't have a cpp file for your class
#define MANUAL_SERIALIZABLE(className, args...)\
virtual const std::string &getType() const { return className::getStaticType(); }\
static const std::string &getStaticType() { static const std::string name = #className; return name; }\
virtual PropertySet* getProperties() const { return initProperties(new PropertySet(), args); }\
static Serializable* instantiate() { return (Serializable*)(new className()); }\
// can be called to init the class manually whenever you want in your program (before any deserialization of this class)
#define MANUAL_INIT_SERIALIZABLE(className) Serializable::addType(#className, className::instantiate);
// RECOMMENDED INITIALIZATION
#define SERIALIZABLE(className, args...)\
virtual const std::string &getType() const { return className::getStaticType(); }\
static const std::string &getStaticType() { static const std::string name = #className; return name; }\
virtual PropertySet* getProperties() const { return initProperties(new PropertySet(), args); }\
static bool init;\
static Serializable* instantiate() { return (Serializable*)(new className()); }\
// used to auto-init the class at startup (with global variables initialization)
#define INIT_SERIALIZABLE(className) bool className::init = MANUAL_INIT_SERIALIZABLE(className)\
// END OF MACROS
/**
* @brief Most of the methods and members of this class are only used by the macros and the automatic serializer, you shouldn't have to worry about them.
*/
class Serializable
{
public:
virtual const std::string &getType() const = 0;
virtual PropertySet* getProperties() const = 0;
typedef Serializable* (*Instancer)(void);
static Instancer getInstancer(const std::string &type) { return (*s_typeMap)[type]; }
private:
static std::unordered_map<std::string, Instancer> *s_typeMap;
protected:
static bool addType(const std::string &name, Instancer fun);
PropertySet* initProperties(PropertySet* properties, AbstractProperty* p) const;
template <typename... Args> // recursive variadic
PropertySet* initProperties(PropertySet* properties, AbstractProperty* p, Args... args) const
{
properties->push_back(p);
return initProperties(properties, args...);
}
};
#endif // SERIALIZABLE_H

View File

@ -0,0 +1,348 @@
#include "serializationmanager.h"
#include "serializable.h"
#include <sstream>
void ObjectSaver::addObject(Serializable *object)
{
if(object == nullptr)
return;
const std::string &type = object->getType();
ObjectType &objectsOfSameType = m_objects[type];
if(objectsOfSameType.count(object) == 0)
{
int n = objectsOfSameType.size();
objectsOfSameType[object] = n;
PropertySet *properties = object->getProperties();
for(AbstractProperty* p : *properties)
{
if(p->getPropertyType() == AbstractProperty::REFERENCE)
addObject(((Property<Serializable*>*)p)->getValueRef());
else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE)
{
std::vector<Serializable*> &vecRef = ((ArrayProperty<Serializable*>*)p)->getValueRef();
for(Serializable *ref : vecRef)
addObject(ref);
}
}
delete properties;
}
}
std::ostream& ObjectSaver::saveBinary(std::ostream& os)
{
// loop on serializable types
Property<int>(m_objects.size()).saveBinary(os); // amount of types
for(const std::pair<std::string, ObjectType> &type : m_objects)
{
// serialize type
Property<std::string>(type.first).saveBinary(os); // type name
Property<int>(type.second.size()).saveBinary(os); // amount of objects of this type
// order the objects
std::vector<Serializable*> orderedObjects(type.second.size());
for(const std::pair<Serializable*, int> &entry : type.second)
orderedObjects[entry.second] = entry.first;
// serialize objects of this type
for(Serializable* object : orderedObjects)
{
PropertySet *properties = object->getProperties();
for(AbstractProperty *p : *properties)
{
if(p->getPropertyType() == AbstractProperty::REFERENCE)
{
Serializable *ref = ((Property<Serializable*>*)p)->getValueRef();
if(ref == nullptr)
Property<std::string>("null").saveBinary(os);
else
{
Property<std::string>(ref->getType()).saveBinary(os); // type name
Property<int>(m_objects[ref->getType()][ref]).saveBinary(os); // id of the object
}
}
else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE)
{
std::vector<Serializable*> &vecRef = ((ArrayProperty<Serializable*>*)p)->getValueRef();
Property<int>(vecRef.size()).saveBinary(os); // amount of references
for(Serializable *ref : vecRef)
{
if(ref == nullptr)
Property<std::string>("null").saveBinary(os);
else
{
Property<std::string>(ref->getType()).saveBinary(os); // type name
Property<int>(m_objects[ref->getType()][ref]).saveBinary(os); // id of the object
}
}
}
else
p->saveBinary(os);
}
delete properties;
}
}
return os;
}
std::ostream& ObjectSaver::saveAscii(std::ostream& os)
{
os << "{" << std::endl;
// loop on serializable types
for(const std::pair<std::string, ObjectType> &type : m_objects)
{
// serialize type
os << "\t\"" << type.first << "\": [" << std::endl;
// order the objects
std::vector<Serializable*> orderedObjects(type.second.size());
for(const std::pair<Serializable*, int> &entry : type.second)
orderedObjects[entry.second] = entry.first;
// serialize objects of this type
for(Serializable* object : orderedObjects)
{
os << "\t\t{" << std::endl;
PropertySet *properties = object->getProperties();
for(AbstractProperty *p : *properties)
{
os << "\t\t\t";
if(p->getPropertyType() == AbstractProperty::REFERENCE)
{
Serializable *ref = ((Property<Serializable*>*)p)->getValueRef();
if(ref == nullptr)
os << "\"null\"";
else
os << "{\"" << ref->getType() << "\", " << (m_objects[ref->getType()][ref]) << "}";
}
else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE)
{
std::vector<Serializable*> &vecRef = ((ArrayProperty<Serializable*>*)p)->getValueRef();
os << "[";
int i = vecRef.size();
for(Serializable *ref : vecRef)
{
if(ref == nullptr)
os << "\"null\"";
else
os << "{\"" << ref->getType() << "\", " << (m_objects[ref->getType()][ref]) << "}";
if(--i) os << ", ";
}
os << "]";
}
else
p->saveAscii(os);
if(properties->back() == p)
os << std::endl;
else
os << "," << std::endl;
}
delete properties;
os << "\t\t}";
if(orderedObjects.back() == object)
os << std::endl;
else
os << "," << std::endl;
}
os << "\t]" << std::endl;
}
return os << "}" << std::endl;
}
std::istream& ObjectLoader::loadBinary(std::istream& is)
{
Property<int> nbTypes;
nbTypes.loadBinary(is); // get amount of types to read
for(int i=0; i<nbTypes.getValueRef(); ++i) // loop on these types
{
Property<std::string> typeName;
typeName.loadBinary(is); // get the type name
Serializable::Instancer instancer = Serializable::getInstancer(typeName.getValueRef());
Property<int> nbObjects;
nbObjects.loadBinary(is); // get the amount of objects of that type
std::vector<Serializable*> &objectsOfThatType = m_objects[typeName.getValueRef()];
for(int j=0; j<nbObjects.getValueRef(); ++j) // loop on these objects
{
Serializable* object = instancer();
objectsOfThatType.push_back(object);
PropertySet* properties = object->getProperties();
for(AbstractProperty* p : *properties)
{
if(p->getPropertyType() == AbstractProperty::REFERENCE)
{
Property<std::string> type; // type name
type.loadBinary(is);
if(type.getValueRef().compare("null") != 0)
{
Property<int> id; // id of the object
id.loadBinary(is);
PendingReference ref;
ref.ptr = &(((Property<Serializable*>*)p)->getValueRef());
ref.type = type.getValueRef();
ref.id = id.getValueRef();
m_references.push_back(ref);
}
else
((Property<Serializable*>*)p)->getValueRef() = nullptr;
}
else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE)
{
Property<int> nbRef; // amount of references in the array
nbRef.loadBinary(is);
std::vector<Serializable*> &vecRef = ((ArrayProperty<Serializable*>*)p)->getValueRef();
vecRef = std::vector<Serializable*>(nbRef.getValueRef(), nullptr);
for(Serializable* &it : vecRef)
{
Property<std::string> type; // type name
type.loadBinary(is);
if(type.getValueRef().compare("null") != 0)
{
Property<int> id; // id of the object
id.loadBinary(is);
PendingReference ref;
ref.ptr = &it;
ref.type = type.getValueRef();
ref.id = id.getValueRef();
m_references.push_back(ref);
}
}
}
else
p->loadBinary(is);
}
delete properties;
}
}
buildReferences();
return is;
}
std::istream& ObjectLoader::loadAscii(std::istream& is)
{
// get trimmed version of the stream into a stringstream
while(is.get() != '{'); // got to first opening brace
int nbBraces = 1;
bool inString = false;
std::stringstream ss;
while(nbBraces > 0) // read and trim until we find the corresponding closing brace
{
int c = is.get();
switch(c)
{
case '{':
++nbBraces;
ss.put(c);
break;
case '}':
--nbBraces;
ss.put(c);
break;
case '\\':
if(is.peek() == '"')
inString = !inString;
ss.put(c);
break;
case '"':
inString = !inString;
ss.put(c);
break;
case ' ':
if(inString)
ss.put(c);
case '\n':
case '\t':
case '\r':
break;
default:
ss.put(c);
}
}
// begin parsing
while(ss.peek() != '}') // loop on serializable types
{
Property<std::string> typeName;
typeName.loadAscii(ss);
Serializable::Instancer instancer = Serializable::getInstancer(typeName.getValueRef());
std::vector<Serializable*> &objectsOfThatType = m_objects[typeName.getValueRef()];
ss.ignore(2); // :[
while(ss.peek() != ']') // loop on the objects of that type
{
ss.ignore(1); // {
Serializable* object = instancer();
objectsOfThatType.push_back(object);
PropertySet* properties = object->getProperties();
for(AbstractProperty* p : *properties)
{
if(p->getPropertyType() == AbstractProperty::REFERENCE)
{
if(ss.peek() == '{')
{
ss.ignore(1); // {
Property<std::string> type; // type name
type.loadAscii(ss);
ss.ignore(1); // ,
Property<int> id; // id of the object
id.loadAscii(ss);
ss.ignore(1);
PendingReference ref;
ref.ptr = &(((Property<Serializable*>*)p)->getValueRef());
ref.type = type.getValueRef();
ref.id = id.getValueRef();
m_references.push_back(ref);
ss.ignore(1); // }
}
else
{
ss.ignore(6); // "null"
((Property<Serializable*>*)p)->getValueRef() = nullptr;
}
}
else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE)
{
std::vector<Serializable*> &vecRef = ((ArrayProperty<Serializable*>*)p)->getValueRef();
ss.ignore(1); // [
while(ss.peek() != ']')
{
vecRef.push_back(nullptr);
if(ss.peek() == '{')
{
ss.ignore(1); // {
Property<std::string> type; // type name
type.loadAscii(ss);
ss.ignore(1); // ,
Property<int> id; // id of the object
id.loadAscii(ss);
ss.ignore(1); // }
PendingReference ref;
ref.ptr = &(vecRef.back());
ref.type = type.getValueRef();
ref.id = id.getValueRef();
m_references.push_back(ref);
}
else
ss.ignore(6); // "null"
if(ss.peek() != ',')
ss.ignore(1); // ,
}
ss.ignore(1); // ]
}
else
p->loadAscii(ss);
if(p != properties->back())
ss.ignore(1); // ,
}
ss.ignore(1); // }
}
ss.ignore(1); // ]
}
buildReferences();
return is;
}
void ObjectLoader::buildReferences()
{
for(PendingReference &ref : m_references)
*(ref.ptr) = m_objects[ref.type][ref.id];
m_references.clear();
}

View File

@ -0,0 +1,44 @@
#ifndef SERIALIZATIONMANAGER_H
#define SERIALIZATIONMANAGER_H
#include <unordered_map>
#include <vector>
class Serializable;
class ObjectSaver
{
typedef std::unordered_map<Serializable*, int> ObjectType;
std::unordered_map<std::string, ObjectType> m_objects;
public:
void addObject(Serializable *object);
template <typename T>
void addObject(T *object) { addObject((Serializable*)object); }
std::ostream& saveBinary(std::ostream& os);
std::ostream& saveAscii(std::ostream& os);
};
class ObjectLoader
{
struct PendingReference
{
Serializable **ptr;
std::string type;
int id;
};
std::vector<PendingReference> m_references;
std::unordered_map<std::string, std::vector<Serializable*>> m_objects;
void buildReferences();
public:
std::istream& loadBinary(std::istream& is);
std::istream& loadAscii(std::istream& is);
const std::vector<Serializable*>& getObjects(const std::string &type) { return m_objects[type]; }
template <typename T>
const std::vector<T*>& getObjects() { return (const std::vector<T*>&)(m_objects[T::getStaticType()]); }
};
#endif // SERIALIZATIONMANAGER_H