I have twin base classes for a container class and contained object (in my case, for a generic graph class made of linked nodes, but I guess the question would stand in all cases where you want to derive from both a container class and a contained object class from twin container/contained base classes). I want to use derived classes of both the container and contained object. How do I avoid having to constantly downcast the contained object to the derived contained object type when I access the container of the derived container class?
// a class that represents the graph
template <typename WrappedObject> class BaseGraph {
... // constructor, virtual destructor, ...
public:
std::map<WrappedObject*, BaseNode<WrappedObject> *> obj_to_node_map_;
bool addEdge(WrappedObject *scr, WrappedObject *trg) {
BaseNode<WrappedObject > * srcNode = getOrCreateNode(src);
BaseNode<WrappedObject > * dstNode = getOrCreateNode(trg);
return addEdge(srcNode, dstNode);
}
bool addEdge(BaseNode<WrappedObject> * src, BaseNode<WrappedObject> * trg) {
if (src == trg) {
return false;
}
src->suc_.insert(trg);
trg->pre_.insert(src);
return true;
}
BaseNode<WrappedObject>* getOrCreateNode(WrappedObject*obj){
BaseNode<WrappedObject> *node = getNode(obj);
if (node == nullptr) {
node = createNode(obj);
obj_to_node_map_[obj] = node;
}
return node;
}
BaseNode<WrappedObject>* createNode(WrappedObject* obj){
BaseNode<WrappedObject> *node = allocateNode(obj);
nodes_.push_back(node);
return node;
}
BaseNode<WrappedObject>* getNode(WrappedObject*obj) const {
auto it = obj_to_node_map_.find(obj);
if (it == obj_to_node_map_.end()) {
return nullptr;
}
return it->second;
}
private:
virtual BaseNode<Object> * allocateNode(Object *obj) = 0;
};
// a class that represents the vertices of the graph
template <typename WrappedObject>
class BaseNode {
friend class BaseGraph<WrappedObject>;
public:
typedef std::set<BaseNode *> Set;
BaseNode(WrappedObject*obj):obj_(obj) {
}
virtual ~BaseNode() {};
WrappedObject *obj_;
Set suc_;
Set pre_;
};
// derived classes for node and graph
class RealObject;
class MyNode : public BaseNode<RealObject> {
private:
std::string name_;
MyNode(RealObject *obj, std::string & name): BaseGraph<RealObject>(obj), name_(name) {}
...
};
class MyGraph: public BaseGraph<RealObject> {
// constructor, virtual destructor, ...
private:
virtual BaseNode<RealObject> * allocateNode(RealObject *obj) {
return new MyNode(obj, this);
}
std::map<std::string, MyNode *> name_lu_map_;
public:
MyNode * getNode(std:string & name) {
return(name_lu_map_[name]);
}
// Have to have this otherwise any call to getNode on a MyGraph instance
// will cause compiler error as getNode is overloaded in derived class
MyNode * getNode(RealObject *obj) const {
return static_cast<MyNode *>(BaseGraph<RealObject>::getNode(obj));
}
void myDerivedClassFunction(RealObject *obj) {
// Lines like the following when I call a method of the base class
// returning a base class pointer will require casting, which I find
// ugly
MyNode *node = static_cast<MyNode *>(getOrCreateNode(obj));
node->name_ = <something>;
...
}
};
Is there a design pattern that would allow to avoid all the awkward downcast to the derived class whenever I use methods of the container base class that return base class contained object pointers? I thought of using a template argument to define what the derived contained object type will be, but I do not see how I can describe in C++ the fact that a typename template argument derives from another class. Possibly, I am looking at this from the wrong angle. Any ideas?