Reputation: 1097
I have a class NodeManager
. It basically keeps a list of Node*
s and their connections to each other. Actually the Nodes it manages are classes derived from the Node
class.
Now I want this NodeManager
to be able to save and all its nodes to file and load it back.
The problem I am facing is how could I store what is the derived type of the Nodes.
I was thinking of something like storing the typeid
of the derived class and keeping a list of possible types the Node
can be derived to, but I have no idea how could I accomplish that.
Performance isn't a problem, in any case we are talking about <100 nodes.
The answer should work multiplatform on windows, linux and mac os.
I want the answer to make adding more node types trivial, if possible, without altering the code of the NodeManager
.
Upvotes: 0
Views: 95
Reputation: 26409
In base class, create abstract virtual method that returns some kind of "ID". (string, int, enum, whatever).
In "save" method, write ID first, for all classes. You could embed ID writing into base class, so derived classes won't override this behavior.
typedef SomeType ClassId;
class Serializeable{
protected:
virtual ClassId getClassId() const = 0;
virtual void saveData(OutStream &out) = 0;
public:
void save(OutStream &out){
out << getClassId();
saveData(out);
}
};
Make a factory, that constructs required class given its ID.
--edit--
Example with factory (C++03 standard):
#include <map>
#include <string>
#include <iostream>
#include <QSharedPointer>
#include <utility>
typedef std::string ClassId;
typedef std::ostream OutStream;
class Serializeable{
protected:
virtual void saveData(OutStream &out) = 0;
public:
virtual ClassId getClassId() const = 0;
void save(OutStream &out){
out << getClassId();
saveData(out);
}
virtual ~Serializeable(){
}
};
class Derived: public Serializeable{
protected:
virtual void saveData(OutStream &out){
out << "test";
}
public:
virtual ClassId getClassId() const{
return "Derived";
}
};
typedef QSharedPointer<Serializeable> SerializeablePtr; //basically std::shared_ptr
SerializeablePtr makeDerived(){
return SerializeablePtr(new Derived());
}
class ClassFactory{
protected:
typedef SerializeablePtr (*BuilderCallback)();
typedef std::map<ClassId, BuilderCallback> BuilderMap;
BuilderMap builderMap;
template<class C> static SerializeablePtr defaultBuilderFunction(){
return SerializeablePtr(new C());
}
public:
SerializeablePtr buildClass(ClassId classId){
BuilderMap::iterator found = builderMap.find(classId);
if (found == builderMap.end())
return SerializeablePtr();//or throw exception
return (*(found->second))();
}
void registerClass(ClassId classId, BuilderCallback callback){
builderMap[classId] = callback;
}
template<typename T> void registerClassByValue(const T &val){
registerClass(val.getClassId(), ClassFactory::defaultBuilderFunction<T>);
}
template<typename T> void registerClassWithTemplate(ClassId classId){
registerClass(classId, ClassFactory::defaultBuilderFunction<T>);
}
};
int main(int argc, char** argv){
ClassFactory factory;
std::string derivedId("Derived");
factory.registerClass(derivedId, makeDerived);
SerializeablePtr created = factory.buildClass(derivedId);
created->save(std::cout);
std::cout << std::endl;
Derived tmp;
factory.registerClassByValue(tmp);
created = factory.buildClass(derivedId);
created->save(std::cout);
std::cout << std::endl;
factory.registerClassWithTemplate<Derived>(derivedId);
created = factory.buildClass(derivedId);
created->save(std::cout);
std::cout << std::endl;
return 0;
}
QSharedPointer
is smart pointer class from Qt 4, roughly equivalent to std::shared_ptr
. Use either std::shared_ptr
or boost::shared_ptr
instead of QSharedPointer
in your code.
Upvotes: 2