Author Topic: Converting Switch Statements To Table Lookup  (Read 1726 times)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Converting Switch Statements To Table Lookup
« on: March 20, 2010, 08:52:21 PM »
Switch statements can often be reduced to table lookup, which makes code shorter, easier to read, and easier to modify.


Here is the perfectly ideal situation where you can apply this transformation:
1) The case values are all consecutive integers in a small range, starting from 0, with no gaps.
2) The code is identical in every case statement, aside from a few changed constants.
3) There is no else part to the switch statement.
We can easily relax some of these constraints as we'll see later on.

In the perfectly ideal case you can make the following transformation:
Code: [Select]
// Original code
switch(switchValue)
{
case 0:
  doStuff(constant0);
  break;
case 1:
  doStuff(constant1);
  break;
case 2:
  doStuff(constant2);
  break;
// ...
}

Code: [Select]
// Modified code
// Note: "DataType" will probably be int.
const DataType constArray = { constant0, constant1, constant2, ... };
doStuff(constArray(switchValue));

You can also extend this to the non perfectly ideal cases. For instance, if the switch value didn't start from 0, but otherwise has no gaps you could just adjust the range back to start at 0 by subtracting from the switch value:
Code: [Select]
// Original code
switch(switchValue)
{
case 500:
  doStuff(constant0);
  break;
case 501:
  doStuff(constant1);
  break;
case 502:
  doStuff(constant2);
  break;
// ...
}

Code: [Select]
// Modified code
// Note: "DataType" will probably be int.
const DataType constArray = { constant0, constant1, constant2, ... };
doStuff(constArray(switchValue - 500));


Perhaps there is an else clause as well:
Code: [Select]
// Original code
switch(switchValue)
{
case 500:
  doStuff(constant0);
  break;
case 501:
  doStuff(constant1);
  break;
case 502:
  doStuff(constant2);
  break;
// ...
case else:
  doSomethingElse();
}

Code: [Select]
// Modified code
// Note: "DataType" will probably be int.
const DataType constArray = { constant0, constant1, constant2, ... };
if (switchValue - 500 < constArrayLength)
{
    doStuff(constArray(switchValue - 500));
}
else
{
    doSomethingElse();
}


You can also extend the idea in other ways, but I think I'll stop there. That covers the majority of the cases where I see switch statements used.



As a side note, logic often has a chaotic "appearance", while data is often highly regular. If you compare the code section to the data section of an executable file in a hex editor, you will notice this. Similarly, if you look at the die of a CPU, and compare the area implementing the computational units to the area used for memory and caches you will notice this. If you look at your source code, you probably should notice this. If you have regular looking code, it probably means you're mixing code and data, and you might get a better design by separating them out. Such a separated design is usually more flexible, and easier to upgrade or fix bugs in. It is also often faster or of comparable speed.