boolean usage

M

Malcolm McLean

Incidentally, sizeof(int)==1 isn't needed, only INT_MAX < UCHAR_MAX
(which is possible even if sizeof(int) > 1).
You use some weird and wonderful architectures.
 
B

Ben Bacarisse

Tim Rentsch said:
Peter Nilsson said:
Jens said:
Use int as a replacement type for _Bool, since any expression
where you use _Bool that expects an integer type will promote
_Bool to int, anyhow.

_Bool is an unsigned type.

right, but you will not notice this much.

I don't with a real _Bool, I do with a standin bool.
In any kind of arithmetic or comparison it will always be
promoted to an int since the two legal values of _Bool
always fit into and int.

To nitpick, C99 does not say that 0 and 1 are the only legal
values for bool, [snip elaboration]

Not said explicitly, but this does hold as a logical consequence
of the rules governing conversions.

But there are other ways to get a value into a _Bool than via a
conversion.

<snip>
 
T

Tim Rentsch

Ben Bacarisse said:
Tim Rentsch said:
Peter Nilsson said:
Use int as a replacement type for _Bool, since any expression
where you use _Bool that expects an integer type will promote
_Bool to int, anyhow.

_Bool is an unsigned type.

right, but you will not notice this much.

I don't with a real _Bool, I do with a standin bool.

In any kind of arithmetic or comparison it will always be
promoted to an int since the two legal values of _Bool
always fit into and int.

To nitpick, C99 does not say that 0 and 1 are the only legal
values for bool, [snip elaboration]

Not said explicitly, but this does hold as a logical consequence
of the rules governing conversions.

But there are other ways to get a value into a _Bool than via a
conversion.

That's true but it doesn't change the result. The question
is what values can _Bool hold as documented values (ie, and
not trap representations), and these are constrained by
conversion rules to be only 0 and 1 (if the implementation
is conforming). Clearly if a _Bool contains a trap
representation then all bets are off, just like any other
undefined behavior. But if what is held isn't a trap
representation then the value must be either 0 or 1.
 
K

Keith Thompson

Tim Rentsch said:
Ben Bacarisse said:
Tim Rentsch said:
Use int as a replacement type for _Bool, since any expression
where you use _Bool that expects an integer type will promote
_Bool to int, anyhow.

_Bool is an unsigned type.

right, but you will not notice this much.

I don't with a real _Bool, I do with a standin bool.

In any kind of arithmetic or comparison it will always be
promoted to an int since the two legal values of _Bool
always fit into and int.

To nitpick, C99 does not say that 0 and 1 are the only legal
values for bool, [snip elaboration]

Not said explicitly, but this does hold as a logical consequence
of the rules governing conversions.

But there are other ways to get a value into a _Bool than via a
conversion.

That's true but it doesn't change the result. The question
is what values can _Bool hold as documented values (ie, and
not trap representations), and these are constrained by
conversion rules to be only 0 and 1 (if the implementation
is conforming). Clearly if a _Bool contains a trap
representation then all bets are off, just like any other
undefined behavior. But if what is held isn't a trap
representation then the value must be either 0 or 1.

If, for a given implementation, all representations of _Bool other
than 0 and 1 are trap representations, then what you say is correct
for that implementation. I'm not sure whether it's correct for
all implementations.

Assume CHAR_BIT==8, sizeof(_Bool)==1, and _Bool has no trap
representations.

#include <stdio.h>

int main(void) {
union {
unsigned char c;
_Bool b;
} u;
u.c = 2;
printf("sizeof(_Bool) = %d, sizeof u = %d, u.b = %d\n",
(int)sizeof(_Bool),
(int)sizeof u,
(int)u.b);
return 0;
}

On my system, this program prints:

sizeof(_Bool) = 1, sizeof u = 1, u.b = 2

See the footnote in C99 6.5.2.3 (it's footnote #82 in N1256):

If the member used to access the contents of a union object
is not the same as the member last used to store a value in
the object, the appropriate part of the object representation
of the value is reinterpreted as an object representation in
the new type as described in 6.2.6 (a process sometimes called
"type punning"). This might be a trap representation.

I don't believe this program violates any constraint or exhibits
undefined behavior.

Interestingly, with optimization enabled, it shows "u.b = 0";
I wonder if that's a violation of the standard.

I suspect the standard is insufficiently clear about the behavior of
_Bool in some circumstances.
 
T

Tim Rentsch

Keith Thompson said:
Tim Rentsch said:
Ben Bacarisse said:
Use int as a replacement type for _Bool, since any expression
where you use _Bool that expects an integer type will promote
_Bool to int, anyhow.

_Bool is an unsigned type.

right, but you will not notice this much.

I don't with a real _Bool, I do with a standin bool.

In any kind of arithmetic or comparison it will always be
promoted to an int since the two legal values of _Bool
always fit into and int.

To nitpick, C99 does not say that 0 and 1 are the only legal
values for bool, [snip elaboration]

Not said explicitly, but this does hold as a logical consequence
of the rules governing conversions.

But there are other ways to get a value into a _Bool than via a
conversion.

That's true but it doesn't change the result. The question
is what values can _Bool hold as documented values (ie, and
not trap representations), and these are constrained by
conversion rules to be only 0 and 1 (if the implementation
is conforming). Clearly if a _Bool contains a trap
representation then all bets are off, just like any other
undefined behavior. But if what is held isn't a trap
representation then the value must be either 0 or 1.

If, for a given implementation, all representations of _Bool other
than 0 and 1 are trap representations, then what you say is correct
for that implementation. I'm not sure whether it's correct for
all implementations.

Assume CHAR_BIT==8, sizeof(_Bool)==1, and _Bool has no trap
representations.

Then one of two things must be true, either: one, _Bool
has 7 padding bits; or two, _Bool has more than 1 value
bit.

If _Bool has 7 padding bits, then the program you
give (below) won't print 'u.b = 2' in a conforming
implementation.

If _Bool has more than 1 value bit, then an implementation
cannot simultaneously satisfy 6.3p2 and 6.3.1.2p1. Hence
such an implementation cannot be conforming.

The conclusion is that either this implementation is
not conforming, or that its _Bool does indeed have
trap representations.
#include <stdio.h>

int main(void) {
union {
unsigned char c;
_Bool b;
} u;
u.c = 2;
printf("sizeof(_Bool) = %d, sizeof u = %d, u.b = %d\n",
(int)sizeof(_Bool),
(int)sizeof u,
(int)u.b);
return 0;
}

On my system, this program prints:

sizeof(_Bool) = 1, sizeof u = 1, u.b = 2

See the footnote in C99 6.5.2.3 (it's footnote #82 in N1256):

If the member used to access the contents of a union object
is not the same as the member last used to store a value in
the object, the appropriate part of the object representation
of the value is reinterpreted as an object representation in
the new type as described in 6.2.6 (a process sometimes called
"type punning"). This might be a trap representation.

I don't believe this program violates any constraint or exhibits
undefined behavior.

Storing and accessing the union members doesn't, but the
program does because u.b being able to hold the value 2 means
that _Bool must have trap representations if the implementation
is conforming, and one of these trap representations is
being converted to (int), which is UB.
Interestingly, with optimization enabled, it shows "u.b = 0";
I wonder if that's a violation of the standard.

I believe it's a consequence of undefined behavior caused
by the presence of a converted trap representation.
I suspect the standard is insufficiently clear about the behavior of
_Bool in some circumstances.

Personally I think that what's specified about _Bool is clear
enough, but it would be nice to have more assurance that what's
specified matches what was intended,

(That doesn't apply to _Bool bitfields, about which I agree
that more clarity is needed.)
 
K

Kenny McCormack

Kiki Thompson said:
If, for a given implementation, all representations of _Bool other
than 0 and 1 are trap representations, then what you say is correct
for that implementation. I'm not sure whether it's correct for
all implementations.
etc, etc...

Remind me again: Just exactly how many angels can dance on the head of a pin?
 
B

Bill Cunningham

Malcolm said:
Bool breaks libraries. Just return an int, to be on the safe side.

Do you mean not even use this header or don't use true just 1 and not
false just 0?

Bill
 
P

Peter Nilsson

Tim Rentsch said:
Ben Bacarisse said:
Tim Rentsch said:
_Bool is an unsigned type. ....
In any kind of arithmetic or comparison it will always be
promoted to an int since the two legal values of _Bool
always fit into and int.

To nitpick, C99 does not say that 0 and 1 are the only legal
values for bool, [snip elaboration]

Not said explicitly, but this does hold as a logical
consequence of the rules governing conversions.

But there are other ways to get a value into a _Bool than via a
conversion.

That's true but it doesn't change the result. The question is
what values can _Bool hold as documented values (ie, and not trap
representations),

My last assertion was that _Bool's integer promotion can potentially
be to unsigned int.

"An object declared as type _Bool is large enough to store the
values 0 and 1."[1] There is nothing stating that _Bool must only
have 1 value bit, just as UINT_MAX being at least 65535 is not a
statement that it can only be 65535.
and these are constrained by conversion rules to be only 0 and 1

That mearely constrains the values that result from conversion.
(if the implementation is conforming).

You haven't demonstrated it would be non-conforming.
Clearly if a _Bool contains a trap representation then all bets
are off, just like any other undefined behavior.

True, but not the issue.
But if what is held isn't a trap representation then the value
must be either 0 or 1.

If it isn't a trap representation then all you can say is the value
is appropriate to the object type.

The Committee's Response in DR335 says _Bool has an implementation
defined width. Has there been any advance on that?

[1] C++'s bool (upon which _Bool is obviously based) is considerably
clearer with regards to specifiction: "Values of type bool are either
true or false."
 
M

Malcolm McLean

    Do you mean not even use this header or don't use true just 1 andnot
false just 0?
Yes, don't even include stdbool.h.

If you want to return a boolean, return an int, if you want to pass
one, pass as an int. If you need to pass or return several booleans,
pack them into an int.

Compare explictly to zero or non-zero.

It's not ideal, but it's the best you can do with the language as it
is. The alternative is that you get bools and Bools and bool_ts, and
that becomes confusing.
 
B

Ben Bacarisse

Peter Nilsson said:
Tim Rentsch said:
Ben Bacarisse said:
_Bool is an unsigned type. ...
In any kind of arithmetic or comparison it will always be
promoted to an int since the two legal values of _Bool
always fit into and int.

To nitpick, C99 does not say that 0 and 1 are the only legal
values for bool, [snip elaboration]

Not said explicitly, but this does hold as a logical
consequence of the rules governing conversions.

But there are other ways to get a value into a _Bool than via a
conversion.

That's true but it doesn't change the result. The question is
what values can _Bool hold as documented values (ie, and not trap
representations),

My last assertion was that _Bool's integer promotion can potentially
be to unsigned int.

"An object declared as type _Bool is large enough to store the
values 0 and 1."[1] There is nothing stating that _Bool must only
have 1 value bit, just as UINT_MAX being at least 65535 is not a
statement that it can only be 65535.
and these are constrained by conversion rules to be only 0 and 1

That mearely constrains the values that result from conversion.

But, crucially, this combines with 6.3 p2 ("conversion of an operand
value to a compatible type causes no change to the value...") to mean
that only 0 and 1 are possible values. _Bool is compatible with _Bool,
so for any _Bool b, (_Bool)b must be 0 or 1 and must not change the
value of b. If b were permitted a value >1 (_Bool)b would have to
change the value.
You haven't demonstrated it would be non-conforming.


True, but not the issue.

A consequence of the above is that _Bool must have padding bits
(CHAR_BIT - 1 of them). One simple solution for implementers is to say
that having any of these padding bits set is a trap representation.
That way there is no need to handle

*(unsigned char *)&b = -1;

in any special way. After this, (_Bool)b will not be equal to b, but
that does not matter since accessing b produced undefined behaviour. If
the implementer ruled out trap representations, then any access to b
would probably have to mask the padding bits out.
If it isn't a trap representation then all you can say is the value
is appropriate to the object type.

The Committee's Response in DR335 says _Bool has an implementation
defined width. Has there been any advance on that?

The width includes any padding bits. If the response suggests that
_Bool can have >1 value bits (sorry, I haven't read it) then I think
they've missed the consequences of 6.3 p2 (as did I first time round).

<snip>
 
K

Keith Thompson

Malcolm McLean said:
Yes, don't even include stdbool.h.

If you want to return a boolean, return an int, if you want to pass
one, pass as an int. If you need to pass or return several booleans,
pack them into an int.

Compare explictly to zero or non-zero.

It's not ideal, but it's the best you can do with the language as it
is. The alternative is that you get bools and Bools and bool_ts, and
that becomes confusing.

You should be aware that Malcolm's advice is, to say, the least,
controversial

Another approach is this: If you know your code will only be used
with C99 compilers, go ahead and use <stdbool.h>, bool, false,
and true whenever they're appropriate.

There is some risk to this approach. Since the identifiers
"bool", "false", and "true were not reserved in C90, there could
be libraries that use them, and you could run into conflicts if
you use <stdbool.h> and such a library in the same source file.
(Note that such libraries are already incompatible with C++, in
which bool, false, and true are keywords.)

Another issue is that many libraries define their own Boolean-like
types, with various names such as BOOL, Boolean, TBool, and so forth.
Interoperability between libraries that define their own potentially
incompatible Boolean-like types can be a problem, but I don't think
it's an argument for int over _Bool/bool. C99 introduced _Bool
and bool specifically to clean up this mess.
 
K

Kenny McCormack

Keith Thompson said:
(Note that such libraries are already incompatible with C++, in
which bool, false, and true are keywords.)

But of course, as you and your friends like to point out at every possible
opportunity, that is, in this newsgroup, as irrelevant as would be any
incompatibility with COBOL.

--
One of the best lines I've heard lately:

Obama could cure cancer tomorrow, and the Republicans would be
complaining that he had ruined the pharmaceutical business.

(Heard on Stephanie Miller = but the sad thing is that there is an awful lot
of direct truth in it. We've constructed an economy in which eliminating
cancer would be a horrible disaster. There are many other such examples.)
 
P

Peter Nilsson

Ben Bacarisse said:
Peter Nilsson said:
Tim Rentsch said:
But there are other ways to get a value into a _Bool than
via a conversion.

That's true but it doesn't change the result. The question
is what values can _Bool hold as documented values (ie, and
not trap representations),

My last assertion was that _Bool's integer promotion can
potentially be to unsigned int.

"An object declared as type _Bool is large enough to store the
values 0 and 1."[1] There is nothing stating that _Bool must
only have 1 value bit, just as UINT_MAX being at least 65535 is
not a statement that it can only be 65535.
and these are constrained by conversion rules to be only 0
and 1

That mearely constrains the values that result from conversion.

But, crucially, this combines with 6.3 p2 ("conversion of an
operand value to a compatible type causes no change to the value
...") to mean that only 0 and 1 are possible values. _Bool is
compatible with _Bool, so for any _Bool b, (_Bool)b must be 0 or
1 and must not change the value of b. ...

No conversion is required to convert a _Bool to a _Bool. Under
[n1256] 6.5.4p4 "A cast that specifies no conversion has no effect
on the type or value of an expression."
The width includes any padding bits.

"The precision of an integer type is the number of bits it uses
to represent values, excluding any sign and padding bits. The
width of an integer type is the same but including any sign
bit; ..."
If the response suggests that _Bool can have >1 value bits
(sorry, I haven't read it) ...

<http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_335.htm>
 
B

Ben Bacarisse

Peter Nilsson said:
Ben Bacarisse said:
Peter Nilsson said:
But there are other ways to get a value into a _Bool than
via a conversion.

That's true but it doesn't change the result. The question
is what values can _Bool hold as documented values (ie, and
not trap representations),

My last assertion was that _Bool's integer promotion can
potentially be to unsigned int.

"An object declared as type _Bool is large enough to store the
values 0 and 1."[1] There is nothing stating that _Bool must
only have 1 value bit, just as UINT_MAX being at least 65535 is
not a statement that it can only be 65535.

and these are constrained by conversion rules to be only 0
and 1

That mearely constrains the values that result from conversion.

But, crucially, this combines with 6.3 p2 ("conversion of an
operand value to a compatible type causes no change to the value
...") to mean that only 0 and 1 are possible values. _Bool is
compatible with _Bool, so for any _Bool b, (_Bool)b must be 0 or
1 and must not change the value of b. ...

No conversion is required to convert a _Bool to a _Bool. Under
[n1256] 6.5.4p4 "A cast that specifies no conversion has no effect
on the type or value of an expression."

Ah that's crucial. Thanks.

In gcc, the fact the after putting 2 into a _Bool b, (_Bool)b *does*
change the value means that gcc's _Bool has only one value bit. Any
value greater than > 1 that you see is simply the effect of undefined
behaviour due to _Bool having padding bits and trap representations.
"The precision of an integer type is the number of bits it uses
to represent values, excluding any sign and padding bits. The
width of an integer type is the same but including any sign
bit; ..."

Yes, I was having "a moment". Thanks for reminding me to check before
posting!

Given the above, gcc should reject any _Bool bit fields with a size > 1
and indeed it does. gcc's _Bool has 1 value bit and all other bit
settings are silent trap representations. Comeau's comiler permits wide
_Bool bit fields. I wonder if (_Bool)b is a no-op on the compiler.
 
P

Peter Nilsson

Tim Rentsch said:
Keith Thompson said:
Use int as a replacement type for _Bool, since any expression
where you use _Bool that expects an integer type will promote
_Bool to int, anyhow.
_Bool is an unsigned type.
right, but you will not notice this much.
I don't with a real _Bool, I do with a standin bool.
In any kind of arithmetic or comparison it will always be
promoted to an int since the two legal values of _Bool
always fit into and int.
To nitpick, C99 does not say that 0 and 1 are the only legal
values for bool, [snip elaboration]
Not said explicitly, but this does hold as a logical consequence
of the rules governing conversions.
But there are other ways to get a value into a _Bool than via a
conversion.
That's true but it doesn't change the result.  The question
is what values can _Bool hold as documented values (ie, and
not trap representations), and these are constrained by
conversion rules to be only 0 and 1 (if the implementation
is conforming).  Clearly if a _Bool contains a trap
representation then all bets are off, just like any other
undefined behavior.  But if what is held isn't a trap
representation then the value must be either 0 or 1.
If, for a given implementation, all representations of _Bool other
than 0 and 1 are trap representations, then what you say is correct
for that implementation.  I'm not sure whether it's correct for
all implementations.
Assume CHAR_BIT==8, sizeof(_Bool)==1, and _Bool has no trap
representations.  
Then one of two things must be true, either:  one, _Bool
has 7 padding bits;  or two, _Bool has more than 1 value
bit.
If _Bool has 7 padding bits, then the program you
give (below) won't print 'u.b = 2' in a conforming
implementation.

Agreed, because the padding bits by definition don't contribute to
the value.
If _Bool has more than 1 value bit, then an implementation
cannot simultaneously satisfy 6.3p2 and 6.3.1.2p1.  Hence
such an implementation cannot be conforming.
The conclusion is that either this implementation is
not conforming, or that its _Bool does indeed have
trap representations.

As has happened several times before, I was in the middle of composing a
refutation of these last two points when I realized you're right.

When I convert the value of u.b to _Bool (a conversion from _Bool to
_Bool), the result is 1 -- which violates 6.3p2 unless u.b held a trap
representation.

I couldn't find any discussion of _Bool's trap
representations and/or padding bits, or lack thereof,
in the gcc documentation, but I did find something in
<http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1356.htm>:

    Joseph S. Myers' reading of this issue is:

    The "width" of a type is the number of value and sign bits
    (6.2.6.2#6), so post-TC2 a _Bool:CHAR_BIT bit-field is valid
    only if the implementation defines _Bool to have CHAR_BIT value
    bits. GCC defines it to have one value bit with the other bits
    being padding bits and undefined behavior if you access a _Bool
    representation with any of the padding bits having a nonzero
    value (such representations being trap representations). Thus the
    width of _Bool is 1 with GCC and the diagnostics are required.

So for gcc (assuming Joseph S. Myers is correct, and it certainly makes
sense), _Bool has 1 value bit and 7 padding bits, and all but 2 of its
256 possible representations are trap representations.

As far as I can tell, _Bool *cannot* have more than 1 value bit, and
must have exactly CHAR_BIT-1 padding bits.  If, in addition, it has no
trap representations, then evaluating a _Bool object must ignore all but
the single value bit, yielding either 0 or 1.  If it does have trap
representations (as gcc does), evaluating one of them has undefined
behavior and may yield (or do) anything.

[snip]
I believe it's a consequence of undefined behavior caused
by the presence of a converted trap representation.

Makes sense.

[snip]

--
Keith Thompson (The_Other_Keith) (e-mail address removed)  <http://www.ghoti.net/~kst>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"- Hide quoted text-

- Show quoted text -
 
P

Peter Nilsson

Keith Thompson said:
...
When I convert the value of u.b to _Bool (a conversion from
_Bool to _Bool)...

_Bool to _Bool does not require a conversion, a _Bool *is* a _Bool.
 
K

Keith Thompson

Peter Nilsson said:
_Bool to _Bool does not require a conversion, a _Bool *is* a _Bool.

I agree.

C99 6.5.4p4:

A cast that specifies no conversion has no effect on the type or
value of an expression.

Which implies that it's possible for a cast to specify no conversion.
(The standard could have said that it specifies a conversion to the same
type, which has no effect.)
 
T

Tim Rentsch

Peter Nilsson said:
_Bool to _Bool does not require a conversion, a _Bool *is* a _Bool.

Assignment _always_ performs a conversion, even if the two types
are the same. 6.5.16.1p2 -- look it up.
 
T

Tim Rentsch

Peter Nilsson said:
Ben Bacarisse said:
Peter Nilsson said:
But there are other ways to get a value into a _Bool than
via a conversion.

That's true but it doesn't change the result. The question
is what values can _Bool hold as documented values (ie, and
not trap representations),

My last assertion was that _Bool's integer promotion can
potentially be to unsigned int.

"An object declared as type _Bool is large enough to store the
values 0 and 1."[1] There is nothing stating that _Bool must
only have 1 value bit, just as UINT_MAX being at least 65535 is
not a statement that it can only be 65535.

and these are constrained by conversion rules to be only 0
and 1

That mearely constrains the values that result from conversion.

But, crucially, this combines with 6.3 p2 ("conversion of an
operand value to a compatible type causes no change to the value
...") to mean that only 0 and 1 are possible values. _Bool is
compatible with _Bool, so for any _Bool b, (_Bool)b must be 0 or
1 and must not change the value of b. ...

No conversion is required to convert a _Bool to a _Bool. Under
[n1256] 6.5.4p4 "A cast that specifies no conversion has no effect
on the type or value of an expression."

That's immaterial, because conversion from _Bool to _Bool
happens at other places besides casts.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,091
Messages
2,570,605
Members
47,225
Latest member
DarrinWhit

Latest Threads

Top