I have a template class defined as such:
template <typename T> void ProxyNoOp(T *) {} template <typename T> void ProxyDelete(T * ptrT) {delete ptrT;} template <typename T, typename C, void (* Release)(T *) = ProxyNoOp<T> > class Proxy { public: class Container : public std::list<T *> { public: Container() {} ~Container() {clear();} void clear() { iterator clsEnd = end(); for (iterator clsCurrent = begin(); clsCurrent != clsEnd; ++clsCurrent) { T * ptrT = *clsCurrent; static_cast<Proxy *>(ptrT)->m_ptrContainer = NULL; Release(ptrT); } } }; private: iterator m_clsPosition; C * m_ptrContainer; public: Proxy() : m_ptrContainer(NULL) {} ~Proxy() { if ( m_ptrContainer != NULL ) { Container * ptrContainer = static_cast<Container *>(m_ptrContainer); static_cast<std::list<T *> >(ptrContainer)->erase(m_clsPosition); } } C * GetContainer() const {return m_ptrContainer;} };
Most of the code has been removed or modified for the sake of brevity. The idea of the data structure is that the container does not support references to the contained elements, but the elements contained in it support the reference to the PLUS container at their position in the container, making deletion quick (constant time) and automatically (called in the destructor)
The problem with this approach can be demonstrated with this code:
#include "proxy.h"
When compiling Parent definition of Proxy<Child, Parent>::Container causes an error in this line: static_cast<Proxy *>(ptrT)->m_ptrContainer = NULL; - At the compilation point, Child not defined, only declared. If you reorder the Parent and Child declarations as follows:
#include "proxy.h" class Parent; class Child : public Proxy<Child, Parent> {}; class Parent : public Proxy<Child, Parent>::Container {};
the definition of Proxy<Child, Parent> causes an error in this line: Container * ptrContainer = static_cast<Container *>(m_ptrContainer); , the reason is the same as before - at this compilation point, Parent not fully defined.
Is there any way around this and using patterns? Is there a standard data structure in STL, BOOST, etc. that does this?
EDIT:
The code will not compile as is, I should have been more clear about this (to quote myself from above: most of the code has been removed or modified for brevity.). It doesn’t actually use std::list<T *> , but the user is double-linked, which is actually the same. I did not want to post a ton of helper code when I asked only about this problem.
I did not ask for criticism on the data structure itself (although I am fine to hear it), but for those who wonder why it was created here, there are several examples:
The game engine has models and instances of these models, for example, a war game in which there is one model of tanks and any number of tanks created from it. Classes can be written as such:
class Instance; class Model : public Proxy<Model, Instance, ProxyDelete<Instance> >::Container {...}; class Instance : public Proxy<Model, Instance, ProxyDelete<Instance> > {...};
When a model is deleted, all instances created from this model must also be deleted (therefore, the template parameter ProxyDelete<Instance> ), since it does not make sense for the instance to exist without the model from which it was made.
Suppose your rendering of a graphical user interface (GUI) keeps track of all visible elements (buttons, frames, etc.), so it knows what to do, but the text is rendered differently, as this is a different call to the graphical API:
class GuiTextElement; class GuiRenderer : public Proxy<GuiRenderer, GuiTextElement, ProxyNoOp<GuiTextElement> >::Container {...}; class GuiTextElement : public Proxy<GuiRenderer, GuiTextElement, ProxyNoOp<GuiTextElement> > {...};
Less discerning would say: "Why don't you just add std::list<Instance *> as a member of Model and std::list<GuiTextElement *> as a member of GuiRenderer ?". Here are a few reasons:
- Elements belonging to their containers are not automatically deleted when the container is destroyed (yes, I know
boost::ptr_list ). - There is no way, without adding another member of the class, to refer to the container using the contained element. For example, to get a
Model from Instance . - If you added a class member to run # 2, you will also need to update it when the items are removed from their containers.
- For speed fanatics - if the contained elements do not support the iterator in their position in the container, before this element can be deleted, you must first find it.