Ответ Якоби может быть расширен в дальнейшем.
Я полагаю, что сериализация может быть реализована способом, подобным управляемым языкам, если кто-то фактически реализует систему отражения.
В течение многих лет мы использовали автоматизированный подход.
Я был одним из разработчиков рабочего постпроцессора C ++ и библиотеки Reflection: инструмент LSDC и ядро Linderdaum Engine Core (iObject + RTTI + Linker / Loader). См. Источник в http://www.linderdaum.com
Фабрика классов абстрагирует процесс создания класса.
Чтобы инициализировать определенные элементы, вы можете добавить некоторые навязчивые RTTI и автоматически сгенерировать для них процедуры загрузки / сохранения.
Предположим, у вас есть класс iObject в верхней части вашей иерархии.
// Base class with intrusive RTTI
class iObject
{
public:
iMetaClass* FMetaClass;
};
///The iMetaClass stores the list of properties and provides the Construct() method:
// List of properties
class iMetaClass: public iObject
{
public:
virtual iObject* Construct() const = 0;
/// List of all the properties (excluding the ones from base class)
vector<iProperty*> FProperties;
/// Support the hierarchy
iMetaClass* FSuperClass;
/// Name of the class
string FName;
};
// The NativeMetaClass<T> template implements the Construct() method.
template <class T> class NativeMetaClass: public iMetaClass
{
public:
virtual iObject* Construct() const
{
iObject* Res = new T();
Res->FMetaClass = this;
return Res;
}
};
// mlNode is the representation of the markup language: xml, json or whatever else.
// The hierarchy might have come from the XML file or JSON or some custom script
class mlNode {
public:
string FName;
string FValue;
vector<mlNode*> FChildren;
};
class iProperty: public iObject {
public:
/// Load the property from internal tree representation
virtual void Load( iObject* TheObject, mlNode* Node ) const = 0;
/// Serialize the property to some internal representation
virtual mlNode* Save( iObject* TheObject ) const = 0;
};
/// function to save a single field
typedef mlNode* ( *SaveFunction_t )( iObject* Obj );
/// function to load a single field from mlNode
typedef void ( *LoadFunction_t )( mlNode* Node, iObject* Obj );
// The implementation for a scalar/iObject field
// The array-based property requires somewhat different implementation
// Load/Save functions are autogenerated by some tool.
class clFieldProperty : public iProperty {
public:
clFieldProperty() {}
virtual ~clFieldProperty() {}
/// Load single field of an object
virtual void Load( iObject* TheObject, mlNode* Node ) const {
FLoadFunction(TheObject, Node);
}
/// Save single field of an object
virtual mlNode* Save( iObject* TheObject, mlNode** Result ) const {
return FSaveFunction(TheObject);
}
public:
// these pointers are set in property registration code
LoadFunction_t FLoadFunction;
SaveFunction_t FSaveFunction;
};
// The Loader class stores the list of metaclasses
class Loader: public iObject {
public:
void RegisterMetaclass(iMetaClass* C) { FClasses[C->FName] = C; }
iObject* CreateByName(const string& ClassName) { return FClasses[ClassName]->Construct(); }
/// The implementation is an almost trivial iteration of all the properties
/// in the metaclass and calling the iProperty's Load/Save methods for each field
void LoadFromNode(mlNode* Source, iObject** Result);
/// Create the tree-based representation of the object
mlNode* Save(iObject* Source);
map<string, iMetaClass*> FClasses;
};
Когда вы определяете ConcreteClass, производный от iObject, вы используете некоторое расширение и инструмент генератора кода для создания списка процедур сохранения / загрузки и кода регистрации для.
Давайте посмотрим код для этого образца.
Где-то в структуре у нас есть пустое формальное определение
#define PROPERTY(...)
/// vec3 is a custom type with implementation omitted for brevity
/// ConcreteClass2 is also omitted
class ConcreteClass: public iObject {
public:
ConcreteClass(): FInt(10), FString("Default") {}
/// Inform the tool about our properties
PROPERTY(Name=Int, Type=int, FieldName=FInt)
/// We can also provide get/set accessors
PROPERTY(Name=Int, Type=vec3, Getter=GetPos, Setter=SetPos)
/// And the other field
PROPERTY(Name=Str, Type=string, FieldName=FString)
/// And the embedded object
PROPERTY(Name=Embedded, Type=ConcreteClass2, FieldName=FEmbedded)
/// public field
int FInt;
/// public field
string FString;
/// public embedded object
ConcreteClass2* FEmbedded;
/// Getter
vec3 GetPos() const { return FPos; }
/// Setter
void SetPos(const vec3& Pos) { FPos = Pos; }
private:
vec3 FPos;
};
Сгенерированный автоматически регистрационный код будет:
/// Call this to add everything to the linker
void Register_ConcreteClass(Linker* L) {
iMetaClass* C = new NativeMetaClass<ConcreteClass>();
C->FName = "ConcreteClass";
iProperty* P;
P = new FieldProperty();
P->FName = "Int";
P->FLoadFunction = &Load_ConcreteClass_FInt_Field;
P->FSaveFunction = &Save_ConcreteClass_FInt_Field;
C->FProperties.push_back(P);
... same for FString and GetPos/SetPos
C->FSuperClass = L->FClasses["iObject"];
L->RegisterClass(C);
}
// The autogenerated loaders (no error checking for brevity):
void Load_ConcreteClass_FInt_Field(iObject* Dest, mlNode* Val) {
dynamic_cast<ConcereteClass*>Object->FInt = Str2Int(Val->FValue);
}
mlNode* Save_ConcreteClass_FInt_Field(iObject* Dest, mlNode* Val) {
mlNode* Res = new mlNode();
Res->FValue = Int2Str( dynamic_cast<ConcereteClass*>Object->FInt );
return Res;
}
/// similar code for FString and GetPos/SetPos pair with obvious changes
Теперь, если у вас есть JSON-подобный иерархический скрипт
Object("ConcreteClass") {
Int 50
Str 10
Pos 1.5 2.2 3.3
Embedded("ConcreteClass2") {
SomeProp Value
}
}
Объект Linker будет разрешать все классы и свойства в методах Save / Load.
Извините за длинный пост, реализация становится еще больше, когда приходит вся обработка ошибок.