digitalmars.D - using enums for flags
- Trass3r (36/36) Jan 24 2012 So I was just reading
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (4/8) Jan 25 2012 Well, it may be a heap of code, but it's a lot less than what you linked...
- Trass3r (4/8) Jan 25 2012 Seconded.
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (5/13) Jan 25 2012 In the codebase I have to work with, having the same enum specified in
- Trass3r (1/5) Jan 25 2012 Why don't you fix it then?
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (4/9) Jan 26 2012 I'm not allowed to. The project I'm on has its tasks, and fixing things
- Era Scarecrow (37/48) Jan 25 2012 To my understanding, converting an int to an enum (In C++ or D) is basi...
- Jonathan M Davis (17/60) Jan 25 2012 I think that it makes sense to use enums as flags, but I do _not_ think ...
- Trass3r (8/17) Jan 25 2012 To me it doesn't make any sense at all to allow bitwise operations on
- Jonathan M Davis (11/35) Jan 25 2012 What type safety? You're dealing with a uint (or ushort or ulong) with a...
- Mantis (21/31) Jan 25 2012 I agree, enum variable should only contain one of the enumerated values....
- Trass3r (5/26) Jan 26 2012 Valid example.
- bearophile (4/8) Jan 26 2012 Those are important topics. D must offer a solution that is both safer a...
- Era Scarecrow (39/76) Jan 25 2012 Indeed. enum should be only a single value, be it a number like 42, or a...
- Marco Leise (6/6) Jan 25 2012 Delphi: http://delphi.about.com/od/beginners/a/delphi_set_type.htm |
- foobar (9/17) Jan 26 2012 vote += MAX_INT for the above suggestion which btw is also the
So I was just reading http://stackoverflow.com/questions/1448396/how-to-use-enums-as-flags-in-c And did a quick test: enum STC { A = 0x1, B = 0x2, C = 0x4 } enum FOO { F = 0x8, G = 0x10 } void main() { STC s = STC.A | STC.C; STC s2 = s & STC.A; auto s2_2 = s & STC.A; static assert(is(typeof(s2_2) == STC)); static assert(!__traits(compiles, { if (s & FOO.F) {} })); // fails. that one's hard to track down static assert(!__traits(compiles, { auto s3 = s & FOO.F; })); // fails, cause implicitly converts to int static assert(!__traits(compiles, { STC s4 = s & FOO.F; })); static assert(!__traits(compiles, { FOO s5 = s & FOO.F; })); static assert(!__traits(compiles, { STC t = STC.A | FOO.F; })); static assert(!__traits(compiles, { auto t = STC.A | FOO.F; })); // fails static assert(!__traits(compiles, { if (s & STC.B == 0) {} })); // works, but error not gagged static assert(!__traits(compiles, { if (s && STC.B) {} })); // fails } Does it really make sense to allow bitwise operations on different enums? There should at least be some way to get this straight without having to resort to a heap of code like in C++: http://www.artima.com/cppsource/safelabels.html
Jan 24 2012
On Wed, 25 Jan 2012 03:22:03 +0100, Trass3r <un known.com> wrote:Does it really make sense to allow bitwise operations on different enums?Maybe. Certainly sometimes, but those could just as easily use casts.There should at least be some way to get this straight without having to resort to a heap of code like in C++: http://www.artima.com/cppsource/safelabels.htmlWell, it may be a heap of code, but it's a lot less than what you linked. Example attached. It's WTFPL or freer, so feel free to use it as you wish.
Jan 25 2012
Examples please.Does it really make sense to allow bitwise operations on different enums?Maybe. Certainly sometimesbut those could just as easily use casts.Seconded. I generally don't see any merit in letting enums *implicitly* convert to their base type.
Jan 25 2012
On Wed, 25 Jan 2012 22:47:49 +0100, Trass3r <un known.com> wrote:In the codebase I have to work with, having the same enum specified in different places is rather common. Yeah, I hate it. This means I might have a filter defined using one enum, and the value to filter being a different type with the same values.Examples please.Does it really make sense to allow bitwise operations on different enums?Maybe. Certainly sometimesbut those could just as easily use casts.Seconded. I generally don't see any merit in letting enums *implicitly* convert to their base type.
Jan 25 2012
In the codebase I have to work with, having the same enum specified in different places is rather common. Yeah, I hate it. This means I might have a filter defined using one enum, and the value to filter being a different type with the same values.Why don't you fix it then?
Jan 25 2012
On Thu, 26 Jan 2012 00:49:58 +0100, Trass3r <un known.com> wrote:I'm not allowed to. The project I'm on has its tasks, and fixing things in other projects is not one of them, especially when such an easy workaround exists.In the codebase I have to work with, having the same enum specified in different places is rather common. Yeah, I hate it. This means I might have a filter defined using one enum, and the value to filter being a different type with the same values.Why don't you fix it then?
Jan 26 2012
On Wed, 25 Jan 2012 03:22:03 +0100, Trass3r <un known.com> wrote:To my understanding, converting an int to an enum (In C++ or D) is basical= ly illegal. You'd have to do force casting to get it to work, which isn't s= afe. Enums seem better suited for a long range of codes that don't have spe= cific bits for on/off If it hasn't been suggested yet, I am currently using a block of personal = code to use enums as flags. I'll give you a stripped down version of the st= ructure, if everyone says 'yay' or perhaps Walter feels he wants it, we can= propose maybe get it in phobos. How it works, is internally it will store the flag as an int (or whatever = type S is), then you can do checks and it will convert the enum to an int a= nd do appropriate bit casts. Haven't tried to consider how to get a array o= f separate flags. ///T of type ENUM, and S of an integral. struct HandleFlags(T, S) { =09S state;=09///Holds state. =09alias T T_Enum; =09this(T[] setFlags...); =09///Returns true/false if a specific ENUM flag has been set. =09bool check(T flag); =09/** =09Checks if a flag has been set, returning that ENUM, otherwise returning = the Else flag. =09*/ =09T checkElse(T flag, T Else); =09 =09///Sets specific flag on or off. Default sets flag =09void setFlag(T flag, bool on_off =3D true); =09 =09///reverses the state of a specific flag. =09void flipFlag(T flag); } Usage is like this. =09enum ETEST {zero =3D 0, one =3D 1, two =3D 2, four =3D 4} =09HandleFlags!(ETEST, int) ftest; =09HandleFlags!(ETEST, int) ftest1(ETEST.one); //1 =09HandleFlags!(ETEST, int) ftest2(ETEST.two, ETEST.four); //6Does it really make sense to allow bitwise operationson different enums? =20 Maybe. Certainly sometimes, but those could just as easily use casts. =20There should at least be some way to get this straightwithout having to=A0=20resort to a heap of code like in C++:=A0=20
Jan 25 2012
On Wednesday, January 25, 2012 03:22:03 Trass3r wrote:So I was just reading http://stackoverflow.com/questions/1448396/how-to-use-enums-as-flags-in-c And did a quick test: enum STC { A = 0x1, B = 0x2, C = 0x4 } enum FOO { F = 0x8, G = 0x10 } void main() { STC s = STC.A | STC.C; STC s2 = s & STC.A; auto s2_2 = s & STC.A; static assert(is(typeof(s2_2) == STC)); static assert(!__traits(compiles, { if (s & FOO.F) {} })); // fails. that one's hard to track down static assert(!__traits(compiles, { auto s3 = s & FOO.F; })); // fails, cause implicitly converts to int static assert(!__traits(compiles, { STC s4 = s & FOO.F; })); static assert(!__traits(compiles, { FOO s5 = s & FOO.F; })); static assert(!__traits(compiles, { STC t = STC.A | FOO.F; })); static assert(!__traits(compiles, { auto t = STC.A | FOO.F; })); // fails static assert(!__traits(compiles, { if (s & STC.B == 0) {} })); // works, but error not gagged static assert(!__traits(compiles, { if (s && STC.B) {} })); // fails } Does it really make sense to allow bitwise operations on different enums? There should at least be some way to get this straight without having to resort to a heap of code like in C++: http://www.artima.com/cppsource/safelabels.htmlI think that it makes sense to use enums as flags, but I do _not_ think that it makes sense to use an enum as the type of the variable _holding_ the flags. STC var = STC.A & STC.B; is an abimination IMHO. adding another enum to the list and doing something like STC var = STC.A & FOO.F; just makes it worse. It should be something like uint var = STC.A & FOO.F; instead. Now anding two different enums like that is still a bit weird, but I don't know that it should really be illegal. It's assigning anded enums to an enum that I want to see die. I'd _love_ it if that were illegal. For instance, std.socket uses flag enums, which is fine, but in some places it uses them as the type of function parameters, which is _not_ a good idea IMHO. Whenever I think about it, I keep meaning to go and fix it, but I never get around to it. - Jonathan M Davis
Jan 25 2012
I think that it makes sense to use enums as flags, but I do _not_ think that it makes sense to use an enum as the type of the variable _holding_ the flags. STC var = STC.A & STC.B;We could easily introduce flags enum or whatever to make it more clearjust makes it worse. It should be something like uint var = STC.A & FOO.F;To me it doesn't make any sense at all to allow bitwise operations on different *named* enums. I also don't see why you would really need implicit conversion to the base type.For instance, std.socket uses flag enums, which is fine, but in some places it uses them as the type of function parameters, which is _not_ a good idea IMHO.You really think int is any better?? No type safety at all.
Jan 25 2012
On Thursday, January 26, 2012 01:17:51 Trass3r wrote:What type safety? You're dealing with a uint (or ushort or ulong) with a bunch of specific bits set which represent flags. What other type would you want? You could typedef it I suppose (well, use the TypeDef library type when it's merged in anyway), but you're dealing with a fixed number of bits, which is exactly what an unsigned integer is. It's just a question of which are on and which are off. That's certainly not what _enum_ is for. It's a fixed set of values. And that's my beef with using it as the result of &ing flags. The result isn't one of those flags, so it has no business being an enum. - Jonathan M DavisI think that it makes sense to use enums as flags, but I do _not_ think that it makes sense to use an enum as the type of the variable _holding_ the flags. STC var = STC.A & STC.B;We could easily introduce flags enum or whatever to make it more clearjust makes it worse. It should be something like uint var = STC.A & FOO.F;To me it doesn't make any sense at all to allow bitwise operations on different *named* enums. I also don't see why you would really need implicit conversion to the base type.For instance, std.socket uses flag enums, which is fine, but in some places it uses them as the type of function parameters, which is _not_ a good idea IMHO.You really think int is any better?? No type safety at all.
Jan 25 2012
26.01.2012 2:40, Jonathan M Davis пишет:What type safety? You're dealing with a uint (or ushort or ulong) with a bunch of specific bits set which represent flags. What other type would you want? You could typedef it I suppose (well, use the TypeDef library type when it's merged in anyway), but you're dealing with a fixed number of bits, which is exactly what an unsigned integer is. It's just a question of which are on and which are off. That's certainly not what _enum_ is for. It's a fixed set of values. And that's my beef with using it as the result of&ing flags. The result isn't one of those flags, so it has no business being an enum. - Jonathan M DavisI agree, enum variable should only contain one of the enumerated values. Here's an example how current way may lead to unexpected result: enum Foo { A = 1, B } void bar( Foo foo ) { final switch( foo ) { case Foo.A: writeln( "A" ); return; case Foo.B: writeln( "B" ); return; } writeln( "Unreachable branch" ); } int main() { bar( Foo.A | Foo.B ); return 0; } It is intuitive to assume that the final switch will always hit one of it's branches, yet this doesn't work here.
Jan 25 2012
I agree, enum variable should only contain one of the enumerated values. Here's an example how current way may lead to unexpected result: enum Foo { A = 1, B } void bar( Foo foo ) { final switch( foo ) { case Foo.A: writeln( "A" ); return; case Foo.B: writeln( "B" ); return; } writeln( "Unreachable branch" ); } int main() { bar( Foo.A | Foo.B ); return 0; } It is intuitive to assume that the final switch will always hit one of it's branches, yet this doesn't work here.Valid example. As I said one could introduce something like flags but I guess a library solution is preferred. I still wonder though if implicit conversion to the basetype has any merit.
Jan 26 2012
Trass3r:As I said one could introduce something like flags but I guess a library solution is preferred. I still wonder though if implicit conversion to the basetype has any merit.Those are important topics. D must offer a solution that is both safer and more handy than the current one. Bye, bearophile
Jan 26 2012
On Wednesday, January 25, 2012 03:22:03 Trass3r wrote:Indeed. enum should be only a single value, be it a number like 42, or a binary bit 1<<42. The type checking enforces it to be a valid value. You can easily turn a enum into an int, but the other direction as said before, is not so easy. I started on C++ recently and came across that problem, and came across this solution (ported shortly after to D). Here's my unittests. (they pass) unittest{ enum ETEST { none = 0, one = 1, two = 2, four = 4 } alias ETEST.none none; alias ETEST.one one; alias ETEST.two two; alias ETEST.four four; HandleFlags!(ETEST, int) ftest; //test all flags off. Uses int checks. assert(ftest.state == 0); //start empty. //set 2 flag ftest.setFlag(two, true); assert(ftest.state == 2); //set 4, should now be 6 ftest.setFlag(four, true); assert(ftest.state == 6); //turn off 2 bit ftest.setFlag(two, false); assert(ftest.state == 4); //flip 1 ftest.flipFlag(one); //4+1 assert(ftest.state == 5); //flip 1 again. ftest.flipFlag(one); //4+0 assert(ftest.state == 4); //check truth/else ETEST x = ftest.checkElse(four, none); assert(x == four); x = ftest.checkElse(one, none); assert(x == none); assert(ftest.check(four) == true); assert(ftest.check(two) == false); }So I was just reading http://stackoverflow.com/questions/1448396/how-to-use-enums-as-flags-in-c And did a quick test:I think that it makes sense to use enums as flags, but I do _not_ think that it makes sense to use an enum as the type of the variable _holding_ the flags. STC var = STC.A & STC.B; is an abimination IMHO. adding another enum to the list and doing something like STC var = STC.A & FOO.F; just makes it worse. It should be something like uint var = STC.A & FOO.F; instead. Now anding two different enums like that is still a bit weird, but I don't know that it should really be illegal. It's assigning anded enums to an enum that I want to see die. I'd _love_ it if that were illegal. For instance, std.socket uses flag enums, which is fine, but in some places it uses them as the type of function parameters, which is _not_ a good idea IMHO. Whenever I think about it, I keep meaning to go and fix it, but I never get around to it.
Jan 25 2012
Delphi: http://delphi.about.com/od/beginners/a/delphi_set_type.htm | Scroll to: "Sets with Enumerations" Sets use the smallest integer type that can hold enough bits for the number of elements in an enum. So up to 8 enum flags use a byte for example. TDaySet in the example code would also be 1 byte in size. As the syntax suggests they don't implicitly convert forth or back to integers.
Jan 25 2012
On Thursday, 26 January 2012 at 01:44:23 UTC, Marco Leise wrote:Delphi: http://delphi.about.com/od/beginners/a/delphi_set_type.htm | Scroll to: "Sets with Enumerations" Sets use the smallest integer type that can hold enough bits for the number of elements in an enum. So up to 8 enum flags use a byte for example. TDaySet in the example code would also be 1 byte in size. As the syntax suggests they don't implicitly convert forth or back to integers.vote += MAX_INT for the above suggestion which btw is also the solution in Java 5.0 which provides an EnumSet type. At the moment eums are completely broken in D and IMO they are even since its design is only very slightly better than the original C semantics. I suggest looking into the Java implementation of Enum and EnumSet types which provides both type safety and convenience.
Jan 26 2012