Note that there are some explanatory texts on larger screens.

plurals
  1. POC++ function dispatch with template parameters
    text
    copied!<p>I'm in the process of refactoring a large class -- let's call it <code>Big</code> -- that has a huge amount of copy-paste code. Much of this copy-paste code exists in <code>switch</code> <code>case</code>s where only the types involved end up being different. The code is switching based on an <code>enum</code> member variable of the class whose value is known only at runtime.</p> <p>My attempt to fix this involves having a <code>Dispatcher</code> class that looks up appropriately typed functions via a <code>static</code> function called <code>lookup()</code>. The functions that do the actual work are always called <code>go()</code> and have to be defined in a wrapper class template (whose sole parameter is the runtime <code>enum</code> value currently being switched on). The <code>go()</code> functions may or may not be template functions themselves.</p> <p>Here is a distilled version of the code. My apologies for the length, but this was as short as I could get it without losing important context.</p> <pre><code>#include &lt;cassert&gt; class Big { public: enum RuntimeValue { a, b }; Big(RuntimeValue rv) : _rv(rv) { } bool equals(int i1, int i2) { return Dispatcher&lt;Equals, bool(int, int)&gt;::lookup(_rv)(i1, i2); } template&lt;typename T&gt; bool isConvertibleTo(int i) { return Dispatcher&lt;IsConvertibleTo, bool(int)&gt;::lookup&lt;T&gt;(_rv)(i); } private: template&lt;RuntimeValue RV&gt; struct Equals { static bool go(int i1, int i2) { // Pretend that this is some complicated code that relies on RV // being a compile-time constant. return i1 == i2; } }; template&lt;RuntimeValue RV&gt; struct IsConvertibleTo { template&lt;typename T&gt; static bool go(int i) { // Pretend that this is some complicated code that relies on RV // being a compile-time constant. return static_cast&lt;T&gt;(i) == i; } }; template&lt;template&lt;RuntimeValue&gt; class FunctionWrapper, typename Function&gt; struct Dispatcher { static Function * lookup(RuntimeValue rv) { switch (rv) { case a: return &amp;FunctionWrapper&lt;a&gt;::go; case b: return &amp;FunctionWrapper&lt;b&gt;::go; default: assert(false); return 0; } } template&lt;typename T&gt; static Function * lookup(RuntimeValue rv) { switch (rv) { case a: return &amp;FunctionWrapper&lt;a&gt;::go&lt;T&gt;; case b: return &amp;FunctionWrapper&lt;b&gt;::go&lt;T&gt;; default: assert(false); return 0; } } // And so on as needed... template&lt;typename T1, typename T2&gt; static Function * lookup(RuntimeValue rv); }; RuntimeValue _rv; }; int main() { Big big(Big::a); assert(big.equals(3, 3)); assert(big.isConvertibleTo&lt;char&gt;(123)); } </code></pre> <p>This mostly works, except that:</p> <ol> <li>It builds and works fine under Visual C++ 9 (2008), but under GCC 4.8 it results in compilation errors in the function-template overload of <code>lookup()</code>.</li> <li>It requires that a new function-template overload of <code>lookup()</code> be written for every new number of function template parameters that we want to support in <code>go()</code>.</li> <li>It's cumbersome and confusing to use.</li> </ol> <p>Here are the errors that occur under GCC:</p> <pre><code>Big.cpp: In static member function 'static Function* Big::Dispatcher&lt;FunctionWrapper, Function&gt;::lookup(Big::RuntimeValue)': Big.cpp(66,65) : error: expected primary-expression before '&gt;' token case a: return &amp;FunctionWrapper&lt;a&gt;::go&lt;T&gt;; ^ Big.cpp(66,66) : error: expected primary-expression before ';' token case a: return &amp;FunctionWrapper&lt;a&gt;::go&lt;T&gt;; ^ Big.cpp(67,65) : error: expected primary-expression before '&gt;' token case b: return &amp;FunctionWrapper&lt;b&gt;::go&lt;T&gt;; ^ Big.cpp(67,66) : error: expected primary-expression before ';' token case b: return &amp;FunctionWrapper&lt;b&gt;::go&lt;T&gt;; ^ </code></pre> <p>My question is twofold:</p> <ol> <li>Why is this failing to build under GCC, and how do I fix it?</li> <li>Is there a better (i.e., less cumbersome and confusing) way to do this?</li> </ol> <p>The code has to be compilable under Visual C++ 9 (2008), so I can't use anything C++11-specific.</p>
 

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