alternatives to long else if and switch statements

Started by
15 comments, last by wack 10 years, 10 months ago

I`m working on a project where there is a massive amount of library/engine files being used from the last project. In quite a few there are crazily long if-else and even nested trailing if-else statements making some changes hard to debug and sometimes it`s hard to see where the missing curly bracket is on compiling errors.

So I`d like to know about some valid alternatives. Obviously there`s the switch statement, but that would also get unmanageable by being overly long and nested too.

I was thinking of arrays of function pointers indexed by case, but in a non-deterministic program surely this would cause program execution to jump all over the place and was wondering on the impact on performance.

Obviously, like many things you can never have it all, something will suffer as a consequence of the benefit of something else but are there any suggestions?

Thanks

Advertisement

I was thinking of arrays of function pointers indexed by case, but in a non-deterministic program surely this would cause program execution to jump all over the place and was wondering on the impact on performance.

If this is what your program needs to do, then that is what it needs to do. If it doesn't need to do it, then don't! If profiling indicates that this is a critical inner loop, consider if the inputs can be sorted, so that each batch of similar inputs takes a similar path.

A long, contiguous switch statement can be optimised by the compiler to the equivalent of this array of function pointers, called a "jump table".

What kinds of situations do these "crazily long" chains appear in? Concrete examples are usually much easier to talk about, and have the benefit of hinting at high level changes in approach that are far less obvious when talking about abstract situations.

You could off course go with a state machine that does the switch changing in a polymorphic manner if they share enough common code to deal with that approach. Often looks nicer and is off course easier to control and extend in the long run.

As a side note here is how a 5 case switch statement looks in unoptimised C++: http://www.altdevblogaday.com/2012/04/10/cc-low-level-curriculum-part-7-more-conditionals/

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

In this case I would not yet care too much about performance. Primary target should always be readability and maintainability of your code. At work, I have to deal with legacy code containing, in multiple places, switch statements and deeply nested if statements with in some cases more than 5000 LOC. This is a programmer's nightmare come true.

Write the simplest, most easily maintainable version you can think of (using a function pointer table or inheritance) and use that, until you have some evidence (i.e. after you have profiled your code) that this has a measurably bad impact on your program's overall performance.

I`m working on a project where there is a massive amount of library/engine files being used from the last project. In quite a few there are crazily long if-else and even nested trailing if-else statements making some changes hard to debug and sometimes it`s hard to see where the missing curly bracket is on compiling errors.

As rnlf points out, readability should be the primary goal. Some techniques:

- Remove reduntant conditions (the most extreme being if (x) … else if (!x) …", but there are other more subtle ones).

- Move the conditions inside the "if(…)" into separate functions. Try to find good names for those functions.

- Move the statements inside the brackets into separate functions. Try to find good names for those functions.

- Sarch for code duplication inside those conditions and statements and put that into separate functions too. Try to find good names for those functions.

- Try to keep the if-elseif-…-else chain consistent. Sometimes it's more logical to split one long chain into two separate ones. Sometimes you can merge two chains into one.

openwar - the real-time tactical war-game platform

With more information on what that piece of code is doing, it would be possible to suggest more appropriate solutions.

At the very least, one big switch statement is more expressive and possibly faster to execute than a long if-else if-...-else chain. Function pointers, signals and slots, and polymorphism are all similar constructs that might be good alternatives, depending on what the code is doing.

It is hard to suggest solutions without seeing the actual code, but generally:

  1. If the code could possibly be a switch-statement then a switch statement would be better than an if-else chain
  2. The code in the case handlers of the switch statement can be easily broken out into functions and if there is a lot of it this is probably a good idea.
  3. If you can do 1. and 2. then it is also possible to set it up as a hash table mapping from values of whatever you are switching on to instances of some kind of handler object, which could just be function pointers/functors but also could be some object that has state if that makes sense.
  4. If it's possible to do 1, 2, and 3 then you could also probably skip the hash table and do the switch functionality as a polymorphically called method of something like the handler objects in 3.

FWIW: IME, a more direct jump through a function pointer can actually be notably faster than going through a long if/else chain, and in-fact in some cases I have actually used function pointers to optimize cases like this (where what would normally require such a long chain can be statically determined to have a particular result, and so a direct jump to the result-logic can be used instead).

but, yeah, unless performance is a big issue here, it should probably be more about code organization and readability than about performance though.

In this case I would not yet care too much about performance. Primary target should always be readability and maintainability of your code. At work, I have to deal with legacy code containing, in multiple places, switch statements and deeply nested if statements with in some cases more than 5000 LOC. This is a programmer's nightmare come true.

Write the simplest, most easily maintainable version you can think of (using a function pointer table or inheritance) and use that, until you have some evidence (i.e. after you have profiled your code) that this has a measurably bad impact on your program's overall performance.

Exactly the kind of situation I`m facing. My colleague thinks it`s super ugly too, and thinks the company we`ve inherited it from must have had some super eccentric coders. 300 line functions and 15000 line files. Crazy. It`s even worse when you have to insert #defines and #ifdefs in between it all to handle seperate formats.

Thanks for the advice, and good luck to you with your code too!

Thanks to everyone for the advice, but due to the sheer length and size of the legacy code it will probably be best just to soldier on. However, if I ever had to start a new project from scratch with many conditionals, to avoid this current situation I will keep all of your tips and handy hints in mind.

As for code examples, I feel a little apprehensive about cutting and pasting production code on a public forum!

Thanks again

This topic is closed to new replies.

Advertisement