Discussion:
operator | for an enum?
(too old to reply)
David Webber
2011-02-20 17:16:56 UTC
Permalink
I wonder if I'm breaking any rules here?

Consider

enum MYTYPE { ZERO=0, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN };

The names have been changed to protect the innocent, but I like enums like
this as the names are shown in the debugger, and if they are meaningful it
means I don't have to grope around in the recesses of my memory to find what
6 represents.

But one thing which doesn't automatically work is

MYTYPE a=ONE; b=TWO;
MYTYPE c = a|b;

But it appears to be fine if I include in my header file

inline MYTYPE operator | (MYTYPE a, MYTYPE b )
{
return (MYTYPE)( ((UINT)a) | ((UINT)b) );
}

Is it legal to define such operators on enums? - So far I haven't found it
in the language spec anywhere but it seems to compile fine.

And while we're at it, is the following syntax ok?

inline MYTYPE operator |= (MYTYPE a, MYTYPE b )
{
a = (MYTYPE)( ((UINT)a) | ((UINT)b) );
return a;
}

[I usually do such things with class members, but obviously that's not
appropriate here, and I'm blowed if I can remember the syntax (or indeed
find it).]

Dave

-- David Webber
Mozart Music Software
http://www.mozart.co.uk
For discussion and support see
http://www.mozart.co.uk/mozartists/mailinglist.htm
Bo Persson
2011-02-20 17:46:37 UTC
Permalink
Post by David Webber
I wonder if I'm breaking any rules here?
Consider
enum MYTYPE { ZERO=0, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN };
The names have been changed to protect the innocent, but I like
enums like this as the names are shown in the debugger, and if they
are meaningful it means I don't have to grope around in the
recesses of my memory to find what 6 represents.
But one thing which doesn't automatically work is
MYTYPE a=ONE; b=TWO;
MYTYPE c = a|b;
But it appears to be fine if I include in my header file
inline MYTYPE operator | (MYTYPE a, MYTYPE b )
{
return (MYTYPE)( ((UINT)a) | ((UINT)b) );
}
Is it legal to define such operators on enums? - So far I haven't
found it in the language spec anywhere but it seems to compile fine.
And while we're at it, is the following syntax ok?
inline MYTYPE operator |= (MYTYPE a, MYTYPE b )
{
a = (MYTYPE)( ((UINT)a) | ((UINT)b) );
return a;
}
[I usually do such things with class members, but obviously that's
not appropriate here, and I'm blowed if I can remember the syntax
(or indeed find it).]
Yes, it is perfectly fine to overload operators for user-defined
types, classes or enums doesn't matter.


Bo Persson
David Webber
2011-02-20 23:06:06 UTC
Permalink
Post by Bo Persson
Yes, it is perfectly fine to overload operators for user-defined
types, classes or enums doesn't matter.<

Thanks

Dave

-- David Webber
Mozart Music Software
http://www.mozart.co.uk
For discussion and support see
http://www.mozart.co.uk/mozartists/mailinglist.htm
Simon Trew
2013-01-15 09:01:17 UTC
Permalink
Do a quick google for Qt QFlags (QFlagsEnum). Not asking you to use Qt etc, just it has the answer you want to whether it is legal or not.

Cast from enum to long is an implicit cast, and so the other way around, but cast from enum to enum (even the same enum type) is not an implicit cast, so you should have a static_cast. Obviously it works in practice. I'm surprised if you don't get a warning for the imlicit cast. But, yep, it is the right thing in practice and the spec doesn't outrule it. It will get a bit complicated if some enums were allowed to have different byte widths than others-- something I think the spec allows, I have read the C++11 spec but it is 2000 pages nearly and I can't remember offhand, but we do have enums of a specific underlying representation now, which came from boost::enum_on_type or something. So that goes away the problem of the represnetition, in practice of course it works and is nicely optimized as I imagine you would expect, and I would imagine you are ahead of me and would also define operator &= etc.

What is DEFINITELY bad, which I have in a legacy codebase too often, is bool x(true or whatever); x|=y;. That goes through a number of hoops and of course does not do the short circuit evaluation; as far as I can work out its behaviour is undefined but it won't do what people think it will, i.e. be more efficient than x = x or y, since it must convert y from bool to integer type and convert it back, and at least on the MS compiler, it does actually do this i.e at the assembler level it does xor eax,eax cmp eax, [y] and so on, not just "or" it into the register variable which usually x will be in. So that's a premature optimization for bool, but for your enums, you're doing exactly the right thing.
Simon Trew
2013-01-15 09:16:46 UTC
Permalink
~hould have noticed the C style explicit casts, which is why you don't get the warnings. I would prefer you use static_cast etc which although more longwinded makes the next developer's job easier to see your intent. but C style cast or static_cast is just syntax and that's why you don't get the warning. The question to ask yourself is what do you think that actually casts to (with a static_cast you specify the type, e.g. long, with a C style cast you don't, and leave it to the compiler to work it out). It will probably cast to long, in practice, but you can read the rules yourself for that, so I won't repeat them. The fact that it doesn't say so in the code is why it is "left as a puzzle to the programmer", if you see what I mean: it's a trivial excerpt and I realise that, but in good faith I am assuming you wouldn't ask here except to get a definitive answer, which is why I am adding my two pennorth. (I haven't been here for a long while so am probably also wrong on almost everything I
I've said, but that's the way you get to a definitive answer from people better than me like Bo Persson--- nice to see you still here after all this time, Bo! I am trying to kinda get back to being an old faithful but I got distracted for a few years with editing Wikipedia, whose laws are even more complex than the C++ spec, but at least with the C++ spec they don't change overnight on a whim).

So make it concrete:

enum x { MY_X_VALUE } my_x;
enum y { MY_Y_VALUE } my_y;

my_x = my_y;

is illegal (in the "spec" sence and should be rejected by the compiler

my_x = (long)my_y;

Is legal (explicit cast my_y to long, implicit cast on the assignment of long to enum).

my_x |= my_y

is VERY illegal

inline my_x::operator|=(const my_y& y) { x = static_cast<unsigned long>(y); } is not just legal but good practice, cos you have done what you wanted to do: hide the slightly odd syntax in one place that can only be argued about in one place, not scattered throughout the code. Exactly the right thing to do, I think.
Loading...