Note that there are some explanatory texts on larger screens.

plurals
  1. POModelling abstract compositions with type-safety
    text
    copied!<p>I have a structural problem that I could use your help with. I'll explain the abstract problem first, then an example to illustrate the problem.</p> <p>Consider an abstract class <b>A</b> holding a number of instances of an abstract class <b>B</b>, also assume the following methods in <b>A</b>: <b>void a.foo1(B val)</b> and <b>B a.foo2()</b>. The problem arises when we inherit the classes (<b>A'</b> inherits <b>A</b> and <b>B'</b> inherits <b>B</b>) and demand that the relation <b>A</b> has to <b>B</b> should be the same as <b>A'</b> has to <b>B'</b>. That is, in <b>A'</b>: <b>void a.foo1(B' val)</b> and <b>B' a.foo2()</b>. The second method will work, but not the first one (unless we do a unsafe type-cast). In other words, in <b>A'</b>: <b>a.foo1(B val)</b> should be illegal unless parameter is an instance of <b>B'</b>. I've tried to model this relationship with generics/templates with little sucess.</p> <p>This problem arises when making a graph framework. Here we have classes <b>Graph</b> and <b>GraphVertex</b>. (In my actual implementation I overload GraphVertex's delete operator.) In C++:</p> <pre><code>template&lt;class T&gt; class Graph; // Forward reference. template&lt;class T&gt; class GraphVertex { public: GraphVertex(Graph&lt;T&gt; &amp;graph) : m_graph(graph){} virtual ~GraphVertex() {} ... // Abstract methods, using T parameter. void remove() { m_graph.removeVertex(this); } protected: Graph&lt;T&gt; &amp;m_graph; } template&lt;class T&gt; class Graph { public: virtual ~Graph(){} ... virtual GraphVertex&lt;T&gt; *add(T val) = 0; virtual void removeVertex(GraphVertex&lt;T&gt; *vertex) = 0; // &lt;--- !!! } </code></pre> <p>The problem here is the <b>removeVertex</b> method. Lets say we have implemented the classes with <b>AdjacencyMatrixGraph</b> and <b>AMGVertex</b>. When removing a vertex in the <b>AdjacencyMatrixGraph</b> we need more data than is provided in the abstract base class <b>GraphVertex</b>. We know that the parameter type <i>should</i> be <b>AMGVertex</b> but sending another type as parameter would not generate an (compile-time) error.</p> <p>To solve this problem I've tried to add a new template parameter, specifying the implemented type. That is:</p> <pre><code>template&lt;class T, class G&gt; class GraphVertex { public: GraphVertex(G &amp;graph) : m_graph(graph) {} ~GraphVertex() {} ... void remove() { m_graph.removeVertex(this); } protected: G &amp;m_graph; } template&lt;class T, class V&gt; class Graph { public: virtual ~Graph() {} ... virtual V *add(T val) = 0; virtual void removeVertex(V *vertex) = 0; } template&lt;class T&gt; AdjacencyMatrixGraph; // Forward declaration. template&lt;class T&gt; AMGVertex : public GraphVertex&lt;T, AdjacencyMatrixGraph&lt;T&gt;&gt; { ... } template&lt;class T&gt; AdjacencyMatrixGraph : public Graph&lt;T, AMGVertex&lt;T&gt;&gt; { ... } </code></pre> <p>However, this will not work. It is not possible to use the base class <b>Graph</b> due to circular reference of the base classes.</p> <pre><code>Graph&lt;int&gt; *p = new AdjacencyMatrixGraph&lt;int&gt;(); // Won't work. </code></pre> <p>The example above has the same problems in Java with generics.</p> <p>So, is there anyway to model the relation in a type-safe way? Or am I stuck with casting pointers around?</p> <p>Thank you for reading!</p> <p>EDIT:</p> <p>An example usage of the above could look like:</p> <pre><code>Graph&lt;int&gt; *someGraph = getSomeGraph(); GraphVertex&lt;int&gt; *newVertex = someGraph-&gt;add(3); ... newVertex-&gt;remove(); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload