C++ types std::boolean, std::true, std::false and std::bool_intermediate

291 views
Skip to first unread message

Jim Smith

unread,
Jun 24, 2013, 4:37:47 AM6/24/13
to std-pr...@isocpp.org
Hi,

I posted this once in more detail, but didn't see it listed.

I'm interested in an implementation of C++ types std::boolean, std::true, std::false, and std::bool_intermediate for states that are not true or false. These types would allow boolean results to be handled using OO techniques instead of if/else statements. Also, std::bool_intermediate take into account the superposition of the two boolean states.

An implementation of these types would allow the result of a boolean method to be handled using multiple dispatch instead of traditional if/else statements:

(multiple dispatch is explained in Scott Meyers "More Effective C++")

//usage example:

class handler_concept
{
public:
...
  void operator()(const std::true<handler_concept>& bt) ;
  void operator()(const std::false<handler_concept>& bf) ;
  void operator()(const std::bool_intermediate<handler_concept>& bi) ;
...
};

  handler_concept hc; // class object that handles boolean states
  std::boolean<handler_concept> bln(hc); // creation of std::boolean object that will invoke method on handler_concept object hc

  SomeOperation s_op;  // class object with boolean method
  bln = s_op.some_bool_function();  // bln is assigned the boolean value and invokes the method on handler_concept object hc

//end of example

I implemented these boolean types to try the purposed technique. It's more elegant than if/else and allows each class to define its own meaning of true or false etc., increasing encapsulation of an application's concepts.


Thanks & Regards,
--James Smith


Jonathan Wakely

unread,
Jun 24, 2013, 6:25:21 AM6/24/13
to std-pr...@isocpp.org


On Monday, June 24, 2013 9:37:47 AM UTC+1, Jim Smith wrote:
Hi,

I posted this once in more detail, but didn't see it listed.

I'm interested in an implementation of C++ types std::boolean, std::true, std::false, and std::bool_intermediate for states that are not true or false. These types would allow boolean results to be handled using OO techniques instead of if/else statements.

Why is that a good thing?

I know that type dispatching with an open set of types is much better handled with virtual functions than with if/else statements, but boolean is not an open set, it has true and it has false.
 
Also, std::bool_intermediate take into account the superposition of the two boolean states.


This seems to be confusing two concepts: compile-time booleans (which we have with std::true_type and std::false_type) and a tri-bool type. Why would we want an intermediate state for booleans?  Does it model this code?

if (some_condition)
{ }
else if (!some_condition)
{ }
else /* ??? */
{ }

If we can't do it with booleans, why should we be able to do it with a type representing booleans?

 
An implementation of these types would allow the result of a boolean method to be handled using multiple dispatch instead of traditional if/else statements:

(multiple dispatch is explained in Scott Meyers "More Effective C++")

//usage example:

class handler_concept
{
public:
...
  void operator()(const std::true<handler_concept>& bt) ;
  void operator()(const std::false<handler_concept>& bf) ;
  void operator()(const std::bool_intermediate<handler_concept>& bi) ;
...
};

  handler_concept hc; // class object that handles boolean states
  std::boolean<handler_concept> bln(hc); // creation of std::boolean object that will invoke method on handler_concept object hc

  SomeOperation s_op;  // class object with boolean method
  bln = s_op.some_bool_function();  // bln is assigned the boolean value and invokes the method on handler_concept object hc

//end of example

I implemented these boolean types to try the purposed technique. It's more elegant than if/else and allows each class to define its own meaning of true or false etc., increasing encapsulation of an application's concepts.
 
How did you implement it when true and false are keywords?

What's so wrong with if/else that "OO techniques" are needed to replace it?  I don't consider your example elegant at all.

This might be useful in your own project, why should it be standardised?  What problem are you solving?



Greg Marr

unread,
Jun 24, 2013, 1:40:51 PM6/24/13
to std-pr...@isocpp.org
On Monday, June 24, 2013 4:37:47 AM UTC-4, Jim Smith wrote:
std::bool_intermediate for states that are not true or false.  ... Also, std::bool_intermediate take into account the superposition of the two boolean states. 

This sounds exactly like std::optional<bool>.  

If you really want OO behavior from a class here, I think this code would be much clearer,
just derive from this dispatch class and implement the virtual functions.

class optional_bool_dispatch
{
public:
  void operator()(std::optional<bool> const &res)
    {
    if(!res)
      if_indeterminate();
    else if(*res)
      if_true();
    else
      if_false();
    }

protected:
  virtual void if_true() = 0;
  virtual void if_false() = 0;
  virtual void if_indeterminate() = 0;
};

  SomeOperation s_op;  // class object with std::optional<bool> method
  optional_bool_dispatch_derived hc; // class derived from optional_bool_dispatch
  hc(s_op.some_bool_function());

Martinho Fernandes

unread,
Jun 26, 2013, 12:11:12 PM6/26/13
to std-pr...@isocpp.org
On Mon, Jun 24, 2013 at 7:40 PM, Greg Marr <greg...@gmail.com> wrote:
>
> On Monday, June 24, 2013 4:37:47 AM UTC-4, Jim Smith wrote:
>>
>> std::bool_intermediate for states that are not true or false. ... Also, std::bool_intermediate take into account the superposition of the two boolean states.
>
>
> This sounds exactly like std::optional<bool>.


I think `optional<bool>` has confusing semantics to be used for
tri-value logic. I'll call the third value "third", just for
exposition. Consider:

// I would normally not use == true, but I think it is worth for clarity here
optional<bool> a = true;
assert(bool(a) == true);
assert(bool(!a) == false);
assert(*a == true);

optional<bool> b = false;
assert(bool(a) == true);
assert(bool(!a) == false);
assert(*a == false);

auto third = none;
optional<bool> c = third;
assert(bool(c) == false);
assert(bool(!c) == true);
// *c is UB

With this `bool(a)` or any contextual conversion to bool, like
`if(a)`, means "Is `a` not the third value?"/
`bool(!a)` means "Is `a` the third value?"
And `*a` means "assuming `a` is not the third value, is it true?"
The question "Is `a` false?" would have to be `!*a`, which is actually
"assuming `a` is not the third value, is it true?"

So a three-way if with optional<bool> would look like:

if(!a) {
// third value
} else if(*a) {
// true
} else {
// false
}

I prefer something like boost::tribool.

tribool a = true;
assert(bool(a) == true);
assert(bool(!a) == false);

tribool b = false;
assert(bool(a) == false);
assert(bool(!a) == true);

tribool c = third;
assert(bool(c) == false);
assert(bool(!c) == false);

With this `bool(a)` means "Is `a` true?", and `bool(!a)` means "Is `a`
false?". The third value, not being true nor false yields "no" for
both queries.

So a three-way if looks like:

if(a) {
// true
} else if(!a) {
// false
} else {
// third value
}

Another thing to notice is that `operator!` works as expected with
tribool: the negation of true is false, and the negation of false is
true. The negation of an indeterminate value is indeterminate (it acts
a bit like NaN in floating point arithmetic; call it not-a-boolean ;).
With optional<bool> the negation of false is false.

I prefer to restrict my usage of optional<bool> for incidental
occurrences in generic code, and not as a tri-valued boolean. I don't
need it often, but I wouldn't mind having a tribool type in the
standard library, as long as it is not some "object-oriented"
nonsense.

Fernando Cacciola

unread,
Jun 26, 2013, 10:30:23 PM6/26/13
to std-pr...@isocpp.org
On Wed, Jun 26, 2013 at 1:11 PM, Martinho Fernandes <martinho....@gmail.com> wrote:
On Mon, Jun 24, 2013 at 7:40 PM, Greg Marr <greg...@gmail.com> wrote:
>
> On Monday, June 24, 2013 4:37:47 AM UTC-4, Jim Smith wrote:
>>
>> std::bool_intermediate for states that are not true or false.  ... Also, std::bool_intermediate take into account the superposition of the two boolean states.
>
>
> This sounds exactly like std::optional<bool>.


I think `optional<bool>` has confusing semantics to be used for
tri-value logic.

FWIW, I totally agree with you here, and in fact, we have boost::tribool precisely because boost::optional<bool> didn't quite cut it.

I thought about having core support for tribool but it's quite tricky to get right, and pure library support is actually enough, even if not as convenient.

I use triboolean logic all over the place when I work on a very specific (and specialized) domain, for which I write code like:

if ( certainly(b) )
{
 // b == true
}
else if ( certainly_not(b) )
{
  // b == false
}
else
{
 // b is indeterminate (or neither true nor false if we consider a more general lattice type)
}

I'm not fond of the extra verbosity or the fact that the triboolean expression b must be evaluated twice (unless is set aside before the statement), but I can live with that.

So, is there any proposal for tribool already?

Best

--
Fernando Cacciola
SciSoft Consulting, Founder
http://www.scisoft-consulting.com

Bjorn Reese

unread,
Jun 27, 2013, 4:56:20 AM6/27/13
to std-pr...@isocpp.org
On 06/27/2013 04:30 AM, Fernando Cacciola wrote:

> I thought about having core support for tribool but it's quite tricky to
> get right, and pure library support is actually enough, even if not as
> convenient.

You should also consider the behavior of the logical operators. I have
had use cases where I could not use boost::tribool because it truth
table did not match my needs. Instead I needed operators that always
yielded determinate values (true/false) unless both operands were
indeterminate. I am going to call this boolean type for neutral bool
below.

I have listed the truth tables of the two below. I use F = false, T =
true, and N = none/indeterminate.

The truth tables for boost::tribool:

NOT |
----+---
F | T
T | F
N | N

AND | F T N
----+-------
F | F F F
T | F T N
N | F N N

OR | F T N
----+-------
F | F T N
T | T T T
N | N T N

The truth tables for neutral bool:

NOT |
----+---
F | T
T | F
N | N

AND | F T N
----+-------
F | F F F
T | F T T
N | F T N

OR | F T N
----+-------
F | F T F
T | T T T
N | F T N

There may also be a valid use case for a NaN-like tribool that always
yields indeterminate if either operand is indeterminate, but I am not
going to elaborate on that here.

Fernando Cacciola

unread,
Jun 27, 2013, 8:23:08 AM6/27/13
to std-pr...@isocpp.org
On Thu, Jun 27, 2013 at 5:56 AM, Bjorn Reese <bre...@mail1.stofanet.dk> wrote:
On 06/27/2013 04:30 AM, Fernando Cacciola wrote:

I thought about having core support for tribool but it's quite tricky to
get right, and pure library support is actually enough, even if not as
convenient.

You should also consider the behavior of the logical operators. I have
had use cases where I could not use boost::tribool because it truth
table did not match my needs. Instead I needed operators that always
yielded determinate values (true/false) unless both operands were
indeterminate. I am going to call this boolean type for neutral bool
below.

Interesting.

Following your truth table below, I would say that, ultimately, you need:

a & b -> possibly ( a & b ) // if using tribool

a | b -> certainly( a | b ) // if using tribool

everything else seems to match tribool.

Would you be able to use the equivalence above and stick to tribool?


Regarding core support, here is where I am at the moment, FWIW:

The difficulty boils down to the fact that there are two paths to reduce from tribool to bool.

We have:

certainly(b)        == true IFF b = true
certainly_not(b) == true IFF b = false
possibly(b)        == true IFF b != false
possibly_not(b) == true IFF b != true

thus, given

tribool tb ;

foo ( bool ) ; foo(tb);
if ( tb )
tb ?
while(tb)

all of them coluld be equivalent to reducing via certainly or possibly, but arbitraryly picking one doesn't seem right.

For example, I normally write

if ( certainly(tb) )
else if ( certainly_not(tb) )
else // only indeterminate falls here

but it's also quite reasonable to do

if ( certainly(tb) )
else // both false and indeterminate falls here

so it would not be at all evident what:

if ( tb )
else

actually means, NOT even if we guessed that if ( tb ) means if ( certainly(tb) )

my current preference  goes to disallow entirely conversion to bool, not even explicit, and add the following new keywords:

certainly_if
possibly_if
certainly_else
possibly_else

so that I can write:

certianly_if ( tb )
{
  // tb == true for sure
}
certainly_else
{
 // tb == false for sure
}
else
{
  // can't tel
}

Granted, certainly_if ( tb ) is so much the same as if ( certainly(tb) ) that a new keyword seems totally unnecesary, but the real thing is the _else keywords:

certainly_else
{
}

is much much better (IMHO) than

else if ( certainly_not(tb) )
{
}

so I like that one to be in core, but then the _if versions make sense just for completeness

Martinho Fernandes

unread,
Jun 27, 2013, 8:45:34 AM6/27/13
to std-pr...@isocpp.org
On Thu, Jun 27, 2013 at 2:23 PM, Fernando Cacciola
<fernando...@gmail.com> wrote:
> my current preference goes to disallow entirely conversion to bool, not
> even explicit, and add the following new keywords:

I don't think there's a good chance of getting new keywords for
something that is a bit of a niche and has proven library-only
implementations :(

Bjorn Reese

unread,
Jun 27, 2013, 10:03:07 AM6/27/13
to std-pr...@isocpp.org
On 06/27/2013 02:23 PM, Fernando Cacciola wrote:

> Following your truth table below, I would say that, ultimately, you need:

If I understood your certainly/possibly functions correctly, the take a
tribool and returns a bool.

> a & b -> possibly ( a & b ) // if using tribool

This will give me

AND | F T N
----+-------
F | F F F
T | F T T
N | F T T

but I need N && N == N.

> a | b -> certainly( a | b ) // if using tribool

This will give me

OR | F T N
----+-------
F | F T F
T | T T T
N | F T F

but I need N || N == N.

Maybe neutral bool could be handled via a policy or trait of tribool?

Fernando Cacciola

unread,
Jun 27, 2013, 11:03:36 AM6/27/13
to std-pr...@isocpp.org
On Thu, Jun 27, 2013 at 11:03 AM, Bjorn Reese <bre...@mail1.stofanet.dk> wrote:
On 06/27/2013 02:23 PM, Fernando Cacciola wrote:

Following your truth table below, I would say that, ultimately, you need:

If I understood your certainly/possibly functions correctly, the take a
tribool and returns a bool.


a & b -> possibly ( a & b ) // if using tribool

This will give me


  AND | F T N
  ----+-------
    F | F F F
    T | F T T
    N | F T T

but I need N && N == N.


a | b -> certainly( a | b ) // if using tribool

This will give me


  OR  | F T N
  ----+-------
    F | F T F
    T | T T T
    N | F T F

but I need N || N == N.


OK.I actually noticed that but didn't pay attention since it matched tribol behavior.

I went the posibly|cerainly road as an attempt to follow why would you need such a behavior.
When you ask: "is a AND b", and you cannot tell a or b, then IMO the answer is 'how should I know". Which is why N/F AND N/F is N in tribool

So, it looked like you are asking more like: "could it be a AND b", hence my version. But I'm still suspicious, "I don't know" and "yes" are not "yes" both at the same time.

Now....

AND and OR can be generalized as a count_true function such that:
 
a AND b AND c AND d means

 count_true( answers )==answers.count

a OR b OR c OR d means

 count_true( answers )==1

From this POV, you seem to need to know whether all of the *actual* answers are true at the same time (or at least one in the case of |).
That is:

actual_answers=filter_out_indeterminate(answers); 

your AND -> count_true(actual_answers)==actual_answers.count with .count != 0

your OR -> count_true(actual_answers)==1


Thus, it looks to me that your real need are specialized AND/OR as opposed to a specialized Truth Table.
That is, we could have tribool and you could have you own AND/OR functions that do what you want, which, like I said, looks to me that as just filtering out the indeterminate.
 
Maybe neutral bool could be handled via a policy or trait of tribool?


 For something like this, a policy would be overkill. But this is a different discussion.

Best

Fernando Cacciola

unread,
Jun 27, 2013, 2:36:51 PM6/27/13
to std-pr...@isocpp.org
On Thu, Jun 27, 2013 at 12:03 PM, Fernando Cacciola <fernando...@gmail.com> wrote:
On Thu, Jun 27, 2013 at 11:03 AM, Bjorn Reese <bre...@mail1.stofanet.dk> wrote:
 
That is, we could have tribool and you could have you own AND/OR functions that do what you want, which, like I said, looks to me that as just filtering out the indeterminate.
 

Well, here is something interesting.

These days I'm creating a DSL for SQL query construction, and I was just looking at the following real code I've created with it:

   Schemas.Doc.Id.Is( aDocModel.Id )
|  (     Schemas.Doc.Label.Is( aDocModel.Name)
     & Schemas.DocCategory.Category.IsAnyOf(aDocModel.Tags)
   )

That code there creates an string that can be added to a WHERE SQL clause.

I wanted this to be super easy to use by the "end-user" programmers, so I made it such that, if any of the "input values" are null, the expression automatically reduces.
That is,

if aDocMode.Id is null (or empty in this case as it is a string), then the above is actually equivalent to just:

        Schemas.Doc.Label.Is( aDocModel.Name)
     & Schemas.DocCategory.Category.IsAnyOf(aDocModel.Tags)

Similarly, if, OTOH, aDocModel.Tags, which is a sequence, happens to be empty, it reduces to:

   Schemas.Doc.Id.Is( aDocModel.Id )
Schemas.Doc.Label.Is( aDocModel.Name)


Now, I just realized that, IIUC, this exactly matches the net effect of your choice of semantic for & and | with your neutral tribool.
In fact, I might even implemented this using your type.

So, FWIW I found real use cases as I wanted.

Best

Bjorn Reese

unread,
Jun 29, 2013, 6:04:25 AM6/29/13
to std-pr...@isocpp.org
On 06/27/2013 08:36 PM, Fernando Cacciola wrote:

> So, FWIW I found real use cases as I wanted.

Intriguingly, my real-world use case also came from an SQL-like setting.
I had to examine if two rows were similar by comparing their entries
pairwise. Each entry could be null, but the result of the pairwise
comparison should only be null if both entries were null. The results
of each pairwise comparison was then aggregated into the final result.
By having the pairwise comparison operator return a neutral bool, the
aggregation simply became:

neutral_bool has_artist = row1.artist == row2.artist;
neutral_bool has_album = row1.album == row2.album;
neutral_bool result = has_artist && has_album;

The actual code did not use the intermediate has_artist and has_album
variables, and there were more entries that the two listed above.

Regarding the use of traits, I put together a simple prototype of
tribool that uses traits to obtain both the traditional tribool
behavior and the neutral bool behavior. Pretty straigh-forward.

https://github.com/breese/tribool/blob/master/include/tribool

Mikhail Semenov

unread,
Jun 30, 2013, 3:48:38 AM6/30/13
to std-pr...@isocpp.org
Multi-value logic (and tribool is part of it) is based on the fact that you use values 0,...,n (total n+1) :
not(v) = n-v
 
and(v1,...,vk) = min(v1,...,vk)
 
or(v1,...,vk) = max(v1,...,vk)
 
In case of tribool (n=2):
F = 0 
N = 1
T = 2
And all the truth tables can be easily derived.
You can further with logic:
F - not true // impossible
N - unknown likelihood
T - true // certain
 
certain(v) iff v = T
uncertain(v) iff  v < T
possible(v) iff v > F
impossible(v) iff v = F
 
 
 
 

Bjorn Reese

unread,
Jun 30, 2013, 5:38:04 AM6/30/13
to std-pr...@isocpp.org
On 06/30/2013 09:48 AM, Mikhail Semenov wrote:
> Multi-value logic (and tribool is part of it) is based on the fact that
> you use values 0,...,n (total n+1) :

This reminded me of N2136 "Bool_set: multi-valued logic"

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2136.pdf

The most recent status of this proposal that I could find is:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3371.html

Reply all
Reply to author
Forward
0 new messages