var ops = new Dictionary<Opcode, Action<opcodeArgs>> ()
{
{0, nop },
{1, add },
...
}
or even better, maybe I'd get a list of the opcodes with their names, name all the functions after those names exactly, then use reflection to automatically create this dictionary.Then in the main loop,
ops[opcode].Invoke(args);
I don't know if something like that would be any use in C, though.For example, there are measurable differences in branch prediction based on whether each opcode's implementation ends with its own dispatch code or branches back to a common dispatch routine. The sequence of instructions is basically the same in both cases, but there is an icache/branch prediction tradeoff to be made. Subtle stuff (see http://repo.or.cz/w/luajit-2.0.git/blob/0ded8e82a88fadb40b4d...)
I often hear this from programmers coming from a higher-level background. O(1) only means constant time. It doesn't mean quickly. This function:
int foo(int x) {
sleep(3600);
return x;
}
is O(1), just as this: int bar(int x) {
return x;
}
Foo takes an hour to commplete, but it's just as O(1) as bar :-).switch is O(1) and fast as the operation is usually 2-3 deterministic time instructions:
1. Look up jump address from lookup table made by compiler.
2. Jump to it.
More people need to have written assembly to actually get this into their heads.
jmp [lookupTable + eax]In general, if I need something to avoid function calls, I'll use macros as a shorthand for a complex expression (like NEXT_BYTE) and inline functions for anything that involves one or more full statements (like set_flags). Obviously I don't follow that too dogmatically, because mem_abs is an inline function.
So when I read the author's code and saw the way he handled a similar problem, I was intrigued. If you had to deal with a massive switch statement with thousands of cases, it would probably be easier to maintain IMHO.
For a hobby project, the author's solution is 100% correct and I'm not critiquing it at all. In fact, the only reason I even commented was because I thought his solution was better than any other procedural solution I had seen before.
With that said, in an enterprise environment, where maintainability is crucial, I'd argue that a massive switch statement is probably a bad idea. Going back to messloop.c, what happens if the user tries to change a floating point value through the UI? Well, I can tell you that there is a case statement for that somewhere in messloop.c. What is that case statement called? I'm not sure, all I know is it's a #define that I'm sure made sense to the original author. It's basically a needle-in-a-haystack problem.
However, a lot of desktop applications are written that way. If you've ever dealt with Win32, you'll see nested switch statements from hell on your average project.
If you have a pure OO language like Java or C# then there's no excuse but some legacy applications built in C tend to be "switchy" because the older APIs seem to favour that form of message dispatch.
There is still no excuse as you can have decent abstraction in C or C++.
However for what is effectively a jump table, a switch statement is exactly spot on for this project.