digitalmars.D - strong enums: why implicit conversion to basetype?
- Trass3r (25/25) Jan 26 2012 I thought it'd be good to outsource this question from the other
- Trass3r (4/10) Jan 26 2012 No. The intention is "Bla and Blub don't have anything to do with
- Manfred Nowak (4/5) Jan 26 2012 Yes. I realized my fault and canceled my message, but wasn't fast
- Manfred Nowak (4/5) Jan 26 2012 Yes. Otherwise it would be at least close to equivalence to a
- Trass3r (3/7) Jan 26 2012 Even typedef implicitly converts in one of the directions.
- Manfred Nowak (30/33) Jan 26 2012 `typedef' is or will be disallowed in D because of reasons I do not
- Trass3r (8/23) Jan 26 2012 C's typedef is equal to D's alias.
- Manfred Nowak (12/17) Jan 26 2012 [...]
- Timon Gehr (4/28) Jan 26 2012 I have argued for banning those operations on strong enums before, but
- Trass3r (2/4) Jan 26 2012 Yep, that's what the other thread 'using enums for flags' is about.
- deadalnix (3/27) Jan 26 2012 I don't see any problem with that : it is perfectly safe. The other way
- Don (3/6) Jan 26 2012 Allowing it to be used as an argument when calling C functions?
- Trass3r (5/7) Jan 26 2012 extern(C):
- Don Clugston (3/11) Jan 27 2012 Some of the Windows functions are made of multiple enums of different
- Trass3r (3/11) Jan 27 2012 -.- Microsuckx.
- Alvaro (16/24) Jan 26 2012 I kind of agree. I understand enums as a way to define "tags" or flags
- Manfred Nowak (5/7) Jan 26 2012 But that the example works is not the fault of the existence of enums.
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (10/28) Jan 26 2012 ad
- Chad J (5/30) Jan 27 2012 You'd think that with some use of a templated struct and some "alias
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (8/42) Jan 27 2012 .
- Era Scarecrow (25/48) Jan 27 2012 I have a personal class i am using, may submit it later to Walter to ad...
- Marco Leise (29/77) Jan 29 2012 ng =
- bearophile (11/11) Jan 29 2012 This is an important topic.
- Era Scarecrow (33/81) Jan 29 2012 Yes, I suppose it could auto detect it, however having it the smallest s...
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
On Thursday, 26 January 2012 at 14:45:02 UTC, Manfred Nowak wrote:Trass3r wrote:No. The intention is "Bla and Blub don't have anything to do with each other". That's why I question the implicit conversion.but by using named enums I made clear that Bla and Blub are totally differentNo. Obviously you decjlared both to be implicitely convertable to a common super type: int. To change this, both supertypes have be changed.
Jan 26 2012
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
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
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.Is there any merit in having implicit conversion to the basetype?Yes. Otherwise it would be at least close to equivalence to a `typedef'.
Jan 26 2012
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
`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=5467In C and C++ their existence introduce problems because they increase the amount of parsing passes.C's typedef is equal to D's alias.Again, this thread is all about discussing the right way to do it and not about what the buggy and holey spec reads.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: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 validThat'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
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
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
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
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
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
extern(C): enum Bla : int {...} void foo(Bla b); How does this require implicit conversion? The codegen treats Bla like basetype anyway.Is there any merit in having implicit conversion to the basetype?Allowing it to be used as an argument when calling C functions?
Jan 26 2012
On 26/01/12 21:26, Trass3r wrote:Some of the Windows functions are made of multiple enums of different types, ORed together.extern(C): enum Bla : int {...} void foo(Bla b); How does this require implicit conversion? The codegen treats Bla like basetype anyway.Is there any merit in having implicit conversion to the basetype?Allowing it to be used as an argument when calling C functions?
Jan 27 2012
-.- Microsuckx. Then I think it should either become a combined enum or just anonymous ones (+ aliases to basetype perhaps).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
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
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
On Thu, 26 Jan 2012 23:49:40 +0100, Alvaro <alvaroDotSegura gmail.com> wrote:El 26/01/2012 14:59, Trass3r escribi=C3=B3:adI thought it'd be good to outsource this question from the other thre==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
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: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.El 26/01/2012 14:59, Trass3r escribió: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.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.
Jan 27 2012
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=gsA 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=atused 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==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=fortwo roles in D - that of an enumeration and that of a set of flags. Only =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
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...); }question from the other threadEl 26/01/2012 14:59, Trass3r escribi?:I thought it'd be good to outsource thisconversion to the basetype?about enums as flags. Is there any merit in having implicitbrings no advantages.Imo it only introduces a severe bug source andnot. Enums play twoSometimes, bitwise operations make sense, other timesroles in D - that of an enumeration and that of a setof flags. Only forthe 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.
Jan 27 2012
Am 27.01.2012, 20:01 Uhr, schrieb Era Scarecrow <rtcvb32 yahoo.com>:=I have a personal class i am using, may submit it later to Walter to =question from the other threadEl 26/01/2012 14:59, Trass3r escribi?:I thought it'd be good to outsource thisconversion to the basetype?about enums as flags. Is there any merit in having implicitbrings no advantages.Imo it only introduces a severe bug source andnot. Enums play twoSometimes, bitwise operations make sense, other timesroles in D - that of an enumeration and that of a setof flags. Only forthe 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.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
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
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)///T of type ENUM, and S of an integral. struct HandleFlags(T, S) =A0 { =A0=A0=A0 S state;=A0=A0=A0 ///Holdsstate.=A0=A0=A0 alias T T_Enum; =A0=A0=A0=A0=A0this(T[] setFlags...); =A0=A0=A0 ///Returns true/false if a specificENUM flag has been set.=A0=A0=A0 bool check(T[] flag...); =A0=A0=A0 ///Returns true/false if all flags are setENUM 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=20the 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 specificflag.=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.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