initial commit
This commit is contained in:
commit
5d9bd6de45
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.user
|
||||
build*
|
14
CMakeLists.txt
Normal file
14
CMakeLists.txt
Normal 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
74
src/main.cpp
Normal 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
145
src/property.cpp
Normal 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
213
src/property.h
Normal 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
16
src/serializable.cpp
Normal 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
83
src/serializable.h
Normal 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
|
348
src/serializationmanager.cpp
Normal file
348
src/serializationmanager.cpp
Normal 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 = ⁢
|
||||
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();
|
||||
}
|
44
src/serializationmanager.h
Normal file
44
src/serializationmanager.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user