From: dougm@titan.cs.rice.edu (Doug Moore)
Subject: New C++ type: boole
Organization: Rice University Computer Science, Houston, Texas
Date: Sun, 26 Jul 1992 05:42:14 GMT

>>>>> On 21 Jul 92 05:05:52 GMT, daveg@synaptics.com (Dave Gillespie) said:
  Dave> If you define "t!!" as a "t" token followed by two "!" tokens, using
  Dave> the existing C++ lexical rules, then you still have the same sort of
  Dave> problem:  "if (t!!==2)" cannot be scanned properly (not left-to-right,
  Dave> anyway).
  
  Dave> If you define "t!!" as a single token, may the fleas of a thousand
  Dave> camels, well, you get the idea.  :-)
  
  Dave> Let's do away with t!! and f!! altogether:  The expressions !!1 and
  Dave> !!0 are just as simple, and we get them for free.  I find them easier
  Dave> to tell apart at a glance, too.

Although the needs of the writers of scanners was not my primary
concern when I proposed t!! and f!!, I can agree that they might make
their lives a little difficult, and might make everyone's scanners a
little slower.

On the other hand, I do want boole literals and not circumlocutions
like !!0 and !!1.  I could live with 0t and 0f, and I don't think the
scanner writers would wish upon me an infestation of dromedary vermin
for inventing those two particular symbols.  Would they?

  Dave> Under "advantages," you might list the ability of a class to define
  Dave> "operator boole()" conversions instead of/as well as "operator int()".
  Dave> Many classes define misleading "int" conversions when they really
  Dave> want only a boolean conversion.

Noted.  Thank you.

  Dave> Is it possible for a template "Array<boole>" to define itself
  Dave> naturally as a packed bit vector?  That would be pretty cool.

Yeah, but I don't see how to do it.

  Dave> Does there need to be any mention of "signed", "unsigned", "short",
  Dave> and "long" as applied to "boole"?  (Specifically, that all of these
  Dave> are disallowed?)

Probably not, but I have added a note.  Thanks.

  Dave> #ifndef __STDCPLUS__
  Dave> typedef int boole;
  Dave> // or: #define boole int
  Dave> #endif
  
  Dave> What can be done to ease the transition from current C++ to C++
  Dave> with booleans?

Well, one could probably cook up a class boole that one could
conditionally include if using an unenlightened compiler.  That
wouldn't solve everything.  Anytime you add to the language, programs
that use the new features can't be compiled with the old compilers.  I
can't see a global solution to that.

  Dougm> The behavior of an expression of the form E1 &&= E2 is equivalent to
  Dougm> E1 ? (E1=(E2)) : f!, except that E1 is evaluated only once.

  Dave> Any reason you wrote it that way instead of as "E1 = E1 && E2"?  The
  Dave> only way I can see that this would make a difference is if "E1" is a
  Dave> volatile variable.  Is that rare case worth sacrificing the elegance
  Dave> of defining "E1 <op>= E2" as "E1 = E1 <op> E2" always?

I wrote it that way to emphasize that in E1 &&= E2 or E1 ||= E2, no
assignment need take place if E1 has the appropriate value.  That
is probably sufficiently obvious to go unsaid.

  Dave> There is a minor typo in section 1 of your proposal:
Bug noted and corrected.  Thank you.

Doug Moore
(dougm@cs.rice.edu)

P. S.  The latest draft of the proposal follows.  Constructive
feedback is still welcome.  Shall I officialize it and send it off to
our beloved standards committee?

Proposed extension to C++: A Boolean type
Version 2: 26 Jul 1992
Doug Moore
dougm@cs.rice.edu

0. Introduction

One of the weakness of the type system that C++ inherited from C is in
the overuse of the int type.  In particular, a Boolean type
representing truth and falsehood does not exist in C, as it does in
many languages, but instead truth and falsehood are represented by
integral values 1 and 0.

To some, the weakness of the type system is a strength of the
language, and expressions like "8 + 5*(a<b)" are part of their
programming idiom.  No proposed change to C++ can invalidate such
expressions.  To many, however, the weakness of the type system that
permits programming errors to go undetected is a language flaw.  The
language should provide greater type safety to those who want it.
Thus, I propose the extension of C++ to define a boolean type.  A
complete list of amendments to the base document that support the
extension appears at the end of this document.

1. Outline of the extension.

I propose to add to C++ a new integral type, boole, and literals of
that type, 0t, and 0f.  Besides paying tribute to a pioneer in the
formalization of logic, the type name is not one that has been popular
with those who have tried to simulate boolean types in the past.
Therefore, that choice of type name is less likely to break current
code than a type name like "bool" or "Boolean".  The literal names 0t
and 0f cannot appear, even accidentally, in current code.

The boole type is generated as the result of comparison operations, as
the result of applying the logical connectives && and ||, and as the
result of applying the unary operator ! to boole, pointer and
arithmetic types.  It is used in the first-expression parts of
conditional expressions (?:), and as the test expressions in if, for,
while and do statements.  When arithmetic or pointer types appear in a
context that requires a boolean value, standard logical conversions
are applied to convert nonzero arithmetic and nonnull pointer types to
0t.  Warnings can be generated when logical conversions are applied;
such warnings should be optional early compilers, and later compilers
should warn by default but optionally disable warnings.

The boole type is an integral type, and in a general arithmetic
context can be promoted to an integer value 0 or 1.  Integral
promotion of boole values generates warnings, optionally at first, but
eventually by default.

2.  New and redefined operators.

Certain operators can be usefully reinterpreted when boole values are
distinguished from other integral values.  In particular, the unary
operator ! can be read as isFalse (when applied to booles), as isZero
(when applied to arithmetic values) and as isNull (when applied to
pointers).  This proposal does not, therefore, require that warnings
be issued when ! is applied to various types.  It is hoped that by
distinguishing the three different meanings of !, confusion about null
pointers and their representation may be lessened.

The ++ and -- operators serve a useful, if redundant, purpose in the
extension.  When applied to boole lvalues, they each invert the
lvalue.  Thus "b[<expr>] = !b[<expr>]" or, more likely, "b[<expr>] ^=
1" could be replaced by "++b[<expr>]".  Note that the use of ++ to
toggle logical values is already a common idiom, particularly in the
decoding of command line options and the corresponding setting of
flags.

New operators &&= and ||= extend the op= paradigm, in that they permit
replacement of, for example, (b1 = b1 && b2) by (b1 &&= b2).  Such
operators could prove especially useful in a context like (b1[<expr>]
&& = b2), particularly if <expr> has side effects.  The expression
could not otherwise be written without the introduction of a temporary
variable.  Like their underlying logical connectives, these assignment
operators are short-circuiting; that is, the right hand side need not
be evaluated if the left hand side determines the result of the
operation.

3. Additional advantages to the extension.

Among the common errors that will generate warnings when the extension
is adopted are the confusion of assignment and equality testing (if (a
= b)) and the confusion that can arise in interval testing (if (a < b
< c)).  In general, the inadvertant mixture of arithmetic and boolean
values will be detected and corresponding bugs avoided.

Functions will be overloadable on boolean values, which will be
convenient for some class designers.  Conversion operators "operator
boole()", "operator void*()", and "operator int()" will be able to
simultaneously coexist, which again may convenience some class
designers.

The primary advantage is probably the resulting elimination of
progammer defined, conflicting boolean types.  That so many
programmers choose to simulate a boolean type suggests that it would
be a popular language feature.  That so many programmers do it
differently suggests that there is not a clearcut way to do it well
within the limits of the current language.

4. Disadvantages to the extension.

Programmers who now deliberately overload a function to take an int
argument, and then call that function with an argument that is of
boole type under this proposal, will have their programs break.  They
will have to cast the result argument to int explicitly.

Programs that use the identifier "boole" will break.

The warnings issued for integral promotions of booles and logical
conversion of arithmetic and pointer types may annoy some.  The
warnings can be eliminated by explicit casts or, better, by replacing
<boolexpr> by (<boolexpr>?1:0) and <arithexpr> by !!(<arithexpr>)
where appropriate.

5. Questions about the extension.

Q. What is the size of the boole type? I suspect that it would
probably be best to define it to have an implementation defined size
so an implementation can choose to pack an array of boole as a bit
vector.

A. The size of the boole type is one.  An implementation cannot
automatically pack booles, since doing so would make it impossible to
have a type boole* (pointer to boole), an array of booles, etc.

6. Conclusions.

The changes proposed here will not break existing programs, except in
rare cases.  They will help standardize a practice of many programmers
and library writers.  There is ample experience with similar types in
many other languages, including Fortran and Pascal, to suggest that
the technical obstacles to supporting such a type are minimal.

7. Amendments to base document.

======================================================================
In 2.4 (Keywords), add "boole" to the list of keyword identifiers.
Add "&&=" and "||=" to the list of "character combinations (...) used
as operators."

======================================================================
In 2.5 (Literals), add "boolean-constant" to the beginning of the list
of literals.

======================================================================
Add a new subsection before 2.5.1 (Integer Constants), to read:
<begin quote>

A boolean constant consists of '0' followed by one of 't' or 'f'.  The
constant 0t denotes truth.  The constant 0f denotes falsehood.

<end quote>

======================================================================
In 3.6.1 (Fundamental Types), add the following after the first
paragraph:
<begin quote>

A boolean type, declared boole, is available.  Booleans provide the
smallest addressable quantity of storage.  The value of a boolean is
equivalent to 1 for 0t and 0 for 0f.

<note> Booles may not be declared signed or unsigned, long or short.

<end quote>

Change the penultimate paragraph to:
<begin quote>

Types boole, char, int of all sizes, and enumerations (7.2) are
collectively called integral types.  Integral and floating types are
collectively called arithmetic types.

<end quote>

======================================================================
In 4.1 (Integral Promotions), change the first paragraph to begin:
<begin quote>

A boole, a char, a short int, ....

<end quote>

<Note: Compilers can detect and warn against the use of implicit
promotions of boole to int.>

======================================================================
Add a new section after 4.5 (Arithmetic Conversions) as follows:

<begin quote>

4.x Logical Conversions

In many contexts, an expression having pointer type or non-boolean
arithmetic type will appear where a value of boolean type is expected.
In these circumstances, a value of pointer type is converted to the
boolean value 0f if and only if the pointer value is that of the null
pointer.  A value of arithmetic type is converted to the boolean value
0f if and only if the value is zero.

<Note: Compilers can detect and warn against the use of implicit
conversions of arithmetic and pointer types to boole.>

======================================================================
In 5.2.5 (Increment and Decrement), replace the fourth sentence of the
first paragraph by:
<begin quote>

After the result is noted, the object is incremented by 1, if
non-boolean, or inverted, if boolean.

<end quote>

======================================================================
In 5.3 (Unary operators), replace the penultimate paragraph by:
<begin quote>

The operand of the operator ! must have arithmetic, pointer, or
boolean type.  For arithmetic types, its value is 0t if its operand
is zero, 0f otherwise.  For pointer types, its value is 0t if its
operand is the null pointer, 0f otherwise.  For boolean types, its
value is the logical negation of its operand.

<end quote>

======================================================================
In 5.3.1 (Increment and Decrement), replace the first sentence of the
first paragraph by:
<begin quote>

The operand of prefix ++ is incremented by 1 if arithmetic, or
inverted if boolean.

<end quote>

======================================================================
In 5.9 (Relational Operators), change the second paragraph to:
<begin quote>

The operands must have arithmetic or pointer type.  The operators <
(less than), > (greater than), <= (less than or equal to), and >=
(greater than or equal to) all yield 0f if the specified relation is
false and 0t if it is true.  The type of the result is boole.

<end quote>

======================================================================
In 5.10 (Equality Operators), change the second sentence of the first
paragraph to:

<begin quote>

(Thus a<b == c<d is 0t whenever a<b and c<d have the same truth-value.)	

<end quote>

======================================================================
In 5.14 (Logical AND Operator), replace the two paragraphs by:

<begin quote>

The && operator groups left-to-right.  It returns 0t if both its
operands are 0t, 0f otherwise.  Unlike &, && guarantees left-to-right
evaluation; moreover the second operand is not evaluated if the first
operand evaluates to 0f.

The operands each have boolean type.  The result is a boole.  All side
effects of the first expression happen before the second expression is
evaluated.

<end quote>

<Note: Logical conversion (4.x) permits any arithmetic or pointer type
to appear as an operand, to be converted to boolean type.>

======================================================================
In 5.15 (Logical OR Operator), replace the two paragraphs by:

<begin quote>

The || operator groups left-to-right.  It returns 0t if either of its
operands are 0t, 0f otherwise.  Unlike |, || guarantees left-to-right
evaluation; moreover the second operand is not evaluated if the first
operand evaluates to 0t.

The operands each have boolean type.  The result is a boole.  All side
effects of the first expression happen before the second expression is
evaluated.

<end quote>

<Note: Logical conversion (4.x) permits any arithmetic or pointer type
to appear as an operand, to be converted to boolean type.>

======================================================================
In 5.16 (Conditional Operator), replace the first paragraph by:

<begin quote>

Conditional expressions group right-to-left.  The first expression
must have boolean type.  It is evaluated and if it is false, the
result of the conditional expression is the value of the second
expression, otherwise that of the third expression.  All side effects
of the first expression happen before the second or third expression
is evaluated.

<end quote>

<Note: Logical conversion (4.x) permits any arithmetic or pointer type
to appear as the first operand, to be converted to boolean type.>

======================================================================
In 5.17 (Assignment Operators), add to the list of assignment
operators "&&=" and "||=".

======================================================================
In 6.4.1 (The if Statement), change the first two paragraphs to:
<begin quote>

The expression must be of boolean type or of a class type for which an
unambiguous conversion to boolean type exists (12.3).

The expression is evaluated and if it is true, the first substatement
is executed.  If else is used, the second substatement is executed if
the expression is false.  The else ambiguity is resolved by connecting
an else with the last encountered else-less if.

<end quote>

<Note: Logical conversion (4.x) permits any arithmetic or pointer type
to appear as the expression, to be converted to boolean type.>

======================================================================
In 6.5.1 (The while Statement), change the first two paragraphs to:
<begin quote>

In the while statement the substatement is executed repeatedly until
the value of the expression becomes false.  The test takes place
before each executeion of the statement.

The expression must be of boolean type or of a class type for which an
unambiguous conversion to boolean type exists (12.3).

<end quote>

<Note: Logical conversion (4.x) permits any arithmetic or pointer type
to appear as the expression, to be converted to boolean type.>

======================================================================
In 6.5.2 (Do statement), change the first two paragraphs to:
<begin quote>

In the do statement the substatement is executed repeatedly until
the value of the expression becomes false.  The test takes place
after each executeion of the statement.

The expression must be of boolean type or of a class type for which an
unambiguous conversion to boolean type exists (12.3).

<end quote>

<Note: Logical conversion (4.x) permits any arithmetic or pointer type
to appear as the expression, to be converted to boolean type.>

======================================================================
In 6.5.3 (The for Statement), change "zero" to "false" in the first
paragraph.  Change the last sentence of the first paragraph to:
<begin quote>

The first expression must be of boolean type or of a class type for
which an unambiguous conversion to boolean type exists (12.3).

<end quote>

Change the second sentence, second paragraph to:
<begin quote>

A missing expression-1 makes the implied while clause equivalent to
while(0t).

<end quote>