stdbool.h

B

Ben Bacarisse

Malcolm McLean said:
People aren't machines.

You mean that they are more complex machines than the ones we make.
They'll pass "true" or "false" to a function because it's already a
descriptive identifier.

It is because they are "not machines" that they should be able to think
about what other human readers will make of their code. A thoughtful
programmer will see that 'true' is exactly as descriptive as 42 -- no
more and no less. Are there some who will pepper their code with 42s
regardless of good practice? Yes. Are there some who will give 42 a
name but yet still pass true and false where the meaning is unclear?
Apparently there are. The advice should be to include all such
constants in the advice about not having magic constants in code.
They'll use bool as a parameter and not provide any defined #constants
because it's obvious that EnableControl(true) means enable it and
EnableControl(false) means disable it.

And in that case they are correct. There is no need to name those
instances. There are analogous situations for other types as well. You
will find good quality code that has i+1 and j-1 in it. The context
will often remove the need to name the constant. It's a judgement made
by people who are not acting like machines.
After all, the other way round
would be silly. They can't make the leap to the poor person totally unfamiliar
with the API who sees EnableControl(false) embedded in a long and confusing
function which is somehow setting the control to the wrong state.

OTOH if you have #define CONTROL_ENABLE 42 few people will pass a raw
42.

I don't get this point at all. Why would you do that? Are you giving
an example of a bad API in order to make some general point?
 
S

Stefan Ram

Malcolm McLean said:
sd = standard_deviation(x, N, true);
sd = standard_deviation(x, N, SD_POPULATION);

The first comparison is not fair because it uses literals
for the booleans and names for everything else. Compare
literals with literals or names with names.

»SD_POPULATION« is no good boolean name. A good boolean name
would be »is_...«, »has_...«, »...able«, or an imperative.
A population is not boolean.
 
M

Malcolm McLean

The first comparison is not fair because it uses literals
for the booleans and names for everything else. Compare
literals with literals or names with names.

»SD_POPULATION« is no good boolean name. A good boolean name
would be »is_...«, »has_...«, »...able«, or an imperative.
A population is not boolean.
A standard deviation is a measure of spread. If you can measure the entire
population, it's defined as root mean squared difference from the mean.
If you can only measure a sample, it's one less in the denominator, i.e
you divide by N-1 instead of N when taking the mean.
So there are two options, and pretty much everyone who writes a standard
deviation routine will want to provide a way of choosing one of them.
It's unlikely that someone will invent another measure of spread that
constitutes a third option, because you inherently have either an entire
population or a sample from that population.
So you can argue about whether the third parameter is philosophically boolean
or an enumeration that happens to have two values. I'm not taking a position
on that, simply noting that C doesn't allow the syntax

sd = standard_deviation( values = x, count = N, issample = false)

you can of course put comments, but that's unlikely to happen.

The third parameter could just as logically be ispopulation as issample
I suppose you could have another philosophical argument about whether a
population or a sample is the more basic type and the natural boolean
representation for that.
 
M

Malcolm McLean

I don't get this point at all. Why would you do that? Are you giving
an example of a bad API in order to make some general point?
We could reasonably do this

#define CONTROL_DISABLE 123
#define CONTROL_ENABLE 42

/* pass CONTROL_DISABLE or CONTROL_ENABLE */
void EnableControl(int action);

Now very few programmers would be stupid enough to pass a raw 42.

However if we declare the function as taking a bool, the same programmer
might well pass true and false. When you see
EnableControl(false)
or worse
EnableControl(0);
in code you're debugging, then that might well be confusing.
 
S

Stefan Ram

Malcolm McLean said:
might well pass true and false. When you see
EnableControl(false)
or worse
EnableControl(0);
in code you're debugging, then that might well be confusing.

In Java, there is a method with the signature
»javax.swing.Timer.setLogTimers( boolean )«.

Following this example, we can use the name »setControlEnabled«:

setControlEnabled( false );
setControlEnabled( 0 );

, which is passably readable.
 
S

Stefan Ram

Malcolm McLean said:
So you can argue about whether the third parameter is philosophically boolean
or an enumeration that happens to have two values.

This thread is about stdbool.h, so I was reading it as bool.

Philosophically, an enumeration would be better, because
it would keep the symmetry between the two possibilities.

One also might use two function names:

standard_sample_deviation( N, x )
standard_population_deviation( N, x )

. A more elaborate approach might have x be a statistical
value container (struct), which already knows whether it is
a sample or the whole population and how many entries it
contains. In this case,

standard_deviation( x )

alone would suffice.
 
M

Malcolm McLean

In Java, there is a method with the signature
»javax.swing.Timer.setLogTimers( boolean )«.



Following this example, we can use the name »setControlEnabled«:

setControlEnabled( false );
setControlEnabled( 0 );


, which is passably readable.

But you often need "propagate" flags when setting values on user interface
elements. The reason is that often you want to set a whole series of states
in response to the user or a third party disabling the control, and it's
very easy to end up in a infinite recursion if you set in response to a
set notifier.
So if you're not familiar, there's no way of knowing if
setControlEnabled(false) is one function which andles the enable/disable
case, or one of a pair of functions which takes a propagate flag.

One general problem with software engineering is that snippets seldom
illustrate real problems well. Any halfway sensible convention or interface
seems ok when you've only half a dozen lines of code to look at. The
difference etween a good design and a bad design only becomes apparent
when you're dealing with real preograms,
 
I

Ian Collins

Malcolm said:
People aren't machines.

They'll pass "true" or "false" to a function because it's already a descriptive
identifier. They'll use bool as a parameter and not provide any defined #constants because it's obvious that EnableControl(true) means enable it
and EnableControl(false) means disable it.

They won't provide #constants because C has moved on form the 80s. We
have const now.
 
K

Kaz Kylheku

I don't get this argument. It is an argument against improper interface
documentation, but not against bool.

It's an argument against boolean parameters for which constants are expectd to
be used in 90% of the calls.

Quick, what kind of event does this create in Windows:

CreateEvent(NULL, FALSE, FALSE, NULL);

The proper documentation is there; the problem is I have to go look at it.

It's tempting to make meaningful synonyms for the booleans. Trobule is there is no
type checking.

Suppose we do this:

#define EV_MANUAL_RESET TRUE
#define EV_AUTO_RESET FALSE
#define EV_INITIALY_SET TRUE
#define EV_INITIALY_RESET FALSE

We can now mix up the order, without any diagnostics from the compiler:

CreateEvent(NULL, EV_INITIALLY_SET, AV_AUTO_RESET, NULL);

Oops! So we still have to check the documentation to get the order right.

When we have ensured that, the readability improvement is undeniable, though.
 
K

Keith Thompson

Sebastian Doht said:
You should rather ask the people you are working with than a NG. I used
to work on a embedded platform with a C compiler with partial C99
support. Using bool there was quite common. But must people working on
the platform had knowledge in at least another programming language so
bool was nothing special for them. We had the innofficial rule that C99
features which are supported when switching to C++ are allowed (bool and
const where ok; VLAs were forbidden).

const is not a C99-specific feature; it was added in C89.
 
K

Keith Thompson

Jorgen Grahn said:
We used to, but for the past fifteen years we have also had bool.

Seems to me C is now in the same place as C++: booleanness is still
based on int, but when you need to store a boolean value you have the
option to give it the type bool.

(After having written that, I'm suddenly unsure whether 1==2 has the
type int or bool in C++ ... the difference is minor.)

In C++, `1==2` has type bool. In C, it has type int. C++'s bool type
is much more tightly integrated into the language than C's _Bool.
 
K

Keith Thompson

Ian Collins said:
Malcolm McLean wrote: [...]
People aren't machines.

They'll pass "true" or "false" to a function because it's already a
descriptive identifier. They'll use bool as a parameter and not
provide any defined #constants because it's obvious that
EnableControl(true) means enable it and EnableControl(false) means
disable it.

They won't provide #constants because C has moved on form the 80s. We
have const now.

More to the point, we have enum.
 
E

Eric Sosman

Ian Collins said:
Malcolm McLean wrote: [...]
People aren't machines.

They'll pass "true" or "false" to a function because it's already a
descriptive identifier. They'll use bool as a parameter and not
provide any defined #constants because it's obvious that
EnableControl(true) means enable it and EnableControl(false) means
disable it.

They won't provide #constants because C has moved on form the 80s. We
have const now.

More to the point, we have enum.

... which I once saw used (in the pre-C99 days) as

typedef enum { TRUE, FALSE } Boolean;

There was much teething of gnash and hairing of tear when
this little earth was ungemmed.
 
J

James Kuyper

On 03/02/2014 06:35 AM, Jorgen Grahn wrote:
....
Seems to me C is now in the same place as C++: booleanness is still
based on int, but when you need to store a boolean value you have the
option to give it the type bool.

You make it sound like _Bool could simply be another name for 'int', or
at least for a type that is handled internally the same way that 'int',
but that's not the case, for three reasons:

* sizeof(_Bool) is not required to match sizeof(int).

* _Bool has a lower integer conversion rank than an other integer type.

* Conversion of a value x of any scalar type to _Bool produces a result
that's 0 if x compares equal to 0, and otherwise produces a result of 1.

* It's not a constraint violation for a _Bool to be the left operand of
an assignment statement when the right operand has pointer type.
 
B

Ben Bacarisse

Malcolm McLean said:
We could reasonably do this

#define CONTROL_DISABLE 123
#define CONTROL_ENABLE 42

/* pass CONTROL_DISABLE or CONTROL_ENABLE */
void EnableControl(int action);

Now very few programmers would be stupid enough to pass a raw 42.

Right, but I note in passing that the function names are wrong (or maybe
just bad).
However if we declare the function as taking a bool, the same programmer
might well pass true and false. When you see
EnableControl(false)
or worse
EnableControl(0);
in code you're debugging, then that might well be confusing.

That's pretty much what you said before. What I don't get is the point
you are making. If the function name makes sense with a bool argument
(as in your last example) then what's the problem with passing a
literal? If the function names don't convey what they should then you
simply have a bad API. I don't see any problem that has anything to do
with bool.
 
D

David Brown

It's an argument against boolean parameters for which constants are expectd to
be used in 90% of the calls.

Quick, what kind of event does this create in Windows:

CreateEvent(NULL, FALSE, FALSE, NULL);

The proper documentation is there; the problem is I have to go look at it.

It's tempting to make meaningful synonyms for the booleans. Trobule is there is no
type checking.

Suppose we do this:

#define EV_MANUAL_RESET TRUE
#define EV_AUTO_RESET FALSE
#define EV_INITIALY_SET TRUE
#define EV_INITIALY_RESET FALSE

We can now mix up the order, without any diagnostics from the compiler:

CreateEvent(NULL, EV_INITIALLY_SET, AV_AUTO_RESET, NULL);

Oops! So we still have to check the documentation to get the order right.

When we have ensured that, the readability improvement is undeniable, though.

That is an argument for why C could really benefit from named parameters
to functions, so that we could write:

CreateEvent(.lpEventAttributes = NULL, .bManualReset = true,
..bInitialState = false, .lpName = NULL);
 
D

David Brown

You can get that syntax already

typedef struct {
void *lpEventAttributes;
bool bManualReset;
bool InitialState;
char *lpName;
} CreateEventArgs;

#define CreateEvent(...) CreateEvent(&(CreateEventArgs){ 0, __VA_ARGS__ })

void *(CreateEvent)(const CreateEventArgs *);

You /can/ do this sort of thing - but it is an ugly mess and cannot be
mixed with normal function syntax. My wish would be that given the
normal declaration of a function, you could then use named parameters as
an when you want, and mix them with positional ones. As long as the
syntax does not conflict with existing C syntax (that's why I suggest
putting a "." before the parameter name), I believe it would be simple
syntactic sugar. I also think it would work in C++ too (although
complications like templates, default arguments, overloading, etc.,
would mean there is more to check). There certainly seems to be no
problem with them in other languages (Python, C#, etc.).
I sometimes use this trick for iterators, where usage looks like

foo_foreach(&item, &collection, .bar = 42, .baz = "acme") {
...
}

I'm sure language help could make the declaration more concise, but not by
that much. A routine taking a large set of arguments is already ugly and
unwieldly. Arguably breaking the arguments out into a separate struct
definition is an improvement.

Even for a function with only a few arguments, named arguments can make
the code clearer. And having them as a language features makes them
zero cost (in run-time, code time, and in source code size and effort) -
you use them if you want to.
 
J

Jorgen Grahn

On 03/02/2014 06:35 AM, Jorgen Grahn wrote:
...

You make it sound like _Bool could simply be another name for 'int', or
at least for a type that is handled internally the same way that 'int',

That wasn't my intention. I meant something more like "moving to
_Bool isn't a radical step".
but that's not the case, for three reasons:

* sizeof(_Bool) is not required to match sizeof(int).

* _Bool has a lower integer conversion rank than an other integer type.

* Conversion of a value x of any scalar type to _Bool produces a result
that's 0 if x compares equal to 0, and otherwise produces a result of 1.

That's one of the featues I'm after. Last year I helped create
this bug:

our_bool_t foo = (bar & MASK);

our_bool_t was an unsigned char, and MASK had only higher bits set.
Oops! Fortunately it was quickly found in testing.
* It's not a constraint violation for a _Bool to be the left operand of
an assignment statement when the right operand has pointer type.

/Jorgen
 
M

Malcolm McLean

That's pretty much what you said before. What I don't get is the point
you are making. If the function name makes sense with a bool argument
(as in your last example) then what's the problem with passing a
literal? If the function names don't convey what they should then you
simply have a bad API. I don't see any problem that has anything to do
with bool.
You're assuming that we can find programmers with English literature degrees
who understand clear communication, and also of course technical degrees
so they understand the mathematical side of what they are doing.
You can maybe get the odd such person. But, generally, a name like
EnableControl() is meaningful to the person writing the function. Enabling
shares substantial logic with disabling, so it's easier to provide the same
function. And we've only got one control.
But to the maintaining programmer

EnableControl(0);

might mean "enable control with index zero". He knows we've
only got one control. Fair enough, that's in for expansion.

I chose EnableControl() because it's a common real example. Plenty of APIs
have it.

With software engineering, the trivial is important. It's not so much than
any one feature is unacceptably bad. It's the interaction of such features.

Consider this.

SetControlState(CONTROL_DISABLED);

acceptable, but a bit confusing, because you can't pass anything except
CONTROL_ENABLED or CONTROL_DISABLED.

Now

SetControlState(true);

the reader is completely in the dark. The bool has interacted with a poor
choice of name to make something very confusing.
 

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
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top