I have a method where performance is really important (I know premature optimization is the root of all evil. I know I should and I did profile my code. In this application every tenth of a second I save is a big win.) This method uses different heuristics to generate and return elements. The heuristics are used sequentially: the first heuristic is used until it can no longer return elements, then the second heuristic is used until it can no longer return elements and so on until all heuristics have been used. On each call of the method I use a switch to move to the right heuristic. This is ugly, but work well. Here is some pseudo code
class MyClass
{
private:
unsigned int m_step;
public:
MyClass() : m_step(0) {};
Elem GetElem()
{
// This switch statement will be optimized as a jump table by the compiler.
// Note that there is no break statments between the cases.
switch (m_step)
{
case 0:
if (UseHeuristic1())
{
m_step = 1; // Heuristic one is special it will never provide more than one element.
return theElem;
}
m_step = 1;
case 1:
DoSomeOneTimeInitialisationForHeuristic2();
m_step = 2;
case 2:
if (UseHeuristic2())
{
return theElem;
}
m_step = 3;
case 3:
if (UseHeuristic3())
{
return theElem;
}
m_step = 4; // But the method should not be called again
}
return someErrorCode;
};
}
As I said, this works and it's efficient since at each call, the execution jumps right where it should. If a heuristic can't provide an element, m_step is incremented (so the next time we don't try this heuristic again) and because there is no break statement, the next heuristic is tried. Also note that some steps (like step 1) never return an element, but are one time initialization for the next heuristic.
The reason initializations are not all done upfront is that they might never be needed. It is always possible (and common) for GetElem to not get called again after it returned an element, even if there are still elements it could return.
While this is an efficient implementation, I find it really ugly. The case statement is a hack; using it without break is also hackish; the method gets really long, even if each heuristic is encapsulated in its own method.
How should I refactor this code so it's more readable and elegant while keeping it as efficient as possible?