Library of Assembled Shared Sources
lass::num::TriBool Class Reference

a three-state boolean value with { true, false and lass::num::unknown } More...

#include <tri_bool.h>

Related Symbols

(Note that these are not member symbols.)

const TriBool unknown (TriBool::sUnknown)
 constant to be used as new keyword like true and false
 

Detailed Description

a three-state boolean value with { true, false and lass::num::unknown }

Author
Bram de Greve [BdG]

Inspired by Boost three-state boolean logic library 'boost::tribool' by Doug Gregor (grego.nosp@m.d@cs.nosp@m..rpi..nosp@m.edu).

Booleans are great to express binary states, to express if something is true or false. But they lack the concept of Not-A-Number (NaN). Indeed, with traditional booleans, there's no way to express that you don't know, that the boolean value is invalid. That's when this TriBool comes into action. TriBool adds one extra state to the traditional true and false: unknown. As said, this unknown acts like the NaN for floating point values and behaves much the same way (though not entirely the same!).

behaviour of unknown

The first thing to know is that the value unknown can not come into existance as long as all your operands are in a known state (true or false). You have to inject an unknown value yourself, and this section merely describes how it will flow through your code.

rule: In TriBool unary expressions, if the argument is unknown, then the result will yield unknown too..

Check out operator! Indeed, if a is unknown, then !a is unknown too.

rule: In TriBool binary expressions, if at least one of the operands is unknown, then the result yields unknown, except if one of the operands is known (true or false) and provides enough information to result in a known state.

The first part is obvious. In an expression a == b , if one of the operands is unknown, we can not possible know if a and b are equal or not. It will yield unknown. However, there are two situations in which a binary expression with an unknown operands still results in a known state. These are:

  • false && unknown yields false. Indeed, for an AND operation to yield true, both operands must be true. Since we know at least one of them is false, we can know for sure the result is false too.
  • true || unknown yields true. Indeed, for an OR operation to yield true, at least one of the operands must be true. Since we know the left operand is true we know for sure we have at least one operand that is true, hence the result is true.

Since AND and OR are commutative, this is also valid for unknown && false and unknown || true . In all other cases, a binary expression with at least one unknown operand will yield unknown.

Note
we said unknown behaves much like NaN, but that's not really true. If you compare two operands that are NaN, you'll get false as result (a known state), where as if both operands are unknown, it will not result false but unknown!
in contrary to traditional boolean logic, a!=b || a==b does not always yield true! It will be unknown if one of the operands is unknown.

consequences on if/else

Care must be taken when TriBool variables are used in boolean context like if/else statements (and for and while loops ...). For instance, the following examples are not equivalent (as they would be for plain old boolean logic). Notice what happens if a is unknown!

TriBool a;
if (a)
{
foo(); // called if a is true.
}
else
{
bar(); // called if a is false OR unknown.
}
if (!a)
{
bar(); // called if a is false
}
else
{
foo(); // called if a is true OR unknown.
}

The discrepancy is due to the fact there isn't an unambigious way to reduce the three states of a TriBool to the two states of the if/else statement, or the traditional boolean logic. In the former, the if block tests if a is true and the else block gets the other two states. In the latter, the if block tests if a is false and again the else block gets the other two states. So, in any case, if a is unknown, you'll end up in the else block.

If you want to test for the three different cases in an if/else construction, your code will typically look like the following:

if (a)
{
foo(); // called if a is true
}
else if (!a)
{
bar(); // called if a is false
}
else
{
fun(); // called if a is unknown
}

This is equivalent to:

switch (a.state())
{
case TriBool::sTrue:
foo();
break;
case TriBool::sFalse:
bar();
break;
default:
LASS_ASSERT(a.state() == TriBool::sUnknown);
fun();
break;
}
Warning
a common mistake is to test if a TriBool a is in a unknown state by comparing it to the constant unknown. You might expect it to yield true if a is indeed, unknown, but it will always return unknown instead. After all, it cannot know if both sides are equal, since at least one is unknown (unknown itself). To test if a TriBool is indeed unknown, you have to use the method TriBool::isUnknown().
TriBool a;
if (a == unknown)
{
// BAD! unreachable code, the test will never yield true.
}
if (a.isUnknown())
{
// GOOD! this code will be reached if the state of a is unknown.
}
const TriBool unknown(TriBool::sUnknown)
constant to be used as new keyword like true and false

Definition at line 199 of file tri_bool.h.


The documentation for this class was generated from the following file: