boolean usage


D

dr.oktopus

Hi,
I am developing a little library and I would like to develop it so
that it will be conforming both to the old and the new (c99) standard.
I have a function returning a boolean value, so I think that something
like:

file.h:

#if __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#else
#define bool int
#endif

/* rest of the header */

bool my_func (void);

file.c:

#if __STDC_VERSION__ >= 199901L
#define false 0
#define true (!false)
#endif

should be. But bool could be defined even in user code.
What's the proper way to handle this kind of things?
 
Ad

Advertisements

K

Keith Thompson

dr.oktopus said:
I am developing a little library and I would like to develop it so
that it will be conforming both to the old and the new (c99) standard.
I have a function returning a boolean value, so I think that something
like:

file.h:

#if __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#else
#define bool int
#endif

/* rest of the header */

bool my_func (void);

file.c:

#if __STDC_VERSION__ >= 199901L
#define false 0
#define true (!false)
#endif

should be. But bool could be defined even in user code.
What's the proper way to handle this kind of things?

Well, code that doesn't use _Bool or <stdbool.h> can still conform to
both C90 and C99 (the C committee carefully designed it that way). But
if you want to use <stdbool.h> if it's available, perhaps with the goal
of dropping pre-C99 support eventually, there are several ways to do it.

The first thing I recommend reading is section 9 of the comp.lang.c FAQ,
<http://c-faq.com/>.

I'd probably use typedef rather than #define for the bool type, but
that's a fairly minor point.

I'm not sure why you define "bool" in file.h and "true" and "false" in
file.c. And there's no point in defining true as (!false); 1 and 0 are
clear enough.

Here's how I might do it:

#if __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#else
typedef enum { false, true } bool;
#endif

The enum isn't *exactly* equivalent to C99's _Bool/bool, but it's not
hard to restrict your usage to cases where it's close enough.

On the other hand, as you say, the names "bool", "false", and "true"
might be defined in some other header and your own names could conflict
with them. In that case, you can pick a unique prefix and do something
like:

#if __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#define FOO_bool bool
#define FOO_false false
#define FOO_true true
#else
typedef enum { FOO_false, FOO_true } FOO_bool;
#endif

That's quite ugly; you'll have to decide whether it's worth it.
 
B

Ben Bacarisse

I'd probably use typedef rather than #define for the bool type, but
that's a fairly minor point.

I think a typedef is neater (in some vague aesthetic way) but a macro
has the advantage that a program that uses the library and also defines
its own bool can suppress it's own definition if need be. You might
even be lucky and not have to do anything if the definitions are the
same. That option won't be available with a typedef in the library's
header file.

<snip>
 
M

Malcolm McLean

But bool could be defined even in user code.
What's the proper way to handle this kind of things?
Bool breaks libraries. Just return an int, to be on the safe side.
 
M

Malcolm McLean

I just don't see a need for _Bool_ in most large programs.

"int" is just fine.
It documents what the variable is.

void circle(int x, int y, int r, bool fill)

is obvious how to use, int fill might be passing an option from a menu
of fill patterns.
 
Ad

Advertisements

K

Keith Thompson

Datesfat Chicks said:
I do embedded work with small micros (2K of RAM is a high-end part for
me), so every bit has to count.

I typically use _Bool_ or _Boolean_ (forget what the right keyword
is). Anyway, that does a few neat things in the development tools:

a)It causes the linker to manage RAM at the bit level (rather than the
byte level). So you can link a bunch of modules together each with
_Bool_'s, and variables from different modules are actually often in
the same byte. No RAM wasted! The tools pack the bits.
[...]

Either your tools are generating C code that emulates 1-bit objects
using either bit fields or bitwise operations, or your C compiler
is non-conforming. In standard C, if you apply sizeof to an object,
type, or expression of any type, the result is at least 1 byte,
which is at least 8 bits. (sizeof can't be applied to bit fields.)

This is just an observation; I don't suggest that there's anything
wrong with a non-conforming implementation. But I don't think your
situation is an argument for or against using _Bool in standard C.
 
S

Seebs

Digression: I've been wondering whether it's possible for a
compiler for a bit-addressable machine to get away with tightly-packing
_Bools and still somehow get away with lying that sizeof(_Bool)==1.
The only way I can think to detect this would be something like
bool bits[16] = {0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1};
bool bp = bits;
char *cp = (char *)bits;
assert((void *)bp == (void *)cp);
-- but is that even mandated?

Hmm. Here's my thought. In such a case, consider the two objects
bits[3] and bits[5]. Obviously, sizeof(bits[3]) can't be less than 1,
and CHAR_BIT can't be less than 8, so we know for sure that there are
8 consecutive bits which are part of bits[3]. Same for bits[5]. But
their storage spaces, it seems to me, clearly overlap. (If not, look
at bits[4], which will overlap one of them.)

So I think it's prohibited, but if you happen never to do anything which
would detect this, and the compiler can prove that you never do such a thing,
I assume it'd be allowed.

-s
 
K

Keith Thompson

Datesfat Chicks said:
[...]
Either your tools are generating C code that emulates 1-bit objects
using either bit fields or bitwise operations, or your C compiler
is non-conforming. In standard C, if you apply sizeof to an object,
type, or expression of any type, the result is at least 1 byte,
which is at least 8 bits. (sizeof can't be applied to bit fields.)

Interesting discussion. I wasn't aware it was non-comforming.

I consider it a gift from The Heavens. Every one of those variables
saves 7 bits of RAM!
[...]

In some environments, the 7 bits of RAM saved for each Boolean
stored in 1 bit rather than 8 would be more than wiped out by the
increase in the size of the code needed to access it.

The tradeoff could well be different in your environment, either if
no extra machine code is needed to access single bits (depends on
the CPU, or if space for variables is in much shorter supply than
code space (ROM?).

For large arrays of booleans, of course, it's a different story.
 
P

Peter Nilsson

dr.oktopus said:
Hi,
I am developing a little library and I would like to develop
it so that it will be conforming both to the old and the new
(c99) standard. I have a function returning a boolean value,
so I think that something like:

file.h:

#if __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#else
#define bool int

I prefer...

typedef unsigned bool;

....since _Bool is an unsigned integer type and should work
as expected in a bitfield of width 1.
 
J

Jens

I prefer...

  typedef unsigned bool;

I think typedef is not a good idea, simply because in C99 it is a
macro and nothing else. I'd just use

#define bool int
#define false 1
#define true 0

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.

Jens
 
Ad

Advertisements

J

James Kuyper

On 05/17/2011 02:37 AM, Jens wrote:
....
I think typedef is not a good idea, simply because in C99 it is a
macro and nothing else. ...

#define PINT int *
typedef int *pint;

const PINT a;
PINT const b;
const pint c;
pint const d;

Tell me which of the above variables have the same type? If you think
that typedef is "a macro and nothing else", you'll probably get one of
them wrong.
 
M

Morris Keesan

On 05/17/2011 02:37 AM, Jens wrote:
...

#define PINT int *
typedef int *pint;

const PINT a;
PINT const b;
const pint c;
pint const d;

Tell me which of the above variables have the same type? If you think
that typedef is "a macro and nothing else", you'll probably get one of
them wrong.

You deleted too much of Jens's post. I think his point (which took a
second
reading, for me) is not that typedef is a macro, but that typedef'ing
bool is a bad idea, because in C99 it (bool) is a macro.
 
P

Peter Nilsson

Jens said:
I think typedef is not a good idea, simply because in C99 it is a
macro and nothing else.

Spice to taste with: #define bool bool
#define bool int

This allows unsigned bool and signed bool.
#define false 1
#define true 0

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. Using int, especially in bitfields of
width 1 can result in negative values. Unsigned char is better
than int, but implementations are not required to support bitfields
of character types.

I have used bool in bitfields, but I can't recall having a problem
with integral promotion of bool as unsigned int. I can think of
circumstances, but they seem contrived. [e.g. flag * (-42)]
 
J

James Kuyper

You deleted too much of Jens's post. I think his point (which took a
second
reading, for me) is not that typedef is a macro, but that typedef'ing
bool is a bad idea, because in C99 it (bool) is a macro.

Yes, that was his point, and I had nothing to say about his point.

I did have something to say about a side-issue he raised. That's
precisely why I deleted everything except the side-issue.
 
J

Jens

This allows unsigned bool and signed bool.



_Bool is an unsigned type.

right, but you will not notice this much. 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. E.g in C99

((bool)something) || -1

will always be -1 and not UINT_MAX. If you have bool defined or
typedefed to unsigned it will have that different value. So using int
is mostly undistinguishable from the original _Bool.
Using int, especially in bitfields of
width 1 can result in negative values. Unsigned char is better
than int, but implementations are not required to support bitfields
of character types.

This is exactly why it is better to have it as `int`, so at least your
code compiles on every platform.

And if you mean an unsigned bitfield of width one, you should use,
well, unsigned for that :)
I have used bool in bitfields, but I can't recall having a problem
with integral promotion of bool as unsigned int. I can think of
circumstances, but they seem contrived. [e.g. flag * (-42)]

bool toto = something();

a += flag ? toto : -1;

If bool is unsigned, this explodes.

Jens
 
Ad

Advertisements

P

Peter Nilsson

Jens said:
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, merely that it must be large enough to store
them, and those are the only values a bool can be through
conversion or assignment.

It may not be the standard's intent, but if sizeof(int) == 1,
and INT_MAX < UCHAR_MAX, then I believe it's possible for _Bool
to promote to unsigned int.
E.g in C99

((bool)something) || -1

will always be -1 and not UINT_MAX. If you have bool defined or
typedefed to unsigned it will have that different value. So using
int is mostly undistinguishable from the original _Bool.

That expression will always be 1 of type int, unless 'something'
invokes UB.
This is exactly why it is better to have it as `int`, so at
least your code compiles on every platform.

It compiles on every platform using unsigned. Moreover, it won't
accidentally obtain -1 for single bit bitfields.
And if you mean an unsigned bitfield of width one, you should
use, well, unsigned for that :)

Okay, so you use boolean arithmetic, I tend not to. I use boolean
bitfields (albeit rarely,) you tend not to, or force yourself to
use unsigned. Our milage varies.
 
J

Jens

That expression will always be 1 of type int, unless 'something'
invokes UB.

I should have said "if something evaluates to 0 will always be -1 and
not UINT_MAX". The thing is that that expression has a type according
to type promotions regardless whether or not it is short evaluated.
Okay, so you use boolean arithmetic, I tend not to. I use boolean
bitfields (albeit rarely,) you tend not to,

I do, but I use _Bool for that :)
or force yourself to use unsigned.

which I think is a good idea whenever you think of bitfields as flags
Our milage varies.

Probably, it also says that there is no replacement in C89 for _Bool
(and bool) that would behave the same as _Bool under all
circumstances.

Jens
 
T

Tim Rentsch

Peter Nilsson said:
Jens said:
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.
It may not be the standard's intent, but if sizeof(int) == 1,
and INT_MAX < UCHAR_MAX, then I believe it's possible for _Bool
to promote to unsigned int.

It isn't, a corollary to the aforementioned result about what
values _Bool may have. (Arguably a _Bool bitfield is a different
matter, because the bitfield-ness may influence the _Bool-ness;
the Standard is somewhat murky about the semantics of _Bool
bitfields.)

Incidentally, sizeof(int)==1 isn't needed, only INT_MAX < UCHAR_MAX
(which is possible even if sizeof(int) > 1).
 
Ad

Advertisements

R

Ralf Damaschke

Jens said:
I should have said "if something evaluates to 0 will always be
-1 and not UINT_MAX". The thing is that that expression has a
type according to type promotions regardless whether or not it
is short evaluated.

No. As Peter said, the logical OR operator always yields a result
of type int with its value being either 0 or 1.

-- Ralf
 

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

Top