portuesi@tweezers.esd.sgi.com (Michael Portuesi) (09/25/89)
In article <1989Sep22.073138.19684@lth.se> newsuser@lth.se (LTH network news server) writes:
We cannot define our own operator functions on
the boolean data type, because one of the arguments must be a class object.
boolean operator == (boolean x, boolean y) {...} // error
I do not understand exactly why this restriction is required, but apparently
it is. Please enlighten me.
I'm pretty sure the reason why one of the arguments must be a class
object is to prevent the redefintion of operator functions for base
types such as int, float and char.
--M
--
__
\/ Michael Portuesi Silicon Graphics Computer Systems, Inc.
portuesi@SGI.COM
"The best length for television programs is either 30 seconds or 8
hours." David Byrnejima@hplsla.HP.COM (Jim Adcock) (09/27/89)
>Alan: >If a boolean type is required, it can easily be provided in C++, with whichever >properties are desired. Some people like, for instance to have: > >enum boolean { false, true, maybe }; Two problems if/when people make their own "bool" classes. First, one is forced to explicetely state those expressions one wants to be bool, otherwise they will still be "int" -- if (a&b | c&d) {dosomething();} will still follow the built-in "int" rules of evaluation, unless one somehow forces the compiler to recognize part of the subexpressions as "bool." One way to do this: if (bool(a&b) | bool(c&d)) {dosomething();} Or one could declare and evaluate a, b, c, and d ahead of time: bool a=something, b=something, c=something, d=something; ... if (a&b | c&d) {dosomething();} would also work. But who would do this compared to the simplicety of "C" "int" evaluation of boolean expressions? Also, the poor reader of this software is going to be darn hard pressed to discover whether "int" rules or "bool" rules of evaluation are in effect in a given expression. IE, the meaning of: if (a&b | c&d) {dosomething();} is going to depend on whether a, b, c, d have been declared int or bool somewhere earlier in the program. Also, present generation compilers insist on making class objects ["C" structures] memory resident, as oppose to allowing them to exist solely in registers. This makes a bool class several times slower than using built-in ints in boolean expressions. Compare the efficiency of the int verses "bool" expressions as compiled on my m680x0 machine: ------------------------------------------------ class bool { int b; public: bool():b(0){} bool(int a):b(a){} friend bool operator|(const bool& a, const bool& b) {return a.b | b.b;} friend bool operator&(const bool& a, const bool& b) {return a.b & b.b;} }; int intexpr(const int a, const int b, const int c, const int d) {return (a&b) | (c&d);} bool boolexpr(const bool& a, const bool& b, const bool& c, const bool& d) {return (a&b) | (c&d);} ------------------------------------------------ Compiles to [AT&T 2.0, HP backend C compiler]: ------------------------------------------------ global intexpr(int,int,int,int) intexpr(int,int,int,int) : link.w %a6,&0 mov.l 8(%a6),%d0 and.l 12(%a6),%d0 mov.l 16(%a6),%d1 and.l 20(%a6),%d1 or.l %d1,%d0 unlk %a6 rts global boolexpr(const bool&,const bool&,const bool&,const bool&) boolexpr(const bool&,const bool&,const bool&,const bool&) : link.w %a6,&-40 mov.l %a1,-4(%a6) mov.l 8(%a6),-8(%a6) mov.l 12(%a6),-12(%a6) mov.l -8(%a6),%a0 mov.l (%a0),%d0 mov.l -12(%a6),%a0 and.l (%a0),%d0 mov.l %d0,-16(%a6) lea -16(%a6),%a0 mov.l (%a0),-32(%a6) mov.l 20(%a6),-(%sp) mov.l 16(%a6),-(%sp) lea -40(%a6),%a1 jsr operator&(const bool&,const bool&) addq.w &8,%sp mov.l %d0,%a0 mov.l (%a0),-36(%a6) mov.l -32(%a6),%d0 or.l -36(%a6),%d0 mov.l %d0,-28(%a6) lea -28(%a6),%a0 mov.l -4(%a6),%a1 mov.l (%a0),(%a1) mov.l -4(%a6),%d0 unlk %a6 rts operator&(const bool&,const bool&) : link.w %a6,&-8 mov.l %a1,-4(%a6) mov.l 8(%a6),%a0 mov.l (%a0),%d0 mov.l 12(%a6),%a0 and.l (%a0),%d0 mov.l %d0,-8(%a6) lea -8(%a6),%a0 mov.l (%a0),(%a1) mov.l -4(%a6),%d0 unlk %a6 rts
jima@hplsla.HP.COM (Jim Adcock) (10/06/89)
>>Bart Massey >> >In article <8862@etana.tut.fi> pl@etana.tut.fi (Lehtinen Pertti) writes: >> >| Then suddenly just behind the corner cames C-compiler from >> >| ACME-corporation and realizes '!!a' -> negation of negation is >> >| same as original -> we can optimize it away. >> >| >> >| Nice, isn't it. And too real too. >> > >> >And wrong, too. Do you know of a compiler that does this in the >> >general case? >> >> Yes, all of them. > >Aiigh! I'm sure I'm about the millionth response to this, but I just >can't stand it anymore! > > "The result of the logical negation operator ! is > 1 if the value of its operand is 0, 0 if the value > of its operand is non-zero. The type of the > result is int. It is applicable to any arithmetic > type or to pointers" -- K&R A:7.2, Stroustrup r.7.2 > > "The result of the logical negation operator ! is 0 > if the value of its operand compares unequal to 0, > 1 if the value of its operand compares equal to 0. > The result has type int. The expression !E is > equivalent to (0==E)." -- ANSI X3J11/88-158 3.3.3.3 19 > >So anything that is supposed to be a standard C or C++ compiler >is *required* to do the right thing! As a test, I tried 4 C compilers >we have around: > > Stanford's V6.0 68k (an ancient PCC based compiler) > Greenhills 68k (a modern commercial compiler) > BSD 4.3Tahoe VAX (a modern PCC based compiler) > GNU GCC1.34 VAX (a cool highly optimizing retargetable compiler) > I agree with Bart, add to this list the following conforming compilers [sorry, I don't have an apollo division compiler] HP-s300 C compilers HP-s800 C compilers HP-s300 C++ 2.0 compilers HP-s300 g++ compilers [from FSF] HP-s300 gcc compilers [from FSF] Below find another program you can use to help convince yourself that 'C' and 'C++' compilers really do evaluate '!' to a zero or one value, or alternately produce code that works equivalent to this statement. For example the s300 gcc -O code produced by the following program is: #NO_APP gcc_compiled.: .text LC0: .ascii "compiler fails on !!-1\12\0" LC1: .ascii "compiler fails on !!0\12\0" LC2: .ascii "compiler fails on !!-0\12\0" LC3: .ascii "compiler fails on !!1\12\0" LC4: .ascii "compiler fails on !!-100\12\0" LC5: .ascii "compiler fails on !!100\12\0" LC6: .ascii "this compiler seems to be in conformance in its use of '!!'\12\0" .even .globl _main _main: link a6,#0 pea LC6 jbsr _printf unlk a6 rts Which clearly 'does not' 'evaluate' the multiple occurances of '!' to a 'zero or one value' -- it doesn't 'evaluate' anything, it just figures out the right result at compile time and generates a little code to print it. Which is conformance. Please warn us if anyone manages to find a compiler that isn't in conformance as tested by the following simple program: ------------------------------------------------------------------------------- #ifdef __cplusplus extern "C" {int printf(const char* const, ...);}; #endif main() { if((!!-1) != 1) {printf("compiler fails on !!-1\n");} else if((!!0) != 0) {printf("compiler fails on !!0\n");} else if((!!-0) != 0) {printf("compiler fails on !!-0\n");} else if((!!1) != 1) {printf("compiler fails on !!1\n");} else if((!!-100) != 1) {printf("compiler fails on !!-100\n");} else if((!!100) != 1) {printf("compiler fails on !!100\n");} else printf("this compiler seems to be in conformance in its use of '!!'\n"); }
richard@pantor.UUCP (Richard Sargent) (10/07/89)
I have justed Microsoft C 5.1 on MS-DOS with Jim Adcock's example program for verifying correct "!!" behaviour. Both optimized and non-optimized compiles reported correct operation. Richard Sargent Internet: richard@pantor.UUCP Systems Analyst UUCP: uunet!pantor!richard
jima@hplsla.HP.COM (Jim Adcock) (10/10/89)
//here's another simple test for those who complain my previous test can
//be evaluated by a compiler completely at compile time while still generating
//bogus runtime evaluations. [Obviously, no amount of testing can "prove" a
//real compiler works right. What I'm trying to do is convince any remaining
//sceptics who still think there is a compiler that evaluates !!x to mean x
//to find it for us! [I've yet to hear from *anyone* who actually has found
//a compiler that generates bogus code for !!x. --Or maybe whoever wrote
//such a compiler is too ashamed to admit it! :-]]
#ifdef __cplusplus
extern "C"
{
int printf(const char* const, ...);
int rand(); // yes I *know* rand is weak -- its also widely available.
void exit(int status);
};
#endif
main()
{
int i,j,k;
for (i=0; i<1000; ++i)
{
j = ((unsigned char)(rand()))-128;
k = (j!=0);
if (k != (!!j))
{
printf("failure on: %d\n",j);
exit(1);
}
else
{
printf("success on: %d\n",j);
}
}
}lsmith@apollo.HP.COM (Lawrence C. Smith) (10/11/89)
In article <6590293@hplsla.HP.COM> jima@hplsla.HP.COM (Jim Adcock) writes: >//here's another simple test for those who complain my previous test can >//be evaluated by a compiler completely at compile time while still generating >//bogus runtime evaluations. [Obviously, no amount of testing can "prove" a >//real compiler works right. What I'm trying to do is convince any remaining >//sceptics who still think there is a compiler that evaluates !!x to mean x >//to find it for us! [I've yet to hear from *anyone* who actually has found >//a compiler that generates bogus code for !!x. --Or maybe whoever wrote >//such a compiler is too ashamed to admit it! :-]] > I was one of the original doubters, and I've been trying a few things on some of the C compilers I have access to. The results were interesting. In general, something like: if(!!x)... compiles as: if(x) in *all* the compilers I tested. I assumed I was vindicated in my opinion. Then I tried a few other things: y = !!x; Sure enough, y was 1 when x was != 0, and 0 when it was. The verbose version of if: if(!!x == 1) compiles correctly. This was a fascinating exercise! It would appear that the optimization in question was related not to expression evaluation (which I assumed it was) but to flow control. I have worked on lots of compilers, but I'd be the first to admit that a C compiler was not among them, and I more or less assumed they worked like the Pascal/Ada/Modula family of languages as far as expression evaluation is concerned. It appears that, to the contrary, flow-control statements like if *do* get the optim- ization, but expressions do not. It is now clear that the distinction is because of cases like !!x - optimization is obvious, yet leads to incorrect code under certain circumstances. In this case, whenever the result of the expression is used as *value* and not as a *flag*. So the majority seems to be right. But I bet it's one royal bitch to write an optimizing C compiler with gotchas like that running around. Larry Smith Internet: lsmith@apollo.hp.com UUCP: {decvax,mit-eddie,umix}!apollo!lsmith
jat@ranger.austin.ibm.com (Johnny Tamplin) (10/17/89)
If you look through the old V7 docs, there is a paper talking about PCC. It
discusses that when evaluating an expression, it has three modes: evaluating
a=b*3+c;
if(!a) {
...
gets parsed as follows:
EvalSideEffect(a=b*3+c)
Assign(a,EvalValue(b*3+c))
Assign(a,b*3+c)
EvalConditional(!a,truelabel,falselabel)
EvalConditional(a,falselabel,truelabel)
Notice that the negate operator is evaluated at compile time by reversing the
sense of the test, so that in a conditional, !!a is exactly a. If the same
expression is being evaluated for value, code will be generated for it.
It is not a lot of code to do this -- in fact, it sort of falls out rather
easily. You have 3 big switch statements which call various other functions
to actually generate code for the operators. I have written several "quickie"
code generators this way (although I usually add evaluate for address to make
things more straightforward), and the code isn't terribly bad without any
optimization other than peephole. Of course, this doesn't get near the code
generated by sophisticated optimizations such as those in gcc.