user61852 has a pretty good answer solving the general problem of simplifying nested conditionals. However, I was intrigued with your proposed state machine-like solution, and wanted to illustrate how that can sometimes be preferable to a set of binary flags.
It all depends on the sequence of obtaining the flags’ values and how many combinations are valid. If you have n
flags, you have 2n states. For 4 flags, that’s 16 states, and as you observed, only 3 of them may have any useful meaning. In situations like that, using a state variable instead can simplify things greatly.
Let’s say you have a lock that will open if 4 keys are pressed in the right order. If you press any key incorrectly, it immediately resets back to the start of the sequence. A very naïve way to implement a keypress handler using binary flags is:
void key_pressed(char key)
{
if (!key1correct)
{
if (key == pin(0))
{
key1correct = true;
}
}
else if (!key2correct)
{
if (key == pin(1))
{
key2correct = true;
}
else
{
key1correct = false;
}
}
else if (!key3correct)
{
if (key == pin(2))
{
key3correct = true;
}
else
{
key1correct = false;
key2correct = false;
}
}
else
{
if (key == pin(3))
{
key4correct = true;
}
else
{
key1correct = false;
key2correct = false;
key3correct = false;
}
}
if (key1correct && key2correct && key3correct && key4correct)
{
open_lock();
key1correct = false;
key2correct = false;
key3correct = false;
key4correct = false;
}
}
A simplified, flattened version using binary flags (like user61852’s answer) looks like this:
void key_pressed(char key)
{
if (!key1correct && key == pin(0))
{
key1correct = true;
return;
}
if (key1correct && !key2correct && key == pin(1))
{
key2correct = true;
return;
}
if (key1correct && key2correct && !key3correct && key == pin(2))
{
key3correct = true;
return;
}
if (key1correct && key2correct && key3correct && key == pin(3))
{
open_lock();
key1correct = false;
key2correct = false;
key3correct = false;
return;
}
key1correct = false;
key2correct = false;
key3correct = false;
}
That’s a lot easier to read, but still in both of these solutions you have states like key2correct && !key1correct
that are completely invalid and unused. Whereas using a state variable num_correct_keys
instead of the binary flags looks like this:
void key_pressed(char key)
{
if (key == pin(num_correct_keys))
++num_correct_keys;
else
num_correct_keys = 0;
if (num_correct_keys == 4)
{
open_lock();
num_correct_keys = 0;
}
}
Granted, this is a contrived example, but people often write code like the first version in less obvious situations, sometimes spread across multiple files. Using a state variable often greatly simplifies code, especially when the flags represent a sequence of events.