[comp.lang.c++] Boolean confusion

haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) (05/23/91)

I've been browsing through include files and it seems that everyone in
the world who ever created a C++ include file created some definition to
support boolean variables. I've seen BOOL bool Bool BOOLEAN boolean
Boolean bool_t BOOL_T, ... Some are #defines, some are typedefs.
Some folks like to associate them with char, unsigned char, int, long,
or enum. Some packages (like XView) must have used a random number
generator to decide which definition would be used for each separate
include file-- I doubt that anyone could be that inconsistant unless
it was deliberate.

So my question is, how can I choose one definition (my own, or someone
else's) for boolean variables, and still remain insulated from the sea
of boolean definitions in assorted include files? For that matter, am
I insulated even without a boolean definition of my own?

I suppose I could pick some extreamly unlikely name for my boolean
variable type, but I really don't want to. One reason is that it makes
code so much clearer to have clear straighforward class and type
names.

The best I can think of right now is to always include "foreign"
include files first, then my own. In my own include files I can do
something like:

#ifdef Bool
#undef Bool
#endif
typedef enum {FALSE, TRUE}	Bool;

I suppose that I could still take a hit from a macro, or inline
function. I don't think I'm immune from name space conflicts either.
The biggest thing I'd like to avoid is having the same class have
different sizes in different include files due to include file
interference.

What's the best way to do this?

	Thanks,

	Hayden Schultz (haydens@juliet.ll.mit.edu)
	MIT Lincoln Lab
	(617) 981-3685

horstman@mathcs.sjsu.edu (Cay Horstmann) (05/24/91)

In article <HAYDENS.91May23103233@natasha.juliet.ll.mit.edu> haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) writes:
>
>The best I can think of right now is to always include "foreign"
>include files first, then my own. In my own include files I can do
>something like:
>
>#ifdef Bool
>#undef Bool
>#endif
>typedef enum {FALSE, TRUE}	Bool;
>
Don't do that. In C++, enums aren't ints. For example, the code
     Bool more, done;
     // ... 
     more = !done && cin.good();
will not compile--the right hand side of the = (the result of &&) is an
int, and it cannot be assigned to an enum without a cast
     more = (Bool)(!done && cin.good()); // bleecch

Cay

     >
>	Thanks,
>
>	Hayden Schultz (haydens@juliet.ll.mit.edu)
>	MIT Lincoln Lab
>	(617) 981-3685

bright@nazgul.UUCP (Walter Bright) (05/26/91)

In article <HAYDENS.91May23103233@natasha.juliet.ll.mit.edu> haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) writes:
/So my question is, how can I choose one definition (my own, or someone
/else's) for boolean variables, and still remain insulated from the sea
/of boolean definitions in assorted include files?
/What's the best way to do this?

After years of fiddling around with various schemes myself, the most practical
one I came up with is just use int:

	/*********************
	 * Test some quantity.
	 * Returns:
	 *	0	False
	 *	!=0	True
	 */

	int func();

I've gone through my code over time and have eliminated all the logical, bool,
boolean, BOOL, etc. clutter. Although the above solution seems boring and
inelegant, it works suprisingly well, given that it mimics C's idea of what
is true and what is false.

Note that you also need to get rid of any TRUE or FALSE #define's. Remember to
always test for 0 or !=0.

(Some other battle-scarred old C programmers have told me that they have
 eventually come to the same conclusions.)

mittle@blinn.watson.ibm.com (Josh Mittleman) (05/29/91)

> Note that you also need to get rid of any TRUE or FALSE #define's.
Remember to
> always test for 0 or !=0.
> 
> (Some other battle-scarred old C programmers have told me that they have
>  eventually come to the same conclusions.)

But that's exactly the point: Testing for == 0 is the perfect C approach,
but it entirely inconsistent with the logic of C++.  C++ should have a
standard built-in Boolean type.

===========================================================================
Josh Mittleman (mittle@watson.ibm.com or joshua@paul.rutgers.edu)
J2-C28 T.J. Watson Research Center, PO Box 704, Yorktown Heights, NY  10598

philip@pescadero.Stanford.EDU (Philip Machanick) (05/30/91)

In article <1991May29.145100.18519@watson.ibm.com>, mittle@blinn.watson.ibm.com (Josh Mittleman) writes:
|> 
|> > Note that you also need to get rid of any TRUE or FALSE #define's.
|> Remember to
|> > always test for 0 or !=0.
|> > 
|> > (Some other battle-scarred old C programmers have told me that they have
|> >  eventually come to the same conclusions.)
|> 
|> But that's exactly the point: Testing for == 0 is the perfect C approach,
|> but it entirely inconsistent with the logic of C++.  C++ should have a
|> standard built-in Boolean type.
 
Yes - maybe it would also catch a lot of those annoying hard to
find bugs caused by typing "if (thing = whatever)" instead of
"==". But how many programs would break? I for one would be prepared
to pay the price for the long-term gain, but I generally try to program
as if boolean and int are distinct types, so the price wouldn't be
very high for me.
-- 
Philip Machanick
philip@pescadero.stanford.edu

dag@control.lth.se (Dag Bruck) (05/30/91)

In article <1991May29.171209.14760@neon.Stanford.EDU> philip@pescadero.stanford.edu writes:
>.... But how many programs would break? I for one would be prepared
>to pay the price for the long-term gain, but I generally try to program
>as if boolean and int are distinct types, so the price wouldn't be
>very high for me.

The "break old code" argument against any changes is the one you hear
most often at the C++ standardization meetings.  It is of course
valid, but often over-used.  Even if there were incompatible changes,
every compiler would have a switch to get the old behaviour.

One of the strongest arguments in favour of a new boolean data type is
that you could do function overloading:

	void f(int);
	void f(boolean);

I also think a boolean data type would increase type safety in C++, at
least the way I program (:-).

I once tried to implement `class boolean' and failed.  Firstly there
was a performance problem, which I think good compilers could
overcome.  Secondly, I don't think I gained anything in type safety
and little in convenience: to be at all useful I needed implicit type
conversions int->boolean and boolean->int.  I think a boolean data
type should _not_ be another kind of integer (at least eventually) which
can be implicitly converted to/from int.  It should instead be a
unique enumerated type, but that doesn't work until all standard
functions (e.g., operator == (int, int)) return a boolean and not
an int.

The transition to a better world (:-) should be done in two phases:

	1.  Introduce the new built-in data type `boolean' (or
	whatever you call it), and specify that all standard
	operator == (etc.) return a boolean.

	2.  Include implicit type conversions between boolean and
	int, to minimize the number of broken programs.  I think
	this automatic conversion will help in most cases.

	3.  Warn about the automatic conversions and label them as
	anachronisms.

	4.  (Some years later) Turn the warnings of (3) into errors.

We had a similar case with C++ 2.0 with respect to handling of `enum',
and I think the transition was relatively painless.  The most common
problem was that people had used an enum for booleans, which caused
warning messages under the new interpretation.

Dag Br!"uck
--
Department of Automatic Control		E-mail: dag@control.lth.se
Lund Institute of Technology
P. O. Box 118				Phone:	+46 46-104287
S-221 00 Lund, SWEDEN			Fax:    +46 46-138118

robert@am.dsir.govt.nz (Robert Davies) (05/30/91)

In article <1991May29.171209.14760@neon.Stanford.EDU> philip@pescadero.stanford.edu writes:
.......
>|>  C++ should have a
>|> standard built-in Boolean type.
> 
>Yes - maybe it would also catch a lot of those annoying hard to
>find bugs caused by typing "if (thing = whatever)" instead of
>"==". But how many programs would break? I for one would be prepared
>to pay the price for the long-term gain, but I generally try to program
>as if boolean and int are distinct types, so the price wouldn't be
>very high for me.

In cases like this where you do need a change that would damage old
code why can't you have a pragma to turn on or off an old or new
feature? So that old code would still run with the addition of one line
to each source file.

Robert

pena@brainware.fi (Olli-Matti Penttinen) (05/30/91)

In article <1991May30.060200.6590@lth.se> dag@control.lth.se (Dag Bruck) writes:

	   void f(int);
	   void f(boolean);

   I also think a boolean data type would increase type safety in C++, at
   least the way I program (:-).

   I once tried to implement `class boolean' and failed.  Firstly there
   was a performance problem, which I think good compilers could
   overcome.  Secondly, I don't think I gained anything in type safety
   and little in convenience: to be at all useful I needed implicit type
   conversions int->boolean and boolean->int.  I think a boolean data
   type should _not_ be another kind of integer (at least eventually) which
   can be implicitly converted to/from int.  It should instead be a
   unique enumerated type, but that doesn't work until all standard
   functions (e.g., operator == (int, int)) return a boolean and not
   an int.

I partly agree. It would be nice, if operator==(T,T) would return a
truly boolean value. On the other hand, it is possible to achieve the
same type safety if one implements not only a class boolean, but
classes real, integer, string, and what not, as well. That would of
course introduce a rather severe performance penalty, however. If only
current compilers did a better job in optimizing common
sub-expressions involving inlined access member functions and such... :-(

I know of at least one project, in which the group (they used Ada)
redefined practically everything, and to some extent succeeded. They
did peculiar things like had an "enum boolean { true, false, i_dont_care,
i_dont_know}" type of a construct.

Summa summarum: once again we face a consept that could be
incorporated to the language and indeed would fit in well, but without
which we can manage. IMHO, that means we shouldn't have it.
Especially, since no current library can support it.

   Dag Br!"uck
   --
   Department of Automatic Control		E-mail: dag@control.lth.se
   Lund Institute of Technology
   P. O. Box 118				Phone:	+46 46-104287
   S-221 00 Lund, SWEDEN			Fax:    +46 46-138118


==pena
--
Olli-Matti Penttinen <pena@brainware.fi> | "When in doubt, use brute force."
Brainware Oy                             |    --Ken Thompson
P.O.Box 330                              +----------------------------------
02151  ESPOO, Finland       Tel. +358 0 4354 2565       Fax. +358 0 461 617

tom@elan.Elan.COM (Thomas Smith) (05/31/91)

[ Continuing thread about merits/pitfalls of defining a boolean type
  in your application, and colliding with definitions in other modules. ]

From article <1991May30.060200.6590@lth.se>, by dag@control.lth.se (Dag Bruck):
> I once tried to implement `class boolean' and failed.  Firstly there
> was a performance problem, which I think good compilers could
> overcome.  Secondly, I don't think I gained anything in type safety
> and little in convenience: to be at all useful I needed implicit type
> conversions int->boolean and boolean->int.

I have successfully implemented Boolean data types in several large
projects (100K+ lines of C++) at the last two companies I have worked
for.  If you set it up right, you do get the type-safety without the
performance penalty.  In most cases, the biggest concern was size -
could we get a Boolean class that would have size == 1 byte (the minimum)
and align on single-byte boundaries when packed in structures.  The
answer is... depends on the architecture.  Here is what I usually end
up with:

    class Boolean {
	unsigned char value;

	friend int operator== (Boolean left, Boolean right);
	friend int operator!= (Boolean left, Boolean right);
    public:
	Boolean()  {}
	Boolean(int i) { value = ((i) ? 1 : 0); }

	operator int() const { return (int) value; }
    };

    inline int operator== (Boolean left, Boolean right)
		    { return left.value == right.value; }
    inline int operator!= (Boolean left, Boolean right)
		    { return left.value != right.value; }

    // defined in the source file as initialized to 0 and 1, respectively
    extern const Boolean FALSE;
    extern const Boolean TRUE;

Notice that initialization by int is allowed (via constructor),
but assignment is not (no operator=(int)).  There are some pitfalls
with this approach, however:
  1) The type name Boolean sometimes collides with "helpful" libraries
     that *just know* you will need a type called Boolean that is a
     typedef'd unsigned char.  The X toolkit (Xt) is a prime offender.
     Folks, what's the point of sticking the Xt prefix on 95% of the
     types defined, and skipping the 5% most likely to collide with
     an application's own types?

     This can be worked around by simply calling your Boolean something
     else, like TBoolean (Tom's Boolean :^), and undefining TRUE and FALSE
     at the beginning of the header file (if they were defines and not an
     enum).
  2) The constant values TRUE and FALSE are globals that are initialized
     via constructor, and thus cannot be safely used in other global
     constructors.  Buckle up for safety...

All in all, though, the good outweighs the bad.  One other reason that I
usually settle on the class approach for Boolean types is that I often
need many other similar types - for instance Status (SUCCESS or FAILURE) -
which are not compatible with Boolean and can cause trouble if they
are assigned to each other.  (Of course, Status is defined as int by
the X11 Xlib.h header file - don't get me started on graduate-student
software again).

Hope this was somewhat helpful - I feel better now.
    Thomas Smith
    Elan Computer Group, Inc.
    (415) 964-2200
    tom@elan.com, ...!{ames, uunet, hplabs}!elan!tom

jgro@lia (Jeremy Grodberg) (05/31/91)

In article <332@nazgul.UUCP> bright@nazgul.UUCP (Walter Bright) writes:
>In article <HAYDENS.91May23103233@natasha.juliet.ll.mit.edu> haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) writes:
>/So my question is, how can I choose one definition (my own, or someone
>/else's) for boolean variables, and still remain insulated from the sea
>/of boolean definitions in assorted include files?
>/What's the best way to do this?
>
>After years of fiddling around with various schemes myself, the most practical
>one I came up with is just use int:
>
>	/*********************
>	 * Test some quantity.
>	 * Returns:
>	 *	0	False
>	 *	!=0	True
>	 */
>
>	int func();
>
>I've gone through my code over time and have eliminated all the logical, bool,
>boolean, BOOL, etc. clutter. Although the above solution seems boring and
>inelegant, it works suprisingly well, given that it mimics C's idea of what
>is true and what is false.
>
>Note that you also need to get rid of any TRUE or FALSE #define's. Remember to
>always test for 0 or !=0.
>
>(Some other battle-scarred old C programmers have told me that they have
> eventually come to the same conclusions.)


I agree about using int for booleans, but I disagree about getting rid of 
TRUE and FALSE.  I, like a lot of programmers, skimp on writing comments.
To make up for it, I try to write self-documenting code, through
appropriate use of varible and function names.  TRUE and FALSE make it 
clear, without comments, that a boolean value is being returned.
Also, since many (bad) compilers in my early C programming days
required TRUE to be !FALSE (which could be 1, 0xFF, 0xFFFF, or something else)
for boolean arithmetic to work properly, I get worried about simply 
returning 1 for true.  Using TRUE and FALSE allows me to avoid problems
like that.  I would sooner give up NULL then TRUE or FALSE.

From my C experience, I always include:

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE !FALSE
#endif

You may complain about using !FALSE instead of (!FALSE), but it has never
caused me a problem.  Many (bad) compilers in my early C programming days
required TRUE to be !FALSE for boolean arithmetic to work properly, 
so I still use that.    If you are worried about getting some incompatible
TRUE or FALSE, you can replace the #ifndefs with #undefines.


You only get what ANSI promises (and not always that), and ANSI only
promises int's for booleans, so that's what I use.  It may mean not
discovering compiler problems, but my job is to get programs to work,
not to fix compilers.
-- 
Jeremy Grodberg      "Show me a new widget that's bug-free, and I'll show
jgro@lia.com         you something that's been through several releases."

dag@control.lth.se (Dag Bruck) (06/02/91)

In article <998@elan.Elan.COM> tom@elan.Elan.COM (Thomas Smith) writes:
>From article <1991May30.060200.6590@lth.se>, by dag@control.lth.se (Dag Bruck):
>> I once tried to implement `class boolean' and failed.  Firstly there
>> was a performance problem, which I think good compilers could
>> overcome.  Secondly, I don't think I gained anything in type safety
>> and little in convenience: to be at all useful I needed implicit type
>> conversions int->boolean and boolean->int.
>
>I have successfully implemented Boolean data types in several large
>projects (100K+ lines of C++) at the last two companies I have worked
>for.  If you set it up right, you do get the type-safety without the
>performance penalty.
>
>    class Boolean {
>	unsigned char value;
>    public:
>	Boolean()  {}
>	Boolean(int i) { value = ((i) ? 1 : 0); }
>
>	operator int() const { return (int) value; }
>    };

Tom's approach is very similar to the ones I tried.  Unfortunately, I
disagree with Tom about the usefulness of such a class.  Let's take a
look at this example:

	Boolean g(int x, Boolean y)
	{
	  Boolean z = y;
	  z = x;
	  return z;
	}

I compiled this on a Sun-3 with -O4 optimization.  The assembly code
is enclosed at the end for those who are interested.  The problems I
have is:

1.  Class Boolean occupies two bytes; a minor inconvenience.

2.  Cfront will not accept `register' declarations for `z' which will
    give me a performance hit.

3.  Assignment by an `int' is possible because there is a constructor.

    >Notice that initialization by int is allowed (via constructor),
    >but assignment is not (no operator=(int)).

    Not true.  We can even assign a `char' or `double' to `z',
    although assigning a double will give us a warning.

4.  Returning a class or a struct is awkward, even in this case where
    the compiler could have returned it in a register.

>All in all, though, the good outweighs the bad.

I think we have gained little.  In particular Boolean is not typesafe,
and the `if' statement will still accept any old garbage.

	-- Dag

===========================================================================

Here is a the MC680x0 assembly code produced for the example above.
Configuration was Sun-3, SunOS 4.1.1, Cfront 2.1, Sun's C compiler
with -O4 option.

LL0:
	.data
	.data
	.text
	.proc
|#PROC# 010
	.data
	.lcomm	L2000000,2
	LF16	=	8
	LS16	=	128
	LFF16	=	4
	LSS16	=	0
	LV16	=	4
	.text
	.globl	_g__Fi7Boolean
_g__Fi7Boolean:
|#PROLOGUE# 0
	link	a6,#-8
|#PROLOGUE# 1
	movw	a6@(14),a6@(-2)
	tstl	a6@(8)
	jeq	L77003
	moveq	#1,d1
	jra	L77004
L77003:
	moveq	#0,d1
L77004:
	movb	d1,a6@(-2)
	movl	#L2000000,a1
	movw	a6@(-2),a1@
	movl	a1,d0
|#PROLOGUE# 2
	unlk	a6
|#PROLOGUE# 3
	rts

npw@eleazar.dartmouth.edu (Nicholas Wilt) (06/02/91)

Defining your own Boolean type (with #defines, typedefs, or classes, as you
prefer) has advantages, but they are far outweighed by the problems
introduced when someone tries to use your code (or, conversely, when you're
trying to deal with code full of Booleans of one flavor or another).
What happens when you try to make use of two libraries, one of which thinks
Boolean is a "typedef int" and another which thinks Boolean is a class?
You could go through renaming and reworking, but that shouldn't be
necessary.

C++ inherits a pretty reasonable treatment of integers as Boolean values 
from C (nonzero is True when evaluating, Boolean expressions evaluate to 
integer 1 or 0).  It's portable, reasonably clean and flexible--for
instance, there's no other way to stuff multiple Booleans into a single 
word, like you can with the : construct in C structures.  A little less
pretty than any of the ten thousand other ways people can think of to
deal with Boolean values, but unlike any of those, it's standard.

Overall, I think the disadvantages outweigh the advantages.  I hate
buying C source code and seeing Boolean defined explicitly.  It gets 
ripped out as soon as a need develops to interface with it more than 
perfunctorily.

--Nick
  npw@eleazar.dartmouth.edu

jk@cs.man.ac.uk (John Kewley ICL) (06/05/91)

Here is an example of a boolean class which does not allow ints to be assigned
to booleans.

I introduce functions which return true and false values. These could be
extended for three valued logic. They are friends which use a static
(own) variable defined using the *private* int conversion constructor.

To offset the inefficiencies of the system (possibly not much worse than any
other I've seen suggested) would be to have

#ifndef NDEBUG
#define TRUE 1
#define FALSE 0
#define bool int /* or perhaps char */
#else
#define TRUE true()
#define FALSE false()

class bool
{
/*
.
.
.
*/
}
#fi

There is no assignment operator for int to bool and no int conversion 
constructor.

Taking the 4 comments made about Tom's class:

1. Boolean takes up 2 bytes
No problem if using #defines as above

2. register declarations are not allowed
I am not certain of this one. I have used registers for some examples OK,
I think (I'm no assembler hacker). register could perhaps (careful slippery
ice here) be #defined away when DEBUGging.

3. Assignment by int possible
Not that I can see in this case, except in friend functions true() and false().

4. returning a class is awkward
I don't understand this one, this may be avoided by my #defines

I think in this case, the good outweighs the bad, in particular there should be
no performance penalty when NDEBUGging.

Note I have not yet tried to implement the #define stuff, I would welcome 
peoples ideas.

OK here we go here is the class + a sample main file.
Sorry about the plethora of operators, I tried to be complete.

Have I missed any functionality?

-------------------------------------------

#include <stdio.h>

class bool
{
public:
	bool() {}
	bool(const bool& b) {c=b.c;}

	// For if statements
	operator void*() const {return (void*) c;}

	bool& operator= (const bool& r) {c= r.c; return *this;};

	bool operator! () {return (!c) ? true() : false();};

	bool operator == (const bool& r) const
		{return (c == r.c) ? true() : false();};
	bool operator != (const bool& r) const
		{return (c != r.c) ? true() : false();};

	bool operator == (int r) const
		{return (r) ? c : !c;};
	bool operator != (int r) const
		{return (r) ? !c : c;};

	friend bool operator == (int l, const bool& r)
		const {return (l) ? r.c : !r.c;};
	friend bool operator != (int l, const bool& r)
		const {return (l) ? !r.c : r.c;};

	bool operator && (const bool& r) const
		{return (c) ? bool(r.c) : false();};
	bool operator || (const bool& r) const
		{return (c) ? true() : bool(r.c);};

	bool operator && (int r) const
		{return (c) ? bool(r) : false();};
	bool operator || (int r) const
		{return (c) ? true() : bool(r);};

	friend bool operator && (int l, const bool& r) const
		{return (l) ? bool(r.c) : false();};
	friend bool operator || (int l, const bool& r) const
		{return (l) ? true() : bool(r.c);};

	friend bool& true();
	friend bool& false();

private:
	char c;
	bool(int i) {c=((i)?1:0);}

	// Disallow assignment to an int.
	// bool& operator= (int) {return *this;};

};

bool& true()
{
	static bool T(1);
	return T;
}

bool& false()
{
	static bool F(0);
	return F;
}

bool one_more( int a, int b )
{
	return (( a - 1 == b ) ? true() : false());
}

int main()
{
	bool t= true();
	bool f= false();
	bool b= true();
	bool c;
	if (one_more(2,1))
		printf("2 is one more than 1\n");
	else
		printf("2 is not one more than 1\n");
	if (one_more(1,2))
		printf("1 is one more than 2\n");
	else
		printf("1 is not one more than 2\n");

	c= false();
	b= c;
	// b= 1; // bool::bool(): private member
	      // I think it refers to bool::bool(int)

	(b) ? printf("b is true\n") : printf("b is not true\n");
	(!b) ? printf("b is false\n") : printf("b is not false\n");

	(b != c) ? printf("b and c are both the same\n")
		 : printf("b and c are not the same\n");

	if (b)
		printf("b = true\n");
	else
		printf("b = false\n");

	return(0);

	if (g(1,f))
	then
		printf("true\n");
	else
		printf("false\n");
}
--
        J.K.
 
John M. Kewley, ICL, Wenlock Way, West Gorton, Manchester. M12 5DR
Tel:   (+44) 61 223 1301 X2138  Email: jk@cs.man.ac.uk / jk@nw.stl.stc.co.uk