defining a boolean type

K

Keith Thompson

Please stop adding "Followup-To: Poster" to your article headers.

Ctalk Project said:
Keith Thompson said:
Ctalk Project said:
<snip>
Probably not what the OP wanted, though what usually works for me is

#define FALSE 0
#define TRUE !(FALSE)

You probably meant (!FALSE). Examples where your definition of TRUE
don't work as expected are a little contrived but they do exist.

Example please.... The parens should not be necessary at all
except that there might also be a contrived case where 0
expands to an expression.

And the ! operator has a higher precedence than &&, ||, ~,
<<, >>, and so forth with the other operators, so that
should rule out mis-evaluation because of grouping of
adjacent operators.

Quibble: ~ has the same precedence as !. But the indexing operator
binds more tightly than !.

Here's a highly contrived example:

#include <stdio.h>

#define FALSE 0
#ifdef DO_IT_RIGHT
#define TRUE (!FALSE)
#else
#define TRUE !(FALSE)
#endif

int main(void)
{
char c = TRUE["hello"];
printf("c = '%c' = %d\n", c, c);
return 0;
}

I find it much easier to cultivate the habit of *always*
parenthesizing each macro argument and the entire definition than
to figure out when it's not necessary. If nothing else, anyone
reading your code could waste substantial time verifying that
there's no problem. By contrast, if you wrote:

#define FALSE 0
#define TRUE (!FALSE)

it would be obvious to any knowledgable C programmer that there won't
be any precedence problems. (Parentheses around FALSE are unnecessary
since it's a single token, not a macro argument -- assuming that FALSE
was defined properly, as it was here.)

Of course this:

#define TRUE 1

is equivalent, simpler, and clearer.

The definition of TRUE here is being used as an lvalue
label, not as a constant expression to initialize a typedef.
(Pardon me if I'm not checking the jargon against any
standards documents, I'm typing this as I go).

Please do check your jargon. I honestly can't understand what you're
trying to say.

None of the macro definitions for FALSE and TRUE have anything
to do with lvalues, nor is there any reason why they should.
(An lvalue is, more or less, an expression that designates an
object; all we have here are constants and constant expressions.)
And "to initialize a typedef" doesn't make any sense. What
exactly do you mean?
I'd add that due to the fact that anything "not 0" -> True,
the further example is still a little iffy, but "good
enough" for most cases. However, to use a compiler's
checking, a Boolean would have to be defined using an enum,
and that could be expensive.

Huh?

Using an enum doesn't provide any type checking. Enum values can be
freely and implicitly converted to and from integer types. In fact,
enumeration constants are of type int, not of the enumerated type.
I did think of an example, btw, when using a typedef'd
variable as an lvalue, instead of a constant. Then one
could say, for example, ++TRUE or --TRUE after the macro
expansion and the program could end up with a false True.

So don't do that. Why would you even consider it?
In fact, it might be better to do away with boolean types
entirely, and rely on macros that are constant expressions,
except that boolean types make source code so darn readable.

You can have macros that expand to constant expressions either
with or without a boolean type. In fact, that's exactly what C99's
<stdbool.h> does. "bool" is a macro that expands to "_Bool", which
is a predefined type. "false" and "true" are macros that expand
to 0 and 1, respectively, which are of type int. The implicit
conversions between int and _Bool mean that the differing types
aren't a problem unless you do something really strange (like
testing whether sizeof (bool) == sizeof true).
 
K

Keith Thompson

Ctalk Project said:
Ben Bacarisse said:
Ctalk Project said:
<snip>
Probably not what the OP wanted, though what usually works for me is

#define FALSE 0
#define TRUE !(FALSE)

You probably meant (!FALSE). Examples where your definition of TRUE
don't work as expected are a little contrived but they do exist.

Example please....

const char *names[] = { "false", "true" };
puts(TRUE[names]);

As I said... contrived.
The parens should not be necessary at all
except that there might also be a contrived case where 0
expands to an expression.

The () round FALSE and not needed, no. I don't see how 0 can expand
to anything.
And the ! operator has a higher precedence than &&, ||, ~,
<<, >>, and so forth with the other operators, so that
should rule out mis-evaluation because of grouping of
adjacent operators.

But postfix operators bind like [] bind tighter. The point being it
is almost always better just to bracket the result (as well as any
embedded arguments) than to try to reason about what is and is not
safe. I don't do it for integer literals, but that is because that
case is reasonably easy to reason about.

It's certainly possible to use the definitions for variable
names, but then the definitions don't mean TRUE or FALSE,
then they're variable names.

What? Who said anything about variable names?

Given:

#define FALSE 0
#define TRUE (!FALSE)

What variable are you seeing that I'm not?
 
K

Kaz Kylheku

Example please.... The parens should not be necessary at all

Yes, the /wrong/ parens you have are not necessary (and should not be). The
reason is that FALSE is a public macro that should be properly defined (and
is). If FALSE is defined such that parentheses are necessary, then it is
improperly defined.

Why are you looking for an excuse not to properly parenthesize an expression
that is produced by macro expansion?

Even if the situations where is wrong are contrived and unlikely, the fact
still remains that the macro definition /looks/ wrong. Someone else reading the
code will be tripped up by this, and wonder whether there is a problem
somewhere because of it.

In programs (production programs, not submissions to the obfuscated
C programming contest), you want wrong code to look wrong, and right code to
look right. Wrong-looking code that isn't wrong is a false positive that
wastes people's time.

See, that is why we have conventions. There are all kinds of ways to make right
code look wrong. You can write C without ever using unnecessary parentheses.
Or without using indentation (or any unnecessary whitespace at all).
 
B

Ben Bacarisse

Ctalk Project said:
Ben Bacarisse said:
Ctalk Project said:
<snip>
Probably not what the OP wanted, though what usually works for me is

#define FALSE 0
#define TRUE !(FALSE)

You probably meant (!FALSE). Examples where your definition of TRUE
don't work as expected are a little contrived but they do exist.

Example please....

const char *names[] = { "false", "true" };
puts(TRUE[names]);
But postfix operators bind like [] bind tighter. The point being it
is almost always better just to bracket the result (as well as any
embedded arguments) than to try to reason about what is and is not
safe. I don't do it for integer literals, but that is because that
case is reasonably easy to reason about.

It's certainly possible to use the definitions for variable
names, but then the definitions don't mean TRUE or FALSE,
then they're variable names.

I think you've missed the point of the example you asked for.
!(FALSE) is not safe if it is followed by an operator that binds more
tightly than !. As I (and I now see, Keith) have pointed out, the
index operator [] is an example.
Per my other reply, using constant expressions as
initializers are, "good enough," though any seriously out of
whack program could invalidate a boolean type. ++TRUE or
--TRUE could cause you to end up with a False -> TRUE or
True -> FALSE.

An enum initializer would allow the compiler to check, but
that could become equally specious and contrived.
It could turn also out to be expensive because then a complete
program would need a booleanThatIsACorrectEnum type and also
a booleanThatIsGoodEnoughAndCompatible type.

I don't follow either of these paragraphs so I don't feel I can just
snip them.
 
F

Frank

Keith Thompson<[email protected]> writes:

[Keith references §7.2 of the standard]
[new subject, new OP]
And, to state the obvious, it won't affect any 'assert's that have been
through the pre-processor (phase) but will affect that have still to be
seen. So part of your code can have 'assert's turned on, and part have
them turned off, without any problems.

Thanks for the obvious comment, Nick. It seems obvious to me, now that
you've said it. My question to Keith regarding the exception that
assert.h represents to the other standard headers with respect to
idempotence was: why?

Let me see if I understand you correctly, though. If I have c1.c and
c2.c, I could compile them separately to become, say c1.o and c2.o. The
preprocesser runs both times. After it evaluates all macros, it is
ready to compile. Whether NDEBUG is defined at this point determines
whether the assert asserts anything, otherwise the expression won't even
be evaluated.

I have a couple questions and wanted to use p 454 H&S as referent source:

#include <assert.h>
int f(int x)
{
assert(x>0 && x<10);
...
}

What would this assert be required to tell me, and how would I handle
these events?

p. 453:
The diagnostic message will include the text of the argument, the name
(__FILE__), and the line number (__LINE__). C99 implementations can also
use the function name (__func__).

At a minimum, I need to get x>0 && x<10. Anyways, my fingers are going
to go solo unless I stop typing. Cheers,
 
M

Moi

[ignoring "Followup-To: Poster"]

#define FALSE 0
#define TRUE (!FALSE)

it would be obvious to any knowledgable C programmer that there won't be
any precedence problems. (Parentheses around FALSE are unnecessary
since it's a single token, not a macro argument -- assuming that FALSE
was defined properly, as it was here.)

Just to be on the safe side, you could also prefer

#define FALSE (0)

;-)
AvK
 
N

Nick Keighley

it might have just been easier to agree here...

  const char *names[] = { "false", "true" };
  puts(TRUE[names]);
As I said... contrived.

0 *is* an expression. How can it "expand"?
The () round FALSE and not needed, no.  I don't see how 0 can expand
to anything.
But postfix operators bind like [] bind tighter.  The point being it
is almost always better just to bracket the result (as well as any
embedded arguments) than to try to reason about what is and is not
safe.  I don't do it for integer literals, but that is because that
case is reasonably easy to reason about.

sounds a good rule

It's certainly possible to use the definitions for variable
names, but then the definitions don't mean TRUE or FALSE,
then they're variable names.

what? Could you post that again in english? How can a definition be a
variable name? What definitions don't mean TRUE or FALSE? How can a
definition mean something? "...then they're variable names"??? what?

Per my other reply, using constant expressions as
initializers are, "good enough,"

when were there any initialisers in any of this?

though any seriously out of
whack program could invalidate a boolean type.  ++TRUE or
--TRUE could cause you to end up with a False -> TRUE or
True -> FALSE.  
what?


An enum initializer

a what?
would allow the compiler to check, but
that could become equally specious and contrived.
?

It could turn also out to be expensive because then a complete
program would need a booleanThatIsACorrectEnum type and also
a booleanThatIsGoodEnoughAndCompatible type.

??

???
 
N

Nick Keighley

Ctalk Project wrote:

Can you please not set the Follow-up to header to "Poster", it makes
replying to your post a pain. Always assuming it was not corrupted at my
end, that is!

the same "corruption" happened to me. I was wondering if I'd typed in
the wrong window!
 
K

Keith Thompson

Frank said:
Keith Thompson<[email protected]> writes:

[Keith references §7.2 of the standard]
[new subject, new OP]
And, to state the obvious, it won't affect any 'assert's that have been
through the pre-processor (phase) but will affect that have still to be
seen. So part of your code can have 'assert's turned on, and part have
them turned off, without any problems.

Thanks for the obvious comment, Nick. It seems obvious to me, now
that you've said it. My question to Keith regarding the exception
that assert.h represents to the other standard headers with respect to
idempotence was: why?

Every standard header other than <assert.h> defines a fixed set of
functionality, regardless of what macros are or are not defined
when the header is #included.

The functionality defined by <assert.h> is very different depending on
whether NDEBUG has been defined or not.

#include <assert.h> with NDEBUG defined does one thing; #include
<assert.h> with NDEBUG undefined does another thing. So <assert.h>
*can't* be idempotent. (Well, it could, if it defined some hidden
symbol that affected the behavior of later includes, but there's no
good reason to do that.)

It's the way it is because it's useful.
Let me see if I understand you correctly, though. If I have c1.c and
c2.c, I could compile them separately to become, say c1.o and c2.o.
The preprocesser runs both times. After it evaluates all macros, it
is ready to compile. Whether NDEBUG is defined at this point
determines whether the assert asserts anything, otherwise the
expression won't even be evaluated.

I have a couple questions and wanted to use p 454 H&S as referent source:

#include <assert.h>
int f(int x)
{
assert(x>0 && x<10);
...
}

What would this assert be required to tell me, and how would I handle
these events?

p. 453:
The diagnostic message will include the text of the argument, the name
(__FILE__), and the line number (__LINE__). C99 implementations can
also use the function name (__func__).

Didn't you just answer your own question?

As for handling it, you can't. A triggered assert() aborts the
program. If you want to handle an error, don't use assert.
 
R

Richard Bos

Scott said:
bs.nethttp://www.seebs.net/log/<-- lawsuits, religion, and funny picturesht=
tp://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!

I agree style matters but I also think that your style is no more
right or wrong than someone else's style. The important thing in
style, in my opinion, is to be consistent.

Well, then you're simply incorrect. Your style of quoting Usenet
articles, for example, is demonstrably inferior to the more conventional
one, in that you split quoted lines and don't snip anything, not even
sigs.
Similarly, if one style of writing some C code is not only less common,
but also generally considered less clear than another, that style can
certainly be called wrong. In this light,

#define FALSE 0
#define TRUE 1

is definitely right, and

#define FALSE (0==1)
#define TRUE (1==1)

is definitely wrong. Not _very_ wrong, granted; not _as_ wrong as

#define FALSE ('-'-'-')
#define TRUE (!!!!!!!!1)

would be; but still wrong.

There are, it is true, cases where there are several reasonable style,
and in those cases the best thing is indeed to choose one, be
consistent, and not be surprised when someone else chooses another. For
example, there are several brace styles. Personally, I prefer K&R style,
so I always write

if (x) {
do_this();
do_that();
}

but Allman style, that is,

if (x)
{
do_this();
do_that();
}

I can't definitely call wrong, either. However, even in this matter it
can definitely be said that GNU style is inferior to both, and if you
write this:

if (x)
{ do_this();
do_that();
}

you are undeniably wrong, wrong, completely and utterly wrong. To state
that this is merely a matter of taste is ludicrous.

Richard
 
R

Richard Bos

Francis Glassborow said:
well I prefer manifest (i.e.named) constants but those are a little more
awkward in C than in C++.

Erm? In C, it is as horribly awkward as

#define SECS_PER_DAY (24*60*60)

or

#define SECS_PER_DAY 86400

If there is an even simpler method in C++, I'd love to know what it is.

Richard
 
K

Keith Thompson

Erm? In C, it is as horribly awkward as

#define SECS_PER_DAY (24*60*60)

or

#define SECS_PER_DAY 86400

If there is an even simpler method in C++, I'd love to know what it is.

const long int SECS_PER_DAY = 86400;

In C++, unlike in C, SECS_PER_DAY is actually a constant.

If you could be sure that 86400 will fit in an int, you could do:

enum { SECS_PER_DAY = 86400 };

in either C or C++.
 
S

Scott

Well, then you're simply incorrect. Your style of quoting Usenet
articles, for example, is demonstrably inferior to the more conventional
one, in that you split quoted lines and don't snip anything, not even
sigs.
Similarly, if one style of writing some C code is not only less common,
but also generally considered less clear than another, that style can
certainly be called wrong. In this light,

  #define FALSE 0
  #define TRUE  1

is definitely right, and

  #define FALSE (0==1)
  #define TRUE (1==1)

is definitely wrong. Not _very_ wrong, granted; not _as_ wrong as

  #define FALSE ('-'-'-')
  #define TRUE (!!!!!!!!1)

would be; but still wrong.

There are, it is true, cases where there are several reasonable style,
and in those cases the best thing is indeed to choose one, be
consistent, and not be surprised when someone else chooses another. For
example, there are several brace styles. Personally, I prefer K&R style,
so I always write

  if (x) {
    do_this();
    do_that();
  }

but Allman style, that is,

  if (x)
  {
    do_this();
    do_that();
  }

I can't definitely call wrong, either. However, even in this matter it
can definitely be said that GNU style is inferior to both, and if you
write this:

                 if (x)
   {        do_this();
            do_that();
                                        }

you are undeniably wrong, wrong, completely and utterly wrong. To state
that this is merely a matter of taste is ludicrous.

I was not suggesting that tis is something acceptable but as you said
what I had is not "very wrong" and I would argue that most anything
that you would consider "not very wrong" indeed falls under that
category of personal preference. I was not attempting to make a
blanket statement although I was, unfortunately, not very clear on
that. This long back and forth was actually why I stated up front that
I really was not looking for input on that part, next time I will
leave it out if I don't want input on it. I am not suggesting that I
don't want to hear others opinions on it just that, as I said, I was
already aware of the various felling on this particular issue.
 
P

Phil Carmody

Similarly, if one style of writing some C code is not only less common,
but also generally considered less clear than another, that style can
certainly be called wrong. In this light,

#define FALSE 0
#define TRUE 1

is definitely right, and

#define FALSE (0==1)
#define TRUE (1==1)

is definitely wrong. Not _very_ wrong, granted; not _as_ wrong as

#define FALSE ('-'-'-')
#define TRUE (!!!!!!!!1)

would be; but still wrong.

Remember, Richard - always program defensively. It's possible to
miscount the number of '!' symbols and get the parity wrong, so
the defensive and therefore less wrong version would cover both
parities just in case:

#define TRUE (!!1||!!!1)

[SNIP - illustrative example of ugly]
you are undeniably wrong, wrong, completely and utterly wrong. To state
that this is merely a matter of taste is ludicrous.

Hmmm, I was found last night swigging both ordinary tabasco and
tabasco habanero straight from the bottles in order to decide which
one tasted better.

I know someone whose real-world coding style preference is _way_
uglier than your example.

Phil
 
F

Frank

Didn't you just answer your own question?

As for handling it, you can't. A triggered assert() aborts the
program. If you want to handle an error, don't use assert.

I see. Thx.
 
J

James Dow Allen

Remember, Richard - always program defensively. It's possible to
miscount the number of '!' symbols and get the parity wrong, so
the defensive and therefore less wrong version would cover both
parities just in case:

#define TRUE (!!1||!!!1)

If we're miscounting '!', how do we know those in the above
expressions
aren't *both* odd-numbered?

The proper way to code TruE and FalsE, which assumes ONLY that
we can count correctly to 1, is

#define FalsE (sizeof (char) != TRUE)
#define MaybeTRUE ( !!!!!!!!!!!!! FalsE )
#define TruE ( MaybeTRUE || ! MaybeTRUE || exit(! FALSE))
Note that these macros solve other problems as well.
They avoid possible name conflict with TRUE, True and true.
Also, the macro invocation will exit quickly if, for any reason,
TruE isn't, well, tRUe.

On the other hand, perhaps I'm not qualified to comment here, since
in 30 years of C programming I'm not sure I've *ever* used TRUE
or FALSE with or without capitalization.

Same to you, buddy!

James Dow Allen
 
J

James Dow Allen

.
This looked like just what I needed for my work, but
I can't compile it:
TruE_FalsE_test_scaffold.c:10982: error: void value not ignored as it ought to be
I tried it in both 32- and 64-bit modes.
I was born in New York City, if that helps you any.

Oops. The easy fix is to substitute ExiT, defined via

int ExiT(int foo)
{
exit(foo);
/* NOTREACHED */
return !!foo != !foo;
}

Hope this helps.
Sheez! I was only kidding.

Well, then use a smiley-face next time, please.

James Dow Allen
 
R

Richard Bos

Keith Thompson said:
const long int SECS_PER_DAY = 86400;

I don't see how that is less awkward. I see how you might prefer it in
certain specific circumstances - e.g., if you want to pass its address
to a function - but I don't see how a #define is more awkward, per se.

Richard
 
K

Keith Thompson

I don't see how that is less awkward. I see how you might prefer it in
certain specific circumstances - e.g., if you want to pass its address
to a function - but I don't see how a #define is more awkward, per se.

A declared constant object is, in some sense, cleaner than a macro.
(Yes, that's vague.) It follows scoping rules rather than being
visible to the end of the translation unit. Depending on the
implementation, it might be visible in the debugger whereas a macro
definition might not be. You can directly specify its type.

On the other hand, the macro expands to the constant 86400, which is
automatically of type int or long int, whichever one is big enough.
(Whereas (24*60*60) is always of type int, and will overflow if
INT_MAX==32767.) The rules for determining the type of a literal
constant depending on its value are handy sometimes; it's too bad
there's no corresponding mechanism for other constant expressions.
 
R

Richard Bos

Keith Thompson said:
A declared constant object is, in some sense, cleaner than a macro.
(Yes, that's vague.) It follows scoping rules rather than being
visible to the end of the translation unit. Depending on the
implementation, it might be visible in the debugger whereas a macro
definition might not be. You can directly specify its type.

On the other hand, the macro expands to the constant 86400, which is
automatically of type int or long int, whichever one is big enough.
(Whereas (24*60*60) is always of type int, and will overflow if
INT_MAX==32767.) The rules for determining the type of a literal
constant depending on its value are handy sometimes; it's too bad
there's no corresponding mechanism for other constant expressions.

Also, the macro is visible from the place where it was #defined, so you
need not worry about someone accidentally masking it - that would need
an intentional #undef.

I don't see how anything here contradicts my assertion that either
option is more useful in some situations, but neither can be called more
awkward /sec/.

Richard
 

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,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top