Library of Assembled Shared Sources
tri_bool.h
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2011 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43
44
45/** @class lass::num::TriBool
46 * @brief a three-state boolean value with { true, false and lass::num::unknown }
47 * @author Bram de Greve [BdG]
48 *
49 * Inspired by Boost three-state boolean logic library 'boost::tribool' by Doug Gregor
50 * (gregod@cs.rpi.edu).
51 *
52 * Booleans are great to express binary states, to express if something is true or false. But they
53 * lack the concept of @e Not-A-Number (@c NaN). Indeed, with traditional booleans, there's no way
54 * to express that you don't know, that the boolean value is invalid. That's when this TriBool
55 * comes into action. TriBool adds one extra state to the traditional @c true and @c false:
56 * @c unknown. As said, this @c unknown acts like the @c NaN for floating point values and behaves
57 * much the same way (though not @e entirely the same!).
58 *
59 * @section behaviour_unknown behaviour of unknown
60 *
61 * The first thing to know is that the value @c unknown can not come into existance as long as all
62 * your operands are in a known state (@c true or @c false). You have to inject an @c unknown
63 * value yourself, and this section merely describes how it will flow through your code.
64 *
65 * @b rule: <i>In TriBool unary expressions, if the argument is @c unknown, then the result will
66 * yield @c unknown too.</i>.
67 *
68 * Check out @c operator! Indeed, if @c a is @c unknown, then @c !a is @c unknown too.
69 *
70 * @b rule: <i>In TriBool binary expressions, if at least one of the operands is @c unknown, then
71 * the result yields @c unknown, @b except if one of the operands is known (@c true or
72 * @c false) and provides enough information to result in a known state.</i>
73 *
74 * The first part is obvious. In an expression @c a @c == @c b , if one of the operands is
75 * @c unknown, we can not possible know if @c a and @c b are equal or not. It will yield
76 * @c unknown. However, there are two situations in which a binary expression with an unknown
77 * operands still results in a known state. These are:
78 *
79 * - @c false @c && @c unknown yields @c false. Indeed, for an AND operation to yield @c true,
80 * @e both operands must be @c true. Since we know at least one of them is @c false, we can know
81 * for sure the result is @c false too.
82 * - @c true @c || @c unknown yields @c true. Indeed, for an OR operation to yield @c true,
83 * at @e least one of the operands must be @c true. Since we know the left operand is @c true
84 * we know for sure we have at least one operand that is true, hence the result is @c true.
85 *
86 * Since AND and OR are commutative, this is also valid for @c unknown @c && @c false and
87 * @c unknown @c || @c true . In all other cases, a binary expression with at least one @c unknown
88 * operand will yield @c unknown.
89 *
90 * @note we said @c unknown behaves much like @c NaN, but that's not really true. If you
91 * compare two operands that are @c NaN, you'll get @c false as result (a known state),
92 * where as if both operands are @c unknown, it will not result @c false but @c unknown!
93 *
94 * @note in contrary to traditional boolean logic, @c a!=b @c || @c a==b does not always yield
95 * @c true! It will be @c unknown if one of the operands is unknown.
96 *
97 * @section if_consequences consequences on if/else
98 *
99 * Care must be taken when TriBool variables are used in boolean context like @c if/else statements
100 * (and @c for and @c while loops ...). For instance, the following examples are not equivalent
101 * (as they would be for plain old boolean logic). Notice what happens if @c a is unknown!
102 *
103 * @code
104 * TriBool a;
105 *
106 * if (a)
107 * {
108 * foo(); // called if a is true.
109 * }
110 * else
111 * {
112 * bar(); // called if a is false OR unknown.
113 * }
114 *
115 * if (!a)
116 * {
117 * bar(); // called if a is false
118 * }
119 * else
120 * {
121 * foo(); // called if a is true OR unknown.
122 * }
123 * @endcode
124 *
125 * The discrepancy is due to the fact there isn't an unambigious way to reduce the three states
126 * of a TriBool to the two states of the @c if/else statement, or the traditional boolean logic.
127 * In the former, the @c if block tests if @c a is @c true and the @c else block gets the other
128 * two states. In the latter, the @c if block tests if @c a is @c false and again the @c else
129 * block gets the other two states. So, in any case, if @c a is unknown, you'll end up in the
130 * @c else block.
131 *
132 * If you want to test for the three different cases in an @c if/else construction, your code will
133 * typically look like the following:
134 *
135 * @code
136 * if (a)
137 * {
138 * foo(); // called if a is true
139 * }
140 * else if (!a)
141 * {
142 * bar(); // called if a is false
143 * }
144 * else
145 * {
146 * fun(); // called if a is unknown
147 * }
148 * @endcode
149 *
150 * This is equivalent to:
151 *
152 * @code
153 * switch (a.state())
154 * {
155 * case TriBool::sTrue:
156 * foo();
157 * break;
158 * case TriBool::sFalse:
159 * bar();
160 * break;
161 * default:
162 * LASS_ASSERT(a.state() == TriBool::sUnknown);
163 * fun();
164 * break;
165 * }
166 * @endcode
167 *
168 * @warning a common mistake is to test if a TriBool @c a is in a unknown state by comparing it to
169 * the constant @c unknown. You might expect it to yield @c true if @c a is indeed,
170 * unknown, but it will always return @c unknown instead. After all, it cannot know if
171 * both sides are equal, since at least one is unknown (@c unknown itself). To test if
172 * @c a TriBool is indeed unknown, you have to use the method @c TriBool::isUnknown().
173 *
174 * @code
175 * TriBool a;
176 *
177 * if (a == unknown)
178 * {
179 * // BAD! unreachable code, the test will never yield true.
180 * }
181 *
182 * if (a.isUnknown())
183 * {
184 * // GOOD! this code will be reached if the state of a is unknown.
185 * }
186 * @endcode
187 */
188
189#ifndef LASS_GUARDIAN_OF_INCLUSION_NUM_TRI_BOOL_H
190#define LASS_GUARDIAN_OF_INCLUSION_NUM_TRI_BOOL_H
191
192#include "num_common.h"
193
194namespace lass
195{
196namespace num
197{
198
199class LASS_DLL TriBool
200{
201public:
202
203 enum State
204 {
205 sFalse,
206 sTrue,
207 sUnknown
208 };
209
210 TriBool(State iState = sUnknown);
211 TriBool(bool iBool);
212
213 State state() const;
214 State& state();
215
216 TriBool operator!() const;
217 explicit operator bool() const;
218
219 bool isTrue() const;
220 bool isFalse() const;
221 bool isUnknown() const;
222
223private:
224
225 State state_;
226};
227
228LASS_DLL TriBool LASS_CALL operator==(TriBool iA, TriBool iB);
229LASS_DLL TriBool LASS_CALL operator!=(TriBool iA, TriBool iB);
230LASS_DLL TriBool LASS_CALL operator&&(TriBool iA, TriBool iB);
231LASS_DLL TriBool LASS_CALL operator||(TriBool iA, TriBool iB);
232
233LASS_DLL std::ostream& LASS_CALL operator<<(std::ostream& ioS, TriBool iB);
234
235
236
237/** constant to be used as new keyword like @c true and @c false
238 * @relates TriBool
239 */
240const TriBool unknown(TriBool::sUnknown);
241
242}
243
244}
245
246#endif
247
248// EOF
a three-state boolean value with { true, false and lass::num::unknown }
Definition tri_bool.h:200
const TriBool unknown(TriBool::sUnknown)
constant to be used as new keyword like true and false
#define LASS_DLL
DLL interface: import or export symbols?
numeric types and traits.
Definition basic_ops.h:70
Library for Assembled Shared Sources.
Definition config.h:53