library of assembled shared sources

http://lass.cocamware.com

tri_bool.h

Go to the documentation of this file.
00001 /** @file
00002  *  @author Bram de Greve (bramz@users.sourceforge.net)
00003  *  @author Tom De Muer (tomdemuer@users.sourceforge.net)
00004  *
00005  *  *** BEGIN LICENSE INFORMATION ***
00006  *  
00007  *  The contents of this file are subject to the Common Public Attribution License 
00008  *  Version 1.0 (the "License"); you may not use this file except in compliance with 
00009  *  the License. You may obtain a copy of the License at 
00010  *  http://lass.sourceforge.net/cpal-license. The License is based on the 
00011  *  Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover 
00012  *  use of software over a computer network and provide for limited attribution for 
00013  *  the Original Developer. In addition, Exhibit A has been modified to be consistent 
00014  *  with Exhibit B.
00015  *  
00016  *  Software distributed under the License is distributed on an "AS IS" basis, WITHOUT 
00017  *  WARRANTY OF ANY KIND, either express or implied. See the License for the specific 
00018  *  language governing rights and limitations under the License.
00019  *  
00020  *  The Original Code is LASS - Library of Assembled Shared Sources.
00021  *  
00022  *  The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
00023  *  The Original Developer is the Initial Developer.
00024  *  
00025  *  All portions of the code written by the Initial Developer are:
00026  *  Copyright (C) 2004-2007 the Initial Developer.
00027  *  All Rights Reserved.
00028  *  
00029  *  Contributor(s):
00030  *
00031  *  Alternatively, the contents of this file may be used under the terms of the 
00032  *  GNU General Public License Version 2 or later (the GPL), in which case the 
00033  *  provisions of GPL are applicable instead of those above.  If you wish to allow use
00034  *  of your version of this file only under the terms of the GPL and not to allow 
00035  *  others to use your version of this file under the CPAL, indicate your decision by 
00036  *  deleting the provisions above and replace them with the notice and other 
00037  *  provisions required by the GPL License. If you do not delete the provisions above,
00038  *  a recipient may use your version of this file under either the CPAL or the GPL.
00039  *  
00040  *  *** END LICENSE INFORMATION ***
00041  */
00042 
00043 
00044 
00045 /** @class lass::num::TriBool
00046  *  @brief a three-state boolean value with { true, false and lass::num::unknown }
00047  *  @author Bram de Greve [BdG]
00048  *
00049  *  Inspired by Boost three-state boolean logic library 'boost::tribool' by Doug Gregor
00050  *  (gregod@cs.rpi.edu).
00051  *
00052  *  Booleans are great to express binary states, to express if something is true or false.  But they
00053  *  lack the concept of @e Not-A-Number (@c NaN).  Indeed, with traditional booleans, there's no way
00054  *  to express that you don't know, that the boolean value is invalid.  That's when this TriBool
00055  *  comes into action.  TriBool adds one extra state to the traditional @c true and @c false:
00056  *  @c unknown.  As said, this @c unknown acts like the @c NaN for floating point values and behaves
00057  *  much the same way (though not @e entirely the same!).
00058  *
00059  *  @section behaviour_unknown behaviour of unknown
00060  *
00061  *  The first thing to know is that the value @c unknown can not come into existance as long as all
00062  *  your operands are in a known state (@c true or @c false).  You have to inject an @c unknown
00063  *  value yourself, and this section merely describes how it will flow through your code.
00064  *
00065  *  @b rule: <i>In TriBool unary expressions, if the argument is @c unknown, then the result will
00066  *           yield @c unknown too.</i>.
00067  *
00068  *  Check out @c operator!  Indeed, if @c a is @c unknown, then @c !a is @c unknown too.
00069  *
00070  *  @b rule: <i>In TriBool binary expressions, if at least one of the operands is @c unknown, then
00071  *           the result yields @c unknown, @b except if one of the operands is known (@c true or
00072  *           @c false) and provides enough information to result in a known state.</i>
00073  *
00074  *  The first part is obvious.  In an expression @c a @c == @c b , if one of the operands is
00075  *  @c unknown, we can not possible know if @c a and @c b are equal or not.  It will yield
00076  *  @c unknown.  However, there are two situations in which a binary expression with an unknown
00077  *  operands still results in a known state.  These are:
00078  *
00079  *  - @c false @c && @c unknown yields @c false.  Indeed, for an AND operation to yield @c true,
00080  *    @e both operands must be @c true.  Since we know at least one of them is @c false, we can know
00081  *    for sure the result is @c false too.
00082  *  - @c true @c || @c unknown yields @c true.  Indeed, for an OR operation to yield @c true,
00083  *    at @e least one of the operands must be @c true.  Since we know the left operand is @c true
00084  *    we know for sure we have at least one operand that is true, hence the result is @c true.
00085  *
00086  *  Since AND and OR are commutative, this is also valid for @c unknown @c && @c false and
00087  *  @c unknown @c || @c true .  In all other cases, a binary expression with at least one @c unknown
00088  *  operand will yield @c unknown.
00089  *
00090  *  @note we said @c unknown behaves much like @c NaN, but that's not really true.  If you
00091  *        compare two operands that are @c NaN, you'll get @c false as result (a known state),
00092  *        where as if both operands are @c unknown, it will not result @c false but @c unknown!
00093  *
00094  *  @note in contrary to traditional boolean logic, @c a!=b @c || @c a==b does not always yield
00095  *        @c true!  It will be @c unknown if one of the operands is unknown.
00096  *
00097  *  @section if_consequences consequences on if/else
00098  *
00099  *  Care must be taken when TriBool variables are used in boolean context like @c if/else statements
00100  *  (and @c for and @c while loops ...).  For instance, the following examples are not equivalent
00101  *  (as they would be for plain old boolean logic).  Notice what happens if @c a is unknown!
00102  *
00103  *  @code
00104  *  TriBool a;
00105  *
00106  *  if (a)
00107  *  {
00108  *      foo(); // called if a is true.
00109  *  }
00110  *  else
00111  *  {
00112  *      bar(); // called if a is false OR unknown.
00113  *  }
00114  *
00115  *  if (!a)
00116  *  {
00117  *      bar(); // called if a is false
00118  *  }
00119  *  else
00120  *  {
00121  *      foo(); // called if a is true OR unknown.
00122  *  }
00123  *  @endcode
00124  *
00125  *  The discrepancy is due to the fact there isn't an unambigious way to reduce the three states
00126  *  of a TriBool to the two states of the @c if/else statement, or the traditional boolean logic.
00127  *  In the former, the @c if block tests if @c a is @c true and the @c else block gets the other
00128  *  two states.  In the latter, the @c if block tests if @c a is @c false and again the @c else
00129  *  block gets the other two states.  So, in any case, if @c a is unknown, you'll end up in the
00130  *  @c else block.
00131  *
00132  *  @note as you might notice, there's no conversion operator to @c bool.  That's because of the
00133  *        ambigious mapping from TriBool to @c bool as described above.  But then how does
00134  *        @c if @c (a) works?  If does need a boolean value, doesn't it?  The trick is in the
00135  *        @c operator @c SafeBool().  It will return a non-NULL pointer if and only if the TriBool
00136  *        is @c true, what invokes the @c if block.  If the TriBool is @c false or @c unknown, then
00137  *        a NULL pointer is returned instead and the @c else block is called.
00138  *
00139  *  If you want to test for the three different cases in an @c if/else construction, your code will
00140  *  typically look like the following:
00141  *
00142  *  @code
00143  *  if (a)
00144  *  {
00145  *      foo(); // called if a is true
00146  *  }
00147  *  else if (!a)
00148  *  {
00149  *      bar(); // called if a is false
00150  *  }
00151  *  else
00152  *  {
00153  *      fun(); // called if a is unknown
00154  *  }
00155  *  @endcode
00156  *
00157  *  This is equivalent to:
00158  *
00159  *  @code
00160  *  switch (a.state())
00161  *  {
00162  *  case TriBool::sTrue:
00163  *      foo();
00164  *      break;
00165  *  case TriBool::sFalse:
00166  *      bar();
00167  *      break;
00168  *  default:
00169  *      LASS_ASSERT(a.state() == TriBool::sUnknown);
00170  *      fun();
00171  *      break;
00172  *  }
00173  *  @endcode
00174  *
00175  *  @warning a common mistake is to test if a TriBool @c a is in a unknown state by comparing it to
00176  *           the constant @c unknown.  You might expect it to yield @c true if @c a is indeed,
00177  *           unknown, but it will always return @c unknown instead.  After all, it cannot know if
00178  *           both sides are equal, since at least one is unknown (@c unknown itself).  To test if
00179  *           @c a TriBool is indeed unknown, you have to use the method @c TriBool::isUnknown().
00180  *
00181  *  @code
00182  *  TriBool a;
00183  *
00184  *  if (a == unknown)
00185  *  {
00186  *      // BAD! unreachable code, the test will never yield true.
00187  *  }
00188  *
00189  *  if (a.isUnknown())
00190  *  {
00191  *      // GOOD! this code will be reached if the state of a is unknown.
00192  *  }
00193  *  @endcode
00194  */
00195 
00196 #ifndef LASS_GUARDIAN_OF_INCLUSION_NUM_TRI_BOOL_H
00197 #define LASS_GUARDIAN_OF_INCLUSION_NUM_TRI_BOOL_H
00198 
00199 #include "num_common.h"
00200 #include "safe_bool.h"
00201 
00202 namespace lass
00203 {
00204 namespace num
00205 {
00206 
00207 class LASS_DLL TriBool
00208 {
00209 public:
00210 
00211     enum State
00212     {
00213         sFalse,
00214         sTrue,
00215         sUnknown
00216     };
00217 
00218     TriBool(State iState = sUnknown);
00219     TriBool(bool iBool);
00220 
00221     const State state() const;
00222     State& state();
00223 
00224     TriBool operator!() const;
00225     operator SafeBool() const;
00226 
00227     bool isTrue() const;
00228     bool isFalse() const;
00229     bool isUnknown() const;
00230 
00231 private:
00232 
00233     State state_;
00234 };
00235 
00236 LASS_DLL TriBool LASS_CALL operator==(TriBool iA, TriBool iB);
00237 LASS_DLL TriBool LASS_CALL operator!=(TriBool iA, TriBool iB);
00238 LASS_DLL TriBool LASS_CALL operator&&(TriBool iA, TriBool iB);
00239 LASS_DLL TriBool LASS_CALL operator||(TriBool iA, TriBool iB);
00240 
00241 LASS_DLL std::ostream& LASS_CALL operator<<(std::ostream& ioS, TriBool iB);
00242 
00243 
00244 
00245 /** constant to be used as new keyword like @c true and @c false
00246  *  @relates TriBool
00247  */
00248 const TriBool unknown(TriBool::sUnknown);
00249 
00250 }
00251 
00252 }
00253 
00254 #endif
00255 
00256 // EOF

Generated on Mon Nov 10 14:21:40 2008 for Library of Assembled Shared Sources by doxygen 1.5.7.1
SourceForge.net Logo