www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - strong enums: why implicit conversion to basetype?

reply "Trass3r" <un known.com> writes:
I thought it'd be good to outsource this question from the other 
thread about enums as flags.

Is there any merit in having implicit conversion to the basetype?
Imo it only introduces a severe bug source and brings no 
advantages.

For example it allows implicit conversion to bool.

enum Bla
{
    S1 = 1,
    S2,
    S3
}
if (Bla.S2) // makes no sense at all, all Blas convert to true
// and if S1 was 0, it would mean false, but it isn't meant as a 
special member!

A better example is something like
if (b && Bla.S2) // written '&&' instead of '&' by mistake, will 
silently pass


In general it allows operations that don't make any sense.

if (Bla.S2 & Blub.S1) // works cause an int is produced
// but by using named enums I made clear that Bla and Blub are 
totally different

Heck even +,-,... work.

Remember that if you do need to do such crazy stuff you can still 
explicitly cast to int or whatever.
Jan 26 2012
next sibling parent reply "Trass3r" <un known.com> writes:
On Thursday, 26 January 2012 at 14:45:02 UTC, Manfred Nowak wrote:
 Trass3r wrote:

 but by using named enums I made clear that Bla and Blub are 
 totally different

No. Obviously you decjlared both to be implicitely convertable to a common super type: int. To change this, both supertypes have be changed.

No. The intention is "Bla and Blub don't have anything to do with each other". That's why I question the implicit conversion.
Jan 26 2012
parent Manfred Nowak <svv1999 hotmail.com> writes:
Trass3r wrote:

 That's why I question the implicit conversion.

Yes. I realized my fault and canceled my message, but wasn't fast enough. -manfred
Jan 26 2012
prev sibling next sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Trass3r wrote:

 Is there any merit in having implicit conversion to the basetype?

Yes. Otherwise it would be at least close to equivalence to a `typedef'. -manfred
Jan 26 2012
parent reply "Trass3r" <un known.com> writes:
 Is there any merit in having implicit conversion to the 
 basetype?

Yes. Otherwise it would be at least close to equivalence to a `typedef'.

Even typedef implicitly converts in one of the directions. A named enum is a separate type with a finite set of allowed values defined by the user.
Jan 26 2012
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Trass3r wrote:


 Even typedef implicitly converts in one of the directions.

`typedef' is or will be disallowed in D because of reasons I do not understand. In C and C++ their existence introduce problems because they increase the amount of parsing passes.
 A named enum is a separate type with a finite set of allowed 
 values defined by the user.

A) Both wrong according to the currently published reference: 1) "The enum EnumTag declares a new type, and all the EnumMembers have that type.", i.e. it is the tag not the hole enum that produces a new type. 2) "Enum declarations are used to define a group of constants.", i.e. only some of the valid values of the basetype are given names. All of the valid values of the basetype are also valid in the type declared by the tag. 3) "An EmptyEnumBody signifies an opaque enum - the enum members are unknown", i.e. none of the valied values of the basetype has got a name, but all valid values of the basetype are also valid in the type declared by the tag. B) It is somehow difficult to write about the reference because the use of the terms declaration, definition or declaration follows rules, which I have not yet detected. I use "declaration" for any entity, which is undefined at the current position of reading _and_ will be defined after that position is passed. This is often an identifier in D. I use "definition" for any entity, which represents the content of the transition from undefined to defined. This is often a type in D. I use "specification" for those two entities, which represents a binding between a declaration and a definition. This is often the absence of operators in D. -manfred
Jan 26 2012
parent reply Trass3r <un known.com> writes:
 `typedef' is or will be disallowed in D because of reasons I do not
 understand.

It's ill-defined. There are 4 possible types of typedef: http://d.puremagic.com/issues/show_bug.cgi?id=5467
 In C and C++ their existence introduce problems because
 they increase the amount of parsing passes.

C's typedef is equal to D's alias.
 A named enum is a separate type with a finite set of allowed
 values defined by the user.

A) Both wrong according to the currently published reference:

Again, this thread is all about discussing the right way to do it and not about what the buggy and holey spec reads.
 2) "Enum declarations are used to define a group of constants.", i.e. 
 only some of the valid values of the basetype are given names. All ofthe  
 valid values of the basetype are also valid

That's what anonymous enums are for.
 3) "An EmptyEnumBody signifies an opaque enum - the enum members are
 unknown", i.e. none of the valied values of the basetype has got a
 name, but all valid values of the basetype are also valid in the type
 declared by the tag.

So the only purpose is to create a new type similar to typedef. I don't see any merit in that.
Jan 26 2012
parent Manfred Nowak <svv1999 hotmail.com> writes:
Trass3r wrote:

 It's ill-defined. There are 4 possible types of typedef:  
 http://d.puremagic.com/issues/show_bug.cgi?id=5467

[...]
 Again, this thread is all about discussing the right way to do it
 and not  about what the buggy and holey spec reads.

[...]
 I don't see any merit in that.

You meight be blind. The only way to eject this possibility is to prove that there cannot be any merit. Currently a good approximation of your intentions for the replacment of `enum's seems to be a wood of rooted almost-DAGs on types, where the edges in the DAGs represent the allowed implicit conversions, the inner nodes are represented by tags and the leaves are represented by members. Maybe that the "almost" is not necessary. -manfred
Jan 26 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01/26/2012 02:59 PM, Trass3r wrote:
 I thought it'd be good to outsource this question from the other thread
 about enums as flags.

 Is there any merit in having implicit conversion to the basetype?
 Imo it only introduces a severe bug source and brings no advantages.

 For example it allows implicit conversion to bool.

 enum Bla
 {
 S1 = 1,
 S2,
 S3
 }
 if (Bla.S2) // makes no sense at all, all Blas convert to true
 // and if S1 was 0, it would mean false, but it isn't meant as a special
 member!

That is not an implicit conversion. if(x) is equivalent to if(cast(bool)x).
 A better example is something like
 if (b && Bla.S2) // written '&&' instead of '&' by mistake, will
 silently pass


 In general it allows operations that don't make any sense.

 if (Bla.S2 & Blub.S1) // works cause an int is produced
 // but by using named enums I made clear that Bla and Blub are totally
 different

 Heck even +,-,... work.

 Remember that if you do need to do such crazy stuff you can still
 explicitly cast to int or whatever.

I have argued for banning those operations on strong enums before, but some objected to it because they wanted to use strong enums as bit flags.
Jan 26 2012
parent Trass3r <un known.com> writes:
 I have argued for banning those operations on strong enums before, but  
 some objected to it because they wanted to use strong enums as bit flags.

Yep, that's what the other thread 'using enums for flags' is about. But implicit conversions seem wrong in any case.
Jan 26 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 26/01/2012 14:59, Trass3r a écrit :
 I thought it'd be good to outsource this question from the other thread
 about enums as flags.

 Is there any merit in having implicit conversion to the basetype?
 Imo it only introduces a severe bug source and brings no advantages.

 For example it allows implicit conversion to bool.

 enum Bla
 {
 S1 = 1,
 S2,
 S3
 }
 if (Bla.S2) // makes no sense at all, all Blas convert to true
 // and if S1 was 0, it would mean false, but it isn't meant as a special
 member!

 A better example is something like
 if (b && Bla.S2) // written '&&' instead of '&' by mistake, will
 silently pass


 In general it allows operations that don't make any sense.

 if (Bla.S2 & Blub.S1) // works cause an int is produced
 // but by using named enums I made clear that Bla and Blub are totally
 different

 Heck even +,-,... work.

 Remember that if you do need to do such crazy stuff you can still
 explicitly cast to int or whatever.

I don't see any problem with that : it is perfectly safe. The other way around isn't and isn't allowed, so the current behaviour seems fine to me.
Jan 26 2012
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
On 26.01.2012 14:59, Trass3r wrote:
 I thought it'd be good to outsource this question from the other thread
 about enums as flags.

 Is there any merit in having implicit conversion to the basetype?

Allowing it to be used as an argument when calling C functions? Without it, how would you support eg the Windows API?
Jan 26 2012
parent reply Trass3r <un known.com> writes:
 Is there any merit in having implicit conversion to the basetype?

Allowing it to be used as an argument when calling C functions?

extern(C): enum Bla : int {...} void foo(Bla b); How does this require implicit conversion? The codegen treats Bla like basetype anyway.
Jan 26 2012
parent reply Don Clugston <dac nospam.com> writes:
On 26/01/12 21:26, Trass3r wrote:
 Is there any merit in having implicit conversion to the basetype?

Allowing it to be used as an argument when calling C functions?

extern(C): enum Bla : int {...} void foo(Bla b); How does this require implicit conversion? The codegen treats Bla like basetype anyway.

Some of the Windows functions are made of multiple enums of different types, ORed together.
Jan 27 2012
parent Trass3r <un known.com> writes:
 extern(C):
 enum Bla : int {...}
 void foo(Bla b);

 How does this require implicit conversion?
 The codegen treats Bla like basetype anyway.

Some of the Windows functions are made of multiple enums of different types, ORed together.

-.- Microsuckx. Then I think it should either become a combined enum or just anonymous ones (+ aliases to basetype perhaps).
Jan 27 2012
prev sibling next sibling parent reply Alvaro <alvaroDotSegura gmail.com> writes:
El 26/01/2012 14:59, Trass3r escribió:
 I thought it'd be good to outsource this question from the other thread
 about enums as flags.

 Is there any merit in having implicit conversion to the basetype?
 Imo it only introduces a severe bug source and brings no advantages.

 A better example is something like
 if (b && Bla.S2) // written '&&' instead of '&' by mistake, will
 silently pass
 Heck even +,-,... work.

I kind of agree. I understand enums as a way to define "tags" or flags used to define things like file open mode Read, Write, ReadWrite, endianness BigEndian, LittleEndian, socket type Stream/Packet, etc. things that under the hood are represented by integer numbers but that don't represent *quantities*, so should not work the same way as integers. What is the result of subtracting or multiplying LittleEndian and BigEndian? Does not make sense. Bitwise operations would be OK because logica tags can be combined, but little more. And this brings the question of whether implicit casting from int to bool is a good thing (or making any integer a valid boolean value). The same way, true and false are not quantities and can't be used in arithmetic. Even if they are internally represented as 1 and 0, they should not be the same thing as numbers. IMO, Java did it better in making them distinct. With a non-int-convertible bool your above weird example would not work.
Jan 26 2012
next sibling parent Manfred Nowak <svv1999 hotmail.com> writes:
Alvaro wrote:

  With a non-int-convertible bool your above weird 
 example would not work.

But that the example works is not the fault of the existence of enums. It is due to the fact that omission, inclusion or change of one character can produce a different value without any warning. -manfred
Jan 26 2012
prev sibling parent reply =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
On Thu, 26 Jan 2012 23:49:40 +0100, Alvaro <alvaroDotSegura gmail.com>
wrote:

 El 26/01/2012 14:59, Trass3r escribi=C3=B3:
 I thought it'd be good to outsource this question from the other thre=


ad
 about enums as flags.

 Is there any merit in having implicit conversion to the basetype?
 Imo it only introduces a severe bug source and brings no advantages.

 A better example is something like
 if (b && Bla.S2) // written '&&' instead of '&' by mistake, will
 silently pass
 Heck even +,-,... work.

I kind of agree. I understand enums as a way to define "tags" or flags=

=
 used to define things like file open mode Read, Write, ReadWrite,  =

 endianness BigEndian, LittleEndian, socket type Stream/Packet, etc.  =

 things that under the hood are represented by integer numbers but that=

=
 don't represent *quantities*, so should not work the same way as  =

 integers. What is the result of subtracting or multiplying LittleEndia=

n =
 and BigEndian? Does not make sense. Bitwise operations would be OK  =

 because logica tags can be combined, but little more.

Sometimes, bitwise operations make sense, other times not. Enums play tw= o roles in D - that of an enumeration and that of a set of flags. Only for= the latter do bitwise operations make sense.
Jan 26 2012
parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
On 01/26/2012 08:17 PM, Simen Kjærås wrote:
 On Thu, 26 Jan 2012 23:49:40 +0100, Alvaro <alvaroDotSegura gmail.com>
 wrote:

 El 26/01/2012 14:59, Trass3r escribió:
 I thought it'd be good to outsource this question from the other thread
 about enums as flags.

 Is there any merit in having implicit conversion to the basetype?
 Imo it only introduces a severe bug source and brings no advantages.

 A better example is something like
 if (b && Bla.S2) // written '&&' instead of '&' by mistake, will
 silently pass
 Heck even +,-,... work.

I kind of agree. I understand enums as a way to define "tags" or flags used to define things like file open mode Read, Write, ReadWrite, endianness BigEndian, LittleEndian, socket type Stream/Packet, etc. things that under the hood are represented by integer numbers but that don't represent *quantities*, so should not work the same way as integers. What is the result of subtracting or multiplying LittleEndian and BigEndian? Does not make sense. Bitwise operations would be OK because logica tags can be combined, but little more.

Sometimes, bitwise operations make sense, other times not. Enums play two roles in D - that of an enumeration and that of a set of flags. Only for the latter do bitwise operations make sense.

You'd think that with some use of a templated struct and some "alias this" we'd be able to have a strongly-typed type for storing sets of flags. It could even offer convenience functions like "getFlag" and "setFlag" to make the code read a bit nicer.
Jan 27 2012
parent =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
On Fri, 27 Jan 2012 13:34:06 +0100, Chad J  =

<chadjoan __spam.is.bad__gmail.com> wrote:

 On 01/26/2012 08:17 PM, Simen Kj=C3=A6r=C3=A5s wrote:
 On Thu, 26 Jan 2012 23:49:40 +0100, Alvaro <alvaroDotSegura gmail.com=

 wrote:

 El 26/01/2012 14:59, Trass3r escribi=C3=B3:
 I thought it'd be good to outsource this question from the other  =




 thread
 about enums as flags.

 Is there any merit in having implicit conversion to the basetype?
 Imo it only introduces a severe bug source and brings no advantages=




.
 A better example is something like
 if (b && Bla.S2) // written '&&' instead of '&' by mistake, will
 silently pass
 Heck even +,-,... work.

I kind of agree. I understand enums as a way to define "tags" or fla=



gs
 used to define things like file open mode Read, Write, ReadWrite,
 endianness BigEndian, LittleEndian, socket type Stream/Packet, etc.
 things that under the hood are represented by integer numbers but th=



at
 don't represent *quantities*, so should not work the same way as
 integers. What is the result of subtracting or multiplying
 LittleEndian and BigEndian? Does not make sense. Bitwise operations
 would be OK because logica tags can be combined, but little more.

Sometimes, bitwise operations make sense, other times not. Enums play=


=
 two
 roles in D - that of an enumeration and that of a set of flags. Only =


for
 the latter do bitwise operations make sense.

You'd think that with some use of a templated struct and some "alias =

 this" we'd be able to have a strongly-typed type for storing sets of  =

 flags.  It could even offer convenience functions like "getFlag" and  =

 "setFlag" to make the code read a bit nicer.

Like that attached?
Jan 27 2012
prev sibling next sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
 El 26/01/2012 14:59, Trass3r escribi?:
 I thought it'd be good to outsource this



question from the other thread
 about enums as flags.

 Is there any merit in having implicit



conversion to the basetype?
 Imo it only introduces a severe bug source and



brings no advantages.

Sometimes, bitwise operations make sense, other times

not. Enums play two
 roles in D - that of an enumeration and that of a set

of flags. Only for
 the latter do bitwise operations make sense.

=20 You'd think that with some use of a templated struct and some "alias=20 this" we'd be able to have a strongly-typed type for storing sets of=20 flags.=A0 It could even offer convenience functions like "getFlag" and=20 "setFlag" to make the code read a bit nicer.

I have a personal class i am using, may submit it later to Walter to add t= o Phobos. Comments? (I have working unittests... :) ) ///T of type ENUM, and S of an integral. struct HandleFlags(T, S) { =09S state;=09///Holds state. =09alias T T_Enum; =09 this(T[] setFlags...); =09///Returns true/false if a specific ENUM flag has been set. =09bool check(T[] flag...); =09///Returns true/false if a specific ENUM flag has been set. =09bool checkAll(T[] flag...); =09 =09/** =09Checks if a flag has been set, returning that ENUM, otherwise returning = the Else flag. =09*/ =09T checkElse(T Else, T[] flag...); =09///Sets specific flag(s) on =09void setFlag(T[] flag...); =09///turns listed flags off. =09void clearFlag(T[] flag...); =09///reverses the state of a specific flag. =09void flipFlag(T[] flag...); }
Jan 27 2012
parent reply "Marco Leise" <Marco.Leise gmx.de> writes:
Am 27.01.2012, 20:01 Uhr, schrieb Era Scarecrow <rtcvb32 yahoo.com>:

 El 26/01/2012 14:59, Trass3r escribi?:
 I thought it'd be good to outsource this



question from the other thread
 about enums as flags.

 Is there any merit in having implicit



conversion to the basetype?
 Imo it only introduces a severe bug source and



brings no advantages.

Sometimes, bitwise operations make sense, other times

not. Enums play two
 roles in D - that of an enumeration and that of a set

of flags. Only for
 the latter do bitwise operations make sense.

You'd think that with some use of a templated struct and some "alias this" we'd be able to have a strongly-typed type for storing sets of flags. It could even offer convenience functions like "getFlag" and "setFlag" to make the code read a bit nicer.

I have a personal class i am using, may submit it later to Walter to =

=
 add to Phobos. Comments? (I have working unittests... :) )


 ///T of type ENUM, and S of an integral.
 struct HandleFlags(T, S)
  {
 	S state;	///Holds state.
 	alias T T_Enum;

 	 this(T[] setFlags...);

 	///Returns true/false if a specific ENUM flag has been set.
 	bool check(T[] flag...);

 	///Returns true/false if a specific ENUM flag has been set.
 	bool checkAll(T[] flag...);
 	=

 	/**
 	Checks if a flag has been set, returning that ENUM, otherwise returni=

ng =
 the Else flag.
 	*/
 	T checkElse(T Else, T[] flag...);

 	///Sets specific flag(s) on
 	void setFlag(T[] flag...);

 	///turns listed flags off.
 	void clearFlag(T[] flag...);

 	///reverses the state of a specific flag.
 	void flipFlag(T[] flag...);
 }

Is it possible to 'auto-detect' the integral type? I makes little sense = to = use a signed type or a type too small for the enum for example. And in the same fashion: Do you map an enum value to "1 << enum_value" i= n = the bit field? I know that in Delphi 'sets' worked great for me and so I= = am biased for everything that works the same way. It is also the most = natural way to work with enums that can be represented as a bit field IM= O. = This means that enum values cannot be larger than 63 of course (so they = = fit inside a ulong). What else=E2=80=A6 hmm=E2=80=A6 I'm wondering if the documentation for f= lipFlag should be = "reverses the state of all given flags". How is checkElse supposed to work. I could imagine the function of a = method with the signature "T (T flag, T else)". What is the use case for= = it? Even check seems to accept multiple flags, where I would expect only= = one. We could also introduce some operator overloading: setFlag <=3D> +=3D, = clearFlag <=3D> -=3D, flipFlag <=3D> ^=3D, check <=3D> in. (setFlag coul= d be |=3D as = well, but +=3D is more consistent with clearFlag as -=3D)
Jan 29 2012
parent bearophile <bearophileHUGS lycos.com> writes:
This is an important topic.

I have an enhancement requests on enums:
http://d.puremagic.com/issues/show_bug.cgi?id=3999
As usual language design is a matter of finding the right balance between
strictness, that helps catch real bugs, and type "sloppiness" that makes code
more handy, and avoids some casts that sometimes are themselves a source of
bugs. C++11 has introduced enums with a stronger static typing compared to D
enums. But it keeps the less strongly typed enum too. This increases C++11
language complexity, but allows the programmer to choose between strictness and
flexibility at will.

Regarding flags management, I think D2 is not good enough about them. They are
a common need for a system language and I think normal enums are not good
enough for this specialized purpose. Normal enums are both not handy enough and
not safe enough for this purpose.
So an idea is to introduce something like a " flags enum". But I think a
library solution is enough here, just like bitfields. I have written this:
http://d.puremagic.com/issues/show_bug.cgi?id=6946
A problem with this implementation is that needs operator overload (with
preconditions) to catch some wrong usages. Another problem is that is doesn't
allow to add flag combinations inside the flag list itself. Maybe if you try to
solve this second problem you increase too much the complexity of this
implementation, so I have left them out.

In this thread I have seen various library solutions to implement flags. I
think Andrei will be glad to add to Phobos a flags library solution that
combines the best ideas of the various implementations.

Bye,
bearophile
Jan 29 2012
prev sibling parent Era Scarecrow <rtcvb32 yahoo.com> writes:
 ///T of type ENUM, and S of an integral.
 struct HandleFlags(T, S)
=A0 {
 =A0=A0=A0 S state;=A0=A0=A0 ///Holds

state.
 =A0=A0=A0 alias T T_Enum;

 =A0=A0=A0=A0=A0this(T[] setFlags...);

 =A0=A0=A0 ///Returns true/false if a specific

ENUM flag has been set.
 =A0=A0=A0 bool check(T[] flag...);

 =A0=A0=A0 ///Returns true/false if all flags are set

ENUM flag has been set.
 =A0=A0=A0 bool checkAll(T[] flag...);
 =A0=A0=A0=20
 =A0=A0=A0 /**
 =A0=A0=A0 Checks if a flag has been set,

returning that ENUM, otherwise returning=A0=20
 the Else flag.
 =A0=A0=A0 */
 =A0=A0=A0 T checkElse(T Else, T[] flag...);

 =A0=A0=A0 ///Sets specific flag(s) on
 =A0=A0=A0 void setFlag(T[] flag...);

 =A0=A0=A0 ///turns listed flags off.
 =A0=A0=A0 void clearFlag(T[] flag...);

 =A0=A0=A0 ///reverses the state of a specific

flag.
 =A0=A0=A0 void flipFlag(T[] flag...);
 }

=20 Is it possible to 'auto-detect' the integral type? I makes little sense to use a signed type or a type too small for the enum for example.

Yes, I suppose it could auto detect it, however having it the smallest size= possible isn't the right answer. If you begin using enums for your flags a= nd you are accessing C code that uses it's enums, it may only use 10 bits b= ut uses a 32bit value. Hopefully by having only one data member (of your in= t type) you can safely forcibly cast it (If it's the same size)
 And in the same fashion: Do you map an enum value to "1 << enum_value" in=

=A0the bit field? I know that in Delphi 'sets' worked great for me and so I= am biased for everything that works the same way. It is also the most=A0 n= atural way to work with enums that can be represented as a bit field IMO.= =A0=20 Current implementation you are explicit (ie 0x1000); Wouldn't be hard to u= se a mixin function to do the work for you.
 This means that enum values cannot be larger than 63 of course (so they f=

it inside a ulong).
 What else? hmm? I'm wondering if the documentation for flipFlag should be=

=A0"reverses the state of all given flags". Correct. So if flag A was set and B wasn't, and you flip both, B is set an= d A isn't. I'm using variadic functions since if you know you are doing mul= tiple flags you shouldn't be required to make x calls when you don't need t= o.
 How is checkElse supposed to work. I could imagine the function of a =20
 method with the signature "T (T flag, T else)". What is the use case for =

it? Even check seems to accept multiple flags, where I would expect only on= e. I've updated the signature to use "const T checkElse(T Else, T flag...)". N= ow how you would use this is if you want an ENUM flag returned and not a tr= ue/false, but you don't want to have to manually check. so you can do this.= (Returns first true ENUM, or if it fails return Else) HandleFlags!(E, int) Flags; somefunc(Flags.checkElse(ETEST.unused, ETEST.isMaster, ETEST.isSlave), ...) Rather than.. if (Flags.check(TEST.isMaster)) somefunc(ETEST.isMaster, ...) else if (Flags.check(TEST.isSlave)) somefunc(ETEST.isSlave, ...) else somefunc(ETEST.unused, ...)
 We could also introduce some operator overloading: setFlag
 <=3D> +=3D,=A0=20
 clearFlag <=3D> -=3D, flipFlag <=3D> ^=3D, check
 <=3D> in. (setFlag could be |=3D as=A0=20
 well, but +=3D is more consistent with clearFlag as -=3D)

Yep, overloading would be useful later :) But not just yet.
Jan 29 2012