If statement replacement

Hello. In games, I notice the need for a large number of if and else if statements. Are there any alternatives?
Last edited on
closed account (SECMoG1T)
AFAIK c++ conditional execution can be currently implemented using the following constructs.

http://www.cplusplus.com/doc/tutorial/control/

hope that helps.
Your question is too broad to be answered in a concise way. No particular strategy is ever correct 100% of the time.
If you gave us an example of a part of the logic that you think has too many if-statements, we could try to help you refactor it.

Otherwise, my answer is refactor your code into functions -- to separate functionality into their own components. This prevents any one function from having too many layers of if/else/while/other conditions n it.

_______________________________________

Edit:
But actually, after thinking about it more, one thing I can think of is to replace if-else with either array offsets, function pointers, or polymorphism.

For example, in some people's code, I will often see constructs like:
1
2
3
4
5
6
7
if (n  == 1)
    thing = 'a';
else if (n == 2)
    thing = 'b';
else if (n == 3) 
    thing = 'c';
etc.


This particular chain can be simplified to just:
thing = n + 'a'; // 0 + 'a' = 'a', 1 + 'a' = 'b', etc.

_______________________________________

Likewise, I sometimes see things like:
1
2
3
4
5
6
7
for (int i = 0; i < 10; i++)
{
    if (i == 0)
        doSpecialThing();
    else
        doThing();
}

This can be simplified to:
1
2
3
4
5
doSpecialThing();
for (int i = 1; i < 10; i++)
{
    doThing();
}

Sometimes, it makes more sense to be an if-statement within the loop though. Depends on what makes the code clearer.

If you find yourself using way too many if-else chains or switch blocks, you may want to consider polymorphism instead -- make the logic in question a virtual function, and just call the function, and the polymorphic lookup table will call the right function for the current subclass object. But this adds its own complexity of having to deal with inheritance and pointers/references.
Last edited on
Depending on the exact circumstances, I can see if-else statements having the following alternatives:

Switch blocks
Function pointers and various function objects
Inheritance
Polymorphism
Templates

Basically, a huge range of alternatives.
This is a very general question, so I'll offer a very general comment based off the other responses:

An if-statement is a kind of selection statement. Selection is just choice-making and is absolutely fundamental: we cannot program a computer without having it make choices.

However, we don't necessarily need to write an if-statement for every choice. Nor do we need to put all our choices in one place, or have long lists of special cases.

One should try to structure their program and its input in order to create uniformity, then exploit that uniformity to minimize the number of choices that need to be made. In a very general sense, the idea of "exploiting uniformity" is called polymorphism, but it's more than virtual functions. This means structuring the program so that data and behavior can be treated without special cases.

The rare blocks of complicated logic that remain in a decently written program can usually be simplified on a case-by-case basis. For instance, by picking a better data structure or lifting the logic into a state machine.

Also worth mentioning, IMO, is that in C++ it's much easier to manipulate data than behavior, so consider lifting behavior into data when possible. This is one important facet of data-driven design.
mbozzi, can you give an example of lifting behavior into data? I've heard the term data-driven design before, but I don't really have a solid concept of it in my head. I've only ever heard of loose, broad examples.
By "lift behavior into data" I mean altering the program so that data controls what the program does. An example is replacing a hard-coded value with input, by adding a function parameter. This is a really boring example, but hopefully adequate because it's easy to see why it might help or hurt a program.

Given some behavior, written down in a function:
1
2
int move_ball(ball& b) 
{ b.x += 2; b.y += 0; };
It might be useful to specify the velocity elsewhere:
1
2
int move_ball(ball& b, vec2 delta)
{ b.x += delta.x; b.y += delta.y; };
This allows the caller to specify the behavior of the ball by passing data around.

This is potentially a benefit, because adding the parameter means that the caller won't need another similar function
1
2
void move_fast_ball(ball& b)
{ b.x += 4; b.y += 0; }

This is also potentially harmful, if the extra flexibility is not required. Perhaps the ball only moves in one direction by definition - then there's an extra parameter that ought not to be a parameter at all.

Maybe in a game a similar transformation involves converting from a hard-coded hierarchy of behaviors
struct player : has_health, has_position {};
Into a set of behavior controlled by data at run-time:
1
2
3
4
5
6
7
struct capabilities {
  bool has_position = false;
  bool has_health = false;
  // ... 
};
struct entity { capabilities c; };
entity make_player();

So that every entity can be treated uniformly. This potentially reduces the number of choices (and sub-classes) from one per combination of capabilities to one per capability.

Also consider function objects like std::greater<void>{}: a bit (well, a byte!) of data that encapsulates the behavior of greater-than comparision. A caller can pass that to std::sort, for instance, to tell the computer to sort values in descending order. Packing the behavior of "compare two objects" into something we can pass around makes a separate sort function for every comparator unnecessary. This ability to manipulate functions like data enables a slew of new programming techniques. These techniques are rich enough that entire programming languages are based on them and the associated theory, so there's plenty of benefit to be drawn.

Another example: consider a regular expression, which is just data that describes the behavior of a subset of finite automata. The regex compiler will build an automaton for the programmer, and it is certainly easier to change a regular expression than the equivalent handwritten parser. And the intent of the change is clearer.
Thank you.
Topic archived. No new replies allowed.