haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) (05/23/91)
I've been browsing through include files and it seems that everyone in the world who ever created a C++ include file created some definition to support boolean variables. I've seen BOOL bool Bool BOOLEAN boolean Boolean bool_t BOOL_T, ... Some are #defines, some are typedefs. Some folks like to associate them with char, unsigned char, int, long, or enum. Some packages (like XView) must have used a random number generator to decide which definition would be used for each separate include file-- I doubt that anyone could be that inconsistant unless it was deliberate. So my question is, how can I choose one definition (my own, or someone else's) for boolean variables, and still remain insulated from the sea of boolean definitions in assorted include files? For that matter, am I insulated even without a boolean definition of my own? I suppose I could pick some extreamly unlikely name for my boolean variable type, but I really don't want to. One reason is that it makes code so much clearer to have clear straighforward class and type names. The best I can think of right now is to always include "foreign" include files first, then my own. In my own include files I can do something like: #ifdef Bool #undef Bool #endif typedef enum {FALSE, TRUE} Bool; I suppose that I could still take a hit from a macro, or inline function. I don't think I'm immune from name space conflicts either. The biggest thing I'd like to avoid is having the same class have different sizes in different include files due to include file interference. What's the best way to do this? Thanks, Hayden Schultz (haydens@juliet.ll.mit.edu) MIT Lincoln Lab (617) 981-3685
horstman@mathcs.sjsu.edu (Cay Horstmann) (05/24/91)
In article <HAYDENS.91May23103233@natasha.juliet.ll.mit.edu> haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) writes: > >The best I can think of right now is to always include "foreign" >include files first, then my own. In my own include files I can do >something like: > >#ifdef Bool >#undef Bool >#endif >typedef enum {FALSE, TRUE} Bool; > Don't do that. In C++, enums aren't ints. For example, the code Bool more, done; // ... more = !done && cin.good(); will not compile--the right hand side of the = (the result of &&) is an int, and it cannot be assigned to an enum without a cast more = (Bool)(!done && cin.good()); // bleecch Cay > > Thanks, > > Hayden Schultz (haydens@juliet.ll.mit.edu) > MIT Lincoln Lab > (617) 981-3685
bright@nazgul.UUCP (Walter Bright) (05/26/91)
In article <HAYDENS.91May23103233@natasha.juliet.ll.mit.edu> haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) writes:
/So my question is, how can I choose one definition (my own, or someone
/else's) for boolean variables, and still remain insulated from the sea
/of boolean definitions in assorted include files?
/What's the best way to do this?
After years of fiddling around with various schemes myself, the most practical
one I came up with is just use int:
/*********************
* Test some quantity.
* Returns:
* 0 False
* !=0 True
*/
int func();
I've gone through my code over time and have eliminated all the logical, bool,
boolean, BOOL, etc. clutter. Although the above solution seems boring and
inelegant, it works suprisingly well, given that it mimics C's idea of what
is true and what is false.
Note that you also need to get rid of any TRUE or FALSE #define's. Remember to
always test for 0 or !=0.
(Some other battle-scarred old C programmers have told me that they have
eventually come to the same conclusions.)
mittle@blinn.watson.ibm.com (Josh Mittleman) (05/29/91)
> Note that you also need to get rid of any TRUE or FALSE #define's. Remember to > always test for 0 or !=0. > > (Some other battle-scarred old C programmers have told me that they have > eventually come to the same conclusions.) But that's exactly the point: Testing for == 0 is the perfect C approach, but it entirely inconsistent with the logic of C++. C++ should have a standard built-in Boolean type. =========================================================================== Josh Mittleman (mittle@watson.ibm.com or joshua@paul.rutgers.edu) J2-C28 T.J. Watson Research Center, PO Box 704, Yorktown Heights, NY 10598
philip@pescadero.Stanford.EDU (Philip Machanick) (05/30/91)
In article <1991May29.145100.18519@watson.ibm.com>, mittle@blinn.watson.ibm.com (Josh Mittleman) writes: |> |> > Note that you also need to get rid of any TRUE or FALSE #define's. |> Remember to |> > always test for 0 or !=0. |> > |> > (Some other battle-scarred old C programmers have told me that they have |> > eventually come to the same conclusions.) |> |> But that's exactly the point: Testing for == 0 is the perfect C approach, |> but it entirely inconsistent with the logic of C++. C++ should have a |> standard built-in Boolean type. Yes - maybe it would also catch a lot of those annoying hard to find bugs caused by typing "if (thing = whatever)" instead of "==". But how many programs would break? I for one would be prepared to pay the price for the long-term gain, but I generally try to program as if boolean and int are distinct types, so the price wouldn't be very high for me. -- Philip Machanick philip@pescadero.stanford.edu
dag@control.lth.se (Dag Bruck) (05/30/91)
In article <1991May29.171209.14760@neon.Stanford.EDU> philip@pescadero.stanford.edu writes: >.... But how many programs would break? I for one would be prepared >to pay the price for the long-term gain, but I generally try to program >as if boolean and int are distinct types, so the price wouldn't be >very high for me. The "break old code" argument against any changes is the one you hear most often at the C++ standardization meetings. It is of course valid, but often over-used. Even if there were incompatible changes, every compiler would have a switch to get the old behaviour. One of the strongest arguments in favour of a new boolean data type is that you could do function overloading: void f(int); void f(boolean); I also think a boolean data type would increase type safety in C++, at least the way I program (:-). I once tried to implement `class boolean' and failed. Firstly there was a performance problem, which I think good compilers could overcome. Secondly, I don't think I gained anything in type safety and little in convenience: to be at all useful I needed implicit type conversions int->boolean and boolean->int. I think a boolean data type should _not_ be another kind of integer (at least eventually) which can be implicitly converted to/from int. It should instead be a unique enumerated type, but that doesn't work until all standard functions (e.g., operator == (int, int)) return a boolean and not an int. The transition to a better world (:-) should be done in two phases: 1. Introduce the new built-in data type `boolean' (or whatever you call it), and specify that all standard operator == (etc.) return a boolean. 2. Include implicit type conversions between boolean and int, to minimize the number of broken programs. I think this automatic conversion will help in most cases. 3. Warn about the automatic conversions and label them as anachronisms. 4. (Some years later) Turn the warnings of (3) into errors. We had a similar case with C++ 2.0 with respect to handling of `enum', and I think the transition was relatively painless. The most common problem was that people had used an enum for booleans, which caused warning messages under the new interpretation. Dag Br!"uck -- Department of Automatic Control E-mail: dag@control.lth.se Lund Institute of Technology P. O. Box 118 Phone: +46 46-104287 S-221 00 Lund, SWEDEN Fax: +46 46-138118
robert@am.dsir.govt.nz (Robert Davies) (05/30/91)
In article <1991May29.171209.14760@neon.Stanford.EDU> philip@pescadero.stanford.edu writes: ....... >|> C++ should have a >|> standard built-in Boolean type. > >Yes - maybe it would also catch a lot of those annoying hard to >find bugs caused by typing "if (thing = whatever)" instead of >"==". But how many programs would break? I for one would be prepared >to pay the price for the long-term gain, but I generally try to program >as if boolean and int are distinct types, so the price wouldn't be >very high for me. In cases like this where you do need a change that would damage old code why can't you have a pragma to turn on or off an old or new feature? So that old code would still run with the addition of one line to each source file. Robert
pena@brainware.fi (Olli-Matti Penttinen) (05/30/91)
In article <1991May30.060200.6590@lth.se> dag@control.lth.se (Dag Bruck) writes:
void f(int);
void f(boolean);
I also think a boolean data type would increase type safety in C++, at
least the way I program (:-).
I once tried to implement `class boolean' and failed. Firstly there
was a performance problem, which I think good compilers could
overcome. Secondly, I don't think I gained anything in type safety
and little in convenience: to be at all useful I needed implicit type
conversions int->boolean and boolean->int. I think a boolean data
type should _not_ be another kind of integer (at least eventually) which
can be implicitly converted to/from int. It should instead be a
unique enumerated type, but that doesn't work until all standard
functions (e.g., operator == (int, int)) return a boolean and not
an int.
I partly agree. It would be nice, if operator==(T,T) would return a
truly boolean value. On the other hand, it is possible to achieve the
same type safety if one implements not only a class boolean, but
classes real, integer, string, and what not, as well. That would of
course introduce a rather severe performance penalty, however. If only
current compilers did a better job in optimizing common
sub-expressions involving inlined access member functions and such... :-(
I know of at least one project, in which the group (they used Ada)
redefined practically everything, and to some extent succeeded. They
did peculiar things like had an "enum boolean { true, false, i_dont_care,
i_dont_know}" type of a construct.
Summa summarum: once again we face a consept that could be
incorporated to the language and indeed would fit in well, but without
which we can manage. IMHO, that means we shouldn't have it.
Especially, since no current library can support it.
Dag Br!"uck
--
Department of Automatic Control E-mail: dag@control.lth.se
Lund Institute of Technology
P. O. Box 118 Phone: +46 46-104287
S-221 00 Lund, SWEDEN Fax: +46 46-138118
==pena
--
Olli-Matti Penttinen <pena@brainware.fi> | "When in doubt, use brute force."
Brainware Oy | --Ken Thompson
P.O.Box 330 +----------------------------------
02151 ESPOO, Finland Tel. +358 0 4354 2565 Fax. +358 0 461 617
tom@elan.Elan.COM (Thomas Smith) (05/31/91)
[ Continuing thread about merits/pitfalls of defining a boolean type in your application, and colliding with definitions in other modules. ] From article <1991May30.060200.6590@lth.se>, by dag@control.lth.se (Dag Bruck): > I once tried to implement `class boolean' and failed. Firstly there > was a performance problem, which I think good compilers could > overcome. Secondly, I don't think I gained anything in type safety > and little in convenience: to be at all useful I needed implicit type > conversions int->boolean and boolean->int. I have successfully implemented Boolean data types in several large projects (100K+ lines of C++) at the last two companies I have worked for. If you set it up right, you do get the type-safety without the performance penalty. In most cases, the biggest concern was size - could we get a Boolean class that would have size == 1 byte (the minimum) and align on single-byte boundaries when packed in structures. The answer is... depends on the architecture. Here is what I usually end up with: class Boolean { unsigned char value; friend int operator== (Boolean left, Boolean right); friend int operator!= (Boolean left, Boolean right); public: Boolean() {} Boolean(int i) { value = ((i) ? 1 : 0); } operator int() const { return (int) value; } }; inline int operator== (Boolean left, Boolean right) { return left.value == right.value; } inline int operator!= (Boolean left, Boolean right) { return left.value != right.value; } // defined in the source file as initialized to 0 and 1, respectively extern const Boolean FALSE; extern const Boolean TRUE; Notice that initialization by int is allowed (via constructor), but assignment is not (no operator=(int)). There are some pitfalls with this approach, however: 1) The type name Boolean sometimes collides with "helpful" libraries that *just know* you will need a type called Boolean that is a typedef'd unsigned char. The X toolkit (Xt) is a prime offender. Folks, what's the point of sticking the Xt prefix on 95% of the types defined, and skipping the 5% most likely to collide with an application's own types? This can be worked around by simply calling your Boolean something else, like TBoolean (Tom's Boolean :^), and undefining TRUE and FALSE at the beginning of the header file (if they were defines and not an enum). 2) The constant values TRUE and FALSE are globals that are initialized via constructor, and thus cannot be safely used in other global constructors. Buckle up for safety... All in all, though, the good outweighs the bad. One other reason that I usually settle on the class approach for Boolean types is that I often need many other similar types - for instance Status (SUCCESS or FAILURE) - which are not compatible with Boolean and can cause trouble if they are assigned to each other. (Of course, Status is defined as int by the X11 Xlib.h header file - don't get me started on graduate-student software again). Hope this was somewhat helpful - I feel better now. Thomas Smith Elan Computer Group, Inc. (415) 964-2200 tom@elan.com, ...!{ames, uunet, hplabs}!elan!tom
jgro@lia (Jeremy Grodberg) (05/31/91)
In article <332@nazgul.UUCP> bright@nazgul.UUCP (Walter Bright) writes: >In article <HAYDENS.91May23103233@natasha.juliet.ll.mit.edu> haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) writes: >/So my question is, how can I choose one definition (my own, or someone >/else's) for boolean variables, and still remain insulated from the sea >/of boolean definitions in assorted include files? >/What's the best way to do this? > >After years of fiddling around with various schemes myself, the most practical >one I came up with is just use int: > > /********************* > * Test some quantity. > * Returns: > * 0 False > * !=0 True > */ > > int func(); > >I've gone through my code over time and have eliminated all the logical, bool, >boolean, BOOL, etc. clutter. Although the above solution seems boring and >inelegant, it works suprisingly well, given that it mimics C's idea of what >is true and what is false. > >Note that you also need to get rid of any TRUE or FALSE #define's. Remember to >always test for 0 or !=0. > >(Some other battle-scarred old C programmers have told me that they have > eventually come to the same conclusions.) I agree about using int for booleans, but I disagree about getting rid of TRUE and FALSE. I, like a lot of programmers, skimp on writing comments. To make up for it, I try to write self-documenting code, through appropriate use of varible and function names. TRUE and FALSE make it clear, without comments, that a boolean value is being returned. Also, since many (bad) compilers in my early C programming days required TRUE to be !FALSE (which could be 1, 0xFF, 0xFFFF, or something else) for boolean arithmetic to work properly, I get worried about simply returning 1 for true. Using TRUE and FALSE allows me to avoid problems like that. I would sooner give up NULL then TRUE or FALSE. From my C experience, I always include: #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE !FALSE #endif You may complain about using !FALSE instead of (!FALSE), but it has never caused me a problem. Many (bad) compilers in my early C programming days required TRUE to be !FALSE for boolean arithmetic to work properly, so I still use that. If you are worried about getting some incompatible TRUE or FALSE, you can replace the #ifndefs with #undefines. You only get what ANSI promises (and not always that), and ANSI only promises int's for booleans, so that's what I use. It may mean not discovering compiler problems, but my job is to get programs to work, not to fix compilers. -- Jeremy Grodberg "Show me a new widget that's bug-free, and I'll show jgro@lia.com you something that's been through several releases."
dag@control.lth.se (Dag Bruck) (06/02/91)
In article <998@elan.Elan.COM> tom@elan.Elan.COM (Thomas Smith) writes: >From article <1991May30.060200.6590@lth.se>, by dag@control.lth.se (Dag Bruck): >> I once tried to implement `class boolean' and failed. Firstly there >> was a performance problem, which I think good compilers could >> overcome. Secondly, I don't think I gained anything in type safety >> and little in convenience: to be at all useful I needed implicit type >> conversions int->boolean and boolean->int. > >I have successfully implemented Boolean data types in several large >projects (100K+ lines of C++) at the last two companies I have worked >for. If you set it up right, you do get the type-safety without the >performance penalty. > > class Boolean { > unsigned char value; > public: > Boolean() {} > Boolean(int i) { value = ((i) ? 1 : 0); } > > operator int() const { return (int) value; } > }; Tom's approach is very similar to the ones I tried. Unfortunately, I disagree with Tom about the usefulness of such a class. Let's take a look at this example: Boolean g(int x, Boolean y) { Boolean z = y; z = x; return z; } I compiled this on a Sun-3 with -O4 optimization. The assembly code is enclosed at the end for those who are interested. The problems I have is: 1. Class Boolean occupies two bytes; a minor inconvenience. 2. Cfront will not accept `register' declarations for `z' which will give me a performance hit. 3. Assignment by an `int' is possible because there is a constructor. >Notice that initialization by int is allowed (via constructor), >but assignment is not (no operator=(int)). Not true. We can even assign a `char' or `double' to `z', although assigning a double will give us a warning. 4. Returning a class or a struct is awkward, even in this case where the compiler could have returned it in a register. >All in all, though, the good outweighs the bad. I think we have gained little. In particular Boolean is not typesafe, and the `if' statement will still accept any old garbage. -- Dag =========================================================================== Here is a the MC680x0 assembly code produced for the example above. Configuration was Sun-3, SunOS 4.1.1, Cfront 2.1, Sun's C compiler with -O4 option. LL0: .data .data .text .proc |#PROC# 010 .data .lcomm L2000000,2 LF16 = 8 LS16 = 128 LFF16 = 4 LSS16 = 0 LV16 = 4 .text .globl _g__Fi7Boolean _g__Fi7Boolean: |#PROLOGUE# 0 link a6,#-8 |#PROLOGUE# 1 movw a6@(14),a6@(-2) tstl a6@(8) jeq L77003 moveq #1,d1 jra L77004 L77003: moveq #0,d1 L77004: movb d1,a6@(-2) movl #L2000000,a1 movw a6@(-2),a1@ movl a1,d0 |#PROLOGUE# 2 unlk a6 |#PROLOGUE# 3 rts
npw@eleazar.dartmouth.edu (Nicholas Wilt) (06/02/91)
Defining your own Boolean type (with #defines, typedefs, or classes, as you prefer) has advantages, but they are far outweighed by the problems introduced when someone tries to use your code (or, conversely, when you're trying to deal with code full of Booleans of one flavor or another). What happens when you try to make use of two libraries, one of which thinks Boolean is a "typedef int" and another which thinks Boolean is a class? You could go through renaming and reworking, but that shouldn't be necessary. C++ inherits a pretty reasonable treatment of integers as Boolean values from C (nonzero is True when evaluating, Boolean expressions evaluate to integer 1 or 0). It's portable, reasonably clean and flexible--for instance, there's no other way to stuff multiple Booleans into a single word, like you can with the : construct in C structures. A little less pretty than any of the ten thousand other ways people can think of to deal with Boolean values, but unlike any of those, it's standard. Overall, I think the disadvantages outweigh the advantages. I hate buying C source code and seeing Boolean defined explicitly. It gets ripped out as soon as a need develops to interface with it more than perfunctorily. --Nick npw@eleazar.dartmouth.edu
jk@cs.man.ac.uk (John Kewley ICL) (06/05/91)
Here is an example of a boolean class which does not allow ints to be assigned to booleans. I introduce functions which return true and false values. These could be extended for three valued logic. They are friends which use a static (own) variable defined using the *private* int conversion constructor. To offset the inefficiencies of the system (possibly not much worse than any other I've seen suggested) would be to have #ifndef NDEBUG #define TRUE 1 #define FALSE 0 #define bool int /* or perhaps char */ #else #define TRUE true() #define FALSE false() class bool { /* . . . */ } #fi There is no assignment operator for int to bool and no int conversion constructor. Taking the 4 comments made about Tom's class: 1. Boolean takes up 2 bytes No problem if using #defines as above 2. register declarations are not allowed I am not certain of this one. I have used registers for some examples OK, I think (I'm no assembler hacker). register could perhaps (careful slippery ice here) be #defined away when DEBUGging. 3. Assignment by int possible Not that I can see in this case, except in friend functions true() and false(). 4. returning a class is awkward I don't understand this one, this may be avoided by my #defines I think in this case, the good outweighs the bad, in particular there should be no performance penalty when NDEBUGging. Note I have not yet tried to implement the #define stuff, I would welcome peoples ideas. OK here we go here is the class + a sample main file. Sorry about the plethora of operators, I tried to be complete. Have I missed any functionality? ------------------------------------------- #include <stdio.h> class bool { public: bool() {} bool(const bool& b) {c=b.c;} // For if statements operator void*() const {return (void*) c;} bool& operator= (const bool& r) {c= r.c; return *this;}; bool operator! () {return (!c) ? true() : false();}; bool operator == (const bool& r) const {return (c == r.c) ? true() : false();}; bool operator != (const bool& r) const {return (c != r.c) ? true() : false();}; bool operator == (int r) const {return (r) ? c : !c;}; bool operator != (int r) const {return (r) ? !c : c;}; friend bool operator == (int l, const bool& r) const {return (l) ? r.c : !r.c;}; friend bool operator != (int l, const bool& r) const {return (l) ? !r.c : r.c;}; bool operator && (const bool& r) const {return (c) ? bool(r.c) : false();}; bool operator || (const bool& r) const {return (c) ? true() : bool(r.c);}; bool operator && (int r) const {return (c) ? bool(r) : false();}; bool operator || (int r) const {return (c) ? true() : bool(r);}; friend bool operator && (int l, const bool& r) const {return (l) ? bool(r.c) : false();}; friend bool operator || (int l, const bool& r) const {return (l) ? true() : bool(r.c);}; friend bool& true(); friend bool& false(); private: char c; bool(int i) {c=((i)?1:0);} // Disallow assignment to an int. // bool& operator= (int) {return *this;}; }; bool& true() { static bool T(1); return T; } bool& false() { static bool F(0); return F; } bool one_more( int a, int b ) { return (( a - 1 == b ) ? true() : false()); } int main() { bool t= true(); bool f= false(); bool b= true(); bool c; if (one_more(2,1)) printf("2 is one more than 1\n"); else printf("2 is not one more than 1\n"); if (one_more(1,2)) printf("1 is one more than 2\n"); else printf("1 is not one more than 2\n"); c= false(); b= c; // b= 1; // bool::bool(): private member // I think it refers to bool::bool(int) (b) ? printf("b is true\n") : printf("b is not true\n"); (!b) ? printf("b is false\n") : printf("b is not false\n"); (b != c) ? printf("b and c are both the same\n") : printf("b and c are not the same\n"); if (b) printf("b = true\n"); else printf("b = false\n"); return(0); if (g(1,f)) then printf("true\n"); else printf("false\n"); } -- J.K. John M. Kewley, ICL, Wenlock Way, West Gorton, Manchester. M12 5DR Tel: (+44) 61 223 1301 X2138 Email: jk@cs.man.ac.uk / jk@nw.stl.stc.co.uk