#ifndef BOOL_H
#define BOOL_H
#if (__STDC__)
#if (__STDC_VERSION__ < 199901L)
typedef enum boolean
{
FALSE = (1==0),
TRUE = (1==1)
}bool;
#else
#include <stdbool.h>
#endif
#endif
#endif /* BOOL_H */
The main problem is that code that uses this can't use the same
names. You should probably define true and false rater than TRUE and
FALSE.
I am also a little curious as to why there will be no bool type at all
unless __STDC__ is defined though that could be just I don't know how
you plan to use this.
--
Ben.
Hmm.
> I am asking mainly about the #if protections not
> the enum itself as I am aware there are other ways to do that.
Yeah, but those are pretty significant too.
> {
> FALSE = (1==0),
> TRUE = (1==1)
No, no, no, no, no. No. No.
You are nominally trying to provide a type compatible with <stdbool.h>.
However:
1. C99 used "false" and "true".
2. The specified values are "0" and "1". Use those.
"(1==0)" is a bad enough way to express 0 that I would become instantly
distrustful of all future code I saw that I thought came from the same
source.
Try:
enum { false, true } bool;
It'll actually be compatible (nearly) with what C99 used. (It won't have
the trait that assigning arbitrary non-zero values into it yields true,
though.)
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
Good point on the __STDC__ thing. I guess I was thinking that
__STDC_VERSION__ would not be defined unless __STDC__ was thus the
nesting but unless I am mistaken if __STDC_VERSION__ is not defined
the #if (__STDC_VERSION__ < 199901L) line would result in something
like #if (0 < 199901L). I probably should just remove that check.
> It'll actually be compatible (nearly) with what C99 used. (It won't have
> the trait that assigning arbitrary non-zero values into it yields true,
> though.)
>
> -s
> --
> Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nos...@seebs.nethttp://www.seebs.net/log/<-- lawsuits, religion, and funny pictureshttp://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
You just declared a variable named "bool".
--
Keith Thompson (The_Other_Keith) ks...@mib.org <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"
> The intent is not to use "(1==0)" as a means to express the value 0 it
> is a means to express the value FALSE, I don't really see how the fact
> that it evaluates to 0 makes any difference.
Very simple:
The C99 bool value false is 0. Not "the value FALSE". 0. So you should
be providing the SAME value or you'll have some weird drift between versions.
> I would say this is
> really just a stylistic difference and as I said I am aware that these
> types of arguments exist on both sides of the issue which is why I
> specified I was not asking about this.
Ahh, but whether or not you asked about it, you should be aware that
doing stuff like that will make any future reader familiar with C assume
that your code is unreliable. It's just plain wrong. There is nothing
more-false-than-0 in C.
> Stylistically you prefer this but mechanically it is identical in
> every way to that written above aside from the capitalization which I
> agree should be changed.
Style matters. In particular, given that you agree that it's mechanically
equivalent, why on earth are you specifying something in a more complicated
way rather than a simpler way?
Why don't you go a bit further and write:
TRUE = (((1!=0)!=(1!=1))!=0)
?
Because it's wrong, for the same reason that what you have is wrong. Write
for clarity. If you are 100% sure that (1==0) is 0, just write 0. If you
aren't 100% sure of it, you should not be writing header files that other
programmers are supposed to use as utilities or wrappers to export basic
functionality.
-s
--
That grown men can imagine C code like this
to be purposeful staggers my imagination.
Help this helps.
James
... Wow.
There is probably a missing typedef in here somewhere. Uhm. I, er, left
it as an exercise to the reader.
There is probably good grant money to be had exploring the connection between
level of caffeine in the bloodstream and frequency with which things are
retroactively left as an exercise to the reader.
Here's how I'd do it (omitting the header guards):
#if __STDC_VERSION__ < 199901L
typedef enum { false, true } bool;
#else
#include {stdbool.h>
#endif
I might write "false=0, true=1" for emphasis, but it's really not
necessary. I wouldn't have a tag on the enum type; you're never going
to refer to the type as "enum boolean", just as "bool".
As a matter of style, I find the use of (1==0) for false and (1==1)
for true to be just obfuscating. 0 is false, 1 is true, and that will
never change in any language called "C".
Also as a matter of style, none of the parentheses in your code are
necessary, and in my opinion none of them are helpful.
See also section 9 of the comp.lang.c FAQ, <http://www.c-faq.com/>.
(It's best not to quote sigs.)
> Good point on the __STDC__ thing. I guess I was thinking that
> __STDC_VERSION__ would not be defined unless __STDC__ was thus the
> nesting but unless I am mistaken if __STDC_VERSION__ is not defined
> the #if (__STDC_VERSION__ < 199901L) line would result in something
> like #if (0 < 199901L). I probably should just remove that check.
The __STDC__ test does not help since between C90 and C95 __STDC__
was defined but __STDC_VERSION__ was not. Anyway, it doesn't
matter since you are right that undefined names get replaced by 0 in
#if lines.
--
Ben.
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. Your stylistic preferences
are formed in large part based on your way of thinking, i.e. the way
you view the world. This is an individual thing and what seems to you
to make the most sense may not make as much sense to someone else
simply because of their world view, that does not make one of them
right and one of them wrong. In this case it seems to me that your
argument makes sense but it also seems to me that someone who says I
don't care that the value is 0 I care that the meaning is false and
(x==y) conveys false to them in a more clear manner than 0 does is
also a reasonable view. Furthermore if they know that it results in
the same thing it is simply stylistic and in my opinion anything that
is stylistic only is not right or wrong only different. Where we part
ways is in the thinking that if someone does not agree with your
stylistic view then everything else they do warrants no more
consideration, seems like an awfully small minded way of thinking to
me. I would agree with you that you could get carried away with this
as you mentioned with TRUE = (((1!=0)!=(1!=1))!=0) but that is not
really what we are talking about.
Thanks. I had read the FAQ prior to posting but was just wondering
about the protections I had around that. What I have at this point
looks very close to what you have above.
That is a moderately testable claim.
The key thing is that code is intended to communicate. You have to think
about what the code communicates.
There is a reason that, when we have indentation wars, you will
rarely see someone advocate for:
if (x)
{ if (y) {
z;}}
> In this case it seems to me that your
> argument makes sense but it also seems to me that someone who says I
> don't care that the value is 0 I care that the meaning is false and
> (x==y) conveys false to them in a more clear manner than 0 does is
> also a reasonable view.
Not in C.
And that's the thing -- we're not working in a pure void, we're working
in an existing language with established conventions and patterns. And
in that language, since everyone knows what the boolean operators yield,
anyone who writes something like this is indirectly communicating that
they don't understand the boolean operators.
> Furthermore if they know that it results in
> the same thing it is simply stylistic and in my opinion anything that
> is stylistic only is not right or wrong only different.
Then your opinion is wrong. See the indentation example above.
There are certainly cases where there's room to assert that a matter of
style is purely a matter of taste, but there are plenty of ways you can
make something which is purely stylistic (as in, the same code will
be generated), where one is clearly superior to another in terms of
long-term maintenance and use of code.
> Where we part
> ways is in the thinking that if someone does not agree with your
> stylistic view then everything else they do warrants no more
> consideration, seems like an awfully small minded way of thinking to
> me.
Not "no more consideration". Merely that, if a particular choice is
sufficiently unreasonable, that warns me that either they don't understand
the language (in which case I need to exercise additional caution in
dealing with their code), or they are prone to making poor decisions
(in which case I need to exercise additional caution in dealing with their
code).
Some kinds of errors or style choices make me more concerned than others.
If I read a block of sixty or so variable declarations which are in no
particular order, of various types for no obvious reason, and with a bunch
of names which are nonsensical or suggest contradictory nomenclatures, I
know *already* that I'm about to read some bad code. Knowing that is useful.
There's a similar pattern; if you see a car with a bashed-in fender, you
can often predict that it will move erratically in ways which, if you are
not taking care on that driver's behalf, would cause you to bash in that
fender. If you have evidence of poor style, being cautious is generally
rewarding.
> I would agree with you that you could get carried away with this
> as you mentioned with TRUE = (((1!=0)!=(1!=1))!=0) but that is not
> really what we are talking about.
How is it different?
-s
--
So someone has good style if their style is consistently bad?
--
John W. Temples, III
Define bad. Your definition will clearly be different from mine and
that is my point.
The point is that consistency is not the *only* important thing about
style.
If I consistently put my opening braces in column 82 and my closing
braces in column 137, that's bad style, and it's bad in a very
important way.
Oops. :-)
--
frank
Indeed. Thanks.
[changing the subject somewhat]
>>> #include {stdbool.h>
>>
>> Oops. :-)
>
> Indeed. Thanks.
>
I had a question for you. Plauger writes that header inclusion is
idempotent. Is it possible to have:
#undef NDEBUG
#include <assert.h>
and somewhere else NDEBUG *is* defined while assert.h is included, such
that they collide?
--
frank
Header inclusion is idempotent for the standard headers, *except* for
<assert.h>. See C99 7.1.2p1:
Standard headers may be included in any order; each may
be included more than once in a given scope, with no effect
different from being included only once, except that the effect
of including <assert.h> depends on the definition of NDEBUG
(see 7.2).
#including <assert.h> multiple times won't cause a collision.
C99 7.2p1:
The assert macro is redefined according to the current state of
NDEBUG each time that <assert.h> is included.
This is an absolutely valid warning, regardless of the actual
reliability of the code.
> Why don't you go a bit further and write:
>
> TRUE = (((1!=0)!=(1!=1))!=0)
Because, as I'm guessing you'd agree, (1==0) is clearly "false", while
(((1!=0)!=(1!=1))!=0) is clearly "insane". :)
> If you are 100% sure that (1==0) is 0, just write 0.
All in the name of clarity, I say. If you found a situation where
saying (1==0) would clarify the code or improve its readability, I'll
bet you'd write it. I don't think the OP is in one of those situations,
though--"FALSE=0" has pretty unambiguous meaning.
But I've written things like "<<0" in code before where I thought it
improved clarity for future maintainers. Or I've left in unsimplified
constant arithmetic expressions where it would help explain the thinking
behind it.
And while I personally wouldn't define FALSE as (1==0) in my code, I
don't think it's nearly as dire a monstrosity as (((1!=0)!=(1!=1))!=0),
nor is it as bad as horribly misplaced squirrely braces at column 97 or
whatever.
But the OP should define FALSE as 0 for some or all of these reasons:
1) C clearly defines it that way
2) All C coders know it
3) Defining it as (1==0) doesn't particularly improve clarity
4) People might harshly judge his code
That really should be enough--it doesn't have to get into a weird
subjective style fight with extreme fabricated coding examples.
-Beej
Probably not what the OP wanted, though what usually works for me is
#define FALSE 0
#define TRUE !(FALSE)
...
<then typedefs, probably ints>
...
though practically, the declarations need to be the following
typedef char Boolean;
#define True 1
#define False 0
for compatibility with Xlib definitions.
Defining horrendous gratuitous macros like those above, btw,
almost always seems to guarantee, believe it or not, that
somewhere, something'll eventually be out of order.
Cheers
--
Ctalk Home Page: http://www.ctalklang.org
Strange, I always thought FALSE was 0 and TRUE was anything else...
Ham
Anything other than 0 is true(adjective), but true(noun) is 1.
You can only define a single value as TRUE (or true). The most
obvious value to use is 1. It's the value yielded by the "!",
relational, equality, "&&", and "||" operators, and it's the value
used in C99's <stdbool.h>.
If it were possible to do something like:
#define false 0
#define true anything-other-than-0
typedef int bool;
and have (2 == true) be true, then that would be more consistent.
The only valid value for "false" is 0, and the only really sensible
value for "true" is 1. Just don't compare boolean values for
equality. For example, rather than
if (condition == true)
write
if (condition)
You probably meant (!FALSE). Examples where your definition of TRUE
don't work as expected are a little contrived but they do exist.
<snip>
--
Ben.
And I even looked at it without seeing it...
Which is the OTHER reason I hate these fancy attempts to calculate the
two most hard-coded values in the C language. This is the only thing I
actually think is worse than sizeof(char), which at least CAN have documentary
value when used to preserve parallels between related cases.
> Frank <fr...@example.invalid> writes:
>
>> I had a question for you. Plauger writes that header inclusion is
>> idempotent. Is it possible to have:
>> #undef NDEBUG
>> #include <assert.h>
>>
>> and somewhere else NDEBUG *is* defined while assert.h is included,
>> such that they collide?
>
> Header inclusion is idempotent for the standard headers, *except* for
> <assert.h>. See C99 7.1.2p1:
>
> Standard headers may be included in any order; each may
> be included more than once in a given scope, with no effect
> different from being included only once, except that the effect
> of including <assert.h> depends on the definition of NDEBUG
> (see 7.2).
>
> #including <assert.h> multiple times won't cause a collision.
> C99 7.2p1:
>
> The assert macro is redefined according to the current state of
> NDEBUG each time that <assert.h> is included.
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.
--
Online waterways route planner | http://canalplan.eu
Plan trips, see photos, check facilities | http://canalplan.org.uk
> But I've written things like "<<0" in code before where I thought it
> improved clarity for future maintainers. Or I've left in unsimplified
> constant arithmetic expressions where it would help explain the thinking
> behind it.
24*60*60 and related expressions - leaving the expression in is better
than a comment explaining what 84600 is (even if half of you know
already).
> >> "(1==0)" is a bad enough way to express 0 that I would become instantly
> >> distrustful of all future code I saw that I thought came from the same
> >> source.
whilst I strongly displike (1==0) I think you're going too far here. I
have known a programmer that was quite insistant on this usage and
otherwise wrote quite good C (though he was a bit of a manic
micro-"optimiser").
I've seen this a lot
#define FALSE 0
#define TRUE !FALSE
> > The intent is not to use "(1==0)" as a means to express the value 0 it
> > is a means to express the value FALSE, I don't really see how the fact
> > that it evaluates to 0 makes any difference.
>
> Very simple:
>
> The C99 bool value false is 0. Not "the value FALSE". 0. So you should
> be providing the SAME value or you'll have some weird drift between versions.
how? Both expressions evaluate to zero!
> > I would say this is
> > really just a stylistic difference and as I said I am aware that these
> > types of arguments exist on both sides of the issue which is why I
> > specified I was not asking about this.
I suspect the (1==0) people think the expression isn't evaluated until
run time or something or aren't convinced that all C compilers for
ever and ever that true is 1 and false is 0.
I've seen this as well
/* shifted but */
#define BIT0 (1<<0)
#define BIT1 (1<<1)
#define BIT2 (1<<2)
I've never understood why this is better than a bunch of hex
constants. Hell *I'd* embed them in the code- or rather I'd use
sematically sensible macro names.
#define OUTER_DOOR_OPEN 0x0001
#define INNER_DOOR_OPEN 0x0002
void start_cycle (void)
{
clear_bit (INNER_DOOR_OPEN);
set_bit (OUTER_DOOR_OPEN);
}
> Ahh, but whether or not you asked about it, you should be aware that
> doing stuff like that will make any future reader familiar with C assume
> that your code is unreliable.
well not necessarily...
> It's just plain wrong. There is nothing
> more-false-than-0 in C.
there's nothing more null pointer constant than 0 in C either but many
people use NULL.
> > Stylistically you prefer this but mechanically it is identical in
> > every way to that written above aside from the capitalization which I
> > agree should be changed.
>
> Style matters. In particular, given that you agree that it's mechanically
> equivalent, why on earth are you specifying something in a more complicated
> way rather than a simpler way?
arguably its clearer. gulp.
> Why don't you go a bit further and write:
>
> TRUE = (((1!=0)!=(1!=1))!=0)
>
> ?
I can't find it now but I used to have an expression like
#define TRUE (1 - 1 + 1)
that spelt out the words TRUE and FALSE in ASCII art. But sadly I
can't find it.
:-(
I've seen this:
#define BIT(n) (1<<n)
> #define OUTER_DOOR_OPEN 0x0001
> #define INNER_DOOR_OPEN 0x0002
followed by
#define OUTER_DOOR_OPEN BIT(0)
#define INNER_DOOR_OPEN BIT(1)
The use of the BIT() macro is handy in embedded systems where bits map
to I/O ports.
--
Ian Collins
Surely you didn't think implementations are at liberty to
assign any non-zero value to x in
x = 7 == 7;
:-) :-)
A good habit may be to prefix a "!!" to boolean values,
i.e. replace
is_red(FOO foo) /* is foo red ?*/
...
return answer;
with
...
return !!answer;
If you neglect this you may be burned when you do
if (is_red(apple) == TRUE)
The fact that such code, without the "!!", may fail
is just another reason to avoid any TRUE silliness.
Hope this helps.
James
<snip>
> > In this case it seems to me that your
> > argument makes sense but it also seems to me that someone who says I
> > don't care that the value is 0 I care that the meaning is false and
> > (x==y) conveys false to them in a more clear manner than 0 does is
> > also a reasonable view.
>
> Not in C.
>
> And that's the thing -- we're not working in a pure void, we're working
> in an existing language with established conventions and patterns. And
> in that language, since everyone knows what the boolean operators yield,
> anyone who writes something like this is indirectly communicating that
> they don't understand the boolean operators.
the same argument could be applied to TRUE and FALSE (or true and
false). Why not just cut out the middle man and use 1 and 0? There are
people who would (strongly) argue that. I think they are wrong but
there you go.
<snip>
> how? Both expressions evaluate to zero!
Sorry, I was trying to get at the distinction between "false" and "FALSE".
And I was confused.
> there's nothing more null pointer constant than 0 in C either but many
> people use NULL.
Well, yeah -- because I hope the compiler will have it cast to (void *) so
it actually gets noticed as a pointer.
> that spelt out the words TRUE and FALSE in ASCII art.
Okay, *that's* a good reason.
> I don't think the OP is in one of those situations,
> though--"FALSE=0" has pretty unambiguous meaning.
a syntax error
A much simpler solution is to avoid equality comparisons for boolean
values. Just write:
if (is_red(apple))
and it doesn't matter how TRUE is defined, or which true value is_red
returns.
Note that isdigit() and friends return arbitrary true values, not
necessarily 1.
> 24*60*60 and related expressions - leaving the expression in is better
> than a comment explaining what 84600 is (even if half of you know
> already).
You mean 86400. Not sure how that affects the validity of your argument.
Tony
well I prefer manifest (i.e.named) constants but those are a little more
awkward in C than in C++.
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.
I actually pretty much agree, but the convention of using the names "true"
and "false" is strong enough to be somewhat-justifiable. I guess. I'm
not totally sold on it.
I tend to use " = 0" and " = 1" when I want to set things to false or true
respectively, but I also freely use "any value that isn't 0" as a true value,
because sometimes that allows me to communicate both truth and data about
what specifically was true.
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!
<snip>
> though practically, the declarations need to be the following
>
> typedef char Boolean;
> #define True 1
> #define False 0
>
> for compatibility with Xlib definitions.
<snip>
No it shouldn't, since the OP was going for compatibility with
stdbool.h, so it should be true and false.
Also, in response to the suggestion of using an enum, although I would
have liked that method it is not compatible with stdbool.h since that
defines true and false as macros and explicitly allows then to be #undef'd
Similarly, for as close as you can get to compatibility with stdbool.h
the OP should be defining
typedef someintegertype _Bool
#define bool _Bool
Depending on usage you might want someintegertype to be (unsigned) int
or (unsigned) char.
Oh, and I know that _Bool is in the implementation name space, but we
are trying to get as close as possible to stdbool.h and if _Bool is
provided anyway you could always comment out (or conditionally define
out) the typedef.
--
Flash Gordon
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.
> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
>
>> Ctalk Project <ct...@ctalklang.org> writes:
>> <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.
--
Ben.
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).
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.
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.
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.
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.
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.
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).
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).
What? Who said anything about variable names?
Given:
#define FALSE 0
#define TRUE (!FALSE)
What variable are you seeing that I'm not?
> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
>
>> Ctalk Project <ct...@ctalklang.org> writes:
>>
>>> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
>>>
>>>> Ctalk Project <ct...@ctalklang.org> writes:
>>>> <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]);
<snip>
>> 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.
--
Ben.
> [ignoring "Followup-To: Poster"]
>
> Ctalk Project <ct...@ctalklang.org> writes:
>> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
>>> Ctalk Project <ct...@ctalklang.org> writes: <snip>
>>>> Probably not what the OP wanted, though what usually works for me is
>>>>
> #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
> >>>> 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.
it might have just been easier to agree here...
> >> 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.
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.
>
> >> 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.
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.
??
???
the same "corruption" happened to me. I was wondering if I'd typed in
the wrong window!
Well if no-one had spotted it, it would have made my argument very
strongly - that putting the calculation in has several advantages
because you know what they mean, which you might skim past
#define SECONDS_IN_DAY 84600
It was an homage to Turing.
[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,
--
frank
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.
> At a minimum, I need to get x>0 && x<10. Anyways, my fingers are
> going to go solo unless I stop typing. Cheers,
--
> On Jan 4, 3:01=A0pm, Seebs <usenet-nos...@seebs.net> wrote:
> > Because it's wrong, for the same reason that what you have is wrong. =A0W=
> rite
> > for clarity. =A0If you are 100% sure that (1=3D=3D0) is 0, just write 0. =
> =A0If you
> > aren't 100% sure of it, you should not be writing header files that other
> > programmers are supposed to use as utilities or wrappers to export basic
> > functionality.
> >
> > -s
> > --
> > Copyright 2010, all wrongs reversed. =A0Peter Seebach / usenet-nos...@see=
> 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
> TonyMc wrote:
> > Nick <3-no...@temporary-address.org.uk> writes:
> >
> >> 24*60*60 and related expressions - leaving the expression in is better
> >> than a comment explaining what 84600 is (even if half of you know
> >> already).
> >
> > You mean 86400. Not sure how that affects the validity of your argument.
>
> 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
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++.
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.
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
--
Any true emperor never needs to wear clothes. -- Devany on r.a.s.f1
>> 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.
I see. Thx.
--
frank
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.
> > you are undeniably wrong, wrong, completely and utterly wrong. To state
> > that this is merely a matter of taste is ludicrous.
Same to you, buddy!
James Dow Allen
Oops. The easy fix is to substitute ExiT, defined via
int ExiT(int foo)
{
exit(foo);
/* NOTREACHED */
return !!foo != !foo;
}
Hope this helps.
>> > > you are undeniably wrong, wrong, completely and utterly wrong. To state
>> > > that this is merely a matter of taste is ludicrous.
>>
>> Same to you, buddy!
> Sheez! I was only kidding.
Well, then use a smiley-face next time, please.
James Dow Allen
> ral...@xs4all.nl (Richard Bos) writes:
> > Francis Glassborow <francis.g...@btinternet.com> wrote:
> >> TonyMc wrote:
> >> > Nick <3-no...@temporary-address.org.uk> writes:
> >> >
> >> >> 24*60*60 and related expressions - leaving the expression in is better
> >> >> than a comment explaining what 84600 is (even if half of you know
> >> >> already).
> >> >
> >> > You mean 86400. Not sure how that affects the validity of your argument.
> >>
> >> 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.
>
> 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
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.
> ral...@xs4all.nl (Richard Bos) writes:
> > Keith Thompson <ks...@mib.org> wrote:
> >> ral...@xs4all.nl (Richard Bos) writes:
> >> > #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;
> >
> > 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.
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
Is it? Isn't _obvious_ it should -1? (-1 a.k.a. all bits set)
IMO, false is zero, true should generate -1, and non-zero values should be
accepted true. When so, then bitwise operations can be used to perform
logical operations. This doesn't affect affect C programmers directly.
But, it does affect the implementation of C, since few (or no) assembly
languages directly support logical operations or boolean operations. They
typically only support bitwise operations.
> It's the value yielded by the "!",
> relational, equality, "&&", and "||" operators, and it's the value
> used in C99's <stdbool.h>.
That's true. But, using 1 as the only valid value "true" means you can only
use it within logical operations. What if you forget and use it with a
binary operation? Shouldn't a value of true be chosen to work with _both_
binary and logical operations? Wouldn't that be wise?
> The only valid value for "false" is 0, and the only really sensible
> value for "true" is 1.
I don't agree. I want my value for "true" to work correctly for both binary
and logical operations as a non-false value.
#include <stdio.h>
#define FALSE 0
/* enable TRUE you wish to test */
/* disable the others */
#if 0
#define TRUE 1
#endif
#if 0
#define TRUE (!FALSE)
#endif
#if 1
/* works for both */
#define TRUE -1
#endif
int main(void)
{
unsigned int i;
for(i=0;i<3;i++)
{
printf("\nFor %d:\n",i);
if(i&FALSE)
printf("FALSE fails as binary-false.\n");
else
printf("FALSE succeeds as binary-false.\n");
if(i&&FALSE)
printf("FALSE fails as logical-false.\n");
else
printf("FALSE succeeds as logical-false.\n");
if(i!=0)
{
if(i&TRUE)
printf("TRUE succeeds as binary-true.\n");
else
printf("TRUE fails as binary-true.\n");
if(i&&TRUE)
printf("TRUE succeeds as logical-true.\n");
else
printf("TRUE fails as logical-true.\n");
}
}
return(0);
}
HTH,
Rod Pemberton
> Is it?
Yes.
>Isn't _obvious_ it should -1? (-1 a.k.a. all bits set)
Not really "obvious". Interestingly, the one other person I've seen argue
that was Edward Nilges. :)
Here's my thought: Consider a single bit. It's either zero or one.
The non-zero value is 1. Otherwise, you have the problem that
((unsigned short) true == (int) true)
might be untrue.
> That's true. But, using 1 as the only valid value "true" means you can only
> use it within logical operations. What if you forget and use it with a
> binary operation? Shouldn't a value of true be chosen to work with _both_
> binary and logical operations? Wouldn't that be wise?
I don't think it works out that way. If you pick -1, then
(x & true)
is always true if x is non-zero -- but
(x & true) & (y & true)
might not be non-zero even if both x and y are non-zero.
So I don't much like the attempt to make the first work, since if the first
works, you might mistakenly expect the second to work.
> I don't agree. I want my value for "true" to work correctly for both binary
> and logical operations as a non-false value.
> if(i&TRUE)
> printf("TRUE succeeds as binary-true.\n");
That doesn't make any sense, though. How is "i & TRUE" being non-zero making
TRUE a "binary-true"?
With the usual value, if (x & true) is non-zero, (x & true) == true. With
yours, it isn't. I like that less.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
Yes, and no, respectively.
> IMO, false is zero, true should generate -1, and non-zero values should be
> accepted true. When so, then bitwise operations can be used to perform
> logical operations. This doesn't affect affect C programmers directly.
> But, it does affect the implementation of C, since few (or no) assembly
> languages directly support logical operations or boolean operations. They
> typically only support bitwise operations.
False is zero, and non-zero values are true, so you're at least two
thirds in agreement with the language.
>> It's the value yielded by the "!",
>> relational, equality, "&&", and "||" operators, and it's the value
>> used in C99's <stdbool.h>.
>
> That's true. But, using 1 as the only valid value "true" means you can only
> use it within logical operations.
Yes, that's what it's for.
> What if you forget and use it with a
> binary operation?
Then you've got a problem. So don't do that.
> Shouldn't a value of true be chosen to work with _both_
> binary and logical operations? Wouldn't that be wise?
I don't think so. Binary and logical operations are different things,
used in different contexts.
>> The only valid value for "false" is 0, and the only really sensible
>> value for "true" is 1.
>
> I don't agree. I want my value for "true" to work correctly for both binary
> and logical operations as a non-false value.
Then feel free to define it that way in your own language. If you
insist on using -1 as the canonical true value, you'll eventually run
into problems with code that uses 1 as the canonical true value.
On the other hand, if you're sufficiently careful, it doesn't matter
which non-zero value you use to represent truth.
[code snipped]
/* Determine how many elements of arr are negative: */
int count = 0;
for (i = 0; i < MAX; i ++) {
count += (arr[i] < 0);
> >> You can only define a single value as TRUE (or true). The most
> >> obvious value to use is 1.
>
> > Is it?
>
> Yes.
false is NIL anything else is true
> >Isn't _obvious_ it should -1? (-1 a.k.a. all bits set)
>
> Not really "obvious". Interestingly, the one other person I've seen argue
> that was Edward Nilges. :)
I'm sure I've come across a language where this was correct (I nearly
wrote "true" there!) but I can't remember which one. BCPL?
Ah yes!
http://www.fh-jena.de/~kleine/history/languages/Richards-BCPL-ReferenceManual.pdf
section 4.4
"The Rvalue of -true- is a bit pattern entirely composed of ones; the
Rvalue of -false- is zero."
<snip>
Actually, if you reread, I'm 100% in agreement with the language:
1) "IMO, false is zero, ..." That's true, and you're in agreement: "False is
zero, ..."
2) "..., and non-zero values should be accepted [as] true." That's true,
and you're in agreement. "... non-zero values are true, ..."
Those two are all the language requires for agreement. My extra condition
doesn't restrict agreement with the language.
> If you
> insist on using -1 as the canonical true value, you'll eventually run
> into problems with code that uses 1 as the canonical true value.
If you insist on using 1 as the canonical true value, you'll eventually run
into problems with code that uses -1 as the cononical true value. It's only
when _everyone_ has agreed to use a single value for the canonical true
value that no one will have problems. But, of course, using either value as
a canonical true is incorrect. Isn't it? Using any specific value for
true, other than testing for non-zero, should be though of as incorrect,
shouldn't it? It is only correct in situations where both true and false
_have been defined_ to be specific values, such as booleans. Yes?
> On the other hand, if you're sufficiently careful, it doesn't matter
> which non-zero value you use to represent truth.
"If you're sufficiently careful", could be said about gets() and ptrdiff_t
and whatever else too.
Rod Pemberton
I was discretely pointing out KT still has no experience with assembly or
the implementation of C using assembly. All the world is ADA...
> Interestingly, the one other person I've seen argue
> that was Edward Nilges. :)
That just means you haven't used other languages. FORTH used 0 and 1 for
false and true, respectively for the first two standard versions: fig-FORTH
and FORTH-77. In FORTH-83, they converted to 0 and -1 for false and true,
respectively. That was 1983: 27 years ago. The reason was the same as for
assembly. Logical operations must be constructed from bitwise operations in
most assembly.
> Here's my thought: Consider a single bit. It's either zero or one.
>
> The non-zero value is 1. Otherwise, you have the problem that
>
> ((unsigned short) true == (int) true)
>
> might be untrue.
Did I miss something? C has that problem currently. So, what's your point?
True is non-zero. If true as non-zero is larger than a C byte, true could
be truncated via a cast from a non-zero value to a zero value. It's only if
you use a #define to specify a value for TRUE that can't be truncated that
"((unsigned short) true == (int) true)" will not be untrue.
> > That's true. But, using 1 as the only valid value "true" means you can
only
> > use it within logical operations. What if you forget and use it with a
> > binary operation? Shouldn't a value of true be chosen to work with
_both_
> > binary and logical operations? Wouldn't that be wise?
>
> I don't think it works out that way. If you pick -1, then
> (x & true)
> is always true if x is non-zero -- but
Correct.
> (x & true) & (y & true)
> might not be non-zero even if both x and y are non-zero.
For -1 as a true value, true. But, (x&true) and (y&true) have both worked
properly using true with a binary operation. It's the later operation,
which doesn't use true, that fails. This is no different than x&y resulting
in zero for certain non-zero binary values.
> So I don't much like the attempt to make the first work, since if the
first
> works, you might mistakenly expect the second to work.
...
> > I don't agree. I want my value for "true" to work correctly for both
binary
> > and logical operations as a non-false value.
>
> > if(i&TRUE)
> > printf("TRUE succeeds as binary-true.\n");
>
> That doesn't make any sense, though. How is "i & TRUE" being non-zero
making
> TRUE a "binary-true"?
From the binary and (&) ... meaning that TRUE works as true with a binary
operation. Using a value of 1 for true will fail to result in true with a
binary and (&) for even values of 'i'.
> With the usual value, if (x & true) is non-zero, (x & true) == true.
(x&true) results in true only for odd values of x. So, (x&true)==true only
reports odd's (non-portably). It produces a zero result for half.
If true is non-zero and x is non-zero, wouldn't you like a non-zero result
for (x&true)? Just like (x&&true)?
> With the usual value, if (x & true) is non-zero, (x & true) == true.
> With yours, it isn't.
However, I'm not sure why anyone would do a binary comparison of a result
with true when you can test this directly via an if()...
RP
Assuming "KT" refers to me, I don't know what you're talking about,
or why. This isn't about what experience I personally may or may
not have. As for ADA, the correct spelling is Ada, and your remark
is neither true nor relevant.
--
Joe Wright
"If you rob Peter to pay Paul you can depend on the support of Paul."
> "Seebs" <usenet...@seebs.net> wrote in message
> news:slrnhmf210.hkn...@guild.seebs.net...
>
>> Here's my thought: Consider a single bit. It's either zero or one.
>>
>> The non-zero value is 1. Otherwise, you have the problem that
>>
>> ((unsigned short) true == (int) true)
>>
>> might be untrue.
>
> Did I miss something? C has that problem currently. So, what's your point?
It does if the "true" in the line of code means "an arbitrary value that
is true". But Seebs' point is about the value you've chosen to define
the symbol "true" as. If it's 1, as he's arguing, then whatever you
cast it to will still be 1. If it's -1, as you were arguing, then these
two casts can yield values that don't compare equal.
> > >> You can only define a single value as TRUE (or true). The most
> > >> obvious value to use is 1.
>
> > > Is it?
>
> > Yes.
>
> > >Isn't _obvious_ it should -1? (-1 a.k.a. all bits set)
>
> > Not really "obvious".
>
> I was discretely pointing out KT still has no experience with assembly or
> the implementation of C using assembly. All the world is ADA...
so discretely it completely passed *me* by. I havn't a clue what
Keith's assemble experience is. I don't see why heavy use of assembly
would lead to -1 being the "obvious" choice for -1. What's Ada got to
do with price of fish?
> > Interestingly, the one other person I've seen argue
> > that was Edward Nilges. :)
>
> That just means you haven't used other languages.
there's plenty of langaues that don't use -1 for true. Algol, pascal,
FORTRAN, Lisp, scheme for starters.
> [...] In FORTH-83, they converted to 0 and -1 for false and true,
> respectively.
[...]
> assembly. Logical operations must be constructed from bitwise operations in
> most assembly.
you'll have to explain. If I want to generate true I could invert all
the bits of false
xora FF
but why is that any easier than
xora 1
If I use single bits for booleans I can multiple booleans into a
single byte (I've generated morse on multiple channels this way). You
way precludes that.
Assembly *increses* your flexibility in low level data representation,
so why limit yourself this way?
> > Here's my thought: Consider a single bit. It's either zero or one.
>
> > The non-zero value is 1. Otherwise, you have the problem that
>
> > ((unsigned short) true == (int) true)
>
> > might be untrue.
>
> Did I miss something? C has that problem currently. So, what's your point?
I'm left equally baffled. "it hurts when I do this, doctor!" "so don't
do that"
> > > That's true. But, using 1 as the only valid value "true" means you can
> > > only use it within logical operations. What if you forget and use it with a
> > > binary operation? Shouldn't a value of true be chosen to work with
> > > _both_ binary and logical operations? Wouldn't that be wise?
I don't see why. If its logically a boolean then use it as a boolean.
If it's a bunch of bits use it as a bunch of bits.
> > I don't think it works out that way. If you pick -1, then
> > (x & true)
> > is always true if x is non-zero -- but
>
> Correct.
>
> > (x & true) & (y & true)
> > might not be non-zero even if both x and y are non-zero.
>
> For -1 as a true value, true. But, (x&true) and (y&true) have both worked
> properly using true with a binary operation. It's the later operation,
> which doesn't use true, that fails. This is no different than x&y resulting
> in zero for certain non-zero binary values.
it's the fundamental flaw in confusing boolean with bunch-of-bits.
> > So I don't much like the attempt to make the first work, since if the
> > first works, you might mistakenly expect the second to work.
>
> > > I don't agree. I want my value for "true" to work correctly for both
> > > binary and logical operations as a non-false value.
why?
> However, I'm not sure why anyone would do a binary comparison of a result
> with true when you can test this directly via an if()...
nor me, but it seems to be widespread. If only C had had a proper
boolean type from day 1... And a null keyword. And proper function
prototypes (what we have now should have been in K&R). Secretly I want
C to be pascal.
I do have experience of a number of assembly languages...
All the ones I've used had tests for the accumulator being zero or non-zero.
>> Interestingly, the one other person I've seen argue
>> that was Edward Nilges. :)
>
> That just means you haven't used other languages. FORTH used 0 and 1 for
> false and true, respectively for the first two standard versions: fig-FORTH
> and FORTH-77. In FORTH-83, they converted to 0 and -1 for false and true,
> respectively. That was 1983: 27 years ago. The reason was the same as for
> assembly. Logical operations must be constructed from bitwise operations in
> most assembly.
Pascal uses 0 for false and 1 for true. At least, all the version I've
used did.
>> Here's my thought: Consider a single bit. It's either zero or one.
>>
>> The non-zero value is 1. Otherwise, you have the problem that
>>
>> ((unsigned short) true == (int) true)
>>
>> might be untrue.
>
> Did I miss something? C has that problem currently. So, what's your point?
You are guaranteed that ((unsigned short)(1==1) == (int)(1==1)) in C.
With your proposal you would not have this guarantee. I though it
obvious the comment was referring to the cannocical value of true (i.e.
the value that boolean operators yield for true)
> True is non-zero. If true as non-zero is larger than a C byte, true could
> be truncated via a cast from a non-zero value to a zero value. It's only if
> you use a #define to specify a value for TRUE that can't be truncated that
> "((unsigned short) true == (int) true)" will not be untrue.
See above.
>>> That's true. But, using 1 as the only valid value "true" means you can
> only
>>> use it within logical operations. What if you forget and use it with a
>>> binary operation? Shouldn't a value of true be chosen to work with
> _both_
>>> binary and logical operations? Wouldn't that be wise?
>> I don't think it works out that way. If you pick -1, then
>> (x & true)
>> is always true if x is non-zero -- but
>
> Correct.
>
>> (x & true) & (y & true)
>> might not be non-zero even if both x and y are non-zero.
>
> For -1 as a true value, true.
I.e. for what you are recommending you get this "problem".
> But, (x&true) and (y&true) have both worked
> properly using true with a binary operation. It's the later operation,
> which doesn't use true, that fails. This is no different than x&y resulting
> in zero for certain non-zero binary values.
I.e. using binary operators as if they are logical operators does not
work, so there is no point in trying to bend the language to make it
appear to work some of the time.
>> So I don't much like the attempt to make the first work, since if the
> first
>> works, you might mistakenly expect the second to work.
> ...
>
>>> I don't agree. I want my value for "true" to work correctly for both
> binary
>>> and logical operations as a non-false value.
>>> if(i&TRUE)
>>> printf("TRUE succeeds as binary-true.\n");
>> That doesn't make any sense, though. How is "i & TRUE" being non-zero
> making
>> TRUE a "binary-true"?
>
> From the binary and (&) ... meaning that TRUE works as true with a binary
> operation. Using a value of 1 for true will fail to result in true with a
> binary and (&) for even values of 'i'.
So don't do that. Binary operators are not designed to be used as
logical operators.
>> With the usual value, if (x & true) is non-zero, (x & true) == true.
>
> (x&true) results in true only for odd values of x. So, (x&true)==true only
> reports odd's (non-portably). It produces a zero result for half.
>
> If true is non-zero and x is non-zero, wouldn't you like a non-zero result
> for (x&true)? Just like (x&&true)?
No, why would I? It doesn't make sense to try and use binary operators
for logical tests or logical values as operands to binary operators.
>> With the usual value, if (x & true) is non-zero, (x & true) == true.
>> With yours, it isn't.
>
> However, I'm not sure why anyone would do a binary comparison of a result
> with true when you can test this directly via an if()...
In other words if you use the language as it is you don't have any of
the problems you are trying to solve!
--
Flash Gordon
> "Rod Pemberton" <do_no...@havenone.cmm> writes:
> > "Keith Thompson" <ks...@mib.org> wrote in message
> >> You can only define a single value as TRUE (or true). The most
> >> obvious value to use is 1.
> >
> > Is it? Isn't _obvious_ it should -1? (-1 a.k.a. all bits set)
>
> Yes, and no, respectively.
>
I have to dissent, at least in in principle. IME there are reasonable
arguments, and use cases, for both +1 and -1 aka ~0u, so I don't think
either is obvious a priori. For C, of course, the choice was made long
ago, and swimming against the tide now is just asking for trouble.
As noted, BCPL chose ~0 (rather its equivalent syntax, which I
forget); but it had only one data size and didn't deal with promotion.
And as noted, FORTH did for the standard, although AFAICT FORTH has
even greater profusion of not-quite-standard dialects than C does.
> /* Determine how many elements of arr are negative: */
> int count = 0;
> for (i = 0; i < MAX; i ++) {
> count += (arr[i] < 0);
> }
OTOH APL used +1 partly to (intentionally) support a more convenient
form of things like this example. And PL/I did kind-of accidentally;
actually it uses BIT or more precisely BIT(1), but that (silently)
converts to integer 0 or +1, which can be used in arithmetic.
And FWIW in languages with user-defined enumerations, they usually are
represented at least by default with the first name 0 and the second 1
etc., so enum ( false, true ) 'naturally' gives you +1, and arguably
more intuitive enum ( true, false ) gives you a big pain.
Sure, in principle there's nothing really wrong with representing
false as 0 and true as -1 (or as 37 if you like). But as you say,
C made the choice of 0 and 1 long ago, in the values yielded by a
number of operators, and more recently in the values chosen for the
"false" and "true" macros in <stdbool.h>. My argument was in the
context of C.
> As noted, BCPL chose ~0 (rather its equivalent syntax, which I
> forget); but it had only one data size and didn't deal with promotion.
> And as noted, FORTH did for the standard, although AFAICT FORTH has
> even greater profusion of not-quite-standard dialects than C does.
>
>> /* Determine how many elements of arr are negative: */
>> int count = 0;
>> for (i = 0; i < MAX; i ++) {
>> count += (arr[i] < 0);
>> }
>
> OTOH APL used +1 partly to (intentionally) support a more convenient
> form of things like this example. And PL/I did kind-of accidentally;
> actually it uses BIT or more precisely BIT(1), but that (silently)
> converts to integer 0 or +1, which can be used in arithmetic.
On the other hand, if C didn't guarantee that "<" yields 0 or 1, the
above could be written as:
int count = 0;
for (i = 0; i < MAX; i ++) {
count += (arr[i] < 0 ? 1 : 0);
}
or:
int count = 0;
for (i = 0; i < MAX; i ++) {
if (arr[i] < 0) {
count ++;
}
}
and there are arguments to be made that either of them is better
style anyway.
> And FWIW in languages with user-defined enumerations, they usually are
> represented at least by default with the first name 0 and the second 1
> etc., so enum ( false, true ) 'naturally' gives you +1, and arguably
> more intuitive enum ( true, false ) gives you a big pain.
At least one language (Ada) explicitly defines its Boolean type as an
enumeration type.
<snip - true, +1 or -1, which is better and/or makes more sense?>
>> And FWIW in languages with user-defined enumerations, they usually are
>> represented at least by default with the first name 0 and the second 1
>> etc., so enum ( false, true ) 'naturally' gives you +1, and arguably
>> more intuitive enum ( true, false ) gives you a big pain.
>
> At least one language (Ada) explicitly defines its Boolean type as an
> enumeration type.
I've always thought that false being less than true (false<true) has a
certain linguistic elegance, and also provides an easy way to remember
which has what value.
--
Flash Gordon
> I've always thought that false being less than true (false<true) has a
> certain linguistic elegance, and also provides an easy way to remember
> which has what value.
oh my god! he's muddled his axiomisations!
Nice page.
I have no idea how wiki happens, yet I consider it this huge advance in
information evangelism.
--
fred
it would have saved an hour of my life. Instead of spending an hour
scribbling increasingly arcane stuff on a blackboard the lecturer
could have just said "read this wiki page". Of course wiki hadn't
been invented then, though just putting the title up at the beginning
would have helped a lot. It's all a bit of shame when you don't find
out what the reason for it all is until you get to the end... Though
of course it's one lecture I'll always remember.