Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You can't get away without storing the list of all possible geometries in <em>some</em> place. Otherwise the compiler won't know which template instantiations to generate. But I've come up with some code where you have to state that list in only a single location, the typedef of <code>GeometryTypes</code>. Everything else builds on that. I'm not using a vistor pattern here, which has the benefit that I you don't have to add boilerplate code to your different geometry class implementations. Implementing <code>intersects</code> for all combinations is enough.</p> <p>First some includes: I'll be using <code>shared_ptr</code> later on, and printing stuff, and aborting in case of unknown geometry types.</p> <pre><code>#include &lt;memory&gt; #include &lt;iostream&gt; #include &lt;cstdlib&gt; </code></pre> <p>Now define some geometries, with a common base class which you can use for polymorphic pointers. You have to include at least one virtual function, so that you get a virtual function table which can be used for <code>dynamic_cast</code> later on. Making the destructor polymorphic ensures that derived classes will be cleaned up properly even if deleted via a polymorphic pointer.</p> <pre><code>struct Geometry { virtual ~Geometry() { } }; struct Circle : public Geometry { }; struct Rectangle : public Geometry { }; </code></pre> <p>Now comes your <code>intersects</code> template. I only write a single catch-all implementation for this demo.</p> <pre><code>template&lt;typename lhs_geometry, typename rhs_geometry&gt; bool intersects(const lhs_geometry&amp; lhs, const rhs_geometry&amp; rhs) { std::cout &lt;&lt; __PRETTY_FUNCTION__ &lt;&lt; " called\n"; // gcc-specific? return false; } </code></pre> <p>This is the place where we declare the list of all geometries. If you have geometries derived from one another, make sure to have the most specific ones first, as these will be tried in order for dynamic casts.</p> <pre><code>template&lt;typename... Ts&gt; class TypeList { }; typedef TypeList&lt;Circle, Rectangle&gt; GeometryTypes; </code></pre> <p>Now a bunch of helper code. The basic idea is to iterate over one such <code>TypeList</code> and try a dynamic cast for every type. The first helper iterates for the lhs argument, the second for the rhs argument. If no match is found, you have an incomplete list, which will cause the application to abort with a hopefully useful error message.</p> <pre><code>template&lt;typename TL1, typename TL2&gt; struct IntersectHelper1; template&lt;typename T1, typename TL2&gt; struct IntersectHelper2; template&lt;typename TL2, typename T1, typename... Ts&gt; struct IntersectHelper1&lt;TypeList&lt;T1, Ts...&gt;, TL2&gt; { static bool isects(Geometry* lhs, Geometry* rhs) { T1* t1 = dynamic_cast&lt;T1*&gt;(lhs); if (!t1) return IntersectHelper1&lt;TypeList&lt;Ts...&gt;, TL2&gt;::isects(lhs, rhs); else return IntersectHelper2&lt;T1, TL2&gt;::isects(t1, rhs); } }; template&lt;typename T1, typename T2, typename... Ts&gt; struct IntersectHelper2&lt;T1, TypeList&lt;T2, Ts...&gt;&gt; { static bool isects(T1* lhs, Geometry* rhs) { T2* t2 = dynamic_cast&lt;T2*&gt;(rhs); if (!t2) return IntersectHelper2&lt;T1, TypeList&lt;Ts...&gt;&gt;::isects(lhs, rhs); else return intersects(*lhs, *t2); } }; // Catch unknown types, where all dynamic casts failed: bool unknownIntersects(Geometry* g) { std::cerr &lt;&lt; "Intersection with unknown type: " &lt;&lt; typeid(*g).name() &lt;&lt; std::endl; std::abort(); return false; // should be irrelevant due to abort } template&lt;typename TL2&gt; struct IntersectHelper1&lt;TypeList&lt;&gt;, TL2&gt; { static bool isects(Geometry* lhs, Geometry* rhs) { return unknownIntersects(lhs); } }; template&lt;typename T1&gt; struct IntersectHelper2&lt;T1, TypeList&lt;&gt;&gt; { static bool isects(T1* lhs, Geometry* rhs) { return unknownIntersects(rhs); } }; </code></pre> <p>With all these helpers in place, you can now do a polymorphic intersection test. I'm introducing a <code>shared_ptr</code> to store such polymorphic pointers, and I suggest you do the same in your <code>collidable_object</code> class. Otherwise you'd have to take responsibility of ensuring that the referenced geometries stay alive as long as the collidable object is alive, but will get cleaned up eventually. Do you want that kind of responsibility?</p> <pre><code>typedef std::shared_ptr&lt;Geometry&gt; GeomPtr; bool intersects(GeomPtr lhs, GeomPtr rhs) { return IntersectHelper1&lt;GeometryTypes, GeometryTypes&gt;:: isects(lhs.get(), rhs.get()); } </code></pre> <p>And finally some main so you can actually run all of the above code in a tiny example.</p> <pre><code>int main() { GeomPtr g1(new Rectangle), g2(new Circle); std::cout &lt;&lt; intersects(g1, g2) &lt;&lt; std::endl; return 0; } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
 

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