Thread overview | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 25, 2012 using enums for flags | ||||
---|---|---|---|---|
| ||||
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 |
January 25, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
Posted in reply to Trass3r Attachments: | 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.html Well, 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. |
January 25, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
Posted in reply to Simen Kjærås | >> Does it really make sense to allow bitwise operations on different enums? > Maybe. Certainly sometimes Examples please. > but those could just as easily use casts. Seconded. I generally don't see any merit in letting enums *implicitly* convert to their base type. |
January 25, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
Posted in reply to Trass3r | On Wed, 25 Jan 2012 22:47:49 +0100, Trass3r <un@known.com> wrote: >>> Does it really make sense to allow bitwise operations on different enums? >> Maybe. Certainly sometimes > Examples please. 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. >> but those could just as easily use casts. > > Seconded. > > I generally don't see any merit in letting enums *implicitly* convert to their base type. |
January 25, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
Posted in reply to Simen Kjærås | > 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?
|
January 26, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
Posted in reply to Trass3r | 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.html
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.
- Jonathan M Davis
|
January 26, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | > 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 clear like in C#. > just 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. |
January 26, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
Posted in reply to Trass3r | On Thursday, January 26, 2012 01:17:51 Trass3r wrote:
> > 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 clear like in C#.
>
> > just 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.
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 Davis
|
January 26, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
Posted in reply to Trass3r | 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. |
January 26, 2012 Re: using enums for flags | ||||
---|---|---|---|---|
| ||||
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 Davis
>
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.
|
Copyright © 1999-2021 by the D Language Foundation